Skip to content

Commit adc76c1

Browse files
committed
Read drive's sector size from disk
GPT header is located at the 1st logical sector of the disk. Up until now we assumed that a sector size is 512 bytes. But this can be changed by user [1]. Read sector size from kernel and use it to calculate the GPT header location. [1] https://wiki.archlinux.org/title/Solid_state_drive#Native_sector_size Closes #119
1 parent 8cfca61 commit adc76c1

File tree

5 files changed

+98
-51
lines changed

5 files changed

+98
-51
lines changed

Diff for: init/blkinfo.go

+57-48
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import (
44
"bytes"
55
"encoding/binary"
66
"fmt"
7-
"io"
87
"os"
8+
9+
"golang.org/x/sys/unix"
910
)
1011

1112
type blkInfo struct {
@@ -28,7 +29,7 @@ func readBlkInfo(path string) (*blkInfo, error) {
2829
}
2930
defer r.Close()
3031

31-
type probeFn func(r io.ReaderAt) *blkInfo
32+
type probeFn func(f *os.File) *blkInfo
3233
probes := []probeFn{probeGpt, probeMbr, probeLuks, probeExt4, probeBtrfs, probeXfs, probeF2fs, probeLvmPv, probeMdraid}
3334
for _, fn := range probes {
3435
blk := fn(r)
@@ -63,17 +64,25 @@ type gptData struct {
6364
partitions []gptPart
6465
}
6566

66-
func probeGpt(r io.ReaderAt) *blkInfo {
67+
func probeGpt(f *os.File) *blkInfo {
6768
const (
6869
// https://wiki.osdev.org/GPT
69-
lbaSize = 0x200
70-
tableHeaderOffset = 1 * lbaSize
71-
signatureOffset = 0x0
72-
guidOffset = 0x38
73-
partLocationOffset = 0x48
70+
tableHeaderOffsetSector = 1
71+
signatureOffset = 0x0
72+
guidOffset = 0x38
73+
partLocationOffset = 0x48
74+
75+
defaultSectorSize = 512
7476
)
77+
78+
lbaSize, err := unix.IoctlGetInt(int(f.Fd()), unix.BLKSSZGET)
79+
if err != nil {
80+
lbaSize = defaultSectorSize
81+
}
82+
tableHeaderOffset := tableHeaderOffsetSector * int64(lbaSize)
83+
7584
signature := make([]byte, 8)
76-
if _, err := r.ReadAt(signature, tableHeaderOffset+signatureOffset); err != nil {
85+
if _, err := f.ReadAt(signature, tableHeaderOffset+signatureOffset); err != nil {
7786
return nil
7887
}
7988
if !bytes.Equal(signature, []byte("EFI PART")) {
@@ -82,25 +91,25 @@ func probeGpt(r io.ReaderAt) *blkInfo {
8291

8392
buff := make([]byte, 16)
8493

85-
if _, err := r.ReadAt(buff, tableHeaderOffset+guidOffset); err != nil {
94+
if _, err := f.ReadAt(buff, tableHeaderOffset+guidOffset); err != nil {
8695
return nil
8796
}
8897
uuid := convertGptUUID(buff)
8998

90-
if _, err := r.ReadAt(buff[:16], tableHeaderOffset+partLocationOffset); err != nil {
99+
if _, err := f.ReadAt(buff[:16], tableHeaderOffset+partLocationOffset); err != nil {
91100
return nil
92101
}
93102
partLba := binary.LittleEndian.Uint64(buff[0:8])
94103
partNum := binary.LittleEndian.Uint32(buff[8:12])
95104
partSize := binary.LittleEndian.Uint32(buff[12:16])
96-
lbaOffset := partLba * lbaSize
105+
lbaOffset := partLba * uint64(lbaSize)
97106

98107
var parts []gptPart
99108
buf := make([]byte, partSize)
100109
zeroUUID := make([]byte, 16) // zero UUID used as a marker for unused partitions
101110
for i := uint32(0); i < partNum; i++ {
102111
start := lbaOffset + uint64(i*partSize)
103-
if _, err := r.ReadAt(buf, int64(start)); err != nil {
112+
if _, err := f.ReadAt(buf, int64(start)); err != nil {
104113
return nil
105114
}
106115
typeGUID := convertGptUUID(buf[0:0x10])
@@ -126,7 +135,7 @@ func convertGptUUID(d []byte) []byte {
126135
d[10], d[11], d[12], d[13], d[14], d[15]}
127136
}
128137

129-
func probeMbr(r io.ReaderAt) *blkInfo {
138+
func probeMbr(f *os.File) *blkInfo {
130139
const (
131140
// https://wiki.osdev.org/GPT
132141
bootSignatureOffset = 0x1fe
@@ -135,51 +144,51 @@ func probeMbr(r io.ReaderAt) *blkInfo {
135144
idOffset = 0x1b8
136145
)
137146
signature := make([]byte, 2)
138-
if _, err := r.ReadAt(signature, bootSignatureOffset); err != nil {
147+
if _, err := f.ReadAt(signature, bootSignatureOffset); err != nil {
139148
return nil
140149
}
141150
if string(signature) != bootSignature {
142151
return nil
143152
}
144153

145-
if _, err := r.ReadAt(signature, diskSignatureOffset); err != nil {
154+
if _, err := f.ReadAt(signature, diskSignatureOffset); err != nil {
146155
return nil
147156
}
148157
if string(signature) != "\x00\x00" && string(signature) != "\x5a\x5a" {
149158
return nil
150159
}
151160

152161
b := make([]byte, 4)
153-
if _, err := r.ReadAt(b, idOffset); err != nil {
162+
if _, err := f.ReadAt(b, idOffset); err != nil {
154163
return nil
155164
}
156165
id := []byte{b[3], b[2], b[1], b[0]} // little endian
157166
return &blkInfo{format: "mbr", uuid: id}
158167
}
159168

160-
func probeLuks(r io.ReaderAt) *blkInfo {
169+
func probeLuks(f *os.File) *blkInfo {
161170
// https://gitlab.com/cryptsetup/cryptsetup/-/wikis/LUKS-standard/on-disk-format.pdf
162171
// both LUKS v1 and v2 have the same magic and UUID offset
163172
const (
164173
uuidOffset = 0xa8
165174
labelV2Offset = 0x18
166175
)
167176
magic := make([]byte, 6)
168-
if _, err := r.ReadAt(magic, 0x0); err != nil {
177+
if _, err := f.ReadAt(magic, 0x0); err != nil {
169178
return nil
170179
}
171180
if !bytes.Equal(magic, []byte("LUKS\xba\xbe")) {
172181
return nil
173182
}
174183

175184
buff := make([]byte, 2)
176-
if _, err := r.ReadAt(buff, 6); err != nil {
185+
if _, err := f.ReadAt(buff, 6); err != nil {
177186
return nil
178187
}
179188
version := int(buff[0])<<8 + int(buff[1])
180189

181190
data := make([]byte, 40)
182-
if _, err := r.ReadAt(data, uuidOffset); err != nil {
191+
if _, err := f.ReadAt(data, uuidOffset); err != nil {
183192
return nil
184193
}
185194
uuidStr := string(data[:uuidLen])
@@ -193,7 +202,7 @@ func probeLuks(r io.ReaderAt) *blkInfo {
193202
if version == 2 {
194203
// Only LUKS 2 has label support
195204
buff := make([]byte, 48)
196-
if _, err := r.ReadAt(buff, labelV2Offset); err != nil {
205+
if _, err := f.ReadAt(buff, labelV2Offset); err != nil {
197206
return nil
198207
}
199208
label = fixedArrayToString(buff)
@@ -202,7 +211,7 @@ func probeLuks(r io.ReaderAt) *blkInfo {
202211
return &blkInfo{format: "luks", uuid: uuid, label: label}
203212
}
204213

205-
func probeExt4(r io.ReaderAt) *blkInfo {
214+
func probeExt4(f *os.File) *blkInfo {
206215
const (
207216
// from fs/ext4/ext4.h
208217
extSuperblockOffset = 0x400
@@ -213,24 +222,24 @@ func probeExt4(r io.ReaderAt) *blkInfo {
213222
)
214223

215224
magic := make([]byte, 2)
216-
if _, err := r.ReadAt(magic, extSuperblockOffset+extMagicOffset); err != nil {
225+
if _, err := f.ReadAt(magic, extSuperblockOffset+extMagicOffset); err != nil {
217226
return nil
218227
}
219228
if string(magic) != extMagic {
220229
return nil
221230
}
222231
uuid := make([]byte, 16)
223-
if _, err := r.ReadAt(uuid, extSuperblockOffset+extUUIDOffset); err != nil {
232+
if _, err := f.ReadAt(uuid, extSuperblockOffset+extUUIDOffset); err != nil {
224233
return nil
225234
}
226235
label := make([]byte, 16)
227-
if _, err := r.ReadAt(label, extSuperblockOffset+extLabelOffset); err != nil {
236+
if _, err := f.ReadAt(label, extSuperblockOffset+extLabelOffset); err != nil {
228237
return nil
229238
}
230239
return &blkInfo{format: "ext4", isFs: true, uuid: uuid, label: fixedArrayToString(label)}
231240
}
232241

233-
func probeBtrfs(r io.ReaderAt) *blkInfo {
242+
func probeBtrfs(f *os.File) *blkInfo {
234243
// https://btrfs.wiki.kernel.org/index.php/On-disk_Format
235244
const (
236245
btrfsSuperblockOffset = 0x10000
@@ -241,24 +250,24 @@ func probeBtrfs(r io.ReaderAt) *blkInfo {
241250
)
242251

243252
magic := make([]byte, 8)
244-
if _, err := r.ReadAt(magic, btrfsSuperblockOffset+btrfsMagicOffset); err != nil {
253+
if _, err := f.ReadAt(magic, btrfsSuperblockOffset+btrfsMagicOffset); err != nil {
245254
return nil
246255
}
247256
if !bytes.Equal(magic, []byte(btrfsMagic)) {
248257
return nil
249258
}
250259
uuid := make([]byte, 16)
251-
if _, err := r.ReadAt(uuid, btrfsSuperblockOffset+btrfsUUIDOffset); err != nil {
260+
if _, err := f.ReadAt(uuid, btrfsSuperblockOffset+btrfsUUIDOffset); err != nil {
252261
return nil
253262
}
254263
label := make([]byte, 256)
255-
if _, err := r.ReadAt(label, btrfsSuperblockOffset+btrfsLabelOffset); err != nil {
264+
if _, err := f.ReadAt(label, btrfsSuperblockOffset+btrfsLabelOffset); err != nil {
256265
return nil
257266
}
258267
return &blkInfo{format: "btrfs", isFs: true, uuid: uuid, label: fixedArrayToString(label)}
259268
}
260269

261-
func probeXfs(r io.ReaderAt) *blkInfo {
270+
func probeXfs(f *os.File) *blkInfo {
262271
// https://righteousit.wordpress.com/2018/05/21/xfs-part-1-superblock
263272
const (
264273
xfsSuperblockOffset = 0x0
@@ -269,24 +278,24 @@ func probeXfs(r io.ReaderAt) *blkInfo {
269278
)
270279

271280
magic := make([]byte, 4)
272-
if _, err := r.ReadAt(magic, xfsSuperblockOffset+xfsMagicOffset); err != nil {
281+
if _, err := f.ReadAt(magic, xfsSuperblockOffset+xfsMagicOffset); err != nil {
273282
return nil
274283
}
275284
if !bytes.Equal(magic, []byte(xfsMagic)) {
276285
return nil
277286
}
278287
id := make([]byte, 16)
279-
if _, err := r.ReadAt(id, xfsSuperblockOffset+xfsUUIDOffset); err != nil {
288+
if _, err := f.ReadAt(id, xfsSuperblockOffset+xfsUUIDOffset); err != nil {
280289
return nil
281290
}
282291
label := make([]byte, 12)
283-
if _, err := r.ReadAt(label, xfsSuperblockOffset+xfsLabelOffset); err != nil {
292+
if _, err := f.ReadAt(label, xfsSuperblockOffset+xfsLabelOffset); err != nil {
284293
return nil
285294
}
286295
return &blkInfo{format: "xfs", isFs: true, uuid: id, label: fixedArrayToString(label)}
287296
}
288297

289-
func probeF2fs(r io.ReaderAt) *blkInfo {
298+
func probeF2fs(f *os.File) *blkInfo {
290299
// https://github.com/torvalds/linux/blob/master/include/linux/f2fs_fs.h
291300
const (
292301
f2fsSuperblockOffset = 0x400
@@ -297,25 +306,25 @@ func probeF2fs(r io.ReaderAt) *blkInfo {
297306
)
298307

299308
magic := make([]byte, 4)
300-
if _, err := r.ReadAt(magic, f2fsSuperblockOffset+f2fsMagicOffset); err != nil {
309+
if _, err := f.ReadAt(magic, f2fsSuperblockOffset+f2fsMagicOffset); err != nil {
301310
return nil
302311
}
303312
if !bytes.Equal(magic, []byte(f2fsMagic)) {
304313
return nil
305314
}
306315
uuid := make([]byte, 16)
307-
if _, err := r.ReadAt(uuid, f2fsSuperblockOffset+f2fsUUIDOffset); err != nil {
316+
if _, err := f.ReadAt(uuid, f2fsSuperblockOffset+f2fsUUIDOffset); err != nil {
308317
return nil
309318
}
310319
buf := make([]byte, 512)
311-
if _, err := r.ReadAt(buf, f2fsSuperblockOffset+f2fsLabelOffset); err != nil {
320+
if _, err := f.ReadAt(buf, f2fsSuperblockOffset+f2fsLabelOffset); err != nil {
312321
return nil
313322
}
314323
label := fromUnicode16(buf, binary.LittleEndian)
315324
return &blkInfo{format: "f2fs", isFs: true, uuid: uuid, label: label}
316325
}
317326

318-
func probeLvmPv(r io.ReaderAt) *blkInfo {
327+
func probeLvmPv(f *os.File) *blkInfo {
319328
// https://github.com/libyal/libvslvm/blob/main/documentation/Logical%20Volume%20Manager%20(LVM)%20format.asciidoc
320329
const (
321330
lvmHeaderOffset = 0x200
@@ -328,27 +337,27 @@ func probeLvmPv(r io.ReaderAt) *blkInfo {
328337
)
329338

330339
magic := make([]byte, 8)
331-
if _, err := r.ReadAt(magic, lvmHeaderOffset+lvmMagicOffset); err != nil {
340+
if _, err := f.ReadAt(magic, lvmHeaderOffset+lvmMagicOffset); err != nil {
332341
return nil
333342
}
334343
if !bytes.Equal(magic, []byte(lvmMagic)) {
335344
return nil
336345
}
337-
if _, err := r.ReadAt(magic, lvmHeaderOffset+lvmTypeMagicOffset); err != nil {
346+
if _, err := f.ReadAt(magic, lvmHeaderOffset+lvmTypeMagicOffset); err != nil {
338347
return nil
339348
}
340349
if !bytes.Equal(magic, []byte(lvmTypeMagic)) {
341350
return nil
342351
}
343352

344353
buf := make([]byte, 4)
345-
if _, err := r.ReadAt(buf, lvmHeaderOffset+lvmHeaderSizeOffset); err != nil {
354+
if _, err := f.ReadAt(buf, lvmHeaderOffset+lvmHeaderSizeOffset); err != nil {
346355
return nil
347356
}
348357
headerSize := binary.LittleEndian.Uint32(buf)
349358

350359
uuid := make([]byte, 32)
351-
if _, err := r.ReadAt(uuid, int64(lvmHeaderOffset+headerSize+lvmUUIDOffset)); err != nil {
360+
if _, err := f.ReadAt(uuid, int64(lvmHeaderOffset+headerSize+lvmUUIDOffset)); err != nil {
352361
return nil
353362
}
354363
return &blkInfo{format: "lvm", isFs: true, uuid: uuid}
@@ -369,7 +378,7 @@ type mdraidData struct {
369378
level uint32
370379
}
371380

372-
func probeMdraid(r io.ReaderAt) *blkInfo {
381+
func probeMdraid(f *os.File) *blkInfo {
373382
// https://raid.wiki.kernel.org/index.php/RAID_superblock_formats
374383
const (
375384
mdraidHeaderOffset = 0x1000
@@ -381,28 +390,28 @@ func probeMdraid(r io.ReaderAt) *blkInfo {
381390
)
382391

383392
magic := make([]byte, 4)
384-
if _, err := r.ReadAt(magic, mdraidHeaderOffset+mdraidMagicOffset); err != nil {
393+
if _, err := f.ReadAt(magic, mdraidHeaderOffset+mdraidMagicOffset); err != nil {
385394
return nil
386395
}
387396
if binary.LittleEndian.Uint32(magic) != mdraidMagic {
388397
return nil
389398
}
390399

391400
version := make([]byte, 4)
392-
if _, err := r.ReadAt(version, mdraidHeaderOffset+mdraidVersioOffset); err != nil {
401+
if _, err := f.ReadAt(version, mdraidHeaderOffset+mdraidVersioOffset); err != nil {
393402
return nil
394403
}
395404
if binary.LittleEndian.Uint32(version) != 1 {
396405
return nil
397406
}
398407

399408
uuid := make([]byte, 16)
400-
if _, err := r.ReadAt(uuid, int64(mdraidHeaderOffset+mdraidUUIDOffset)); err != nil {
409+
if _, err := f.ReadAt(uuid, int64(mdraidHeaderOffset+mdraidUUIDOffset)); err != nil {
401410
return nil
402411
}
403412

404413
levelBuff := make([]byte, 4)
405-
if _, err := r.ReadAt(levelBuff, int64(mdraidHeaderOffset+mdraidLevelOffset)); err != nil {
414+
if _, err := f.ReadAt(levelBuff, int64(mdraidHeaderOffset+mdraidLevelOffset)); err != nil {
406415
return nil
407416
}
408417
level := binary.LittleEndian.Uint32(levelBuff)

Diff for: tests/generate_asset_gpt_4ksector.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
trap 'quit' EXIT ERR
2+
3+
quit() {
4+
set +o errexit
5+
sudo umount $dir
6+
rm -r $dir
7+
sudo losetup -d $lodev
8+
}
9+
10+
truncate --size 100M $OUTPUT
11+
lodev=$(sudo losetup --sector-size 4096 -f --show $OUTPUT)
12+
sudo fdisk $lodev <<<"g
13+
n
14+
15+
16+
17+
x
18+
u
19+
d4699213-6e73-41d5-ad81-3daf5dfcecfb
20+
r
21+
w
22+
" || true # Re-reading the partition table failed.: Invalid argument
23+
24+
sudo partprobe $lodev
25+
26+
sudo mkfs.ext4 ${lodev}p1
27+
dir=$(mktemp -d)
28+
sudo mount ${lodev}p1 $dir
29+
30+
sudo chown $USER $dir
31+
mkdir $dir/sbin
32+
cp assets/init $dir/sbin/init

0 commit comments

Comments
 (0)