Skip to content

Commit 67a31fe

Browse files
Use entire 32 bit range
1 parent 1c00ba3 commit 67a31fe

File tree

4 files changed

+223
-101
lines changed

4 files changed

+223
-101
lines changed

pkg/filesystem/pool/block_device_backed_file_pool.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package pool
33
import (
44
"fmt"
55
"io"
6+
"math"
67

78
"github.com/buildbarn/bb-storage/pkg/blockdevice"
89
"github.com/buildbarn/bb-storage/pkg/filesystem"
@@ -138,26 +139,37 @@ func (f *blockDeviceBackedFile) GetNextRegionOffset(off int64, regionType filesy
138139
}
139140

140141
sectorSizeBytes := int64(f.fp.sectorSizeBytes)
141-
sectorIndex := int(off / sectorSizeBytes)
142+
sectorIndex := uint32(off / sectorSizeBytes)
142143
switch regionType {
143144
case filesystem.Data:
144-
index, err := f.sectorMapper.GetNextMappedSector(uint32(sectorIndex))
145-
if err == io.EOF {
145+
if sectorIndex >= f.sectorMapper.GetLogicalSize() {
146146
// Inside the hole at the end of the file.
147147
return 0, io.EOF
148-
} else if err != nil {
149-
return 0, status.Errorf(codes.Internal, "Failed to get next mapped sector from index %d: %v", sectorIndex, err)
148+
}
149+
if f.sectorMapper.GetPhysicalIndex(sectorIndex) != 0 {
150+
// Already inside a sector containing data.
151+
return off, nil
152+
}
153+
// Find the next sector containing data.
154+
index, err := f.sectorMapper.GetNextMappedSector(uint32(sectorIndex))
155+
if err != nil {
156+
return 0, status.Errorf(codes.Internal, "Failed to get next mapped sector: %v", err)
150157
}
151158
return int64(index) * sectorSizeBytes, nil
152159
case filesystem.Hole:
160+
if f.sectorMapper.GetPhysicalIndex(uint32(sectorIndex)) == 0 {
161+
// Already inside a hole.
162+
return off, nil
163+
}
164+
// Find the next sector containing a hole.
153165
index, err := f.sectorMapper.GetNextUnmappedSector(uint32(sectorIndex))
154-
if err != nil {
155-
// This should not be possible as we know that we're inside of the file.
156-
return 0, status.Errorf(codes.Internal, "Failed to get next unmapped sector from index %d: %v", sectorIndex, err)
166+
if err == io.EOF {
167+
// This can happen if the last logical possible
168+
// sector index is mapped.
169+
index = math.MaxUint32
157170
}
158-
// Correct for file ending in the middle of a sector containing data
159-
offset := min(int64(f.sizeBytes), int64(index)*sectorSizeBytes)
160-
return offset, nil
171+
// Correct for file ending in the middle of a sector containing data.
172+
return min(int64(f.sizeBytes), int64(index)*sectorSizeBytes), nil
161173
default:
162174
panic("Unknown region type")
163175
}
@@ -265,6 +277,10 @@ func (f *blockDeviceBackedFile) Truncate(size int64) error {
265277
if size < 0 {
266278
return status.Errorf(codes.InvalidArgument, "Negative truncation size: %d", size)
267279
}
280+
maxFileSize := int64(f.fp.sectorSizeBytes) * int64(math.MaxUint32)
281+
if size > maxFileSize {
282+
return status.Errorf(codes.InvalidArgument, "Truncation size %d exceeds maximum file size allowed by file system %d", size, maxFileSize)
283+
}
268284

269285
sectorIndex := uint32(size / int64(f.fp.sectorSizeBytes))
270286
offsetWithinSector := int(size % int64(f.fp.sectorSizeBytes))
@@ -399,6 +415,10 @@ func (f *blockDeviceBackedFile) WriteAt(p []byte, off int64) (int, error) {
399415
if len(p) == 0 {
400416
return 0, nil
401417
}
418+
maxFileSize := int64(f.fp.sectorSizeBytes) * int64(math.MaxUint32)
419+
if off+int64(len(p)) > maxFileSize {
420+
return 0, status.Errorf(codes.InvalidArgument, "Write exceeds maximum file size: %d", maxFileSize)
421+
}
402422

403423
// As the file may be stored on disk non-contiguously or may be
404424
// a sparse file with holes, the write may need to be decomposed

pkg/filesystem/pool/block_device_backed_file_pool_test.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,21 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
2222

2323
blockDevice := mock.NewMockBlockDevice(ctrl)
2424
sectorAllocator := mock.NewMockSectorAllocator(ctrl)
25+
freedSectors := make([]uint32, 0, 16)
26+
sectorAllocator.EXPECT().FreeList(gomock.Any()).DoAndReturn(func(sectors []uint32) {
27+
toFree := make([]uint32, 0, len(sectors))
28+
for _, s := range sectors {
29+
if s != 0 {
30+
toFree = append(toFree, s)
31+
}
32+
}
33+
freedSectors = append(freedSectors, toFree...)
34+
}).AnyTimes()
2535
pool := pool.NewBlockDeviceBackedFilePool(blockDevice, sectorAllocator, 16)
2636

2737
t.Run("ReadEmptyFile", func(t *testing.T) {
2838
// Test that reads on an empty file work as expected.
39+
freedSectors = freedSectors[:0]
2940
f, err := pool.NewFile()
3041
require.NoError(t, err)
3142

@@ -50,11 +61,12 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
5061
require.Equal(t, 0, n)
5162
require.Equal(t, io.EOF, err)
5263

53-
sectorAllocator.EXPECT().FreeList(make([]uint32, 12))
64+
require.Equal(t, []uint32{}, freedSectors)
5465
require.NoError(t, f.Close())
5566
})
5667

5768
t.Run("Truncate", func(t *testing.T) {
69+
freedSectors = freedSectors[:0]
5870
f, err := pool.NewFile()
5971
require.NoError(t, err)
6072

@@ -96,15 +108,13 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
96108

97109
// Continuing to shrink the file should eventually cause
98110
// the final sector to be released.
99-
sectorAllocator.EXPECT().FreeList([]uint32{5})
100111
require.NoError(t, f.Truncate(80))
101-
// Closing of the file should cause the direct sector
102-
// list to be released.
103-
sectorAllocator.EXPECT().FreeList(make([]uint32, 12))
112+
require.Equal(t, []uint32{5}, freedSectors)
104113
require.NoError(t, f.Close())
105114
})
106115

107116
t.Run("WritesAndReadOnSingleSector", func(t *testing.T) {
117+
freedSectors = freedSectors[:0]
108118
f, err := pool.NewFile()
109119
require.NoError(t, err)
110120

@@ -137,11 +147,12 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
137147
require.Equal(t, io.EOF, err)
138148
require.Equal(t, []byte("\x00\x00Hello\x00world"), buf[:n])
139149

140-
sectorAllocator.EXPECT().FreeList([]uint32{12})
141150
require.NoError(t, f.Close())
151+
require.Equal(t, []uint32{12}, freedSectors)
142152
})
143153

144154
t.Run("WriteFragmentation", func(t *testing.T) {
155+
freedSectors = freedSectors[:0]
145156
f, err := pool.NewFile()
146157
require.NoError(t, err)
147158

@@ -170,11 +181,12 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
170181
require.Equal(t, 137, n)
171182
require.NoError(t, err)
172183

173-
sectorAllocator.EXPECT().FreeList([]uint32{0, 0, 75, 76, 77, 21, 22, 23, 24, 105, 106, 40})
174184
require.NoError(t, f.Close())
185+
require.Equal(t, []uint32{75, 76, 77, 21, 22, 23, 24, 105, 106, 40}, freedSectors)
175186
})
176187

177188
t.Run("WriteSectorAllocatorFailure", func(t *testing.T) {
189+
freedSectors = freedSectors[:0]
178190
f, err := pool.NewFile()
179191
require.NoError(t, err)
180192

@@ -190,11 +202,12 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
190202
require.Equal(t, 6, n)
191203
require.Equal(t, status.Error(codes.ResourceExhausted, "Out of storage space"), err)
192204

193-
sectorAllocator.EXPECT().FreeList([]uint32{0, 0, 75})
194205
require.NoError(t, f.Close())
206+
require.Equal(t, []uint32{75}, freedSectors)
195207
})
196208

197209
t.Run("WriteIOFailure", func(t *testing.T) {
210+
freedSectors = freedSectors[:0]
198211
f, err := pool.NewFile()
199212
require.NoError(t, err)
200213

@@ -212,12 +225,13 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
212225
require.Equal(t, 6, n)
213226
require.Equal(t, status.Error(codes.Internal, "Disk failure"), err)
214227

215-
sectorAllocator.EXPECT().FreeList([]uint32{0, 0, 75})
216228
require.NoError(t, f.Close())
229+
require.Equal(t, []uint32{75}, freedSectors)
217230
})
218231

219232
t.Run("GetNextRegionOffset", func(t *testing.T) {
220233
// Test the behavior on empty files.
234+
freedSectors = freedSectors[:0]
221235
f, err := pool.NewFile()
222236
require.NoError(t, err)
223237

@@ -311,8 +325,8 @@ func TestBlockDeviceBackedFilePool(t *testing.T) {
311325
_, err = f.GetNextRegionOffset(384, filesystem.Hole)
312326
require.Equal(t, io.EOF, err)
313327

314-
sectorAllocator.EXPECT().FreeList([]uint32{0, 0, 0, 0, 0, 0, 0, 0, 5})
315328
require.NoError(t, f.Close())
329+
require.Equal(t, []uint32{5}, freedSectors)
316330
})
317331

318332
t.Run("WriteAt", func(t *testing.T) {

0 commit comments

Comments
 (0)