Filesystems: FAT32

📚 Filesystems (2 of 7)
  1. 1. Introduction
  2. 2. FAT32 You are here
  3. 3. Ext4
  4. 4. NTFS
  5. 5. XFS
  6. 6. Btrfs
  7. 7. ZFS
FAT32

In the introduction to this series , we covered the foundational concepts every filesystem needs: sectors, blocks, partitions, and the four essential services—naming, allocation, metadata, and crash recovery. Now let’s see how a real filesystem puts these into practice.

FAT32 is everywhere. USB drives, SD cards, digital cameras—if you’ve ever moved files between a Windows PC, a Mac, a Linux machine, or your car’s stereo, FAT32 made it work. What makes it fascinating isn’t complexity—it’s the opposite. FAT32 is beautifully simple, and that simplicity is exactly why it conquered the world.

The FAT family has several variants—FAT12, FAT16, FAT32, and exFAT—but in this post we’ll focus exclusively on FAT32. The others share the same core ideas but differ in sizes and details that would distract from the main story.

Remember the core filesystem structures we discussed? In FAT32, you’ll see the most straightforward implementations of these concepts. The boot sector is its superblock. Clusters are its blocks. And the FAT table itself is both the free space tracker and the file data mapper—a linked list that does double duty.

Let’s start by looking at how FAT32 organizes a partition.

The Physical Layout: A Simple Design

The entire disk is divided into just a few regions:

FAT32 partition layout: boot sector, reserved sectors, two FAT copies, and data area

That’s it. Let’s go through each region.

The Boot Sector

The very first 512 bytes of the partition are the boot sector. It has two separate responsibilities crammed into the same space:

  • It’s boot code. If this partition is bootable (say, a Windows installation), the BIOS/UEFI jumps here to start loading the operating system. The first 3 bytes are a jump instruction that skips over the metadata and lands on the actual bootstrap code at the end of the sector.
  • It’s the superblock. The bytes in between the jump instruction and the boot code describe how the filesystem is organized—cluster size, where the FAT tables are, where the root directory starts.

These two roles are interleaved in a single structure. Here are the most important fields:

Boot sector structure showing key fields: jump instruction, OEM name, bytes per sector, sectors per cluster, reserved sectors, FAT copies, total sectors, and FAT32-specific fields

When you mount a FAT32 drive, the operating system reads this boot sector first. From these fields, the OS can figure out where everything is: how big the clusters are, where the FAT tables start, how large they are, and where the root directory lives.

One important field here is root_cluster—the entrypoint of our filesystem, pointing to where the root directory information lives. We’ll get into the details of that later.

Right after the boot sector come a few more reserved sectors.

Reserved Sectors

The boot sector is part of a larger reserved area—FAT32 typically reserves 32 sectors, though most of them are just empty space filled with zeros.

Right after the boot sector, at sector 1, sits the FSINFO sector—a small structure that caches the number of free clusters and a hint for where to start looking when allocating new ones. We’ll talk about it in more detail later.

At sector 6 you’ll find a backup copy of the boot sector, and at sector 7 a backup of the FSINFO sector. If the originals get corrupted, recovery tools can restore them from here.

Sectors 2 and 8 can optionally hold additional boot code. Remember that the boot sector only has about 420 bytes available for code (the rest is taken by the filesystem description), so more complex boot loaders spill into these continuation sectors—the jump instruction in sector 0 simply jumps to sector 2 to keep going. The remaining sectors are unused.

After the reserved sectors come the FAT tables.

The FAT Tables (File Allocation Tables)

This is the heart of the filesystem—the structure that gives FAT its name. The FAT is essentially a giant array of 4-byte entries. There’s one entry for every cluster in the data area. So if your partition has 4 million clusters, the FAT has 4 million entries.

What does each entry tell you? It answers a simple question: “what comes after this cluster?” If a file is stored across clusters 1000, 1001, and 1002, then FAT entry 1000 says “next is 1001”, entry 1001 says “next is 1002”, and entry 1002 says “this is the end”. That’s a linked list—each element points to the next one. We’ll see exactly how this works with a concrete example in the next section.

