Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

FAT12 on 1.44 MB Floppy Images: Layout, Metadata, and a Linux Walkthrough

Tech 1

FAT12 on 1.44 MB media organizes space using clusters (allocation units), which are made of one or more sectors; sectors are the smallest addressable blocks. A standard 3.5" floppy uses 80 cylinders × 2 heads × 18 sectors per track × 512 bytes per sector = 2880 sectors (1,474,560 bytes).

Logical sectors (LBA) are numbered from 0 upward. On typical controllers the medium is read cylinder-by-cylinder; CHS-to-LBA mapping for 1.44 MB is LBA = (cylinder × heads + head) × sectors_per_track + (sector − 1), with heads=2 and sectors_per_track=18.

FAT12 volume layout (1.44 MB, defaults)

  • Sector 0: Boot sector (BPB + bootstrap)
  • Sectors 1–9: FAT #0 (9 sectors)
  • Sectors 10–18: FAT #1 (mirrored copy)
  • Sectors 19–32: Root directory (14 sectors; 224 entries × 32 bytes)
  • Sectors 33–2879: Data region (clusters start here)

With SecPerClus=1, the first data sector is 33. The LBA of cluster N is first_data_lba + (N − 2) × SecPerClus; for this format: LBA(cluster N) = 33 + (N − 2).

Boot sector and BPB (FAT12) The 512-byte boot sector carries the BIOS Parameter Block (BPB), optional boot code, and the signature 0x55AA at bytes 510–511.

Typical field layout for a 1.44 MB FAT12 volume:

Field Offset Size Meaning Typical
BS_jmpBoot 0 3 x86 jump to boot code EB 3C 90, etc.
BS_OEMName 3 8 OEM ID (8 chars) "mkfs.fat"
BPB_BytesPerSec 11 2 Bytes per sector 0x0200 (512)
BPB_SecPerClus 13 1 Sectors per cluster 0x01
BPB_RsvdSecCnt 14 2 Reserved sectors 0x0001
BPB_NumFATs 16 1 FAT copies 0x02
BPB_RootEntCnt 17 2 Root entries 0x00E0 (224)
BPB_TotSec16 19 2 Total sectors (if < 65536) 0x0B40 (2880)
BPB_Media 21 1 Media descriptor 0xF0
BPB_FATSz16 22 2 Sectors per FAT 0x0009
BPB_SecPerTrk 24 2 Sectors/track 0x0012 (18)
BPB_NumHeads 26 2 Number of heads 0x0002
BPB_HiddSec 28 4 Hidden sectors 0x00000000
BPB_TotSec32 32 4 Total sectors if TotSec16==0 0x00000000
BS_DrvNum 36 1 Int 13h drive number 0x00
BS_Reserved1 37 1 Reserved 0x00
BS_BootSig 38 1 Extenedd signature 0x29
BS_VolID 39 4 Volume serial varies
BS_VolLab 43 11 Volume label (11 chars) "NO NAME "
BS_FileSysType 54 8 Filesystem tag (8 chars) "FAT12 "
Boot code 62 448 Bootstrap/data implementation-defined
Signature 510 2 0x55AA 0xAA55

FAT (File Allocation Table)

  • Two identical copies (FAT1 and FAT2), each 9 sectors.
  • Each FAT12 entry is 12 bits; entries 0 and 1 are reserved.
  • Entry values:
    • 0xFF0–0xFF6: reserved
    • 0xFF7: bad cluster
    • 0xFF8–0xFFF: end-of-chain (EOC)
    • other: next cluster in the chain
  • Cluster numbering begins at 2; cluster 2 maps to the first cluster in the data region.
  • Packing: two 12-bit entries are stored in three bytes. For entries n (even) and n+1:
    • byte0 = low 8 bits of entry n
    • byte1 = high 4 bits of entry n (low nibble) | low 4 bits of entry n+1 (high nibble)
    • byte2 = high 8 bits of entry n+1

Root directory entries (SFN) Each directory entry is 32 bytes. Root directory size is fixed by BPB_RootEntCnt (224 entries → 14 sectors).

Offset Size Description
0 8 Filename (padded with spaces)
8 3 Extension
11 1 Attributes
12 2 Reserved (NT/Case)
14 2 Creation time
16 2 Creation date
18 2 Last access date
20 2 High word of first cluster (FAT16/32; ignored in FAT12)
22 2 Last write time
24 2 Last write date
26 2 First cluster (FAT12)
28 4 File size in bytes

Attributes include: 0x10 (directory), 0x08 (volume label), 0x20 (archive), 0x02 (hidden), 0x01 (read-only), and the 0x0F LFN marker for long filename entries.

Long File Name (LFN) entries LFNs are stored as one or more special entries (attribute 0x0F) placed immediately before the short-name entry. Each LFN entry is 32 bytes with UTF-16 name fragments.

Byte range Description
0 Ordinal (1..n, high bit set in the last LFN entry)
1–10 Name chars (5 UTF‑16 code units)
11 Attribute = 0x0F
12 Type (0)
13 Checksum of SFN
14–25 Name chars (6 UTF‑16 code units)
26–27 Must be 0
28–31 Name chars (2 UTF‑16 code units)

Allocation differences

  • Root directory items live in the dedicated root region; creating a file or a directory there consumes one 32-byte entry (plus LFN entries if present). The data for a file/directory consumes clusters from the data region.
  • Creating a subdirectory allocates at least one cluster for its own directory table (with "." and ".." entries) and one directory entry in its parent.

Data region and cluster chains Files occupy one or more clusters, linked by the FAT. The first cluster is in the directory entry; follow FAT entries untill an EOC value.

