diff --git a/bin/df/df.1 b/bin/df/df.1 --- a/bin/df/df.1 +++ b/bin/df/df.1 @@ -29,7 +29,7 @@ .\" @(#)df.1 8.3 (Berkeley) 5/8/95 .\" $FreeBSD$ .\" -.Dd March 11, 2022 +.Dd March 29, 2023 .Dt DF 1 .Os .Sh NAME @@ -246,6 +246,17 @@ Filesystem Size Used Avail Capacity Mounted on /dev/ada1p2 213G 152G 44G 78% / .Ed +.Sh NOTES +For non-Unix file systems, the reported values of used and free inodes +may have a different meaning than that of used and available files and +directories. +An example is msdosfs, which in the case of FAT12 or FAT16 file systems +reports the number of available and free root directory entries instead +of inodes +.Po +where 1 to 21 such directory entries are required to store +each file or directory name or disk label +.Pc . .Sh SEE ALSO .Xr lsvfs 1 , .Xr quota 1 , diff --git a/lib/libc/sys/statfs.2 b/lib/libc/sys/statfs.2 --- a/lib/libc/sys/statfs.2 +++ b/lib/libc/sys/statfs.2 @@ -28,7 +28,7 @@ .\" @(#)statfs.2 8.5 (Berkeley) 5/24/95 .\" $FreeBSD$ .\" -.Dd March 30, 2020 +.Dd March 29, 2023 .Dt STATFS 2 .Os .Sh NAME @@ -230,6 +230,22 @@ .It Bq Er EINTEGRITY Corrupted data was detected while reading from the file system. .El +.Sh NOTES +The fields in the +.Vt statfs +structure have been defined to provide the parameters relevant for +traditional +.Tm UNIX +file systems. +For some other file systems, values that have similar, but not +identical, semantics to those described above may be returned. +An example is msdosfs, which in case of FAT12 or FAT13 file systems +reports the number of available and of free root directory entries +instead of inodes +.Po +where 1 to 21 such directory entries are required to store +each file or directory name or disk label +.Pc . .Sh SEE ALSO .Xr fhstatfs 2 , .Xr getfsstat 2 diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c --- a/sys/fs/msdosfs/msdosfs_lookup.c +++ b/sys/fs/msdosfs/msdosfs_lookup.c @@ -684,6 +684,7 @@ return error; } ndep = bptoep(pmp, bp, ddep->de_fndoffset); + rootde_alloced(ddep); DE_EXTERNALIZE(ndep, dep); @@ -721,6 +722,7 @@ ndep--; ddep->de_fndoffset -= sizeof(struct direntry); } + rootde_alloced(ddep); if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum, pmp)) break; @@ -1015,6 +1017,7 @@ */ offset -= sizeof(struct direntry); ep--->deName[0] = SLOT_DELETED; + rootde_freed(pdep); if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) || !(offset & pmp->pm_crbomask) || ep->deAttributes != ATTR_WIN95) diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c --- a/sys/fs/msdosfs/msdosfs_vfsops.c +++ b/sys/fs/msdosfs/msdosfs_vfsops.c @@ -409,6 +409,97 @@ return (0); } +/* + * The FAT12 and FAT16 file systems use a limited size root directory that + * can be created with 1 to 65535 entries for files, directories, or a disk + * label (but DOS or Windows creates at most 512 root directory entries). + * This function calculates the number of free root directory entries by + * counting the non-deleted entries (not starting with 0xE5) and by adding + * the amount of never used entries (with the position indicated by an + * entry that starts with 0x00). + */ +static int +rootdir_free(struct msdosfsmount* pmp) +{ + struct buf *bp; + struct direntry *dep; + u_long readsize; + int dirclu; + int diridx; + int dirmax; + int dirleft; + int ffree; + + dirclu = pmp->pm_rootdirblk; + + /* + * The msdosfs code ignores pm_RootDirEnts and uses pm_rootdirsize + * (measured in DEV_BSIZE) to prevent excess root dir allocations. + */ + dirleft = howmany(pmp->pm_rootdirsize * DEV_BSIZE, + sizeof(struct direntry)); + + /* Read in chunks of default maximum root directory size */ + readsize = 512 * sizeof(struct direntry); + +#ifdef MSDOSFS_DEBUG + printf("rootdir_free: blkpersec=%lu fatblksize=%lu dirsize=%lu " + "firstclu=%lu dirclu=%d entries=%d rootdirsize=%lu " + "bytespersector=%hu bytepercluster=%lu\n", + pmp->pm_BlkPerSec, pmp->pm_fatblocksize, readsize, + pmp->pm_firstcluster, dirclu, dirleft, pmp->pm_rootdirsize, + pmp->pm_BytesPerSec, pmp->pm_bpcluster); +#endif + ffree = dirleft; + while (dirleft > 0 && ffree > 0) { + if (readsize > dirleft * sizeof(struct direntry)) + readsize = dirleft * sizeof(struct direntry); +#ifdef MSDOSFS_DEBUG + printf("rootdir_free: dirclu=%d dirleft=%d readsize=%lu\n", + dirclu, dirleft, readsize); +#endif + if (bread(pmp->pm_devvp, dirclu, readsize, NOCRED, &bp) != 0) { + printf("rootdir_free: read error\n"); + if (bp != NULL) + brelse(bp); + return (-1); + } + dirmax = readsize / sizeof(struct direntry); + for (diridx = 0; diridx < dirmax && dirleft > 0; + diridx++, dirleft--) { + dep = (struct direntry*)bp->b_data + diridx; +#ifdef MSDOSFS_DEBUG + if (dep->deName[0] == SLOT_DELETED) + printf("rootdir_free: idx=%d \n", + diridx); + else if (dep->deName[0] == SLOT_EMPTY) + printf("rootdir_free: idx=%d \n", + diridx); + else if (dep->deAttributes == ATTR_WIN95) + printf("rootdir_free: idx=%d \n", + diridx, (dep->deName[0] & 0x1f) + 1); + else if (dep->deAttributes & ATTR_VOLUME) + printf("rootdir_free: idx=%d label='%11.11s'\n", + diridx, dep->deName); + else if (dep->deAttributes & ATTR_DIRECTORY) + printf("rootdir_free: idx=%d dir='%11.11s'\n", + diridx, dep->deName); + else + printf("rootdir_free: idx=%d file='%11.11s'\n", + diridx, dep->deName); +#endif + if (dep->deName[0] == SLOT_EMPTY) + dirleft = 0; + else if (dep->deName[0] != SLOT_DELETED) + ffree--; + } + brelse(bp); + bp = NULL; + dirclu += readsize / DEV_BSIZE; + } + return (ffree); +} + static int mountmsdosfs(struct vnode *odevvp, struct mount *mp) { @@ -747,6 +838,15 @@ goto error_exit; pmp->pm_fmod = 1; } + + if (FAT32(pmp)) { + pmp->pm_rootdirfree = 0; + } else { + pmp->pm_rootdirfree = rootdir_free(pmp); + if (pmp->pm_rootdirfree < 0) + goto error_exit; + } + mp->mnt_data = pmp; mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; @@ -948,8 +1048,9 @@ sbp->f_blocks = pmp->pm_maxcluster + 1; sbp->f_bfree = pmp->pm_freeclustercount; sbp->f_bavail = pmp->pm_freeclustercount; - sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ - sbp->f_ffree = 0; /* what to put in here? */ + sbp->f_files = howmany(pmp->pm_rootdirsize * DEV_BSIZE, + sizeof(struct direntry)); + sbp->f_ffree = pmp->pm_rootdirfree; return (0); } diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h --- a/sys/fs/msdosfs/msdosfsmount.h +++ b/sys/fs/msdosfs/msdosfsmount.h @@ -108,6 +108,7 @@ u_int pm_fatmult; /* these 2 values are used in FAT */ u_int pm_fatdiv; /* offset computation */ u_int pm_curfat; /* current FAT for FAT32 (0 otherwise) */ + int pm_rootdirfree; /* number of free slots in FAT12/16 root directory */ u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */ uint64_t pm_flags; /* see below */ void *pm_u2w; /* Local->Unicode iconv handle */ @@ -222,6 +223,22 @@ ? roottobn((pmp), (dirofs)) \ : cntobn((pmp), (dirclu))) +/* + * Increment the number of used entries in a fixed size FAT12/16 root + * directory + */ +#define rootde_alloced(dep) \ + if ((dep)->de_StartCluster == MSDOSFSROOT) \ + (dep)->de_pmp->pm_rootdirfree--; + +/* + * Decrement the number of used entries in a fixed size FAT12/16 root + * directory + */ +#define rootde_freed(dep) \ + if ((dep)->de_StartCluster == MSDOSFSROOT) \ + (dep)->de_pmp->pm_rootdirfree++; + #define MSDOSFS_LOCK_MP(pmp) \ lockmgr(&(pmp)->pm_fatlock, LK_EXCLUSIVE, NULL) #define MSDOSFS_UNLOCK_MP(pmp) \