Repository navigation
FAT Filesystem
JNode's FAT12/FAT16/FAT32 filesystem implementation with Long File Name (LFN) support.
The FAT (File Allocation Table) filesystem in JNode provides read-write support for FAT12, FAT16, and FAT32 variants. It integrates with JNode's VFS layer through the FatFileSystemType factory and uses the block device layer for sector-level I/O. The implementation includes support for Long File Names (VFAT) to overcome the 8.3 filename limitation.
| Class / File | Purpose |
|---|---|
fs/src/fs/org/jnode/fs/fat/ |
Original FAT12/FAT16/FAT32 implementation — legacy, less actively maintained |
| Class / File | Purpose |
|---|---|
FatFileSystemType.java |
Factory implementing FileSystemType, probes block devices to determine FAT type |
FatFileSystem.java |
Main filesystem, extends AbstractFileSystem, manages FAT and root entries |
BootSector.java |
BPB parsing (offsets 11-64), FAT type detection via RootEntCnt==0 && FATSz16==0, computes CountOfClusters, DataSec, FirstDataSector
|
Fat.java |
Abstract FAT table, cluster chain operations, size() returns CountOfClusters + 2
|
Fat32.java |
FAT32 variant — 4 bytes per entry, EOF mask 0x0FFFFFFF
|
Fat16.java |
FAT16 variant — 2 bytes per entry, EOF threshold 0xFFF8
|
Fat12.java |
FAT12 variant — 1.5 bytes per entry, EOF threshold 0xFF8
|
FatChain.java |
Cluster chain traversal, allocateAndClear(), read(), write() methods |
FatFile.java |
File I/O via cluster chain reads/writes |
FatDirectory.java |
Directory iteration via FatEntriesFactory
|
FatRootDirectory.java |
FAT32 root dir stored in cluster (not fixed-size like FAT12/16) |
FatEntriesFactory.java |
Scans directory entries, handles chain errors gracefully |
FatShortDirEntry.java, FatLongDirEntry.java
|
Short 8.3 and Long File Name directory entries |
The FatFileSystemType class implements the FileSystemType interface, allowing the VFS to probe block devices and instantiate FatFileSystem instances. This follows the plugin extension point pattern used throughout JNode.
The BootSector class parses the BIOS Parameter Block (BPB) from the first sector:
-
getBytesPerSector(),getSectorsPerCluster()— cluster size calculation -
getNrFats()— number of FAT copies -
getSectorsPerFat()— FAT size -
getNrRootDirEntries()— root directory size (FAT12/FAT16 only) -
getMediumDescriptor()— determines FAT12 vs FAT16
The Fat class handles cluster chain operations:
- Read/Write: Binary encoding for each FAT variant (1.5 bytes/entry for FAT12, 2 bytes for FAT16, 4 bytes for FAT32)
-
Cluster allocation:
allocNew(),allocAppend()for file growth -
Chain traversal:
getChain(),getNextCluster()for file I/O -
EOF detection: Uses variant-specific EOF markers from
FatType
FatDirectory extends AbstractDirectory and handles:
- Root directory vs subdirectory (root has fixed size, subdirectories are cluster-based)
- Entry iteration via
FSEntryinterface - Label (volume name) management in root directory
- LFN sequence handling via
FatLfnDirectory
Long File Names are stored in special directory entries (FatLfnDirEntry) preceding the 8.3 alias entry:
- Unicode name storage (up to 255 characters)
- Checksum-based association with the short name
- Unicode conversion via
LfnEntryutilities
FatFileSystem constructor:
1. Read boot sector (512 bytes)
2. Determine FAT type from medium descriptor (0xf8 = FAT16)
3. Create Fat instances for each FAT copy
4. Read root directory (fixed-size for FAT12/16, cluster-based for FAT32)
5. Validate FAT consistency across copies
FatFile.getData():
1. Get starting cluster from directory entry
2. Fat.getChain(startCluster) → array of cluster numbers
3. Map clusters to sector offsets
4. Read data from block device
FatFileSystem.flush():
1. Write dirty boot sector
2. Flush all open FatFile instances
3. Write dirty FAT copies
4. Flush dirty root/subdirectories
Commit 18fb3f1bf (#666) fixed four defects that showed up as data loss or allocation failure:
-
flush()lost data.FatEntry.flush()wrote the directory entry but not the FAT table, so a file longer than one cluster could read back truncated after a remount. It now callsgetFatFileSystem().getFat().flush()andgetApi().flush(), flushing both the FAT and the block device. -
A free-hint wraparound allocated the wrong cluster.
FatChain.allocateAndClearscans fromfat.getLastFree()to the end, then wraps tofirstCluster(). The wrap loop overwrotelon every free entry (instead of keeping the last one from the suffix scan), and it could fall through withlstill 0 when nothing was free. The loop now trackssuffixFound/lcorrectly, bounds every backward scan atfirstCluster(), and throwsFileSystemFullExceptionwhen the allocation is short — instead of returning a bogus chain. -
A circular chain looped forever. The
FatChainiterator now throwsIOException("circular chain at: …")once its index reachesfat.size(), rather than following the cycle indefinitely. -
Mounting raced device start.
FileSystemMounter.deviceStartedqueued mounting throughWorkUtils.add(...), so a filesystem could be used before the mount finished. It now callsasyncDeviceStarted(device)synchronously from the listener.
FatWriteTest gained two regressions: testFileFlushPersistsExtendedFile (3-cluster file, close, reopen read-only, compare bytes) and testAllocationWrapsWhenNoFreeClustersAfterHint (exhaust the suffix, allocate one cluster, read it back).
- Root directory: FAT12/FAT16 root has fixed size from BPB; FAT32 root is a cluster chain like any directory
-
Cluster size: Derived from
bytesPerSector * sectorsPerCluster, used for all allocation -
Dirty tracking: Files, FAT, and directories track changes independently; flush must handle all three.
FatEntry.flush()must flush the FAT and the block device, or a multi-cluster file reads back short after remount. -
The free-cluster hint is only a hint: allocation scans to the end and then wraps, but every scan must be bounded at
firstCluster()and the result checked; an unbounded wrap returns cluster 0 or a short chain. -
Circular chains are detected by an index bound, not by a visited set: the iterator throws once
index >= fat.size(). - FAT consistency: On mount, JNode logs warnings if FAT copies differ (first FAT is used)
- LFN ordering: LFN entries must immediately precede their 8.3 alias; deletion disrupts the chain
-
FAT32 detection (jfat): Must use
RootEntCnt==0 && FATSz16==0BEFORE computingtype— Microsoft spec. Some mkfs.fat versions setTotSec16with actual value whileTotSec32=0, breaking naive detection -
FAT32 CountOfClusters (jfat): Must be computed in FAT32 branch of
compute()—DataSec = TotSec - FirstDataSector, not includingRootDirSectorswhich is 0 for FAT32
- Filesystem-Layer — filesystem driver integration
- VFS-Layer — mount mechanism and path resolution
- Block-Device-Layer — sector I/O APIs
- Ext2-Ext3-Filesystem — JNode's other filesystem (ext2/ext3)