git: 8b67c670a49b - main - msdosfs: fix directory corruption after rename operation

From: Stefan Eßer <se_at_FreeBSD.org>
Date: Sat, 17 Feb 2024 21:11:11 UTC
The branch main has been updated by se:

URL: https://cgit.FreeBSD.org/src/commit/?id=8b67c670a49b4efe7e1557121b5bbae682ea3bc7

commit 8b67c670a49b4efe7e1557121b5bbae682ea3bc7
Author:     Stefan Eßer <se@FreeBSD.org>
AuthorDate: 2024-02-17 21:04:49 +0000
Commit:     Stefan Eßer <se@FreeBSD.org>
CommitDate: 2024-02-17 21:04:49 +0000

    msdosfs: fix directory corruption after rename operation
    
    The is a bug in MSDOSFS that can be triggered when the target of a
    rename operation exists. It is caused by the lack of inodes in the
    FAT file system, which are substituted by the location of the DOS 8.3
    directory entry in the file system. This causes the "inode" of a file
    to change when its directory entry is moved to a different location.
    
    The rename operation wants to re-use the existing directory entry
    position of an existing target file name (POS1). But the code does
    instead locate the first position in the directory that provides
    sufficient free directory slots (POS2) to hold the target file name
    and fills it with the directory data.
    
    The rename operation continues and at the end writes directory data to
    the initially retrieved location (POS1) of the old target directory.
    This leads to 2 directory entries for the target file, but with
    inconsistent data in the directory and in the cached file system
    state.
    
    The location that should have been re-used (POS1) is marked as deleted
    in the directory, and new directory data has been written to a
    different location (POS2). But the VFS cache has the newly written
    data stored under the inode number that corresponds to the initially
    planned position (POS1).
    
    If then a new file is written, it can allocate the deleted directory
    entries (POS1) and when it queries the cache, it retrieves data that
    is valid for the target of the prior rename operation, leading to a
    corrupt directory entry (at POS1) being written (DOS file name of the
    earlier rename target combined with the Windows long file name of the
    newly written file).
    
    PR:             268005
    Reported by:    wbe@psr.com
    Approved by:    kib, mckusick
    Fixes:          2c9cbc2d45b94
    MFC after:      3 days
    Differential Revision:  https://reviews.freebsd.org/D43951
---
 sys/fs/msdosfs/msdosfs_vnops.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c
index 31d9b003a6fa..078ea5e52312 100644
--- a/sys/fs/msdosfs/msdosfs_vnops.c
+++ b/sys/fs/msdosfs/msdosfs_vnops.c
@@ -1180,8 +1180,10 @@ relock:
 	memcpy(oldname, fip->de_Name, 11);
 	memcpy(fip->de_Name, toname, 11);	/* update denode */
 	error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
-	if (error == EJUSTRETURN)
+	if (error == EJUSTRETURN) {
+		tdip->de_fndoffset = to_diroffset;
 		error = createde(fip, tdip, NULL, tcnp);
+	}
 	if (error != 0) {
 		memcpy(fip->de_Name, oldname, 11);
 		goto unlock;