Bug 16261 - Rename with ReplaceIfExists to a target differing only in case creates a duplicate instead of replacing (regression in bb130f32)
Summary: Rename with ReplaceIfExists to a target differing only in case creates a dupl...
Status: ASSIGNED
Alias: None
Product: Samba 4.1 and newer
Classification: Unclassified
Component: File services (show other bugs)
Version: 4.24.3
Hardware: All All
: P5 normal (vote)
Target Milestone: ---
Assignee: Samba release manager
QA Contact: Samba QA Contact
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-09-24 16:17 UTC by Aurélien Merle
Modified: 2026-10-07 15:20 UTC (History)
2 users (show)

See Also:


Attachments
patch from master for v4-25-test (5.73 KB, patch)
2026-10-05 09:07 UTC, Anoop C S
vl: review+
Details
patch from master for v4-24-test (5.73 KB, patch)
2026-10-05 09:08 UTC, Anoop C S
vl: review+
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Aurélien Merle 2026-09-24 16:17:58 UTC
--- Description ---

An SMB2 SETINFO / FileRenameInformation with ReplaceIfExists = 1, whose
target exists on disk only under a different case, returns STATUS_SUCCESS
but creates a second directory entry instead of replacing the existing
file. Both files remain on disk.

Windows replaces the existing file in this situation. MS-FSA 2.1.5.15.11
specifies that the target lookup is case-insensitive.

--- Affected versions ---

Good: f0a4e897daa3a8002905fb5d161372d3b27cd42f (bb130f32^)
Bad:  bb130f32c9fdbb02a4e99d23953d090c3e992d0a
      "smbd: Rewrite rename_internals_fsp()", 2025-09-24

Both built from the same tree with identical configure options, tested on
the same host, same filesystem, same smb.conf and same client. 4.24.3 is
affected. The code path is unchanged in v4-25-stable and master, so those
are very likely affected as well (verified by reading, not by testing).

--- Reproducer ---

Server: Ubuntu 22.04.5, ext4, upstream build, prefix /usr/local/samba
configure --enable-debug --without-ad-dc --bundled-libraries=ALL

smb.conf:

[global]
        workgroup = WORKGROUP
        security = user
        server min protocol = SMB2_02

[reprocase]
        path = /srv/reprocase
        read only = No

No VFS objects, no AD membership, no explicit "case sensitive".

Client: Windows 11 21H2, calling MoveFileEx() with
MOVEFILE_REPLACE_EXISTING:

  create  dzdz.lnk        (content "old")
  create  {tmp}.tmp       (content "new")
  MoveFileEx("{tmp}.tmp", "DZDZ.lnk", MOVEFILE_REPLACE_EXISTING)

Result on bb130f32^ -- correct:

  806385 -rwx--r-- 1 testsmb testsmb 5 dzdz.lnk

Result on bb130f32 and on 4.24.3 -- two entries, call returned success:

  806386 -rwx--r-- 1 testsmb testsmb 5 dzdz.lnk
  806387 -rwx--r-- 1 testsmb testsmb 5 DZDZ.lnk

--- Analysis ---

bb130f32 introduces a divergence between the name used for the existence
check and the name passed to the kernel.

smb_fname_dst comes from filename_convert_dirfsp(), i.e. the
case-insensitively resolved name. VALID_STAT(smb_fname_dst->st) is
therefore true and, with replace_if_exists set, the rename is allowed to
proceed.

smb_fname_dst_rel is built from get_original_lcomp(), i.e. the literal
name as sent by the client, and that is what reaches SMB_VFS_RENAMEAT()
(source3/smbd/smb2_reply.c, line 1543 in master). Since that literal name
does not exist on disk, the kernel creates a new entry and the original
file is left untouched.

The existing guard does not cover this case: SET_STAT_INVALID() is only
reached when source and destination differ merely in case, i.e. a pure
case change of one file. Here the source is an unrelated .tmp name, so
that branch is never taken.

--- Impact ---

Observed in production across several unrelated customers after upgrading
a fleet of Samba file servers from 4.18 to 4.24.3. Roaming profiles fail
to load and users fall back to temporary profiles: duplicate .lnk entries
differing only in case accumulate under
AppData\Roaming\Microsoft\Windows\Recent, and the Windows User Profile
Service cannot reconcile them when copying to NTFS at logon
(events 1509 then 1511, access denied).

No configuration workaround was found: "case sensitive",
"smb3 unix extensions" and "smb3 directory leases" have no effect.

