pixeldrain: fix incorrect modtime and missing hash after uploading a file

The objects returned from Put, Move and Update kept the caller's full
nanosecond precision modtime (which write responses echo back) while
the server stores millisecond precision, so until the object was
re-read the modtime did not match what a fresh listing would report.
Update also discarded the upload response so the object had no SHA256
checksum until re-read. This broke wrappers which compare exact
modtimes and hashes, such as the hasher backend's fingerprint and the
VFS cache. Truncate the modtime to millisecond precision to match
what the server stores and populate the object from the upload
response.
This commit is contained in:
Nick Craig-Wood
2026-07-14 14:13:49 +01:00
parent 397c0f872a
commit 72907a7a90
2 changed files with 20 additions and 5 deletions
+5
View File
@@ -179,6 +179,11 @@ func (f *Fs) nodeToObject(node FilesystemNode) (o *Object) {
// operations. Saving it here would confuse rclone a lot. So instead we
// strip it here and add it back for every API request we need to perform
node.Path = strings.TrimPrefix(node.Path, f.pathPrefix)
// The server stores modtimes with millisecond precision, but
// write responses echo back the full precision time which was
// sent, so truncate to make the object identical to the one a
// fresh listing returns.
node.Modified = node.Modified.Truncate(time.Millisecond)
return &Object{fs: f, base: node}
}
+15 -5
View File
@@ -459,7 +459,10 @@ func (f *Fs) About(ctx context.Context) (usage *fs.Usage, err error) {
func (o *Object) SetModTime(ctx context.Context, modTime time.Time) (err error) {
_, err = o.fs.update(ctx, o.base.Path, fs.Metadata{"mtime": modTime.Format(timeFormat)})
if err == nil {
o.base.Modified = modTime
// The server stores modtimes with millisecond precision so
// truncate here too to keep the in-memory modtime identical
// to the one a fresh listing returns.
o.base.Modified = modTime.Truncate(time.Millisecond)
}
return err
}
@@ -475,12 +478,19 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
//
// The new object may have been created if an error is returned.
func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
// Copy the parameters and update the object
o.base.Modified = src.ModTime(ctx)
// Copy the parameters and update the object, truncating the
// modtime to the millisecond precision the server stores it with
o.base.Modified = src.ModTime(ctx).Truncate(time.Millisecond)
o.base.FileSize = src.Size()
o.base.SHA256Sum, _ = src.Hash(ctx, hash.SHA256)
_, err = o.fs.Put(ctx, in, o, options...)
return err
newObject, err := o.fs.Put(ctx, in, o, options...)
if err != nil {
return err
}
// Keep the metadata from the upload response so the object has
// the hash the server computed.
o.base = newObject.(*Object).base
return nil
}
// Remove an object