[Bug 243178] newfs_msdos: wrong FAT type determination

[email protected] Wed, 20 May 2026 18:21:11 +0000
Newsgroups gmane.os.freebsd.devel.file-systems
Message-ID <[email protected]/bugzilla/>
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=243178

--- Comment #5 from Damjan Jovanovic <[email protected]> ---
There are several problems here.

The code is from /usr/src/sbin/newfs_msdos/mkfs_msdos.c on FreeBSD 15.

=========
ISSUE 1
=========

The fundamental cause of this bug is that the FAT bitness selection code here:

    457     if (!fat) {
    458         if (...)
...
    468             fat = 12;
    469         else if (...)
...
    477             fat = 16;
    478         else
    479             fat = 32;
    480     }

wants to select the minimum FAT bitness that spans the whole volume.

This is in direct contradiction to the FAT bitness minimum cluster check later:

    593     if (cls < mincls(fat)) {
    594         warnx("%u clusters too few clusters for FAT%u, need %u", cls,
fat,
    595             mincls(fat));
    596         goto done;
    597     }

which cannot allow FAT32 to be below 65525 clusters, as per its
Microsoft-mandated limit in
http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc

This cannot be. One of the rules MUST have priority over the other. Either
spanning the whole disk must have priority, violating Microsoft's min/max
clusters rule, or clusters must be in range of Microsoft's min/max clusters
rule for that FAT bitness, but waste some disk space in clusters that are
beyond the addressable range.

In particular, for the given example, we have:

2,147,483,648 (== 2^31) bytes
    4,194,304           512-byte sectors

Now a FAT16 filesystem would have this before its data region:
           32           reserved sectors
          512           FAT region sectors
           32           root directory sectors

The data region would have the remaining sectors:
  4,194,304 - (32 + 512 + 32)
= 4,193,728 sectors
At 64 sectors per cluster, this is:
  4,193,728 / 64
= 65,527 clusters in the data region

But the maximum addressable number of clusters for FAT16 is: 
#define MAXCLS16  0xfff4U       /* maximum FAT16 clusters */
= 65,524 maximum addressable clusters

Which means 65,527 - 65,524 = 3 clusters can't be addressed, the FAT16
filesystem cannot completely span this volume. The last 3 clusters (== 192
sectors == 98,304 bytes of disk space), cannot be addressed, and would be
wasted.

What should newfs_msdos do? It can either use FAT16 and waste a little space,
or use FAT32 which will violate Microsoft's rule on the min number of FAT32
clusters.  Currently it does the latter, and errors out when checking that
rule. We should probably allow wasting some space, but satisfy the cluster
limit rules.

=========
ISSUE 2
=========

In the calculation for the number of clusters, the size of the FAT region is
incorrectly added *TWICE*!!

Note how lines 559 and 568 *BOTH* add "bpbBigFATsecs * bpb.bpbFATs" to "x1":

    554         x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1;
    555         if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors) {
    556             warnx("meta data exceeds file system size");
    557             goto done;
    558         }
    559         x1 += x * bpb.bpbFATs;
    560         x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec *
NPB /
    561             (bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB +
    562             fat / BPN * bpb.bpbFATs);
    563         x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
    564             bpb.bpbBytesPerSec * NPB);
    565         if (set_spf) {
    566             if (bpb.bpbBigFATsecs == 0)
    567                 bpb.bpbBigFATsecs = x2;
    568             x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs;
    569         }
...
    581     } while (alignment != 0 && attempts < 2);

I am not sure how to fix this (this is an incredibly complicated section of
code - no wonder it's buggy), but since in this particular example I know the
loop runs twice, I commented out the bottom one.

With the FAT region half its previous size, this allow the data region to be
slightly bigger, causing the following check to fail on a smaller range of disk
sizes:

    593     if (cls < mincls(fat)) {
    594         warnx("%u clusters too few clusters for FAT%u, need %u", cls,
fat,
    595             mincls(fat));
    596         goto done;
    597     }

but it does not fully fix the bug, due to "issue 1" I previously described.

-- 
You are receiving this mail because:
You are the assignee for the bug.