@@ -3,6 +3,7 @@ package filepool
3
3
import (
4
4
"fmt"
5
5
"io"
6
+ "strings"
6
7
7
8
re_filesystem "github.com/buildbarn/bb-remote-execution/pkg/filesystem"
8
9
"github.com/buildbarn/bb-storage/pkg/blockdevice"
@@ -33,16 +34,31 @@ func NewBlockDeviceBackedFilePool(blockDevice blockdevice.BlockDevice, sectorAll
33
34
}
34
35
}
35
36
36
- func (fp * blockDeviceBackedFilePool ) NewFile () (filesystem.FileReadWriter , error ) {
37
- return & blockDeviceBackedFile {
38
- fp : fp ,
39
- }, nil
37
+ func (fp * blockDeviceBackedFilePool ) NewFile (sparseReaderAt SparseReaderAt , initialSize uint64 ) (filesystem.FileReadWriter , error ) {
38
+ var err error
39
+ if sparseReaderAt == nil {
40
+ if initialSize != 0 {
41
+ return nil , status .Errorf (codes .InvalidArgument , "initial size must be zero when sparseReaderAt is nil" )
42
+ }
43
+ if sparseReaderAt , err = NewSimpleSparseReaderAt (strings .NewReader ("" ), nil , 0 ); err != nil {
44
+ return nil , status .Errorf (codes .Internal , "failed to create empty SparseReaderAt: %v" , err )
45
+ }
46
+ }
47
+ fr := & blockDeviceBackedFile {
48
+ fp : fp ,
49
+ underlying : NewTruncatableSparseReaderAt (sparseReaderAt , int64 (initialSize )),
50
+ }
51
+ if err = fr .Truncate (int64 (initialSize )); err != nil {
52
+ return nil , status .Errorf (codes .Internal , "failed to truncate file to initial size: %v" , err )
53
+ }
54
+ return fr , nil
40
55
}
41
56
42
57
type blockDeviceBackedFile struct {
43
- fp * blockDeviceBackedFilePool
44
- sizeBytes uint64
45
- sectors []uint32
58
+ fp * blockDeviceBackedFilePool
59
+ underlying TruncatableSparseReaderAt
60
+ sizeBytes uint64
61
+ sectors []uint32
46
62
}
47
63
48
64
func (f * blockDeviceBackedFile ) Close () error {
@@ -115,7 +131,7 @@ func (f *blockDeviceBackedFile) limitBufferToSectorBoundary(p []byte, sectorCoun
115
131
return p
116
132
}
117
133
118
- func (f * blockDeviceBackedFile ) GetNextRegionOffset (off int64 , regionType filesystem.RegionType ) (int64 , error ) {
134
+ func (f * blockDeviceBackedFile ) getNextRegionOffsetForOverlay (off int64 , regionType filesystem.RegionType ) (int64 , error ) {
119
135
// Short circuit calls that are out of bounds.
120
136
if off < 0 {
121
137
return 0 , status .Errorf (codes .InvalidArgument , "Negative seek offset: %d" , off )
@@ -165,30 +181,95 @@ func (f *blockDeviceBackedFile) GetNextRegionOffset(off int64, regionType filesy
165
181
}
166
182
}
167
183
184
+ func (f * blockDeviceBackedFile ) GetNextRegionOffset (off int64 , regionType filesystem.RegionType ) (int64 , error ) {
185
+ // Short circuit calls that are out of bounds.
186
+ if off < 0 {
187
+ return 0 , status .Errorf (codes .InvalidArgument , "Negative seek offset: %d" , off )
188
+ }
189
+ if uint64 (off ) >= f .sizeBytes {
190
+ return 0 , io .EOF
191
+ }
192
+
193
+ // Data is represented by the existence of a written sector in
194
+ // either the overlay or the underlying file. Holes are represented
195
+ // by the absence of a written sector in the overlay _and_ a hole in
196
+ // the underlying file.
197
+ //
198
+ // For data this is the lowest valued offset of the two candidates.
199
+ // For holes it's the first position which both sources agree upon
200
+ // are holes.
201
+ switch regionType {
202
+ case filesystem .Data :
203
+ data1 , err := f .underlying .GetNextRegionOffset (off , filesystem .Data )
204
+ if err == io .EOF {
205
+ // No more data in the underlying file. Return the result
206
+ // from the overlay.
207
+ return f .getNextRegionOffsetForOverlay (off , filesystem .Data )
208
+ }
209
+ if err != nil {
210
+ return data1 , status .Errorf (codes .Internal , "unexpected error while searching for data in underlying file: %v" , err )
211
+ }
212
+ data2 , err := f .getNextRegionOffsetForOverlay (off , filesystem .Data )
213
+ if err == io .EOF {
214
+ // No more data in the overlay, return the data from the
215
+ // underlying file.
216
+ return data1 , nil
217
+ }
218
+ if err != nil {
219
+ return data2 , status .Errorf (codes .Internal , "unexpected error while searching for data in underlying file: %v" , err )
220
+ }
221
+ if data1 < data2 {
222
+ return data1 , nil
223
+ }
224
+ return data2 , nil
225
+ case filesystem .Hole :
226
+ for {
227
+ // Since we have already ruled out that we are past the EOF
228
+ // boundary no calls to GetNextRegionOffset should be
229
+ // capable of returning holes.
230
+ hole1 , err := f .underlying .GetNextRegionOffset (off , filesystem .Hole )
231
+ if err != nil {
232
+ return hole1 , status .Errorf (codes .Internal , "unexpected error while searching for hole in underlying file: %v" , err )
233
+ }
234
+ hole2 , err := f .getNextRegionOffsetForOverlay (off , filesystem .Hole )
235
+ if err != nil {
236
+ return hole2 , status .Errorf (codes .Internal , "unexpected error while searching for hole in overlay file: %v" , err )
237
+ }
238
+ if hole1 == hole2 {
239
+ // Both sources agree that it's a hole.
240
+ return hole1 , nil
241
+ }
242
+ if hole1 == int64 (f .sizeBytes ) || hole2 == int64 (f .sizeBytes ) {
243
+ // The only possible hole is the implicit hole at the
244
+ // end of the file.
245
+ return int64 (f .sizeBytes ), nil
246
+ }
247
+ // Continue searching at the next possible offset.
248
+ off = max (hole1 , hole2 )
249
+ }
250
+ default :
251
+ panic ("Unknown region type" )
252
+ }
253
+ }
254
+
168
255
// readFromSectors performs a single read against the block device. It
169
256
// attempts to read as much data into the output buffer as is possible
170
257
// in a single read operation. If the file is fragmented, multiple reads
171
258
// are necessary, requiring this function to be called repeatedly.
172
259
func (f * blockDeviceBackedFile ) readFromSectors (p []byte , sectorIndex , lastSectorIndex , offsetWithinSector int ) (int , error ) {
173
260
if sectorIndex >= len (f .sectors ) {
174
261
// Attempted to read from a hole located at the
175
- // end of the file. Fill up all of the remaining
176
- // space with zero bytes.
177
- for i := 0 ; i < len (p ); i ++ {
178
- p [i ] = 0
179
- }
180
- return len (p ), nil
262
+ // end of the file. Delegate to ReadLayer.
263
+ offset := f .fp .sectorSizeBytes * sectorIndex + offsetWithinSector
264
+ return f .underlying .ReadAt (p , int64 (offset ))
181
265
}
182
266
183
267
sector , sectorsToRead := f .getSectorsContiguous (sectorIndex , lastSectorIndex )
184
268
p = f .limitBufferToSectorBoundary (p , sectorsToRead , offsetWithinSector )
185
269
if sector == 0 {
186
270
// Attempted to read from a sparse region of the file.
187
- // Fill in zero bytes.
188
- for i := 0 ; i < len (p ); i ++ {
189
- p [i ] = 0
190
- }
191
- return len (p ), nil
271
+ offset := f .fp .sectorSizeBytes * sectorIndex + offsetWithinSector
272
+ return f .underlying .ReadAt (p , int64 (offset ))
192
273
}
193
274
194
275
// Attempted to read from a region of the file that contains
@@ -267,6 +348,9 @@ func (f *blockDeviceBackedFile) Truncate(size int64) error {
267
348
if size < 0 {
268
349
return status .Errorf (codes .InvalidArgument , "Negative truncation size: %d" , size )
269
350
}
351
+ if err := f .underlying .Truncate (size ); err != nil {
352
+ return status .Errorf (codes .Internal , "truncating the underlying file failed: %v" , err )
353
+ }
270
354
271
355
sectorIndex := int (size / int64 (f .fp .sectorSizeBytes ))
272
356
offsetWithinSector := int (size % int64 (f .fp .sectorSizeBytes ))
@@ -299,7 +383,7 @@ func (f *blockDeviceBackedFile) Truncate(size int64) error {
299
383
// writeToNewSectors is used to write data into new sectors. This
300
384
// function is called when holes in a sparse file are filled up or when
301
385
// data is appended to the end of a file.
302
- func (f * blockDeviceBackedFile ) writeToNewSectors (p []byte , offsetWithinSector int ) (int , uint32 , int , error ) {
386
+ func (f * blockDeviceBackedFile ) writeToNewSectors (p []byte , fromSector , offsetWithinSector int ) (int , uint32 , int , error ) {
303
387
// Allocate space to store the data.
304
388
sectorsToAllocate := int ((uint64 (offsetWithinSector ) + uint64 (len (p )) + uint64 (f .fp .sectorSizeBytes ) - 1 ) / uint64 (f .fp .sectorSizeBytes ))
305
389
firstSector , sectorsAllocated , err := f .fp .sectorAllocator .AllocateContiguous (sectorsToAllocate )
@@ -314,10 +398,15 @@ func (f *blockDeviceBackedFile) writeToNewSectors(p []byte, offsetWithinSector i
314
398
nWritten := len (p )
315
399
316
400
// Write the first sector separately when we need to introduce
317
- // leading zero padding.
401
+ // leading read layer padding.
318
402
sector := firstSector
319
403
if offsetWithinSector > 0 {
320
404
buf := make ([]byte , f .fp .sectorSizeBytes )
405
+ logicalOffset := fromSector * f .fp .sectorSizeBytes
406
+ if _ , err := f .underlying .ReadAt (buf [:offsetWithinSector ], int64 (logicalOffset )); err != nil {
407
+ f .fp .sectorAllocator .FreeContiguous (firstSector , sectorsAllocated )
408
+ return 0 , 0 , 0 , err
409
+ }
321
410
nWritten := copy (buf [offsetWithinSector :], p )
322
411
if _ , err := f .fp .blockDevice .WriteAt (buf , f .toDeviceOffset (sector , 0 )); err != nil {
323
412
f .fp .sectorAllocator .FreeContiguous (firstSector , sectorsAllocated )
@@ -340,9 +429,14 @@ func (f *blockDeviceBackedFile) writeToNewSectors(p []byte, offsetWithinSector i
340
429
}
341
430
342
431
// Write the last sector separately when we need to introduce
343
- // trailing zero padding.
432
+ // trailing read layer padding.
344
433
if len (p ) > 0 {
345
434
buf := make ([]byte , f .fp .sectorSizeBytes )
435
+ logicalOffset := uint32 (len (p )) + (sector - firstSector )* uint32 (f .fp .sectorSizeBytes )
436
+ if _ , err := f .underlying .ReadAt (buf [len (p ):], int64 (logicalOffset )); err != nil {
437
+ f .fp .sectorAllocator .FreeContiguous (firstSector , sectorsAllocated )
438
+ return 0 , 0 , 0 , err
439
+ }
346
440
copy (buf , p )
347
441
if _ , err := f .fp .blockDevice .WriteAt (buf , f .toDeviceOffset (sector , 0 )); err != nil {
348
442
f .fp .sectorAllocator .FreeContiguous (firstSector , sectorsAllocated )
@@ -375,7 +469,7 @@ func (f *blockDeviceBackedFile) writeToSectors(p []byte, sectorIndex, lastSector
375
469
// Attempted to write past the end-of-file or within a
376
470
// hole located at the end of a sparse file. Allocate
377
471
// space and grow the file.
378
- bytesWritten , firstSector , sectorsAllocated , err := f .writeToNewSectors (p , offsetWithinSector )
472
+ bytesWritten , firstSector , sectorsAllocated , err := f .writeToNewSectors (p , sectorIndex , offsetWithinSector )
379
473
if err != nil {
380
474
return 0 , err
381
475
}
@@ -389,7 +483,7 @@ func (f *blockDeviceBackedFile) writeToSectors(p []byte, sectorIndex, lastSector
389
483
if sector == 0 {
390
484
// Attempted to write to a hole within a sparse file.
391
485
// Allocate space and insert sectors into the file.
392
- bytesWritten , firstSector , sectorsAllocated , err := f .writeToNewSectors (p , offsetWithinSector )
486
+ bytesWritten , firstSector , sectorsAllocated , err := f .writeToNewSectors (p , sectorIndex , offsetWithinSector )
393
487
if err != nil {
394
488
return 0 , err
395
489
}
@@ -409,6 +503,13 @@ func (f *blockDeviceBackedFile) WriteAt(p []byte, off int64) (int, error) {
409
503
if len (p ) == 0 {
410
504
return 0 , nil
411
505
}
506
+ // Truncate the file to a larger size if needed to accomodate the
507
+ // read.
508
+ if f .sizeBytes < uint64 (off )+ uint64 (len (p )) {
509
+ if err := f .Truncate (off + int64 (len (p ))); err != nil {
510
+ return 0 , err
511
+ }
512
+ }
412
513
413
514
// As the file may be stored on disk non-contiguously or may be
414
515
// a sparse file with holes, the write may need to be decomposed
0 commit comments