blob: d99cc97839e26f84a2668acdd42accd14a0b61b8 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package bootrecord
import (
"errors"
"fmt"
"thinfs/bitops"
)
const (
bootSig = 0xAA55
bootSigExtended = 0x29
driveNumberDisk = 0x80
driveNumberFloppy = 0x00
)
// FATType describes the type of underlying filesystem described by the boot record.
type FATType int
// Identify the type of filesystem described by this boot record.
const (
FATInvalid FATType = 0
FAT12 FATType = 12
FAT16 FATType = 16
FAT32 FATType = 32
)
func bootSectorSignatureValid(sig []uint8) error {
s := bitops.GetLE16(sig)
if s != bootSig {
return fmt.Errorf("Expected boot signature: %x, but got %x", bootSig, s)
}
return nil
}
// Shared beginning of bootsector.
type bootsectorPrefix struct {
// Boot Sector
jmpBoot [3]uint8 // Jump instruction 0xEB__90 or 0xE9____
oemName [8]uint8 // Informal name of system which formatted the volume
}
func (b *bootsectorPrefix) validate() error {
if b.jmpBoot[0] == 0xE9 {
return nil
}
if b.jmpBoot[0] == 0xEB && b.jmpBoot[2] == 0x90 {
return nil
}
return errors.New("Invalid jmpBoot instruction")
}
// Shared BPB fields between FAT12/16/32.
type bpbShared struct {
bytesPerSec [2]uint8 // 512, 1024, 2048, or 4096
sectorsPerCluster uint8 // Must be a power of 2 between 1 and 128
numSectorsReserved [2]uint8 // FAT12/16: 1. FAT32: 32
numFATs uint8 // Greater than or equal to 1. Usually 2
numRootEntries [2]uint8 // FAT12/16: Root directory entries. FAT32: 0
totalSectors16 [2]uint8 // FAT12/16: Sector count, if < 0x10000. FAT32: Always 0
media uint8 // Media descriptor
sectorsPerFAT16 [2]uint8 // FAT12/16: Sectors for ONE FAT. FAT32: 0
sectorsPerTrack [2]uint8 // Geometry info
numHeads [2]uint8 // Geometry info
numSectorsHidden [4]uint8 // Hidden sectors preceding partition
totalSectors32 [4]uint8 // FAT12/16: Sector count if >= 0x10000. FAT32: Sector count
}
func (b *bpbShared) BytesPerSec() uint32 {
return uint32(bitops.GetLE16(b.bytesPerSec[:]))
}
func (b *bpbShared) SectorsPerCluster() uint32 {
return uint32(b.sectorsPerCluster)
}
func (b *bpbShared) NumSectorsReserved() uint32 {
return uint32(bitops.GetLE16(b.numSectorsReserved[:]))
}
func (b *bpbShared) NumFATs() uint32 {
return uint32(b.numFATs)
}
func (b *bpbShared) NumRootEntries() uint32 {
return uint32(bitops.GetLE16(b.numRootEntries[:]))
}
func (b *bpbShared) TotalSectors16() uint32 {
return uint32(bitops.GetLE16(b.totalSectors16[:]))
}
func (b *bpbShared) SectorsPerFAT16() uint32 {
return uint32(bitops.GetLE16(b.sectorsPerFAT16[:]))
}
func (b *bpbShared) SectorsPerTrack() uint32 {
return uint32(bitops.GetLE16(b.sectorsPerTrack[:]))
}
func (b *bpbShared) NumHeads() uint32 {
return uint32(bitops.GetLE16(b.numHeads[:]))
}
func (b *bpbShared) SectorsHidden() uint32 {
return uint32(bitops.GetLE32(b.numSectorsHidden[:]))
}
func (b *bpbShared) TotalSectors32() uint32 {
return uint32(bitops.GetLE32(b.totalSectors32[:]))
}
func (b *bpbShared) TotalSectors() uint32 {
ts16 := b.TotalSectors16()
ts32 := b.TotalSectors32()
if ts16 != 0 {
return ts16
}
return ts32
}
// Without verification, tries to guess FAT group. Returns true for FAT32, false for FAT12/16.
// Does not verify any correctness.
func (b *bpbShared) guessFATType() bool {
return b.NumRootEntries() == 0
}
func (b *bpbShared) TotalClusters(firstDataSector uint32) uint32 {
totalSectors := b.TotalSectors()
sectorsPerCluster := b.SectorsPerCluster()
numDataSectors := totalSectors - firstDataSector
return numDataSectors / sectorsPerCluster
}
// For a detailed description of the validation occurring here, see pages 9-13 of "FAT: General
// Overview of On-Disk Format".
// The "large" argument is true if the filesystem supported by bpbShared is FAT32.
// "large" is false for FAT12 and FAT16.
func (b *bpbShared) validate(large bool) error {
bytesPerSec := b.BytesPerSec()
switch bytesPerSec {
case 512, 1024, 2048, 4096:
default:
return fmt.Errorf("Bytes/Sector invalid: %d", bytesPerSec)
}
sectorsPerCluster := uint32(b.sectorsPerCluster)
switch sectorsPerCluster {
case 1, 2, 4, 8, 16, 32, 64, 128:
default:
return fmt.Errorf("Sectors/Cluster invalid: %d", sectorsPerCluster)
}
// Validation of both bytesPerSec and sectorsPerCluster together.
bytesPerCluster := bytesPerSec * sectorsPerCluster
if bytesPerCluster > 32*1024 {
return fmt.Errorf("Byte/Cluster greater than 32K: %d", bytesPerCluster)
}
numSectorsReserved := b.NumSectorsReserved()
if large && numSectorsReserved == 0 {
return errors.New("FAT32 cannot have zero reserved sectors")
} else if !large && numSectorsReserved != 1 {
return errors.New("FAT12/16 cannot have any number of reserved sectors other than one")
}
if b.NumFATs() == 0 {
return errors.New("NumFATs cannot be zero")
}
numRootEntries := b.NumRootEntries()
if large && numRootEntries != 0 {
return errors.New("FAT32 must set numRootEntries to zero")
} else if !large {
if numRootEntries == 0 {
return errors.New("FAT12/16 must have at least one root entry")
} else if numRootEntries*32%bytesPerSec != 0 {
// "For FAT12 and FAT16 volumes, this value should always specify a count that when
// multiplied by 32 results in an even multiple of BPB_BytsPerSec".
return errors.New("NumRootEntries * 32 must be an even multiple of bytes per sector")
}
}
// totalSectors16 and totalSectors32 should be evaluated together.
totalSectors16 := b.TotalSectors16()
totalSectors32 := b.TotalSectors32()
if large {
if totalSectors16 != 0 {
return errors.New("FAT32 must set totalSectors16 to zero")
} else if totalSectors32 == 0 {
return errors.New("FAT32 must set totalSectors32 to something other than zero")
}
} else {
if totalSectors16 == 0 && totalSectors32 == 0 {
return errors.New("FAT12/16 must set either totalSectors16 or totalSectors32, not neither")
} else if totalSectors16 != 0 && totalSectors32 != 0 {
return errors.New("FAT12/16 must set at most one of totalSectors16 or totalSectors32, not both")
}
}
sectorsPerFAT16 := b.SectorsPerFAT16()
if large && sectorsPerFAT16 != 0 {
return errors.New("FAT32 must set sectorsPerFAT16 to zero")
} else if !large && sectorsPerFAT16 == 0 {
return errors.New("FAT12/16 must set sectorsPerFAT16 to something other than zero")
}
return nil
}
// Shared bootsector fields between FAT12/16 and FAT32, but at different offsets.
type bootsectorSuffix struct {
driveNumber uint8 // Drive Number: 0x80 or 0x00
reserved1 uint8 // Zero
extBootSig uint8 // bootSigExtended
volumeID [4]uint8 // Volume Serial Number (usually related to creation time)
volumeLabel [11]uint8 // Volume Label
fsType [8]uint8 // FS Type (either FAT12, FAT16 or FAT32)
}
func (b *bootsectorSuffix) validate() error {
if b.driveNumber != driveNumberDisk && b.driveNumber != driveNumberFloppy {
return fmt.Errorf("Expected drive number %x, found: %x", driveNumberDisk, b.driveNumber)
}
if b.extBootSig != bootSigExtended {
return fmt.Errorf("Expected boot signature: %x, found %x", bootSigExtended, b.extBootSig)
}
return nil
}