The bug requires a client that writes via a temporary file and then
renames over a target already present under a different case, which may
explain why it has gone unreported.
Comment 1 Volker Lendecke 2026-09-28 21:43:58 UTC
Can you try the patches from https://gitlab.com/samba-team/samba/-/merge_requests/4730?
Comment 2 Samba QA Contact 2026-09-29 19:35:03 UTC
This bug was referenced in samba master:

a798b16fc76b968d8af64409e6365791bbd62419
15f64c46c935b428d1827a170ee4082694464c19
Comment 3 Aurélien Merle 2026-09-29 20:25:39 UTC
Confirmed fixed. Tested on Ubuntu 22.04.5 / ext4, upstream build,
--enable-debug --without-ad-dc --bundled-libraries=ALL, plain share
(no VFS objects, no AD), Windows client calling MoveFileEx().

Baseline, master at 972663c5772 (4.26.0pre1), without the patch:

  case change only, MoveFileEx("dzdz.lnk", "DZDZ.lnk", REPLACE_EXISTING)
    -> OK: single file DZDZ.lnk, content "old"

  replace, MoveFileEx("{tmp}.tmp", "DZDZ.lnk", REPLACE_EXISTING)
    -> BUG: returned TRUE, two entries
       917512 dzdz.lnk = "old"
       917513 DZDZ.lnk = "new"

  no replace flag, MoveFileEx("{tmp}.tmp", "DZDZ.lnk", 0)
    -> OK: returned FALSE, error 183, both files untouched

Same tree with 39d11db6b26 and 47e97bc713f applied:

  case change only   -> unchanged, single file DZDZ.lnk, content "old"

  replace            -> FIXED: returned TRUE, single entry
                        917513 dzdz.lnk = "new"
                        The surviving entry keeps the on-disk
                        capitalization, and the content is replaced.

  no replace flag    -> unchanged, returned FALSE, error 183

No regression observed on the pure case-change path or on the
name-collision path.

Thanks for the very quick turnaround!
Comment 4 Aurélien Merle 2026-09-29 20:30:51 UTC
One question, if I may: would it be possible to consider a backport to v4-24-stable and v4-25-stable?

We run 4.24.3 on production file servers, where this affects roaming profile loading for end users. I realise that is our problem to manage and not a reason to rush anything -- I am only mentioning it in case it helps judge how far the issue reaches beyond our setup.

If it would be useful, I still have the test environment in place and I am happy to test the patches on top of v4-24-stable or v4-25-stable and report back, with the same three scenarios as above.

Either way, thank you again for looking at this so quickly.
Comment 5 Anoop C S 2026-10-05 09:07:43 UTC
Created attachment 19270 [details]
patch from master for v4-25-test
Comment 6 Anoop C S 2026-10-05 09:08:13 UTC
Created attachment 19271 [details]
patch from master for v4-24-test
Comment 7 Anoop C S 2026-10-05 09:09:45 UTC
(In reply to Aurélien Merle from comment #4)
Once approved backports should be available with the next release from v4.25 and v4.24.
Comment 8 Samba QA Contact 2026-10-05 15:54:13 UTC
This bug was referenced in samba v4-24-test:

132c18f8925bbe0efbae538c536436e098c30719
31e9fe26a360a41e28c7f448de84c5f81d6de919
Comment 9 Aurélien Merle 2026-10-07 15:20:52 UTC
Thanks for preparing the backports.

I tested attachment 19271 [details] (v4-24-test). It applies cleanly with git am on top of tag samba-4.24.7, which is currently the head of v4-24-stable, so no adjustment was needed.

Tested on Ubuntu 22.04.5 / ext4, built with our production configure options, on a domain-joined member server with real shares and ACLs. Windows client calling MoveFileEx(), three scenarios:

  1. case change only, MoveFileEx("dzdz.lnk", "DZDZ.lnk", REPLACE_EXISTING)
     -> single file DZDZ.lnk, content "old" -- unchanged, no regression

  2. replace, MoveFileEx("{tmp}.tmp", "DZDZ.lnk", REPLACE_EXISTING)
     -> FIXED: single entry dzdz.lnk, content "new"
        The surviving entry keeps the on-disk capitalization and the
        content is replaced. Before the patch this produced two entries.

  3. no replace flag, MoveFileEx("{tmp}.tmp", "DZDZ.lnk", 0)
     -> returned FALSE, error 183, both files untouched -- unchanged

So: fix confirmed, and no regression on the pure case-change path or on the name-collision path.

I have not tested attachment 19270 [details] (v4-25-test), as we do not run 4.25 -- happy to do so if that would help.