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.
This commit is contained in:
Chris Lu committed 2026-08-10 17:23:23 -07:00
1 parent b65bcd4afa
commit a7fec8004e
1 file changed
+16 -1
+16 -1
View File
@@ -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()