filen: fix incorrect modtime after updating a file or setting its modtime

Object.Update and SetModTime left the in-memory modtime at the
source's nanosecond precision, while the server stores milliseconds.
Afterwards the in-memory state did not match what a fresh List would
report, which broke wrappers that compare modtimes (e.g. the hasher
backend's fingerprint, which made it lose track of hashes whenever a
file was replaced).

The SDK serializes modtimes as UnixMilli and NewIncompleteFile
already rounds to milliseconds, so Put and the chunked upload path
are unaffected. Round the modtime to milliseconds in Update and
SetModTime too, so every path holds the same value the server
returns, without needing to re-read the metadata from the server
after upload.

Fixes #9308
This commit is contained in:
Nick Craig-Wood
2026-07-13 11:22:04 +01:00
parent 7c803db8f3
commit 2eaaf91b83
+8 -2
View File
@@ -710,7 +710,10 @@ func (o *Object) Storable() bool {
// SetModTime sets the metadata on the object to set the modification date // SetModTime sets the metadata on the object to set the modification date
func (o *Object) SetModTime(ctx context.Context, t time.Time) error { func (o *Object) SetModTime(ctx context.Context, t time.Time) error {
o.file.LastModified = t // The server stores modtimes with millisecond precision so round
// here too to keep the in-memory modtime identical to the one a
// fresh listing returns.
o.file.LastModified = t.Round(time.Millisecond)
return o.fs.filen.UpdateMeta(ctx, o.file) return o.fs.filen.UpdateMeta(ctx, o.file)
} }
@@ -752,7 +755,10 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
fs.Logf(option, "Unsupported mandatory option: %v", option) fs.Logf(option, "Unsupported mandatory option: %v", option)
} }
} }
newModTime := src.ModTime(ctx) // The server stores modtimes with millisecond precision so round
// here too to keep the in-memory modtime identical to the one a
// fresh listing returns.
newModTime := src.ModTime(ctx).Round(time.Millisecond)
newIncomplete, err := o.file.NewFromBase(o.fs.filen.FileEncryptionVersion) newIncomplete, err := o.file.NewFromBase(o.fs.filen.FileEncryptionVersion)
if err != nil { if err != nil {
return err return err