Skip to content

Instantly share code, notes, and snippets.

@apparentlymart
Created December 20, 2015 06:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apparentlymart/88fa7e59ace5607c284e to your computer and use it in GitHub Desktop.
Save apparentlymart/88fa7e59ace5607c284e to your computer and use it in GitHub Desktop.
Make VFAT filesystem from Go
package main
import (
"encoding/binary"
"fmt"
"io"
"os"
)
const bytesPerSector = 512
func main() {
err := do()
if err != nil {
fmt.Println(err)
}
}
func do() error {
f, err := os.Create("made_vfat.img")
if err != nil {
return err
}
defer f.Close()
//// BIOS Parameter Block
// Jump to boot code
f.Write([]byte{0xeb, 0x58, 0x90, 'M', 'S', 'W', 'I', 'N', '4', '.', '1'})
// Basic parameter block
write(f, uint16(bytesPerSector))
write(f, uint8(1)) // 1 sector per cluster
write(f, uint16(2)) // 2 reserved sectors
write(f, uint8(1)) // 1 FAT
write(f, uint16(0)) // 0 directory entries
write(f, uint16(4)) // 4 sectors in this volume
write(f, uint8(0xf8)) // This is a "fixed disk", rather than a floppy disk
write(f, uint16(0)) // Not used on FAT32 (Sectors per FAT, on FAT16)
write(f, uint16(1)) // 1 sector per track (dummy value)
write(f, uint16(64)) // 64 heads (dummy value)
write(f, uint32(0)) // 0 sectors before partition, since we have no partitions
write(f, uint32(0)) // "Large sector count" not used; used small count above
//// Extended Boot Record
// Extended BIOS Parameter Block
write(f, uint32(1)) // FAT is 1 sector long
write(f, uint16(0)) // No flags
write(f, uint16(0)) // FAT version number
write(f, uint32(2)) // Root directory is in cluster 2
write(f, uint16(1)) // FSInfo structure is in sector 1
write(f, uint16(0)) // Backup boot sector is the real boot sector (no backup)
f.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
write(f, uint8(0x80)) // This is a hard disk
write(f, uint8(0)) // No flags
write(f, uint8(0x29)) // Signature
write(f, uint32(0)) // Serial number
f.Write([]byte{'H', 'e', 'l', 'l', 'o', ' ', ' ', ' ', ' ', ' ', ' '})
f.Write([]byte{'F', 'A', 'T', '3', '2', ' ', ' ', ' '})
// Skip over the "boot code"
seek(f, 0, bytesPerSector - 2)
// Bootable partition signature
// (We're not actually bootable so maybe we should't set this?)
write(f, uint16(0xAA55))
//// FS Information Sector
seek(f, 1, 0)
f.Write([]byte{0x52, 0x52, 0x61, 0x41}) // Signature
seek(f, 1, 0x1e4)
f.Write([]byte{0x72, 0x72, 0x41, 0x61}) // Signature
write(f, uint32(0xffffffff)) // Unknown number of free data clusters
write(f, uint32(0xffffffff)) // Unknown most recently allocated cluster
seek(f, 1, 0x1fc)
f.Write([]byte{0x00, 0x00, 0x55, 0xaa}) // Signature
//// File Allocation Table
seek(f, 2, 0)
f.Write([]byte{0xf8, 0xff, 0xff, 0x0f}) // Signature
f.Write([]byte{0xff, 0xff, 0xff, 0x0f}) // End of chain marker
f.Write([]byte{0xff, 0xff, 0xff, 0x0f}) // Root directory ends here
f.Write([]byte{0xff, 0xff, 0xff, 0x0f}) // HELLO.TXT ends here
//// Root Directory Table
filename := []byte("HELLO TXT")
seek(f, 3, 0)
// Long filename entry
write(f, uint8(0x41))
f.Write([]byte{'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0})
write(f, uint8(0xf)) // "Long filename" attribute
write(f, uint8(0x0)) // Reserved
checksum := uint8(0)
for _, b := range filename {
checksum = ((checksum & 1) << 7) + (checksum >> 1) + b
}
write(f, checksum) // Checksum of the short name
f.Write([]byte{'.', 0, 't', 0, 'x', 0, 't', 0, 0, 0, 0xff, 0xff})
write(f, uint16(0x0)) // Dummy first cluster
f.Write([]byte{0xff, 0xff, 0xff, 0xff})
// Short filename entry
f.Write(filename)
write(f, uint8(0x0)) // No File Attributes
write(f, uint8(0x0)) // No Additional Flags
write(f, uint8(0x0)) // Dummy fine create time
write(f, uint16(0x0)) // Dummy create time
write(f, uint16(0x0)) // Dummy create date
write(f, uint16(0x0)) // Dummy access date
write(f, uint16(0x0)) // MSBs of first cluster id
write(f, uint16(0x0)) // Dummy last modified time
write(f, uint16(0x0)) // Dummy last modified date
write(f, uint16(0x3)) // LSBs of first cluster id
write(f, uint32(0x6)) // File size
//// First cluster of HELLO.TXT
seek(f, 4, 0)
f.Write([]byte{'H', 'e', 'l', 'l', 'o', '\n'})
// Pad out the rest of the HELLO.TXT data cluster
seek(f, 5, -1)
write(f, uint8(0))
return nil
}
func write(w io.Writer, data interface{}) error {
return binary.Write(w, binary.LittleEndian, data)
}
func seek(f *os.File, sectorNum, offset int64) error {
_, err := f.Seek((bytesPerSector * sectorNum) + offset, 0)
return err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment