Re: renameat2() RENAME_NOREPLACE errors trying to change case of filename

From: NeilBrown

Date: Mon May 18 2026 - 07:15:23 EST


On Thu, 14 May 2026, Joshua Hudson wrote:
> Consider a file on a FAT filesystem (USB stick, EFI partition, etc.)
>
> If it becomes desirable or necessary to change the case of the file,
> the old way of calling rename() works, however calling renameat2(-1,
> "NAME", -1, "name", RENAME_NOREPLACE); fails with EEXIST. I have
> traced this within the kernel, the noreplace check is very simplistic.
>
> Logically the bug is as follows:
> 1. Get source dnode
> 2. Get directory of destination (and lock)
> 3. Check if destination exists
> 4. If it does, return -EEXIST
>
> A fix would look like this:
> 1. Get source dnode
> 2. Get directory of destination (and lock)
> 3. Check if destination exists and not the same dnode as #1
> 4. If it does, return -EEXIST
>
> I deliberately wrote dnode and not inode; we don't want to clobber a
> different link to the same file.
>
> I encountered this the first time in running a battery of unit tests
> for library code, and basically said "who cares"; even I really didn't
> at the time, just documented faulting kernel call and moved on.
>
> I encountered this a second time when correcting something on my EFI
> partition and got slightly annoyed.
>
> Related: Last I checked, mv -i has a TOCCOU bug in how it avoids
> clobbering existing files without prompting. Someday they may fix this
> bug (which would be done by calling renameat2(); when they do,
> observing this on FAT filesystems becomes trivial.
>
> This came up again trying to write some other documentation and I
> thought time to report this.
>
>

Thanks for the report.
I agree that using rename to adjust the case on a case-insensitive
filesystem does not "replace" the file, so in that sense
"RENAME_NOREPLACE" should allow it.
However RENAME_NOREPLACE is not documented as "does not replace an
existing file" but as "Don't overwrite newpath of the rename. Return an
error if newpath already exists." So we have to return an error in the
case you mention.

We could introduce a new flag such as RENAME_NOREPLACE2 which only gives
an error if newpath exists and is not exactly oldpath from the
perspective of the filesystem.

Individual filesystems would need to explicitly enable support for
RENAME_NOREPLACE2. It would be simplest to only enable it on
filesystems where it matters - i.e. case-insensitive filesystems.
Userspace tools could then try RENAME_NOREPLACE2 and if that fails with
-EINVAL, try again with RENAME_NOREPLACE.

I've Cc:ed this reply to linux-fsdevel which might make it visible to
more people who might have an interest in this.

NeilBrown