From 1d8a6e832c5662536ab95deba04cc2801089e985 Mon Sep 17 00:00:00 2001 From: qzhello <951012707@qq.com> Date: Sat, 4 Jul 2026 02:18:46 +0800 Subject: [PATCH] fix(ec): detect truncated .ecx instead of treating it as clean EOF (#10217) --- weed/storage/erasure_coding/ec_decoder.go | 13 +++++++------ weed/storage/erasure_coding/ec_decoder_test.go | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/weed/storage/erasure_coding/ec_decoder.go b/weed/storage/erasure_coding/ec_decoder.go index d6175c215..1fd890e8e 100644 --- a/weed/storage/erasure_coding/ec_decoder.go +++ b/weed/storage/erasure_coding/ec_decoder.go @@ -150,12 +150,13 @@ func iterateEcxFile(baseFileName string, processNeedleFn func(key types.NeedleId buf := make([]byte, types.NeedleMapEntrySize) for { - n, err := ecxFile.Read(buf) - if n != types.NeedleMapEntrySize { - if err == io.EOF { - return nil - } - return err + // .ecx is a sealed index: a partial trailing record means corruption, not a torn append. + _, err := io.ReadFull(ecxFile, buf) + if err == io.EOF { + return nil + } + if err != nil { + return fmt.Errorf("read ecx %s.ecx: %w", baseFileName, err) } key, offset, size := idx.IdxFileEntry(buf) if processNeedleFn != nil { diff --git a/weed/storage/erasure_coding/ec_decoder_test.go b/weed/storage/erasure_coding/ec_decoder_test.go index cd639fe9d..6793d890d 100644 --- a/weed/storage/erasure_coding/ec_decoder_test.go +++ b/weed/storage/erasure_coding/ec_decoder_test.go @@ -73,6 +73,22 @@ func TestHasLiveNeedles_EmptyFileIsFalse(t *testing.T) { } } +func TestHasLiveNeedles_TruncatedEcxErrors(t *testing.T) { + dir := t.TempDir() + + base := filepath.Join(dir, "foo_1") + + entry := makeNeedleMapEntry(types.NeedleId(1), types.Offset{}, types.TombstoneFileSize) + truncated := append(entry, entry[:types.NeedleIdSize]...) + if err := os.WriteFile(base+".ecx", truncated, 0644); err != nil { + t.Fatalf("write ecx: %v", err) + } + + if _, err := erasure_coding.HasLiveNeedles(base); err == nil { + t.Fatalf("expected error for truncated ecx") + } +} + func makeNeedleMapEntry(key types.NeedleId, offset types.Offset, size types.Size) []byte { b := make([]byte, types.NeedleIdSize+types.OffsetSize+types.SizeSize) types.NeedleIdToBytes(b[0:types.NeedleIdSize], key)