mina-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lgoldst...@apache.org
Subject [3/3] mina-sshd git commit: [SSDH-573] Canonicalize the permissions field of SFTP v3 into a matching type of v4+
Date Mon, 02 Nov 2015 05:25:53 GMT
[SSDH-573] Canonicalize the permissions field of SFTP v3 into a matching type of v4+


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e2187987
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e2187987
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e2187987

Branch: refs/heads/master
Commit: e218798715f9b59fb085c8c86c388a37eecc714b
Parents: beffd2a
Author: Lyor Goldstein <lgoldstein@vmware.com>
Authored: Mon Nov 2 07:25:36 2015 +0200
Committer: Lyor Goldstein <lgoldstein@vmware.com>
Committed: Mon Nov 2 07:25:36 2015 +0200

----------------------------------------------------------------------
 .../subsystem/sftp/AbstractSftpClient.java      | 312 +++++++------------
 .../sshd/client/subsystem/sftp/SftpClient.java  |  89 ++++--
 .../sshd/client/subsystem/sftp/SftpCommand.java |   5 +-
 .../client/subsystem/sftp/SftpPathIterator.java |   4 +-
 .../sshd/common/subsystem/sftp/SftpHelper.java  |  53 ++++
 .../client/simple/SimpleSftpClientTest.java     |   2 +-
 .../sshd/client/subsystem/sftp/SftpTest.java    |   4 +-
 .../client/subsystem/sftp/SftpVersionsTest.java |  58 ++++
 8 files changed, 304 insertions(+), 223 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
index bb72962..8e59971 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
@@ -43,71 +43,6 @@ import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.ACE4_APPEND_DATA;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.ACE4_READ_ATTRIBUTES;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.ACE4_READ_DATA;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.ACE4_WRITE_ATTRIBUTES;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.ACE4_WRITE_DATA;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SFTP_V3;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SFTP_V4;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SFTP_V5;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SFTP_V6;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_ALL;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_CREATETIME;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_SIZE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_UIDGID;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_TYPE_REGULAR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_TYPE_SYMLINK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_APPEND;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_CREAT;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_CREATE_NEW;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_CREATE_TRUNCATE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_EXCL;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_OPEN_EXISTING;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_OPEN_OR_CREATE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_READ;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_TRUNC;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_TRUNCATE_EXISTING;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXF_WRITE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_ATTRS;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_BLOCK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_CLOSE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_DATA;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_FSETSTAT;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_FSTAT;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_HANDLE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_LINK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_LSTAT;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_MKDIR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_NAME;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_OPEN;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_OPENDIR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_READ;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_READDIR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_READLINK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_REALPATH;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_REMOVE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_RENAME;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_RENAME_ATOMIC;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_RENAME_OVERWRITE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_RMDIR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_SETSTAT;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_STAT;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_STATUS;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_SYMLINK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_UNBLOCK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FXP_WRITE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FX_EOF;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FX_OK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IFDIR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IFLNK;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IFREG;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -279,7 +214,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_STATUS) {
+        if (type == SftpConstants.SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
@@ -302,7 +237,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             log.trace("checkStatus(id=" + id + ") status: " + substatus + " [" + lang + "]" + msg);
         }
 
-        if (substatus != SSH_FX_OK) {
+        if (substatus != SftpConstants.SSH_FX_OK) {
             throwStatusException(id, substatus, msg, lang);
         }
     }
@@ -330,11 +265,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_HANDLE) {
+        if (type == SftpConstants.SSH_FXP_HANDLE) {
             return ValidateUtils.checkNotNullAndNotEmpty(buffer.getBytes(), "Null/empty handle in buffer", GenericUtils.EMPTY_OBJECT_ARRAY);
         }
 
-        if (type == SSH_FXP_STATUS) {
+        if (type == SftpConstants.SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
@@ -366,11 +301,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_ATTRS) {
+        if (type == SftpConstants.SSH_FXP_ATTRS) {
             return readAttributes(buffer);
         }
 
-        if (type == SSH_FXP_STATUS) {
+        if (type == SftpConstants.SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
@@ -402,7 +337,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_NAME) {
+        if (type == SftpConstants.SSH_FXP_NAME) {
             int len = buffer.getInt();
             if (len != 1) {
                 throw new SshException("SFTP error: received " + len + " names instead of 1");
@@ -410,7 +345,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             String name = buffer.getString();
             String longName = null;
             int version = getVersion();
-            if (version == SFTP_V3) {
+            if (version == SftpConstants.SFTP_V3) {
                 longName = buffer.getString();
             }
             Attributes attrs = readAttributes(buffer);
@@ -420,7 +355,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             return name;
         }
 
-        if (type == SSH_FXP_STATUS) {
+        if (type == SftpConstants.SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
@@ -438,56 +373,48 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         Attributes attrs = new Attributes();
         int flags = buffer.getInt();
         int version = getVersion();
-        if (version == SFTP_V3) {
-            if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+        if (version == SftpConstants.SFTP_V3) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
                 attrs.setSize(buffer.getLong());
             }
-            if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UIDGID) != 0) {
                 attrs.owner(buffer.getInt(), buffer.getInt());
             }
-            if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
-                attrs.setPermissions(buffer.getInt());
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+                int perms = buffer.getInt();
+                attrs.setPermissions(perms);
+                attrs.setType(SftpHelper.permissionsToFileType(perms));
             }
-            if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
                 attrs.setAccessTime(readTime(buffer, flags));
                 attrs.setModifyTime(readTime(buffer, flags));
             }
-        } else if (version >= SFTP_V4) {
-            attrs.type = buffer.getUByte();
-            if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+        } else if (version >= SftpConstants.SFTP_V4) {
+            attrs.setType(buffer.getUByte());
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
                 attrs.setSize(buffer.getLong());
             }
-            if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
                 attrs.setOwner(buffer.getString());
                 attrs.setGroup(buffer.getString());
             }
-            if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
                 attrs.setPermissions(buffer.getInt());
             }
 
             // update the permissions according to the type
             int perms = attrs.getPermissions();
-            switch (attrs.type) {
-                case SSH_FILEXFER_TYPE_REGULAR:
-                    perms |= S_IFREG;
-                    break;
-                case SSH_FILEXFER_TYPE_DIRECTORY:
-                    perms |= S_IFDIR;
-                    break;
-                case SSH_FILEXFER_TYPE_SYMLINK:
-                    perms |= S_IFLNK;
-                    break;
-                default:    // do nothing
-            }
+            perms |= SftpHelper.fileTypeToPermission(attrs.getType());
             attrs.setPermissions(perms);
 
-            if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
                 attrs.setAccessTime(readTime(buffer, flags));
             }
-            if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
                 attrs.setCreateTime(readTime(buffer, flags));
             }
