fat: roll back the new entry when create/mkdir fails to build the inode

vfat_create(), vfat_mkdir(), msdos_create() and msdos_mkdir() add the
on-disk entry before calling fat_build_inode() to set up the VFS inode. 
If fat_build_inode() fails, the error is returned but the entry stays on
disk and the file or directory shows up after remount.

Remove the entry in that case.  For mkdir, also free the directory cluster
and decrement the parent's link count.

[akpm@linux-foundation.org" export _fat_msg() to modules]
Link: https://lore.kernel.org/tencent_4EDD66523CED299DB0C99498B4D8C939A009@qq.com
Signed-off-by: zhoumin <teczm@foxmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
This commit is contained in:
zhoumin 2026-10-12 21:20:06 +08:00 committed by Andrew Morton
parent e60b8f7a1d
commit edb528227f
3 changed files with 37 additions and 8 deletions

View File

@ -62,6 +62,7 @@ void _fat_msg(struct super_block *sb, const char *level, const char *fmt, ...)
_printk(FAT_PRINTK_PREFIX "%pV\n", level, sb->s_id, &vaf);
va_end(args);
}
EXPORT_SYMBOL_GPL(_fat_msg);
/* Flushes the number of free clusters on FAT32 */
/* XXX: Need to write one per FSINFO block. Currently only writes 1 */

View File

@ -292,11 +292,17 @@ static int msdos_create(struct mnt_idmap *idmap, struct inode *dir,
if (err)
goto out;
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
brelse(sinfo.bh);
if (IS_ERR(inode)) {
int err2;
err = PTR_ERR(inode);
err2 = fat_remove_entries(dir, &sinfo);
if (err2)
fat_msg(sb, KERN_WARNING,
"failed to roll back new entry (%d)", err2);
goto out;
}
brelse(sinfo.bh);
fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
@ -381,12 +387,20 @@ static struct dentry *msdos_mkdir(struct mnt_idmap *idmap, struct inode *dir,
inc_nlink(dir);
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
brelse(sinfo.bh);
if (IS_ERR(inode)) {
int err2;
err = PTR_ERR(inode);
/* the directory was completed, just return a error */
goto out;
err2 = fat_remove_entries(dir, &sinfo);
if (err2) {
fat_msg(sb, KERN_WARNING,
"failed to roll back new entry (%d)", err2);
goto out;
}
drop_nlink(dir);
goto out_free;
}
brelse(sinfo.bh);
set_nlink(inode, 2);
fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */

View File

@ -771,11 +771,17 @@ static int vfat_create(struct mnt_idmap *idmap, struct inode *dir,
inode_inc_iversion(dir);
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
brelse(sinfo.bh);
if (IS_ERR(inode)) {
int err2;
err = PTR_ERR(inode);
err2 = fat_remove_entries(dir, &sinfo);
if (err2)
fat_msg(sb, KERN_WARNING,
"failed to roll back new entry (%d)", err2);
goto out;
}
brelse(sinfo.bh);
inode_inc_iversion(inode);
d_instantiate(dentry, inode);
@ -870,12 +876,20 @@ static struct dentry *vfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
inc_nlink(dir);
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
brelse(sinfo.bh);
if (IS_ERR(inode)) {
int err2;
err = PTR_ERR(inode);
/* the directory was completed, just return a error */
goto out;
err2 = fat_remove_entries(dir, &sinfo);
if (err2) {
fat_msg(sb, KERN_WARNING,
"failed to roll back new entry (%d)", err2);
goto out;
}
drop_nlink(dir);
goto out_free;
}
brelse(sinfo.bh);
inode_inc_iversion(inode);
set_nlink(inode, 2);