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/02 06:25:52 UTC
[2/3] mina-sshd git commit: [SSHD-572] Invalid handling of set time
values for SFTP version 3
[SSHD-572] Invalid handling of set time values for SFTP version 3
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/beffd2aa
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/beffd2aa
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/beffd2aa
Branch: refs/heads/master
Commit: beffd2aae2bd029aafe56f4d300ad3098907a46f
Parents: b70ce99
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Mon Nov 2 07:20:15 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Mon Nov 2 07:20:15 2015 +0200
----------------------------------------------------------------------
.../sshd/client/session/ClientSession.java | 17 +
.../sshd/client/session/ClientSessionImpl.java | 17 +-
.../subsystem/sftp/AbstractSftpClient.java | 173 +++---
.../subsystem/sftp/DefaultSftpClient.java | 3 +-
.../sshd/client/subsystem/sftp/SftpClient.java | 174 ++++--
.../sshd/client/subsystem/sftp/SftpCommand.java | 4 +-
.../client/subsystem/sftp/SftpFileChannel.java | 2 +-
.../client/subsystem/sftp/SftpFileSystem.java | 20 +-
.../subsystem/sftp/SftpFileSystemProvider.java | 58 +-
.../subsystem/sftp/SftpPosixFileAttributes.java | 19 +-
.../subsystem/sftp/SftpVersionSelector.java | 122 ++++
.../sshd/common/subsystem/sftp/SftpHelper.java | 551 +++++++++++++++++++
.../apache/sshd/common/util/GenericUtils.java | 34 ++
.../apache/sshd/common/util/ValidateUtils.java | 26 +-
.../sshd/server/subsystem/sftp/SftpHelper.java | 546 ------------------
.../server/subsystem/sftp/SftpSubsystem.java | 10 +-
.../client/simple/SimpleSftpClientTest.java | 2 +-
.../subsystem/sftp/SftpFileSystemTest.java | 19 +-
.../sshd/client/subsystem/sftp/SftpTest.java | 3 +-
.../subsystem/sftp/SftpVersionSelectorTest.java | 122 ++++
.../client/subsystem/sftp/SftpVersionsTest.java | 143 +++++
.../org/apache/sshd/util/test/JSchLogger.java | 2 +-
sshd-core/src/test/resources/log4j.properties | 12 +-
sshd-git/src/test/resources/log4j.properties | 12 +-
24 files changed, 1354 insertions(+), 737 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index a217630..87e50f5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -231,6 +231,19 @@ public interface ClientSession extends Session, KeyPairProviderHolder {
SftpClient createSftpClient() throws IOException;
/**
+ * Creates an SFTP client using the specified version
+ *
+ * @param version The version to use - <B>Note:</B> if the specified
+ * version is not supported by the server then an exception
+ * will occur
+ * @return The created {@link SftpClient}
+ * @throws IOException If failed to create the client or use the specified version
+ */
+ SftpClient createSftpClient(int version) throws IOException;
+
+ /**
+ * Creates an SFTP client while allowing the selection of a specific version
+ *
* @param selector The {@link SftpVersionSelector} to use - <B>Note:</B>
* if the server does not support versions re-negotiation then the
* selector will be presented with only one "choice" - the
@@ -242,10 +255,14 @@ public interface ClientSession extends Session, KeyPairProviderHolder {
FileSystem createSftpFileSystem() throws IOException;
+ FileSystem createSftpFileSystem(int version) throws IOException;
+
FileSystem createSftpFileSystem(SftpVersionSelector selector) throws IOException;
FileSystem createSftpFileSystem(int readBufferSize, int writeBufferSize) throws IOException;
+ FileSystem createSftpFileSystem(int version, int readBufferSize, int writeBufferSize) throws IOException;
+
FileSystem createSftpFileSystem(SftpVersionSelector selector, int readBufferSize, int writeBufferSize) throws IOException;
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index ff95265..ce597ec 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -442,11 +442,16 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
return new DefaultScpClient(this, listener);
}
- @Override
+ @Override // TODO make this a default method in JDK-8
public SftpClient createSftpClient() throws IOException {
return createSftpClient(SftpVersionSelector.CURRENT);
}
+ @Override // TODO make this a default method in JDK-8
+ public SftpClient createSftpClient(final int version) throws IOException {
+ return createSftpClient(SftpVersionSelector.Utils.fixedVersionSelector(version));
+ }
+
@Override
public SftpClient createSftpClient(SftpVersionSelector selector) throws IOException {
DefaultSftpClient client = new DefaultSftpClient(this);
@@ -460,11 +465,21 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
}
@Override
+ public FileSystem createSftpFileSystem(int version) throws IOException {
+ return createSftpFileSystem(SftpVersionSelector.Utils.fixedVersionSelector(version));
+ }
+
+ @Override
public FileSystem createSftpFileSystem(SftpVersionSelector selector) throws IOException {
return createSftpFileSystem(selector, SftpClient.DEFAULT_READ_BUFFER_SIZE, SftpClient.DEFAULT_WRITE_BUFFER_SIZE);
}
@Override
+ public FileSystem createSftpFileSystem(int version, int readBufferSize, int writeBufferSize) throws IOException {
+ return createSftpFileSystem(SftpVersionSelector.Utils.fixedVersionSelector(version), readBufferSize, writeBufferSize);
+ }
+
+ @Override
public FileSystem createSftpFileSystem(int readBufferSize, int writeBufferSize) throws IOException {
return createSftpFileSystem(SftpVersionSelector.CURRENT, readBufferSize, writeBufferSize);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/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 3655571..bb72962 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
@@ -28,7 +28,6 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.client.subsystem.sftp.extensions.BuiltinSftpClientExtensions;
@@ -36,6 +35,7 @@ import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension;
import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtensionFactory;
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.extensions.ParserUtils;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
@@ -60,7 +60,6 @@ import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_A
import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_OWNERGROUP;
import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS;
import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_SIZE;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_SUBSECOND_TIMES;
import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_ATTR_UIDGID;
import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY;
import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SSH_FILEXFER_TYPE_REGULAR;
@@ -441,67 +440,55 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
int version = getVersion();
if (version == SFTP_V3) {
if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- attrs.flags.add(Attribute.Size);
- attrs.size = buffer.getLong();
+ attrs.setSize(buffer.getLong());
}
if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
- attrs.flags.add(Attribute.UidGid);
- attrs.uid = buffer.getInt();
- attrs.gid = buffer.getInt();
+ attrs.owner(buffer.getInt(), buffer.getInt());
}
if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- attrs.flags.add(Attribute.Perms);
- attrs.perms = buffer.getInt();
+ attrs.setPermissions(buffer.getInt());
}
if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- attrs.flags.add(Attribute.AcModTime);
- attrs.atime = buffer.getInt();
- attrs.mtime = buffer.getInt();
+ attrs.setAccessTime(readTime(buffer, flags));
+ attrs.setModifyTime(readTime(buffer, flags));
}
} else if (version >= SFTP_V4) {
attrs.type = buffer.getUByte();
if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- attrs.flags.add(Attribute.Size);
- attrs.size = buffer.getLong();
+ attrs.setSize(buffer.getLong());
}
if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
- attrs.flags.add(Attribute.OwnerGroup);
- attrs.owner = buffer.getString();
- attrs.group = buffer.getString();
+ attrs.setOwner(buffer.getString());
+ attrs.setGroup(buffer.getString());
}
if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- attrs.flags.add(Attribute.Perms);
- attrs.perms = buffer.getInt();
+ attrs.setPermissions(buffer.getInt());
}
// update the permissions according to the type
+ int perms = attrs.getPermissions();
switch (attrs.type) {
case SSH_FILEXFER_TYPE_REGULAR:
- attrs.perms |= S_IFREG;
+ perms |= S_IFREG;
break;
case SSH_FILEXFER_TYPE_DIRECTORY:
- attrs.perms |= S_IFDIR;
+ perms |= S_IFDIR;
break;
case SSH_FILEXFER_TYPE_SYMLINK:
- attrs.perms |= S_IFLNK;
+ perms |= S_IFLNK;
break;
default: // do nothing
}
+ attrs.setPermissions(perms);
if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
- attrs.flags.add(Attribute.AccessTime);
- attrs.accessTime = readTime(buffer, flags);
- attrs.atime = (int) attrs.accessTime.to(TimeUnit.SECONDS);
+ attrs.setAccessTime(readTime(buffer, flags));
}
if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
- attrs.flags.add(Attribute.CreateTime);
- attrs.createTime = readTime(buffer, flags);
- attrs.ctime = (int) attrs.createTime.to(TimeUnit.SECONDS);
+ attrs.setCreateTime(readTime(buffer, flags));
}
if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
- attrs.flags.add(Attribute.ModifyTime);
- attrs.modifyTime = readTime(buffer, flags);
- attrs.mtime = (int) attrs.modifyTime.to(TimeUnit.SECONDS);
+ attrs.setModifyTime(readTime(buffer, flags));
}
// TODO: acl
} else {
@@ -511,12 +498,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
}
protected FileTime readTime(Buffer buffer, int flags) {
- long secs = buffer.getLong();
- long millis = secs * 1000;
- if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
- millis += buffer.getInt() / 1000000L;
- }
- return FileTime.from(millis, TimeUnit.MILLISECONDS);
+ return SftpHelper.readTime(buffer, getVersion(), flags);
}
protected void writeAttributes(Buffer buffer, Attributes attributes) throws IOException {
@@ -534,26 +516,34 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
case Perms:
flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
break;
- case AcModTime:
- flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+ case AccessTime:
+ if (attributes.flags.contains(Attribute.ModifyTime)) {
+ flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+ }
+ break;
+ case ModifyTime:
+ if (attributes.flags.contains(Attribute.AccessTime)) {
+ flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+ }
break;
default: // do nothing
}
}
buffer.putInt(flags);
if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- buffer.putLong(attributes.size);
+ buffer.putLong(attributes.getSize());
}
if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
- buffer.putInt(attributes.uid);
- buffer.putInt(attributes.gid);
+ buffer.putInt(attributes.getUserId());
+ buffer.putInt(attributes.getGroupId());
}
if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- buffer.putInt(attributes.perms);
+ buffer.putInt(attributes.getPermissions());
}
+
if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
- buffer.putInt(attributes.atime);
- buffer.putInt(attributes.mtime);
+ SftpHelper.writeTime(buffer, version, flags, attributes.getAccessTime());
+ SftpHelper.writeTime(buffer, version, flags, attributes.getModifyTime());
}
} else if (version >= SFTP_V4) {
int flags = 0;
@@ -583,42 +573,28 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
buffer.putInt(flags);
buffer.putByte((byte) attributes.type);
if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
- buffer.putLong(attributes.size);
+ buffer.putLong(attributes.getSize());
}
if ((flags & SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
- buffer.putString(attributes.owner != null ? attributes.owner : "OWNER@");
- buffer.putString(attributes.group != null ? attributes.group : "GROUP@");
+ String owner = attributes.getOwner();
+ buffer.putString(GenericUtils.isEmpty(owner) ? "OWNER@" : owner);
+
+ String group = attributes.getGroup();
+ buffer.putString(GenericUtils.isEmpty(group) ? "GROUP@" : group);
}
if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
- buffer.putInt(attributes.perms);
+ buffer.putInt(attributes.getPermissions());
}
if ((flags & SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
- buffer.putLong(attributes.accessTime.to(TimeUnit.SECONDS));
- if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
- long nanos = attributes.accessTime.to(TimeUnit.NANOSECONDS);
- nanos = nanos % TimeUnit.SECONDS.toNanos(1);
- buffer.putInt((int) nanos);
- }
- buffer.putInt(attributes.atime);
+ SftpHelper.writeTime(buffer, version, flags, attributes.getAccessTime());
}
if ((flags & SSH_FILEXFER_ATTR_CREATETIME) != 0) {
- buffer.putLong(attributes.createTime.to(TimeUnit.SECONDS));
- if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
- long nanos = attributes.createTime.to(TimeUnit.NANOSECONDS);
- nanos = nanos % TimeUnit.SECONDS.toNanos(1);
- buffer.putInt((int) nanos);
- }
- buffer.putInt(attributes.atime);
+ SftpHelper.writeTime(buffer, version, flags, attributes.getCreateTime());
}
if ((flags & SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
- buffer.putLong(attributes.modifyTime.to(TimeUnit.SECONDS));
- if ((flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
- long nanos = attributes.modifyTime.to(TimeUnit.NANOSECONDS);
- nanos = nanos % TimeUnit.SECONDS.toNanos(1);
- buffer.putInt((int) nanos);
- }
- buffer.putInt(attributes.atime);
+ SftpHelper.writeTime(buffer, version, flags, attributes.getModifyTime());
}
+ // 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);
@@ -695,7 +671,12 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
}
buffer.putInt(mode);
writeAttributes(buffer, new Attributes());
- return new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPEN, buffer));
+
+ CloseableHandle handle = new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPEN, buffer));
+ if (log.isTraceEnabled()) {
+ log.trace("open({})[{}] options={}: {}", getClientSession(), path, options, handle);
+ }
+ return handle;
}
@Override
@@ -704,7 +685,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("close(" + handle + ") client is closed");
}
- byte[] id = handle.getIdentifier();
+ if (log.isTraceEnabled()) {
+ log.trace("close({}) {}", getClientSession(), handle);
+ }
+
+ byte[] id = ValidateUtils.checkNotNull(handle, "No handle").getIdentifier();
Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* some extra fields */);
buffer.putBytes(id);
checkStatus(SSH_FXP_CLOSE, buffer);
@@ -716,6 +701,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("remove(" + path + ") client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("remove({}) {}", getClientSession(), path);
+ }
+
Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
buffer.putString(path);
checkStatus(SSH_FXP_REMOVE, buffer);
@@ -727,6 +716,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("rename(" + oldPath + " => " + newPath + ")[" + options + "] client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("rename({}) {} => {}", getClientSession(), oldPath, newPath);
+ }
+
Buffer buffer = new ByteArrayBuffer(oldPath.length() + newPath.length() + Long.SIZE /* some extra fields */);
buffer.putString(oldPath);
buffer.putString(newPath);
@@ -835,6 +828,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("mkdir(" + path + ") client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("mkdir({}) {}", getClientSession(), path);
+ }
+
Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
buffer.putString(path);
buffer.putInt(0);
@@ -853,6 +850,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("rmdir(" + path + ") client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("rmdir({}) {}", getClientSession(), path);
+ }
+
Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
buffer.putString(path);
checkStatus(SSH_FXP_RMDIR, buffer);
@@ -866,7 +867,13 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
buffer.putString(path);
- return new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPENDIR, buffer));
+
+ CloseableHandle handle = new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPENDIR, buffer));
+ if (log.isTraceEnabled()) {
+ log.trace("openDir({})[{}}: {}", getClientSession(), path, handle);
+ }
+
+ return handle;
}
@Override
@@ -989,6 +996,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("setStat(" + path + ")[" + attributes + "] client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("setStat({})[{}]: {}", getClientSession(), path, attributes);
+ }
+
Buffer buffer = new ByteArrayBuffer();
buffer.putString(path);
writeAttributes(buffer, attributes);
@@ -1001,6 +1012,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("setStat(" + handle + ")[" + attributes + "] client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("setStat({})[{}]: {}", getClientSession(), handle, attributes);
+ }
byte[] id = handle.getIdentifier();
Buffer buffer = new ByteArrayBuffer(id.length + (2 * Long.SIZE) /* some extras */);
buffer.putBytes(id);
@@ -1025,6 +1039,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("link(" + linkPath + " => " + targetPath + ")[symbolic=" + symbolic + "] client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("link({})[symbolic={}] {} => {}", getClientSession(), symbolic, linkPath, targetPath);
+ }
+
Buffer buffer = new ByteArrayBuffer(linkPath.length() + targetPath.length() + Long.SIZE /* some extra fields */);
int version = getVersion();
if (version < SFTP_V6) {
@@ -1048,6 +1066,11 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("lock(" + handle + ")[offset=" + offset + ", length=" + length + ", mask=0x" + Integer.toHexString(mask) + "] client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("lock({})[{}] offset={}, length={}, mask=0x{}",
+ getClientSession(), handle, offset, length, Integer.toHexString(mask));
+ }
+
byte[] id = handle.getIdentifier();
Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* a bit extra */);
buffer.putBytes(id);
@@ -1063,6 +1086,10 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("unlock" + handle + ")[offset=" + offset + ", length=" + length + "] client is closed");
}
+ if (log.isDebugEnabled()) {
+ log.debug("unlock({})[{}] offset={}, length={}", getClientSession(), handle, offset, length);
+ }
+
byte[] id = handle.getIdentifier();
Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* a bit extra */);
buffer.putBytes(id);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
index aa8aead..8d2c057 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
@@ -219,8 +219,7 @@ public class DefaultSftpClient extends AbstractSftpClient {
int id = cmdId.incrementAndGet();
int len = buffer.available();
if (log.isTraceEnabled()) {
- log.trace("send(cmd={}, len={}) id = {}",
- cmd, len, id);
+ log.trace("send(cmd={}, len={}) id={}", cmd, len, id);
}
OutputStream dos = channel.getInvertedIn();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/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 538b198..652b3d7 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
@@ -66,18 +66,17 @@ public interface SftpClient extends SubsystemClient {
Size,
UidGid,
Perms,
- AcModTime,
OwnerGroup,
AccessTime,
ModifyTime,
- CreateTime,
+ CreateTime
}
class Handle {
private final byte[] id;
Handle(byte[] id) {
- // clone the original so the handle is imutable
+ // clone the original so the handle is immutable
this.id = ValidateUtils.checkNotNullAndNotEmpty(id, "No handle ID").clone();
}
@@ -133,58 +132,92 @@ public interface SftpClient extends SubsystemClient {
class Attributes {
// CHECKSTYLE:OFF
public final Set<Attribute> flags = EnumSet.noneOf(Attribute.class);
- public long size;
public int type;
- public int uid;
- public int gid;
- public int perms;
- public int atime;
- public int ctime;
- public int mtime;
- public String owner;
- public String group;
- public FileTime accessTime;
- public FileTime createTime;
- public FileTime modifyTime;
// CHECKSTYLE:ON
+ private int perms;
+ private int uid;
+ private int gid;
+ private String owner;
+ private String group;
+ private long size;
+ private FileTime accessTime;
+ private FileTime createTime;
+ private FileTime modifyTime;
+
+ public Attributes() {
+ super();
+ }
+
@Override
public String toString() {
return "type=" + type
- + ";size=" + size
- + ";uid=" + uid
- + ";gid=" + gid
- + ";perms=0x" + Integer.toHexString(perms)
- + ";flags=" + flags
- + ";owner=" + owner
- + ";group=" + group
- + ";aTime=(" + atime + ")[" + accessTime + "]"
- + ";cTime=(" + ctime + ")[" + createTime + "]"
- + ";mTime=(" + mtime + ")[" + modifyTime + "]";
+ + ";size=" + getSize()
+ + ";uid=" + getUserId()
+ + ";gid=" + getGroupId()
+ + ";perms=0x" + Integer.toHexString(getPermissions())
+ + ";flags=" + flags
+ + ";owner=" + getOwner()
+ + ";group=" + getGroup()
+ + ";aTime=" + getAccessTime()
+ + ";cTime=" + getCreateTime()
+ + ";mTime=" + getModifyTime();
+ }
+
+ public long getSize() {
+ return size;
}
public Attributes size(long size) {
+ setSize(size);
+ return this;
+ }
+
+ public void setSize(long size) {
flags.add(Attribute.Size);
this.size = size;
- return this;
+ }
+
+ public String getOwner() {
+ return owner;
}
public Attributes owner(String owner) {
+ setOwner(owner);
+ return this;
+ }
+
+ public void setOwner(String owner) {
flags.add(Attribute.OwnerGroup);
this.owner = owner;
if (GenericUtils.isEmpty(group)) {
group = "GROUP@";
}
- return this;
+ }
+
+ public String getGroup() {
+ return group;
}
public Attributes group(String group) {
+ setGroup(group);
+ return this;
+ }
+
+ public void setGroup(String group) {
flags.add(Attribute.OwnerGroup);
this.group = group;
if (GenericUtils.isEmpty(owner)) {
owner = "OWNER@";
}
- return this;
+ }
+
+ public int getUserId() {
+ return uid;
+ }
+
+ public int getGroupId() {
+ return gid;
}
public Attributes owner(int uid, int gid) {
@@ -194,61 +227,86 @@ public interface SftpClient extends SubsystemClient {
return this;
}
+ public int getPermissions() {
+ return perms;
+ }
+
public Attributes perms(int perms) {
+ setPermissions(perms);
+ return this;
+ }
+
+ public void setPermissions(int perms) {
flags.add(Attribute.Perms);
this.perms = perms;
- return this;
}
- public Attributes atime(int atime) {
- flags.add(Attribute.AccessTime);
- this.atime = atime;
- this.accessTime = FileTime.from(atime, TimeUnit.SECONDS);
- return this;
+ public FileTime getAccessTime() {
+ return accessTime;
}
- public Attributes ctime(int ctime) {
- flags.add(Attribute.CreateTime);
- this.ctime = ctime;
- this.createTime = FileTime.from(atime, TimeUnit.SECONDS);
- return this;
+ public Attributes accessTime(long atime) {
+ return accessTime(atime, TimeUnit.SECONDS);
}
- public Attributes mtime(int mtime) {
- flags.add(Attribute.ModifyTime);
- this.mtime = mtime;
- this.modifyTime = FileTime.from(atime, TimeUnit.SECONDS);
- return this;
+ public Attributes accessTime(long atime, TimeUnit unit) {
+ return accessTime(FileTime.from(atime, unit));
}
- public Attributes time(int atime, int mtime) {
- flags.add(Attribute.AcModTime);
- this.atime = atime;
- this.mtime = mtime;
+ public Attributes accessTime(FileTime atime) {
+ setAccessTime(atime);
return this;
}
- public Attributes accessTime(FileTime atime) {
+ public void setAccessTime(FileTime atime) {
flags.add(Attribute.AccessTime);
- this.atime = (int) atime.to(TimeUnit.SECONDS);
- this.accessTime = atime;
- return this;
+ accessTime = ValidateUtils.checkNotNull(atime, "No access time");
+ }
+
+ public FileTime getCreateTime() {
+ return createTime;
+ }
+
+ public Attributes createTime(long ctime) {
+ return createTime(ctime, TimeUnit.SECONDS);
+ }
+
+ public Attributes createTime(long ctime, TimeUnit unit) {
+ return createTime(FileTime.from(ctime, unit));
}
public Attributes createTime(FileTime ctime) {
- flags.add(Attribute.CreateTime);
- this.ctime = (int) ctime.to(TimeUnit.SECONDS);
- this.createTime = ctime;
+ setCreateTime(ctime);
return this;
}
+ public void setCreateTime(FileTime ctime) {
+ flags.add(Attribute.CreateTime);
+ createTime = ValidateUtils.checkNotNull(ctime, "No create time");
+ }
+
+ public FileTime getModifyTime() {
+ return modifyTime;
+ }
+
+ public Attributes modifyTime(long mtime) {
+ return modifyTime(mtime, TimeUnit.SECONDS);
+ }
+
+ public Attributes modifyTime(long mtime, TimeUnit unit) {
+ return modifyTime(FileTime.from(mtime, unit));
+ }
+
public Attributes modifyTime(FileTime mtime) {
- flags.add(Attribute.ModifyTime);
- this.mtime = (int) mtime.to(TimeUnit.SECONDS);
- this.modifyTime = mtime;
+ setModifyTime(mtime);
return this;
}
+ public void setModifyTime(FileTime mtime) {
+ flags.add(Attribute.ModifyTime);
+ modifyTime = ValidateUtils.checkNotNull(mtime, "No modify time");
+ }
+
public boolean isRegularFile() {
return (perms & S_IFMT) == S_IFREG;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
index a00e05d..e07e3ed 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
@@ -145,8 +145,8 @@ public class SftpCommand implements Channel {
}
protected <A extends Appendable> A appendFileAttributes(A stdout, SftpClient sftp, String path, Attributes attrs) throws IOException {
- stdout.append('\t').append(Long.toString(attrs.size))
- .append('\t').append(SftpFileSystemProvider.getRWXPermissions(attrs.perms));
+ stdout.append('\t').append(Long.toString(attrs.getSize()))
+ .append('\t').append(SftpFileSystemProvider.getRWXPermissions(attrs.getPermissions()));
if (attrs.isSymbolicLink()) {
String linkValue = sftp.readLink(path);
stdout.append(" => ")
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
index bf69719..b656582 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
@@ -217,7 +217,7 @@ public class SftpFileChannel extends FileChannel {
@Override
public long size() throws IOException {
ensureOpen(Collections.<SftpClient.OpenMode>emptySet());
- return sftp.stat(handle).size;
+ return sftp.stat(handle).getSize();
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/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 a348a98..cf91f90 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
@@ -40,6 +40,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.session.ClientSession;
+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;
@@ -47,7 +48,7 @@ import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
-public class SftpFileSystem extends BaseFileSystem<SftpPath> {
+public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSessionHolder {
public static final String POOL_SIZE_PROP = "sftp-fs-pool-size";
public static final int DEFAULT_POOL_SIZE = 8;
@@ -59,7 +60,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
)));
private final String id;
- private final ClientSession session;
+ private final ClientSession clientSession;
private final SftpVersionSelector selector;
private final Queue<SftpClient> pool;
private final ThreadLocal<Wrapper> wrappers = new ThreadLocal<>();
@@ -71,7 +72,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
public SftpFileSystem(SftpFileSystemProvider provider, String id, ClientSession session, SftpVersionSelector selector) throws IOException {
super(provider);
this.id = id;
- this.session = session;
+ this.clientSession = ValidateUtils.checkNotNull(session, "No client session");
this.selector = ValidateUtils.checkNotNull(selector, "No SFTP version selector provided");
this.stores = Collections.unmodifiableList(Collections.<FileStore>singletonList(new SftpFileStore(id, this)));
this.pool = new LinkedBlockingQueue<>(PropertyResolverUtils.getIntProperty(session, POOL_SIZE_PROP, DEFAULT_POOL_SIZE));
@@ -127,8 +128,9 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
return new SftpPath(this, root, names);
}
- public ClientSession getSession() {
- return session;
+ @Override
+ public ClientSession getClientSession() {
+ return clientSession;
}
@SuppressWarnings("synthetic-access")
@@ -138,6 +140,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
while (wrapper == null) {
SftpClient client = pool.poll();
if (client == null) {
+ ClientSession session = getClientSession();
client = session.createSftpClient(getSftpVersionSelector());
}
if (!client.isClosing()) {
@@ -157,6 +160,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
SftpFileSystemProvider provider = provider();
String fsId = getId();
SftpFileSystem fs = provider.removeFileSystem(fsId);
+ ClientSession session = getClientSession();
session.close(true);
if ((fs != null) && (fs != this)) {
@@ -167,6 +171,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
@Override
public boolean isOpen() {
+ ClientSession session = getClientSession();
return session.isOpen();
}
@@ -185,6 +190,11 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> {
return defaultDir;
}
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + String.valueOf(getClientSession()) + "]";
+ }
+
private final class Wrapper extends AbstractSftpClient {
private final SftpClient delegate;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/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 ad00511..dd6e9a4 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
@@ -200,6 +200,9 @@ public class SftpFileSystemProvider extends FileSystemProvider {
fileSystem.setReadBufferSize(PropertyResolverUtils.getIntProperty(resolver, READ_BUFFER_PROP_NAME, DEFAULT_READ_BUFFER_SIZE));
fileSystem.setWriteBufferSize(PropertyResolverUtils.getIntProperty(resolver, WRITE_BUFFER_PROP_NAME, DEFAULT_WRITE_BUFFER_SIZE));
+ if (log.isDebugEnabled()) {
+ log.debug("newFileSystem({}): {}", uri.toASCIIString(), fileSystem);
+ }
return fileSystem;
}
@@ -216,6 +219,10 @@ public class SftpFileSystemProvider extends FileSystemProvider {
fileSystem.setReadBufferSize(PropertyResolverUtils.getIntProperty(session, READ_BUFFER_PROP_NAME, DEFAULT_READ_BUFFER_SIZE));
fileSystem.setWriteBufferSize(PropertyResolverUtils.getIntProperty(session, WRITE_BUFFER_PROP_NAME, DEFAULT_WRITE_BUFFER_SIZE));
+ if (log.isDebugEnabled()) {
+ log.debug("newFileSystem: {}", fileSystem);
+ }
+
return fileSystem;
}
@@ -238,9 +245,15 @@ public class SftpFileSystemProvider extends FileSystemProvider {
return null;
}
+ SftpFileSystem removed;
synchronized (fileSystems) {
- return fileSystems.remove(id);
+ removed = fileSystems.remove(id);
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("removeFileSystem({}): {}", id, removed);
}
+ return removed;
}
/**
@@ -315,6 +328,9 @@ public class SftpFileSystemProvider extends FileSystemProvider {
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
SftpPath p = toSftpPath(dir);
SftpFileSystem fs = p.getFileSystem();
+ if (log.isDebugEnabled()) {
+ log.debug("createDirectory({}) {} ({})", fs, dir, Arrays.asList(attrs));
+ }
try (SftpClient sftp = fs.getClient()) {
try {
sftp.mkdir(dir.toString());
@@ -347,6 +363,10 @@ public class SftpFileSystemProvider extends FileSystemProvider {
checkAccess(p, AccessMode.WRITE);
SftpFileSystem fs = p.getFileSystem();
+ if (log.isDebugEnabled()) {
+ log.debug("delete({}) {}", fs, path);
+ }
+
try (SftpClient sftp = fs.getClient()) {
BasicFileAttributes attributes = readAttributes(path, BasicFileAttributes.class);
if (attributes.isDirectory()) {
@@ -388,6 +408,10 @@ public class SftpFileSystemProvider extends FileSystemProvider {
throw new AccessDeniedException("Existence cannot be determined for copy target: " + target);
}
+ if (log.isDebugEnabled()) {
+ log.debug("copy({})[{}] {} => {}", src.getFileSystem(), Arrays.asList(options), src, dst);
+ }
+
if (replaceExisting) {
deleteIfExists(target);
} else {
@@ -447,7 +471,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
// attributes of source file
BasicFileAttributes attrs = readAttributes(source, BasicFileAttributes.class, linkOptions);
if (attrs.isSymbolicLink()) {
- throw new IOException("Moving of source symbolic link not supported: " + source);
+ throw new IOException("Moving of source symbolic link (" + source + ") to " + target + " not supported");
}
// delete target if it exists and REPLACE_EXISTING is specified
@@ -456,6 +480,10 @@ public class SftpFileSystemProvider extends FileSystemProvider {
throw new AccessDeniedException("Existence cannot be determined for move target " + target);
}
+ if (log.isDebugEnabled()) {
+ log.debug("move({})[{}] {} => {}", src.getFileSystem(), Arrays.asList(options), src, dst);
+ }
+
if (replaceExisting) {
deleteIfExists(target);
} else if (status) {
@@ -525,6 +553,11 @@ public class SftpFileSystemProvider extends FileSystemProvider {
if (fsLink != t.getFileSystem()) {
throw new ProviderMismatchException("Mismatched file system providers for " + l + " vs. " + t);
}
+
+ if (log.isDebugEnabled()) {
+ log.debug("createSymbolicLink({})[{}] {} => {}", fsLink, Arrays.asList(attrs), link, target);
+ }
+
try (SftpClient client = fsLink.getClient()) {
client.symLink(l.toString(), t.toString());
}
@@ -535,7 +568,12 @@ public class SftpFileSystemProvider extends FileSystemProvider {
SftpPath l = toSftpPath(link);
SftpFileSystem fsLink = l.getFileSystem();
try (SftpClient client = fsLink.getClient()) {
- return fsLink.getPath(client.readLink(l.toString()));
+ String linkPath = client.readLink(l.toString());
+ if (log.isDebugEnabled()) {
+ log.debug("readSymbolicLink({})[{}] {} => {}", fsLink, link, linkPath);
+ }
+
+ return fsLink.getPath(linkPath);
}
}
@@ -658,7 +696,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
break;
default:
if (log.isTraceEnabled()) {
- log.trace("readAttributes({})[{}] ignored {}={}", path, attributes, attr, v);
+ log.trace("readAttributes({})[{}] ignored {}={} for {}", fs, path, attr, v, attributes);
}
}
}
@@ -687,13 +725,13 @@ public class SftpFileSystemProvider extends FileSystemProvider {
SftpClient.Attributes attributes = new SftpClient.Attributes();
switch (attr) {
case "lastModifiedTime":
- attributes.mtime((int) ((FileTime) value).to(TimeUnit.SECONDS));
+ attributes.modifyTime((int) ((FileTime) value).to(TimeUnit.SECONDS));
break;
case "lastAccessTime":
- attributes.atime((int) ((FileTime) value).to(TimeUnit.SECONDS));
+ attributes.accessTime((int) ((FileTime) value).to(TimeUnit.SECONDS));
break;
case "creationTime":
- attributes.ctime((int) ((FileTime) value).to(TimeUnit.SECONDS));
+ attributes.createTime((int) ((FileTime) value).to(TimeUnit.SECONDS));
break;
case "size":
attributes.size(((Number) value).longValue());
@@ -718,10 +756,14 @@ public class SftpFileSystemProvider extends FileSystemProvider {
+ " unknown view=" + view + " attribute: " + attr);
default:
if (log.isTraceEnabled()) {
- log.trace("setAttribute({})[{}] ignore {}={}", path, attribute, attr, value);
+ log.trace("setAttribute({})[{}] ignore {}/{}={}", fs, path, attribute, attr, value);
}
}
+ if (log.isDebugEnabled()) {
+ log.debug("setAttribute({}) {}: {}", fs, path, attributes);
+ }
+
try (SftpClient client = fs.getClient()) {
client.setStat(p.toString(), attributes);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/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 9b0e0d3..b510907 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
@@ -25,9 +25,8 @@ import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.UserPrincipal;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
+import org.apache.sshd.common.util.GenericUtils;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -50,32 +49,34 @@ public class SftpPosixFileAttributes implements PosixFileAttributes {
@Override
public UserPrincipal owner() {
- return attributes.owner != null ? new SftpFileSystem.DefaultGroupPrincipal(attributes.owner) : null;
+ String owner = attributes.getOwner();
+ return GenericUtils.isEmpty(owner) ? null : new SftpFileSystem.DefaultGroupPrincipal(owner);
}
@Override
public GroupPrincipal group() {
- return attributes.group != null ? new SftpFileSystem.DefaultGroupPrincipal(attributes.group) : null;
+ String group = attributes.getGroup();
+ return GenericUtils.isEmpty(group) ? null : new SftpFileSystem.DefaultGroupPrincipal(group);
}
@Override
public Set<PosixFilePermission> permissions() {
- return SftpFileSystemProvider.permissionsToAttributes(attributes.perms);
+ return SftpFileSystemProvider.permissionsToAttributes(attributes.getPermissions());
}
@Override
public FileTime lastModifiedTime() {
- return FileTime.from(attributes.mtime, TimeUnit.SECONDS);
+ return attributes.getModifyTime();
}
@Override
public FileTime lastAccessTime() {
- return FileTime.from(attributes.atime, TimeUnit.SECONDS);
+ return attributes.getAccessTime();
}
@Override
public FileTime creationTime() {
- return FileTime.from(attributes.ctime, TimeUnit.SECONDS);
+ return attributes.getCreateTime();
}
@Override
@@ -100,7 +101,7 @@ public class SftpPosixFileAttributes implements PosixFileAttributes {
@Override
public long size() {
- return attributes.size;
+ return attributes.getSize();
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
index e5f41bf..240950e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
@@ -19,8 +19,12 @@
package org.apache.sshd.client.subsystem.sftp;
+import java.util.Collection;
import java.util.List;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@@ -37,6 +41,42 @@ public interface SftpVersionSelector {
};
/**
+ * An {@link SftpVersionSelector} that returns the maximum available version
+ */
+ SftpVersionSelector MAXIMUM = new SftpVersionSelector() {
+ @Override
+ public int selectVersion(int current, List<Integer> available) {
+ int candidate = current;
+ if (GenericUtils.size(available) > 0) {
+ for (Number version : available) {
+ if (candidate < version.intValue()) {
+ candidate = version.intValue();
+ }
+ }
+ }
+ return candidate;
+ }
+ };
+
+ /**
+ * An {@link SftpVersionSelector} that returns the maximum available version
+ */
+ SftpVersionSelector MINIMUM = new SftpVersionSelector() {
+ @Override
+ public int selectVersion(int current, List<Integer> available) {
+ int candidate = current;
+ if (GenericUtils.size(available) > 0) {
+ for (Number version : available) {
+ if (candidate > version.intValue()) {
+ candidate = version.intValue();
+ }
+ }
+ }
+ return candidate;
+ }
+ };
+
+ /**
* @param current The current version negotiated with the server
* @param available Extra versions available - may be empty and/or contain
* only the current one
@@ -44,4 +84,86 @@ public interface SftpVersionSelector {
*/
int selectVersion(int current, List<Integer> available);
+ /**
+ * Utility class to help using {@link SftpVersionSelector}s
+ */
+ // CHECKSTYLE:OFF
+ final class Utils {
+ // CHECKSTYLE:ON
+
+ private Utils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * Creates a selector the always returns the requested (fixed version) regardless
+ * of what the current or reported available versions are. If the requested version
+ * is not reported as available then an exception will be eventually thrown by the
+ * client during re-negotiation phase.
+ *
+ * @param version The requested version
+ * @return The {@link SftpVersionSelector}
+ */
+ public static SftpVersionSelector fixedVersionSelector(final int version) {
+ return new SftpVersionSelector() {
+ @Override
+ public int selectVersion(int current, List<Integer> available) {
+ return version;
+ }
+ };
+ }
+
+ /**
+ * Selects a version in order of preference - if none of the preferred
+ * versions is listed as available then an exception is thrown when the
+ * {@link SftpVersionSelector#selectVersion(int, List)} method is invoked
+ *
+ * @param preferred The preferred versions in decreasing order of
+ * preference (i.e., most preferred is 1st) - may not be {@code null}/empty
+ * @return A {@link SftpVersionSelector} that attempts to select
+ * the most preferred version that is also listed as available.
+ */
+ public static SftpVersionSelector preferredVersionSelector(final int ... preferred) {
+ return preferredVersionSelector(GenericUtils.asList(preferred));
+
+ }
+
+ /**
+ * Selects a version in order of preference - if none of the preferred
+ * versions is listed as available then an exception is thrown when the
+ * {@link SftpVersionSelector#selectVersion(int, List)} method is invoked
+ *
+ * @param preferred The preferred versions in decreasing order of
+ * preference (i.e., most preferred is 1st)
+ * @return A {@link SftpVersionSelector} that attempts to select
+ * the most preferred version that is also listed as available.
+ */
+ public static SftpVersionSelector preferredVersionSelector(final Iterable<? extends Number> preferred) {
+ if (preferred instanceof Collection<?>) {
+ ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) preferred, "Empty preferred versions");
+ } else {
+ ValidateUtils.checkNotNull(preferred, "No preferred versions");
+ }
+
+ return new SftpVersionSelector() {
+ @Override
+ public int selectVersion(int current, List<Integer> available) {
+ for (Number prefValue : preferred) {
+ int version = prefValue.intValue();
+ if (version == current) {
+ return version;
+ }
+
+ for (Integer avail : available) {
+ if (version == avail.intValue()) {
+ return version;
+ }
+ }
+ }
+
+ throw new IllegalStateException("Preferred versions (" + preferred + ") not available: " + available);
+ }
+ };
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/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
new file mode 100644
index 0000000..f62274a
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
@@ -0,0 +1,551 @@
+/*
+ * 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.nio.channels.OverlappingFileLockException;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.DirectoryNotEmptyException;
+import java.nio.file.FileAlreadyExistsException;
+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.UserPrincipal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.server.subsystem.sftp.DefaultGroupPrincipal;
+import org.apache.sshd.server.subsystem.sftp.InvalidHandleException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SftpHelper {
+
+ private SftpHelper() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * Writes a file / folder's attributes to a buffer
+ *
+ * @param buffer The target {@link Buffer}
+ * @param version The output encoding version
+ * @param attributes The {@link Map} of attributes
+ * @see #writeAttrsV3(Buffer, int, Map)
+ * @see #writeAttrsV4(Buffer, int, Map)
+ */
+ public static void writeAttrs(Buffer buffer, int version, Map<String, ?> attributes) {
+ if (version == SftpConstants.SFTP_V3) {
+ writeAttrsV3(buffer, version, attributes);
+ } else if (version >= SftpConstants.SFTP_V4) {
+ writeAttrsV4(buffer, version, attributes);
+ } else {
+ throw new IllegalStateException("Unsupported SFTP version: " + version);
+ }
+ }
+
+ /**
+ * Writes the retrieved file / directory attributes in V3 format
+ *
+ * @param buffer The target {@link Buffer}
+ * @param version The actual version - must be {@link SftpConstants#SFTP_V3}
+ * @param attributes The {@link Map} of attributes
+ */
+ public static void writeAttrsV3(Buffer 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");
+
+ 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);
+ 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) {
+ writeTime(buffer, version, flags, lastAccessTime);
+ writeTime(buffer, version, flags, lastModifiedTime);
+ }
+ }
+
+ /**
+ * Writes the retrieved file / directory attributes in V3 format
+ *
+ * @param buffer The target {@link Buffer}
+ * @param version The actual version - must be at least {@link SftpConstants#SFTP_V4}
+ * @param attributes The {@link Map} of attributes
+ */
+ public static void writeAttrsV4(Buffer 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");
+ 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);
+ 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"), null));
+ buffer.putString(Objects.toString(attributes.get("group"), null));
+ }
+ if ((flags & SftpConstants.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ buffer.putInt(attributesToPermissions(isReg, isDir, isLnk, perms));
+ }
+
+ if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+ writeTime(buffer, version, flags, lastAccessTime);
+ }
+
+ if ((flags & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+ writeTime(buffer, version, flags, lastAccessTime);
+ }
+ if ((flags & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+ writeTime(buffer, version, flags, lastModifiedTime);
+ }
+ // TODO: acls
+ // TODO: bits
+ // TODO: extended
+ }
+
+ /**
+ * @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;
+ }
+
+ /**
+ * 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
+ */
+ 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) {
+ 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 {
+ return SftpConstants.SSH_FX_FAILURE;
+ }
+ }
+
+ public static AclEntry buildAclEntry(int aclType, int aclFlag, int aclMask, final String aclWho) {
+ AclEntryType type;
+ switch (aclType) {
+ case SftpConstants.ACE4_ACCESS_ALLOWED_ACE_TYPE:
+ type = AclEntryType.ALLOW;
+ break;
+ case SftpConstants.ACE4_ACCESS_DENIED_ACE_TYPE:
+ type = AclEntryType.DENY;
+ break;
+ case SftpConstants.ACE4_SYSTEM_AUDIT_ACE_TYPE:
+ type = AclEntryType.AUDIT;
+ break;
+ case SftpConstants.ACE4_SYSTEM_ALARM_ACE_TYPE:
+ type = AclEntryType.AUDIT;
+ break;
+ default:
+ throw new IllegalStateException("Unknown acl type: " + aclType);
+ }
+ 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);
+ }
+
+ 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);
+ }
+ UserPrincipal who = new DefaultGroupPrincipal(aclWho);
+ return AclEntry.newBuilder()
+ .setType(type)
+ .setFlags(flags)
+ .setPermissions(mask)
+ .setPrincipal(who)
+ .build();
+ }
+
+ 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
+ }
+ }
+ if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SIZE) != 0) {
+ attrs.put("size", buffer.getLong());
+ }
+ if ((flags & SftpConstants.SSH_FILEXFER_ATTR_ALLOCATION_SIZE) != 0) {
+ attrs.put("allocationSize", buffer.getLong());
+ }
+ if ((flags & SftpConstants.SSH_FILEXFER_ATTR_UIDGID) != 0) {
+ attrs.put("uid", buffer.getInt());
+ attrs.put("gid", buffer.getInt());
+ }
+ 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 ((flags & SftpConstants.SSH_FILEXFER_ATTR_CTIME) != 0) {
+ attrs.put("ctime", readTime(buffer, version, flags));
+ }
+ }
+ 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);
+ }
+ 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);
+ }
+
+ return attrs;
+ }
+
+ // 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
+ *
+ * @param buffer The target {@link Buffer}
+ * @param version The encoding version
+ * @param flags The encoding flags
+ * @param time The value to encode
+ */
+ public static void writeTime(Buffer buffer, int version, int flags, FileTime time) {
+ 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));
+ }
+ }
+
+ /**
+ * 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) {
+ 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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index c82dabf..faffd01 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -21,6 +21,7 @@ package org.apache.sshd.common.util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -28,6 +29,7 @@ import java.util.Comparator;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -214,10 +216,18 @@ public final class GenericUtils {
return length(a) <= 0;
}
+ public static boolean isEmpty(int[] a) {
+ return length(a) <= 0;
+ }
+
public static int length(byte... a) {
return a == null ? 0 : a.length;
}
+ public static int length(int... a) {
+ return a == null ? 0 : a.length;
+ }
+
@SafeVarargs
public static <T> int length(T... a) {
return a == null ? 0 : a.length;
@@ -268,6 +278,30 @@ public final class GenericUtils {
return result;
}
+ @SafeVarargs
+ public static <T> List<T> asList(T ... values) {
+ int len = length(values);
+ if (len <= 0) {
+ return Collections.emptyList();
+ } else {
+ return Arrays.asList(values);
+ }
+ }
+
+ public static List<Integer> asList(int ... values) {
+ int len = length(values);
+ if (len <= 0) {
+ return Collections.emptyList();
+ }
+
+ List<Integer> l = new ArrayList<>(len);
+ for (int v : values) {
+ l.add(Integer.valueOf(v));
+ }
+
+ return l;
+ }
+
@SuppressWarnings({"unchecked", "rawtypes"})
public static <V extends Comparable<V>> Comparator<V> naturalComparator() {
// TODO for JDK-8 use Comparator.naturalOrder()
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/beffd2aa/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
index 89c5fd5..b48f811 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
@@ -80,10 +80,28 @@ public final class ValidateUtils {
return t;
}
- public static byte[] checkNotNullAndNotEmpty(byte[] t, String message, Object... args) {
- t = checkNotNull(t, message, args);
- checkTrue(GenericUtils.length(t) > 0, message, args);
- return t;
+ public static byte[] checkNotNullAndNotEmpty(byte[] a, String message) {
+ a = checkNotNull(a, message);
+ checkTrue(GenericUtils.length(a) > 0, message);
+ return a;
+ }
+
+ public static byte[] checkNotNullAndNotEmpty(byte[] a, String message, Object... args) {
+ a = checkNotNull(a, message, args);
+ checkTrue(GenericUtils.length(a) > 0, message, args);
+ return a;
+ }
+
+ public static int[] checkNotNullAndNotEmpty(int[] a, String message) {
+ a = checkNotNull(a, message);
+ checkTrue(GenericUtils.length(a) > 0, message);
+ return a;
+ }
+
+ public static int[] checkNotNullAndNotEmpty(int[] a, String message, Object... args) {
+ a = checkNotNull(a, message, args);
+ checkTrue(GenericUtils.length(a) > 0, message, args);
+ return a;
}
public static <T> T[] checkNotNullAndNotEmpty(T[] t, String message, Object... args) {