From b63610cf8f3f1cb77fc3ba82d22fc868e0992c5a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 19 May 2026 09:58:47 -0700 Subject: [PATCH] volume: accept legacy needle CRC encoding on read (#9564) Volumes written by versions before 3.09 (commit 056c480eb) store the needle checksum using the deprecated CRC.Value() transform. When the read path moved into readNeedleTail, the fallback that accepts both encodings was dropped, so .dat files copied from old installs now fail verification with "invalid CRC ... data on disk corrupted" even though the data is intact. Restore the dual check, matching the surviving fallback in volume_read.go. --- weed/storage/needle/needle_read_tail.go | 8 +-- weed/storage/needle/needle_read_tail_test.go | 53 ++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 weed/storage/needle/needle_read_tail_test.go diff --git a/weed/storage/needle/needle_read_tail.go b/weed/storage/needle/needle_read_tail.go index dfaab1706..156dbbf65 100644 --- a/weed/storage/needle/needle_read_tail.go +++ b/weed/storage/needle/needle_read_tail.go @@ -13,10 +13,10 @@ func (n *Needle) readNeedleTail(needleBody []byte, version Version) error { if len(n.Data) > 0 { expectedChecksum := CRC(util.BytesToUint32(needleBody[0:NeedleChecksumSize])) dataChecksum := NewCRC(n.Data) - if expectedChecksum != dataChecksum { - // the crc.Value() function is to be deprecated. this double checking is for backward compatibility - // with seaweed version using crc.Value() instead of uint32(crc), which appears in commit 056c480eb - // and switch appeared in version 3.09. + if expectedChecksum != dataChecksum && uint32(expectedChecksum) != dataChecksum.Value() { + // crc.Value() is the deprecated legacy transform used before commit 056c480eb + // (volume server <3.09). Accept either encoding so .dat files written by older + // versions still verify after an upgrade. stats.VolumeServerHandlerCounter.WithLabelValues(stats.ErrorCRC).Inc() return fmt.Errorf("invalid CRC for needle %v (got %08x, want %08x), data on disk corrupted: %w", n.Id, dataChecksum, expectedChecksum, ErrorCorrupted) } diff --git a/weed/storage/needle/needle_read_tail_test.go b/weed/storage/needle/needle_read_tail_test.go new file mode 100644 index 000000000..b5817a511 --- /dev/null +++ b/weed/storage/needle/needle_read_tail_test.go @@ -0,0 +1,53 @@ +package needle + +import ( + "testing" + + . "github.com/seaweedfs/seaweedfs/weed/storage/types" + "github.com/seaweedfs/seaweedfs/weed/util" +) + +// .dat files written by volume server versions prior to 3.09 (before commit +// 056c480eb) store the needle checksum using the legacy CRC.Value() transform. +// Reads of such needles must still verify after an upgrade. +func TestReadNeedleTailLegacyChecksum(t *testing.T) { + data := []byte("hello seaweed") + rawChecksum := NewCRC(data) + legacyChecksum := rawChecksum.Value() + + tail := make([]byte, NeedleChecksumSize+TimestampSize) + util.Uint32toBytes(tail[0:NeedleChecksumSize], legacyChecksum) + + n := &Needle{Data: data} + if err := n.readNeedleTail(tail, Version3); err != nil { + t.Fatalf("legacy checksum should verify, got %v", err) + } + if n.Checksum != rawChecksum { + t.Fatalf("expected raw checksum stored, got %x want %x", uint32(n.Checksum), uint32(rawChecksum)) + } +} + +func TestReadNeedleTailRawChecksum(t *testing.T) { + data := []byte("hello seaweed") + rawChecksum := NewCRC(data) + + tail := make([]byte, NeedleChecksumSize+TimestampSize) + util.Uint32toBytes(tail[0:NeedleChecksumSize], uint32(rawChecksum)) + + n := &Needle{Data: data} + if err := n.readNeedleTail(tail, Version3); err != nil { + t.Fatalf("raw checksum should verify, got %v", err) + } +} + +func TestReadNeedleTailCorrupted(t *testing.T) { + data := []byte("hello seaweed") + + tail := make([]byte, NeedleChecksumSize+TimestampSize) + util.Uint32toBytes(tail[0:NeedleChecksumSize], 0xdeadbeef) + + n := &Needle{Data: data} + if err := n.readNeedleTail(tail, Version3); err == nil { + t.Fatal("expected CRC mismatch error, got nil") + } +}