269 lines
5.8 KiB
Go
269 lines
5.8 KiB
Go
package fat
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/mitchellh/go-fs"
|
|
"math"
|
|
)
|
|
|
|
// The first cluster that can really hold user data is always 2
|
|
const FirstCluster = 2
|
|
|
|
// FAT is the actual file allocation table data structure that is
|
|
// stored on disk to describe the various clusters on the disk.
|
|
type FAT struct {
|
|
bs *BootSectorCommon
|
|
entries []uint32
|
|
}
|
|
|
|
func DecodeFAT(device fs.BlockDevice, bs *BootSectorCommon, n int) (*FAT, error) {
|
|
if n > int(bs.NumFATs) {
|
|
return nil, fmt.Errorf("FAT #%d greater than total FATs: %d", n, bs.NumFATs)
|
|
}
|
|
|
|
data := make([]byte, bs.SectorsPerFat*uint32(bs.BytesPerSector))
|
|
if _, err := device.ReadAt(data, int64(bs.FATOffset(n))); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &FAT{
|
|
bs: bs,
|
|
entries: make([]uint32, FATEntryCount(bs)),
|
|
}
|
|
|
|
fatType := bs.FATType()
|
|
for i := 0; i < int(FATEntryCount(bs)); i++ {
|
|
var entryData uint32
|
|
switch fatType {
|
|
case FAT12:
|
|
entryData = fatReadEntry12(data, i)
|
|
case FAT16:
|
|
entryData = fatReadEntry16(data, i)
|
|
default:
|
|
entryData = fatReadEntry32(data, i)
|
|
}
|
|
|
|
result.entries[i] = entryData
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// NewFAT creates a new FAT data structure, properly initialized.
|
|
func NewFAT(bs *BootSectorCommon) (*FAT, error) {
|
|
result := &FAT{
|
|
bs: bs,
|
|
entries: make([]uint32, FATEntryCount(bs)),
|
|
}
|
|
|
|
// Set the initial two entries according to spec
|
|
result.entries[0] = (uint32(bs.Media) & 0xFF) |
|
|
(0xFFFFFF00 & result.entryMask())
|
|
result.entries[1] = 0xFFFFFFFF & result.entryMask()
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Bytes returns the raw bytes for the FAT that should be written to
|
|
// the block device.
|
|
func (f *FAT) Bytes() []byte {
|
|
result := make([]byte, f.bs.SectorsPerFat*uint32(f.bs.BytesPerSector))
|
|
|
|
for i, entry := range f.entries {
|
|
switch f.bs.FATType() {
|
|
case FAT12:
|
|
f.writeEntry12(result, i, entry)
|
|
case FAT16:
|
|
f.writeEntry16(result, i, entry)
|
|
default:
|
|
f.writeEntry32(result, i, entry)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (f *FAT) AllocChain() (uint32, error) {
|
|
return f.allocNew()
|
|
}
|
|
|
|
func (f *FAT) allocNew() (uint32, error) {
|
|
dataSize := (f.bs.TotalSectors * uint32(f.bs.BytesPerSector))
|
|
dataSize -= f.bs.DataOffset()
|
|
clusterCount := dataSize / f.bs.BytesPerCluster()
|
|
lastClusterIndex := clusterCount + FirstCluster
|
|
|
|
var availIdx uint32
|
|
found := false
|
|
for i := uint32(FirstCluster); i < lastClusterIndex; i++ {
|
|
if f.entries[i] == 0 {
|
|
availIdx = i
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return 0, errors.New("FAT FULL")
|
|
}
|
|
|
|
// Mark that this is now in use
|
|
f.entries[availIdx] = 0xFFFFFFFF & f.entryMask()
|
|
|
|
return availIdx, nil
|
|
}
|
|
|
|
// Chain returns the chain of clusters starting at a certain cluster.
|
|
func (f *FAT) Chain(start uint32) []uint32 {
|
|
chain := make([]uint32, 0, 2)
|
|
|
|
cluster := start
|
|
for {
|
|
chain = append(chain, cluster)
|
|
cluster = f.entries[cluster]
|
|
|
|
if f.isEofCluster(cluster) || cluster == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return chain
|
|
}
|
|
|
|
// ResizeChain takes a given cluster number and resizes the chain
|
|
// to the given length. It returns the new chain of clusters.
|
|
func (f *FAT) ResizeChain(start uint32, length int) ([]uint32, error) {
|
|
chain := f.Chain(start)
|
|
if len(chain) == length {
|
|
return chain, nil
|
|
}
|
|
|
|
change := int(math.Abs(float64(length - len(chain))))
|
|
if length > len(chain) {
|
|
var lastCluster uint32
|
|
|
|
lastCluster = chain[0]
|
|
for i := 1; i < len(chain); i++ {
|
|
if f.isEofCluster(f.entries[lastCluster]) {
|
|
break
|
|
}
|
|
|
|
lastCluster = chain[i]
|
|
}
|
|
|
|
for i := 0; i < change; i++ {
|
|
newCluster, err := f.allocNew()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
f.entries[lastCluster] = newCluster
|
|
lastCluster = newCluster
|
|
}
|
|
} else {
|
|
panic("making chains smaller not implemented yet")
|
|
}
|
|
|
|
return f.Chain(start), nil
|
|
}
|
|
|
|
func (f *FAT) WriteToDevice(device fs.BlockDevice) error {
|
|
fatBytes := f.Bytes()
|
|
for i := 0; i < int(f.bs.NumFATs); i++ {
|
|
offset := int64(f.bs.FATOffset(i))
|
|
if _, err := device.WriteAt(fatBytes, offset); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *FAT) entryMask() uint32 {
|
|
switch f.bs.FATType() {
|
|
case FAT12:
|
|
return 0x0FFF
|
|
case FAT16:
|
|
return 0xFFFF
|
|
default:
|
|
return 0x0FFFFFFF
|
|
}
|
|
}
|
|
|
|
func (f *FAT) isEofCluster(cluster uint32) bool {
|
|
return cluster >= (0xFFFFFF8 & f.entryMask())
|
|
}
|
|
|
|
func (f *FAT) writeEntry12(data []byte, idx int, entry uint32) {
|
|
dataIdx := idx + (idx / 2)
|
|
data = data[dataIdx : dataIdx+2]
|
|
|
|
if idx%2 == 1 {
|
|
// ODD
|
|
data[0] |= byte((entry & 0x0F) << 4)
|
|
data[1] = byte((entry >> 4) & 0xFF)
|
|
} else {
|
|
// Even
|
|
data[0] = byte(entry & 0xFF)
|
|
data[1] = byte((entry >> 8) & 0x0F)
|
|
}
|
|
}
|
|
|
|
func (f *FAT) writeEntry16(data []byte, idx int, entry uint32) {
|
|
idx <<= 1
|
|
data[idx] = byte(entry & 0xFF)
|
|
data[idx+1] = byte((entry >> 8) & 0xFF)
|
|
}
|
|
|
|
func (f *FAT) writeEntry32(data []byte, idx int, entry uint32) {
|
|
idx <<= 2
|
|
data[idx] = byte(entry & 0xFF)
|
|
data[idx+1] = byte((entry >> 8) & 0xFF)
|
|
data[idx+2] = byte((entry >> 16) & 0xFF)
|
|
data[idx+3] = byte((entry >> 24) & 0xFF)
|
|
}
|
|
|
|
// FATEntryCount returns the number of entries per fat for the given
|
|
// boot sector.
|
|
func FATEntryCount(bs *BootSectorCommon) uint32 {
|
|
// Determine the number of entries that'll go in the FAT.
|
|
var entryCount uint32 = bs.SectorsPerFat * uint32(bs.BytesPerSector)
|
|
switch bs.FATType() {
|
|
case FAT12:
|
|
entryCount = uint32((uint64(entryCount) * 8) / 12)
|
|
case FAT16:
|
|
entryCount /= 2
|
|
case FAT32:
|
|
entryCount /= 4
|
|
default:
|
|
panic("impossible fat type")
|
|
}
|
|
|
|
return entryCount
|
|
}
|
|
|
|
func fatReadEntry12(data []byte, idx int) uint32 {
|
|
idx += idx / 2
|
|
|
|
var result uint32 = (uint32(data[idx+1]) << 8) | uint32(data[idx])
|
|
if idx%2 == 0 {
|
|
return result & 0xFFF
|
|
} else {
|
|
return result >> 4
|
|
}
|
|
}
|
|
|
|
func fatReadEntry16(data []byte, idx int) uint32 {
|
|
idx <<= 1
|
|
return (uint32(data[idx+1]) << 8) | uint32(data[idx])
|
|
}
|
|
|
|
func fatReadEntry32(data []byte, idx int) uint32 {
|
|
idx <<= 2
|
|
return (uint32(data[idx+3]) << 24) |
|
|
(uint32(data[idx+2]) << 16) |
|
|
(uint32(data[idx+1]) << 8) |
|
|
uint32(data[idx+0])
|
|
}
|