]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fat: Fix lost inode update in do_msdos_rename() with DIRSYNC
authorChristian Brauner <brauner@kernel.org>
Mon, 27 Jul 2026 15:15:40 +0000 (17:15 +0200)
committerChristian Brauner <brauner@kernel.org>
Mon, 27 Jul 2026 15:15:40 +0000 (17:15 +0200)
Commit e668e0668181 ("fat: Replace fat_sync_inode() with
sync_inode_metadata()") hoisted mark_inode_dirty() in front of the
IS_DIRSYNC conditional in all converted callers except for the main
rename path of do_msdos_rename(). There old_inode is generally still
clean when the target directory has DIRSYNC set and, unlike
fat_sync_inode(), sync_inode_metadata() does nothing for a clean inode.

Thus the directory entry at the new location is never updated with the
contents of old_inode: it stays the way msdos_add_entry() created it,
with start cluster 0 and size 0 (or, when the rename replaced an
existing target, it keeps describing the deleted target). Since
old_inode is also never marked dirty, later writeback doesn't update
the entry either and the stale directory entry ends up on disk even on
a clean unmount, so the renamed file loses its contents.

Mark old_inode dirty before calling sync_inode_metadata() like all the
other call sites do.

Fixes: e668e0668181 ("fat: Replace fat_sync_inode() with sync_inode_metadata()")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/fat/namei_msdos.c

index 91b8d2fc94072d7d898787456be804ce82ad1b94..94f9df06a784e35b0f04ecd45df80d3ec9e6dd62 100644 (file)
@@ -516,12 +516,12 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
                MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
        else
                MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+       mark_inode_dirty(old_inode);
        if (IS_DIRSYNC(new_dir)) {
                err = sync_inode_metadata(old_inode, 1);
                if (err)
                        goto error_inode;
-       } else
-               mark_inode_dirty(old_inode);
+       }
 
        if (update_dotdot) {
                fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);