You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2018/04/16 11:48:11 UTC

[24/30] mina-sshd git commit: [SSHD-815] Extract SFTP in its own module

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/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
deleted file mode 100644
index ad6234c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpConstants.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * 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.Map;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.logging.LoggingUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@SuppressWarnings("PMD.AvoidUsingOctalValues")
-public final class SftpConstants {
-    public static final String SFTP_SUBSYSTEM_NAME = "sftp";
-
-    public static final int SSH_FXP_INIT = 1;
-    public static final int SSH_FXP_VERSION = 2;
-    public static final int SSH_FXP_OPEN = 3;
-    public static final int SSH_FXP_CLOSE = 4;
-    public static final int SSH_FXP_READ = 5;
-    public static final int SSH_FXP_WRITE = 6;
-    public static final int SSH_FXP_LSTAT = 7;
-    public static final int SSH_FXP_FSTAT = 8;
-    public static final int SSH_FXP_SETSTAT = 9;
-    public static final int SSH_FXP_FSETSTAT = 10;
-    public static final int SSH_FXP_OPENDIR = 11;
-    public static final int SSH_FXP_READDIR = 12;
-    public static final int SSH_FXP_REMOVE = 13;
-    public static final int SSH_FXP_MKDIR = 14;
-    public static final int SSH_FXP_RMDIR = 15;
-    public static final int SSH_FXP_REALPATH = 16;
-    public static final int SSH_FXP_STAT = 17;
-    public static final int SSH_FXP_RENAME = 18;
-    public static final int SSH_FXP_READLINK = 19;
-    public static final int SSH_FXP_SYMLINK = 20; // v3 -> v5
-    public static final int SSH_FXP_LINK = 21; // v6
-    public static final int SSH_FXP_BLOCK = 22; // v6
-    public static final int SSH_FXP_UNBLOCK = 23; // v6
-    public static final int SSH_FXP_STATUS = 101;
-    public static final int SSH_FXP_HANDLE = 102;
-    public static final int SSH_FXP_DATA = 103;
-    public static final int SSH_FXP_NAME = 104;
-    public static final int SSH_FXP_ATTRS = 105;
-    public static final int SSH_FXP_EXTENDED = 200;
-    public static final int SSH_FXP_EXTENDED_REPLY = 201;
-
-    public static final int SSH_FX_OK = 0;
-    public static final int SSH_FX_EOF = 1;
-    public static final int SSH_FX_NO_SUCH_FILE = 2;
-    public static final int SSH_FX_PERMISSION_DENIED = 3;
-    public static final int SSH_FX_FAILURE = 4;
-    public static final int SSH_FX_BAD_MESSAGE = 5;
-    public static final int SSH_FX_NO_CONNECTION = 6;
-    public static final int SSH_FX_CONNECTION_LOST = 7;
-    public static final int SSH_FX_OP_UNSUPPORTED = 8;
-    public static final int SSH_FX_INVALID_HANDLE = 9;
-    public static final int SSH_FX_NO_SUCH_PATH = 10;
-    public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
-    public static final int SSH_FX_WRITE_PROTECT = 12;
-    public static final int SSH_FX_NO_MEDIA = 13;
-    public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
-    public static final int SSH_FX_QUOTA_EXCEEDED = 15;
-    public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
-    public static final int SSH_FX_LOCK_CONFLICT = 17;
-    public static final int SSH_FX_DIR_NOT_EMPTY = 18;
-    public static final int SSH_FX_NOT_A_DIRECTORY = 19;
-    public static final int SSH_FX_INVALID_FILENAME = 20;
-    public static final int SSH_FX_LINK_LOOP = 21;
-    public static final int SSH_FX_CANNOT_DELETE = 22;
-    public static final int SSH_FX_INVALID_PARAMETER = 23;
-    public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
-    public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
-    public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
-    public static final int SSH_FX_DELETE_PENDING = 27;
-    public static final int SSH_FX_FILE_CORRUPT = 28;
-    public static final int SSH_FX_OWNER_INVALID = 29;
-    public static final int SSH_FX_GROUP_INVALID = 30;
-    public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
-
-    public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
-    public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
-    public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
-    public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; // v3 naming convention
-    public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008; // v4
-    public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010; // v4
-    public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020; // v4
-    public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040; // v4
-    public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080; // v4
-    public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100; // v5
-    public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200; // v5
-    public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400; // v6
-    public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800; // v6
-    public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000; // v6
-    public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000; // v6
-    public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000; // v6
-    public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000; // v6
-    public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
-
-    public static final int SSH_FILEXFER_ATTR_ALL = 0x0000FFFF; // All attributes
-
-    public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200;
-    public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400;
-
-    public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
-    public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
-    public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
-    public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
-    public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
-    public static final int SSH_FILEXFER_TYPE_SOCKET = 6; // v5
-    public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7; // v5
-    public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8; // v5
-    public static final int SSH_FILEXFER_TYPE_FIFO = 9; // v5
-
-    public static final int SSH_FXF_READ = 0x00000001;
-    public static final int SSH_FXF_WRITE = 0x00000002;
-    public static final int SSH_FXF_APPEND = 0x00000004;
-    public static final int SSH_FXF_CREAT = 0x00000008;
-    public static final int SSH_FXF_TRUNC = 0x00000010;
-    public static final int SSH_FXF_EXCL = 0x00000020;
-    public static final int SSH_FXF_TEXT = 0x00000040;
-
-    public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
-    public static final int SSH_FXF_CREATE_NEW = 0x00000000;
-    public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
-    public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
-    public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
-    public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
-    public static final int SSH_FXF_APPEND_DATA = 0x00000008;
-    public static final int SSH_FXF_APPEND_DATA_ATOMIC = 0x00000010;
-    public static final int SSH_FXF_TEXT_MODE = 0x00000020;
-    public static final int SSH_FXF_READ_LOCK = 0x00000040;
-    public static final int SSH_FXF_WRITE_LOCK = 0x00000080;
-    public static final int SSH_FXF_DELETE_LOCK = 0x00000100;
-    public static final int SSH_FXF_BLOCK_ADVISORY = 0x00000200;
-    public static final int SSH_FXF_NOFOLLOW = 0x00000400;
-    public static final int SSH_FXF_DELETE_ON_CLOSE = 0x00000800;
-    public static final int SSH_FXF_ACCESS_AUDIT_ALARM_INFO = 0x00001000;
-    public static final int SSH_FXF_ACCESS_BACKUP = 0x00002000;
-    public static final int SSH_FXF_BACKUP_STREAM = 0x00004000;
-    public static final int SSH_FXF_OVERRIDE_OWNER = 0x00008000;
-
-    public static final int SSH_FXP_RENAME_OVERWRITE = 0x00000001;
-    public static final int SSH_FXP_RENAME_ATOMIC = 0x00000002;
-    public static final int SSH_FXP_RENAME_NATIVE = 0x00000004;
-
-    public static final int SSH_FXP_REALPATH_NO_CHECK = 0x00000001;
-    public static final int SSH_FXP_REALPATH_STAT_IF = 0x00000002;
-    public static final int SSH_FXP_REALPATH_STAT_ALWAYS = 0x00000003;
-
-    public static final int SSH_FXF_RENAME_OVERWRITE = 0x00000001;
-    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;
-    public static final int ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003;
-
-    public static final int ACE4_FILE_INHERIT_ACE = 0x00000001;
-    public static final int ACE4_DIRECTORY_INHERIT_ACE = 0x00000002;
-    public static final int ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004;
-    public static final int ACE4_INHERIT_ONLY_ACE = 0x00000008;
-    public static final int ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010;
-    public static final int ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020;
-    public static final int ACE4_IDENTIFIER_GROUP = 0x00000040;
-
-    public static final int ACE4_READ_DATA = 0x00000001;
-    public static final int ACE4_LIST_DIRECTORY = 0x00000001;
-    public static final int ACE4_WRITE_DATA = 0x00000002;
-    public static final int ACE4_ADD_FILE = 0x00000002;
-    public static final int ACE4_APPEND_DATA = 0x00000004;
-    public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004;
-    public static final int ACE4_READ_NAMED_ATTRS = 0x00000008;
-    public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010;
-    public static final int ACE4_EXECUTE = 0x00000020;
-    public static final int ACE4_DELETE_CHILD = 0x00000040;
-    public static final int ACE4_READ_ATTRIBUTES = 0x00000080;
-    public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100;
-    public static final int ACE4_DELETE = 0x00010000;
-    public static final int ACE4_READ_ACL = 0x00020000;
-    public static final int ACE4_WRITE_ACL = 0x00040000;
-    public static final int ACE4_WRITE_OWNER = 0x00080000;
-    public static final int ACE4_SYNCHRONIZE = 0x00100000;
-
-    public static final int S_IFMT = 0170000;  // bitmask for the file type bitfields
-    public static final int S_IFSOCK = 0140000;  // socket
-    public static final int S_IFLNK = 0120000;  // symbolic link
-    public static final int S_IFREG = 0100000;  // regular file
-    public static final int S_IFBLK = 0060000;  // block device
-    public static final int S_IFDIR = 0040000;  // directory
-    public static final int S_IFCHR = 0020000;  // character device
-    public static final int S_IFIFO = 0010000;  // fifo
-    public static final int S_ISUID = 0004000;  // set UID bit
-    public static final int S_ISGID = 0002000;  // set GID bit
-    public static final int S_ISVTX = 0001000;  // sticky bit
-    public static final int S_IRUSR = 0000400;
-    public static final int S_IWUSR = 0000200;
-    public static final int S_IXUSR = 0000100;
-    public static final int S_IRGRP = 0000040;
-    public static final int S_IWGRP = 0000020;
-    public static final int S_IXGRP = 0000010;
-    public static final int S_IROTH = 0000004;
-    public static final int S_IWOTH = 0000002;
-    public static final int S_IXOTH = 0000001;
-
-    public static final int SFTP_V3 = 3;
-    public static final int SFTP_V4 = 4;
-    public static final int SFTP_V5 = 5;
-    public static final int SFTP_V6 = 6;
-
-    // (Some) names of known extensions
-    public static final String EXT_VERSIONS = "versions";
-    public static final String EXT_NEWLINE = "newline";
-    public static final String EXT_VENDOR_ID = "vendor-id";
-    public static final String EXT_SUPPORTED = "supported";
-    public static final String EXT_SUPPORTED2 = "supported2";
-    public static final String EXT_TEXT_SEEK = "text-seek";
-    public static final String EXT_VERSION_SELECT = "version-select";
-    public static final String EXT_COPY_FILE = "copy-file";
-
-    public static final String EXT_MD5_HASH = "md5-hash";
-    public static final String EXT_MD5_HASH_HANDLE = "md5-hash-handle";
-    public static final int MD5_QUICK_HASH_SIZE = 2048;
-
-    public static final String EXT_CHECK_FILE_HANDLE = "check-file-handle";
-    public static final String EXT_CHECK_FILE_NAME = "check-file-name";
-    public static final int MIN_CHKFILE_BLOCKSIZE = 256;
-
-    public static final String EXT_CHECK_FILE = "check-file";
-    public static final String EXT_COPY_DATA = "copy-data";
-    public static final String EXT_SPACE_AVAILABLE = "space-available";
-
-    // see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-11 section 5.4
-    public static final String EXT_ACL_SUPPORTED = "acl-supported";
-    public static final int SSH_ACL_CAP_ALLOW = 0x00000001;
-    public static final int SSH_ACL_CAP_DENY = 0x00000002;
-    public static final int SSH_ACL_CAP_AUDIT = 0x00000004;
-    public static final int SSH_ACL_CAP_ALARM = 0x00000008;
-    public static final int SSH_ACL_CAP_INHERIT_ACCESS = 0x00000010;
-    public static final int SSH_ACL_CAP_INHERIT_AUDIT_ALARM = 0x00000020;
-
-    private SftpConstants() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    private static class LazyCommandNameHolder {
-        private static final Map<Integer, String> NAMES_MAP =
-                Collections.unmodifiableMap(
-                    LoggingUtils.generateMnemonicMap(SftpConstants.class, f -> {
-                        String name = f.getName();
-                        return name.startsWith("SSH_FXP_")
-                            // exclude the rename modes which are not opcodes
-                            && (!name.startsWith("SSH_FXP_RENAME_"))
-                            // exclude the realpath modes wich are not opcodes
-                            && (!name.startsWith("SSH_FXP_REALPATH_"));
-                    }));
-    }
-
-    /**
-     * Converts a command value to a user-friendly name
-     *
-     * @param cmd The command value
-     * @return The user-friendly name - if not one of the defined {@code SSH_FXP_XXX}
-     * values then returns the string representation of the command's value
-     */
-    public static String getCommandMessageName(int cmd) {
-        @SuppressWarnings("synthetic-access")
-        String name = LazyCommandNameHolder.NAMES_MAP.get(cmd);
-        if (GenericUtils.isEmpty(name)) {
-            return Integer.toString(cmd);
-        } else {
-            return name;
-        }
-    }
-
-    private static class LazyStatusNameHolder {
-        private static final Map<Integer, String> STATUS_MAP =
-                Collections.unmodifiableMap(LoggingUtils.generateMnemonicMap(SftpConstants.class, "SSH_FX_"));
-    }
-
-    /**
-     * Converts a return status value to a user-friendly name
-     *
-     * @param status The status value
-     * @return The user-friendly name - if not one of the defined {@code SSH_FX_XXX}
-     * values then returns the string representation of the status value
-     */
-    public static String getStatusName(int status) {
-        @SuppressWarnings("synthetic-access")
-        String name = LazyStatusNameHolder.STATUS_MAP.get(status);
-        if (GenericUtils.isEmpty(name)) {
-            return Integer.toString(status);
-        } else {
-            return name;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpException.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpException.java
deleted file mode 100644
index b7fd157..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpException.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.io.IOException;
-
-/**
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SftpException extends IOException {
-    private static final long serialVersionUID = 8096963562429466995L;
-    private final int status;
-
-    public SftpException(int status, String msg) {
-        super(msg);
-        this.status = status;
-    }
-
-    public int getStatus() {
-        return status;
-    }
-
-    @Override
-    public String toString() {
-        return "SFTP error (" + SftpConstants.getStatusName(getStatus()) + "): " + getMessage();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/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
deleted file mode 100644
index 5f83818..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
+++ /dev/null
@@ -1,1114 +0,0 @@
-/*
- * 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.io.EOFException;
-import java.io.FileNotFoundException;
-import java.net.UnknownServiceException;
-import java.nio.channels.OverlappingFileLockException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.AccessDeniedException;
-import java.nio.file.DirectoryNotEmptyException;
-import java.nio.file.FileAlreadyExistsException;
-import java.nio.file.FileSystemLoopException;
-import java.nio.file.InvalidPathException;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.NotDirectoryException;
-import java.nio.file.attribute.AclEntry;
-import java.nio.file.attribute.AclEntryFlag;
-import java.nio.file.attribute.AclEntryPermission;
-import java.nio.file.attribute.AclEntryType;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.PosixFilePermissions;
-import java.nio.file.attribute.UserPrincipal;
-import java.nio.file.attribute.UserPrincipalNotFoundException;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-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;
-import org.apache.sshd.server.subsystem.sftp.UnixDateFormat;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class SftpHelper {
-    /**
-     * Used to control whether to append the end-of-list indicator for
-     * SSH_FXP_NAME responses via {@link #indicateEndOfNamesList(Buffer, int, PropertyResolver, Boolean)}
-     * call, as indicated by <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4">SFTP v6 - section 9.4</A>
-     */
-    public static final String APPEND_END_OF_LIST_INDICATOR = "sftp-append-eol-indicator";
-
-    /**
-     * Default value for {@link #APPEND_END_OF_LIST_INDICATOR} if none configured
-     */
-    public static final boolean DEFAULT_APPEND_END_OF_LIST_INDICATOR = true;
-
-    public static final NavigableMap<Integer, String> DEFAULT_SUBSTATUS_MESSAGE =
-        Collections.unmodifiableNavigableMap(new TreeMap<Integer, String>(Comparator.naturalOrder()) {
-            // Not serializing it
-            private static final long serialVersionUID = 1L;
-
-            {
-                put(SftpConstants.SSH_FX_OK, "Success");
-                put(SftpConstants.SSH_FX_EOF, "End of file");
-                put(SftpConstants.SSH_FX_NO_SUCH_FILE, "No such file or directory");
-                put(SftpConstants.SSH_FX_PERMISSION_DENIED, "Permission denied");
-                put(SftpConstants.SSH_FX_FAILURE, "General failure");
-                put(SftpConstants.SSH_FX_BAD_MESSAGE, "Bad message data");
-                put(SftpConstants.SSH_FX_NO_CONNECTION, "No connection to server");
-                put(SftpConstants.SSH_FX_CONNECTION_LOST, "Connection lost");
-                put(SftpConstants.SSH_FX_OP_UNSUPPORTED, "Unsupported operation requested");
-                put(SftpConstants.SSH_FX_INVALID_HANDLE, "Invalid handle value");
-                put(SftpConstants.SSH_FX_NO_SUCH_PATH, "No such path");
-                put(SftpConstants.SSH_FX_FILE_ALREADY_EXISTS, "File/Directory already exists");
-                put(SftpConstants.SSH_FX_WRITE_PROTECT, "File/Directory is write-protected");
-                put(SftpConstants.SSH_FX_NO_MEDIA, "No such meadia");
-                put(SftpConstants.SSH_FX_NO_SPACE_ON_FILESYSTEM, "No space left on device");
-                put(SftpConstants.SSH_FX_QUOTA_EXCEEDED, "Quota exceeded");
-                put(SftpConstants.SSH_FX_UNKNOWN_PRINCIPAL, "Unknown user/group");
-                put(SftpConstants.SSH_FX_LOCK_CONFLICT, "Lock conflict");
-                put(SftpConstants.SSH_FX_DIR_NOT_EMPTY, "Directory not empty");
-                put(SftpConstants.SSH_FX_NOT_A_DIRECTORY, "Accessed location is not a directory");
-                put(SftpConstants.SSH_FX_INVALID_FILENAME, "Invalid filename");
-                put(SftpConstants.SSH_FX_LINK_LOOP, "Link loop");
-                put(SftpConstants.SSH_FX_CANNOT_DELETE, "Cannot remove");
-                put(SftpConstants.SSH_FX_INVALID_PARAMETER, "Invalid parameter");
-                put(SftpConstants.SSH_FX_FILE_IS_A_DIRECTORY, "Accessed location is a directory");
-                put(SftpConstants.SSH_FX_BYTE_RANGE_LOCK_CONFLICT, "Range lock conflict");
-                put(SftpConstants.SSH_FX_BYTE_RANGE_LOCK_REFUSED, "Range lock refused");
-                put(SftpConstants.SSH_FX_DELETE_PENDING, "Delete pending");
-                put(SftpConstants.SSH_FX_FILE_CORRUPT, "Corrupted file/directory");
-                put(SftpConstants.SSH_FX_OWNER_INVALID, "Invalid file/directory owner");
-                put(SftpConstants.SSH_FX_GROUP_INVALID, "Invalid file/directory group");
-                put(SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK, "No matching byte range lock");
-            }
-        });
-
-    private SftpHelper() {
-        throw new UnsupportedOperationException("No instance allowed");
-    }
-
-    /**
-     * Retrieves the end-of-file indicator for {@code SSH_FXP_DATA} responses, provided
-     * the version is at least 6, and the buffer has enough available data
-     *
-     * @param buffer  The {@link Buffer} to retrieve the data from
-     * @param version The SFTP version being used
-     * @return The indicator value - {@code null} if none retrieved
-     * @see <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.3">SFTP v6 - section 9.3</A>
-     */
-    public static Boolean getEndOfFileIndicatorValue(Buffer buffer, int version) {
-        return (version <  SftpConstants.SFTP_V6) || (buffer.available() < 1) ? null : buffer.getBoolean();
-    }
-
-    /**
-     * Retrieves the end-of-list indicator for {@code SSH_FXP_NAME} responses, provided
-     * the version is at least 6, and the buffer has enough available data
-     *
-     * @param buffer  The {@link Buffer} to retrieve the data from
-     * @param version The SFTP version being used
-     * @return The indicator value - {@code null} if none retrieved
-     * @see <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4">SFTP v6 - section 9.4</A>
-     * @see #indicateEndOfNamesList(Buffer, int, PropertyResolver, Boolean)
-     */
-    public static Boolean getEndOfListIndicatorValue(Buffer buffer, int version) {
-        return (version <  SftpConstants.SFTP_V6) || (buffer.available() < 1) ? null : buffer.getBoolean();
-    }
-
-    /**
-     * Appends the end-of-list={@code TRUE} indicator for {@code SSH_FXP_NAME} responses, provided
-     * the version is at least 6 and the feature is enabled
-     *
-     * @param buffer   The {@link Buffer} to append the indicator
-     * @param version  The SFTP version being used
-     * @param resolver The {@link PropertyResolver} to query whether to enable the feature
-     * @return The actual indicator value used - {@code null} if none appended
-     * @see #indicateEndOfNamesList(Buffer, int, PropertyResolver, Boolean)
-     */
-    public static Boolean indicateEndOfNamesList(Buffer buffer, int version, PropertyResolver resolver) {
-        return indicateEndOfNamesList(buffer, version, resolver, Boolean.TRUE);
-    }
-
-    /**
-     * Appends the end-of-list indicator for {@code SSH_FXP_NAME} responses, provided the version
-     * is at least 6, the feature is enabled and the indicator value is not {@code null}
-     *
-     * @param buffer         The {@link Buffer} to append the indicator
-     * @param version        The SFTP version being used
-     * @param resolver       The {@link PropertyResolver} to query whether to enable the feature
-     * @param indicatorValue The indicator value - {@code null} means don't append the indicator
-     * @return The actual indicator value used - {@code null} if none appended
-     * @see <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4">SFTP v6 - section 9.4</A>
-     * @see #APPEND_END_OF_LIST_INDICATOR
-     * @see #DEFAULT_APPEND_END_OF_LIST_INDICATOR
-     */
-    public static Boolean indicateEndOfNamesList(Buffer buffer, int version, PropertyResolver resolver, Boolean indicatorValue) {
-        if ((version < SftpConstants.SFTP_V6) || (indicatorValue == null)) {
-            return null;
-        }
-
-        if (!resolver.getBooleanProperty(APPEND_END_OF_LIST_INDICATOR, DEFAULT_APPEND_END_OF_LIST_INDICATOR)) {
-            return null;
-        }
-
-        buffer.putBoolean(indicatorValue);
-        return indicatorValue;
-    }
-
-    /**
-     * Writes a file / folder's attributes to a buffer
-     *
-     * @param <B> Type of {@link Buffer} being updated
-     * @param buffer The target buffer instance
-     * @param version The output encoding version
-     * @param attributes The {@link Map} of attributes
-     * @return The updated buffer
-     * @see #writeAttrsV3(Buffer, int, Map)
-     * @see #writeAttrsV4(Buffer, int, Map)
-     */
-    public static <B extends Buffer> B writeAttrs(B buffer, int version, Map<String, ?> attributes) {
-        if (version == SftpConstants.SFTP_V3) {
-            return writeAttrsV3(buffer, version, attributes);
-        } else if (version >= SftpConstants.SFTP_V4) {
-            return writeAttrsV4(buffer, version, attributes);
-        } else {
-            throw new IllegalStateException("Unsupported SFTP version: " + version);
-        }
-    }
-
-    /**
-     * Writes the retrieved file / directory attributes in V3 format
-     *
-     * @param <B> Type of {@link Buffer} being updated
-     * @param buffer The target buffer instance
-     * @param version The actual version - must be {@link SftpConstants#SFTP_V3}
-     * @param attributes The {@link Map} of attributes
-     * @return The updated buffer
-     */
-    public static <B extends Buffer> B writeAttrsV3(B buffer, int version, Map<String, ?> attributes) {
-        ValidateUtils.checkTrue(version == SftpConstants.SFTP_V3, "Illegal version: %d", version);
-
-        boolean isReg = getBool((Boolean) attributes.get("isRegularFile"));
-        boolean isDir = getBool((Boolean) attributes.get("isDirectory"));
-        boolean isLnk = getBool((Boolean) attributes.get("isSymbolicLink"));
-        @SuppressWarnings("unchecked")
-        Collection<PosixFilePermission> perms = (Collection<PosixFilePermission>) attributes.get("permissions");
-        Number size = (Number) attributes.get("size");
-        FileTime lastModifiedTime = (FileTime) attributes.get("lastModifiedTime");
-        FileTime lastAccessTime = (FileTime) attributes.get("lastAccessTime");
-        Map<?, ?> extensions = (Map<?, ?>) attributes.get("extended");
-        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)
-                  | (((lastModifiedTime != null) && (lastAccessTime != null)) ? SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME : 0)
-                  | ((extensions != null) ? SftpConstants.SSH_FILEXFER_ATTR_EXTENDED : 0);
-        buffer.putInt(flags);
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
-            buffer.putLong(size.longValue());
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UIDGID) != 0) {
-            buffer.putInt(((Number) attributes.get("uid")).intValue());
-            buffer.putInt(((Number) attributes.get("gid")).intValue());
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
-            buffer.putInt(attributesToPermissions(isReg, isDir, isLnk, perms));
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
-            buffer = writeTime(buffer, version, flags, lastAccessTime);
-            buffer = writeTime(buffer, version, flags, lastModifiedTime);
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_EXTENDED) != 0) {
-            buffer = writeExtensions(buffer, extensions);
-        }
-
-        return buffer;
-    }
-
-    /**
-     * Writes the retrieved file / directory attributes in V4+ format
-     *
-     * @param <B> Type of {@link Buffer} being updated
-     * @param buffer The target buffer instance
-     * @param version The actual version - must be at least {@link SftpConstants#SFTP_V4}
-     * @param attributes The {@link Map} of attributes
-     * @return The updated buffer
-     */
-    public static <B extends Buffer> B writeAttrsV4(B buffer, int version, Map<String, ?> attributes) {
-        ValidateUtils.checkTrue(version >= SftpConstants.SFTP_V4, "Illegal version: %d", version);
-
-        boolean isReg = getBool((Boolean) attributes.get("isRegularFile"));
-        boolean isDir = getBool((Boolean) attributes.get("isDirectory"));
-        boolean isLnk = getBool((Boolean) attributes.get("isSymbolicLink"));
-        @SuppressWarnings("unchecked")
-        Collection<PosixFilePermission> perms = (Collection<PosixFilePermission>) attributes.get("permissions");
-        Number size = (Number) attributes.get("size");
-        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");
-        Map<?, ?> extensions = (Map<?, ?>) attributes.get("extended");
-        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)
-                  | ((acl != null) ? SftpConstants.SSH_FILEXFER_ATTR_ACL : 0)
-                  | ((extensions != null) ? SftpConstants.SSH_FILEXFER_ATTR_EXTENDED : 0);
-        buffer.putInt(flags);
-        buffer.putByte((byte) (isReg ? SftpConstants.SSH_FILEXFER_TYPE_REGULAR
-                : isDir ? SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY
-                : isLnk ? SftpConstants.SSH_FILEXFER_TYPE_SYMLINK
-                : SftpConstants.SSH_FILEXFER_TYPE_UNKNOWN));
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
-            buffer.putLong(size.longValue());
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
-            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));
-        }
-
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
-            buffer = writeTime(buffer, version, flags, lastAccessTime);
-        }
-
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
-            buffer = writeTime(buffer, version, flags, lastAccessTime);
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
-            buffer = writeTime(buffer, version, flags, lastModifiedTime);
-        }
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACL) != 0) {
-            buffer = writeACLs(buffer, version, acl);
-        }
-        // TODO: ctime
-        // TODO: bits
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_EXTENDED) != 0) {
-            buffer = writeExtensions(buffer, extensions);
-        }
-
-        return buffer;
-    }
-
-    /**
-     * @param bool The {@link Boolean} value
-     * @return {@code true} it the argument is non-{@code null} and
-     * its {@link Boolean#booleanValue()} is {@code true}
-     */
-    public static boolean getBool(Boolean bool) {
-        return bool != null && bool;
-    }
-
-    /**
-     * Converts a file / folder's attributes into a mask
-     *
-     * @param isReg {@code true} if this is a normal file
-     * @param isDir {@code true} if this is a directory
-     * @param isLnk {@code true} if this is a symbolic link
-     * @param perms The file / folder's access {@link PosixFilePermission}s
-     * @return A mask encoding the file / folder's attributes
-     */
-    public static int attributesToPermissions(boolean isReg, boolean isDir, boolean isLnk, Collection<PosixFilePermission> perms) {
-        int pf = 0;
-        if (perms != null) {
-            for (PosixFilePermission p : perms) {
-                switch (p) {
-                    case OWNER_READ:
-                        pf |= SftpConstants.S_IRUSR;
-                        break;
-                    case OWNER_WRITE:
-                        pf |= SftpConstants.S_IWUSR;
-                        break;
-                    case OWNER_EXECUTE:
-                        pf |= SftpConstants.S_IXUSR;
-                        break;
-                    case GROUP_READ:
-                        pf |= SftpConstants.S_IRGRP;
-                        break;
-                    case GROUP_WRITE:
-                        pf |= SftpConstants.S_IWGRP;
-                        break;
-                    case GROUP_EXECUTE:
-                        pf |= SftpConstants.S_IXGRP;
-                        break;
-                    case OTHERS_READ:
-                        pf |= SftpConstants.S_IROTH;
-                        break;
-                    case OTHERS_WRITE:
-                        pf |= SftpConstants.S_IWOTH;
-                        break;
-                    case OTHERS_EXECUTE:
-                        pf |= SftpConstants.S_IXOTH;
-                        break;
-                    default: // ignored
-                }
-            }
-        }
-        pf |= isReg ? SftpConstants.S_IFREG : 0;
-        pf |= isDir ? SftpConstants.S_IFDIR : 0;
-        pf |= isLnk ? SftpConstants.S_IFLNK : 0;
-        return pf;
-    }
-
-    /**
-     * 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
-     * @return A {@link Set} of the equivalent {@link PosixFilePermission}s
-     */
-    public static Set<PosixFilePermission> permissionsToAttributes(int perms) {
-        Set<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class);
-        if ((perms & SftpConstants.S_IRUSR) != 0) {
-            p.add(PosixFilePermission.OWNER_READ);
-        }
-        if ((perms & SftpConstants.S_IWUSR) != 0) {
-            p.add(PosixFilePermission.OWNER_WRITE);
-        }
-        if ((perms & SftpConstants.S_IXUSR) != 0) {
-            p.add(PosixFilePermission.OWNER_EXECUTE);
-        }
-        if ((perms & SftpConstants.S_IRGRP) != 0) {
-            p.add(PosixFilePermission.GROUP_READ);
-        }
-        if ((perms & SftpConstants.S_IWGRP) != 0) {
-            p.add(PosixFilePermission.GROUP_WRITE);
-        }
-        if ((perms & SftpConstants.S_IXGRP) != 0) {
-            p.add(PosixFilePermission.GROUP_EXECUTE);
-        }
-        if ((perms & SftpConstants.S_IROTH) != 0) {
-            p.add(PosixFilePermission.OTHERS_READ);
-        }
-        if ((perms & SftpConstants.S_IWOTH) != 0) {
-            p.add(PosixFilePermission.OTHERS_WRITE);
-        }
-        if ((perms & SftpConstants.S_IXOTH) != 0) {
-            p.add(PosixFilePermission.OTHERS_EXECUTE);
-        }
-        return p;
-    }
-
-    /**
-     * Returns the most adequate sub-status for the provided exception
-     *
-     * @param t The thrown {@link Throwable}
-     * @return The matching sub-status
-     */
-    @SuppressWarnings("checkstyle:ReturnCount")
-    public static int resolveSubstatus(Throwable t) {
-        if ((t instanceof NoSuchFileException) || (t instanceof FileNotFoundException)) {
-            return SftpConstants.SSH_FX_NO_SUCH_FILE;
-        } else if (t instanceof InvalidHandleException) {
-            return SftpConstants.SSH_FX_INVALID_HANDLE;
-        } else if (t instanceof FileAlreadyExistsException) {
-            return SftpConstants.SSH_FX_FILE_ALREADY_EXISTS;
-        } else if (t instanceof DirectoryNotEmptyException) {
-            return SftpConstants.SSH_FX_DIR_NOT_EMPTY;
-        } else if (t instanceof NotDirectoryException) {
-            return SftpConstants.SSH_FX_NOT_A_DIRECTORY;
-        } else if (t instanceof AccessDeniedException) {
-            return SftpConstants.SSH_FX_PERMISSION_DENIED;
-        } else if (t instanceof EOFException) {
-            return SftpConstants.SSH_FX_EOF;
-        } else if (t instanceof OverlappingFileLockException) {
-            return SftpConstants.SSH_FX_LOCK_CONFLICT;
-        } else if ((t instanceof UnsupportedOperationException)
-                || (t instanceof UnknownServiceException)) {
-            return SftpConstants.SSH_FX_OP_UNSUPPORTED;
-        } else if (t instanceof InvalidPathException) {
-            return SftpConstants.SSH_FX_INVALID_FILENAME;
-        } else if (t instanceof IllegalArgumentException) {
-            return SftpConstants.SSH_FX_INVALID_PARAMETER;
-        } else if (t instanceof UserPrincipalNotFoundException) {
-            return SftpConstants.SSH_FX_UNKNOWN_PRINCIPAL;
-        } else if (t instanceof FileSystemLoopException) {
-            return SftpConstants.SSH_FX_LINK_LOOP;
-        } else if (t instanceof SftpException) {
-            return ((SftpException) t).getStatus();
-        } else {
-            return SftpConstants.SSH_FX_FAILURE;
-        }
-    }
-
-    public static String resolveStatusMessage(int subStatus) {
-        String message = DEFAULT_SUBSTATUS_MESSAGE.get(subStatus);
-        return GenericUtils.isEmpty(message) ? ("Unknown error: " + subStatus) : message;
-    }
-
-    public static NavigableMap<String, Object> readAttrs(Buffer buffer, int version) {
-        NavigableMap<String, Object> attrs = new TreeMap<>(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_SOCKET:
-                case SftpConstants.SSH_FILEXFER_TYPE_CHAR_DEVICE:
-                case SftpConstants.SSH_FILEXFER_TYPE_BLOCK_DEVICE:
-                case SftpConstants.SSH_FILEXFER_TYPE_FIFO:
-                    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
-                }
-            }
-        }
-
-        if ((flags & SftpConstants.SSH_FILEXFER_ATTR_EXTENDED) != 0) {
-            attrs.put("extended", readExtensions(buffer));
-        }
-
-        return attrs;
-    }
-
-    public static NavigableMap<String, byte[]> readExtensions(Buffer buffer) {
-        int count = buffer.getInt();
-        // NOTE
-        NavigableMap<String, byte[]> extended = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        for (int i = 0; i < count; i++) {
-            String key = buffer.getString();
-            byte[] val = buffer.getBytes();
-            byte[] prev = extended.put(key, val);
-            ValidateUtils.checkTrue(prev == null, "Duplicate values for extended key=%s", key);
-        }
-
-        return extended;
-    }
-
-    public static  <B extends Buffer> B writeExtensions(B buffer, Map<?, ?> extensions) {
-        int numExtensions = GenericUtils.size(extensions);
-        buffer.putInt(numExtensions);
-        if (numExtensions <= 0) {
-            return buffer;
-        }
-
-        extensions.forEach((key, value) -> {
-            Objects.requireNonNull(key, "No extension type");
-            Objects.requireNonNull(value, "No extension value");
-            buffer.putString(key.toString());
-            if (value instanceof byte[]) {
-                buffer.putBytes((byte[]) value);
-            } else {
-                buffer.putString(value.toString());
-            }
-        });
-
-        return buffer;
-    }
-
-    public static NavigableMap<String, String> toStringExtensions(Map<String, ?> extensions) {
-        if (GenericUtils.isEmpty(extensions)) {
-            return Collections.emptyNavigableMap();
-        }
-
-        // NOTE: even though extensions are probably case sensitive we do not allow duplicate name that differs only in case
-        NavigableMap<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        extensions.forEach((key, value) -> {
-            ValidateUtils.checkNotNull(value, "No value for extension=%s", key);
-            String prev = map.put(key, (value instanceof byte[]) ? new String((byte[]) value, StandardCharsets.UTF_8) : value.toString());
-            ValidateUtils.checkTrue(prev == null, "Multiple values for extension=%s", key);
-        });
-
-        return map;
-    }
-
-    public static NavigableMap<String, byte[]> toBinaryExtensions(Map<String, String> extensions) {
-        if (GenericUtils.isEmpty(extensions)) {
-            return Collections.emptyNavigableMap();
-        }
-
-        // NOTE: even though extensions are probably case sensitive we do not allow duplicate name that differs only in case
-        NavigableMap<String, byte[]> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        extensions.forEach((key, value) -> {
-            ValidateUtils.checkNotNull(value, "No value for extension=%s", key);
-            byte[] prev = map.put(key, value.getBytes(StandardCharsets.UTF_8));
-            ValidateUtils.checkTrue(prev == null, "Multiple values for extension=%s", key);
-        });
-
-        return map;
-    }
-
-    // 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:
-                return AclEntryType.ALLOW;
-            case SftpConstants.ACE4_ACCESS_DENIED_ACE_TYPE:
-                return AclEntryType.DENY;
-            case SftpConstants.ACE4_SYSTEM_AUDIT_ACE_TYPE:
-                return AclEntryType.AUDIT;
-            case SftpConstants.ACE4_SYSTEM_ALARM_ACE_TYPE:
-                return AclEntryType.ALARM;
-            default:
-                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);
-        }
-        if ((aclFlag & SftpConstants.ACE4_DIRECTORY_INHERIT_ACE) != 0) {
-            flags.add(AclEntryFlag.DIRECTORY_INHERIT);
-        }
-        if ((aclFlag & SftpConstants.ACE4_NO_PROPAGATE_INHERIT_ACE) != 0) {
-            flags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
-        }
-        if ((aclFlag & SftpConstants.ACE4_INHERIT_ONLY_ACE) != 0) {
-            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);
-        }
-        if ((aclMask & SftpConstants.ACE4_LIST_DIRECTORY) != 0) {
-            mask.add(AclEntryPermission.LIST_DIRECTORY);
-        }
-        if ((aclMask & SftpConstants.ACE4_WRITE_DATA) != 0) {
-            mask.add(AclEntryPermission.WRITE_DATA);
-        }
-        if ((aclMask & SftpConstants.ACE4_ADD_FILE) != 0) {
-            mask.add(AclEntryPermission.ADD_FILE);
-        }
-        if ((aclMask & SftpConstants.ACE4_APPEND_DATA) != 0) {
-            mask.add(AclEntryPermission.APPEND_DATA);
-        }
-        if ((aclMask & SftpConstants.ACE4_ADD_SUBDIRECTORY) != 0) {
-            mask.add(AclEntryPermission.ADD_SUBDIRECTORY);
-        }
-        if ((aclMask & SftpConstants.ACE4_READ_NAMED_ATTRS) != 0) {
-            mask.add(AclEntryPermission.READ_NAMED_ATTRS);
-        }
-        if ((aclMask & SftpConstants.ACE4_WRITE_NAMED_ATTRS) != 0) {
-            mask.add(AclEntryPermission.WRITE_NAMED_ATTRS);
-        }
-        if ((aclMask & SftpConstants.ACE4_EXECUTE) != 0) {
-            mask.add(AclEntryPermission.EXECUTE);
-        }
-        if ((aclMask & SftpConstants.ACE4_DELETE_CHILD) != 0) {
-            mask.add(AclEntryPermission.DELETE_CHILD);
-        }
-        if ((aclMask & SftpConstants.ACE4_READ_ATTRIBUTES) != 0) {
-            mask.add(AclEntryPermission.READ_ATTRIBUTES);
-        }
-        if ((aclMask & SftpConstants.ACE4_WRITE_ATTRIBUTES) != 0) {
-            mask.add(AclEntryPermission.WRITE_ATTRIBUTES);
-        }
-        if ((aclMask & SftpConstants.ACE4_DELETE) != 0) {
-            mask.add(AclEntryPermission.DELETE);
-        }
-        if ((aclMask & SftpConstants.ACE4_READ_ACL) != 0) {
-            mask.add(AclEntryPermission.READ_ACL);
-        }
-        if ((aclMask & SftpConstants.ACE4_WRITE_ACL) != 0) {
-            mask.add(AclEntryPermission.WRITE_ACL);
-        }
-        if ((aclMask & SftpConstants.ACE4_WRITE_OWNER) != 0) {
-            mask.add(AclEntryPermission.WRITE_OWNER);
-        }
-        if ((aclMask & SftpConstants.ACE4_SYNCHRONIZE) != 0) {
-            mask.add(AclEntryPermission.SYNCHRONIZE);
-        }
-
-        return mask;
-    }
-
-    public static <B extends Buffer> B writeACLs(B buffer, int version, Collection<? extends AclEntry> acl) {
-        int lenPos = buffer.wpos();
-        buffer.putInt(0);   // length placeholder
-        buffer = encodeACLs(buffer, version, acl);
-        BufferUtils.updateLengthPlaceholder(buffer, lenPos);
-        return buffer;
-    }
-
-    public static <B extends Buffer> B encodeACLs(B buffer, int version, Collection<? extends AclEntry> acl) {
-        Objects.requireNonNull(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) {
-                buffer = writeAclEntry(buffer, e);
-            }
-        }
-
-        return buffer;
-    }
-
-    public static <B extends Buffer> B writeAclEntry(B buffer, AclEntry acl) {
-        Objects.requireNonNull(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());
-        return buffer;
-    }
-
-    /**
-     * 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;
-        }
-
-        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;
-        }
-    }
-
-    public static long encodeAclFlags(Collection<AclEntryFlag> flags) {
-        if (GenericUtils.isEmpty(flags)) {
-            return 0L;
-        }
-
-        long aclFlag = 0L;
-        if (flags.contains(AclEntryFlag.FILE_INHERIT)) {
-            aclFlag |= SftpConstants.ACE4_FILE_INHERIT_ACE;
-        }
-        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;
-        }
-
-        return aclFlag;
-    }
-
-    public static long encodeAclMask(Collection<AclEntryPermission> mask) {
-        if (GenericUtils.isEmpty(mask)) {
-            return 0L;
-        }
-
-        long aclMask = 0L;
-        if (mask.contains(AclEntryPermission.READ_DATA)) {
-            aclMask |= SftpConstants.ACE4_READ_DATA;
-        }
-        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 aclMask;
-    }
-
-    /**
-     * Encodes a {@link FileTime} value into a buffer
-     *
-     * @param <B> Type of {@link Buffer} being updated
-     * @param buffer The target buffer instance
-     * @param version The encoding version
-     * @param flags The encoding flags
-     * @param time The value to encode
-     * @return The updated buffer
-     */
-    public static <B extends Buffer> B writeTime(B 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) {
-                long nanos = time.to(TimeUnit.NANOSECONDS);
-                nanos = nanos % TimeUnit.SECONDS.toNanos(1);
-                buffer.putInt((int) nanos);
-            }
-        } else {
-            buffer.putInt(time.to(TimeUnit.SECONDS));
-        }
-
-        return buffer;
-    }
-
-    /**
-     * Decodes a {@link FileTime} value from a buffer
-     *
-     * @param buffer The source {@link Buffer}
-     * @param version The encoding version
-     * @param flags The encoding flags
-     * @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)) {
-            long nanoseconds = buffer.getUInt();
-            millis += TimeUnit.NANOSECONDS.toMillis(nanoseconds);
-        }
-        return FileTime.from(millis, TimeUnit.MILLISECONDS);
-    }
-
-    /**
-     * Creates an &quot;ls -l&quot; compatible long name string
-     *
-     * @param shortName The short file name - can also be &quot;.&quot; or &quot;..&quot;
-     * @param attributes The file's attributes - e.g., size, owner, permissions, etc.
-     * @return A {@link String} representing the &quot;long&quot; file name as per
-     * <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02">SFTP version 3 - section 7</A>
-     */
-    public static String getLongName(String shortName, Map<String, ?> attributes) {
-        String owner = Objects.toString(attributes.get("owner"), null);
-        String username = OsUtils.getCanonicalUser(owner);
-        if (GenericUtils.isEmpty(username)) {
-            username = SftpUniversalOwnerAndGroup.Owner.getName();
-        }
-
-        String group = Objects.toString(attributes.get("group"), null);
-        group = OsUtils.resolveCanonicalGroup(group, owner);
-        if (GenericUtils.isEmpty(group)) {
-            group = SftpUniversalOwnerAndGroup.Group.getName();
-        }
-
-        Number length = (Number) attributes.get("size");
-        if (length == null) {
-            length = 0L;
-        }
-
-        String lengthString = String.format("%1$8s", length);
-        String linkCount = Objects.toString(attributes.get("nlink"), null);
-        if (GenericUtils.isEmpty(linkCount)) {
-            linkCount = "1";
-        }
-
-        Boolean isDirectory = (Boolean) attributes.get("isDirectory");
-        Boolean isLink = (Boolean) attributes.get("isSymbolicLink");
-        @SuppressWarnings("unchecked")
-        Set<PosixFilePermission> perms = (Set<PosixFilePermission>) attributes.get("permissions");
-        if (perms == null) {
-            perms = EnumSet.noneOf(PosixFilePermission.class);
-        }
-        String permsString = PosixFilePermissions.toString(perms);
-        String timeStamp = UnixDateFormat.getUnixDate((FileTime) attributes.get("lastModifiedTime"));
-        StringBuilder sb = new StringBuilder(
-                GenericUtils.length(linkCount) + GenericUtils.length(username) + GenericUtils.length(group)
-              + GenericUtils.length(timeStamp) + GenericUtils.length(lengthString)
-              + GenericUtils.length(permsString) + GenericUtils.length(shortName)
-              + Integer.SIZE);
-        sb.append(SftpHelper.getBool(isDirectory) ? 'd' : (SftpHelper.getBool(isLink) ? 'l' : '-')).append(permsString);
-
-        sb.append(' ');
-        for (int index = linkCount.length(); index < 3; index++) {
-            sb.append(' ');
-        }
-        sb.append(linkCount);
-
-        sb.append(' ').append(username);
-        for (int index = username.length(); index < 8; index++) {
-            sb.append(' ');
-        }
-
-        sb.append(' ').append(group);
-        for (int index = group.length(); index < 8; index++) {
-            sb.append(' ');
-        }
-
-        sb.append(' ').append(lengthString).append(' ').append(timeStamp).append(' ').append(shortName);
-        return sb.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/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
deleted file mode 100644
index 68ab055..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroup.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java
deleted file mode 100644
index 15cb3fe..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.extensions;
-
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @param <T> Parse result type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractParser<T> implements ExtensionParser<T> {
-    private final String name;
-
-    protected AbstractParser(String name) {
-        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No extension name");
-    }
-
-    @Override
-    public final String getName() {
-        return name;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java
deleted file mode 100644
index ea2d12d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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.extensions;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.apache.sshd.common.subsystem.sftp.SftpConstants;
-import org.apache.sshd.common.subsystem.sftp.extensions.AclSupportedParser.AclCapabilities;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.logging.LoggingUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class AclSupportedParser extends AbstractParser<AclCapabilities> {
-    /**
-     * The &quot;acl-supported&quot; information as per
-     * <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-11">DRAFT 11 - section 5.4</A>
-     *
-     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
-     */
-    public static class AclCapabilities implements Serializable, Cloneable {
-        private static final long serialVersionUID = -3118426327336468237L;
-        private int capabilities;
-
-        public AclCapabilities() {
-            this(0);
-        }
-
-        public AclCapabilities(int capabilities) {
-            this.capabilities = capabilities;
-        }
-
-        public int getCapabilities() {
-            return capabilities;
-        }
-
-        public void setCapabilities(int capabilities) {
-            this.capabilities = capabilities;
-        }
-
-        @Override
-        public int hashCode() {
-            return getCapabilities();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == null) {
-                return false;
-            }
-            if (obj == this) {
-                return true;
-            }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-
-            return ((AclCapabilities) obj).getCapabilities() == getCapabilities();
-        }
-
-        @Override
-        public AclCapabilities clone() {
-            try {
-                return getClass().cast(super.clone());
-            } catch (CloneNotSupportedException e) {
-                throw new RuntimeException("Failed to clone " + toString() + ": " + e.getMessage(), e);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return Objects.toString(decodeAclCapabilities(getCapabilities()));
-        }
-
-        private static class LazyAclCapabilityNameHolder {
-            private static final String ACL_CAP_NAME_PREFIX = "SSH_ACL_CAP_";
-            private static final Map<Integer, String> ACL_VALUES_MAP = LoggingUtils.generateMnemonicMap(SftpConstants.class, ACL_CAP_NAME_PREFIX);
-            private static final Map<String, Integer> ACL_NAMES_MAP =
-                    Collections.unmodifiableMap(GenericUtils.flipMap(ACL_VALUES_MAP, GenericUtils.caseInsensitiveMap(), false));
-        }
-
-        @SuppressWarnings("synthetic-access")
-        public static Map<String, Integer> getAclCapabilityNamesMap() {
-            return LazyAclCapabilityNameHolder.ACL_NAMES_MAP;
-        }
-
-        /**
-         * @param name The ACL capability name - may be without the &quot;SSH_ACL_CAP_xxx&quot; prefix.
-         * Ignored if {@code null}/empty
-         * @return The matching {@link Integer} value - or {@code null} if no match found
-         */
-        public static Integer getAclCapabilityValue(String name) {
-            if (GenericUtils.isEmpty(name)) {
-                return null;
-            }
-
-            name = name.toUpperCase();
-            if (!name.startsWith(LazyAclCapabilityNameHolder.ACL_CAP_NAME_PREFIX)) {
-                name += LazyAclCapabilityNameHolder.ACL_CAP_NAME_PREFIX;
-            }
-
-            Map<String, Integer> map = getAclCapabilityNamesMap();
-            return map.get(name);
-        }
-
-        @SuppressWarnings("synthetic-access")
-        public static Map<Integer, String> getAclCapabilityValuesMap() {
-            return LazyAclCapabilityNameHolder.ACL_VALUES_MAP;
-        }
-
-        public static String getAclCapabilityName(int aclCapValue) {
-            Map<Integer, String> map = getAclCapabilityValuesMap();
-            String name = map.get(aclCapValue);
-            if (GenericUtils.isEmpty(name)) {
-                return Integer.toString(aclCapValue);
-            } else {
-                return name;
-            }
-        }
-
-        public static Set<String> decodeAclCapabilities(int mask) {
-            if (mask == 0) {
-                return Collections.emptySet();
-            }
-
-            Set<String> caps = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
-            Map<Integer, String> map = getAclCapabilityValuesMap();
-            map.forEach((value, name) -> {
-                if ((mask & value) != 0) {
-                    caps.add(name);
-                }
-            });
-
-            return caps;
-        }
-
-        public static int constructAclCapabilities(Collection<Integer> maskValues) {
-            if (GenericUtils.isEmpty(maskValues)) {
-                return 0;
-            }
-
-            int mask = 0;
-            for (Integer v : maskValues) {
-                mask |= v;
-            }
-
-            return mask;
-        }
-
-        public static Set<Integer> deconstructAclCapabilities(int mask) {
-            if (mask == 0) {
-                return Collections.emptySet();
-            }
-
-            Map<Integer, String> map = getAclCapabilityValuesMap();
-            Set<Integer> caps = new HashSet<>(map.size());
-            for (Integer v : map.keySet()) {
-                if ((mask & v) != 0) {
-                    caps.add(v);
-                }
-            }
-
-            return caps;
-        }
-    }
-
-    public static final AclSupportedParser INSTANCE = new AclSupportedParser();
-
-    public AclSupportedParser() {
-        super(SftpConstants.EXT_ACL_SUPPORTED);
-    }
-
-    @Override
-    public AclCapabilities parse(byte[] input, int offset, int len) {
-        return parse(new ByteArrayBuffer(input, offset, len));
-    }
-
-    public AclCapabilities parse(Buffer buffer) {
-        return new AclCapabilities(buffer.getInt());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ExtensionParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ExtensionParser.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ExtensionParser.java
deleted file mode 100644
index cd74a26..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ExtensionParser.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.extensions;
-
-import java.util.function.Function;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.NumberUtils;
-
-/**
- * @param <T> Result type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface ExtensionParser<T> extends NamedResource, Function<byte[], T> {
-    default T parse(byte[] input) {
-        return parse(input, 0, NumberUtils.length(input));
-    }
-
-    T parse(byte[] input, int offset, int len);
-
-    @Override
-    default T apply(byte[] input) {
-        return parse(input);
-    }
-}