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/30 08:20:15 UTC
mina-sshd git commit: Allow more fine-grained control over modifying
file attributes via SFTP
Repository: mina-sshd
Updated Branches:
refs/heads/master fa996f296 -> 5fd4fbaf4
Allow more fine-grained control over modifying file attributes via SFTP
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/5fd4fbaf
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/5fd4fbaf
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/5fd4fbaf
Branch: refs/heads/master
Commit: 5fd4fbaf440ff3a91055a80327e732902cbb203e
Parents: fa996f2
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Mon Nov 30 09:19:58 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Mon Nov 30 09:19:58 2015 +0200
----------------------------------------------------------------------
.../client/subsystem/sftp/SftpFileSystem.java | 4 +-
.../server/subsystem/sftp/SftpSubsystem.java | 145 +++++++++++++++----
2 files changed, 119 insertions(+), 30 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5fd4fbaf/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 5f4e6ae..608246d 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
@@ -200,7 +200,7 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSe
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
- return new DefaultUserPrincipalLookupService();
+ return DefaultUserPrincipalLookupService.INSTANCE;
}
@Override
@@ -521,6 +521,8 @@ public class SftpFileSystem extends BaseFileSystem<SftpPath> implements ClientSe
}
public static class DefaultUserPrincipalLookupService extends UserPrincipalLookupService {
+ public static final DefaultUserPrincipalLookupService INSTANCE = new DefaultUserPrincipalLookupService();
+
public DefaultUserPrincipalLookupService() {
super();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5fd4fbaf/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
index 20399aa..a89ba33 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
@@ -24,6 +24,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.StreamCorruptedException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.AccessDeniedException;
@@ -42,6 +43,7 @@ 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.FileOwnerAttributeView;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;
@@ -49,6 +51,7 @@ import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
+import java.nio.file.attribute.UserPrincipalNotFoundException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -56,7 +59,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -64,6 +66,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -2516,7 +2519,7 @@ public class SftpSubsystem
return getLongName(f, true, options);
}
- private String getLongName(Path f, boolean sendAttrs, LinkOption... options) throws IOException {
+ protected String getLongName(Path f, boolean sendAttrs, LinkOption... options) throws IOException {
Map<String, Object> attributes;
if (sendAttrs) {
attributes = getAttributes(f, options);
@@ -2526,7 +2529,7 @@ public class SftpSubsystem
return getLongName(f, attributes);
}
- private String getLongName(Path f, Map<String, ?> attributes) throws IOException {
+ protected String getLongName(Path f, Map<String, ?> attributes) throws IOException {
String username;
if (attributes.containsKey("owner")) {
username = Objects.toString(attributes.get("owner"), null);
@@ -2820,7 +2823,7 @@ public class SftpSubsystem
}
protected void setFileAttributes(Path file, Map<String, ?> attributes, LinkOption ... options) throws IOException {
- Set<String> unsupported = new HashSet<>();
+ Set<String> unsupported = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for (Map.Entry<String, ?> ae : attributes.entrySet()) {
String attribute = ae.getKey();
Object value = ae.getValue();
@@ -2867,18 +2870,41 @@ public class SftpSubsystem
break;
default: // ignored
}
- if ((view != null) && (value != null)) {
+ if ((GenericUtils.length(view) > 0) && (value != null)) {
try {
setFileAttribute(file, view, attribute, value, options);
- } catch (UnsupportedOperationException e) {
- unsupported.add(attribute);
+ } catch (Exception e) {
+ handleSetFileAttributeFailure(file, view, attribute, value, unsupported, e);
}
}
}
+
handleUnsupportedAttributes(unsupported);
}
+ protected void handleSetFileAttributeFailure(Path file, String view, String attribute, Object value, Collection<String> unsupported, Exception e) throws IOException {
+ if (e instanceof UnsupportedOperationException) {
+ if (log.isDebugEnabled()) {
+ log.debug("handleSetFileAttributeFailure({})[{}] {}:{}={} unsupported: {}",
+ getServerSession(), file, view, attribute, value, e.getMessage());
+ }
+ unsupported.add(attribute);
+ } else {
+ log.warn("handleSetFileAttributeFailure({})[{}] {}:{}={} - failed ({}) to set: {}",
+ getServerSession(), file, view, attribute, value, e.getClass().getSimpleName(), e.getMessage());
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(e);
+ }
+ }
+ }
+
protected void setFileAttribute(Path file, String view, String attribute, Object value, LinkOption ... options) throws IOException {
+ if (log.isTraceEnabled()) {
+ log.trace("setFileAttribute({})[{}] {}:{}={}", getServerSession(), file, view, attribute, value);
+ }
+
if ("acl".equalsIgnoreCase(attribute) && "acl".equalsIgnoreCase(view)) {
@SuppressWarnings("unchecked")
List<AclEntry> acl = (List<AclEntry>) value;
@@ -2887,6 +2913,10 @@ public class SftpSubsystem
@SuppressWarnings("unchecked")
Set<PosixFilePermission> perms = (Set<PosixFilePermission>) value;
setFilePermissions(file, perms, options);
+ } else if ("owner".equalsIgnoreCase(attribute) || "group".equalsIgnoreCase(attribute)) {
+ setFileOwnership(file, attribute, (Principal) value, options);
+ } else if ("creationTime".equalsIgnoreCase(attribute) || "lastModifiedTime".equalsIgnoreCase(attribute) || "lastAccessTime".equalsIgnoreCase(attribute)) {
+ setFileTime(file, view, attribute, (FileTime) value, options);
} else if ("extended".equalsIgnoreCase(view) && "extended".equalsIgnoreCase(attribute)) {
@SuppressWarnings("unchecked")
Map<String, byte[]> extensions = (Map<String, byte[]>) value;
@@ -2896,6 +2926,61 @@ public class SftpSubsystem
}
}
+ protected void setFileTime(Path file, String view, String attribute, FileTime value, LinkOption ... options) throws IOException {
+ if (value == null) {
+ return;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("setFileTime({})[{}] {}:{}={}", getServerSession(), file, view, attribute, value);
+ }
+
+ Files.setAttribute(file, view + ":" + attribute, value, options);
+ }
+
+ protected void setFileOwnership(Path file, String attribute, Principal value, LinkOption ... options) throws IOException {
+ if (value == null) {
+ return;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("setFileOwnership({})[{}] {}={}", getServerSession(), file, attribute, value);
+ }
+
+ /*
+ * Quoting from Javadoc of FileOwnerAttributeView#setOwner:
+ *
+ * To ensure consistent and correct behavior across platforms
+ * it is recommended that this method should only be used
+ * to set the file owner to a user principal that is not a group.
+ */
+ if ("owner".equalsIgnoreCase(attribute)) {
+ FileOwnerAttributeView view = Files.getFileAttributeView(file, FileOwnerAttributeView.class, options);
+ if (view == null) {
+ throw new UnsupportedOperationException("Owner view not supported for " + file);
+ }
+
+ if (!(value instanceof UserPrincipal)) {
+ throw new StreamCorruptedException("Owner is not " + UserPrincipal.class.getSimpleName() + ": " + value.getClass().getSimpleName());
+ }
+
+ view.setOwner((UserPrincipal) value);
+ } else if ("group".equalsIgnoreCase(attribute)) {
+ PosixFileAttributeView view = Files.getFileAttributeView(file, PosixFileAttributeView.class, options);
+ if (view == null) {
+ throw new UnsupportedOperationException("POSIX view not supported");
+ }
+
+ if (!(value instanceof GroupPrincipal)) {
+ throw new StreamCorruptedException("Group is not " + GroupPrincipal.class.getSimpleName() + ": " + value.getClass().getSimpleName());
+ }
+
+ view.setGroup((GroupPrincipal) value);
+ } else {
+ throw new UnsupportedOperationException("Unknown ownership attribute: " + attribute);
+ }
+ }
+
protected void setFileExtensions(Path file, Map<String, byte[]> extensions, LinkOption ... options) throws IOException {
if (GenericUtils.isEmpty(extensions)) {
return;
@@ -2913,7 +2998,7 @@ public class SftpSubsystem
log.debug("setFileExtensions({})[{}]: {}", getServerSession(), file, extensions);
}
} else {
- throw new UnsupportedOperationException("File extensions not supported for " + file);
+ throw new UnsupportedOperationException("File extensions not supported");
}
}
@@ -2947,33 +3032,32 @@ public class SftpSubsystem
}
protected void handleUnsupportedAttributes(Collection<String> attributes) {
- if (!attributes.isEmpty()) {
- StringBuilder sb = new StringBuilder();
- for (String attr : attributes) {
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(attr);
- }
- switch (unsupportedAttributePolicy) {
- case Ignore:
- break;
- case Warn:
- log.warn("Unsupported attributes: " + sb.toString());
- break;
- case ThrowException:
- throw new UnsupportedOperationException("Unsupported attributes: " + sb.toString());
- default:
- log.warn("Unknown policy for attributes=" + sb.toString() + ": " + unsupportedAttributePolicy);
- }
+ if (attributes.isEmpty()) {
+ return;
+ }
+
+ String attrsList = GenericUtils.join(attributes, ',');
+ switch (unsupportedAttributePolicy) {
+ case Ignore:
+ break;
+ case Warn:
+ log.warn("Unsupported attributes: " + attrsList);
+ break;
+ case ThrowException:
+ throw new UnsupportedOperationException("Unsupported attributes: " + attrsList);
+ default:
+ log.warn("Unknown policy for attributes=" + attrsList + ": " + unsupportedAttributePolicy);
}
}
- private GroupPrincipal toGroup(Path file, GroupPrincipal name) throws IOException {
+ protected GroupPrincipal toGroup(Path file, GroupPrincipal name) throws IOException {
String groupName = name.toString();
FileSystem fileSystem = file.getFileSystem();
UserPrincipalLookupService lookupService = fileSystem.getUserPrincipalLookupService();
try {
+ if (lookupService == null) {
+ throw new UserPrincipalNotFoundException("No lookup service");
+ }
return lookupService.lookupPrincipalByGroupName(groupName);
} catch (IOException e) {
handleUserPrincipalLookupServiceException(GroupPrincipal.class, groupName, e);
@@ -2981,11 +3065,14 @@ public class SftpSubsystem
}
}
- private UserPrincipal toUser(Path file, UserPrincipal name) throws IOException {
+ protected UserPrincipal toUser(Path file, UserPrincipal name) throws IOException {
String username = name.toString();
FileSystem fileSystem = file.getFileSystem();
UserPrincipalLookupService lookupService = fileSystem.getUserPrincipalLookupService();
try {
+ if (lookupService == null) {
+ throw new UserPrincipalNotFoundException("No lookup service");
+ }
return lookupService.lookupPrincipalByName(username);
} catch (IOException e) {
handleUserPrincipalLookupServiceException(UserPrincipal.class, username, e);