You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wincmd/ntfs/bootsect/bootsect.go

73 lines
2.7 KiB
Go

/*
Package bootsect provides functions to parse the boot sector (also sometimes called Volume Boot Record, VBR, or
$Boot file) of an NTFS volume.
*/
package bootsect
import (
"fmt"
"b612.me/wincmd/ntfs/binutil"
)
// BootSector represents the parsed data of an NTFS boot sector. The OemId should typically be "NTFS " ("NTFS"
// followed by 4 trailing spaces) for a valid NTFS boot sector.
type BootSector struct {
OemId string
BytesPerSector int
SectorsPerCluster int
MediaDescriptor byte
SectorsPerTrack int
NumberofHeads int
HiddenSectors int
TotalSectors uint64
MftClusterNumber uint64
MftMirrorClusterNumber uint64
FileRecordSegmentSizeInBytes int
IndexBufferSizeInBytes int
VolumeSerialNumber []byte
}
// Parse parses the data of an NTFS boot sector into a BootSector structure.
func Parse(data []byte) (BootSector, error) {
if len(data) < 80 {
return BootSector{}, fmt.Errorf("boot sector data should be at least 80 bytes but is %d", len(data))
}
r := binutil.NewLittleEndianReader(data)
bytesPerSector := int(r.Uint16(0x0B))
sectorsPerCluster := int(int8(r.Byte(0x0D)))
if sectorsPerCluster < 0 {
// Quoth Wikipedia: The number of sectors in a cluster. If the value is negative, the amount of sectors is 2
// to the power of the absolute value of this field.
sectorsPerCluster = 1 << -sectorsPerCluster
}
bytesPerCluster := bytesPerSector * sectorsPerCluster
return BootSector{
OemId: string(r.Read(0x03, 8)),
BytesPerSector: bytesPerSector,
SectorsPerCluster: sectorsPerCluster,
MediaDescriptor: r.Byte(0x15),
SectorsPerTrack: int(r.Uint16(0x18)),
NumberofHeads: int(r.Uint16(0x1A)),
HiddenSectors: int(r.Uint16(0x1C)),
TotalSectors: r.Uint64(0x28),
MftClusterNumber: r.Uint64(0x30),
MftMirrorClusterNumber: r.Uint64(0x38),
FileRecordSegmentSizeInBytes: bytesOrClustersToBytes(r.Byte(0x40), bytesPerCluster),
IndexBufferSizeInBytes: bytesOrClustersToBytes(r.Byte(0x44), bytesPerCluster),
VolumeSerialNumber: binutil.Duplicate(r.Read(0x48, 8)),
}, nil
}
func bytesOrClustersToBytes(b byte, bytesPerCluster int) int {
// From Wikipedia:
// A positive value denotes the number of clusters in a File Record Segment. A negative value denotes the amount of
// bytes in a File Record Segment, in which case the size is 2 to the power of the absolute value.
// (0xF6 = -10 → 210 = 1024).
i := int(int8(b))
if i < 0 {
return 1 << -i
}
return i * bytesPerCluster
}