From 03f857dc2b0f58ff398d412e39410c42930161be Mon Sep 17 00:00:00 2001 From: chrislu Date: Mon, 27 Oct 2025 19:50:28 -0700 Subject: [PATCH] fix: add validation for EC shard counts from VolumeInfo - Validate DataShards/ParityShards are positive and within MaxShardCount - Prevent zero or invalid values that could cause divide-by-zero - Fallback to defaults if validation fails, with warning log - VolumeEcShardsGenerate now preserves existing EC config when regenerating - Critical safety fix for corrupted or legacy .vif files --- weed/server/volume_grpc_erasure_coding.go | 18 ++++++++++++++++-- weed/storage/erasure_coding/ec_volume.go | 21 +++++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/weed/server/volume_grpc_erasure_coding.go b/weed/server/volume_grpc_erasure_coding.go index 522432dde..dca9c5866 100644 --- a/weed/server/volume_grpc_erasure_coding.go +++ b/weed/server/volume_grpc_erasure_coding.go @@ -50,9 +50,23 @@ func (vs *VolumeServer) VolumeEcShardsGenerate(ctx context.Context, req *volume_ return nil, fmt.Errorf("existing collection:%v unexpected input: %v", v.Collection, req.Collection) } - // Create EC context (Phase 1: always uses default 10+4) + // Create EC context - prefer existing .vif config if present (for regeneration scenarios) ecCtx := erasure_coding.NewDefaultECContext(req.Collection, needle.VolumeId(req.VolumeId)) - glog.V(0).Infof("Using EC context for volume %d: %s", req.VolumeId, ecCtx.String()) + if volumeInfo, _, found, _ := volume_info.MaybeLoadVolumeInfo(baseFileName + ".vif"); found && volumeInfo.EcShardConfig != nil { + ds := int(volumeInfo.EcShardConfig.DataShards) + ps := int(volumeInfo.EcShardConfig.ParityShards) + + // Validate and use existing EC config + if ds > 0 && ps > 0 && ds+ps <= erasure_coding.MaxShardCount { + ecCtx.DataShards = ds + ecCtx.ParityShards = ps + glog.V(0).Infof("Using existing EC config for volume %d: %s", req.VolumeId, ecCtx.String()) + } else { + glog.Warningf("Invalid EC config in .vif for volume %d (data=%d, parity=%d), using defaults", req.VolumeId, ds, ps) + } + } else { + glog.V(0).Infof("Using default EC config for volume %d: %s", req.VolumeId, ecCtx.String()) + } shouldCleanup := true defer func() { diff --git a/weed/storage/erasure_coding/ec_volume.go b/weed/storage/erasure_coding/ec_volume.go index 9074bd9ad..3e323163e 100644 --- a/weed/storage/erasure_coding/ec_volume.go +++ b/weed/storage/erasure_coding/ec_volume.go @@ -77,13 +77,22 @@ func NewEcVolume(diskType types.DiskType, dir string, dirIdx string, collection // Initialize EC context from .vif if present; fallback to defaults if volumeInfo.EcShardConfig != nil { - ev.ECContext = &ECContext{ - Collection: collection, - VolumeId: vid, - DataShards: int(volumeInfo.EcShardConfig.DataShards), - ParityShards: int(volumeInfo.EcShardConfig.ParityShards), + ds := int(volumeInfo.EcShardConfig.DataShards) + ps := int(volumeInfo.EcShardConfig.ParityShards) + + // Validate shard counts to prevent zero or invalid values + if ds <= 0 || ps <= 0 || ds+ps > MaxShardCount { + glog.Warningf("Invalid EC config in VolumeInfo for volume %d (data=%d, parity=%d), using defaults", vid, ds, ps) + ev.ECContext = NewDefaultECContext(collection, vid) + } else { + ev.ECContext = &ECContext{ + Collection: collection, + VolumeId: vid, + DataShards: ds, + ParityShards: ps, + } + glog.V(1).Infof("Loaded EC config from VolumeInfo for volume %d: %s", vid, ev.ECContext.String()) } - glog.V(1).Infof("Loaded EC config from VolumeInfo for volume %d: %s", vid, ev.ECContext.String()) } else { ev.ECContext = NewDefaultECContext(collection, vid) }