blob: 9816b1e9d0cdcd40a35c7a43e047b6123d206db3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sshd.common.file.nativefs;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.sshd.common.file.FileSystemView;
import org.apache.sshd.common.file.SshFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.sshd.common.file.nativefs.NativeSshFile.normalizeSeparateChar;
/**
* <strong>Internal class, do not use directly.</strong>
*
* File system view based on native file system. Here the root directory will be
* user virtual root (/).
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
public class NativeFileSystemView implements FileSystemView {
private final Logger LOG = LoggerFactory.getLogger(NativeFileSystemView.class);
private Map<String, String> roots;
// the first and the last character will always be '/'
// It is always with respect to one of the roots.
private String current;
private String userName;
private char separator;
private boolean caseInsensitive = false;
/**
* Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
*/
public NativeFileSystemView(String userName) {
this(userName, false);
}
/**
* Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
*/
public NativeFileSystemView(String userName, boolean caseInsensitive) {
this(userName, getAllRoots(), System.getProperty("user.dir"), File.separatorChar, caseInsensitive);
}
/**
* Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
*/
public NativeFileSystemView(String userName, Map<String, String> roots, String current) {
this(userName, roots, current, File.separatorChar, false);
}
/**
* Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
*
* @param userName the user name
* @param roots known root mapping, key is the virtual root name, value is the physical file
* @param current the virtual current dir
*/
public NativeFileSystemView(String userName, Map<String, String> roots, String current, char separator, boolean caseInsensitive) {
if (userName == null) {
throw new IllegalArgumentException("user can not be null");
}
// Normalize roots
Map<String, String> verRoots = new LinkedHashMap<String, String>();
for (String r : roots.keySet()) {
String virtual = appendSlash(normalizeSeparateChar(r));
String physical = appendSlash(normalizeSeparateChar(roots.get(r)));
verRoots.put(virtual, physical);
}
// add last '/' if necessary
current = appendSlash(normalizeSeparateChar(current));
// Verify the current dir is relative to a known root
String root = null;
for (String r : verRoots.keySet()) {
if (current.startsWith(r)) {
root = r;
break;
}
}
if (root == null) {
throw new IllegalArgumentException("Current dir " + current + " does not start from a known root: " + new ArrayList<String>(verRoots.keySet()));
}
this.separator = separator;
this.caseInsensitive = caseInsensitive;
this.roots = verRoots;
this.current = current;
this.userName = userName;
LOG.debug("Native filesystem view created for user \"{}\" with current dir \"{}\"", userName, this.current);
}
private String appendSlash(String path) {
return path.endsWith("/") ? path : path + "/";
}
private static Map<String, String> getAllRoots() {
Map<String, String> roots = new LinkedHashMap<String, String>();
if (isWindows) {
for (File file : File.listRoots()) {
if (file.exists()) {
String root = file.toString();
String name = root.substring(0, root.length() - 1);
roots.put(name, root);
}
}
} else {
roots.put("/", "/");
}
return roots;
}
public String getUserName() {
return userName;
}
public char getSeparator() {
return separator;
}
/**
* Get file object.
*/
public SshFile getFile(String file) {
return getFile(current, file);
}
public SshFile getFile(SshFile baseDir, String file) {
return getFile(baseDir.getAbsolutePath(), file);
}
protected SshFile getFile(String dir, String file) {
dir = appendSlash(normalizeSeparateChar(dir));
file = normalizeSeparateChar(file);
// Compute root + non rooted absolute file
String root = null;
if (roots.size() > 1 && file.startsWith("/")) {
file = file.substring(1);
}
for (String r : roots.keySet()) {
if (!file.isEmpty() && r.equals(file + "/")) {
file += "/";
}
if (file.startsWith(r)) {
root = r;
file = "/" + file.substring(r.length());
break;
}
}
if (root == null) {
// file is relative to dir
file = dir + file;
for (String r : roots.keySet()) {
if (file.startsWith(r)) {
root = r;
file = "/" + file.substring(r.length());
break;
}
}
}
if (root == null) {
throw new IllegalStateException("Could not find root dir for file(" + dir + ", " + file + ")");
}
// Physical root
String physicalRoot = roots.get(root);
// get actual file object
String physicalName = NativeSshFile.getPhysicalName(physicalRoot, "/", file, caseInsensitive);
File fileObj = new File(physicalName);
// strip the root directory and return
String userFileName = root + physicalName.substring(physicalRoot.length());
return createNativeSshFile(userFileName, fileObj, userName);
}
static boolean isJava7;
static boolean isWindows;
static {
// Check java 7
boolean j7 = false;
try {
ClassLoader.getSystemClassLoader().loadClass("java.nio.file.Files");
j7 = true;
} catch (Throwable t) {
// Ignore
}
isJava7 = j7;
// Check windows
boolean win = false;
try {
win = System.getProperty("os.name").toLowerCase().contains("win");
} catch (Throwable t) {
// Ignore
}
isWindows = win;
}
public NativeSshFile createNativeSshFile(String name, File file, String userName) {
name = deNormalizeSeparateChar(name);
if (isJava7 && !isWindows) {
return new NativeSshFileNio(this, name, file, userName);
} else {
return new NativeSshFile(this, name, file, userName);
}
}
/**
* Normalize separate character. Separate character should be '/' always.
*/
public final String deNormalizeSeparateChar(final String pathName) {
return pathName.replace('/', separator);
}
public FileSystemView getNormalizedView() {
if (roots.size() == 1 && roots.containsKey("/") && separator == '/') {
return this;
}
return new NativeFileSystemView(userName, roots, current, '/', caseInsensitive) {
public SshFile getFile(String file) {
return getFile(reroot(current), file);
}
public SshFile getFile(SshFile baseDir, String file) {
return getFile(baseDir.getAbsolutePath(), file);
}
public FileSystemView getNormalizedView() {
return this;
}
protected String reroot(String file) {
file = appendSlash(file);
for (String r : roots.keySet()) {
if (file.startsWith(r)) {
return "/" + normalizeRoot(r) + file.substring(r.length());
}
}
throw new IllegalArgumentException();
}
protected SshFile getFile(String dir, String file) {
dir = appendSlash(normalizeSeparateChar(dir));
file = normalizeSeparateChar(file);
// Compute root + non rooted absolute file
if (!file.startsWith("/")) {
file = dir + file;
}
// get actual file object
String userFileName = NativeSshFile.getPhysicalName("/", "/", file, caseInsensitive);
if (userFileName.equals("/")) {
return new RootFile();
}
int idx = userFileName.indexOf("/", 1);
if (idx < 0) {
String root = userFileName + "/";
String physRoot = null;
for (String r : roots.keySet()) {
if (normalizeRoot(r).equals(root)) {
physRoot = roots.get(r);
break;
}
}
if (physRoot == null) {
throw new IllegalArgumentException("Unknown root " + userFileName);
}
File fileObj = new File(physRoot);
userFileName = normalizeSeparateChar(userFileName);
return createNativeSshFile(userFileName, fileObj, userName);
} else {
String root = userFileName.substring(1, idx) + "/";
String physRoot = null;
for (String r : roots.keySet()) {
if (normalizeRoot(r).equals(root)) {
physRoot = roots.get(r);
break;
}
}
if (physRoot == null) {
throw new IllegalArgumentException("Unknown root " + userFileName);
}
File fileObj = new File(physRoot + userFileName.substring(idx + 1));
userFileName = normalizeSeparateChar(userFileName);
return createNativeSshFile(userFileName, fileObj, userName);
}
}
};
}
protected static String normalizeRoot(String root) {
return root.replace(":", "");
}
class RootFile implements SshFile {
public String getAbsolutePath() {
return "/";
}
public String getName() {
return "/";
}
public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException {
return null;
}
public void setAttributes(Map<Attribute, Object> attributes) throws IOException {
throw new UnsupportedOperationException();
}
public Object getAttribute(Attribute attribute, boolean followLinks) throws IOException {
return null;
}
public void setAttribute(Attribute attribute, Object value) throws IOException {
throw new UnsupportedOperationException();
}
public String readSymbolicLink() throws IOException {
return null;
}
public void createSymbolicLink(SshFile destination) throws IOException {
}
public String getOwner() {
return null;
}
public boolean isDirectory() {
return true;
}
public boolean isFile() {
return false;
}
public boolean doesExist() {
return true;
}
public boolean isReadable() {
return true;
}
public boolean isWritable() {
return false;
}
public boolean isExecutable() {
return false;
}
public boolean isRemovable() {
return false;
}
public SshFile getParentFile() {
return null;
}
public long getLastModified() {
return 0;
}
public boolean setLastModified(long time) {
return false;
}
public long getSize() {
return 0;
}
public boolean mkdir() {
return false;
}
public boolean delete() {
return false;
}
public boolean create() throws IOException {
return false;
}
public void truncate() throws IOException {
}
public boolean move(SshFile destination) {
return false;
}
public List<SshFile> listSshFiles() {
List<SshFile> list = new ArrayList<SshFile>();
for (String root : roots.keySet()) {
String display = normalizeRoot(root);
display = "/" + display.substring(display.length() - 1);
list.add(createNativeSshFile(display, new File(roots.get(root)), userName));
}
return list;
}
public OutputStream createOutputStream(long offset) throws IOException {
return null;
}
public InputStream createInputStream(long offset) throws IOException {
return null;
}
public void handleClose() throws IOException {
}
}
}