-            if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
                 attrs.setModifyTime(readTime(buffer, flags));
             }
             // TODO: acl
@@ -503,96 +430,97 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
     protected void writeAttributes(Buffer buffer, Attributes attributes) throws IOException {
         int version = getVersion();
-        if (version == SFTP_V3) {
-            int flags = 0;
-            for (Attribute a : attributes.flags) {
+        Collection<Attribute> flags = ValidateUtils.checkNotNull(attributes, "No attributes").getFlags();
+        if (version == SftpConstants.SFTP_V3) {
+            int flagsMask = 0;
+            for (Attribute a : flags) {
                 switch (a) {
                     case Size:
-                        flags |= SSH_FILEXFER_ATTR_SIZE;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_SIZE;
                         break;
                     case UidGid:
-                        flags |= SSH_FILEXFER_ATTR_UIDGID;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_UIDGID;
                         break;
                     case Perms:
-                        flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS;
                         break;
                     case AccessTime:
-                        if (attributes.flags.contains(Attribute.ModifyTime)) {
-                            flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+                        if (flags.contains(Attribute.ModifyTime)) {
+                            flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME;
                         }
                         break;
                     case ModifyTime:
-                        if (attributes.flags.contains(Attribute.AccessTime)) {
-                            flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+                        if (flags.contains(Attribute.AccessTime)) {
+                            flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME;
                         }
                         break;
                     default:    // do nothing
                 }
             }
-            buffer.putInt(flags);
-            if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+            buffer.putInt(flagsMask);
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
                 buffer.putLong(attributes.getSize());
             }
-            if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_UIDGID) != 0) {
                 buffer.putInt(attributes.getUserId());
                 buffer.putInt(attributes.getGroupId());
             }
-            if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
                 buffer.putInt(attributes.getPermissions());
             }
 
-            if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
-                SftpHelper.writeTime(buffer, version, flags, attributes.getAccessTime());
-                SftpHelper.writeTime(buffer, version, flags, attributes.getModifyTime());
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+                SftpHelper.writeTime(buffer, version, flagsMask, attributes.getAccessTime());
+                SftpHelper.writeTime(buffer, version, flagsMask, attributes.getModifyTime());
             }
-        } else if (version >= SFTP_V4) {
-            int flags = 0;
-            for (Attribute a : attributes.flags) {
+        } else if (version >= SftpConstants.SFTP_V4) {
+            int flagsMask = 0;
+            for (Attribute a : flags) {
                 switch (a) {
                     case Size:
-                        flags |= SSH_FILEXFER_ATTR_SIZE;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_SIZE;
                         break;
                     case OwnerGroup:
-                        flags |= SSH_FILEXFER_ATTR_OWNERGROUP;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP;
                         break;
                     case Perms:
-                        flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS;
                         break;
                     case AccessTime:
-                        flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME;
                         break;
                     case ModifyTime:
-                        flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME;
                         break;
                     case CreateTime:
-                        flags |= SSH_FILEXFER_ATTR_CREATETIME;
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_CREATETIME;
                         break;
                     default:    // do nothing
                 }
             }
-            buffer.putInt(flags);
-            buffer.putByte((byte) attributes.type);
-            if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+            buffer.putInt(flagsMask);
+            buffer.putByte((byte) attributes.getType());
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
                 buffer.putLong(attributes.getSize());
             }
-            if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
                 String owner = attributes.getOwner();
                 buffer.putString(GenericUtils.isEmpty(owner) ? "OWNER@" : owner);
 
                 String group = attributes.getGroup();
                 buffer.putString(GenericUtils.isEmpty(group) ? "GROUP@" : group);
             }
