diff --git a/pkg/local_object_storage/blobstor/fstree/doc.go b/pkg/local_object_storage/blobstor/fstree/doc.go index 3c19974cd0..80c03272c8 100644 --- a/pkg/local_object_storage/blobstor/fstree/doc.go +++ b/pkg/local_object_storage/blobstor/fstree/doc.go @@ -38,6 +38,7 @@ can not collide with protobuf-encoded or ZSTD-compressed data. Files using this prefix can contain an unspecified number of objects (configured via [WithCombinedCountLimit]) that all follow the same serialization pattern: - the first byte is magic 0x7F described above + - one byte version of the subsequent structure (currently zero only) - 32-byte OID of the next object then - 4-byte BE integer length of the next object - followed by protobuf-encoded or ZSTD-compressed object data (of the length @@ -45,7 +46,7 @@ this prefix can contain an unspecified number of objects (configured via Overall the structure is like this: - [0x7F [OID A] [uint32 size]][object A][0x7F [OID B] [uint32 size]][object B]... + [0x7F 0x00 [OID A] [uint32 size]][object A][0x7F 0x00 [OID B] [uint32 size]][object B]... Even though this file contains several objects it has hard links for all of them in the FS tree, so finding a file containing some object doesn't require diff --git a/pkg/local_object_storage/blobstor/fstree/fstree.go b/pkg/local_object_storage/blobstor/fstree/fstree.go index 12098e7845..4ae66cc9ae 100644 --- a/pkg/local_object_storage/blobstor/fstree/fstree.go +++ b/pkg/local_object_storage/blobstor/fstree/fstree.go @@ -75,7 +75,7 @@ const ( combinedLenSize = 4 // combinedIDOff is the offset from the start of the combined prefix to OID. - combinedIDOff = 1 + combinedIDOff = 2 // combinedLengthOff is the offset from the start of the combined prefix to object length. combinedLengthOff = combinedIDOff + oid.Size @@ -365,6 +365,16 @@ func getRawObjectBytes(id oid.ID, p string) ([]byte, error) { return data, nil } +// parseCombinedPrefix checks the given array for combined data prefix and +// returns a subslice with OID and object length if so (nil and 0 otherwise). +func parseCombinedPrefix(p [combinedDataOff]byte) ([]byte, uint32) { + if p[0] != combinedPrefix || p[1] != 0 { // Only version 0 is supported now. + return nil, 0 + } + return p[combinedIDOff:combinedLengthOff], + binary.BigEndian.Uint32(p[combinedLengthOff:combinedDataOff]) +} + func extractCombinedObject(id oid.ID, f *os.File) ([]byte, error) { var ( comBuf [combinedDataOff]byte @@ -383,7 +393,8 @@ func extractCombinedObject(id oid.ID, f *os.File) ([]byte, error) { } return nil, err } - if comBuf[0] != combinedPrefix { + thisOID, l := parseCombinedPrefix(comBuf) + if thisOID == nil { st, err := f.Stat() if err != nil { return nil, err @@ -401,8 +412,7 @@ func extractCombinedObject(id oid.ID, f *os.File) ([]byte, error) { return data, nil } isCombined = true - var l = binary.BigEndian.Uint32(comBuf[combinedLengthOff:combinedDataOff]) - if bytes.Equal(comBuf[combinedIDOff:combinedLengthOff], id[:]) { + if bytes.Equal(thisOID, id[:]) { data = make([]byte, l) _, err = io.ReadFull(f, data) if err != nil {