http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/21c1cee9/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 ef555a1..39f80f9 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 @@ -83,161 +83,77 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste public static final String ALL_SFTP_IMPL = "3"; public static final int MAX_PACKET_LENGTH = 1024 * 16; - public static final int SSH_FXP_INIT = 1; - public static final int SSH_FXP_VERSION = 2; - public static final int SSH_FXP_OPEN = 3; - public static final int SSH_FXP_CLOSE = 4; - public static final int SSH_FXP_READ = 5; - public static final int SSH_FXP_WRITE = 6; - public static final int SSH_FXP_LSTAT = 7; - public static final int SSH_FXP_FSTAT = 8; - public static final int SSH_FXP_SETSTAT = 9; - public static final int SSH_FXP_FSETSTAT = 10; - public static final int SSH_FXP_OPENDIR = 11; - public static final int SSH_FXP_READDIR = 12; - public static final int SSH_FXP_REMOVE = 13; - public static final int SSH_FXP_MKDIR = 14; - public static final int SSH_FXP_RMDIR = 15; - public static final int SSH_FXP_REALPATH = 16; - public static final int SSH_FXP_STAT = 17; - public static final int SSH_FXP_RENAME = 18; - public static final int SSH_FXP_READLINK = 19; - public static final int SSH_FXP_LINK = 21; - public static final int SSH_FXP_BLOCK = 22; - public static final int SSH_FXP_UNBLOCK = 23; - - public static final int SSH_FXP_STATUS = 101; - public static final int SSH_FXP_HANDLE = 102; - public static final int SSH_FXP_DATA = 103; - public static final int SSH_FXP_NAME = 104; - public static final int SSH_FXP_ATTRS = 105; - - public static final int SSH_FXP_EXTENDED = 200; + public static final int SSH_FXP_INIT = 1; + public static final int SSH_FXP_VERSION = 2; + public static final int SSH_FXP_OPEN = 3; + public static final int SSH_FXP_CLOSE = 4; + public static final int SSH_FXP_READ = 5; + public static final int SSH_FXP_WRITE = 6; + public static final int SSH_FXP_LSTAT = 7; + public static final int SSH_FXP_FSTAT = 8; + public static final int SSH_FXP_SETSTAT = 9; + public static final int SSH_FXP_FSETSTAT = 10; + public static final int SSH_FXP_OPENDIR = 11; + public static final int SSH_FXP_READDIR = 12; + public static final int SSH_FXP_REMOVE = 13; + public static final int SSH_FXP_MKDIR = 14; + public static final int SSH_FXP_RMDIR = 15; + public static final int SSH_FXP_REALPATH = 16; + public static final int SSH_FXP_STAT = 17; + public static final int SSH_FXP_RENAME = 18; + public static final int SSH_FXP_READLINK = 19; + public static final int SSH_FXP_SYMLINK = 20; + public static final int SSH_FXP_STATUS = 101; + public static final int SSH_FXP_HANDLE = 102; + public static final int SSH_FXP_DATA = 103; + public static final int SSH_FXP_NAME = 104; + public static final int SSH_FXP_ATTRS = 105; + public static final int SSH_FXP_EXTENDED = 200; public static final int SSH_FXP_EXTENDED_REPLY = 201; - public static final int SSH_FX_OK = 0; - public static final int SSH_FX_EOF = 1; - public static final int SSH_FX_NO_SUCH_FILE = 2; + public static final int SSH_FX_OK = 0; + public static final int SSH_FX_EOF = 1; + public static final int SSH_FX_NO_SUCH_FILE = 2; public static final int SSH_FX_PERMISSION_DENIED = 3; - public static final int SSH_FX_FAILURE = 4; - public static final int SSH_FX_BAD_MESSAGE = 5; - public static final int SSH_FX_NO_CONNECTION = 6; - public static final int SSH_FX_CONNECTION_LOST = 7; - public static final int SSH_FX_OP_UNSUPPORTED = 8; - public static final int SSH_FX_INVALID_HANDLE = 9; - public static final int SSH_FX_NO_SUCH_PATH = 10; - public static final int SSH_FX_FILE_ALREADY_EXISTS = 11; - public static final int SSH_FX_WRITE_PROTECT = 12; - public static final int SSH_FX_NO_MEDIA = 13; - public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14; - public static final int SSH_FX_QUOTA_EXCEEDED = 15; - public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16; - public static final int SSH_FX_LOCK_CONFLICT = 17; - public static final int SSH_FX_DIR_NOT_EMPTY = 18; - public static final int SSH_FX_NOT_A_DIRECTORY = 19; - public static final int SSH_FX_INVALID_FILENAME = 20; - public static final int SSH_FX_LINK_LOOP = 21; - public static final int SSH_FX_CANNOT_DELETE = 22; - public static final int SSH_FX_INVALID_PARAMETER = 23; - public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24; - public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25; - public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26; - public static final int SSH_FX_DELETE_PENDING = 27; - public static final int SSH_FX_FILE_CORRUPT = 28; - public static final int SSH_FX_OWNER_INVALID = 29; - public static final int SSH_FX_GROUP_INVALID = 30; - public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31; - - public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001; + public static final int SSH_FX_FAILURE = 4; + public static final int SSH_FX_BAD_MESSAGE = 5; + public static final int SSH_FX_NO_CONNECTION = 6; + public static final int SSH_FX_CONNECTION_LOST = 7; + public static final int SSH_FX_OP_UNSUPPORTED = 8; + + public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001; + public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002; public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004; - public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; //v3 naming convention - public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008; - public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010; - public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020; - public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040; - public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080; - public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100; - public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200; - public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400; - public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800; - public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000; - public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000; - public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000; - public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000; - public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000; - - public static final int SSH_FILEXFER_TYPE_REGULAR = 1; - public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2; - public static final int SSH_FILEXFER_TYPE_SYMLINK = 3; - public static final int SSH_FILEXFER_TYPE_SPECIAL = 4; - public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5; - public static final int SSH_FILEXFER_TYPE_SOCKET = 6; - public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7; - public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8; - public static final int SSH_FILEXFER_TYPE_FIFO = 9; - - - public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007; - public static final int SSH_FXF_CREATE_NEW = 0x00000000; - public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001; - public static final int SSH_FXF_OPEN_EXISTING = 0x00000002; - public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003; - public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004; - public static final int SSH_FXF_APPEND_DATA = 0x00000008; - public static final int SSH_FXF_APPEND_DATA_ATOMIC = 0x00000010; - public static final int SSH_FXF_TEXT_MODE = 0x00000020; - public static final int SSH_FXF_BLOCK_READ = 0x00000040; - public static final int SSH_FXF_BLOCK_WRITE = 0x00000080; - public static final int SSH_FXF_BLOCK_DELETE = 0x00000100; - public static final int SSH_FXF_BLOCK_ADVISORY = 0x00000200; - public static final int SSH_FXF_NOFOLLOW = 0x00000400; - public static final int SSH_FXF_DELETE_ON_CLOSE = 0x00000800; - public static final int SSH_FXF_ACCESS_AUDIT_ALARM_INFO = 0x00001000; - public static final int SSH_FXF_ACCESS_BACKUP = 0x00002000; - public static final int SSH_FXF_BACKUP_STREAM = 0x00004000; - public static final int SSH_FXF_OVERRIDE_OWNER = 0x00008000; - - public static final int SSH_FXF_READ = 0x00000001; - public static final int SSH_FXF_WRITE = 0x00000002; + public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; //v3 naming convention + public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000; + + public static final int SSH_FXF_READ = 0x00000001; + public static final int SSH_FXF_WRITE = 0x00000002; public static final int SSH_FXF_APPEND = 0x00000004; - public static final int SSH_FXF_CREAT = 0x00000008; - public static final int SSH_FXF_TRUNC = 0x00000010; - public static final int SSH_FXF_EXCL = 0x00000020; - public static final int SSH_FXF_TEXT = 0x00000040; - - public static final int SSH_FXP_REALPATH_NO_CHECK = 0x00000001; - public static final int SSH_FXP_REALPATH_STAT_IF = 0x00000002; - public static final int SSH_FXP_REALPATH_STAT_ALWAYS = 0x00000003; - - public static final int ACE4_READ_DATA = 0x00000001; - public static final int ACE4_LIST_DIRECTORY = 0x00000001; - public static final int ACE4_WRITE_DATA = 0x00000002; - public static final int ACE4_ADD_FILE = 0x00000002; - public static final int ACE4_APPEND_DATA = 0x00000004; - public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004; - public static final int ACE4_READ_NAMED_ATTRS = 0x00000008; - public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010; - public static final int ACE4_EXECUTE = 0x00000020; - public static final int ACE4_DELETE_CHILD = 0x00000040; - public static final int ACE4_READ_ATTRIBUTES = 0x00000080; - public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100; - public static final int ACE4_DELETE = 0x00010000; - public static final int ACE4_READ_ACL = 0x00020000; - public static final int ACE4_WRITE_ACL = 0x00040000; - public static final int ACE4_WRITE_OWNER = 0x00080000; - - public static final int S_IRUSR = 0000400; - public static final int S_IWUSR = 0000200; - public static final int S_IXUSR = 0000100; - public static final int S_IRGRP = 0000040; - public static final int S_IWGRP = 0000020; - public static final int S_IXGRP = 0000010; - public static final int S_IROTH = 0000004; - public static final int S_IWOTH = 0000002; - public static final int S_IXOTH = 0000001; - public static final int S_ISUID = 0004000; - public static final int S_ISGID = 0002000; - public static final int S_ISVTX = 0001000; + public static final int SSH_FXF_CREAT = 0x00000008; + public static final int SSH_FXF_TRUNC = 0x00000010; + public static final int SSH_FXF_EXCL = 0x00000020; + + public static final int S_IFMT = 0170000; // bitmask for the file type bitfields + public static final int S_IFSOCK = 0140000; // socket + public static final int S_IFLNK = 0120000; // symbolic link + public static final int S_IFREG = 0100000; // regular file + public static final int S_IFBLK = 0060000; // block device + public static final int S_IFDIR = 0040000; // directory + public static final int S_IFCHR = 0020000; // character device + public static final int S_IFIFO = 0010000; // fifo + public static final int S_ISUID = 0004000; // set UID bit + public static final int S_ISGID = 0002000; // set GID bit + public static final int S_ISVTX = 0001000; // sticky bit + public static final int S_IRUSR = 0000400; + public static final int S_IWUSR = 0000200; + public static final int S_IXUSR = 0000100; + public static final int S_IRGRP = 0000040; + public static final int S_IWGRP = 0000020; + public static final int S_IXGRP = 0000010; + public static final int S_IROTH = 0000004; + public static final int S_IWOTH = 0000002; + public static final int S_IXOTH = 0000001; private ExitCallback callback; @@ -254,91 +170,6 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste private Map<String, Handle> handles = new HashMap<String, Handle>(); - protected static int mapV4ToV3(int code) { - switch (code) { - case SSH_FX_INVALID_HANDLE: - return SSH_FX_FAILURE; - case SSH_FX_NO_SUCH_PATH: - return SSH_FX_NO_SUCH_FILE; - case SSH_FX_FILE_ALREADY_EXISTS: - return SSH_FX_FAILURE; - case SSH_FX_WRITE_PROTECT: - return SSH_FX_PERMISSION_DENIED; - case SSH_FX_NO_MEDIA: - return SSH_FX_FAILURE; - default: - return code; - } - } - - protected static int mapV5ToV4(int code) { - switch (code) { - case SSH_FX_NO_SPACE_ON_FILESYSTEM: - return SSH_FX_FAILURE; - case SSH_FX_QUOTA_EXCEEDED: - return SSH_FX_FAILURE; - case SSH_FX_UNKNOWN_PRINCIPAL: - return SSH_FX_FAILURE; - case SSH_FX_LOCK_CONFLICT: - return SSH_FX_FAILURE; - default: - return code; - } - } - - protected static int mapV6ToV5(int code) { - switch (code) { - case SSH_FX_DIR_NOT_EMPTY: - return SSH_FX_FAILURE; - case SSH_FX_NOT_A_DIRECTORY: - return SSH_FX_NO_SUCH_FILE; - case SSH_FX_INVALID_FILENAME: - return SSH_FX_NO_SUCH_FILE; - case SSH_FX_LINK_LOOP: - return SSH_FX_FAILURE; - case SSH_FX_CANNOT_DELETE: - return SSH_FX_PERMISSION_DENIED; - case SSH_FX_INVALID_PARAMETER: - return SSH_FX_FAILURE; - case SSH_FX_FILE_IS_A_DIRECTORY: - return SSH_FX_NO_SUCH_FILE; - case SSH_FX_BYTE_RANGE_LOCK_CONFLICT: - return SSH_FX_FAILURE; - case SSH_FX_BYTE_RANGE_LOCK_REFUSED: - return SSH_FX_FAILURE; - case SSH_FX_DELETE_PENDING: - return SSH_FX_FAILURE; - case SSH_FX_FILE_CORRUPT: - return SSH_FX_FAILURE; - case SSH_FX_OWNER_INVALID: - return SSH_FX_PERMISSION_DENIED; - case SSH_FX_GROUP_INVALID: - return SSH_FX_PERMISSION_DENIED; - case SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK: - return SSH_FX_FAILURE; - default: - return code; - } - } - - protected static int mapToVersion(int code, int version) { - int mappedCode = code; - if (version < 6) { - mappedCode = mapV6ToV5(mappedCode); - } - if (version < 5) { - mappedCode = mapV5ToV4(mappedCode); - } - if (version < 4) { - mappedCode = mapV4ToV3(mappedCode); - } - return mappedCode; - } - - protected int mapToVersion(int code) { - return mapToVersion(code, version); - } - protected static abstract class Handle { SshFile file; @@ -572,103 +403,35 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste } } - if (version <= 4) { - String path = buffer.getString(); - int pflags = buffer.getInt(); - // attrs - try { - SshFile file = resolveFile(path); - if (file.doesExist()) { - if (((pflags & SSH_FXF_CREAT) != 0) && ((pflags & SSH_FXF_EXCL) != 0)) { - sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, path); + String path = buffer.getString(); + int pflags = buffer.getInt(); + // attrs + try { + SshFile file = resolveFile(path); + if (file.doesExist()) { + if (((pflags & SSH_FXF_CREAT) != 0) && ((pflags & SSH_FXF_EXCL) != 0)) { + sendStatus(id, SSH_FX_FAILURE, path); + return; + } + } else { + if (((pflags & SSH_FXF_CREAT) != 0)) { + if (!file.isWritable()) { + sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path); return; } - } else { - if (((pflags & SSH_FXF_CREAT) != 0)) { - if (!file.isWritable()) { - sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path); - return; - } - file.create(); - } - } - String acc = ((pflags & (SSH_FXF_READ | SSH_FXF_WRITE)) != 0 ? "r" : "") + - ((pflags & SSH_FXF_WRITE) != 0 ? "w" : ""); - if ((pflags & SSH_FXF_TRUNC) != 0) { - file.truncate(); + file.create(); } - String handle = UUID.randomUUID().toString(); - handles.put(handle, new FileHandle(file, pflags)); // handle flags conversion - sendHandle(id, handle); - } catch (IOException e) { - sendStatus(id, SSH_FX_FAILURE, e.getMessage() == null ? "" : e.getMessage()); } - } else { - String path = buffer.getString(); - int acc = buffer.getInt(); - int flags = buffer.getInt(); - // attrs - try { - SshFile file = resolveFile(path); - switch (flags & SSH_FXF_ACCESS_DISPOSITION) { - case SSH_FXF_CREATE_NEW: { - if (file.doesExist()) { - sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, path); - return; - } else if (!file.isWritable()) { - sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path); - } - file.create(); - break; - } - case SSH_FXF_CREATE_TRUNCATE: { - if (file.doesExist()) { - sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, path); - return; - } else if (!file.isWritable()) { - sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path); - } - file.truncate(); - break; - } - case SSH_FXF_OPEN_EXISTING: { - if (!file.doesExist()) { - if (!file.getParentFile().doesExist()) { - sendStatus(id, SSH_FX_NO_SUCH_PATH, path); - } else { - sendStatus(id, SSH_FX_NO_SUCH_FILE, path); - } - return; - } - break; - } - case SSH_FXF_OPEN_OR_CREATE: { - if (!file.doesExist()) { - file.create(); - } - break; - } - case SSH_FXF_TRUNCATE_EXISTING: { - if (!file.doesExist()) { - if (!file.getParentFile().doesExist()) { - sendStatus(id, SSH_FX_NO_SUCH_PATH, path); - } else { - sendStatus(id, SSH_FX_NO_SUCH_FILE, path); - } - return; - } - file.truncate(); - break; - } - default: - throw new IllegalArgumentException("Unsupported open mode: " + flags); - } - String handle = UUID.randomUUID().toString(); - handles.put(handle, new FileHandle(file, flags)); - sendHandle(id, handle); - } catch (IOException e) { - sendStatus(id, SSH_FX_FAILURE, e.getMessage()); + String acc = ((pflags & (SSH_FXF_READ | SSH_FXF_WRITE)) != 0 ? "r" : "") + + ((pflags & SSH_FXF_WRITE) != 0 ? "w" : ""); + if ((pflags & SSH_FXF_TRUNC) != 0) { + file.truncate(); } + String handle = UUID.randomUUID().toString(); + handles.put(handle, new FileHandle(file, pflags)); // handle flags conversion + sendHandle(id, handle); + } catch (IOException e) { + sendStatus(id, SSH_FX_FAILURE, e.getMessage() == null ? "" : e.getMessage()); } break; } @@ -677,7 +440,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste try { Handle h = handles.get(handle); if (h == null) { - sendStatus(id, SSH_FX_INVALID_HANDLE, handle, ""); + sendStatus(id, SSH_FX_FAILURE, handle, ""); } else { handles.remove(handle); h.close(); @@ -695,7 +458,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste try { Handle p = handles.get(handle); if (!(p instanceof FileHandle)) { - sendStatus(id, SSH_FX_INVALID_HANDLE, handle); + sendStatus(id, SSH_FX_FAILURE, handle); } else { FileHandle fh = (FileHandle) p; byte[] b = new byte[Math.min(len, 1024 * 32)]; @@ -705,9 +468,6 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste buf.putByte((byte) SSH_FXP_DATA); buf.putInt(id); buf.putBytes(b, 0, len); - if (version >= 6) { - buf.putBoolean(len == 0); - } send(buf); } else { sendStatus(id, SSH_FX_EOF, ""); @@ -725,7 +485,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste try { Handle p = handles.get(handle); if (!(p instanceof FileHandle)) { - sendStatus(id, SSH_FX_INVALID_HANDLE, handle); + sendStatus(id, SSH_FX_FAILURE, handle); } else { FileHandle fh = (FileHandle) p; fh.write(data, offset); @@ -758,7 +518,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste try { Handle p = handles.get(handle); if (p == null) { - sendStatus(id, SSH_FX_INVALID_HANDLE, handle); + sendStatus(id, SSH_FX_FAILURE, handle); } else { sendAttrs(id, p.getFile()); } @@ -776,7 +536,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste if (!p.doesExist()) { sendStatus(id, SSH_FX_NO_SUCH_FILE, path); } else if (!p.isDirectory()) { - sendStatus(id, SSH_FX_NOT_A_DIRECTORY, path); + sendStatus(id, SSH_FX_NO_SUCH_FILE, path); } else if (!p.isReadable()) { sendStatus(id, SSH_FX_PERMISSION_DENIED, path); } else { @@ -794,13 +554,13 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste try { Handle p = handles.get(handle); if (!(p instanceof DirectoryHandle)) { - sendStatus(id, SSH_FX_INVALID_HANDLE, handle); + sendStatus(id, SSH_FX_FAILURE, handle); } else if (((DirectoryHandle) p).isDone()) { sendStatus(id, SSH_FX_EOF, "", ""); } else if (!p.getFile().doesExist()) { sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().getAbsolutePath()); } else if (!p.getFile().isDirectory()) { - sendStatus(id, SSH_FX_NOT_A_DIRECTORY, p.getFile().getAbsolutePath()); + sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().getAbsolutePath()); } else if (!p.getFile().isReadable()) { sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getFile().getAbsolutePath()); } else { @@ -834,7 +594,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste if (!p.doesExist()) { sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath()); } else if (p.isDirectory()) { - sendStatus(id, SSH_FX_FILE_IS_A_DIRECTORY, p.getAbsolutePath()); + sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath()); } else if (!p.delete()) { sendStatus(id, SSH_FX_FAILURE, "Failed to delete file"); } else { @@ -852,9 +612,9 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste SshFile p = resolveFile(path); if (p.doesExist()) { if (p.isDirectory()) { - sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, p.getAbsolutePath()); + sendStatus(id, SSH_FX_FAILURE, p.getAbsolutePath()); } else { - sendStatus(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath()); + sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath()); } } else if (!p.isWritable()) { sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getAbsolutePath()); @@ -882,13 +642,13 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste sendStatus(id, SSH_FX_FAILURE, "Unable to delete directory " + path); } } else { - sendStatus(id, SSH_FX_DIR_NOT_EMPTY, path); + sendStatus(id, SSH_FX_FAILURE, path); } } else { - sendStatus(id, SSH_FX_NO_SUCH_PATH, path); + sendStatus(id, SSH_FX_NO_SUCH_FILE, path); } } else { - sendStatus(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath()); + sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath()); } } catch (IOException e) { sendStatus(id, SSH_FX_FAILURE, e.getMessage()); @@ -900,20 +660,9 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste if (path.trim().length() == 0) { path = "."; } - byte options = SSH_FXP_REALPATH_NO_CHECK; - List<String> compose = new ArrayList<String>(); - if (version >= 6 && buffer.available() > 0) { - options = buffer.getByte(); - } - while (version >= 6 && buffer.available() > 0) { - compose.add(buffer.getString()); - } try { SshFile p = resolveFile(path); - for (String s : compose) { - p = this.root.getFile(p, s); - } - sendPath(id, p, options); + sendPath(id, p); } catch (FileNotFoundException e) { e.printStackTrace(); sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage()); @@ -932,7 +681,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste if (!o.doesExist()) { sendStatus(id, SSH_FX_NO_SUCH_FILE, o.getAbsolutePath()); } else if (n.doesExist()) { - sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, n.getAbsolutePath()); + sendStatus(id, SSH_FX_FAILURE, n.getAbsolutePath()); } else if (!o.move(n)) { sendStatus(id, SSH_FX_FAILURE, "Failed to rename file"); } else { @@ -976,16 +725,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste send(buffer); } - protected void sendAttrs(int id, SshFile file, int flags) throws IOException { - Buffer buffer = new Buffer(); - buffer.putByte((byte) SSH_FXP_ATTRS); - buffer.putInt(id); - writeAttrs(buffer, file, flags); - send(buffer); - } - - - protected void sendPath(int id, SshFile f, byte options) throws IOException { + protected void sendPath(int id, SshFile f) throws IOException { Buffer buffer = new Buffer(); buffer.putByte((byte) SSH_FXP_NAME); buffer.putInt(id); @@ -1000,19 +740,8 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste if (f.getName().length() == 0) { f = resolveFile("."); } - if (options == SSH_FXP_REALPATH_STAT_IF && f.doesExist() || options == SSH_FXP_REALPATH_STAT_ALWAYS) { - buffer.putString(f.getName()); // Supposed to be UTF-8 - writeAttrs(buffer, f); - } else { - if (version <= 3) { - buffer.putString(getLongName(f)); // Format specified in the specs - buffer.putInt(0); - } else if (version >= 4 || options == SSH_FXP_REALPATH_NO_CHECK) { - buffer.putString(f.getName()); - buffer.putInt(0); - buffer.putByte((byte) 0); - } - } + buffer.putString(getLongName(f)); // Format specified in the specs + buffer.putInt(0); send(buffer); } @@ -1026,11 +755,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste while (files.hasNext() && buffer.wpos() < MAX_PACKET_LENGTH) { SshFile f = files.next(); buffer.putString(f.getName()); - if (version <= 3) { - buffer.putString(getLongName(f)); // Format specified in the specs - } else { - buffer.putString(f.getName()); // Supposed to be UTF-8 - } + buffer.putString(getLongName(f)); // Format specified in the specs writeAttrs(buffer, f); nb++; } @@ -1041,8 +766,9 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste send(buffer); } - private String getLongName(SshFile f) { - String username = f.getOwner(); + private String getLongName(SshFile f) throws IOException { + Map<SshFile.Attribute, Object> attributes = f.getAttributes(); + String username = (String) attributes.get(SshFile.Attribute.Owner); if (username.length() > 8) { username = username.substring(0, 8); } else { @@ -1050,31 +776,43 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste username = username + " "; } } + String group = (String) attributes.get(SshFile.Attribute.Group); + if (group.length() > 8) { + group = group.substring(0, 8); + } else { + for (int i = group.length(); i < 8; i++) { + group = group + " "; + } + } - long length = f.getSize(); + long length = (Long) attributes.get(SshFile.Attribute.Size); String lengthString = String.format("%1$8s", length); + boolean isDirectory = (Boolean) attributes.get(SshFile.Attribute.IsDirectory); + boolean isLink = (Boolean) attributes.get(SshFile.Attribute.IsSymbolicLink); + int perms = (Integer) attributes.get(SshFile.Attribute.Permissions); + StringBuilder sb = new StringBuilder(); - sb.append((f.isDirectory() ? "d" : "-")); - sb.append((f.isReadable() ? "r" : "-")); - sb.append((f.isWritable() ? "w" : "-")); - sb.append((f.isExecutable() ? "x" : "-")); - sb.append((f.isReadable() ? "r" : "-")); - sb.append((f.isWritable() ? "w" : "-")); - sb.append((f.isExecutable() ? "x" : "-")); - sb.append((f.isReadable() ? "r" : "-")); - sb.append((f.isWritable() ? "w" : "-")); - sb.append((f.isExecutable() ? "x" : "-")); + sb.append(isDirectory ? "d" : isLink ? "l" : "-"); + sb.append((perms & 0000400) != 0 ? "r" : "-"); + sb.append((perms & 0000200) != 0 ? "w" : "-"); + sb.append((perms & 0000100) != 0 ? "x" : "-"); + sb.append((perms & 0000040) != 0 ? "r" : "-"); + sb.append((perms & 0000020) != 0 ? "w" : "-"); + sb.append((perms & 0000010) != 0 ? "x" : "-"); + sb.append((perms & 0000004) != 0 ? "r" : "-"); + sb.append((perms & 0000002) != 0 ? "w" : "-"); + sb.append((perms & 0000001) != 0 ? "x" : "-"); sb.append(" "); sb.append(" 1"); sb.append(" "); sb.append(username); sb.append(" "); - sb.append(username); + sb.append(group); sb.append(" "); sb.append(lengthString); sb.append(" "); - sb.append(getUnixDate(f.getLastModified())); + sb.append(getUnixDate((Long) attributes.get(SshFile.Attribute.LastModifiedTime))); sb.append(" "); sb.append(f.getName()); @@ -1082,76 +820,39 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste } protected void writeAttrs(Buffer buffer, SshFile file) throws IOException { - writeAttrs(buffer, file, 0); - } - - - protected void writeAttrs(Buffer buffer, SshFile file, int flags) throws IOException { if (!file.doesExist()) { throw new FileNotFoundException(file.getAbsolutePath()); } - if (version >= 4) { - long size = file.getSize(); - String username = session.getUsername(); - long lastModif = file.getLastModified(); - int p = 0; - if (file.isReadable()) { - p |= S_IRUSR; - } - if (file.isWritable()) { - p |= S_IWUSR; - } - if (file.isExecutable()) { - p |= S_IXUSR; - } - if (file.isFile()) { - buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS); - buffer.putByte((byte) SSH_FILEXFER_TYPE_REGULAR); - buffer.putInt(p); - } else if (file.isDirectory()) { - buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS); - buffer.putByte((byte) SSH_FILEXFER_TYPE_DIRECTORY); - buffer.putInt(p); - } else { - buffer.putInt(0); - buffer.putByte((byte) SSH_FILEXFER_TYPE_UNKNOWN); - } + Map<SshFile.Attribute, Object> attributes = file.getAttributes(); + 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 p = (Integer) attributes.get(SshFile.Attribute.Permissions); + p |= isReg ? S_IFREG : 0; + p |= isDir ? S_IFDIR : 0; + p |= isLnk ? S_IFLNK : 0; + if (isReg) { + buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); + buffer.putLong((Long) attributes.get(SshFile.Attribute.Size)); + buffer.putInt((Integer) attributes.get(SshFile.Attribute.Uid)); + buffer.putInt((Integer) attributes.get(SshFile.Attribute.Gid)); + buffer.putInt(p); + buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastAccessTime)) / 1000); + buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastModifiedTime)) / 1000); + } else if (isDir || isLnk) { + 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(p); + buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastAccessTime)) / 1000); + buffer.putInt(((Long) attributes.get(SshFile.Attribute.LastModifiedTime)) / 1000); } else { - int p = 0; - if (file.isFile()) { - p |= 0100000; - } - if (file.isDirectory()) { - p |= 0040000; - } - if (file.isReadable()) { - p |= 0000400; - } - if (file.isWritable()) { - p |= 0000200; - } - if (file.isExecutable()) { - p |= 0000100; - } - if (file.isFile()) { - buffer.putInt(SSH_FILEXFER_ATTR_SIZE| SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); - buffer.putLong(file.getSize()); - buffer.putInt(p); - buffer.putInt(file.getLastModified()/1000); - buffer.putInt(file.getLastModified()/1000); - } else if (file.isDirectory()) { - buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); - buffer.putInt(p); - buffer.putInt(file.getLastModified()/1000); - buffer.putInt(file.getLastModified()/1000); - } else { - buffer.putInt(0); - } + buffer.putInt(0); } } protected void sendStatus(int id, int substatus, String msg) throws IOException { - sendStatus(id, mapToVersion(substatus), msg, ""); + sendStatus(id, substatus, msg, ""); } protected void sendStatus(int id, int substatus, String msg, String lang) throws IOException {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/21c1cee9/sshd-core/src/test/java/org/apache/sshd/SftpTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java index 7259b61..94af9a5 100644 --- a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java @@ -25,12 +25,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.util.Arrays; +import java.util.EnumSet; import java.util.Vector; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Logger; import com.jcraft.jsch.UserInfo; +import org.apache.sshd.client.SftpClient; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.server.Command; import org.apache.sshd.server.command.ScpCommandFactory; @@ -39,10 +41,13 @@ import org.apache.sshd.util.BogusPasswordAuthenticator; import org.apache.sshd.util.EchoShellFactory; import org.apache.sshd.util.Utils; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -120,6 +125,62 @@ public class SftpTest { } @Test + public void testClient() throws Exception { + SshClient client = SshClient.setUpDefaultClient(); + client.start(); + ClientSession session = client.connect("localhost", port).await().getSession(); + session.authPassword("x", "x").await(); + + new File("target/scp").mkdirs(); + new File("target/scp/client/test.txt").delete(); + new File("target/scp/client").delete(); + + SftpClient sftp = session.createSftpClient(); + + sftp.mkdir("target/scp/client"); + + SftpClient.Handle h = sftp.open("target/scp/client/test.txt", EnumSet.of(SftpClient.OpenMode.Write)); + byte[] d = "0123456789\n".getBytes(); + sftp.write(h, 0, d, 0, d.length); + sftp.write(h, d.length, d, 0, d.length); + + SftpClient.Attributes attrs = sftp.stat(h); + Assert.assertNotNull(attrs); + + sftp.close(h); + + h = sftp.openDir("target/scp/client"); + SftpClient.DirEntry[] dir = sftp.readDir(h); + assertNotNull(dir); + assertEquals(1, dir.length); + assertNull(sftp.readDir(h)); + sftp.close(h); + + sftp.remove("target/scp/client/test.txt"); + + OutputStream os = sftp.write("target/scp/client/test.txt"); + os.write(new byte[1024 * 128]); + os.close(); + + InputStream is = sftp.read("target/scp/client/test.txt"); + is.read(new byte[1024 * 128]); + int i = is.read(); + is.close(); + + int nb = 0; + for (SftpClient.DirEntry entry : sftp.readDir("target/scp/client")) { + nb++; + } + assertEquals(1, nb); + + sftp.remove("target/scp/client/test.txt"); + + sftp.rmdir("target/scp/client/"); + + client.stop(); + } + + @Test public void testSftp() throws Exception { String d = "0123456789\n"; http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/21c1cee9/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java index 8e5cfee..9722480 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/SshFxpStatusReply.java @@ -20,9 +20,10 @@ package org.apache.sshd.sftp.reply; import java.io.IOException; -import org.apache.sshd.server.sftp.SftpSubsystem; import org.apache.sshd.sftp.subsystem.SftpConstants; +import static org.apache.sshd.sftp.subsystem.SftpConstants.*; + /** * Data container for 'SSH_FXP_STATUS' reply. * @@ -67,52 +68,52 @@ public class SshFxpStatusReply extends BaseReply { this.msg = msg; switch (substatus) { - case SftpSubsystem.SSH_FX_FILE_ALREADY_EXISTS: + case SSH_FX_FILE_ALREADY_EXISTS: substatusAsSTR = "SSH_FX_FILE_ALREADY_EXISTS"; break; - case SftpSubsystem.SSH_FX_DIR_NOT_EMPTY: + case SSH_FX_DIR_NOT_EMPTY: substatusAsSTR = "SSH_FX_DIR_NOT_EMPTY"; break; - case SftpSubsystem.SSH_FX_EOF: + case SSH_FX_EOF: substatusAsSTR = "SSH_FX_EOF"; break; - case SftpSubsystem.SSH_FX_FILE_IS_A_DIRECTORY: + case SSH_FX_FILE_IS_A_DIRECTORY: substatusAsSTR = "SSH_FX_FILE_IS_A_DIRECTORY"; break; - case SftpSubsystem.SSH_FX_INVALID_HANDLE: + case SSH_FX_INVALID_HANDLE: substatusAsSTR = "SSH_FX_INVALID_HANDLE"; break; - case SftpSubsystem.SSH_FX_NO_SUCH_FILE: + case SSH_FX_NO_SUCH_FILE: substatusAsSTR = "SSH_FX_NO_SUCH_FILE"; break; - case SftpSubsystem.SSH_FX_NO_SUCH_PATH: + case SSH_FX_NO_SUCH_PATH: substatusAsSTR = "SSH_FX_NO_SUCH_PATH"; break; - case SftpSubsystem.SSH_FX_NOT_A_DIRECTORY: + case SSH_FX_NOT_A_DIRECTORY: substatusAsSTR = "SSH_FX_NOT_A_DIRECTORY"; break; - case SftpSubsystem.SSH_FX_OK: + case SSH_FX_OK: substatusAsSTR = "SSH_FX_OK"; break; - case SftpSubsystem.SSH_FX_OP_UNSUPPORTED: + case SSH_FX_OP_UNSUPPORTED: substatusAsSTR = "SSH_FX_OP_UNSUPPORTED"; break; - case SftpSubsystem.SSH_FX_FAILURE: + case SSH_FX_FAILURE: substatusAsSTR = "SSH_FX_FAILURE"; break; - case SftpSubsystem.SSH_FX_PERMISSION_DENIED: + case SSH_FX_PERMISSION_DENIED: substatusAsSTR = "SSH_FX_PERMISSION_DENIED"; break; - case SftpSubsystem.SSH_FXP_MKDIR: + case SSH_FXP_MKDIR: substatusAsSTR = "SSH_FXP_MKDIR"; break; - case SftpSubsystem.SSH_FXP_REMOVE: + case SSH_FXP_REMOVE: substatusAsSTR = "SSH_FXP_REMOVE"; break; - case SftpSubsystem.SSH_FXP_RMDIR: + case SSH_FXP_RMDIR: substatusAsSTR = "SSH_FXP_RMDIR"; break; - case SftpSubsystem.SSH_FX_WRITE_PROTECT: + case SSH_FX_WRITE_PROTECT: substatusAsSTR = "SSH_FX_WRITE_PROTECT"; break; default: http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/21c1cee9/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java index 9652c3b..a1df9e8 100644 --- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java +++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java @@ -24,7 +24,18 @@ import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.GroupPrincipal; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; import java.util.Arrays; +import java.util.Map; +import java.util.Set; import java.util.Vector; import com.jcraft.jsch.ChannelSftp;
