mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 04:43:53 +00:00
msdosfs: fix potential inode collision on FAT12 and FAT16
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 directories with 512 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. A sub-directory in cluster 2 and with 512 byte clusters will therefore lead to a re-use of inode 1024 when there are at least 32 DOS 8.3 style filenames in the root directory (or 11 14-character Windows long file names, each of which takes up 3 directory entries). FAT32 file systems are not affected by this issue and FAT12/FAT16 file systems with larger cluster sizes are unlikely to have as many directory entries in the root directory as are required to cause the collision. This commit leads to inode numbers that are guaranteed to not collide for all valid FAT12 and FAT16 file system parameters. It does also provide a small speed-up due to more efficient use of the vnode cache. Approved by: mckusick MFC after: 3 days Differential Revision: https://reviews.freebsd.org/D43978
This commit is contained in:
parent
81dc3a4d3b
commit
445d3d227e
|
@ -162,7 +162,7 @@ struct denode {
|
|||
u_long de_FileSize; /* size of file in bytes */
|
||||
struct fatcache de_fc[FC_SIZE]; /* FAT cache */
|
||||
u_quad_t de_modrev; /* Revision level for lease. */
|
||||
uint64_t de_inode; /* Inode number (really byte offset of direntry) */
|
||||
uint64_t de_inode; /* Inode number (really index of DOS style direntry) */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -217,6 +217,12 @@ struct denode {
|
|||
#define VTODE(vp) ((struct denode *)(vp)->v_data)
|
||||
#define DETOV(de) ((de)->de_vnode)
|
||||
|
||||
#define DETOI(pmp, cn, off) \
|
||||
((cn) == MSDOSFSROOT \
|
||||
? (((uint64_t)(off) >> 5)) \
|
||||
: (((((uint64_t)pmp->pm_bpcluster * ((cn) - 2) + (off))) >> 5) \
|
||||
+ pmp->pm_RootDirEnts))
|
||||
|
||||
#define DETIMES(dep, acc, mod, cre) do { \
|
||||
if ((dep)->de_flag & DE_UPDATE) { \
|
||||
(dep)->de_flag |= DE_MODIFIED; \
|
||||
|
|
|
@ -133,10 +133,13 @@ deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
|
|||
* entry that represented the file happens to be reused while the
|
||||
* deleted file is still open.
|
||||
*/
|
||||
inode = (uint64_t)pmp->pm_bpcluster * dirclust + diroffset;
|
||||
inode = DETOI(pmp, dirclust, diroffset);
|
||||
|
||||
error = vfs_hash_get(mntp, inode, lkflags, curthread, &nvp,
|
||||
de_vncmpf, &inode);
|
||||
#ifdef MSDOSFS_DEBUG
|
||||
printf("vfs_hash_get(inode %lu) error %d\n", inode, error);
|
||||
#endif
|
||||
if (error)
|
||||
return (error);
|
||||
if (nvp != NULL) {
|
||||
|
@ -191,6 +194,9 @@ deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
|
|||
}
|
||||
error = vfs_hash_insert(nvp, inode, lkflags, curthread, &xvp,
|
||||
de_vncmpf, &inode);
|
||||
#ifdef MSDOSFS_DEBUG
|
||||
printf("vfs_hash_insert(inode %lu) error %d\n", inode, error);
|
||||
#endif
|
||||
if (error) {
|
||||
*depp = NULL;
|
||||
return (error);
|
||||
|
@ -589,8 +595,11 @@ reinsert(struct denode *dep)
|
|||
return;
|
||||
#endif
|
||||
vp = DETOV(dep);
|
||||
dep->de_inode = (uint64_t)dep->de_pmp->pm_bpcluster * dep->de_dirclust +
|
||||
dep->de_diroffset;
|
||||
dep->de_inode = DETOI(dep->de_pmp, dep->de_dirclust, dep->de_diroffset);
|
||||
#ifdef MSDOSFS_DEBUG
|
||||
printf("vfs_hash_rehash(inode %lu, refcnt %lu, vp %p)\n",
|
||||
dep->de_inode, dep->de_refcnt, vp);
|
||||
#endif
|
||||
vfs_hash_rehash(vp, dep->de_inode);
|
||||
}
|
||||
|
||||
|
@ -608,6 +617,10 @@ msdosfs_reclaim(struct vop_reclaim_args *ap)
|
|||
/*
|
||||
* Remove the denode from its hash chain.
|
||||
*/
|
||||
#ifdef MSDOSFS_DEBUG
|
||||
printf("vfs_hash_remove(inode %lu, refcnt %lu, vp %p)\n",
|
||||
dep->de_inode, dep->de_refcnt, vp);
|
||||
#endif
|
||||
vfs_hash_remove(vp);
|
||||
/*
|
||||
* Purge old data structures associated with the denode.
|
||||
|
|
|
@ -586,7 +586,7 @@ msdosfs_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname
|
|||
}
|
||||
if (FAT32(pmp) && scn == MSDOSFSROOT)
|
||||
scn = pmp->pm_rootdirblk;
|
||||
inode1 = scn * pmp->pm_bpcluster + blkoff;
|
||||
inode1 = DETOI(pmp, scn, blkoff);
|
||||
if (VTODE(*vpp)->de_inode != inode1) {
|
||||
vput(*vpp);
|
||||
goto restart;
|
||||
|
|
Loading…
Reference in a new issue