An entry can also tell you that a cluster is free (0x00000000 — available for new data) or bad (0x0FFFFFF7 — the hardware has a defect there, don’t use it).

FAT32 keeps two identical copies of this table for redundancy. If one gets corrupted, the other serves as a backup.

And finally, where the actual content lives.

The Data Area

Everything left over is the data area, divided into fixed-size clusters (typically 4KB each). This is where your actual file content and directories live.

Clusters are numbered starting from 2 (clusters 0 and 1 are reserved for special values). If you have a 16GB USB drive with 4KB clusters, you have about 4 million clusters.

A cluster is FAT32’s name for a block—the smallest amount of space the filesystem can give to a file. Even if your file is just 1 byte, it still occupies an entire cluster (4KB) on disk. The rest of that cluster is wasted space. This is a tradeoff: larger clusters mean less overhead for the FAT table but more wasted space for small files.

Now that we know the layout, let’s see how these pieces work together.

How the FAT Works: Following the Chain

Let’s say we want to read a file, and we already know it starts at cluster 1000 (we’ll see later how we get that information). The file is 12KB—that’s 3 clusters worth of data at 4KB each. Here’s what the FAT table looks like for this file:

FAT[1000] = 1001        → "the cluster after 1000 is 1001"
FAT[1001] = 1002        → "the cluster after 1001 is 1002"
FAT[1002] = 0x0FFFFFFF  → "1002 is the last one, stop here"

The OS reads cluster 1000 from the data area, and then needs to find what comes next. So it checks position 1000 in the FAT table—and finds the value 1001. That means cluster 1001 is next. It reads that cluster, checks position 1001 in the FAT, finds 1002. Reads cluster 1002, checks position 1002 in the FAT, and finds 0x0FFFFFFF—the end-of-chain marker. Done.

Now, the clusters don’t have to be consecutive. Your file could just as easily be scattered around:

FAT[1000] = 5472        → "after cluster 1000, jump to 5472"
FAT[5472] = 312         → "after cluster 5472, jump to 312"
FAT[312]  = 0x0FFFFFFF  → "cluster 312 is the last one, stop here"

Same file, same 12KB, same linked list logic—the data is just spread across the disk. This is fragmentation, and it’s one of FAT32’s main performance weaknesses: the more scattered the clusters, the more the disk has jumping around to do to read a single file.

How much this matters depends on the hardware. On traditional spinning hard drives, fragmentation is a real problem—the physical read head has to move back and forth across the disk, and that takes time. That’s why defragmentation tools exist: they reorganize files so their clusters are consecutive again. On SSDs and flash memory (like USB drives and SD cards), there’s no moving head, so reading scattered clusters is almost as fast as reading consecutive ones. Fragmentation still wastes a bit of time due to extra FAT lookups, but it’s far less of a concern.

We know how to follow a cluster chain, but something is missing—how does the OS know where a file’s chain starts?

Directory Entries: Where Filenames Live

So we know how to follow a chain of clusters to read a file’s data. But how do we know which cluster to start from? And where does the filename come from? That’s what directory entries are for.

In FAT32, a directory is just a regular file whose content happens to be a list of 32-byte entries—one for each file or subdirectory it contains. Each entry packs everything the OS needs to know about a file into those 32 bytes:

  • The filename (11 bytes): stored in the classic “8.3” format—8 characters for the name, 3 for the extension, all uppercase and space-padded. So “README.TXT” fits perfectly, but “my_vacation_photo.jpg”… doesn’t. We’ll see how FAT32 handles long filenames in a moment.
  • Attributes (1 byte): flags like read-only, hidden, system, or—importantly—whether this entry is a directory rather than a regular file.
  • Timestamps (several fields): creation time, modification time, and last access date.
  • Starting cluster (4 bytes, split across two fields): this is the key piece—it tells you which cluster to go to first, and from there you follow the FAT chain as we just saw.
  • File size (4 bytes): the exact size in bytes. This is how the OS knows when to stop reading, even if the last cluster isn’t full.

