From a7fec8004e4dd2a3263414f4c6e297dd62f1ce3e Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 10 Aug 2026 17:23:14 -0700 Subject: [PATCH] filer: repack refuses versioned entries and keeps the remaining TTL S3 object versions may share one chunk list, so deleting the old chunks after a repack could corrupt sibling versions; reject those entries until chunk ownership is tracked. New chunks also carried the full original TTL, restarting needle expiry at repack time while entry expiry stayed anchored to creation: a nearly expired entry left chunks stored for almost a full extra span. Assign the remaining lifetime instead, and reject entries already past it. --- weed/server/filer_server_format.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/weed/server/filer_server_format.go b/weed/server/filer_server_format.go index fa2211c1e..94ba095b6 100644 --- a/weed/server/filer_server_format.go +++ b/weed/server/filer_server_format.go @@ -260,6 +260,12 @@ func (fs *FilerServer) formatRepack(ctx context.Context, w http.ResponseWriter, writeJsonError(w, r, http.StatusBadRequest, errors.New("cannot repack hard-linked or remote entries")) return } + // S3 object versions may share one chunk list; deleting the old chunks + // here would corrupt sibling versions. + if entry.IsS3Versioning() { + writeJsonError(w, r, http.StatusBadRequest, errors.New("cannot repack S3-versioned entries")) + return + } for _, chunk := range oldChunks { if chunk.SseType != filer_pb.SSEType_NONE { writeJsonError(w, r, http.StatusBadRequest, errors.New("cannot repack server-side encrypted entries")) @@ -268,8 +274,17 @@ func (fs *FilerServer) formatRepack(ctx context.Context, w http.ResponseWriter, } // Repack rewrites where bytes are cut, never their lifetime: new chunks - // must carry the entry's TTL, not whatever the request query implies. + // carry the entry's remaining TTL, not the request query's nor a restart + // of the original span. so.TtlSeconds = entry.TtlSec + if entry.TtlSec > 0 { + remaining := int64(entry.TtlSec) - int64(time.Since(entry.Crtime)/time.Second) + if remaining <= 0 { + writeJsonError(w, r, http.StatusBadRequest, errors.New("entry TTL has already expired")) + return + } + so.TtlSeconds = int32(remaining) + } size := int64(entry.FileSize) lookup := fs.filer.MasterClient.GetLookupFileIdFunction()