Linux walkthrough: create, inspect, and manipulate a FAT12 image

Create a 1.44 MB image Option A (truncate):

  • truncate -s 1440K floppy.img

Option B (dd):

  • dd if=/dev/zero of=floppy.img bs=1K count=1440 status=none

Format as FAT12

  • mkfs.fat -F 12 -S 512 -s 1 -n "NO NAME" floppy.img

Peek at the boot sector and signature

  • xxd -g 1 -l 64 floppy.img
  • tail -c 2 floppy.img | xxd -g 1

Extract the FAT and root directory

  • dd if=floppy.img bs=512 skip=1 count=9 status=none | xxd -g 1 | head
  • dd if=floppy.img bs=512 skip=19 count=14 status=none | xxd -g 1 | head

Mount, write a file, unmount

  • sudo mkdir -p /mnt/f12
  • sudo mount -o loop,uid=$(id -u),gid=$(id -g) floppy.img /mnt/f12
  • printf "hello\n" > a.txt && cp a.txt /mnt/f12/
  • sync && sudo umount /mnt/f12

Interpret the structures after copying a.txt

  • Root directory starts at LBA 19 → offset 19×512 = 0x2600. One entry for a short name is 32 bytes; if an LFN was produced by the OS, one or more 0x0F entries precede the base entry.
  • The first cluster field (offset 26 in the base entry) might be 0x0003 on a freshly formatted volume. With SecPerClus=1, LBA = 33 + (3 − 2) = 34 → offset 0x4400 for file data.
  • FAT1 begins at LBA 1. The first three bytes are typically F0 FF FF (FAT[0]=0xFF0, FAT[1]=0xFFF). If the file uses cluster 3 and ends there, FAT[3]=0xFFF. Entries 2 (free) and 3 (EOC) are packed as 00 F0 FF.

Sample bytes from FAT showing entries 0–5 after creating one single-cluster file at cluster 3:

  • dd if=floppy.img bs=1 skip=$((5121)) count=$((34)) status=none | xxd -g 1 -l 24

You should see a sequence beginning like:

  • f0 ff ff | 00 f0 ff | 00 00 00 ...
    • f0 ff ff → FAT[0]=FF0, FAT[1]=FFF
    • 00 f0 ff → FAT[2]=000, FAT[3]=FFF

Data region start and a.txt content

  • First data sector: 33 → offset 0x4200 for LBA 33, 0x4400 for LBA 34 (cluster 3). Inspect with:
    • dd if=floppy.img bs=512 skip=34 count=1 status=none | xxd -g 1

Create a file larger than one sector and a subdirectory

  • sudo mount -o loop,uid=$(id -u),gid=$(id -g) floppy.img /mnt/f12
  • head -c 522 /dev/zero > big.bin && cp big.bin /mnt/f12/
  • mkdir /mnt/f12/dir
  • sync && sudo umount /mnt/f12

Expected FAT changes (illustrative)

  • Suppose big.bin gets clusters 4 → 5 → EOC. Entries 4 and 5 pack as 05 F0 FF.
  • A newly created subdirectory "dir" may be allocated cluster 6 with EOC; entries 6 and 7 pack into FF 0F 00 (entry 6 = FFF, entry 7 = 000).

You can verify with:

  • dd if=floppy.img bs=512 skip=1 count=1 status=none | xxd -g 1 | head
    • Look for sequences f0 ff ff (entries 0–1), 00 f0 ff (2–3), 05 f0 ff (4–5), ff 0f 00 (6–7).

Inspect the subdirectory’s data

  • Cluster 6 LBA = 33 + (6 − 2) = 37 → offset 0x4A00. Dump it:
    • dd if=floppy.img bs=512 skip=37 count=1 status=none | xxd -g 1
  • The first two entries are "." and "..". Additional entries describe files in that subdirectory.

Create a long filename and a file in the subdirectory

  • sudo mount -o loop,uid=$(id -u),gid=$(id -g) floppy.img /mnt/f12
  • printf "content\n" > /mnt/f12/abcdefghijklmn.txt
  • printf "aaaaaaaa\n" > /mnt/f12/dir/c.txt
  • sync && sudo umount /mnt/f12

Root directory view for the long name

  • Dump the root region again and look for a sequence of LFN entries (attribute 0x0F) immediately before the base 8.3 entry. For "abcdefghijklmn.txt" the LFN spans multiple entries; the SFN may appear as ABCDEF~1.TXT.

  • dd if=floppy.img bs=512 skip=19 count=2 status=none | xxd -g 1 | sed -n '1,80p'

File in subdirectory

  • Locate the directory "dir" entry (in the root) to read its first cluster, then dump that cluster to find c.txt’s directory entry. Its first cluster value maps to a single sector if FAT shows EOC right away.

  • Example: if c.txt uses cluster 9 and FAT[9]=FFF, the file is one cluster long. Its content lives at LBA 33 + (9 − 2) = 40.

  • dd if=floppy.img bs=512 skip=40 count=1 status=none | xxd -g 1

Quick reference formulas

  • root_dir_sectors = ceil((BPB_RootEntCnt × 32) / BPB_BytesPerSec)
  • first_data_lba = BPB_RsvdSecCnt + (BPB_NumFATs × BPB_FATSz16) + root_dir_sectors
  • lba(cluster) = first_data_lba + (cluster − 2) × BPB_SecPerClus
  • max clusters (FAT12) ≈ floor((FATSectors × 512 × 8) / 12)

On a 1.44 MB image with the defaults shown: root_dir_sectors=14, first_data_lba=1 + (2×9) + 14 = 33, and LBA(cluster N)=33 + (N − 2).

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.