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;