]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fat: avoid parent link count underflow in rmdir
authorZhiyu Zhang <zhiyuzhang999@gmail.com>
Thu, 1 Jan 2026 11:11:48 +0000 (19:11 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 27 Jan 2026 03:07:13 +0000 (19:07 -0800)
Corrupted FAT images can leave a directory inode with an incorrect
i_nlink (e.g. 2 even though subdirectories exist). rmdir then
unconditionally calls drop_nlink(dir) and can drive i_nlink to 0,
triggering the WARN_ON in drop_nlink().

Add a sanity check in vfat_rmdir() and msdos_rmdir(): only drop the
parent link count when it is at least 3, otherwise report a filesystem
error.

Link: https://lkml.kernel.org/r/20260101111148.1437-1-zhiyuzhang999@gmail.com
Fixes: 9a53c3a783c2 ("[PATCH] r/o bind mounts: unlink: monitor i_nlink")
Signed-off-by: Zhiyu Zhang <zhiyuzhang999@gmail.com>
Reported-by: Zhiyu Zhang <zhiyuzhang999@gmail.com>
Closes: https://lore.kernel.org/linux-fsdevel/aVN06OKsKxZe6-Kv@casper.infradead.org/T/#t
Tested-by: Zhiyu Zhang <zhiyuzhang999@gmail.com>
Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/fat/namei_msdos.c
fs/fat/namei_vfat.c

index 0b920ee40a7f9fe3c57af5d939d3efedf001a3d9..262ec1b790b560fae7aadeeb6a5aaca136625a01 100644 (file)
@@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
        err = fat_remove_entries(dir, &sinfo);  /* and releases bh */
        if (err)
                goto out;
-       drop_nlink(dir);
+       if (dir->i_nlink >= 3)
+               drop_nlink(dir);
+       else {
+               fat_fs_error(sb, "parent dir link count too low (%u)",
+                       dir->i_nlink);
+       }
 
        clear_nlink(inode);
        fat_truncate_time(inode, NULL, S_CTIME);
index 4f3cc2b3089e747fba0a3e9fd16f35245273eb31..8bf5f7a9fd2382c7381eb8e77804a087c4993d50 100644 (file)
@@ -804,7 +804,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
        err = fat_remove_entries(dir, &sinfo);  /* and releases bh */
        if (err)
                goto out;
-       drop_nlink(dir);
+       if (dir->i_nlink >= 3)
+               drop_nlink(dir);
+       else {
+               fat_fs_error(sb, "parent dir link count too low (%u)",
+                       dir->i_nlink);
+       }
 
        clear_nlink(inode);
        fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);