From eebffd9df67f5f38420d44b25c3347dd8c735ec6 Mon Sep 17 00:00:00 2001 From: chenshi Date: Wed, 29 Apr 2026 18:22:29 +0000 Subject: [PATCH] fix(storage): fix verifyDeletedNeedleIntegrity using wrong offset (#9273) * fix(storage): fix verifyDeletedNeedleIntegrity using wrong offset verifyDeletedNeedleIntegrity was ignoring the offset recorded in the index file and instead always reading from fileSize-DiskSize(0), which is only correct when the deleted needle happens to be the very last entry in the .dat file. When a deletion tombstone is written in the middle of the volume (e.g. after subsequent writes), the function reads the wrong bytes, causing the key-mismatch check to either silently pass (if the bytes happen to form a valid needle with the same ID) or produce a spurious integrity error. Fix: accept the actual offset from the index entry and use types.TombstoneFileSize as the size, mirroring the approach used by verifyNeedleIntegrity for live needles. * fix(storage): fix error message in doCheckAndFixVolumeData The error message in the deleted-needle branch referenced verifyNeedleIntegrity, but the function being called is verifyDeletedNeedleIntegrity. This makes log output misleading when a deleted-needle integrity check fails. Suggested by gemini-code-assist review on PR #9273. --------- Co-authored-by: chenshi5012 --- weed/storage/volume_checking.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/weed/storage/volume_checking.go b/weed/storage/volume_checking.go index 98eb71c06..29d84f3a9 100644 --- a/weed/storage/volume_checking.go +++ b/weed/storage/volume_checking.go @@ -155,8 +155,8 @@ func doCheckAndFixVolumeData(v *Volume, indexFile *os.File, indexOffset int64) ( } if size < 0 { // read the deletion entry - if lastAppendAtNs, err = verifyDeletedNeedleIntegrity(v.DataBackend, v.Version(), key); err != nil { - return lastAppendAtNs, fmt.Errorf("verifyNeedleIntegrity %s failed: %v", indexFile.Name(), err) + if lastAppendAtNs, err = verifyDeletedNeedleIntegrity(v.DataBackend, v.Version(), offset.ToActualOffset(), key); err != nil { + return lastAppendAtNs, fmt.Errorf("verifyDeletedNeedleIntegrity %s failed: %v", indexFile.Name(), err) } } else { if lastAppendAtNs, err = verifyNeedleIntegrity(v.DataBackend, v.Version(), offset.ToActualOffset(), key, size); err != nil { @@ -241,16 +241,11 @@ func verifyNeedleIntegrity(datFile backend.BackendStorageFile, v needle.Version, return n.AppendAtNs, err } -func verifyDeletedNeedleIntegrity(datFile backend.BackendStorageFile, v needle.Version, key types.NeedleId) (lastAppendAtNs uint64, err error) { +func verifyDeletedNeedleIntegrity(datFile backend.BackendStorageFile, v needle.Version, offset int64, key types.NeedleId) (lastAppendAtNs uint64, err error) { n := new(needle.Needle) - size := n.DiskSize(v) - var fileSize int64 - fileSize, _, err = datFile.GetStat() - if err != nil { - return 0, fmt.Errorf("GetStat: %w", err) - } - if err = n.ReadData(datFile, fileSize-size, types.Size(0), v); err != nil { - return n.AppendAtNs, fmt.Errorf("read data [%d,%d) : %v", fileSize-size, size, err) + size := types.TombstoneFileSize + if err = n.ReadData(datFile, offset, size, v); err != nil { + return n.AppendAtNs, fmt.Errorf("read data [%d,%d) : %v", offset, offset+needle.GetActualSize(size, v), err) } if n.Id != key { return n.AppendAtNs, fmt.Errorf("index key %v does not match needle's Id %v", key, n.Id)