All of lore.kernel.org
 help / color / mirror / Atom feed
From: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
To: Cihan Karadag <cihan.cihan@gmail.com>
Cc: linux-kernel@vger.kernel.org,  linux-fsdevel@vger.kernel.org,
	 Shuah Khan <shuah@kernel.org>
Subject: Re: [PATCH] fat: preserve reserved lcase bits across rename
Date: Sun, 26 Jul 2026 17:31:08 +0900	[thread overview]
Message-ID: <87v7a2gnjn.fsf@mail.parknet.co.jp> (raw)
In-Reply-To: <20260723010227.88190-1-cihan.cihan@gmail.com>

Cihan Karadag <cihan.cihan@gmail.com> writes:

> The lcase field of struct msdos_dir_entry
> (include/uapi/linux/msdos_fs.h) has only two bits the kernel
> interprets, CASE_LOWER_BASE and CASE_LOWER_EXT; the rest are reserved
> and other implementations may use them to carry their own metadata
> (e.g. the Windows EFS "encrypted file" bit pattern).
>
> vfat_rename() currently loses those reserved bits in both of its
> paths: when the destination name doesn't already exist, a brand new
> directory entry is built from scratch with a freshly computed lcase
> byte, zeroing anything outside the two known bits; when the rename
> overwrites an existing destination, the destination's own (unrelated)
> reserved bits are left in place instead of the source file's.
>
> Add vfat_overwrite_lcase_reserved_bits() and call it once new_i_pos is
> established inside vfat_rename(), overwriting the surviving entry's
> reserved lcase bits with the original file's, so the metadata is
> carried over across the rename instead of being lost.
>
> Tested by booting the patched kernel under QEMU and replaying the bug
> report's own before/after FAT32 images: the renamed entry's lcase byte
> now matches the source's reserved bits instead of being zeroed.

A bit strange design. It is mixing different lifetime, one is for dirent
and one is for inode.

Well, so, windows normal FAT really reserve those bits too over rename?
And implementation side, vfat_overwrite_lcase_reserved_bits() doesn't
race with fat_write_inode()?

If it has to reserve the inode lifetime bits, I feel it should write via
fat_write_inode().

Thanks.

> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=215093
> Signed-off-by: Cihan Karadag <cihan.cihan@gmail.com>
> ---
>  fs/fat/namei_vfat.c | 43 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>
> diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
> index e909447873e36..d4677c1eaabc3 100644
> --- a/fs/fat/namei_vfat.c
> +++ b/fs/fat/namei_vfat.c
> @@ -931,6 +931,38 @@ static void vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)
>  		mark_inode_dirty(dir);
>  }
>  
> +/*
> + * Overwrite the reserved lcase bits (those not used by the
> + * kernel) of the entry at i_pos with those from the source
> + * lcase byte.
> + */
> +static int vfat_overwrite_lcase_reserved_bits(struct inode *dir, loff_t i_pos,
> +					      unsigned char src_lcase)
> +{
> +	/* lcase bits the kernel actually assigns meaning to */
> +	const unsigned char lcase_unreserved_bits = CASE_LOWER_BASE | CASE_LOWER_EXT;
> +	struct super_block *sb = dir->i_sb;
> +	struct buffer_head *bh;
> +	struct msdos_dir_entry *de;
> +	sector_t blocknr;
> +	int offset, err = 0;
> +
> +	fat_get_blknr_offset(MSDOS_SB(sb), i_pos, &blocknr, &offset);
> +	bh = sb_bread(sb, blocknr);
> +	if (!bh)
> +		return -EIO;
> +
> +	de = &((struct msdos_dir_entry *)bh->b_data)[offset];
> +	de->lcase = (de->lcase & lcase_unreserved_bits) |
> +		    (src_lcase & ~lcase_unreserved_bits);
> +
> +	mmb_mark_buffer_dirty(bh, &MSDOS_I(dir)->i_metadata_bhs);
> +	if (IS_DIRSYNC(dir))
> +		err = sync_dirty_buffer(bh);
> +	brelse(bh);
> +	return err;
> +}
> +
>  static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
>  		       struct inode *new_dir, struct dentry *new_dentry)
>  {
> @@ -974,6 +1006,17 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
>  			goto out;
>  		new_i_pos = sinfo.i_pos;
>  	}
> +
> +	/*
> +	 * The kernel only interprets some of the lcase bits; make
> +	 * sure the rest carry over from old_dentry's lcase instead
> +	 * of being silently clobbered.
> +	 */
> +	err = vfat_overwrite_lcase_reserved_bits(new_dir, new_i_pos,
> +						 old_sinfo.de->lcase);
> +	if (err)
> +		goto error_inode;
> +
>  	inode_inc_iversion(new_dir);
>  
>  	fat_detach(old_inode);

-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

      reply	other threads:[~2026-07-26  8:40 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-23  1:02 [PATCH] fat: preserve reserved lcase bits across rename Cihan Karadag
2026-07-26  8:31 ` OGAWA Hirofumi [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87v7a2gnjn.fsf@mail.parknet.co.jp \
    --to=hirofumi@mail.parknet.co.jp \
    --cc=cihan.cihan@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.