mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-22 22:11:39 +02:00
Missing data read in super block extra parsing (#8392)
* storage: fail on short super block extra read even without an error Some backends (e.g. s3_backend) clear io.EOF after a short read, returning n < len(p) with a nil error. Checking n only when the error is non-nil lets truncated extra data reach proto.Unmarshal with a zero-filled tail. * storage: apply the same short-read check to the super block header A backend that returns n < SuperBlockSize with a nil error previously slipped through and parsed a partially zero-filled header. * storage: test super block extra reads Covers a round trip through ReadSuperBlock, a truncated extra region, a backend returning io.EOF alongside a full read, and a backend returning a short read with a nil error. --------- Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com>
This commit is contained in:
1 parent
1aad4cfe70
commit
d35f4c1c6b
2 files changed
+44
-8
No files matched your search
@@ -2,6 +2,7 @@ package super_block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
@@ -15,11 +16,12 @@ import (
|
||||
func ReadSuperBlock(datBackend backend.BackendStorageFile) (superBlock SuperBlock, err error) {
|
||||
|
||||
header := make([]byte, SuperBlockSize)
|
||||
if n, e := datBackend.ReadAt(header, 0); e != nil {
|
||||
if n != SuperBlockSize {
|
||||
err = fmt.Errorf("cannot read volume %s super block: %v", datBackend.Name(), e)
|
||||
return
|
||||
if n, e := datBackend.ReadAt(header, 0); n != SuperBlockSize {
|
||||
if e == nil {
|
||||
e = io.ErrUnexpectedEOF
|
||||
}
|
||||
err = fmt.Errorf("cannot read volume %s super block: %v", datBackend.Name(), e)
|
||||
return
|
||||
}
|
||||
|
||||
superBlock.Version = needle.Version(header[0])
|
||||
@@ -34,11 +36,12 @@ func ReadSuperBlock(datBackend backend.BackendStorageFile) (superBlock SuperBloc
|
||||
if superBlock.ExtraSize > 0 {
|
||||
// read more
|
||||
extraData := make([]byte, int(superBlock.ExtraSize))
|
||||
if n, e := datBackend.ReadAt(extraData, SuperBlockSize); e != nil {
|
||||
if n != len(extraData) {
|
||||
err = fmt.Errorf("cannot read volume %s super block extra: %v", datBackend.Name(), e)
|
||||
return
|
||||
if n, e := datBackend.ReadAt(extraData, SuperBlockSize); n != len(extraData) {
|
||||
if e == nil {
|
||||
e = io.ErrUnexpectedEOF
|
||||
}
|
||||
err = fmt.Errorf("cannot read volume %s super block extra: %v", datBackend.Name(), e)
|
||||
return
|
||||
}
|
||||
superBlock.Extra = &master_pb.SuperBlockExtra{}
|
||||
err = proto.Unmarshal(extraData, superBlock.Extra)
|
||||
|
||||
@@ -142,3 +142,36 @@ func TestReadSuperBlockTruncatedExtra(t *testing.T) {
|
||||
t.Fatalf("expected read error for truncated extra, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// s3_backend.ReadAt clears io.EOF even when it returns fewer bytes than requested.
|
||||
type shortReadBackendStorageFile struct {
|
||||
backend.DiskFile
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (f *shortReadBackendStorageFile) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
return copy(p, f.data[off:]), nil
|
||||
}
|
||||
|
||||
func TestReadSuperBlockShortReadWithoutError(t *testing.T) {
|
||||
rp, _ := NewReplicaPlacementFromByte(byte(001))
|
||||
ttl, _ := needle.ReadTTL("15d")
|
||||
s := &SuperBlock{
|
||||
Version: needle.GetCurrentVersion(),
|
||||
ReplicaPlacement: rp,
|
||||
Ttl: ttl,
|
||||
Extra: &master_pb.SuperBlockExtra{
|
||||
ErasureCoding: &master_pb.SuperBlockExtra_ErasureCoding{
|
||||
Data: 10,
|
||||
Parity: 4,
|
||||
VolumeIds: []uint32{1, 2, 3},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
data := s.Bytes()
|
||||
_, err := ReadSuperBlock(&shortReadBackendStorageFile{data: data[:len(data)-1]})
|
||||
if err == nil || !strings.Contains(err.Error(), "cannot read") {
|
||||
t.Fatalf("expected read error for short extra read, got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user