You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/11/03 07:06:10 UTC

[2/4] mina-sshd git commit: [SSHD-574] Add decoding of SFTP ACL field for version 4+

[SSHD-574] Add decoding of SFTP ACL field for version 4+


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

Branch: refs/heads/master
Commit: 1b5acb71a211bdc0e15f29201be45735fd1fdfd3
Parents: bec90dd
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Nov 3 07:50:54 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Nov 3 07:50:54 2015 +0200

----------------------------------------------------------------------
 pom.xml                                         |   2 +-
 .../subsystem/sftp/AbstractSftpClient.java      |  76 +++-
 .../sftp/AbstractSftpFileAttributeView.java     |  87 ++++
 .../sftp/SftpAclFileAttributeView.java          |  65 +++
 .../sshd/client/subsystem/sftp/SftpClient.java  |  48 +-
 .../client/subsystem/sftp/SftpFileStore.java    |   2 +-
 .../client/subsystem/sftp/SftpFileSystem.java   |  22 +-
 .../subsystem/sftp/SftpFileSystemProvider.java  | 162 ++++++-
 .../sftp/SftpPosixFileAttributeView.java        |  67 +--
 .../subsystem/sftp/SftpPosixFileAttributes.java |   2 +-
 .../common/subsystem/sftp/SftpConstants.java    |   6 +
 .../sshd/common/subsystem/sftp/SftpHelper.java  | 446 ++++++++++++++-----
 .../sftp/SftpUniversalOwnerAndGroup.java        |  67 +++
 .../apache/sshd/common/util/buffer/Buffer.java  |   3 +-
 .../sshd/common/util/buffer/BufferUtils.java    |   3 +
 .../sshd/server/command/UnknownCommand.java     |   2 +
 .../sftp/AbstractSftpEventListenerAdapter.java  | 212 +++++++++
 .../server/subsystem/sftp/SftpSubsystem.java    |  69 ++-
 .../subsystem/sftp/SftpFileSystemTest.java      |  35 +-
 .../sshd/client/subsystem/sftp/SftpTest.java    | 103 +----
 .../client/subsystem/sftp/SftpVersionsTest.java | 170 ++++++-
 .../sftp/SftpUniversalOwnerAndGroupTest.java    |  67 +++
 .../jaas/JaasPasswordAuthenticatorTest.java     |   5 +
 .../apache/sshd/util/test/BaseTestSupport.java  |  15 +
 24 files changed, 1365 insertions(+), 371 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 8e504d7..4821880 100644
--- a/pom.xml
+++ b/pom.xml
@@ -730,7 +730,7 @@
 								</module>
 								<!-- <module name="RegexpHeader" /> -->
 								<module name="FileLength">
-									<property name="max" value="3072" />
+									<property name="max" value="4096" />
 								</module>
 								<module name="FileTabCharacter">
 									<property name="eachLine" value="true" />

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/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 8e59971..c2120d3 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
@@ -36,6 +36,7 @@ import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtensionFacto
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.subsystem.sftp.SftpHelper;
+import org.apache.sshd.common.subsystem.sftp.SftpUniversalOwnerAndGroup;
 import org.apache.sshd.common.subsystem.sftp.extensions.ParserUtils;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -387,14 +388,20 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             }
 
             if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
-                attrs.setAccessTime(readTime(buffer, flags));
-                attrs.setModifyTime(readTime(buffer, flags));
+                attrs.setAccessTime(SftpHelper.readTime(buffer, version, flags));
+                attrs.setModifyTime(SftpHelper.readTime(buffer, version, flags));
             }
         } else if (version >= SftpConstants.SFTP_V4) {
             attrs.setType(buffer.getUByte());
             if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
                 attrs.setSize(buffer.getLong());
             }
+
+            if ((version >= SftpConstants.SFTP_V6) && ((flags & SftpConstants.SSH_FILEXFER_ATTR_ALLOCATION_SIZE) != 0)) {
+                @SuppressWarnings("unused")
+                long allocSize = buffer.getLong();    // TODO handle allocation size
+            }
+
             if ((flags & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
                 attrs.setOwner(buffer.getString());
                 attrs.setGroup(buffer.getString());
@@ -409,30 +416,64 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             attrs.setPermissions(perms);
 
             if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
-                attrs.setAccessTime(readTime(buffer, flags));
+                attrs.setAccessTime(SftpHelper.readTime(buffer, version, flags));
             }
             if ((flags & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
-                attrs.setCreateTime(readTime(buffer, flags));
+                attrs.setCreateTime(SftpHelper.readTime(buffer, version, flags));
             }
             if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
-                attrs.setModifyTime(readTime(buffer, flags));
+                attrs.setModifyTime(SftpHelper.readTime(buffer, version, flags));
+            }
+            if ((version >= SftpConstants.SFTP_V6) && (flags & SftpConstants.SSH_FILEXFER_ATTR_CTIME) != 0) {
+                @SuppressWarnings("unused")
+                FileTime attrsChangedTime = SftpHelper.readTime(buffer, version, flags);    // TODO the last time the file attributes were changed
+            }
+
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACL) != 0) {
+                attrs.setAcl(SftpHelper.readACLs(buffer, version));
+            }
+
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_BITS) != 0) {
+                @SuppressWarnings("unused")
+                int bits = buffer.getInt();
+                @SuppressWarnings("unused")
+                int valid = 0xffffffff;
+                if (version >= SftpConstants.SFTP_V6) {
+                    valid = buffer.getInt();
+                }
+                // TODO: handle attrib bits
+            }
+
+            if (version >= SftpConstants.SFTP_V6) {
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_TEXT_HINT) != 0) {
+                    @SuppressWarnings("unused")
+                    boolean text = buffer.getBoolean(); // TODO: handle text
+                }
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MIME_TYPE) != 0) {
+                    @SuppressWarnings("unused")
+                    String mimeType = buffer.getString(); // TODO: handle mime-type
+                }
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_LINK_COUNT) != 0) {
+                    @SuppressWarnings("unused")
+                    int nlink = buffer.getInt(); // TODO: handle link-count
+                }
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UNTRANSLATED_NAME) != 0) {
+                    @SuppressWarnings("unused")
+                    String untranslated = buffer.getString(); // TODO: handle untranslated-name
+                }
             }
-            // TODO: acl
         } else {
             throw new IllegalStateException("readAttributes - unsupported version: " + version);
         }
-        return attrs;
-    }
 
-    protected FileTime readTime(Buffer buffer, int flags) {
-        return SftpHelper.readTime(buffer, getVersion(), flags);
+        return attrs;
     }
 
     protected void writeAttributes(Buffer buffer, Attributes attributes) throws IOException {
         int version = getVersion();
+        int flagsMask = 0;
         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:
@@ -474,7 +515,6 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
                 SftpHelper.writeTime(buffer, version, flagsMask, attributes.getModifyTime());
             }
         } else if (version >= SftpConstants.SFTP_V4) {
-            int flagsMask = 0;
             for (Attribute a : flags) {
                 switch (a) {
                     case Size:
@@ -495,6 +535,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
                     case CreateTime:
                         flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_CREATETIME;
                         break;
+                    case Acl:
+                        flagsMask |= SftpConstants.SSH_FILEXFER_ATTR_ACL;
+                        break;
                     default:    // do nothing
                 }
             }
@@ -505,10 +548,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             }
             if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
                 String owner = attributes.getOwner();
-                buffer.putString(GenericUtils.isEmpty(owner) ? "OWNER@" : owner);
+                buffer.putString(GenericUtils.isEmpty(owner) ? SftpUniversalOwnerAndGroup.Owner.getName() : owner);
 
                 String group = attributes.getGroup();