Notice that 4-byte file size field—it means FAT32 can’t represent files larger than 4GB (2^32 - 1 bytes, to be precise). That’s one of FAT32’s most well-known limitations.

But where does the first directory live? Every path starts somewhere.

The Root Directory

One thing the boot sector provides is the root cluster—the cluster where the root directory starts. From there, the root directory is just another cluster chain in the data area, working exactly like any other directory. It can grow as large as needed, spanning as many clusters as necessary.

So far so good, but there’s a pretty big limitation we haven’t addressed yet.

Long Filenames: The VFAT Extension

You might have noticed a problem: 11 bytes for a filename is not a lot. “README.TXT” fits, but what about “my_vacation_photo.jpg”? In the 1990s, Microsoft solved this with an extension called VFAT (Virtual FAT), and the trick is clever.

Remember that each directory entry is 32 bytes. VFAT uses extra directory entries placed right before the real one to spell out the long filename. Each extra entry can hold 13 characters of the long name, and they’re marked with a special attribute value (0x0F) so the filesystem knows they’re not actual files—just pieces of a long name.

Let’s say you create a file called “my_vacation_photo.jpg” (21 characters). Each extra entry holds 13 characters, so this name needs two extra entries. On disk, the directory looks like this:

VFAT long name directory entries

Notice the extra entries are stored in reverse order—the last piece of the name comes first on disk. The real entry still has an 8.3 name—MY_VAC~1.JPG—because old DOS programs that don’t understand VFAT need something they can work with. Modern programs read the extra entries and reconstruct “my_vacation_photo.jpg”. The tilde-number (~1, ~2, etc.) handles conflicts: if you also create “my_vacation_photos.jpg”, its short name becomes MY_VAC~2.JPG.

The longer the name, the more extra entries it takes—each one stores 13 characters, so a 100-character filename needs 8 extra entries before the real one.

We’ve covered all the data structures. Now let’s put them together and trace what actually happens when you use a file.

Finding a File: Directory Lookup

Let’s say you want to open /photos/vacation.jpg. How does FAT32 find it?

It starts at the root directory—the boot sector tells us which cluster it lives in. The OS reads that cluster and gets a list of directory entries. Now it needs to find one called “photos”, so it scans through them one by one. There’s no index, no hash table, no shortcut—just a linear scan from the first entry to the last. It checks each entry’s name (both the short 8.3 name and the VFAT long name) until it finds a match.

Once it finds “photos” and sees that it’s a directory, it reads the starting cluster from that entry and follows it to the photos directory. Now it does the same thing again: scan through all the entries in this directory looking for “vacation.jpg”.

When it finally finds the entry, it has everything it needs: the starting cluster (say, 5000) and the file size (about 2MB). From here, reading the file is just following the FAT chain like we saw earlier.

Directory lookup: from boot sector to root directory to photos directory to file data

This linear scan approach is simple but has an obvious downside: the more files in a directory, the slower the lookup. A directory with thousands of files means scanning thousands of entries every time you open a file. More modern filesystems like ext4 use tree-based indexing to avoid this, but FAT32 keeps it simple.

Once we’ve found the file, we need to actually read its content.

Reading a File: Traversing the Cluster Chain

Let’s keep going with our 2MB vacation photo. We know it starts at cluster 5000 and is about 500 clusters long. How does the kernel actually read it?

It does exactly what we described in the FAT chain section, but 500 times: read a cluster’s data, check the FAT to find the next cluster, read that cluster’s data, check the FAT again, and so on until it hits the end marker.

But there’s a subtlety here. What if you want to read from the middle of the file—say, you seek to byte 1,600,000? The kernel needs to figure out which cluster that byte lives in. With 4KB clusters, that’s cluster number 390 in the file’s chain. And to find cluster 390, the kernel has to start at cluster 5000 and follow 390 FAT entries one by one. There’s no way to jump directly to “the 390th cluster”—you have to walk the chain from the beginning every time.

