Created
December 20, 2015 06:24
-
-
Save apparentlymart/88fa7e59ace5607c284e to your computer and use it in GitHub Desktop.
Make VFAT filesystem from Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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