Make sure nio2 is disabled on jdk < 7 while still supporting various commands correctly
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/d7c21f79 Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/d7c21f79 Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/d7c21f79 Branch: refs/heads/master Commit: d7c21f79990e17fcafa7df97c5eda7122189ecce Parents: ca990bd Author: Guillaume Nodet <[email protected]> Authored: Mon Jul 22 20:25:15 2013 +0200 Committer: Guillaume Nodet <[email protected]> Committed: Mon Jul 22 20:25:15 2013 +0200 ---------------------------------------------------------------------- .../file/nativefs/NativeFileSystemView.java | 18 ++- .../common/file/nativefs/NativeSshFile.java | 127 ++++------------ .../common/file/nativefs/NativeSshFileNio.java | 149 +++++++++++++++++++ .../apache/sshd/server/sftp/SftpSubsystem.java | 51 +++++-- 4 files changed, 233 insertions(+), 112 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d7c21f79/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java index dbf07bc..a39c4c8 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java @@ -113,7 +113,23 @@ public class NativeFileSystemView implements FileSystemView { return System.getProperty("user.dir"); } + static boolean isJava7; + static { + boolean j7 = false; + try { + ClassLoader.getSystemClassLoader().loadClass("java.nio.file.Files"); + j7 = true; + } catch (Throwable t) { + // Ignore + } + isJava7 = j7; + } + public NativeSshFile createNativeSshFile(final String fileName2, final File fileObj, final String userName2) { - return new NativeSshFile(this, fileName2, fileObj, userName2); + if (isJava7) { + return new NativeSshFileNio(this, fileName2, fileObj, userName2); + } else { + return new NativeSshFile(this, fileName2, fileObj, userName2); + } } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d7c21f79/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java index 2366706..53d573b 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java @@ -27,23 +27,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.lang.reflect.Method; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; -import java.nio.file.attribute.GroupPrincipal; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.UserPrincipal; -import java.nio.file.attribute.UserPrincipalLookupService; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.StringTokenizer; import org.apache.sshd.common.file.SshFile; @@ -59,18 +49,18 @@ import org.slf4j.LoggerFactory; */ public class NativeSshFile implements SshFile { - private static final Logger LOG = LoggerFactory.getLogger(NativeSshFile.class); + protected static final Logger LOG = LoggerFactory.getLogger(NativeSshFile.class); // the file name with respect to the user root. // The path separator character will be '/' and // it will always begin with '/'. - private String fileName; + protected String fileName; - private File file; + protected File file; - private String userName; + protected String userName; - private final NativeFileSystemView nativeFileSystemView; + protected final NativeFileSystemView nativeFileSystemView; /** * Constructor, internal do not use directly. @@ -606,90 +596,39 @@ public class NativeSshFile implements SshFile { } public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException { - Map<String, Object> a = Files.readAttributes( - file.toPath(), - "unix:size,uid,owner,gid,group,isDirectory,isRegularFile,isSymbolicLink,permissions,creationTime,lastModifiedTime,lastAccessTime", - followLinks ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS }); Map<Attribute, Object> map = new HashMap<Attribute, Object>(); - map.put(Attribute.Size, a.get("size")); - map.put(Attribute.Uid, a.get("uid")); - map.put(Attribute.Owner, ((UserPrincipal) a.get("owner")).getName()); - map.put(Attribute.Gid, a.get("gid")); - map.put(Attribute.Group, ((GroupPrincipal) a.get("group")).getName()); - map.put(Attribute.IsDirectory, a.get("isDirectory")); - map.put(Attribute.IsRegularFile, a.get("isRegularFile")); - map.put(Attribute.IsSymbolicLink, a.get("isSymbolicLink")); - map.put(Attribute.CreationTime, ((FileTime) a.get("creationTime")).toMillis()); - map.put(Attribute.LastModifiedTime, ((FileTime) a.get("lastModifiedTime")).toMillis()); - map.put(Attribute.LastAccessTime, ((FileTime) a.get("lastAccessTime")).toMillis()); - map.put(Attribute.Permissions, fromPerms((Set<PosixFilePermission>) a.get("permissions"))); - return map; - } - - private EnumSet<Permission> fromPerms(Set<PosixFilePermission> perms) { + map.put(Attribute.Size, getSize()); + map.put(Attribute.IsDirectory, isDirectory()); + map.put(Attribute.IsRegularFile, isFile()); + map.put(Attribute.IsSymbolicLink, false); + map.put(Attribute.LastModifiedTime, getLastModified()); + map.put(Attribute.LastAccessTime, getLastModified()); + map.put(Attribute.Owner, userName); + map.put(Attribute.Group, userName); EnumSet<Permission> p = EnumSet.noneOf(Permission.class); - for (PosixFilePermission perm : perms) { - switch (perm) { - case OWNER_READ: p.add(Permission.UserRead); break; - case OWNER_WRITE: p.add(Permission.UserWrite); break; - case OWNER_EXECUTE: p.add(Permission.UserExecute); break; - case GROUP_READ: p.add(Permission.GroupRead); break; - case GROUP_WRITE: p.add(Permission.GroupWrite); break; - case GROUP_EXECUTE: p.add(Permission.GroupExecute); break; - case OTHERS_READ: p.add(Permission.OthersRead); break; - case OTHERS_WRITE: p.add(Permission.OthersWrite); break; - case OTHERS_EXECUTE: p.add(Permission.OthersExecute); break; - } + if (isReadable()) { + p.add(Permission.UserRead); + p.add(Permission.GroupRead); + p.add(Permission.OthersRead); } - return p; - } - - public void setAttributes(Map<Attribute, Object> attributes) throws IOException { - for (Attribute attribute : attributes.keySet()) { - String name = null; - Object value = attributes.get(attribute); - switch (attribute) { - case Uid: name = "unix:uid"; break; - case Owner: name = "unix:owner"; value = toUser((String) value); break; - case Gid: name = "unix:gid"; break; - case Group: name = "unix:group"; value = toGroup((String) value); break; - case CreationTime: name = "unix:creationTime"; value = FileTime.fromMillis((Long) value); break; - case LastModifiedTime: name = "unix:lastModifiedTime"; value = FileTime.fromMillis((Long) value); break; - case LastAccessTime: name = "unix:lastAccessTime"; value = FileTime.fromMillis((Long) value); break; - case Permissions: name = "unix:permissions"; value = toPerms((EnumSet<Permission>) value); break; - } - if (name != null && value != null) { - Files.setAttribute(file.toPath(), name, value, LinkOption.NOFOLLOW_LINKS); - } + if (isWritable()) { + p.add(Permission.UserWrite); + p.add(Permission.GroupWrite); + p.add(Permission.OthersWrite); } + if (isExecutable()) { + p.add(Permission.UserExecute); + p.add(Permission.GroupExecute); + p.add(Permission.OthersExecute); + } + map.put(Attribute.Permissions, p); + return map; } - private GroupPrincipal toGroup(String name) throws IOException { - UserPrincipalLookupService lookupService = file.toPath().getFileSystem().getUserPrincipalLookupService(); - return lookupService.lookupPrincipalByGroupName(name); - } - - private UserPrincipal toUser(String name) throws IOException { - UserPrincipalLookupService lookupService = file.toPath().getFileSystem().getUserPrincipalLookupService(); - return lookupService.lookupPrincipalByName(name); - } - - private Set<PosixFilePermission> toPerms(EnumSet<Permission> perms) { - Set<PosixFilePermission> set = new HashSet<PosixFilePermission>(); - for (Permission p : perms) { - switch (p) { - case UserRead: set.add(PosixFilePermission.OWNER_READ); break; - case UserWrite: set.add(PosixFilePermission.OWNER_WRITE); break; - case UserExecute: set.add(PosixFilePermission.OWNER_EXECUTE); break; - case GroupRead: set.add(PosixFilePermission.GROUP_READ); break; - case GroupWrite: set.add(PosixFilePermission.GROUP_WRITE); break; - case GroupExecute: set.add(PosixFilePermission.GROUP_EXECUTE); break; - case OthersRead: set.add(PosixFilePermission.OTHERS_READ); break; - case OthersWrite: set.add(PosixFilePermission.OTHERS_WRITE); break; - case OthersExecute: set.add(PosixFilePermission.OTHERS_EXECUTE); break; - } + public void setAttributes(Map<Attribute, Object> attributes) throws IOException { + if (!attributes.isEmpty()) { + throw new UnsupportedOperationException(); } - return set; } public Object getAttribute(Attribute attribute, boolean followLinks) throws IOException { @@ -703,8 +642,6 @@ public class NativeSshFile implements SshFile { } public String readSymbolicLink() throws IOException { - Path path = file.toPath(); - Path link = Files.readSymbolicLink(path); - return link.toString(); + throw new UnsupportedOperationException(); } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d7c21f79/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java new file mode 100644 index 0000000..765410c --- /dev/null +++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFileNio.java @@ -0,0 +1,149 @@ +/* + * 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.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.GroupPrincipal; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * <strong>Internal class, do not use directly.</strong> + * + * This class wraps native file object. + * + * @author <a href="http://mina.apache.org">Apache MINA Project</a> + */ +public class NativeSshFileNio extends NativeSshFile { + + /** + * Constructor, internal do not use directly. + * @param nativeFileSystemView + */ + public NativeSshFileNio(NativeFileSystemView nativeFileSystemView, String fileName, File file, String userName) { + super(nativeFileSystemView, fileName, file, userName); + } + + public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException { + Map<String, Object> a = Files.readAttributes( + file.toPath(), + "unix:size,uid,owner,gid,group,isDirectory,isRegularFile,isSymbolicLink,permissions,creationTime,lastModifiedTime,lastAccessTime", + followLinks ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS }); + Map<Attribute, Object> map = new HashMap<Attribute, Object>(); + map.put(Attribute.Size, a.get("size")); + map.put(Attribute.Uid, a.get("uid")); + map.put(Attribute.Owner, ((UserPrincipal) a.get("owner")).getName()); + map.put(Attribute.Gid, a.get("gid")); + map.put(Attribute.Group, ((GroupPrincipal) a.get("group")).getName()); + map.put(Attribute.IsDirectory, a.get("isDirectory")); + map.put(Attribute.IsRegularFile, a.get("isRegularFile")); + map.put(Attribute.IsSymbolicLink, a.get("isSymbolicLink")); + map.put(Attribute.CreationTime, ((FileTime) a.get("creationTime")).toMillis()); + map.put(Attribute.LastModifiedTime, ((FileTime) a.get("lastModifiedTime")).toMillis()); + map.put(Attribute.LastAccessTime, ((FileTime) a.get("lastAccessTime")).toMillis()); + map.put(Attribute.Permissions, fromPerms((Set<PosixFilePermission>) a.get("permissions"))); + return map; + } + + public void setAttributes(Map<Attribute, Object> attributes) throws IOException { + for (Attribute attribute : attributes.keySet()) { + String name = null; + Object value = attributes.get(attribute); + switch (attribute) { + case Uid: name = "unix:uid"; break; + case Owner: name = "unix:owner"; value = toUser((String) value); break; + case Gid: name = "unix:gid"; break; + case Group: name = "unix:group"; value = toGroup((String) value); break; + case CreationTime: name = "unix:creationTime"; value = FileTime.fromMillis((Long) value); break; + case LastModifiedTime: name = "unix:lastModifiedTime"; value = FileTime.fromMillis((Long) value); break; + case LastAccessTime: name = "unix:lastAccessTime"; value = FileTime.fromMillis((Long) value); break; + case Permissions: name = "unix:permissions"; value = toPerms((EnumSet<Permission>) value); break; + case Size: throw new UnsupportedOperationException("Can not set Size attribute"); + } + if (name != null && value != null) { + Files.setAttribute(file.toPath(), name, value, LinkOption.NOFOLLOW_LINKS); + } + } + } + + public String readSymbolicLink() throws IOException { + Path path = file.toPath(); + Path link = Files.readSymbolicLink(path); + return link.toString(); + } + + private EnumSet<Permission> fromPerms(Set<PosixFilePermission> perms) { + EnumSet<Permission> p = EnumSet.noneOf(Permission.class); + for (PosixFilePermission perm : perms) { + switch (perm) { + case OWNER_READ: p.add(Permission.UserRead); break; + case OWNER_WRITE: p.add(Permission.UserWrite); break; + case OWNER_EXECUTE: p.add(Permission.UserExecute); break; + case GROUP_READ: p.add(Permission.GroupRead); break; + case GROUP_WRITE: p.add(Permission.GroupWrite); break; + case GROUP_EXECUTE: p.add(Permission.GroupExecute); break; + case OTHERS_READ: p.add(Permission.OthersRead); break; + case OTHERS_WRITE: p.add(Permission.OthersWrite); break; + case OTHERS_EXECUTE: p.add(Permission.OthersExecute); break; + } + } + return p; + } + + private GroupPrincipal toGroup(String name) throws IOException { + UserPrincipalLookupService lookupService = file.toPath().getFileSystem().getUserPrincipalLookupService(); + return lookupService.lookupPrincipalByGroupName(name); + } + + private UserPrincipal toUser(String name) throws IOException { + UserPrincipalLookupService lookupService = file.toPath().getFileSystem().getUserPrincipalLookupService(); + return lookupService.lookupPrincipalByName(name); + } + + private Set<PosixFilePermission> toPerms(EnumSet<Permission> perms) { + Set<PosixFilePermission> set = new HashSet<PosixFilePermission>(); + for (Permission p : perms) { + switch (p) { + case UserRead: set.add(PosixFilePermission.OWNER_READ); break; + case UserWrite: set.add(PosixFilePermission.OWNER_WRITE); break; + case UserExecute: set.add(PosixFilePermission.OWNER_EXECUTE); break; + case GroupRead: set.add(PosixFilePermission.GROUP_READ); break; + case GroupWrite: set.add(PosixFilePermission.GROUP_WRITE); break; + case GroupExecute: set.add(PosixFilePermission.GROUP_EXECUTE); break; + case OthersRead: set.add(PosixFilePermission.OTHERS_READ); break; + case OthersWrite: set.add(PosixFilePermission.OTHERS_WRITE); break; + case OthersExecute: set.add(PosixFilePermission.OTHERS_EXECUTE); break; + } + } + return set; + } + +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d7c21f79/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java index c2c5b92..dbbae10 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java @@ -542,6 +542,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage()); } catch (IOException e) { sendStatus(id, SSH_FX_FAILURE, e.getMessage()); + } catch (UnsupportedOperationException e) { + sendStatus(id, SSH_FX_FAILURE, ""); } break; } @@ -561,6 +563,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage()); } catch (IOException e) { sendStatus(id, SSH_FX_FAILURE, e.getMessage()); + } catch (UnsupportedOperationException e) { + sendStatus(id, SSH_FX_FAILURE, e.getMessage()); } break; } @@ -754,6 +758,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste SshFile f = resolveFile(path); String l = f.readSymbolicLink(); sendLink(id, l); + } catch (UnsupportedOperationException e) { + sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command " + type + " is unsupported or not implemented"); } catch (IOException e) { sendStatus(id, SSH_FX_FAILURE, e.getMessage()); } @@ -919,30 +925,43 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste throw new FileNotFoundException(file.getAbsolutePath()); } Map<SshFile.Attribute, Object> attributes = file.getAttributes(followLinks); - boolean isReg = (Boolean) attributes.get(SshFile.Attribute.IsRegularFile); - boolean isDir = (Boolean) attributes.get(SshFile.Attribute.IsDirectory); - boolean isLnk = (Boolean) attributes.get(SshFile.Attribute.IsSymbolicLink); - int pf = getPermissions(attributes); - if (isReg || isLnk) { - buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); + boolean isReg = getBool((Boolean) attributes.get(SshFile.Attribute.IsRegularFile)); + boolean isDir = getBool((Boolean) attributes.get(SshFile.Attribute.IsDirectory)); + boolean isLnk = getBool((Boolean) attributes.get(SshFile.Attribute.IsSymbolicLink)); + int flags = 0; + if ((isReg || isLnk) && attributes.containsKey(SshFile.Attribute.Size)) { + flags |= SSH_FILEXFER_ATTR_SIZE; + } + if (attributes.containsKey(SshFile.Attribute.Uid) && attributes.containsKey(SshFile.Attribute.Gid)) { + flags |= SSH_FILEXFER_ATTR_UIDGID; + } + if (attributes.containsKey(SshFile.Attribute.Permissions)) { + flags |= SSH_FILEXFER_ATTR_PERMISSIONS; + } + if (attributes.containsKey(SshFile.Attribute.LastAccessTime) && attributes.containsKey(SshFile.Attribute.LastModifiedTime)) { + flags |= SSH_FILEXFER_ATTR_ACMODTIME; + } + buffer.putInt(flags); + if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) { buffer.putLong((Long) attributes.get(SshFile.Attribute.Size)); + } + if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) { buffer.putInt((Integer) attributes.get(SshFile.Attribute.Uid)); buffer.putInt((Integer) attributes.get(SshFile.Attribute.Gid)); - buffer.putInt(pf); - buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastAccessTime)) / 1000); - buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastModifiedTime)) / 1000); - } else if (isDir) { - buffer.putInt(SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); - buffer.putInt((Integer) attributes.get(SshFile.Attribute.Uid)); - buffer.putInt((Integer) attributes.get(SshFile.Attribute.Gid)); - buffer.putInt(pf); + } + if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) { + buffer.putInt(getPermissions(attributes)); + } + if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) { buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastAccessTime)) / 1000); buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastModifiedTime)) / 1000); - } else { - buffer.putInt(0); } } + protected boolean getBool(Boolean bool) { + return bool != null && bool; + } + protected Map<SshFile.Attribute, Object> readAttrs(Buffer buffer) throws IOException { Map<SshFile.Attribute, Object> attrs = new HashMap<SshFile.Attribute, Object>(); int flags = buffer.getInt();
