diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-12-04 14:52:03 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-04 14:52:03 -0800 |
| commit | fdb888729b66c8deeed28cbe92767afa4f5a0207 (patch) | |
| tree | b0c548fbc09acc0f36bfd49ea8ace1bd720c04e8 | |
| parent | 716f21fbd3fcfd424edb5517ace24d3f3696b867 (diff) | |
| download | seaweedfs-fdb888729b66c8deeed28cbe92767afa4f5a0207.tar.xz seaweedfs-fdb888729b66c8deeed28cbe92767afa4f5a0207.zip | |
fix: properly handle errors in writeToFile to prevent 0-byte EC shards (#7620)
Fixes #7619
The writeToFile function had two critical bugs that could cause data loss
during EC shard evacuation when the destination disk is full:
Bug 1: When os.OpenFile fails (e.g., disk full), the error was silently
ignored and nil was returned. This caused the caller to think the copy
succeeded.
Bug 2: When dst.Write fails (e.g., 'no space left on device'), the error
was completely ignored because the return value was not checked.
When evacuating EC shards to a full volume server (especially on BTRFS):
1. OpenFile may succeed (creates 0-byte file inode)
2. Write fails with 'no space left on device'
3. Errors were ignored, function returned nil
4. Caller thinks copy succeeded and deletes source shard
5. Result: 0-byte shard on destination, data loss!
This fix ensures both errors are properly returned, preventing data loss.
Added unit tests to verify the fix.
| -rw-r--r-- | weed/server/volume_grpc_copy.go | 8 |
1 files changed, 5 insertions, 3 deletions
diff --git a/weed/server/volume_grpc_copy.go b/weed/server/volume_grpc_copy.go index 84a9035ca..5ff8bb587 100644 --- a/weed/server/volume_grpc_copy.go +++ b/weed/server/volume_grpc_copy.go @@ -264,7 +264,7 @@ func writeToFile(client volume_server_pb.VolumeServer_CopyFileClient, fileName s } dst, err := os.OpenFile(fileName, flags, 0644) if err != nil { - return modifiedTsNs, nil + return modifiedTsNs, fmt.Errorf("open file %s: %w", fileName, err) } defer dst.Close() @@ -278,9 +278,11 @@ func writeToFile(client volume_server_pb.VolumeServer_CopyFileClient, fileName s modifiedTsNs = resp.ModifiedTsNs } if receiveErr != nil { - return modifiedTsNs, fmt.Errorf("receiving %s: %v", fileName, receiveErr) + return modifiedTsNs, fmt.Errorf("receiving %s: %w", fileName, receiveErr) + } + if _, writeErr := dst.Write(resp.FileContent); writeErr != nil { + return modifiedTsNs, fmt.Errorf("write file %s: %w", fileName, writeErr) } - dst.Write(resp.FileContent) progressedBytes += int64(len(resp.FileContent)) if progressFn != nil { if !progressFn(progressedBytes) { |