-            if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
                 buffer.putInt(attributes.getPermissions());
             }
-            if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
-                SftpHelper.writeTime(buffer, version, flags, attributes.getAccessTime());
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+                SftpHelper.writeTime(buffer, version, flagsMask, attributes.getAccessTime());
             }
-            if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
-                SftpHelper.writeTime(buffer, version, flags, attributes.getCreateTime());
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+                SftpHelper.writeTime(buffer, version, flagsMask, attributes.getCreateTime());
             }
-            if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
-                SftpHelper.writeTime(buffer, version, flags, attributes.getModifyTime());
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+                SftpHelper.writeTime(buffer, version, flagsMask, attributes.getModifyTime());
             }
             // TODO: for v6+ add CTIME (see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-21)
             // TODO: acl
@@ -618,61 +546,61 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putString(path);
         int version = getVersion();
         int mode = 0;
-        if (version == SFTP_V3) {
+        if (version == SftpConstants.SFTP_V3) {
             for (OpenMode m : options) {
                 switch (m) {
                     case Read:
-                        mode |= SSH_FXF_READ;
+                        mode |= SftpConstants.SSH_FXF_READ;
                         break;
                     case Write:
-                        mode |= SSH_FXF_WRITE;
+                        mode |= SftpConstants.SSH_FXF_WRITE;
                         break;
                     case Append:
-                        mode |= SSH_FXF_APPEND;
+                        mode |= SftpConstants.SSH_FXF_APPEND;
                         break;
                     case Create:
-                        mode |= SSH_FXF_CREAT;
+                        mode |= SftpConstants.SSH_FXF_CREAT;
                         break;
                     case Truncate:
-                        mode |= SSH_FXF_TRUNC;
+                        mode |= SftpConstants.SSH_FXF_TRUNC;
                         break;
                     case Exclusive:
-                        mode |= SSH_FXF_EXCL;
+                        mode |= SftpConstants.SSH_FXF_EXCL;
                         break;
                     default:    // do nothing
                 }
             }
         } else {
-            if (version >= SFTP_V5) {
+            if (version >= SftpConstants.SFTP_V5) {
                 int access = 0;
                 if (options.contains(OpenMode.Read)) {
-                    access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
+                    access |= SftpConstants.ACE4_READ_DATA | SftpConstants.ACE4_READ_ATTRIBUTES;
                 }
                 if (options.contains(OpenMode.Write)) {
-                    access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
+                    access |= SftpConstants.ACE4_WRITE_DATA | SftpConstants.ACE4_WRITE_ATTRIBUTES;
                 }
                 if (options.contains(OpenMode.Append)) {
-                    access |= ACE4_APPEND_DATA;
+                    access |= SftpConstants.ACE4_APPEND_DATA;
                 }
                 buffer.putInt(access);
             }
 
             if (options.contains(OpenMode.Create) && options.contains(OpenMode.Exclusive)) {
-                mode |= SSH_FXF_CREATE_NEW;
+                mode |= SftpConstants.SSH_FXF_CREATE_NEW;
             } else if (options.contains(OpenMode.Create) && options.contains(OpenMode.Truncate)) {
-                mode |= SSH_FXF_CREATE_TRUNCATE;
+                mode |= SftpConstants.SSH_FXF_CREATE_TRUNCATE;
             } else if (options.contains(OpenMode.Create)) {
-                mode |= SSH_FXF_OPEN_OR_CREATE;
+                mode |= SftpConstants.SSH_FXF_OPEN_OR_CREATE;
             } else if (options.contains(OpenMode.Truncate)) {
-                mode |= SSH_FXF_TRUNCATE_EXISTING;
+                mode |= SftpConstants.SSH_FXF_TRUNCATE_EXISTING;
             } else {
-                mode |= SSH_FXF_OPEN_EXISTING;
+                mode |= SftpConstants.SSH_FXF_OPEN_EXISTING;
             }
         }
         buffer.putInt(mode);
         writeAttributes(buffer, new Attributes());
 
-        CloseableHandle handle = new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPEN, buffer));
+        CloseableHandle handle = new DefaultCloseableHandle(this, checkHandle(SftpConstants.SSH_FXP_OPEN, buffer));
         if (log.isTraceEnabled()) {
             log.trace("open({})[{}] options={}: {}", getClientSession(), path, options, handle);
         }
