! This might be a candidate for FreeBSD-13.3, but I consider it as less urgent than the previous FAT file system fix. !
FAT file systems do not use inodes, instead all file meta-information is stored in directory entries.
FAT12 and FAT16 use a fixed size area for root directories, with typically 512 entries of 32 bytes each (for a total of 16 KB) on hard disk formats.
The file system data is stored in clusters of typically 512 to 4096 bytes. depending on the size of the file system.
The current code uses the offset of a DOS 8.3 style directory entry as a pseudo-inode, which leads to inode values of 0 to 16368 for typical root directory entries.
Sub-directories use 2 cluster length plus the byte offset of the directory entry in the data area for the pseudo-inode, which may be as low as 1024 in case of 512 byte clusters.
This issue is demonstrated by the following test script:
#!/bin/sh FS=/mnt/MSDOSFS-TEST MDUNIT=0 cleanup () { cd / umount /dev/md$MDUNIT rm -rf $FS fsck_msdosfs -n /dev/md$MDUNIT mdconfig -u $MDUNIT -d } mdconfig -u $MDUNIT -t malloc -s 4m newfs_msdos -F 16 -c 1 /dev/md$MDUNIT mkdir -p $FS mount -t msdos /dev/md$MDUNIT $FS trap "cleanup" EXIT cd $FS mkdir TESTDIR touch TESTDIR/TEST for i in $(jot 33) do touch TEST.$i done
This script reports an error when writing the 32nd entry and the file system will have been remounted r/o when the 33rd file is to be written.
The 32th file gets a pseudo-inode value of 1024, the same value already assigned to TESTDIR, leading to a directory and a file with colliding inode numbers.
The patch changes the calculation of pseudo-inodes to account for the actual number of directory entries in the root file system and avoids the collision for all supported file-system parameters.