This is an automated email from the ASF dual-hosted git repository. twolf pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 067e734ae9ded8391a7ecd35e631b6144783236b Author: Thomas Wolf <tw...@apache.org> AuthorDate: Mon Nov 22 14:40:54 2021 +0100 SFTP: support file timestamps with sub-millisecond resolution Refactor the reading and writing of FileTimes to avoid manual nanosecond calculations. Go through Instant instead. On writing, this may saturate, but for times that far in the past or future nanos are irrelevant. On reading, drop nanos if the value would be outside the range of Instant. The advantage of doing it this way is that we can actually report and recover sub-millisecond timestamps (for SFTP version >= 4). Newer Java does report file timestamps with the OS resolution (100ns on Windows/ NTFS, may be even lower on other OSes depending on the file system). --- .../org/apache/sshd/sftp/common/SftpHelper.java | 42 +++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/SftpHelper.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/SftpHelper.java index 1bf606b..5de859d 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/SftpHelper.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/SftpHelper.java @@ -40,6 +40,8 @@ import java.nio.file.attribute.PosixFilePermissions; import java.nio.file.attribute.UserPrincipal; import java.nio.file.attribute.UserPrincipalNotFoundException; import java.security.Principal; +import java.time.DateTimeException; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -375,8 +377,8 @@ public final class SftpHelper { } if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_ACMODTIME) != 0) { - buffer = SftpHelper.writeTime(buffer, sftpVersion, flagsMask, attributes.getAccessTime()); - buffer = SftpHelper.writeTime(buffer, sftpVersion, flagsMask, attributes.getModifyTime()); + buffer = writeTime(buffer, sftpVersion, flagsMask, attributes.getAccessTime()); + buffer = writeTime(buffer, sftpVersion, flagsMask, attributes.getModifyTime()); } } else if (sftpVersion >= SftpConstants.SFTP_V4) { for (Attribute a : flags) { @@ -437,16 +439,16 @@ public final class SftpHelper { buffer.putInt(attributes.getPermissions()); } if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) { - buffer = SftpHelper.writeTime(buffer, sftpVersion, flagsMask, attributes.getAccessTime()); + buffer = writeTime(buffer, sftpVersion, flagsMask, attributes.getAccessTime()); } if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_CREATETIME) != 0) { - buffer = SftpHelper.writeTime(buffer, sftpVersion, flagsMask, attributes.getCreateTime()); + buffer = writeTime(buffer, sftpVersion, flagsMask, attributes.getCreateTime()); } if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) { - buffer = SftpHelper.writeTime(buffer, sftpVersion, flagsMask, attributes.getModifyTime()); + buffer = writeTime(buffer, sftpVersion, flagsMask, attributes.getModifyTime()); } if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_ACL) != 0) { - buffer = SftpHelper.writeACLs(buffer, sftpVersion, attributes.getAcl()); + buffer = writeACLs(buffer, sftpVersion, attributes.getAcl()); } // TODO: for v5 ? 6? add CTIME (see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-16 - v6) @@ -455,7 +457,7 @@ public final class SftpHelper { } if ((flagsMask & SftpConstants.SSH_FILEXFER_ATTR_EXTENDED) != 0) { - buffer = SftpHelper.writeExtensions(buffer, attributes.getExtensions()); + buffer = writeExtensions(buffer, attributes.getExtensions()); } return buffer; @@ -1165,9 +1167,7 @@ public final class SftpHelper { 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); + buffer.putInt(time.toInstant().getNano()); } } else { buffer.putInt(time.to(TimeUnit.SECONDS)); @@ -1187,13 +1187,23 @@ public final class SftpHelper { public static FileTime readTime(Buffer buffer, int version, int flags) { // for v3 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#page-8 // for v6 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-16 - long secs = (version >= SftpConstants.SFTP_V4) ? buffer.getLong() : buffer.getUInt(); - long millis = TimeUnit.SECONDS.toMillis(secs); - if ((version >= SftpConstants.SFTP_V4) && ((flags & SftpConstants.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0)) { - long nanoseconds = buffer.getUInt(); - millis += TimeUnit.NANOSECONDS.toMillis(nanoseconds); + long secs; + if (version >= SftpConstants.SFTP_V4) { + secs = buffer.getLong(); + if ((flags & SftpConstants.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) { + long nanos = buffer.getUInt(); + if (nanos != 0) { + try { + return FileTime.from(Instant.ofEpochSecond(secs, nanos)); + } catch (DateTimeException | ArithmeticException e) { + // Beyond Instant range; drop nanos + } + } + } + } else { + secs = buffer.getUInt(); } - return FileTime.from(millis, TimeUnit.MILLISECONDS); + return FileTime.from(secs, TimeUnit.SECONDS); } /**