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);
     }
 
     /**

Reply via email to