From 26754fca4d65a78db1323a9f017cb2c2cc3f4a54 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 13 Jun 2026 22:15:13 -0700 Subject: [PATCH] fix(ec): don't fabricate a stub .vif when mounting an EC volume (#9951) When an EC volume's .vif was missing, NewEcVolume wrote a stub holding only the version. That stub implies the default 10+4 ratio with DatFileSize=0 and no encode identity, which the custom-ratio resolver and the startup credibility checks then read as an authoritative config -- masking the real ratio of a custom-ratio volume and defeating the byte-exact .vif gate. Mount with in-memory defaults instead and leave the real .vif to the encoder or a recovery tool. The Rust volume server already behaves this way. --- weed/storage/erasure_coding/ec_volume.go | 9 ++++-- weed/storage/erasure_coding/ec_volume_test.go | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/weed/storage/erasure_coding/ec_volume.go b/weed/storage/erasure_coding/ec_volume.go index 7ea3373fb..ac919b37a 100644 --- a/weed/storage/erasure_coding/ec_volume.go +++ b/weed/storage/erasure_coding/ec_volume.go @@ -173,8 +173,13 @@ func NewEcVolume(diskType types.DiskType, dir string, dirIdx string, collection ev.ECContext = NewDefaultECContext(collection, vid) } } else { - glog.Warningf("vif file not found,volumeId:%d, filename:%s", vid, vifFileName) - volume_info.SaveVolumeInfo(dataBaseFileName+".vif", &volume_server_pb.VolumeInfo{Version: uint32(ev.Version)}) + // Don't fabricate a stub .vif here: a version-only stub implies the + // default 10+4 ratio with DatFileSize=0 and no encode identity, which + // the custom-ratio resolver and the startup credibility checks must not + // mistake for an authoritative config. Mount with in-memory defaults and + // leave the real .vif to the encoder or a recovery tool (the Rust volume + // server already behaves this way). + glog.Warningf("vif file not found, using defaults, volumeId:%d, filename:%s", vid, vifFileName) ev.ECContext = NewDefaultECContext(collection, vid) } diff --git a/weed/storage/erasure_coding/ec_volume_test.go b/weed/storage/erasure_coding/ec_volume_test.go index 0864a2bd4..639a4c157 100644 --- a/weed/storage/erasure_coding/ec_volume_test.go +++ b/weed/storage/erasure_coding/ec_volume_test.go @@ -91,3 +91,31 @@ func TestNewEcVolumeLoadsEncodeTsNs(t *testing.T) { t.Errorf("EncodeTsNs = %d, want %d", ev.EncodeTsNs, tsNs) } } + +// TestNewEcVolumeDoesNotWriteStubVif pins that mounting an EC volume whose .vif +// is missing does NOT fabricate a stub .vif. A version-only stub would imply +// the default ratio with DatFileSize=0 and no encode identity, which the +// custom-ratio resolver and startup credibility checks must not trust. +func TestNewEcVolumeDoesNotWriteStubVif(t *testing.T) { + dir := t.TempDir() + const vid = needle.VolumeId(124) + base := EcShardFileName("", dir, int(vid)) + + if err := os.WriteFile(base+".ecx", nil, 0o644); err != nil { + t.Fatalf("write .ecx: %v", err) + } + + ev, err := NewEcVolume(types.HardDriveType, dir, dir, "", vid) + if err != nil { + t.Fatalf("NewEcVolume: %v", err) + } + defer ev.Close() + + if _, statErr := os.Stat(base + ".vif"); !os.IsNotExist(statErr) { + t.Fatalf("mounting without a .vif must not create one, stat err=%v", statErr) + } + // Mount still succeeds with the build's default EC ratio in memory. + if ev.ECContext == nil || ev.ECContext.DataShards != int(DataShardsCount) { + t.Fatalf("expected default EC context, got %+v", ev.ECContext) + } +}