@@ -692,7 +620,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         byte[] id = ValidateUtils.checkNotNull(handle, "No handle").getIdentifier();
         Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* some extra fields */);
         buffer.putBytes(id);
-        checkStatus(SSH_FXP_CLOSE, buffer);
+        checkStatus(SftpConstants.SSH_FXP_CLOSE, buffer);
     }
 
     @Override
@@ -707,7 +635,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
         buffer.putString(path);
-        checkStatus(SSH_FXP_REMOVE, buffer);
+        checkStatus(SftpConstants.SSH_FXP_REMOVE, buffer);
     }
 
     @Override
@@ -726,16 +654,16 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         int numOptions = GenericUtils.size(options);
         int version = getVersion();
-        if (version >= SFTP_V5) {
+        if (version >= SftpConstants.SFTP_V5) {
             int opts = 0;
             if (numOptions > 0) {
                 for (CopyMode opt : options) {
                     switch (opt) {
                         case Atomic:
-                            opts |= SSH_FXP_RENAME_ATOMIC;
+                            opts |= SftpConstants.SSH_FXP_RENAME_ATOMIC;
                             break;
                         case Overwrite:
-                            opts |= SSH_FXP_RENAME_OVERWRITE;
+                            opts |= SftpConstants.SSH_FXP_RENAME_OVERWRITE;
                             break;
                         default:    // do nothing
                     }
@@ -746,7 +674,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             throw new UnsupportedOperationException("rename(" + oldPath + " => " + newPath + ")"
                     + " - copy options can not be used with this SFTP version: " + options);
         }
-        checkStatus(SSH_FXP_RENAME, buffer);
+        checkStatus(SftpConstants.SSH_FXP_RENAME, buffer);
     }
 
     @Override
@@ -760,7 +688,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putBytes(id);
         buffer.putLong(fileOffset);
         buffer.putInt(len);
-        return checkData(SSH_FXP_READ, buffer, dstOffset, dst);
+        return checkData(SftpConstants.SSH_FXP_READ, buffer, dstOffset, dst);
     }
 
     protected int checkData(int cmd, Buffer request, int dstOffset, byte[] dst) throws IOException {
@@ -773,13 +701,13 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_DATA) {
+        if (type == SftpConstants.SSH_FXP_DATA) {
             int len = buffer.getInt();
             buffer.getRawBytes(dst, dstoff, len);
             return len;
         }
 
-        if (type == SSH_FXP_STATUS) {
+        if (type == SftpConstants.SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
@@ -787,7 +715,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
                 log.trace("checkData(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
             }
 
-            if (substatus == SSH_FX_EOF) {
+            if (substatus == SftpConstants.SSH_FX_EOF) {
                 return -1;
             }
 
@@ -819,7 +747,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putBytes(id);
         buffer.putLong(fileOffset);
         buffer.putBytes(src, srcOffset, len);
-        checkStatus(SSH_FXP_WRITE, buffer);
+        checkStatus(SftpConstants.SSH_FXP_WRITE, buffer);
     }
 
     @Override
@@ -837,11 +765,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putInt(0);
 
         int version = getVersion();
-        if (version != SFTP_V3) {
+        if (version != SftpConstants.SFTP_V3) {
             buffer.putByte((byte) 0);
         }
 
-        checkStatus(SSH_FXP_MKDIR, buffer);
+        checkStatus(SftpConstants.SSH_FXP_MKDIR, buffer);
     }
 
     @Override
@@ -856,7 +784,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
         buffer.putString(path);
-        checkStatus(SSH_FXP_RMDIR, buffer);
+        checkStatus(SftpConstants.SSH_FXP_RMDIR, buffer);
     }
 
     @Override
@@ -868,7 +796,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
         buffer.putString(path);
 
-        CloseableHandle handle = new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPENDIR, buffer));
+        CloseableHandle handle = new DefaultCloseableHandle(this, checkHandle(SftpConstants.SSH_FXP_OPENDIR, buffer));
         if (log.isTraceEnabled()) {
             log.trace("openDir({})[{}}: {}", getClientSession(), path, handle);
         }
@@ -885,20 +813,20 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         byte[] id = handle.getIdentifier();
         Buffer buffer = new ByteArrayBuffer(id.length + Byte.SIZE /* some extra fields */);
         buffer.putBytes(id);
-        return checkDir(receive(send(SSH_FXP_READDIR, buffer)));
+        return checkDir(receive(send(SftpConstants.SSH_FXP_READDIR, buffer)));
     }
 
     protected List<DirEntry> checkDir(Buffer buffer) throws IOException {
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_NAME) {
+        if (type == SftpConstants.SSH_FXP_NAME) {
             int len = buffer.getInt();
             List<DirEntry> entries = new ArrayList<DirEntry>(len);
             for (int i = 0; i < len; i++) {
                 String name = buffer.getString();
                 int version = getVersion();
-                String longName = (version == SFTP_V3) ? buffer.getString() : null;
+                String longName = (version == SftpConstants.SFTP_V3) ? buffer.getString() : null;
                 Attributes attrs = readAttributes(buffer);
                 if (log.isTraceEnabled()) {
                     log.trace("checkDir(id={})[{}] ({})[{}]: {}", Integer.valueOf(id), Integer.valueOf(i), name, longName, attrs);
@@ -909,7 +837,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             return entries;
         }
 
-        if (type == SSH_FXP_STATUS) {
+        if (type == SftpConstants.SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
@@ -917,7 +845,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
                 log.trace("checkDir(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
             }
 
-            if (substatus == SSH_FX_EOF) {
+            if (substatus == SftpConstants.SSH_FX_EOF) {
                 return null;
             }
 
@@ -935,7 +863,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer();
         buffer.putString(path);
-        return checkOneName(SSH_FXP_REALPATH, buffer);
+        return checkOneName(SftpConstants.SSH_FXP_REALPATH, buffer);
     }
 
     @Override
@@ -948,11 +876,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putString(path);
 
         int version = getVersion();
-        if (version >= SFTP_V4) {
-            buffer.putInt(SSH_FILEXFER_ATTR_ALL);
+        if (version >= SftpConstants.SFTP_V4) {
+            buffer.putInt(SftpConstants.SSH_FILEXFER_ATTR_ALL);
         }
 
-        return checkAttributes(SSH_FXP_STAT, buffer);
+        return checkAttributes(SftpConstants.SSH_FXP_STAT, buffer);
     }
 
     @Override
@@ -965,11 +893,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putString(path);
 
         int version = getVersion();
-        if (version >= SFTP_V4) {
-            buffer.putInt(SSH_FILEXFER_ATTR_ALL);
+        if (version >= SftpConstants.SFTP_V4) {
+            buffer.putInt(SftpConstants.SSH_FILEXFER_ATTR_ALL);
         }
 
-        return checkAttributes(SSH_FXP_LSTAT, buffer);
+        return checkAttributes(SftpConstants.SSH_FXP_LSTAT, buffer);
     }
 
     @Override
@@ -983,11 +911,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putBytes(id);
 
         int version = getVersion();
-        if (version >= SFTP_V4) {
-            buffer.putInt(SSH_FILEXFER_ATTR_ALL);
+        if (version >= SftpConstants.SFTP_V4) {
+            buffer.putInt(SftpConstants.SSH_FILEXFER_ATTR_ALL);
         }
 
-        return checkAttributes(SSH_FXP_FSTAT, buffer);
+        return checkAttributes(SftpConstants.SSH_FXP_FSTAT, buffer);
     }
 
     @Override
@@ -1003,7 +931,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         Buffer buffer = new ByteArrayBuffer();
         buffer.putString(path);
         writeAttributes(buffer, attributes);
-        checkStatus(SSH_FXP_SETSTAT, buffer);
+        checkStatus(SftpConstants.SSH_FXP_SETSTAT, buffer);
     }
 
     @Override
@@ -1019,7 +947,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         Buffer buffer = new ByteArrayBuffer(id.length + (2 * Long.SIZE) /* some extras */);
         buffer.putBytes(id);
         writeAttributes(buffer, attributes);
-        checkStatus(SSH_FXP_FSETSTAT, buffer);
+        checkStatus(SftpConstants.SSH_FXP_FSETSTAT, buffer);
     }
 
     @Override
@@ -1030,7 +958,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
         buffer.putString(path);
-        return checkOneName(SSH_FXP_READLINK, buffer);
+        return checkOneName(SftpConstants.SSH_FXP_READLINK, buffer);
     }
 
     @Override
@@ -1045,18 +973,18 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(linkPath.length() + targetPath.length() + Long.SIZE /* some extra fields */);
         int version = getVersion();
-        if (version < SFTP_V6) {
+        if (version < SftpConstants.SFTP_V6) {
             if (!symbolic) {
                 throw new UnsupportedOperationException("Hard links are not supported in sftp v" + version);
             }
             buffer.putString(targetPath);
             buffer.putString(linkPath);
-            checkStatus(SSH_FXP_SYMLINK, buffer);
+            checkStatus(SftpConstants.SSH_FXP_SYMLINK, buffer);
         } else {
             buffer.putString(targetPath);
             buffer.putString(linkPath);
             buffer.putBoolean(symbolic);
-            checkStatus(SSH_FXP_LINK, buffer);
+            checkStatus(SftpConstants.SSH_FXP_LINK, buffer);
         }
     }
 
@@ -1077,7 +1005,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putLong(offset);
         buffer.putLong(length);
         buffer.putInt(mask);
-        checkStatus(SSH_FXP_BLOCK, buffer);
+        checkStatus(SftpConstants.SSH_FXP_BLOCK, buffer);
     }
 
     @Override
@@ -1095,7 +1023,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putBytes(id);
         buffer.putLong(offset);
         buffer.putLong(length);
-        checkStatus(SSH_FXP_UNBLOCK, buffer);
+        checkStatus(SftpConstants.SSH_FXP_UNBLOCK, buffer);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
index 652b3d7..6acd599 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
@@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.client.subsystem.SubsystemClient;
 import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension;
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -129,12 +130,9 @@ public interface SftpClient extends SubsystemClient {
     }
     // CHECKSTYLE:ON
 
-    class Attributes {
-        // CHECKSTYLE:OFF
-        public final Set<Attribute> flags = EnumSet.noneOf(Attribute.class);
-        public int type;
-        // CHECKSTYLE:ON
-
+    class Attributes implements Cloneable {
+        private Set<Attribute> flags = EnumSet.noneOf(Attribute.class);
+        private int type = SftpConstants.SSH_FILEXFER_TYPE_UNKNOWN;
         private int perms;
         private int uid;
         private int gid;
@@ -149,19 +147,21 @@ public interface SftpClient extends SubsystemClient {
             super();
         }
 
-        @Override
-        public String toString() {
-            return "type=" + type
-                 + ";size=" + getSize()
-                 + ";uid=" + getUserId()
-                 + ";gid=" + getGroupId()
-                 + ";perms=0x" + Integer.toHexString(getPermissions())
-                 + ";flags=" + flags
-                 + ";owner=" + getOwner()
-                 + ";group=" + getGroup()
-                 + ";aTime=" + getAccessTime()
-                 + ";cTime=" + getCreateTime()
-                 + ";mTime=" + getModifyTime();
+        public Set<Attribute> getFlags() {
+            return flags;
+        }
+
+        public Attributes addFlag(Attribute flag) {
+            flags.add(flag);
+            return this;
+        }
+
+        public int getType() {
+            return type;
+        }
+
+        public void setType(int type) {
+            this.type = type;
         }
 
         public long getSize() {
@@ -322,20 +322,61 @@ public interface SftpClient extends SubsystemClient {
         public boolean isOther() {
             return !isRegularFile() && !isDirectory() && !isSymbolicLink();
         }
+
+        @Override
+        public Attributes clone()  {
+            try {
+                Attributes cloned = getClass().cast(super.clone());
+                cloned.flags = EnumSet.copyOf(getFlags());
+                return cloned;
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException("Failed to clone " + toString() + ": " + e.getMessage(), e);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "type=" + getType()
+                 + ";size=" + getSize()
+                 + ";uid=" + getUserId()
+                 + ";gid=" + getGroupId()
+                 + ";perms=0x" + Integer.toHexString(getPermissions())
+                 + ";flags=" + flags
+                 + ";owner=" + getOwner()
+                 + ";group=" + getGroup()
+                 + ";aTime=" + getAccessTime()
+                 + ";cTime=" + getCreateTime()
+                 + ";mTime=" + getModifyTime();
+        }
     }
 
     class DirEntry {
-        // CHECKSTYLE:OFF
-        public String filename;
-        public String longFilename;
-        public Attributes attributes;
-        // CHECKSTYLE:ON
+        private final String filename;
+        private final String longFilename;
+        private final Attributes attributes;
 
         DirEntry(String filename, String longFilename, Attributes attributes) {
             this.filename = filename;
             this.longFilename = longFilename;
             this.attributes = attributes;
         }
+
+        public String getFilename() {
+            return filename;
+        }
+
+        public String getLongFilename() {
+            return longFilename;
+        }
+
+        public Attributes getAttributes() {
+            return attributes;
+        }
+
+        @Override
+        public String toString() {
+            return getFilename() + "[" + getLongFilename() + "]: " + getAttributes();
+        }
     }
 
     // default values used if none specified

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
index e07e3ed..eaf6f46 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
@@ -329,8 +329,9 @@ public class SftpCommand implements Channel {
             String path = resolveRemotePath(pathArg);
             SftpClient sftp = getClient();
             for (SftpClient.DirEntry entry : sftp.readDir(path)) {
-                SftpClient.Attributes attrs = entry.attributes;
-                appendFileAttributes(stdout.append('\t').append(entry.filename), sftp, path + "/" + entry.filename, attrs).println();
+                String fileName = entry.getFilename();
+                SftpClient.Attributes attrs = entry.getAttributes();
+                appendFileAttributes(stdout.append('\t').append(fileName), sftp, path + "/" + fileName, attrs).println();
             }
 
             return false;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java
index 4a01ca5..49b4b48 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java
@@ -56,13 +56,13 @@ public class SftpPathIterator implements Iterator<Path> {
 
         SftpClient.DirEntry entry = curEntry;
         curEntry = nextEntry();
-        return p.resolve(entry.filename);
+        return p.resolve(entry.getFilename());
     }
 
     private SftpClient.DirEntry nextEntry() {
         while ((it != null) && it.hasNext()) {
             SftpClient.DirEntry entry = it.next();
-            String name = entry.filename;
+            String name = entry.getFilename();
             if (".".equals(name) && (!dotIgnored)) {
                 dotIgnored = true;
             } else if ("..".equals(name) && (!dotdotIgnored)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
index f62274a..80951ef 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
@@ -234,6 +234,59 @@ public final class SftpHelper {
     }
 
     /**
+     * Converts a POSIX permissions mask to a file type value
+     *
+     * @param perms The POSIX permissions mask
+     * @return The file type - see {@code SSH_FILEXFER_TYPE_xxx} values
+     */
+    public static int permissionsToFileType(int perms) {
+        if ((SftpConstants.S_IFLNK & perms) == SftpConstants.S_IFLNK) {
+            return SftpConstants.SSH_FILEXFER_TYPE_SYMLINK;
+        } else if ((SftpConstants.S_IFREG & perms) == SftpConstants.S_IFREG) {
+            return SftpConstants.SSH_FILEXFER_TYPE_REGULAR;
+        } else if ((SftpConstants.S_IFDIR & perms) == SftpConstants.S_IFDIR) {
+            return SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY;
+        } else if ((SftpConstants.S_IFSOCK & perms) == SftpConstants.S_IFSOCK) {
+            return SftpConstants.SSH_FILEXFER_TYPE_SOCKET;
+        } else if ((SftpConstants.S_IFBLK & perms) == SftpConstants.S_IFBLK) {
+            return SftpConstants.SSH_FILEXFER_TYPE_BLOCK_DEVICE;
+        } else if ((SftpConstants.S_IFCHR & perms) == SftpConstants.S_IFCHR) {
+            return SftpConstants.SSH_FILEXFER_TYPE_CHAR_DEVICE;
+        } else if ((SftpConstants.S_IFIFO & perms) == SftpConstants.S_IFIFO) {
+            return SftpConstants.SSH_FILEXFER_TYPE_FIFO;
+        } else {
+            return SftpConstants.SSH_FILEXFER_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Converts a file type into a POSIX permission mask value
+
+     * @param type File type - see {@code SSH_FILEXFER_TYPE_xxx} values
+     * @return The matching POSIX permission mask value
+     */
+    public static int fileTypeToPermission(int type) {
+        switch (type) {
+            case SftpConstants.SSH_FILEXFER_TYPE_REGULAR:
+                return SftpConstants.S_IFREG;
+            case SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY:
+                return SftpConstants.S_IFDIR;
+            case SftpConstants.SSH_FILEXFER_TYPE_SYMLINK:
+                return SftpConstants.S_IFLNK;
+            case SftpConstants.SSH_FILEXFER_TYPE_SOCKET:
+                return SftpConstants.S_IFSOCK;
+            case SftpConstants.SSH_FILEXFER_TYPE_BLOCK_DEVICE:
+                return SftpConstants.S_IFBLK;
+            case SftpConstants.SSH_FILEXFER_TYPE_CHAR_DEVICE:
+                return SftpConstants.S_IFCHR;
+            case SftpConstants.SSH_FILEXFER_TYPE_FIFO:
+                return SftpConstants.S_IFIFO;
+            default:
+                return 0;
+        }
+    }
+
+    /**
      * Translates a mask of permissions into its enumeration values equivalents
      *
      * @param perms The permissions mask

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java
index 9351a98..6482442 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java
@@ -122,7 +122,7 @@ public class SimpleSftpClientTest extends BaseSimpleClientTestSupport {
                 boolean matchFound = false;
                 for (Iterator<SftpClient.DirEntry> it = dirEntries.iterator(); it.hasNext(); ) {
                     SftpClient.DirEntry entry = it.next();
-                    String name = entry.filename;
+                    String name = entry.getFilename();
                     if (clientFileName.equals(name)) {
                         matchFound = true;
                         break;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
index e2b4f71..feaf9fc 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
@@ -1071,7 +1071,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
             boolean dotFiltered = false, dotdotFiltered = false;
             for (Iterator<SftpClient.DirEntry> it = dirEntries.iterator(); it.hasNext(); ) {
                 SftpClient.DirEntry entry = it.next();
-                String name = entry.filename;
+                String name = entry.getFilename();
                 if (".".equals(name) && (!dotFiltered)) {
                     it.remove();
                     dotFiltered = true;
@@ -1118,7 +1118,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
         boolean dotFiltered = false, dotdotFiltered = false;
         for (SftpClient.DirEntry entry : sftp.readDir(dir)) {
             assertNotNull("Unexpected null entry", entry);
-            String name = entry.filename;
+            String name = entry.getFilename();
             if (".".equals(name) && (!dotFiltered)) {
                 dotFiltered = true;
             } else if ("..".equals(name) && (!dotdotFiltered)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e2187987/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
index 338ede3..52acf0e 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
@@ -33,7 +33,9 @@ import java.util.concurrent.TimeUnit;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.subsystem.sftp.SftpHelper;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
 import org.apache.sshd.util.test.Utils;
 import org.junit.After;
@@ -100,6 +102,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
             }
         }
     }
+
     @Test   // see SSHD-572
     public void testSftpFileTimesUpdate() throws Exception {
         Path targetPath = detectTargetFolder();
@@ -140,4 +143,59 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
         }
     }
 
+    @Test   // see SSHD-573
+    public void testSftpFileTypeAndPermissionsUpdate() throws Exception {
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path subFolder = Files.createDirectories(lclSftp.resolve("sub-folder"));
+        String subFolderName = subFolder.getFileName().toString();
+        Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + version + ".txt");
+        String lclFileName = lclFile.getFileName().toString();
+        Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
+
+        Path parentPath = targetPath.getParent();
+        String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+        try (SshClient client = setupTestClient()) {
+            client.start();
+
+            try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(5L, TimeUnit.SECONDS);
+
+                try (SftpClient sftp = session.createSftpClient(version)) {
+                    for (DirEntry entry : sftp.readDir(remotePath)) {
+                        String fileName = entry.getFilename();
+                        if (".".equals(fileName) || "..".equals(fileName)) {
+                            continue;
+                        }
+
+                        Attributes attrs = validateSftpFileTypeAndPermissions(fileName, version, entry.getAttributes());
+                        if (subFolderName.equals(fileName)) {
+                            assertEquals("Mismatched sub-folder type", SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY, attrs.getType());
+                            assertTrue("Sub-folder not marked as directory", attrs.isDirectory());
+                        } else if (lclFileName.equals(fileName)) {
+                            assertEquals("Mismatched sub-file type", SftpConstants.SSH_FILEXFER_TYPE_REGULAR, attrs.getType());
+                            assertTrue("Sub-folder not marked as directory", attrs.isRegularFile());
+                        }
+                    }
+                }
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
+    private static Attributes validateSftpFileTypeAndPermissions(String fileName, int version, Attributes attrs) {
+        int actualPerms = attrs.getPermissions();
+        if (version == SftpConstants.SFTP_V3) {
+            int expected = SftpHelper.permissionsToFileType(actualPerms);
+            assertEquals(fileName + ": Mismatched file type", expected, attrs.getType());
+        } else {
+            int expected = SftpHelper.fileTypeToPermission(attrs.getType());
+            assertTrue(fileName + ": Missing permision=0x" + Integer.toHexString(expected) + " in 0x" + Integer.toHexString(actualPerms),
+                       (actualPerms & expected) == expected);
+        }
+
+        return attrs;
+    }
 }


Mime
View raw message