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 2019/09/09 09:54:31 UTC
[mina-sshd] branch master updated: [SSHD-926] Add support for
OpenSSH 'lsetstat@openssh.com' SFTP protocol extension
This is an automated email from the ASF dual-hosted git repository.
lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
The following commit(s) were added to refs/heads/master by this push:
new de2e460 [SSHD-926] Add support for OpenSSH 'lsetstat@openssh.com' SFTP protocol extension
de2e460 is described below
commit de2e460098a98f8cb8fd2414794971e3ba8a6b0c
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Wed Aug 28 15:22:58 2019 +0300
[SSHD-926] Add support for OpenSSH 'lsetstat@openssh.com' SFTP protocol extension
---
CHANGES.md | 7 +-
docs/sftp.md | 11 +++
.../subsystem/sftp/extensions/ParserUtils.java | 4 +-
.../openssh/AbstractOpenSSHExtensionParser.java | 2 +-
.../extensions/openssh/FsyncExtensionParser.java | 2 +-
...ionParser.java => LSetStatExtensionParser.java} | 13 ++-
.../sftp/AbstractSftpSubsystemHelper.java | 102 +++++++++++++--------
.../sshd/server/subsystem/sftp/FileHandle.java | 12 ++-
.../sshd/server/subsystem/sftp/SftpSubsystem.java | 5 +-
9 files changed, 106 insertions(+), 52 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 62f36db..7f77a16 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,6 +22,9 @@ session is initiated and protect their instance from shutdown when session is de
`createSubsystem` method that accepts the `ChannelSession` through which the request
has been made
+* `AbstractSftpSubsystemHelper#resolvePathResolutionFollowLinks` is consulted wherever
+the standard does not specifically specify the behavior regarding symbolic links handling.
+
* `UserAuthFactory` is a proper interface and it has been refactored to contain a
`createUserAuth` method that accepts the session instance through which the request is made.
@@ -35,6 +38,8 @@ peer version data is received.
## Behavioral changes and enhancements
+* [SSHD-926](https://issues.apache.org/jira/browse/SSHD-930) - Add support for OpenSSH 'lsetstat@openssh.com' SFTP protocol extension.
+
* [SSHD-930](https://issues.apache.org/jira/browse/SSHD-930) - Added configuration allowing the user to specify whether client should wait
for the server's identification before sending its own.
@@ -42,4 +47,4 @@ for the server's identification before sending its own.
* [SSHD-934](https://issues.apache.org/jira/browse/SSHD-934) - Fixed ECDSA public key encoding into OpenSSH format.
-* [SSHD-937](https://issues.apache.org/jira/browse/SSHD-937) - Provide session instance when creating a subsystem, user authentication, channel.
\ No newline at end of file
+* [SSHD-937](https://issues.apache.org/jira/browse/SSHD-937) - Provide session instance when creating a subsystem, user authentication, channel.
diff --git a/docs/sftp.md b/docs/sftp.md
index 919d761..ff017be 100644
--- a/docs/sftp.md
+++ b/docs/sftp.md
@@ -126,6 +126,16 @@ reasonable buffer size by setting the `channel-session-max-extdata-bufsize` prop
extended data handler is registered it will be buffered (up to the specified max. size). **Note:** if a buffer size is configured
but no extended data handler is registered when channel is spawning the command then an exception will occur.
+### Symbolic links handling
+
+Whenever the server needs to execute a command that may behave differently if applied to a symbolic link instead of its target
+it consults the `AbstractSftpSubsystemHelper#resolvePathResolutionFollowLinks` method. By default, this method simply consults
+the value of the `sftp-auto-follow-links` configuration property (default=*true*).
+
+**Note:** the property is consulted only for cases where there is no clear indication in the standard how to behave for the
+specific command. E.g., the `lsetstat@openssh.com` specifically specifies that symbolic links should not be followed, so the
+implementation does not consult the aforementioned property.
+
## Client-side SFTP
In order to obtain an `SftpClient` instance one needs to use an `SftpClientFactory`:
@@ -424,6 +434,7 @@ Furthermore several [OpenSSH SFTP extensions](https://github.com/openssh/openssh
* `hardlink@openssh.com`
* `posix-rename@openssh.com`
* `statvfs@openssh.com`
+* `lsetstat@openssh.com`
On the server side, the reported standard extensions are configured via the `SftpSubsystem.CLIENT_EXTENSIONS_PROP` configuration
key, and the _OpenSSH_ ones via the `SftpSubsystem.OPENSSH_EXTENSIONS_PROP`.
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java
index 9c4231a..119fe81 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java
@@ -39,6 +39,7 @@ import org.apache.sshd.common.subsystem.sftp.extensions.SupportedParser.Supporte
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.FstatVfsExtensionParser;
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.FsyncExtensionParser;
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.HardLinkExtensionParser;
+import org.apache.sshd.common.subsystem.sftp.extensions.openssh.LSetStatExtensionParser;
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.PosixRenameExtensionParser;
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.StatVfsExtensionParser;
import org.apache.sshd.common.util.GenericUtils;
@@ -62,7 +63,8 @@ public final class ParserUtils {
StatVfsExtensionParser.INSTANCE,
FstatVfsExtensionParser.INSTANCE,
HardLinkExtensionParser.INSTANCE,
- FsyncExtensionParser.INSTANCE
+ FsyncExtensionParser.INSTANCE,
+ LSetStatExtensionParser.INSTANCE
));
private static final NavigableMap<String, ExtensionParser<?>> PARSERS_MAP =
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
index 8590e64..9fb30fe 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
@@ -80,7 +80,7 @@ public abstract class AbstractOpenSSHExtensionParser extends AbstractParser<Open
OpenSSHExtension other = (OpenSSHExtension) obj;
return Objects.equals(getName(), other.getName())
- && Objects.equals(getVersion(), other.getVersion());
+ && Objects.equals(getVersion(), other.getVersion());
}
@Override
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
index e9967ab..64c1249 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.subsystem.sftp.extensions.openssh;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH - section 10</A>
+ * @see <A HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH - section 10</A>
*/
public class FsyncExtensionParser extends AbstractOpenSSHExtensionParser {
public static final String NAME = "fsync@openssh.com";
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/LSetStatExtensionParser.java
similarity index 67%
copy from sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
copy to sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/LSetStatExtensionParser.java
index e9967ab..b60d63c 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/LSetStatExtensionParser.java
@@ -20,14 +20,17 @@
package org.apache.sshd.common.subsystem.sftp.extensions.openssh;
/**
+ * Replicates the functionality of the existing {@code SSH_FXP_SETSTAT} operation
+ * but does not follow symbolic links
+ *
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH - section 10</A>
+ * @see <A HREF="https://www.openssh.com/txt/release-8.0">OpenSSH v8.0 release notes</A>
*/
-public class FsyncExtensionParser extends AbstractOpenSSHExtensionParser {
- public static final String NAME = "fsync@openssh.com";
- public static final FsyncExtensionParser INSTANCE = new FsyncExtensionParser();
+public class LSetStatExtensionParser extends AbstractOpenSSHExtensionParser {
+ public static final String NAME = "lsetstat@openssh.com";
+ public static final LSetStatExtensionParser INSTANCE = new LSetStatExtensionParser();
- public FsyncExtensionParser() {
+ public LSetStatExtensionParser() {
super(NAME);
}
}
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpSubsystemHelper.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpSubsystemHelper.java
index 3423398..d489e5f 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpSubsystemHelper.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpSubsystemHelper.java
@@ -89,6 +89,7 @@ import org.apache.sshd.common.subsystem.sftp.extensions.SpaceAvailableExtensionI
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.AbstractOpenSSHExtensionParser.OpenSSHExtension;
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.FsyncExtensionParser;
import org.apache.sshd.common.subsystem.sftp.extensions.openssh.HardLinkExtensionParser;
+import org.apache.sshd.common.subsystem.sftp.extensions.openssh.LSetStatExtensionParser;
import org.apache.sshd.common.util.EventListenerUtils;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.MapEntryUtils.NavigableMapBuilder;
@@ -161,7 +162,8 @@ public abstract class AbstractSftpSubsystemHelper
Collections.unmodifiableList(
Arrays.asList(
new OpenSSHExtension(FsyncExtensionParser.NAME, "1"),
- new OpenSSHExtension(HardLinkExtensionParser.NAME, "1")
+ new OpenSSHExtension(HardLinkExtensionParser.NAME, "1"),
+ new OpenSSHExtension(LSetStatExtensionParser.NAME, "1")
));
public static final List<String> DEFAULT_OPEN_SSH_EXTENSIONS_NAMES =
@@ -393,7 +395,7 @@ public abstract class AbstractSftpSubsystemHelper
doFStat(buffer, id);
break;
case SftpConstants.SSH_FXP_SETSTAT:
- doSetStat(buffer, id);
+ doSetStat(buffer, id, "", type, null);
break;
case SftpConstants.SSH_FXP_FSETSTAT:
doFSetStat(buffer, id);
@@ -660,11 +662,13 @@ public abstract class AbstractSftpSubsystemHelper
return resolveFileAttributes(p, flags, IoUtils.getLinkOptions(false));
}
- protected void doSetStat(Buffer buffer, int id) throws IOException {
+ protected void doSetStat(
+ Buffer buffer, int id, String extension, int cmd, Boolean followLinks /* null = auto-resolve */)
+ throws IOException {
String path = buffer.getString();
Map<String, Object> attrs = readAttrs(buffer);
try {
- doSetStat(id, path, attrs);
+ doSetStat(id, path, cmd, extension, attrs, followLinks);
} catch (IOException | RuntimeException e) {
sendStatus(prepareReply(buffer), id, e, SftpConstants.SSH_FXP_SETSTAT, path);
return;
@@ -673,13 +677,19 @@ public abstract class AbstractSftpSubsystemHelper
sendStatus(prepareReply(buffer), id, SftpConstants.SSH_FX_OK, "");
}
- protected void doSetStat(int id, String path, Map<String, ?> attrs) throws IOException {
+ protected void doSetStat(
+ int id, String path, int cmd, String extension, Map<String, ?> attrs, Boolean followLinks /* null = auto-resolve */)
+ throws IOException {
if (log.isDebugEnabled()) {
- log.debug("doSetStat({})[id={}] SSH_FXP_SETSTAT (path={}, attrs={})",
- getServerSession(), id, path, attrs);
+ log.debug("doSetStat({})[id={}, cmd={}, extension={}] (path={}, attrs={}, followLinks={})",
+ getServerSession(), id, cmd, extension, path, attrs, followLinks);
}
+
Path p = resolveFile(path);
- doSetAttributes(p, attrs);
+ if (followLinks == null) {
+ followLinks = resolvePathResolutionFollowLinks(cmd, extension, p);
+ }
+ doSetAttributes(p, attrs, followLinks);
}
protected void doFStat(Buffer buffer, int id) throws IOException {
@@ -1579,7 +1589,9 @@ public abstract class AbstractSftpSubsystemHelper
listener.creating(session, p, attrs);
try {
Files.createDirectory(p);
- doSetAttributes(p, attrs);
+ boolean followLinks = resolvePathResolutionFollowLinks(
+ SftpConstants.SSH_FXP_MKDIR, "", p);
+ doSetAttributes(p, attrs, followLinks);
} catch (IOException | RuntimeException e) {
listener.created(session, p, attrs, e);
throw e;
@@ -1682,9 +1694,11 @@ public abstract class AbstractSftpSubsystemHelper
case HardLinkExtensionParser.NAME:
doOpenSSHHardLink(buffer, id);
break;
+ case LSetStatExtensionParser.NAME:
+ doSetStat(buffer, id, extension, -1, Boolean.FALSE);
+ break;
default:
doUnsupportedExtension(buffer, id, extension);
- break;
}
}
@@ -1697,35 +1711,38 @@ public abstract class AbstractSftpSubsystemHelper
}
protected void appendExtensions(Buffer buffer, String supportedVersions) {
- appendVersionsExtension(buffer, supportedVersions);
- appendNewlineExtension(buffer, resolveNewlineValue(getServerSession()));
- appendVendorIdExtension(buffer, VersionProperties.getVersionProperties());
- appendOpenSSHExtensions(buffer);
- appendAclSupportedExtension(buffer);
+ ServerSession session = getServerSession();
+ appendVersionsExtension(buffer, supportedVersions, session);
+ appendNewlineExtension(buffer, session);
+ appendVendorIdExtension(buffer, VersionProperties.getVersionProperties(), session);
+ appendOpenSSHExtensions(buffer, session);
+ appendAclSupportedExtension(buffer, session);
- Map<String, OptionalFeature> extensions = getSupportedClientExtensions();
+ Map<String, OptionalFeature> extensions = getSupportedClientExtensions(session);
int numExtensions = GenericUtils.size(extensions);
- List<String> extras = (numExtensions <= 0) ? Collections.emptyList() : new ArrayList<>(numExtensions);
+ List<String> extras =
+ (numExtensions <= 0) ? Collections.emptyList() : new ArrayList<>(numExtensions);
if (numExtensions > 0) {
- ServerSession session = getServerSession();
boolean debugEnabled = log.isDebugEnabled();
- extensions.forEach((name, f) -> {
+ for (Map.Entry<String, OptionalFeature> ee : extensions.entrySet()) {
+ String name = ee.getKey();
+ OptionalFeature f = ee.getValue();
if (!f.isSupported()) {
if (debugEnabled) {
log.debug("appendExtensions({}) skip unsupported extension={}", session, name);
}
- return;
+ continue;
}
extras.add(name);
- });
+ }
}
+
appendSupportedExtension(buffer, extras);
appendSupported2Extension(buffer, extras);
}
- protected int appendAclSupportedExtension(Buffer buffer) {
- ServerSession session = getServerSession();
+ protected int appendAclSupportedExtension(Buffer buffer, ServerSession session) {
Collection<Integer> maskValues = resolveAclSupportedCapabilities(session);
int mask = AclSupportedParser.AclCapabilities.constructAclCapabilities(maskValues);
if (mask != 0) {
@@ -1772,8 +1789,8 @@ public abstract class AbstractSftpSubsystemHelper
return maskValues;
}
- protected List<OpenSSHExtension> appendOpenSSHExtensions(Buffer buffer) {
- List<OpenSSHExtension> extList = resolveOpenSSHExtensions(getServerSession());
+ protected List<OpenSSHExtension> appendOpenSSHExtensions(Buffer buffer, ServerSession session) {
+ List<OpenSSHExtension> extList = resolveOpenSSHExtensions(session);
if (GenericUtils.isEmpty(extList)) {
return extList;
}
@@ -1820,8 +1837,7 @@ public abstract class AbstractSftpSubsystemHelper
return extList;
}
- protected Map<String, OptionalFeature> getSupportedClientExtensions() {
- ServerSession session = getServerSession();
+ protected Map<String, OptionalFeature> getSupportedClientExtensions(ServerSession session) {
String value = session.getString(CLIENT_EXTENSIONS_PROP);
if (value == null) {
return DEFAULT_SUPPORTED_CLIENT_EXTENSIONS;
@@ -1855,15 +1871,16 @@ public abstract class AbstractSftpSubsystemHelper
*
* @param buffer The {@link Buffer} to append to
* @param value The recommended value - ignored if {@code null}/empty
+ * @param session The {@link ServerSession} for which this extension is added
* @see SftpConstants#EXT_VERSIONS
*/
- protected void appendVersionsExtension(Buffer buffer, String value) {
+ protected void appendVersionsExtension(Buffer buffer, String value, ServerSession session) {
if (GenericUtils.isEmpty(value)) {
return;
}
if (log.isDebugEnabled()) {
- log.debug("appendVersionsExtension({}) value={}", getServerSession(), value);
+ log.debug("appendVersionsExtension({}) value={}", session, value);
}
buffer.putString(SftpConstants.EXT_VERSIONS);
@@ -1876,10 +1893,12 @@ public abstract class AbstractSftpSubsystemHelper
* or use the correct extension name
*
* @param buffer The {@link Buffer} to append to
- * @param value The recommended value - ignored if {@code null}/empty
+ * @param session The {@link ServerSession} for which this extension is added
* @see SftpConstants#EXT_NEWLINE
+ * @see #resolveNewlineValue(ServerSession)
*/
- protected void appendNewlineExtension(Buffer buffer, String value) {
+ protected void appendNewlineExtension(Buffer buffer, ServerSession session) {
+ String value = resolveNewlineValue(session);
if (GenericUtils.isEmpty(value)) {
return;
}
@@ -1915,20 +1934,22 @@ public abstract class AbstractSftpSubsystemHelper
* <LI>{@code artifactId} - as the product name</LI>
* <LI>{@code version} - as the product version</LI>
* </UL>
+ * @param session The {@link ServerSession} for which these properties are added
* @see SftpConstants#EXT_VENDOR_ID
* @see <A HREF="http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt">DRAFT 09 - section 4.4</A>
*/
- protected void appendVendorIdExtension(Buffer buffer, Map<String, ?> versionProperties) {
+ protected void appendVendorIdExtension(Buffer buffer, Map<String, ?> versionProperties, ServerSession session) {
if (GenericUtils.isEmpty(versionProperties)) {
return;
}
if (log.isDebugEnabled()) {
- log.debug("appendVendorIdExtension({}): {}", getServerSession(), versionProperties);
+ log.debug("appendVendorIdExtension({}): {}", session, versionProperties);
}
buffer.putString(SftpConstants.EXT_VENDOR_ID);
- PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(Collections.unmodifiableMap(versionProperties));
+ PropertyResolver resolver =
+ PropertyResolverUtils.toPropertyResolver(Collections.unmodifiableMap(versionProperties));
// placeholder for length
int lenPos = buffer.wpos();
buffer.putInt(0);
@@ -2409,12 +2430,12 @@ public abstract class AbstractSftpSubsystemHelper
return Collections.emptyNavigableMap();
}
- protected void doSetAttributes(Path file, Map<String, ?> attributes) throws IOException {
+ protected void doSetAttributes(Path file, Map<String, ?> attributes, boolean followLinks) throws IOException {
SftpEventListener listener = getSftpEventListenerProxy();
ServerSession session = getServerSession();
listener.modifyingAttributes(session, file, attributes);
try {
- setFileAttributes(file, attributes, IoUtils.getLinkOptions(false));
+ setFileAttributes(file, attributes, IoUtils.getLinkOptions(followLinks));
} catch (IOException | RuntimeException e) {
listener.modifiedAttributes(session, file, attributes, e);
throw e;
@@ -2423,11 +2444,16 @@ public abstract class AbstractSftpSubsystemHelper
}
protected LinkOption[] getPathResolutionLinkOption(int cmd, String extension, Path path) throws IOException {
- ServerSession session = getServerSession();
- boolean followLinks = PropertyResolverUtils.getBooleanProperty(session, AUTO_FOLLOW_LINKS, DEFAULT_AUTO_FOLLOW_LINKS);
+ boolean followLinks = resolvePathResolutionFollowLinks(cmd, extension, path);
return IoUtils.getLinkOptions(followLinks);
}
+ protected boolean resolvePathResolutionFollowLinks(int cmd, String extension, Path path) throws IOException {
+ ServerSession session = getServerSession();
+ return PropertyResolverUtils.getBooleanProperty(
+ session, AUTO_FOLLOW_LINKS, DEFAULT_AUTO_FOLLOW_LINKS);
+ }
+
protected void setFileAttributes(Path file, Map<String, ?> attributes, LinkOption... options) throws IOException {
Set<String> unsupported = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
// Cannot use forEach because of the potential IOException being thrown
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java
index 19bd602..d14cf86 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java
@@ -51,7 +51,9 @@ public class FileHandle extends Handle {
private final Set<StandardOpenOption> openOptions;
private final Collection<FileAttribute<?>> fileAttributes;
- public FileHandle(SftpSubsystem subsystem, Path file, String handle, int flags, int access, Map<String, Object> attrs) throws IOException {
+ public FileHandle(
+ SftpSubsystem subsystem, Path file, String handle, int flags, int access, Map<String, Object> attrs)
+ throws IOException {
super(subsystem, file, handle);
this.access = access;
@@ -67,10 +69,12 @@ public class FileHandle extends Handle {
ServerSession session = subsystem.getServerSession();
SeekableByteChannel channel;
try {
- channel = accessor.openFile(session, subsystem, this, file, handle, openOptions, fileAttrs);
+ channel = accessor.openFile(
+ session, subsystem, this, file, handle, openOptions, fileAttrs);
} catch (UnsupportedOperationException e) {
- channel = accessor.openFile(session, subsystem, this, file, handle, openOptions, IoUtils.EMPTY_FILE_ATTRIBUTES);
- subsystem.doSetAttributes(file, attrs);
+ channel = accessor.openFile(
+ session, subsystem, this, file, handle, openOptions, IoUtils.EMPTY_FILE_ATTRIBUTES);
+ subsystem.doSetAttributes(file, attrs, false);
}
this.fileChannel = channel;
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
index 92a2b29..29f0c9f 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
@@ -767,7 +767,10 @@ public class SftpSubsystem
}
Handle fileHandle = validateHandle(handle, h, Handle.class);
- doSetAttributes(fileHandle.getFile(), attrs);
+ Path path = fileHandle.getFile();
+ boolean followLinks = resolvePathResolutionFollowLinks(
+ SftpConstants.SSH_FXP_FSETSTAT, "", path);
+ doSetAttributes(fileHandle.getFile(), attrs, followLinks);
}
@Override