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

262 lines
6.5 KiB
Go

package fat
import (
"errors"
"fmt"
"time"
"github.com/mitchellh/go-fs"
)
// SuperFloppyConfig is the configuration for various properties of
// a new super floppy formatted block device. Once this configuration is used
// to format a device, it must not be modified.
type SuperFloppyConfig struct {
// The type of FAT filesystem to use.
FATType FATType
// The label of the drive. Defaults to "NONAME"
Label string
// The OEM name for the FAT filesystem. Defaults to "gofs" if not set.
OEMName string
}
// Formats an fs.BlockDevice with the "super floppy" format according
// to the given configuration. The "super floppy" standard means that the
// device will be formatted so that it does not contain a partition table.
// Instead, the entire device holds a single FAT file system.
func FormatSuperFloppy(device fs.BlockDevice, config *SuperFloppyConfig) error {
formatter := &superFloppyFormatter{
config: config,
device: device,
}
return formatter.format()
}
// An internal struct that helps maintain state and perform calculations
// during a single formatting pass.
type superFloppyFormatter struct {
config *SuperFloppyConfig
device fs.BlockDevice
}
func (f *superFloppyFormatter) format() error {
// First, create the boot sector on the device. Start by configuring
// the common elements of the boot sector.
sectorsPerCluster, err := f.SectorsPerCluster()
if err != nil {
return err
}
bsCommon := BootSectorCommon{
BytesPerSector: uint16(f.device.SectorSize()),
Media: MediaFixed,
NumFATs: 2,
NumHeads: 16,
OEMName: f.config.OEMName,
ReservedSectorCount: f.ReservedSectorCount(),
SectorsPerCluster: sectorsPerCluster,
SectorsPerTrack: 32,
TotalSectors: uint32(f.device.Len() / int64(f.device.SectorSize())),
}
// Next, fill in the FAT-type specific boot sector information
switch f.config.FATType {
case FAT12, FAT16:
// Determine the filesystem type label, standard from the spec sheet
var label string
if f.config.FATType == FAT12 {
label = "FAT12 "
} else {
label = "FAT16 "
}
// For 1.44MB Floppy, for other floppy formats see https://support.microsoft.com/en-us/kb/75131.
// We make an exception for this most common usecase as the calculations don't create a working image for older operating systems
if f.config.FATType == FAT12 && f.device.Len() == 1474560 {
bsCommon.RootEntryCount = 224
bsCommon.SectorsPerFat = 9
bsCommon.SectorsPerTrack = 18
bsCommon.Media = 240
bsCommon.NumHeads = 2
} else {
// Determine the number of root directory entries
if f.device.Len() > 512*5*32 {
bsCommon.RootEntryCount = 512
} else {
bsCommon.RootEntryCount = uint16(f.device.Len() / (5 * 32))
}
bsCommon.SectorsPerFat = f.sectorsPerFat(bsCommon.RootEntryCount, sectorsPerCluster)
}
bs := &BootSectorFat16{
BootSectorCommon: bsCommon,
FileSystemTypeLabel: label,
VolumeLabel: f.config.Label,
}
// Write the boot sector
bsBytes, err := bs.Bytes()
if err != nil {
return err
}
if _, err := f.device.WriteAt(bsBytes, 0); err != nil {
return err
}
case FAT32:
bsCommon.SectorsPerFat = f.sectorsPerFat(0, sectorsPerCluster)
bs := &BootSectorFat32{
BootSectorCommon: bsCommon,
FileSystemTypeLabel: "FAT32 ",
FSInfoSector: 1,
VolumeID: uint32(time.Now().Unix()),
VolumeLabel: f.config.Label,
}
// Write the boot sector
bsBytes, err := bs.Bytes()
if err != nil {
return err
}
if _, err := f.device.WriteAt(bsBytes, 0); err != nil {
return err
}
// TODO(mitchellh): Create the fsinfo structure
// TODO(mitchellh): write the boot sector copy
default:
return fmt.Errorf("Unknown FAT type: %d", f.config.FATType)
}
// Create the FATs
fat, err := NewFAT(&bsCommon)
if err != nil {
return err
}
// Write the FAT
if err := fat.WriteToDevice(f.device); err != nil {
return err
}
var rootDir *DirectoryCluster
if f.config.FATType == FAT32 {
panic("TODO")
} else {
rootDir, err = NewFat16RootDirectoryCluster(&bsCommon, f.config.Label)
if err != nil {
return err
}
offset := int64(bsCommon.RootDirOffset())
if _, err := f.device.WriteAt(rootDir.Bytes(), offset); err != nil {
return err
}
}
return nil
}
func (f *superFloppyFormatter) ReservedSectorCount() uint16 {
if f.config.FATType == FAT32 {
return 32
} else {
return 1
}
}
func (f *superFloppyFormatter) SectorsPerCluster() (uint8, error) {
if f.config.FATType == FAT12 {
return f.defaultSectorsPerCluster12()
} else if f.config.FATType == FAT16 {
return f.defaultSectorsPerCluster16()
} else {
return f.defaultSectorsPerCluster32()
}
}
func (f *superFloppyFormatter) defaultSectorsPerCluster12() (uint8, error) {
var result uint8 = 1
sectors := f.device.Len() / int64(f.device.SectorSize())
for (sectors / int64(result)) > 4084 {
result *= 2
if int(result)*f.device.SectorSize() > 4096 {
return 0, errors.New("disk too large for FAT12")
}
}
return result, nil
}
func (f *superFloppyFormatter) defaultSectorsPerCluster16() (uint8, error) {
sectors := f.device.Len() / int64(f.device.SectorSize())
if sectors <= 8400 {
return 0, errors.New("disk too small for FAT16")
} else if sectors > 4194304 {
return 0, errors.New("disk too large for FAT16")
}
switch {
case sectors > 2097152:
return 64, nil
case sectors > 1048576:
return 32, nil
case sectors > 524288:
return 16, nil
case sectors > 262144:
return 8, nil
case sectors > 32680:
return 4, nil
default:
return 2, nil
}
}
func (f *superFloppyFormatter) defaultSectorsPerCluster32() (uint8, error) {
sectors := f.device.Len() / int64(f.device.SectorSize())
if sectors <= 66600 {
return 0, errors.New("disk too small for FAT32")
}
switch {
case sectors > 67108864:
return 64, nil
case sectors > 33554432:
return 32, nil
case sectors > 16777216:
return 16, nil
case sectors > 532480:
return 8, nil
default:
return 1, nil
}
}
func (f *superFloppyFormatter) fatCount() uint8 {
return 2
}
func (f *superFloppyFormatter) sectorsPerFat(rootEntCount uint16, sectorsPerCluster uint8) uint32 {
bytesPerSec := f.device.SectorSize()
totalSectors := int(f.device.Len()) / bytesPerSec
rootDirSectors := ((int(rootEntCount) * 32) + (bytesPerSec - 1)) / bytesPerSec
tmp1 := totalSectors - (int(f.ReservedSectorCount()) + rootDirSectors)
tmp2 := (256 * int(sectorsPerCluster)) + int(f.fatCount())
if f.config.FATType == FAT32 {
tmp2 /= 2
}
return uint32((tmp1 + (tmp2 - 1)) / tmp2)
}