Skip to content

Commit 5f6c428

Browse files
authored
Fix ext4 offset (#313)
* Implement sub-storage backend for reading from an offset filesystem at any point within an img file * Create a second ext4 test image with offset, along with tests for offset ext4 img
1 parent 59b94e1 commit 5f6c428

File tree

5 files changed

+481
-254
lines changed

5 files changed

+481
-254
lines changed

backend/substorage.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package backend
2+
3+
import (
4+
"io"
5+
"io/fs"
6+
"os"
7+
)
8+
9+
type SubStorage struct {
10+
underlying Storage
11+
offset int64
12+
size int64
13+
}
14+
15+
func Sub(u Storage, offset, size int64) Storage {
16+
return SubStorage{
17+
underlying: u,
18+
offset: offset,
19+
size: size,
20+
}
21+
}
22+
23+
func (s SubStorage) Stat() (fs.FileInfo, error) {
24+
return s.underlying.Stat()
25+
}
26+
27+
func (s SubStorage) Read(bytes []byte) (int, error) {
28+
return s.underlying.Read(bytes)
29+
}
30+
31+
func (s SubStorage) Close() error {
32+
return s.underlying.Close()
33+
}
34+
35+
func (s SubStorage) ReadAt(p []byte, off int64) (n int, err error) {
36+
return s.underlying.ReadAt(p, s.offset+off)
37+
}
38+
39+
func (s SubStorage) Seek(offset int64, whence int) (int64, error) {
40+
var (
41+
pos int64
42+
err error
43+
)
44+
45+
switch whence {
46+
case io.SeekStart:
47+
pos, err = s.underlying.Seek(offset+s.offset, io.SeekStart)
48+
case io.SeekCurrent:
49+
pos, err = s.underlying.Seek(offset, io.SeekCurrent)
50+
case io.SeekEnd:
51+
pos, err = s.underlying.Seek(s.offset+s.size+offset, io.SeekStart)
52+
default:
53+
return -1, ErrNotSuitable
54+
}
55+
56+
if err != nil {
57+
return -1, err
58+
}
59+
60+
return pos - s.offset, nil
61+
}
62+
63+
func (s SubStorage) Sys() (*os.File, error) {
64+
return s.underlying.Sys()
65+
}
66+
67+
func (s SubStorage) Writable() (WritableFile, error) {
68+
uw, err := s.underlying.Writable()
69+
if err != nil {
70+
return nil, err
71+
}
72+
return subWritable{
73+
underlying: uw,
74+
offset: s.offset,
75+
size: s.size,
76+
}, nil
77+
}
78+
79+
type subWritable struct {
80+
underlying WritableFile
81+
offset int64
82+
size int64
83+
}
84+
85+
func (sw subWritable) Stat() (fs.FileInfo, error) {
86+
return sw.underlying.Stat()
87+
}
88+
89+
func (sw subWritable) Read(b []byte) (int, error) {
90+
return sw.underlying.Read(b)
91+
}
92+
93+
func (sw subWritable) Close() error {
94+
return sw.underlying.Close()
95+
}
96+
97+
func (sw subWritable) ReadAt(p []byte, off int64) (n int, err error) {
98+
return sw.underlying.ReadAt(p, sw.offset+off)
99+
}
100+
101+
func (sw subWritable) Seek(offset int64, whence int) (int64, error) {
102+
var (
103+
pos int64
104+
err error
105+
)
106+
107+
switch whence {
108+
case io.SeekStart:
109+
pos, err = sw.underlying.Seek(offset+sw.offset, io.SeekStart)
110+
case io.SeekCurrent:
111+
pos, err = sw.underlying.Seek(offset, io.SeekCurrent)
112+
case io.SeekEnd:
113+
pos, err = sw.underlying.Seek(sw.offset+sw.size+offset, io.SeekStart)
114+
default:
115+
return -1, ErrNotSuitable
116+
}
117+
118+
if err != nil {
119+
return -1, err
120+
}
121+
122+
return pos - sw.offset, nil
123+
}
124+
125+
func (sw subWritable) WriteAt(p []byte, off int64) (n int, err error) {
126+
return sw.underlying.WriteAt(p, sw.offset+off)
127+
}

filesystem/ext4/common_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
const (
2020
imgFile = "testdata/dist/ext4.img"
21+
imgFileOffset = "testdata/dist/ext4-offset.img"
2122
fooDirFile = "testdata/dist/foo_dir.txt"
2223
testGDTFile = "testdata/dist/gdt.bin"
2324
rootDirFile = "testdata/dist/root_dir.txt"
@@ -30,7 +31,14 @@ const (
3031
// TestMain sets up the test environment and runs the tests
3132
func TestMain(m *testing.M) {
3233
// Check and generate artifacts if necessary
34+
needGen := false
3335
if _, err := os.Stat(imgFile); os.IsNotExist(err) {
36+
needGen = true
37+
}
38+
if _, err := os.Stat(imgFileOffset); os.IsNotExist(err) {
39+
needGen = true
40+
}
41+
if needGen {
3442
// Run the genartifacts.sh script
3543
cmd := exec.Command("sh", "buildimg.sh")
3644
cmd.Stdout = os.Stdout

filesystem/ext4/ext4.go

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,10 @@ func Create(b backend.Storage, size, start, sectorsize int64, p *Params) (*FileS
559559
groupCount := sb.blockGroupCount()
560560
gdtSize := uint64(gdSize) * groupCount
561561
// write the superblock and GDT to the various locations on disk
562+
563+
// Make SubStorage Backend
564+
fsBackend := backend.Sub(b, start, size)
565+
562566
for _, bg := range backupSuperblocks {
563567
block := bg * int64(blocksPerGroup)
564568
blockStart := block * int64(blocksize)
@@ -568,13 +572,13 @@ func Create(b backend.Storage, size, start, sectorsize int64, p *Params) (*FileS
568572
incr = int64(SectorSize512) * 2
569573
}
570574

571-
writable, err := b.Writable()
575+
writable, err := fsBackend.Writable()
572576
if err != nil {
573577
return nil, err
574578
}
575579

576580
// write the superblock
577-
count, err := writable.WriteAt(superblockBytes, incr+blockStart+start)
581+
count, err := writable.WriteAt(superblockBytes, incr+blockStart)
578582
if err != nil {
579583
return nil, fmt.Errorf("error writing Superblock for block %d to disk: %v", block, err)
580584
}
@@ -583,7 +587,7 @@ func Create(b backend.Storage, size, start, sectorsize int64, p *Params) (*FileS
583587
}
584588

585589
// write the GDT
586-
count, err = writable.WriteAt(g, incr+blockStart+int64(SuperblockSize)+start)
590+
count, err = writable.WriteAt(g, incr+blockStart+int64(SuperblockSize))
587591
if err != nil {
588592
return nil, fmt.Errorf("error writing GDT for block %d to disk: %v", block, err)
589593
}
@@ -601,7 +605,7 @@ func Create(b backend.Storage, size, start, sectorsize int64, p *Params) (*FileS
601605
blockGroups: blockGroups,
602606
size: size,
603607
start: start,
604-
backend: b,
608+
backend: fsBackend,
605609
}, nil
606610
}
607611

@@ -631,10 +635,13 @@ func Read(b backend.Storage, size, start, sectorsize int64) (*FileSystem, error)
631635
return nil, fmt.Errorf("requested size is smaller than minimum allowed ext4 size %d", Ext4MinSize)
632636
}
633637

638+
// Make SubStorage Backend
639+
fsBackend := backend.Sub(b, start, size)
640+
634641
// load the information from the disk
635642
// read boot sector code
636643
bs := make([]byte, BootSectorSize)
637-
n, err := b.ReadAt(bs, start)
644+
n, err := fsBackend.ReadAt(bs, 0)
638645
if err != nil {
639646
return nil, fmt.Errorf("could not read boot sector bytes from file: %v", err)
640647
}
@@ -645,7 +652,7 @@ func Read(b backend.Storage, size, start, sectorsize int64) (*FileSystem, error)
645652
// read the superblock
646653
// the superblock is one minimal block, i.e. 2 sectors
647654
superblockBytes := make([]byte, SuperblockSize)
648-
n, err = b.ReadAt(superblockBytes, start+int64(BootSectorSize))
655+
n, err = fsBackend.ReadAt(superblockBytes, int64(BootSectorSize))
649656
if err != nil {
650657
return nil, fmt.Errorf("could not read superblock bytes from file: %v", err)
651658
}
@@ -677,7 +684,7 @@ func Read(b backend.Storage, size, start, sectorsize int64) (*FileSystem, error)
677684
if sb.blockSize == 1024 {
678685
gdtBlock = 2
679686
}
680-
n, err = b.ReadAt(gdtBytes, start+int64(gdtBlock)*int64(sb.blockSize))
687+
n, err = fsBackend.ReadAt(gdtBytes, int64(gdtBlock)*int64(sb.blockSize))
681688
if err != nil {
682689
return nil, fmt.Errorf("could not read Group Descriptor Table bytes from file: %v", err)
683690
}
@@ -696,7 +703,7 @@ func Read(b backend.Storage, size, start, sectorsize int64) (*FileSystem, error)
696703
blockGroups: int64(sb.blockGroupCount()),
697704
size: size,
698705
start: start,
699-
backend: b,
706+
backend: fsBackend,
700707
}, nil
701708
}
702709

@@ -969,7 +976,7 @@ func (fs *FileSystem) Remove(p string) error {
969976
gd.freeBlocks += freedByBG[bg]
970977
gd.blockBitmapChecksum = bitmapChecksum(dataBlockBitmap.ToBytes(), fs.superblock.checksumSeed)
971978
gdBytes := gd.toBytes(fs.superblock.gdtChecksumType(), fs.superblock.checksumSeed)
972-
if _, err := writableFile.WriteAt(gdBytes, fs.start+int64(gdtBlock)*int64(fs.superblock.blockSize)+int64(gd.number)*int64(fs.superblock.groupDescriptorSize)); err != nil {
979+
if _, err := writableFile.WriteAt(gdBytes, int64(gdtBlock)*int64(fs.superblock.blockSize)+int64(gd.number)*int64(fs.superblock.groupDescriptorSize)); err != nil {
973980
return fmt.Errorf("could not write Group Descriptor bytes to file: %v", err)
974981
}
975982
}
@@ -1011,7 +1018,7 @@ func (fs *FileSystem) Remove(p string) error {
10111018
}
10121019
b := dirBytes[start:end]
10131020

1014-
fileOff := fs.start + (int64(e.startingBlock)+int64(i))*int64(bs)
1021+
fileOff := (int64(e.startingBlock) + int64(i)) * int64(bs)
10151022

10161023
if _, err := writableFile.WriteAt(b, fileOff); err != nil {
10171024
return fmt.Errorf("could not write inode bitmap back to disk: %v", err)
@@ -1058,7 +1065,7 @@ func (fs *FileSystem) Remove(p string) error {
10581065

10591066
// write the group descriptor back
10601067
gdBytes := gd.toBytes(fs.superblock.gdtChecksumType(), fs.superblock.checksumSeed)
1061-
if _, err := writableFile.WriteAt(gdBytes, fs.start+int64(gdtBlock)*int64(fs.superblock.blockSize)+int64(gd.number)*int64(fs.superblock.groupDescriptorSize)); err != nil {
1068+
if _, err := writableFile.WriteAt(gdBytes, int64(gdtBlock)*int64(fs.superblock.blockSize)+int64(gd.number)*int64(fs.superblock.groupDescriptorSize)); err != nil {
10621069
return fmt.Errorf("could not write Group Descriptor bytes to file: %v", err)
10631070
}
10641071

@@ -1650,7 +1657,7 @@ func (fs *FileSystem) allocateInode(parent uint32) (uint32, error) {
16501657
// gdt starts in block 1 of any redundant copies, specifically in BG 0
16511658
gdtBlock := 1
16521659
blockByteLocation := gdtBlock * int(fs.superblock.blockSize)
1653-
gdOffset := fs.start + int64(blockByteLocation) + int64(bg)*int64(fs.superblock.groupDescriptorSize)
1660+
gdOffset := int64(blockByteLocation) + int64(bg)*int64(fs.superblock.groupDescriptorSize)
16541661
wrote, err := writableFile.WriteAt(gdBytes, gdOffset)
16551662
if err != nil {
16561663
return 0, fmt.Errorf("unable to write group descriptor bytes for blockgroup %d: %v", bg, err)
@@ -1802,7 +1809,7 @@ func (fs *FileSystem) readInodeBitmap(group int) (*bitmap.Bitmap, error) {
18021809
bitmapLocation := gd.inodeBitmapLocation
18031810
bitmapByteCount := fs.superblock.inodesPerGroup / 8
18041811
b := make([]byte, bitmapByteCount)
1805-
offset := int64(bitmapLocation*uint64(fs.superblock.blockSize) + uint64(fs.start))
1812+
offset := int64(bitmapLocation * uint64(fs.superblock.blockSize))
18061813
read, err := fs.backend.ReadAt(b, offset)
18071814
if err != nil {
18081815
return nil, fmt.Errorf("unable to read inode bitmap for blockgroup %d: %w", gd.number, err)
@@ -1831,7 +1838,7 @@ func (fs *FileSystem) writeInodeBitmap(bm *bitmap.Bitmap, group int) error {
18311838
gd := fs.groupDescriptors.descriptors[group]
18321839
bitmapByteCount := fs.superblock.inodesPerGroup / 8
18331840
bitmapLocation := gd.inodeBitmapLocation
1834-
offset := int64(bitmapLocation*uint64(fs.superblock.blockSize) + uint64(fs.start))
1841+
offset := int64(bitmapLocation * uint64(fs.superblock.blockSize))
18351842
wrote, err := writableFile.WriteAt(b, offset)
18361843
if err != nil {
18371844
return fmt.Errorf("unable to write inode bitmap for blockgroup %d: %w", gd.number, err)
@@ -1850,7 +1857,7 @@ func (fs *FileSystem) readBlockBitmap(group int) (*bitmap.Bitmap, error) {
18501857
gd := fs.groupDescriptors.descriptors[group]
18511858
bitmapLocation := gd.blockBitmapLocation
18521859
b := make([]byte, fs.superblock.blockSize)
1853-
offset := int64(bitmapLocation*uint64(fs.superblock.blockSize) + uint64(fs.start))
1860+
offset := int64(bitmapLocation * uint64(fs.superblock.blockSize))
18541861
read, err := fs.backend.ReadAt(b, offset)
18551862
if err != nil {
18561863
return nil, fmt.Errorf("unable to read block bitmap for blockgroup %d: %w", gd.number, err)
@@ -1876,7 +1883,7 @@ func (fs *FileSystem) writeBlockBitmap(bm *bitmap.Bitmap, group int) error {
18761883
b := bm.ToBytes()
18771884
gd := fs.groupDescriptors.descriptors[group]
18781885
bitmapLocation := gd.blockBitmapLocation
1879-
offset := int64(bitmapLocation*uint64(fs.superblock.blockSize) + uint64(fs.start))
1886+
offset := int64(bitmapLocation * uint64(fs.superblock.blockSize))
18801887
wrote, err := writableFile.WriteAt(b, offset)
18811888
if err != nil {
18821889
return fmt.Errorf("unable to write block bitmap for blockgroup %d: %w", gd.number, err)
@@ -1897,7 +1904,7 @@ func (fs *FileSystem) writeSuperblock() error {
18971904
if err != nil {
18981905
return fmt.Errorf("could not convert superblock to bytes: %v", err)
18991906
}
1900-
_, err = writableFile.WriteAt(superblockBytes, fs.start+int64(BootSectorSize))
1907+
_, err = writableFile.WriteAt(superblockBytes, int64(BootSectorSize))
19011908
return err
19021909
}
19031910

0 commit comments

Comments
 (0)