ec.decode: check the rebuilt .dat is complete before the shards can be deleted (#10768)

A decode ends by deleting the shards it read, and the only thing standing
between that and a bad reconstruction is verifyDecodedVolumeBeforeDelete,
which asks whether .dat and .idx are non-empty. A .dat truncated to a
single byte passes, and the shards -- the only other copy of everything
past the cut -- are deleted on the strength of it.

The server already knows the answer it never checks: FindDatFileSize
returns the extent the EC index references, and WriteDatFile rebuilds to
it. Compare the two once the file is written and fail the decode instead
of reporting a short volume as a good one.

Longer than the extent still verifies -- padding is not missing data --
so only a genuinely short rebuild is rejected.

Needle counts cannot answer this: .idx is written from .ecx, so the count
matches by construction and a truncated .dat still reports every needle.
This commit is contained in:
Chris Lu authored and GitHub committed 2026-08-15 13:28:49 -07:00
1 parent 829064af71
commit fbd85d31b0
3 files changed
+115

No files matched your search

@@ -1007,6 +1007,13 @@ func (vs *VolumeServer) VolumeEcShardsToVolume(ctx context.Context, req *volume_
return nil, fmt.Errorf("WriteDatFile %s: %v", dataBaseFileName, err)
}
// Fail the decode rather than report a short volume: the caller deletes the
// shards once this call returns, and today its only check is that the files
// are non-empty.
if err := erasure_coding.VerifyDecodedDatFile(dataBaseFileName, datFileSize); err != nil {
return nil, err
}
// write .idx file from .ecx and .ecj files
if err := erasure_coding.WriteIdxFileFromEcIndex(indexBaseFileName); err != nil {
return nil, fmt.Errorf("WriteIdxFileFromEcIndex %s: %v", v.IndexBaseFileName(), err)