Skip to content

Commit 21c4afb

Browse files
committed
better gpt logic and add fat entry formatting
1 parent d854131 commit 21c4afb

File tree

3 files changed

+180
-24
lines changed

3 files changed

+180
-24
lines changed

Diff for: fat_test.go

+18-3
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ var fatInit = map[int64][512]byte{
329329
484: 0x72, 0x72, 0x41, 0x61, 0xfb, 0xf1, 0x1d, 0x00, 0x02,
330330
510: 0x55, 0xaa},
331331

332+
// Below is the FAT.
332333
32: {0xf8, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f,
333334
0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f},
334335
15368: {0xf8, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f,
@@ -387,9 +388,23 @@ const rootFileContents = "this is\nthe root file\n"
387388
const dirFileContents = "this is not\nnot the root\nnot the root file\nnope. \nThis file has 5 lines.\n"
388389

389390
func TestBootSector(t *testing.T) {
390-
var bs bootsector
391+
// Print out the boot sector data.
391392
dat := fatInit[0]
392-
bs.data = dat[:]
393-
393+
bs := bootsector{data: dat[:]}
394+
fsiLBA := bs.FSInfo()
395+
fatLBA := 0 + bs.ReservedSectors()
396+
fatSz := bs.SectorsPerFAT() * uint32(bs.SectorSize())
394397
t.Log(bs.String())
398+
399+
// Print out the FSInfo sector data.
400+
datfsi := fatInit[int64(fsiLBA)]
401+
fsi := fsinfoSector{data: datfsi[:]}
402+
t.Log(fsi.String())
403+
404+
// Print out the FAT.
405+
datFAT := fatInit[int64(fatLBA)]
406+
fat := fat32Sector{data: datFAT[:]}
407+
t.Log(fat.String())
408+
_ = fatSz
409+
395410
}

Diff for: format.go

+152-14
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ func (bs *bootsector) SetSectorsPerCluster(spclus uint16) {
142142
}
143143

144144
// ReservedSectors returns the number of reserved sectors at the beginning of the volume.
145-
// Should be at least 1.
145+
// Should be at least 1. Reserved sectors include the boot sector, FS information sector and
146+
// redundant sectors with these first two. The number of reserved sectors is usually
147+
// 32 for FAT32 systems (~16k for 512 byte sectors).
148+
// Sectors 6 and 7 are usually the backup boot sector and the FS information sector, respectively.
146149
func (bs *bootsector) ReservedSectors() uint16 {
147150
return binary.LittleEndian.Uint16(bs.data[bpbRsvdSecCnt:])
148151
}
@@ -272,21 +275,35 @@ func (bs *bootsector) String() string {
272275
return string(bs.Appendf(nil, '\n'))
273276
}
274277

278+
func labelAppend(dst []byte, label string, data []byte, sep byte) []byte {
279+
if len(data) == 0 {
280+
return dst
281+
}
282+
dst = append(dst, label...)
283+
dst = append(dst, ':')
284+
dst = append(dst, data...)
285+
dst = append(dst, sep)
286+
return dst
287+
}
288+
289+
func labelAppendUint(label string, dst []byte, data uint64, sep byte) []byte {
290+
dst = append(dst, label...)
291+
dst = append(dst, ':')
292+
dst = strconv.AppendUint(dst, data, 10)
293+
dst = append(dst, sep)
294+
return dst
295+
}
296+
297+
func labelAppendUint32(label string, dst []byte, data uint32, sep byte) []byte {
298+
return labelAppendUint(label, dst, uint64(data), sep)
299+
}
300+
275301
func (bs *bootsector) Appendf(dst []byte, separator byte) []byte {
276302
appendData := func(name string, data []byte, sep byte) {
277-
if len(data) == 0 {
278-
return
279-
}
280-
dst = append(dst, name...)
281-
dst = append(dst, ':')
282-
dst = append(dst, data...)
283-
dst = append(dst, sep)
303+
dst = labelAppend(dst, name, data, sep)
284304
}
285305
appendInt := func(name string, data uint32, sep byte) {
286-
dst = append(dst, name...)
287-
dst = append(dst, ':')
288-
dst = strconv.AppendUint(dst, uint64(data), 10)
289-
dst = append(dst, sep)
306+
dst = labelAppendUint32(name, dst, data, sep)
290307
}
291308
oem := bs.OEMName()
292309
appendData("OEM", clipname(oem[:]), separator)
@@ -314,7 +331,128 @@ func (bs *bootsector) Appendf(dst []byte, separator byte) []byte {
314331
return dst
315332
}
316333

317-
// BootCode returns the boot code at the end of the boot sector.
318-
func (bs *bootsector) BootCode() []byte {
334+
// bootcode returns the boot code at the end of the boot sector.
335+
func (bs *bootsector) bootcode() []byte {
319336
return bs.data[bsBootCode32:bs55AA]
320337
}
338+
339+
// fsinfoSector is the FS Information Sector for FAT32 volumes.
340+
type fsinfoSector struct {
341+
data []byte
342+
}
343+
344+
// Signatures returns the 3 signatures at the beginning, middle and end of the sector.
345+
// Expect them to be 0x41615252, 0x61417272, 0xAA550000 respectively.
346+
func (fsi *fsinfoSector) Signatures() (sigStart, sigMid, sigEnd uint32) {
347+
return binary.LittleEndian.Uint32(fsi.data[0:]),
348+
binary.LittleEndian.Uint32(fsi.data[0x1e4:]),
349+
binary.LittleEndian.Uint32(fsi.data[0x1fc:])
350+
}
351+
352+
// SetSignatures sets the 3 signatures at the beginning, middle and end of the sector.
353+
// Should be called as follows to set valid signatures expected by most implementations:
354+
//
355+
// fsi.SetSignatures(0x41615252, 0x61417272, 0xAA550000)
356+
func (fsi *fsinfoSector) SetSignatures(sigStart, sigMid, sigEnd uint32) {
357+
binary.LittleEndian.PutUint32(fsi.data[0:], sigStart)
358+
binary.LittleEndian.PutUint32(fsi.data[0x1e4:], sigMid)
359+
binary.LittleEndian.PutUint32(fsi.data[0x1fc:], sigEnd)
360+
}
361+
362+
// FreeClusterCount is the last known number of free data clusters on the volume,
363+
// or 0xFFFFFFFF if unknown. Should be set to 0xFFFFFFFF during format and updated by
364+
// the operating system later on. Must not be absolutely relied upon to be correct in all scenarios.
365+
// Before using this value, the operating system should sanity check this value to
366+
// be less than or equal to the volume's count of clusters.
367+
func (fsi *fsinfoSector) FreeClusterCount() uint32 {
368+
return binary.LittleEndian.Uint32(fsi.data[0x1e8:])
369+
}
370+
371+
// SetFreeClusterCount sets the last known number of free data clusters on the volume.
372+
func (fsi *fsinfoSector) SetFreeClusterCount(count uint32) {
373+
binary.LittleEndian.PutUint32(fsi.data[0x1e8:], count)
374+
}
375+
376+
// LastAllocatedCluster is the number of the most recently known to be allocated data cluster.
377+
// Should be set to 0xFFFFFFFF during format and updated by the operating system later on.
378+
// With 0xFFFFFFFF the system should start at cluster 0x00000002. Must not be absolutely
379+
// relied upon to be correct in all scenarios. Before using this value, the operating system
380+
// should sanity check this value to be a valid cluster number on the volume.
381+
func (fsi *fsinfoSector) LastAllocatedCluster() uint32 {
382+
return binary.LittleEndian.Uint32(fsi.data[0x1ec:])
383+
}
384+
385+
// SetLastAllocatedCluster sets the number of the most recently known to be allocated data cluster.
386+
func (fsi *fsinfoSector) SetLastAllocatedCluster(cluster uint32) {
387+
binary.LittleEndian.PutUint32(fsi.data[0x1ec:], cluster)
388+
}
389+
390+
func (fsi *fsinfoSector) String() string {
391+
return string(fsi.Appendf(nil, '\n'))
392+
}
393+
394+
func (fsi *fsinfoSector) Appendf(dst []byte, separator byte) []byte {
395+
lo, mid, hi := fsi.Signatures()
396+
if lo != 0x41615252 || mid != 0x61417272 || hi != 0xAA550000 {
397+
dst = append(dst, "invalid fsi signatures"...)
398+
dst = append(dst, separator)
399+
}
400+
dst = labelAppendUint32("FreeClusterCount", dst, fsi.FreeClusterCount(), separator)
401+
dst = labelAppendUint32("LastAllocatedCluster", dst, fsi.LastAllocatedCluster(), separator)
402+
return dst
403+
}
404+
405+
// fatSector is a File Allocation Table sector.
406+
type fat32Sector struct {
407+
data []byte
408+
}
409+
410+
type entry uint32
411+
412+
func (fs *fat32Sector) Entry(idx int) entry {
413+
return entry(binary.LittleEndian.Uint32(fs.data[idx*4:]))
414+
}
415+
416+
func (fs *fat32Sector) SetEntry(idx int, ent entry) {
417+
binary.LittleEndian.PutUint32(fs.data[idx*4:], uint32(ent))
418+
}
419+
420+
func (fs entry) Cluster() uint32 {
421+
return uint32(fs) & 0x0FFF_FFFF
422+
}
423+
424+
func (e entry) Appendf(dst []byte, separator byte) []byte {
425+
if e.IsEOF() {
426+
dst = labelAppendUint32("entry", dst, e.Cluster(), ' ')
427+
return append(dst, "EOF"...)
428+
}
429+
return labelAppendUint32("entry", dst, e.Cluster(), separator)
430+
}
431+
432+
func (e entry) IsEOF() bool {
433+
return e&0x0FFF_FFFF >= 0x0FFF_FFF8
434+
}
435+
436+
func (fs *fat32Sector) String() string {
437+
return string(fs.AppendfEntries(nil, " -> ", '\n'))
438+
}
439+
440+
func (fs *fat32Sector) AppendfEntries(dst []byte, entrySep string, chainSep byte) []byte {
441+
var inChain bool
442+
for i := 0; i < len(fs.data)/4; i++ {
443+
entry := fs.Entry(i)
444+
if entry == 0 {
445+
break
446+
}
447+
dst = entry.Appendf(dst, chainSep)
448+
if entry.IsEOF() {
449+
dst = append(dst, chainSep)
450+
inChain = false
451+
} else if inChain {
452+
dst = append(dst, entrySep...)
453+
} else {
454+
inChain = true
455+
}
456+
}
457+
return dst
458+
}

Diff for: internal/gpt/gpt.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,10 @@ func (p *PartitionEntry) SetAttributes(attr PartitionAttributes) {
224224
binary.LittleEndian.PutUint64(p.data[48:56], uint64(attr))
225225
}
226226

227-
// ReadName reads the partition name from the partition entry and
227+
// ReadNameAsUTF8 reads the partition name from the partition entry and
228228
// encodes it as utf-8 into the provided slice. The number of bytes
229229
// read is returned along with any error.
230-
func (p *PartitionEntry) ReadName(b []byte) (int, error) {
230+
func (p *PartitionEntry) ReadNameAsUTF8(b []byte) (int, error) {
231231
// Find the length of the name.
232232
nameLen := 0
233233
for nameLen < pteNameLen && p.data[pteNameOff+nameLen] != 0 {
@@ -242,18 +242,21 @@ func (p *PartitionEntry) ReadName(b []byte) (int, error) {
242242
}
243243

244244
func (p *PartitionEntry) ClearName() {
245-
p.data[pteNameOff] = 0
245+
p.clearNameAfter(0)
246246
}
247247

248-
// WriteName writes a utf-8 encoded string as the Partition Entry's name.
249-
func (p *PartitionEntry) WriteName(name []byte) error {
248+
// SetNameUTF8 writes a utf-8 encoded string as the Partition Entry's name.
249+
func (p *PartitionEntry) SetNameUTF8(name []byte) error {
250250
n, err := utf16x.FromUTF8(p.data[pteNameOff:pteNameOff+pteNameLen], name, binary.LittleEndian)
251251
if err != nil {
252252
return err
253253
}
254+
p.clearNameAfter(n)
255+
return nil
256+
}
254257

255-
for i := n; i < pteNameLen; i++ {
258+
func (p *PartitionEntry) clearNameAfter(idx int) {
259+
for i := idx; i < pteNameLen; i++ {
256260
p.data[pteNameOff+i] = 0
257261
}
258-
return nil
259262
}

0 commit comments

Comments
 (0)