diff --git a/cmd/serve/sftp/handler.go b/cmd/serve/sftp/handler.go index 04adaf432..1b3a1ab58 100644 --- a/cmd/serve/sftp/handler.go +++ b/cmd/serve/sftp/handler.go @@ -76,8 +76,9 @@ func (v vfsHandler) Filecmd(r *sftp.Request) error { } } if flags.Acmodtime { - modTime := time.Unix(int64(attr.Mtime), 0) - err := v.Chtimes(r.Filepath, modTime, modTime) + atime := time.Unix(int64(attr.Atime), 0) + mtime := time.Unix(int64(attr.Mtime), 0) + err := v.Chtimes(r.Filepath, atime, mtime) if err != nil { return err } diff --git a/cmd/serve/sftp/handler_test.go b/cmd/serve/sftp/handler_test.go index f85af9543..32cdec212 100644 --- a/cmd/serve/sftp/handler_test.go +++ b/cmd/serve/sftp/handler_test.go @@ -12,6 +12,7 @@ import ( "os" "strings" "testing" + "time" "github.com/pkg/sftp" _ "github.com/rclone/rclone/backend/local" @@ -183,6 +184,24 @@ func TestStatVFS(t *testing.T) { assert.Equal(t, uint64(255), st.Namemax) } +// Test that a SETSTAT request setting the modification time is honoured, even +// when the time happens to be the Unix epoch +func TestSetstatMtime(t *testing.T) { + vfsOpt := vfscommon.Opt + vfsOpt.CacheMode = vfscommon.CacheModeOff + client := startTestServer(t, &vfsOpt) + + const fileName = "file.bin" + require.NoError(t, writeFile(client, fileName, "hello")) + + epoch := time.Unix(0, 0) + require.NoError(t, client.Chtimes(fileName, epoch, epoch)) + + fi, err := client.Stat(fileName) + require.NoError(t, err) + assert.True(t, fi.ModTime().Equal(epoch), "mtime not applied: got %v want %v", fi.ModTime(), epoch) +} + // writeFile writes contents to fileName via the client truncating any existing // data, the way a normal upload does. func writeFile(client *sftp.Client, fileName, contents string) error {