packer-cn/vendor/github.com/mitchellh/go-fs/fat/boot_sector.go

348 lines
8.4 KiB
Go
Raw Normal View History

2016-02-04 20:33:36 -05:00
package fat
import (
"encoding/binary"
"errors"
"fmt"
"github.com/mitchellh/go-fs"
"unicode"
)
type MediaType uint8
// The standard value for "fixed", non-removable media, directly
// from the FAT specification.
const MediaFixed MediaType = 0xF8
type BootSectorCommon struct {
OEMName string
BytesPerSector uint16
SectorsPerCluster uint8
ReservedSectorCount uint16
NumFATs uint8
RootEntryCount uint16
TotalSectors uint32
Media MediaType
SectorsPerFat uint32
SectorsPerTrack uint16
NumHeads uint16
}
// DecodeBootSector takes a BlockDevice and decodes the FAT boot sector
// from it.
func DecodeBootSector(device fs.BlockDevice) (*BootSectorCommon, error) {
var sector [512]byte
if _, err := device.ReadAt(sector[:], 0); err != nil {
return nil, err
}
if sector[510] != 0x55 || sector[511] != 0xAA {
return nil, errors.New("corrupt boot sector signature")
}
result := new(BootSectorCommon)
// BS_OEMName
result.OEMName = string(sector[3:11])
// BPB_BytsPerSec
result.BytesPerSector = binary.LittleEndian.Uint16(sector[11:13])
// BPB_SecPerClus
result.SectorsPerCluster = sector[13]
// BPB_RsvdSecCnt
result.ReservedSectorCount = binary.LittleEndian.Uint16(sector[14:16])
// BPB_NumFATs
result.NumFATs = sector[16]
// BPB_RootEntCnt
result.RootEntryCount = binary.LittleEndian.Uint16(sector[17:19])
// BPB_Media
result.Media = MediaType(sector[21])
// BPB_SecPerTrk
result.SectorsPerTrack = binary.LittleEndian.Uint16(sector[24:26])
// BPB_NumHeads
result.NumHeads = binary.LittleEndian.Uint16(sector[26:28])
// BPB_TotSec16 / BPB_TotSec32
result.TotalSectors = uint32(binary.LittleEndian.Uint16(sector[19:21]))
if result.TotalSectors == 0 {
result.TotalSectors = binary.LittleEndian.Uint32(sector[32:36])
}
// BPB_FATSz16 / BPB_FATSz32
result.SectorsPerFat = uint32(binary.LittleEndian.Uint16(sector[22:24]))
if result.SectorsPerFat == 0 {
result.SectorsPerFat = binary.LittleEndian.Uint32(sector[36:40])
}
return result, nil
}
func (b *BootSectorCommon) Bytes() ([]byte, error) {
var sector [512]byte
// BS_jmpBoot
sector[0] = 0xEB
sector[1] = 0x3C
sector[2] = 0x90
// BS_OEMName
if len(b.OEMName) > 8 {
return nil, errors.New("OEMName must be 8 bytes or less")
}
for i, r := range b.OEMName {
if r > unicode.MaxASCII {
return nil, fmt.Errorf("'%s' in OEM name not a valid ASCII char. Must be ASCII.", r)
}
sector[0x3+i] = byte(r)
}
// BPB_BytsPerSec
binary.LittleEndian.PutUint16(sector[11:13], b.BytesPerSector)
// BPB_SecPerClus
sector[13] = uint8(b.SectorsPerCluster)
// BPB_RsvdSecCnt
binary.LittleEndian.PutUint16(sector[14:16], b.ReservedSectorCount)
// BPB_NumFATs
sector[16] = b.NumFATs
// BPB_RootEntCnt
binary.LittleEndian.PutUint16(sector[17:19], b.RootEntryCount)
// BPB_Media
sector[21] = byte(b.Media)
// BPB_SecPerTrk
binary.LittleEndian.PutUint16(sector[24:26], b.SectorsPerTrack)
// BPB_Numheads
binary.LittleEndian.PutUint16(sector[26:28], b.NumHeads)
// BPB_Hiddsec
// sector[28:32] - it is always set to 0 because we don't partition drives yet.
// Important signature of every FAT boot sector
sector[510] = 0x55
sector[511] = 0xAA
return sector[:], nil
}
// BytesPerCluster returns the number of bytes per cluster.
func (b *BootSectorCommon) BytesPerCluster() uint32 {
return uint32(b.SectorsPerCluster) * uint32(b.BytesPerSector)
}
// ClusterOffset returns the offset of the data section of a particular
// cluster.
func (b *BootSectorCommon) ClusterOffset(n int) uint32 {
offset := b.DataOffset()
offset += (uint32(n) - FirstCluster) * b.BytesPerCluster()
return offset
}
// DataOffset returns the offset of the data section of the disk.
func (b *BootSectorCommon) DataOffset() uint32 {
offset := uint32(b.RootDirOffset())
offset += uint32(b.RootEntryCount * DirectoryEntrySize)
return offset
}
// FATOffset returns the offset in bytes for the given index of the FAT
func (b *BootSectorCommon) FATOffset(n int) int {
offset := uint32(b.ReservedSectorCount * b.BytesPerSector)
offset += b.SectorsPerFat * uint32(b.BytesPerSector) * uint32(n)
return int(offset)
}
// Calculates the FAT type that this boot sector represents.
func (b *BootSectorCommon) FATType() FATType {
var rootDirSectors uint32
rootDirSectors = (uint32(b.RootEntryCount) * 32) + (uint32(b.BytesPerSector) - 1)
rootDirSectors /= uint32(b.BytesPerSector)
dataSectors := b.SectorsPerFat * uint32(b.NumFATs)
dataSectors += uint32(b.ReservedSectorCount)
dataSectors += rootDirSectors
dataSectors = b.TotalSectors - dataSectors
countClusters := dataSectors / uint32(b.SectorsPerCluster)
switch {
case countClusters < 4085:
return FAT12
case countClusters < 65525:
return FAT16
default:
return FAT32
}
}
// RootDirOffset returns the byte offset when the root directory
// entries for FAT12/16 filesystems start. NOTE: This is absolutely useless
// for FAT32 because the root directory is just the beginning of the data
// region.
func (b *BootSectorCommon) RootDirOffset() int {
offset := b.FATOffset(0)
offset += int(uint32(b.NumFATs) * b.SectorsPerFat * uint32(b.BytesPerSector))
return offset
}
// BootSectorFat16 is the BootSector for FAT12 and FAT16 filesystems.
// It contains the common fields to all FAT filesystems and also some
// unique.
type BootSectorFat16 struct {
BootSectorCommon
DriveNumber uint8
VolumeID uint32
VolumeLabel string
FileSystemTypeLabel string
}
func (b *BootSectorFat16) Bytes() ([]byte, error) {
sector, err := b.BootSectorCommon.Bytes()
if err != nil {
return nil, err
}
// BPB_TotSec16 AND BPB_TotSec32
if b.TotalSectors < 0x10000 {
binary.LittleEndian.PutUint16(sector[19:21], uint16(b.TotalSectors))
} else {
binary.LittleEndian.PutUint32(sector[32:36], b.TotalSectors)
}
// BPB_FATSz16
if b.SectorsPerFat > 0x10000 {
return nil, fmt.Errorf("SectorsPerFat value too big for non-FAT32: %d", b.SectorsPerFat)
}
binary.LittleEndian.PutUint16(sector[22:24], uint16(b.SectorsPerFat))
// BS_DrvNum
sector[36] = b.DriveNumber
// BS_BootSig
sector[38] = 0x29
// BS_VolID
binary.LittleEndian.PutUint32(sector[39:43], b.VolumeID)
// BS_VolLab
if len(b.VolumeLabel) > 11 {
return nil, errors.New("VolumeLabel must be 11 bytes or less")
}
for i, r := range b.VolumeLabel {
if r > unicode.MaxASCII {
return nil, fmt.Errorf("'%s' in VolumeLabel not a valid ASCII char. Must be ASCII.", r)
}
sector[43+i] = byte(r)
}
// BS_FilSysType
if len(b.FileSystemTypeLabel) > 8 {
return nil, errors.New("FileSystemTypeLabel must be 8 bytes or less")
}
for i, r := range b.FileSystemTypeLabel {
if r > unicode.MaxASCII {
return nil, fmt.Errorf("'%s' in FileSystemTypeLabel not a valid ASCII char. Must be ASCII.", r)
}
sector[54+i] = byte(r)
}
return sector, nil
}
type BootSectorFat32 struct {
BootSectorCommon
RootCluster uint32
FSInfoSector uint16
BackupBootSector uint16
DriveNumber uint8
VolumeID uint32
VolumeLabel string
FileSystemTypeLabel string
}
func (b *BootSectorFat32) Bytes() ([]byte, error) {
sector, err := b.BootSectorCommon.Bytes()
if err != nil {
return nil, err
}
// BPB_RootEntCount - must be 0
sector[17] = 0
sector[18] = 0
// BPB_FATSz32
binary.LittleEndian.PutUint32(sector[36:40], b.SectorsPerFat)
// BPB_ExtFlags - Unused?
// BPB_FSVer. Explicitly set to 0 because that is really important
// to get correct.
sector[42] = 0
sector[43] = 0
// BPB_RootClus
binary.LittleEndian.PutUint32(sector[44:48], b.RootCluster)
// BPB_FSInfo
binary.LittleEndian.PutUint16(sector[48:50], b.FSInfoSector)
// BPB_BkBootSec
binary.LittleEndian.PutUint16(sector[50:52], b.BackupBootSector)
// BS_DrvNum
sector[64] = b.DriveNumber
// BS_BootSig
sector[66] = 0x29
// BS_VolID
binary.LittleEndian.PutUint32(sector[67:71], b.VolumeID)
// BS_VolLab
if len(b.VolumeLabel) > 11 {
return nil, errors.New("VolumeLabel must be 11 bytes or less")
}
for i, r := range b.VolumeLabel {
if r > unicode.MaxASCII {
return nil, fmt.Errorf("'%s' in VolumeLabel not a valid ASCII char. Must be ASCII.", r)
}
sector[71+i] = byte(r)
}
// BS_FilSysType
if len(b.FileSystemTypeLabel) > 8 {
return nil, errors.New("FileSystemTypeLabel must be 8 bytes or less")
}
for i, r := range b.FileSystemTypeLabel {
if r > unicode.MaxASCII {
return nil, fmt.Errorf("'%s' in FileSystemTypeLabel not a valid ASCII char. Must be ASCII.", r)
}
sector[82+i] = byte(r)
}
return sector, nil
}