diff --git a/cmd/serve/s3/backend.go b/cmd/serve/s3/backend.go index 913f0dc39..1ac2d2cba 100644 --- a/cmd/serve/s3/backend.go +++ b/cmd/serve/s3/backend.go @@ -424,8 +424,11 @@ func (b *s3Backend) PutObject( return result, _vfs.Chtimes(fp, ti, ti) } // ignore error since the file is successfully created + } - if val, ok := meta["mtime"]; ok { + if val, ok := meta["mtime"]; ok { + ti, err := swift.FloatStringToTime(val) + if err == nil { b.storeModtime(fp, meta, val) return result, _vfs.Chtimes(fp, ti, ti) } diff --git a/cmd/serve/s3/put_test.go b/cmd/serve/s3/put_test.go index 5cb266b63..88998b1b3 100644 --- a/cmd/serve/s3/put_test.go +++ b/cmd/serve/s3/put_test.go @@ -15,6 +15,7 @@ import ( "testing" "time" + "github.com/ncw/swift/v2" "github.com/rclone/gofakes3" "github.com/rclone/rclone/cmd/serve/proxy" "github.com/rclone/rclone/fs" @@ -152,6 +153,28 @@ func TestPutObjectFailureNewKey(t *testing.T) { } } +// TestPutObjectMtime checks that the object's modtime is set from the +// "X-Amz-Meta-Mtime" or "mtime" metadata supplied with the PUT. +func TestPutObjectMtime(t *testing.T) { + want := fstest.Time("2011-12-25T12:59:59.123456789Z") + for _, metaKey := range []string{"X-Amz-Meta-Mtime", "mtime"} { + t.Run(metaKey, func(t *testing.T) { + b, f, bucket := newPutTestBackend(t, "", nil) + ctx := context.Background() + const object = "mtime.txt" + + contents := []byte(random.String(50)) + meta := map[string]string{metaKey: swift.TimeToFloatString(want)} + _, err := b.PutObject(ctx, bucket, object, meta, bytes.NewReader(contents), int64(len(contents))) + require.NoError(t, err) + + o, err := f.NewObject(ctx, path.Join(bucket, object)) + require.NoError(t, err) + fstest.AssertTimeEqualWithPrecision(t, object, want, o.ModTime(ctx), f.Precision()) + }) + } +} + // waitForObject waits for bucket/object to appear on the backing Fs (e.g. // after the VFS write-back delay). func waitForObject(t *testing.T, f fs.Fs, bucket, object string) {