From 6ed0079d52292205f5b7dbd54f7d6aed260e2dc1 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 4 May 2026 10:51:02 +0100 Subject: [PATCH] filen: fix incorrect in-memory modtime after upload Put and Update were returning Objects whose in-memory modtime was the nanosecond-precision source modtime, while the server stores millisecond precision. After the upload returned, 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). The standard for backends is to re-read metadata after Upload so this re-reads the file via FindFile after the upload completes so the Object reflects the server's authoritative state. The chunked upload path already re-reads via NewObject in multiThreadCopy, so this only needs to apply to Put and Update. Fixes #9308 --- backend/filen/filen.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/backend/filen/filen.go b/backend/filen/filen.go index 783ad45b1..adf89ea62 100644 --- a/backend/filen/filen.go +++ b/backend/filen/filen.go @@ -339,7 +339,10 @@ func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options . if err != nil { return nil, err } - uploadedFile, err := f.filen.UploadFile(ctx, incompleteFile, in) + if _, err := f.filen.UploadFile(ctx, incompleteFile, in); err != nil { + return nil, err + } + uploadedFile, err := f.readMetadata(ctx, resolvedPath) if err != nil { return nil, err } @@ -350,6 +353,22 @@ func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options . }, nil } +// readMetadata re-reads the file's metadata from the server after an +// upload so the in-memory state reflects what is actually stored +// (e.g. modtime at the server's precision). Without this, callers +// would see higher-precision modtimes locally than List would return, +// breaking layers like the hasher backend that fingerprint by modtime. +func (f *Fs) readMetadata(ctx context.Context, resolvedPath string) (*types.File, error) { + file, err := f.filen.FindFile(ctx, resolvedPath) + if err != nil { + return nil, err + } + if file == nil { + return nil, fs.ErrorObjectNotFound + } + return file, nil +} + // PutStream uploads to the remote path with the modTime given of indeterminate size // // May create the object even if it returns an error - if so @@ -760,7 +779,10 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op newIncomplete.LastModified = newModTime newIncomplete.Created = newModTime newIncomplete.SetMimeType(fs.MimeType(ctx, src)) - uploadedFile, err := o.fs.filen.UploadFile(ctx, newIncomplete, in) + if _, err := o.fs.filen.UploadFile(ctx, newIncomplete, in); err != nil { + return err + } + uploadedFile, err := o.fs.readMetadata(ctx, o.fs.resolvePath(o.path)) if err != nil { return err }