For sequential reads this isn’t so bad—the kernel reads clusters in order and just follows the chain as it goes. But for random access on large files, it adds up fast. It’s one of the prices you pay for FAT32’s simple linked-list design.

Before we get to writing files, there’s one more structure worth knowing about.

The FSINFO Sector

Think about what happens when you plug in a USB drive and your OS wants to show you how much free space is available. With what we’ve seen so far, the only way to answer that question is to walk through the entire FAT table, counting every entry that’s marked as free. On a 64GB drive with 16 million clusters, that means reading 64MB of FAT data just to mount the drive. Not great.

FAT32 solves this with a dedicated 512-byte sector called FSINFO, sitting right at sector 1. It caches just two numbers:

  • How many free clusters are left — so the OS can tell you available space instantly.
  • Where was the last free cluster found — a hint for where to start searching when allocating new clusters.

With FSINFO, plugging in a drive and making it usable is instant—the kernel reads one sector instead of scanning millions of entries.

There’s a catch, though: FSINFO is just a cache, and it can go stale. If the system crashes before updating it, the numbers might be wrong. That’s why Linux doesn’t trust it by default and scans the FAT anyway on mount. You can tell Linux to trust it with the usefree mount option if you want faster mounts on media you trust.

Now we’re ready for the other side of the story.

Writing a File: Allocating Clusters

We’ve seen how to find and read files. What about writing? Let’s say you create a new file called “test.txt” and write 12KB to it.

First, the OS needs to create the directory entry. It scans the parent directory for free space—it needs room for a VFAT long name entry plus the short 8.3 entry. It writes them, but with size 0 and no starting cluster—the file exists now, it’s just empty.

Then you write 12KB. That’s 3 clusters worth of data, so the OS needs to find 3 free clusters. Remember the FSINFO sector? One of the things it stores is the last free cluster that was found. Instead of scanning the FAT from the very beginning, the kernel starts looking from that cluster. It walks through the FAT entries until it finds one marked as free (0x00000000), claims it, and keeps going until it has enough.

Let’s say it finds clusters 8000, 8001, and 8002. Now it needs to link them together into a chain:

FAT[8000] = 8001        → "after 8000 comes 8001"
FAT[8001] = 8002        → "after 8001 comes 8002"
FAT[8002] = 0x0FFFFFFF  → "8002 is the last one"

Then it mirrors these changes to the backup FAT copy, updates the directory entry with the starting cluster (8000) and the file size (12288 bytes), and writes the actual data to those three clusters.

And that covers the full picture—reading and writing files on FAT32. Let’s wrap up.

Summary

So that’s FAT32. The partition starts with a boot sector that doubles as both bootstrap code and filesystem description. After that come the FAT tables—two identical copies of a giant array where each entry points to the next cluster in a file’s chain. The rest is the data area, split into fixed-size clusters where your actual files and directories live.

Directories are just files full of 32-byte entries, each one holding a filename, a starting cluster, and a file size. VFAT bolts long filenames on top by stacking extra entries before the real one. Finding a file means scanning directory entries one by one, and reading it means following the FAT chain cluster by cluster. Writing means finding free clusters, linking them together, and updating the directory entry. The FSINFO sector keeps a hint of where to start looking so the kernel doesn’t have to scan from the beginning every time.

No trees, no journaling, no indexing—just a linked list and linear scans. That simplicity is why a digital camera with a tiny embedded processor can write photos to an SD card, and your Linux workstation can read them perfectly. They’re both speaking the same simple language.

In the next article , we’ll look at ext4, which solves the same problems in very different ways: extent trees instead of linked lists, HTree indexing instead of linear scans, and journaling instead of hoping for the best. Same fundamental questions, much more sophisticated answers.


Want to dive deeper? You can check out the FAT filesystem implementation in the Linux kernel source code . Key files: inode.c (mounting), dir.c (directory operations), fatent.c (FAT table access). If you prefer Go, there’s a clean implementation in go-diskfs (docs ).