-                buffer.putString(GenericUtils.isEmpty(group) ? "GROUP@" : group);
+                buffer.putString(GenericUtils.isEmpty(group) ? SftpUniversalOwnerAndGroup.Group.getName() : group);
             }
             if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
                 buffer.putInt(attributes.getPermissions());
@@ -522,8 +565,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
                 SftpHelper.writeTime(buffer, version, flagsMask, attributes.getModifyTime());
             }
+            if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_ACL) != 0) {
+                SftpHelper.writeACLs(buffer, version, attributes.getAcl());
+            }
+
             // TODO: for v6+ add CTIME (see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-21)
-            // TODO: acl
         } else {
             throw new UnsupportedOperationException("writeAttributes(" + attributes + ") unsupported version: " + version);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpFileAttributeView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpFileAttributeView.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpFileAttributeView.java
new file mode 100644
index 0000000..79078d7
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpFileAttributeView.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileAttributeView;
+
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSftpFileAttributeView extends AbstractLoggingBean implements FileAttributeView {
+    protected final SftpFileSystemProvider provider;
+    protected final Path path;
+    protected final LinkOption[] options;
+
+    protected AbstractSftpFileAttributeView(SftpFileSystemProvider provider, Path path, LinkOption... options) {
+        this.provider = ValidateUtils.checkNotNull(provider, "No file system provider instance");
+        this.path = ValidateUtils.checkNotNull(path, "No path");
+        this.options = options;
+    }
+
+    @Override
+    public String name() {
+        return "view";
+    }
+
+    /**
+     * @return The underlying {@link SftpFileSystemProvider} used to
+     * provide the view functionality
+     */
+    public final SftpFileSystemProvider provider() {
+        return provider;
+    }
+
+    /**
+     * @return The referenced view {@link Path}
+     */
+    public final Path getPath() {
+        return path;
+    }
+
+    protected SftpClient.Attributes readRemoteAttributes() throws IOException {
+        return provider.readRemoteAttributes(provider.toSftpPath(path), options);
+    }
+
+    protected void writeRemoteAttributes(SftpClient.Attributes attrs) throws IOException {
+        SftpPath p = provider.toSftpPath(path);
+        SftpFileSystem fs = p.getFileSystem();
+        try (SftpClient client = fs.getClient()) {
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("writeRemotAttributes({})[{}]: {}", fs, p, attrs);
+                }
+                client.setStat(p.toString(), attrs);
+            } catch (SftpException e) {
+                if (e.getStatus() == SftpConstants.SSH_FX_NO_SUCH_FILE) {
+                    throw new NoSuchFileException(p.toString());
+                }
+                throw e;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
new file mode 100644
index 0000000..f3ca4f3
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.UserPrincipal;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpAclFileAttributeView extends AbstractSftpFileAttributeView implements AclFileAttributeView {
+    public SftpAclFileAttributeView(SftpFileSystemProvider provider, Path path, LinkOption... options) {
+        super(provider, path, options);
+    }
+
+    @Override
+    public UserPrincipal getOwner() throws IOException {
+        PosixFileAttributes v = provider.readAttributes(path, PosixFileAttributes.class, options);
+        return v.owner();
+    }
+
+    @Override
+    public void setOwner(UserPrincipal owner) throws IOException {
+        provider.setAttribute(path, "posix", "owner", owner, options);
+    }
+
+    @Override
+    public String name() {
+        return "acl";
+    }
+
+    @Override
+    public List<AclEntry> getAcl() throws IOException {
+        return readRemoteAttributes().getAcl();
+    }
+
+    @Override
+    public void setAcl(List<AclEntry> acl) throws IOException {
+        writeRemoteAttributes(new SftpClient.Attributes().acl(acl));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/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 6acd599..1d50351 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
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.channels.Channel;
+import java.nio.file.attribute.AclEntry;
 import java.nio.file.attribute.FileTime;
 import java.util.Collection;
 import java.util.EnumSet;
@@ -34,16 +35,12 @@ 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.subsystem.sftp.SftpUniversalOwnerAndGroup;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.bouncycastle.util.Arrays;
 
-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_IFMT;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IFREG;
-
 /**
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
@@ -70,7 +67,8 @@ public interface SftpClient extends SubsystemClient {
         OwnerGroup,
         AccessTime,
         ModifyTime,
-        CreateTime
+        CreateTime,
+        Acl
     }
 
     class Handle {
@@ -130,7 +128,7 @@ public interface SftpClient extends SubsystemClient {
     }
     // CHECKSTYLE:ON
 
-    class Attributes implements Cloneable {
+    class Attributes {
         private Set<Attribute> flags = EnumSet.noneOf(Attribute.class);
         private int type = SftpConstants.SSH_FILEXFER_TYPE_UNKNOWN;
         private int perms;
@@ -142,6 +140,7 @@ public interface SftpClient extends SubsystemClient {
         private FileTime accessTime;
         private FileTime createTime;
         private FileTime modifyTime;
+        private List<AclEntry> acl;
 
         public Attributes() {
             super();
@@ -191,7 +190,7 @@ public interface SftpClient extends SubsystemClient {
             flags.add(Attribute.OwnerGroup);
             this.owner = owner;
             if (GenericUtils.isEmpty(group)) {
-                group = "GROUP@";
+                group = SftpUniversalOwnerAndGroup.Group.getName();
             }
         }
 
@@ -208,7 +207,7 @@ public interface SftpClient extends SubsystemClient {
             flags.add(Attribute.OwnerGroup);
             this.group = group;
             if (GenericUtils.isEmpty(owner)) {
-                owner = "OWNER@";
+                owner = SftpUniversalOwnerAndGroup.Owner.getName();
             }
         }
 
@@ -307,16 +306,30 @@ public interface SftpClient extends SubsystemClient {
             modifyTime = ValidateUtils.checkNotNull(mtime, "No modify time");
         }
 
+        public List<AclEntry> getAcl() {
+            return acl;
+        }
+
+        public Attributes acl(List<AclEntry> acl) {
+            setAcl(acl);
+            return this;
+        }
+
+        public void setAcl(List<AclEntry> acl) {
+            this.flags.add(Attribute.Acl);
+            this.acl = acl;
+        }
+
         public boolean isRegularFile() {
-            return (perms & S_IFMT) == S_IFREG;
+            return (getPermissions() & SftpConstants.S_IFMT) == SftpConstants.S_IFREG;
         }
 
         public boolean isDirectory() {
-            return (perms & S_IFMT) == S_IFDIR;
+            return (getPermissions() & SftpConstants.S_IFMT) == SftpConstants.S_IFDIR;
         }
 
         public boolean isSymbolicLink() {
-            return (perms & S_IFMT) == S_IFLNK;
+            return (getPermissions() & SftpConstants.S_IFMT) == SftpConstants.S_IFLNK;
         }
 
         public boolean isOther() {
@@ -324,17 +337,6 @@ public interface SftpClient extends SubsystemClient {
         }
 
         @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()

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileStore.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileStore.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileStore.java
index ec193c7..8a6f1f1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileStore.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileStore.java
@@ -79,7 +79,7 @@ public class SftpFileStore extends FileStore {
     public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
         SftpFileSystem sftpFs = getFileSystem();
         SftpFileSystemProvider provider = sftpFs.provider();
-        return provider.isSupportedFileAttributeView(type);
+        return provider.isSupportedFileAttributeView(sftpFs, type);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystem.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystem.java
index cf91f90..91b6849 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystem.java
@@ -35,6 +35,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Queue;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -44,6 +45,7 @@ import org.apache.sshd.client.session.ClientSessionHolder;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.file.util.BaseFileSystem;
 import org.apache.sshd.common.file.util.ImmutableList;
+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.Buffer;
@@ -52,7 +54,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSe
     public static final String POOL_SIZE_PROP = "sftp-fs-pool-size";
     public static final int DEFAULT_POOL_SIZE = 8;
 
-    public static final Set<String> SUPPORTED_VIEWS =
+    public static final Set<String> UNIVERSAL_SUPPORTED_VIEWS =
             Collections.unmodifiableSet(
                     GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER,
                             Arrays.asList(
@@ -64,6 +66,8 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSe
     private final SftpVersionSelector selector;
     private final Queue<SftpClient> pool;
     private final ThreadLocal<Wrapper> wrappers = new ThreadLocal<>();
+    private final int version;
+    private final Set<String> supportedViews;
     private SftpPath defaultDir;
     private int readBufferSize = SftpClient.DEFAULT_READ_BUFFER_SIZE;
     private int writeBufferSize = SftpClient.DEFAULT_WRITE_BUFFER_SIZE;
@@ -77,8 +81,18 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSe
         this.stores = Collections.unmodifiableList(Collections.<FileStore>singletonList(new SftpFileStore(id, this)));
         this.pool = new LinkedBlockingQueue<>(PropertyResolverUtils.getIntProperty(session, POOL_SIZE_PROP, DEFAULT_POOL_SIZE));
         try (SftpClient client = getClient()) {
+            version = client.getVersion();
             defaultDir = getPath(client.canonicalPath("."));
         }
+
+        if (version >= SftpConstants.SFTP_V4) {
+            Set<String> views = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+            views.addAll(UNIVERSAL_SUPPORTED_VIEWS);
+            views.add("acl");
+            supportedViews = Collections.unmodifiableSet(views);
+        } else {
+            supportedViews = UNIVERSAL_SUPPORTED_VIEWS;
+        }
     }
 
     public final SftpVersionSelector getSftpVersionSelector() {
@@ -89,6 +103,10 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSe
         return id;
     }
 
+    public final int getVersion() {
+        return version;
+    }
+
     @Override
     public SftpFileSystemProvider provider() {
         return (SftpFileSystemProvider) super.provider();
@@ -177,7 +195,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSe
 
     @Override
     public Set<String> supportedFileAttributeViews() {
-        return SUPPORTED_VIEWS;
+        return supportedViews;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
index dd6e9a4..6b7e74e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
@@ -43,10 +43,13 @@ import java.nio.file.Path;
 import java.nio.file.ProviderMismatchException;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclFileAttributeView;
 import java.nio.file.attribute.BasicFileAttributeView;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileAttribute;
 import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileOwnerAttributeView;
 import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.GroupPrincipal;
 import java.nio.file.attribute.PosixFileAttributeView;
@@ -60,8 +63,10 @@ import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.client.SshClient;
@@ -89,13 +94,16 @@ public class SftpFileSystemProvider extends FileSystemProvider {
     public static final String AUTH_TIME_PROP_NAME = "sftp-fs-auth-time";
     public static final long DEFAULT_AUTH_TIME = SftpClient.DEFAULT_WAIT_TIMEOUT;
 
-    public static final Set<Class<? extends FileAttributeView>> SUPPORTED_VIEWS =
-            Collections.unmodifiableSet(
-                    new HashSet<>(
-                            Arrays.<Class<? extends FileAttributeView>>asList(
-                                    BasicFileAttributeView.class, PosixFileAttributeView.class
-                            )));
+    public static final Set<Class<? extends FileAttributeView>> UNIVERSAL_SUPPORTED_VIEWS =
+            Collections.unmodifiableSet(new HashSet<Class<? extends FileAttributeView>>() {
+                private static final long serialVersionUID = 1L;    // we're not serializing it
 
+                {
+                    add(PosixFileAttributeView.class);
+                    add(FileOwnerAttributeView.class);
+                    add(BasicFileAttributeView.class);
+                }
+            });
     protected final Logger log;
 
     private final SshClient client;
@@ -611,16 +619,37 @@ public class SftpFileSystemProvider extends FileSystemProvider {
     }
 
     @Override
-    public <V extends FileAttributeView> V getFileAttributeView(final Path path, Class<V> type, final LinkOption... options) {
-        if (isSupportedFileAttributeView(type)) {
-            return type.cast(new SftpPosixFileAttributeView(this, path, options));
-        } else {
-            throw new UnsupportedOperationException("getFileAttributeView(" + path + ") view not supported: " + type.getSimpleName());
+    public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, final LinkOption... options) {
+        if (isSupportedFileAttributeView(path, type)) {
+            if (AclFileAttributeView.class.isAssignableFrom(type)) {
+                return type.cast(new SftpAclFileAttributeView(this, path, options));
+            } else if (BasicFileAttributeView.class.isAssignableFrom(type)) {
+                return type.cast(new SftpPosixFileAttributeView(this, path, options));
+            }
         }
+
+        throw new UnsupportedOperationException("getFileAttributeView(" + path + ") view not supported: " + type.getSimpleName());
     }
 
-    public boolean isSupportedFileAttributeView(Class<? extends FileAttributeView> type) {
-        return (type != null) && SUPPORTED_VIEWS.contains(type);
+    public boolean isSupportedFileAttributeView(Path path, Class<? extends FileAttributeView> type) {
+        return isSupportedFileAttributeView(toSftpPath(path).getFileSystem(), type);
+    }
+
+    public boolean isSupportedFileAttributeView(SftpFileSystem fs, Class<? extends FileAttributeView> type) {
+        Collection<String> views = fs.supportedFileAttributeViews();
+        if ((type == null) || GenericUtils.isEmpty(views)) {
+            return false;
+        } else if (PosixFileAttributeView.class.isAssignableFrom(type)) {
+            return views.contains("posix");
+        } else if (AclFileAttributeView.class.isAssignableFrom(type)) {
+            return views.contains("acl");   // must come before owner view
+        } else if (FileOwnerAttributeView.class.isAssignableFrom(type)) {
+            return views.contains("owner");
+        } else if (BasicFileAttributeView.class.isAssignableFrom(type)) {
+            return views.contains("basic"); // must be last
+        } else {
+            return false;
+        }
     }
 
     @Override
@@ -644,18 +673,97 @@ public class SftpFileSystemProvider extends FileSystemProvider {
             view = attributes.substring(0, i++);
             attrs = attributes.substring(i);
         }
+
+        return readAttributes(path, view, attrs, options);
+    }
+
+    public Map<String, Object> readAttributes(Path path, String view, String attrs, LinkOption... options) throws IOException {
         SftpPath p = toSftpPath(path);
         SftpFileSystem fs = p.getFileSystem();
         Collection<String> views = fs.supportedFileAttributeViews();
         if (GenericUtils.isEmpty(views) || (!views.contains(view))) {
-            throw new UnsupportedOperationException("readAttributes(" + path + ")[" + attributes + "] view " + view + " not supported: " + views);
+            throw new UnsupportedOperationException("readAttributes(" + path + ")[" + view + ":" + attrs + "] view not supported: " + views);
+        }
+
+        if ("basic".equalsIgnoreCase(view) || "posix".equalsIgnoreCase(view) || "owner".equalsIgnoreCase(view)) {
+            return readPosixViewAttributes(p, view, attrs, options);
+        } else if ("acl".equalsIgnoreCase(view)) {
+            return readAclViewAttributes(p, view, attrs, options);
+        } else  {
+            return readCustomViewAttributes(p, view, attrs, options);
+        }
+    }
+
+    protected Map<String, Object> readCustomViewAttributes(SftpPath path, String view, String attrs, LinkOption... options) throws IOException {
+        throw new UnsupportedOperationException("readCustomViewAttributes(" + path + ")[" + view + ":" + attrs + "] view not supported");
+    }
+
+    protected Map<String, Object> readAclViewAttributes(SftpPath path, String view, String attrs, LinkOption... options) throws IOException {
+        if ("*".equals(attrs)) {
+            attrs = "acl,owner";
+        }
+
+        SftpFileSystem fs = path.getFileSystem();
+        SftpClient.Attributes attributes;
+        try (SftpClient client = fs.getClient()) {
+            attributes = readRemoteAttributes(path, options);
         }
 
+        Map<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        for (String attr : attrs.split(",")) {
+            switch (attr) {
+                case "acl":
+                    List<AclEntry> acl = attributes.getAcl();
+                    if (acl != null) {
+                        map.put(attr, acl);
+                    }
+                    break;
+                case "owner":
+                    String owner = attributes.getOwner();
+                    if (GenericUtils.length(owner) > 0) {
+                        map.put(attr, new SftpFileSystem.DefaultUserPrincipal(owner));
+                    }
+                    break;
+                default:
+                    if (log.isTraceEnabled()) {
+                        log.trace("readAclViewAttributes({})[{}] unknown attribute: {}", fs, attrs, attr);
+                    }
+            }
+        }
+
+        return map;
+    }
+
+    protected SftpClient.Attributes readRemoteAttributes(SftpPath path, LinkOption... options) throws IOException {
+        SftpFileSystem fs = path.getFileSystem();
+        try (SftpClient client = fs.getClient()) {
+            try {
+                SftpClient.Attributes attrs;
+                if (IoUtils.followLinks(options)) {
+                    attrs = client.stat(path.toString());
+                } else {
+                    attrs = client.lstat(path.toString());
+                }
+                if (log.isTraceEnabled()) {
+                    log.trace("readRemoteAttributes({})[{}]: {}", fs, path, attrs);
+                }
+                return attrs;
+            } catch (SftpException e) {
+                if (e.getStatus() == SftpConstants.SSH_FX_NO_SUCH_FILE) {
+                    throw new NoSuchFileException(path.toString());
+                }
+                throw e;
+            }
+        }
+    }
+
+    protected Map<String, Object> readPosixViewAttributes(SftpPath path, String view, String attrs, LinkOption... options) throws IOException {
         PosixFileAttributes v = readAttributes(path, PosixFileAttributes.class, options);
         if ("*".equals(attrs)) {
             attrs = "lastModifiedTime,lastAccessTime,creationTime,size,isRegularFile,isDirectory,isSymbolicLink,isOther,fileKey,owner,permissions,group";
         }
-        Map<String, Object> map = new HashMap<>();
+
+        Map<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
         for (String attr : attrs.split(",")) {
             switch (attr) {
                 case "lastModifiedTime":
@@ -696,7 +804,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
                     break;
                 default:
                     if (log.isTraceEnabled()) {
-                        log.trace("readAttributes({})[{}] ignored {}={} for {}", fs, path, attr, v, attributes);
+                        log.trace("readPosixViewAttributes({})[{}:{}] ignored for {}", path, view, attr, attrs);
                     }
             }
         }
@@ -715,11 +823,16 @@ public class SftpFileSystemProvider extends FileSystemProvider {
             view = attribute.substring(0, i++);
             attr = attribute.substring(i);
         }
+
+        setAttribute(path, view, attr, value, options);
+    }
+
+    public void setAttribute(Path path, String view, String attr, Object value, LinkOption... options) throws IOException {
         SftpPath p = toSftpPath(path);
         SftpFileSystem fs = p.getFileSystem();
         Collection<String> views = fs.supportedFileAttributeViews();
-        if (GenericUtils.isEmpty(views) || (!view.contains(view))) {
-            throw new UnsupportedOperationException("setAttribute(" + path + ")[" + attribute + "=" + value + "] view " + view + " not supported: " + views);
+        if (GenericUtils.isEmpty(views) || (!views.contains(view))) {
+            throw new UnsupportedOperationException("setAttribute(" + path + ")[" + view + ":" + attr + "=" + value + "] view " + view + " not supported: " + views);
         }
 
         SftpClient.Attributes attributes = new SftpClient.Attributes();
@@ -747,16 +860,21 @@ public class SftpFileSystemProvider extends FileSystemProvider {
             case "group":
                 attributes.group(((GroupPrincipal) value).getName());
                 break;
+            case "acl":
+                ValidateUtils.checkTrue("acl".equalsIgnoreCase(view), "ACL cannot be set via view=%s", view);
+                @SuppressWarnings("unchecked")
+                List<AclEntry> acl = (List<AclEntry>) value;
+                attributes.acl(acl);
+                break;
             case "isRegularFile":
             case "isDirectory":
             case "isSymbolicLink":
             case "isOther":
             case "fileKey":
-                throw new UnsupportedOperationException("setAttribute(" + path + ")[" + attribute + "=" + value + "]"
-                        + " unknown view=" + view + " attribute: " + attr);
+                throw new UnsupportedOperationException("setAttribute(" + path + ")[" + view + ":" + attr + "=" + value + "] modification N/A");
             default:
                 if (log.isTraceEnabled()) {
-                    log.trace("setAttribute({})[{}] ignore {}/{}={}", fs, path, attribute, attr, value);
+                    log.trace("setAttribute({})[{}] ignore {}:{}={}", fs, path, view, attr, value);
                 }
         }
 
@@ -769,7 +887,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
         }
     }
 
-    protected SftpPath toSftpPath(Path path) {
+    public SftpPath toSftpPath(Path path) {
         ValidateUtils.checkNotNull(path, "No path provided");
         if (!(path instanceof SftpPath)) {
             throw new ProviderMismatchException("Path is not SFTP: " + path);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java
index 00c1d9f..13d2119 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java
@@ -20,7 +20,6 @@ package org.apache.sshd.client.subsystem.sftp;
 
 import java.io.IOException;
 import java.nio.file.LinkOption;
-import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.GroupPrincipal;
@@ -30,77 +29,45 @@ import java.nio.file.attribute.PosixFilePermission;
 import java.nio.file.attribute.UserPrincipal;
 import java.util.Set;
 
-import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
-import org.apache.sshd.common.subsystem.sftp.SftpConstants;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.GenericUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class SftpPosixFileAttributeView implements PosixFileAttributeView {
-    private final SftpFileSystemProvider provider;
-    private final Path path;
-    private final LinkOption[] options;
-
+public class SftpPosixFileAttributeView extends AbstractSftpFileAttributeView implements PosixFileAttributeView {
     public SftpPosixFileAttributeView(SftpFileSystemProvider provider, Path path, LinkOption... options) {
-        this.provider = ValidateUtils.checkNotNull(provider, "No file system provider instance");
-        this.path = path;
-        this.options = options;
+        super(provider, path, options);
     }
 
     @Override
     public String name() {
-        return "view";
-    }
-
-    /**
-     * @return The underlying {@link SftpFileSystemProvider} used to
-     * provide the view functionality
-     */
-    public final SftpFileSystemProvider provider() {
-        return provider;
-    }
-
-    /**
-     * @return The referenced view {@link Path}
-     */
-    public final Path getPath() {
-        return path;
+        return "posix";
     }
 
     @Override
     public PosixFileAttributes readAttributes() throws IOException {
-        SftpPath p = provider.toSftpPath(path);
-        SftpFileSystem fs = p.getFileSystem();
-        final Attributes attributes;
-        try (SftpClient client = fs.getClient()) {
-            try {
-                if (IoUtils.followLinks(options)) {
-                    attributes = client.stat(p.toString());
-                } else {
-                    attributes = client.lstat(p.toString());
-                }
-            } catch (SftpException e) {
-                if (e.getStatus() == SftpConstants.SSH_FX_NO_SUCH_FILE) {
-                    throw new NoSuchFileException(p.toString());
-                }
-                throw e;
-            }
-        }
-        return new SftpPosixFileAttributes(path, attributes);
+        return new SftpPosixFileAttributes(path, readRemoteAttributes());
     }
 
     @Override
     public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException {
+        SftpClient.Attributes attrs = new SftpClient.Attributes();
         if (lastModifiedTime != null) {
-            provider.setAttribute(path, "lastModifiedTime", lastModifiedTime, options);
+            attrs.modifyTime(lastModifiedTime);
         }
         if (lastAccessTime != null) {
-            provider.setAttribute(path, "lastAccessTime", lastAccessTime, options);
+            attrs.accessTime(lastAccessTime);
         }
         if (createTime != null) {
-            provider.setAttribute(path, "createTime", createTime, options);
+            attrs.createTime(createTime);
+        }
+
+        if (GenericUtils.isEmpty(attrs.getFlags())) {
+            if (log.isDebugEnabled()) {
+                log.debug("setTimes({}) no changes", path);
+            }
+        } else {
+            writeRemoteAttributes(attrs);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java
index b510907..d5c9df3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java
@@ -50,7 +50,7 @@ public class SftpPosixFileAttributes implements PosixFileAttributes {
     @Override
     public UserPrincipal owner() {
         String owner = attributes.getOwner();
-        return GenericUtils.isEmpty(owner) ? null : new SftpFileSystem.DefaultGroupPrincipal(owner);
+        return GenericUtils.isEmpty(owner) ? null : new SftpFileSystem.DefaultUserPrincipal(owner);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpConstants.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpConstants.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpConstants.java
index 9b9a87a..8cbb6f7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpConstants.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpConstants.java
@@ -164,6 +164,12 @@ public final class SftpConstants {
     public static final int SSH_FXF_RENAME_ATOMIC = 0x00000002;
     public static final int SSH_FXF_RENAME_NATIVE = 0x00000004;
 
+    public static final int SFX_ACL_CONTROL_INCLUDED = 0x00000001;
+    public static final int SFX_ACL_CONTROL_PRESENT = 0x00000002;
+    public static final int SFX_ACL_CONTROL_INHERITED = 0x00000004;
+    public static final int SFX_ACL_AUDIT_ALARM_INCLUDED = 0x00000010;
+    public static final int SFX_ACL_AUDIT_ALARM_INHERITED = 0x00000020;
+
     public static final int ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000;
     public static final int ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001;
     public static final int ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/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 80951ef..9f0c656 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
@@ -34,8 +34,10 @@ import java.nio.file.attribute.AclEntryType;
 import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.PosixFilePermission;
 import java.nio.file.attribute.UserPrincipal;
+import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
@@ -44,8 +46,11 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.server.subsystem.sftp.DefaultGroupPrincipal;
 import org.apache.sshd.server.subsystem.sftp.InvalidHandleException;
 
@@ -95,7 +100,6 @@ public final class SftpHelper {
         Number size = (Number) attributes.get("size");
         FileTime lastModifiedTime = (FileTime) attributes.get("lastModifiedTime");
         FileTime lastAccessTime = (FileTime) attributes.get("lastAccessTime");
-
         int flags = ((isReg || isLnk) && (size != null) ? SftpConstants.SSH_FILEXFER_ATTR_SIZE : 0)
                   | (attributes.containsKey("uid") && attributes.containsKey("gid") ? SftpConstants.SSH_FILEXFER_ATTR_UIDGID : 0)
                   | ((perms != null) ? SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS : 0)
@@ -118,7 +122,7 @@ public final class SftpHelper {
     }
 
     /**
-     * Writes the retrieved file / directory attributes in V3 format
+     * Writes the retrieved file / directory attributes in V4+ format
      *
      * @param buffer The target {@link Buffer}
      * @param version The actual version - must be at least {@link SftpConstants#SFTP_V4}
@@ -136,12 +140,15 @@ public final class SftpHelper {
         FileTime lastModifiedTime = (FileTime) attributes.get("lastModifiedTime");
         FileTime lastAccessTime = (FileTime) attributes.get("lastAccessTime");
         FileTime creationTime = (FileTime) attributes.get("creationTime");
+        @SuppressWarnings("unchecked")
+        Collection<AclEntry> acl = (Collection<AclEntry>) attributes.get("acl");
         int flags = (((isReg || isLnk) && (size != null)) ? SftpConstants.SSH_FILEXFER_ATTR_SIZE : 0)
                   | ((attributes.containsKey("owner") && attributes.containsKey("group")) ? SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP : 0)
                   | ((perms != null) ? SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS : 0)
                   | ((lastModifiedTime != null) ? SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME : 0)
                   | ((creationTime != null) ? SftpConstants.SSH_FILEXFER_ATTR_CREATETIME : 0)
-                  | ((lastAccessTime != null) ? SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME : 0);
+                  | ((lastAccessTime != null) ? SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME : 0)
+                  | ((acl != null) ? SftpConstants.SSH_FILEXFER_ATTR_ACL : 0);
         buffer.putInt(flags);
         buffer.putByte((byte) (isReg ? SftpConstants.SSH_FILEXFER_TYPE_REGULAR
                 : isDir ? SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY
@@ -151,8 +158,8 @@ public final class SftpHelper {
             buffer.putLong(size.longValue());
         }
         if ((flags & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
-            buffer.putString(Objects.toString(attributes.get("owner"), null));
-            buffer.putString(Objects.toString(attributes.get("group"), null));
+            buffer.putString(Objects.toString(attributes.get("owner"), SftpUniversalOwnerAndGroup.Owner.getName()));
+            buffer.putString(Objects.toString(attributes.get("group"), SftpUniversalOwnerAndGroup.Group.getName()));
         }
         if ((flags & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
             buffer.putInt(attributesToPermissions(isReg, isDir, isLnk, perms));
@@ -168,9 +175,12 @@ public final class SftpHelper {
         if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
             writeTime(buffer, version, flags, lastModifiedTime);
         }
-        // TODO: acls
+        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACL) != 0) {
+            writeACLs(buffer, version, acl);
+        }
+        // TODO: ctime
         // TODO: bits
-        // TODO: extended
+        // TODO: extensions
     }
 
     /**
@@ -353,29 +363,183 @@ public final class SftpHelper {
             return SftpConstants.SSH_FX_INVALID_FILENAME;
         } else if (t instanceof IllegalArgumentException) {
             return SftpConstants.SSH_FX_INVALID_PARAMETER;
+        } else if (t instanceof UnsupportedOperationException) {
+            return SftpConstants.SSH_FX_OP_UNSUPPORTED;
         } else {
             return SftpConstants.SSH_FX_FAILURE;
         }
     }
 
-    public static AclEntry buildAclEntry(int aclType, int aclFlag, int aclMask, final String aclWho) {
-        AclEntryType type;
+    public static Map<String, Object> readAttrs(Buffer buffer, int version) {
+        Map<String, Object> attrs = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
+        int flags = buffer.getInt();
+        if (version >= SftpConstants.SFTP_V4) {
+            int type = buffer.getUByte();
+            switch (type) {
+                case SftpConstants.SSH_FILEXFER_TYPE_REGULAR:
+                    attrs.put("isRegular", Boolean.TRUE);
+                    break;
+                case SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY:
+                    attrs.put("isDirectory", Boolean.TRUE);
+                    break;
+                case SftpConstants.SSH_FILEXFER_TYPE_SYMLINK:
+                    attrs.put("isSymbolicLink", Boolean.TRUE);
+                    break;
+                case SftpConstants.SSH_FILEXFER_TYPE_UNKNOWN:
+                    attrs.put("isOther", Boolean.TRUE);
+                    break;
+                default:    // ignored
+            }
+        }
+        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
+            attrs.put("size", buffer.getLong());
+        }
+
+        if (version == SftpConstants.SFTP_V3) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UIDGID) != 0) {
+                attrs.put("uid", buffer.getInt());
+                attrs.put("gid", buffer.getInt());
+            }
+        } else {
+            if ((version >= SftpConstants.SFTP_V6) && ((flags & SftpConstants.SSH_FILEXFER_ATTR_ALLOCATION_SIZE) != 0)) {
+                @SuppressWarnings("unused")
+                long allocSize = buffer.getLong();    // TODO handle allocation size
+            }
+
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
+                attrs.put("owner", new DefaultGroupPrincipal(buffer.getString()));
+                attrs.put("group", new DefaultGroupPrincipal(buffer.getString()));
+            }
+        }
+
+        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+            attrs.put("permissions", permissionsToAttributes(buffer.getInt()));
+        }
+
+        if (version == SftpConstants.SFTP_V3) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+                attrs.put("lastAccessTime", readTime(buffer, version, flags));
+                attrs.put("lastModifiedTime", readTime(buffer, version, flags));
+            }
+        } else if (version >= SftpConstants.SFTP_V4) {
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+                attrs.put("lastAccessTime", readTime(buffer, version, flags));
+            }
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+                attrs.put("creationTime", readTime(buffer, version, flags));
+            }
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+                attrs.put("lastModifiedTime", readTime(buffer, version, flags));
+            }
+            if ((version >= SftpConstants.SFTP_V6) && (flags & SftpConstants.SSH_FILEXFER_ATTR_CTIME) != 0) {
+                attrs.put("ctime", readTime(buffer, version, flags));
+            }
+
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACL) != 0) {
+                attrs.put("acl", readACLs(buffer, version));
+            }
+
+            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_BITS) != 0) {
+                @SuppressWarnings("unused")
+                int bits = buffer.getInt();
+                @SuppressWarnings("unused")
+                int valid = 0xffffffff;
+                if (version >= SftpConstants.SFTP_V6) {
+                    valid = buffer.getInt();
+                }
+                // TODO: handle attrib bits
+            }
+
+            if (version >= SftpConstants.SFTP_V6) {
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_TEXT_HINT) != 0) {
+                    @SuppressWarnings("unused")
+                    boolean text = buffer.getBoolean(); // TODO: handle text
+                }
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MIME_TYPE) != 0) {
+                    @SuppressWarnings("unused")
+                    String mimeType = buffer.getString(); // TODO: handle mime-type
+                }
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_LINK_COUNT) != 0) {
+                    @SuppressWarnings("unused")
+                    int nlink = buffer.getInt(); // TODO: handle link-count
+                }
+                if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UNTRANSLATED_NAME) != 0) {
+                    @SuppressWarnings("unused")
+                    String untranslated = buffer.getString(); // TODO: handle untranslated-name
+                }
+            }
+        }
+
+        return attrs;
+    }
+
+    // for v4,5 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#page-15
+    // for v6 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-21
+    public static List<AclEntry> readACLs(Buffer buffer, int version) {
+        int aclSize = buffer.getInt();
+        int startPos = buffer.rpos();
+        Buffer aclBuffer = new ByteArrayBuffer(buffer.array(), startPos, aclSize, true);
+        List<AclEntry> acl = decodeACLs(aclBuffer, version);
+        buffer.rpos(startPos + aclSize);
+        return acl;
+    }
+
+    public static List<AclEntry> decodeACLs(Buffer buffer, int version) {
+        @SuppressWarnings("unused")
+        int aclFlags = 0;   // TODO handle ACL flags
+        if (version >= SftpConstants.SFTP_V6) {
+            aclFlags = buffer.getInt();
+        }
+
+        int count = buffer.getInt();
+        // NOTE: although the value is defined as UINT32 we do not expected a count greater than Integer.MAX_VALUE
+        ValidateUtils.checkTrue(count >= 0, "Invalid ACL entries count: %d", count);
+        if (count == 0) {
+            return Collections.emptyList();
+        }
+
+        List<AclEntry> acls = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            int aclType = buffer.getInt();
+            int aclFlag = buffer.getInt();
+            int aclMask = buffer.getInt();
+            String aclWho = buffer.getString();
+            acls.add(buildAclEntry(aclType, aclFlag, aclMask, aclWho));
+        }
+
+        return acls;
+    }
+
+    public static AclEntry buildAclEntry(int aclType, int aclFlag, int aclMask, String aclWho) {
+        UserPrincipal who = new DefaultGroupPrincipal(aclWho);
+        return AclEntry.newBuilder()
+                .setType(ValidateUtils.checkNotNull(decodeAclEntryType(aclType), "Unknown ACL type: %d", aclType))
+                .setFlags(decodeAclFlags(aclFlag))
+                .setPermissions(decodeAclMask(aclMask))
+                .setPrincipal(who)
+                .build();
+    }
+
+    /**
+     * @param aclType The {@code ACE4_ACCESS_xxx_ACE_TYPE} value
+     * @return The matching {@link AclEntryType} or {@code null} if unknown value
+     */
+    public static AclEntryType decodeAclEntryType(int aclType) {
         switch (aclType) {
             case SftpConstants.ACE4_ACCESS_ALLOWED_ACE_TYPE:
-                type = AclEntryType.ALLOW;
-                break;
+                return AclEntryType.ALLOW;
             case SftpConstants.ACE4_ACCESS_DENIED_ACE_TYPE:
-                type = AclEntryType.DENY;
-                break;
+                return AclEntryType.DENY;
             case SftpConstants.ACE4_SYSTEM_AUDIT_ACE_TYPE:
-                type = AclEntryType.AUDIT;
-                break;
+                return AclEntryType.AUDIT;
             case SftpConstants.ACE4_SYSTEM_ALARM_ACE_TYPE:
-                type = AclEntryType.AUDIT;
-                break;
+                return AclEntryType.ALARM;
             default:
-                throw new IllegalStateException("Unknown acl type: " + aclType);
+                return null;
         }
+    }
+
+    public static Set<AclEntryFlag> decodeAclFlags(int aclFlag) {
         Set<AclEntryFlag> flags = EnumSet.noneOf(AclEntryFlag.class);
         if ((aclFlag & SftpConstants.ACE4_FILE_INHERIT_ACE) != 0) {
             flags.add(AclEntryFlag.FILE_INHERIT);
@@ -390,6 +554,10 @@ public final class SftpHelper {
             flags.add(AclEntryFlag.INHERIT_ONLY);
         }
 
+        return flags;
+    }
+
+    public static Set<AclEntryPermission> decodeAclMask(int aclMask) {
         Set<AclEntryPermission> mask = EnumSet.noneOf(AclEntryPermission.class);
         if ((aclMask & SftpConstants.ACE4_READ_DATA) != 0) {
             mask.add(AclEntryPermission.READ_DATA);
@@ -442,127 +610,155 @@ public final class SftpHelper {
         if ((aclMask & SftpConstants.ACE4_SYNCHRONIZE) != 0) {
             mask.add(AclEntryPermission.SYNCHRONIZE);
         }
-        UserPrincipal who = new DefaultGroupPrincipal(aclWho);
-        return AclEntry.newBuilder()
-                .setType(type)
-                .setFlags(flags)
-                .setPermissions(mask)
-                .setPrincipal(who)
-                .build();
+
+        return mask;
     }
 
-    public static Map<String, Object> readAttrs(Buffer buffer, int version) {
-        Map<String, Object> attrs = new TreeMap<>();
-        int flags = buffer.getInt();
-        if (version >= SftpConstants.SFTP_V4) {
-            int type = buffer.getUByte();
-            switch (type) {
-                case SftpConstants.SSH_FILEXFER_TYPE_REGULAR:
-                    attrs.put("isRegular", Boolean.TRUE);
-                    break;
-                case SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY:
-                    attrs.put("isDirectory", Boolean.TRUE);
-                    break;
-                case SftpConstants.SSH_FILEXFER_TYPE_SYMLINK:
-                    attrs.put("isSymbolicLink", Boolean.TRUE);
-                    break;
-                case SftpConstants.SSH_FILEXFER_TYPE_UNKNOWN:
-                    attrs.put("isOther", Boolean.TRUE);
-                    break;
-                default:    // ignored
+    public static void writeACLs(Buffer buffer, int version, Collection<? extends AclEntry> acl) {
+        int lenPos = buffer.wpos();
+        buffer.putInt(0);   // length placeholder
+        encodeACLs(buffer, version, acl);
+        BufferUtils.updateLengthPlaceholder(buffer, lenPos);
+    }
+
+    public static void encodeACLs(Buffer buffer, int version, Collection<? extends AclEntry> acl) {
+        ValidateUtils.checkNotNull(acl, "No ACL");
+        if (version >= SftpConstants.SFTP_V6) {
+            buffer.putInt(0);   // TODO handle ACL flags
+        }
+
+        int numEntries = GenericUtils.size(acl);
+        buffer.putInt(numEntries);
+        if (numEntries > 0) {
+            for (AclEntry e : acl) {
+                writeAclEntry(buffer, e);
             }
         }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
-            attrs.put("size", buffer.getLong());
+    }
+
+    public static void writeAclEntry(Buffer buffer, AclEntry acl) {
+        ValidateUtils.checkNotNull(acl, "No ACL");
+
+        AclEntryType type = acl.type();
+        int aclType = encodeAclEntryType(type);
+        ValidateUtils.checkTrue(aclType >= 0, "Unknown ACL type: %s", type);
+        buffer.putInt(aclType);
+        buffer.putInt(encodeAclFlags(acl.flags()));
+        buffer.putInt(encodeAclMask(acl.permissions()));
+
+        Principal user = acl.principal();
+        buffer.putString(user.getName());
+    }
+
+    /**
+     * Returns the equivalent SFTP value for the ACL type
+     *
+     * @param type The {@link AclEntryType}
+     * @return The equivalent {@code ACE_SYSTEM_xxx_TYPE} or negative
+     * if {@code null} or unknown type
+     */
+    public static int encodeAclEntryType(AclEntryType type) {
+        if (type == null) {
+            return Integer.MIN_VALUE;
         }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ALLOCATION_SIZE) != 0) {
-            attrs.put("allocationSize", buffer.getLong());
+
+        switch(type) {
+            case ALARM:
+                return SftpConstants.ACE4_SYSTEM_ALARM_ACE_TYPE;
+            case ALLOW:
+                return SftpConstants.ACE4_ACCESS_ALLOWED_ACE_TYPE;
+            case AUDIT:
+                return SftpConstants.ACE4_SYSTEM_AUDIT_ACE_TYPE;
+            case DENY:
+                return SftpConstants.ACE4_ACCESS_DENIED_ACE_TYPE;
+            default:
+                return -1;
         }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UIDGID) != 0) {
-            attrs.put("uid", buffer.getInt());
-            attrs.put("gid", buffer.getInt());
+    }
+
+    public static long encodeAclFlags(Collection<AclEntryFlag> flags) {
+        if (GenericUtils.isEmpty(flags)) {
+            return 0L;
         }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
-            attrs.put("owner", new DefaultGroupPrincipal(buffer.getString()));
-            attrs.put("group", new DefaultGroupPrincipal(buffer.getString()));
+
+        long aclFlag = 0L;
+        if (flags.contains(AclEntryFlag.FILE_INHERIT)) {
+            aclFlag |= SftpConstants.ACE4_FILE_INHERIT_ACE;
         }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
-            attrs.put("permissions", permissionsToAttributes(buffer.getInt()));
+        if (flags.contains(AclEntryFlag.DIRECTORY_INHERIT)) {
+            aclFlag |= SftpConstants.ACE4_DIRECTORY_INHERIT_ACE;
+        }
+        if (flags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT)) {
+            aclFlag |= SftpConstants.ACE4_NO_PROPAGATE_INHERIT_ACE;
+        }
+        if (flags.contains(AclEntryFlag.INHERIT_ONLY)) {
+            aclFlag |= SftpConstants.ACE4_INHERIT_ONLY_ACE;
         }
 
-        if (version == SftpConstants.SFTP_V3) {
-            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
-                attrs.put("lastAccessTime", readTime(buffer, version, flags));
-                attrs.put("lastModifiedTime", readTime(buffer, version, flags));
-            }
-        } else if (version >= SftpConstants.SFTP_V4) {
-            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
-                attrs.put("lastAccessTime", readTime(buffer, version, flags));
-            }
-            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
-                attrs.put("creationTime", readTime(buffer, version, flags));
-            }
-            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
-                attrs.put("lastModifiedTime", readTime(buffer, version, flags));
-            }
-            if ((flags & SftpConstants.SSH_FILEXFER_ATTR_CTIME) != 0) {
-                attrs.put("ctime", readTime(buffer, version, flags));
-            }
+        return aclFlag;
+    }
+
+    public static long encodeAclMask(Collection<AclEntryPermission> mask) {
+        if (GenericUtils.isEmpty(mask)) {
+            return 0L;
         }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACL) != 0) {
-            int count = buffer.getInt();
-            List<AclEntry> acls = new ArrayList<>();
-            for (int i = 0; i < count; i++) {
-                int aclType = buffer.getInt();
-                int aclFlag = buffer.getInt();
-                int aclMask = buffer.getInt();
-                String aclWho = buffer.getString();
-                acls.add(buildAclEntry(aclType, aclFlag, aclMask, aclWho));
-            }
-            attrs.put("acl", acls);
+
+        long aclMask = 0L;
+        if (mask.contains(AclEntryPermission.READ_DATA)) {
+            aclMask |= SftpConstants.ACE4_READ_DATA;
         }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_BITS) != 0) {
-            int bits = buffer.getInt();
-            int valid = 0xffffffff;
-            if (version >= SftpConstants.SFTP_V6) {
-                valid = buffer.getInt();
-            }
-            // TODO: handle attrib bits
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_TEXT_HINT) != 0) {
-            boolean text = buffer.getBoolean();
-            // TODO: handle text
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MIME_TYPE) != 0) {
-            String mimeType = buffer.getString();
-            // TODO: handle mime-type
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_LINK_COUNT) != 0) {
-            int nlink = buffer.getInt();
-            // TODO: handle link-count
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UNTRANSLATED_NAME) != 0) {
-            String untranslated = buffer.getString();
-            // TODO: handle untranslated-name
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_EXTENDED) != 0) {
-            int count = buffer.getInt();
-            Map<String, String> extended = new TreeMap<>();
-            for (int i = 0; i < count; i++) {
-                String key = buffer.getString();
-                String val = buffer.getString();
-                extended.put(key, val);
-            }
-            attrs.put("extended", extended);
+        if (mask.contains(AclEntryPermission.LIST_DIRECTORY)) {
+            aclMask |= SftpConstants.ACE4_LIST_DIRECTORY;
+        }
+        if (mask.contains(AclEntryPermission.WRITE_DATA)) {
+            aclMask |= SftpConstants.ACE4_WRITE_DATA;
+        }
+        if (mask.contains(AclEntryPermission.ADD_FILE)) {
+            aclMask |= SftpConstants.ACE4_ADD_FILE;
+        }
+        if (mask.contains(AclEntryPermission.APPEND_DATA)) {
+            aclMask |= SftpConstants.ACE4_APPEND_DATA;
+        }
+        if (mask.contains(AclEntryPermission.ADD_SUBDIRECTORY)) {
+            aclMask |= SftpConstants.ACE4_ADD_SUBDIRECTORY;
+        }
+        if (mask.contains(AclEntryPermission.READ_NAMED_ATTRS)) {
+            aclMask |= SftpConstants.ACE4_READ_NAMED_ATTRS;
+        }
+        if (mask.contains(AclEntryPermission.WRITE_NAMED_ATTRS)) {
+            aclMask |= SftpConstants.ACE4_WRITE_NAMED_ATTRS;
+        }
+        if (mask.contains(AclEntryPermission.EXECUTE)) {
+            aclMask |= SftpConstants.ACE4_EXECUTE;
+        }
+        if (mask.contains(AclEntryPermission.DELETE_CHILD)) {
+            aclMask |= SftpConstants.ACE4_DELETE_CHILD;
+        }
+        if (mask.contains(AclEntryPermission.READ_ATTRIBUTES)) {
+            aclMask |= SftpConstants.ACE4_READ_ATTRIBUTES;
+        }
+        if (mask.contains(AclEntryPermission.WRITE_ATTRIBUTES)) {
+            aclMask |= SftpConstants.ACE4_WRITE_ATTRIBUTES;
+        }
+        if (mask.contains(AclEntryPermission.DELETE)) {
+            aclMask |= SftpConstants.ACE4_DELETE;
+        }
+        if (mask.contains(AclEntryPermission.READ_ACL)) {
+            aclMask |= SftpConstants.ACE4_READ_ACL;
+        }
+        if (mask.contains(AclEntryPermission.WRITE_ACL)) {
+            aclMask |= SftpConstants.ACE4_WRITE_ACL;
+        }
+        if (mask.contains(AclEntryPermission.WRITE_OWNER)) {
+            aclMask |= SftpConstants.ACE4_WRITE_OWNER;
+        }
+        if (mask.contains(AclEntryPermission.SYNCHRONIZE)) {
+            aclMask |= SftpConstants.ACE4_SYNCHRONIZE;
         }
 
-        return attrs;
+        return aclMask;
     }
 
-    // for v3 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#page-8
-    // for v4 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#page-10
-    // for v6 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-16
-
     /**
      * Encodes a {@link FileTime} value into a buffer
      *
@@ -572,6 +768,8 @@ public final class SftpHelper {
      * @param time The value to encode
      */
     public static void writeTime(Buffer buffer, int version, int flags, FileTime time) {
+        // for v3 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#page-8
+        // for v6 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-16
         if (version >= SftpConstants.SFTP_V4) {
             buffer.putLong(time.to(TimeUnit.SECONDS));
             if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
@@ -593,6 +791,8 @@ public final class SftpHelper {
      * @return The decoded value
      */
     public static FileTime readTime(Buffer buffer, int version, int flags) {
+        // for v3 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#page-8
+        // for v6 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-16
         long secs = (version >= SftpConstants.SFTP_V4) ? buffer.getLong() : buffer.getUInt();
         long millis = TimeUnit.SECONDS.toMillis(secs);
         if ((version >= SftpConstants.SFTP_V4) && ((flags & SftpConstants.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroup.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroup.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroup.java
new file mode 100644
index 0000000..c8068f1
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroup.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.subsystem.sftp;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * Some universal identifiers used in owner and/or group specification strings
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#page-12">SFTP ACL</A>
+ */
+public enum SftpUniversalOwnerAndGroup implements NamedResource {
+    Owner,          // The owner of the file.
+    Group,          // The group associated with the file.
+    Everyone,       // The world.
+    Interactive,    // Accessed from an interactive terminal.
+    Network,        // Accessed via the network.
+    Dialup,         // Accessed as a dialup user to the server.
+    Batch,          // Accessed from a batch job.
+    Anonymous,      // Accessed without any authentication.
+    Authenticated,  // Any authenticated user (opposite of ANONYMOUS).
+    Service;        // Access from a system service.
+
+    public static final Set<SftpUniversalOwnerAndGroup> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(SftpUniversalOwnerAndGroup.class));
+
+    private final String name;
+
+    SftpUniversalOwnerAndGroup() {
+        name = name().toUpperCase() + "@";
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+
+    public static SftpUniversalOwnerAndGroup fromName(String name) {
+        return NamedResource.Utils.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
index b3b52fa..c7aa090 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
@@ -147,7 +147,6 @@ public abstract class Buffer implements Readable {
         // TODO use Long.BYTES for JDK-8
         ensureAvailable(Long.SIZE / Byte.SIZE);
         getRawBytes(workBuf, 0, Long.SIZE / Byte.SIZE);
-        @SuppressWarnings("cast")
         long l = ((long) workBuf[0] << 56) & 0xff00000000000000L;
         l |= ((long) workBuf[1] << 48) & 0x00ff000000000000L;
         l |= ((long) workBuf[2] << 40) & 0x0000ff0000000000L;
@@ -155,7 +154,7 @@ public abstract class Buffer implements Readable {
         l |= ((long) workBuf[4] << 24) & 0x00000000ff000000L;
         l |= ((long) workBuf[5] << 16) & 0x0000000000ff0000L;
         l |= ((long) workBuf[6] << 8) & 0x000000000000ff00L;
-        l |= ((long) workBuf[7]) & 0x00000000000000ffL;
+        l |= (workBuf[7]) & 0x00000000000000ffL;
         return l;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
index c0f261a..ac2df3a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
@@ -25,6 +25,7 @@ import java.io.StreamCorruptedException;
 
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.Int2IntFunction;
+import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 
 /**
@@ -336,6 +337,8 @@ public final class BufferUtils {
         int startPos = lenPos + (Integer.SIZE / Byte.SIZE);
         int endPos = buffer.wpos();
         int dataLength = endPos - startPos;
+        // NOTE: although data length is defined as UINT32, we do not expected sizes above Integer.MAX_VALUE
+        ValidateUtils.checkTrue(dataLength >= 0, "Illegal data length: %d", dataLength);
         buffer.wpos(lenPos);
         buffer.putInt(dataLength);
         buffer.wpos(endPos);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1b5acb71/sshd-core/src/main/java/org/apache/sshd/server/command/UnknownCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/UnknownCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/command/UnknownCommand.java
index 196a7a0..4754525 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/command/UnknownCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/command/UnknownCommand.java
@@ -40,7 +40,9 @@ public class UnknownCommand implements Command {
 
     private final String command;
     private final String message;
+    @SuppressWarnings("unused")
     private InputStream in;
+    @SuppressWarnings("unused")
     private OutputStream out;
     private OutputStream err;
     private ExitCallback callback;