Windows: "Rename file" dialog fails with "Source file does not exist" - FileSystem::rename() computes longWinPath() but does not use it
- Dominant language
- C++
- Stars
- 3.9k
- Forks
- 1k
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 123
Description
### ⚠️ Before submitting, please verify the following: ⚠️
- [x] This is a **bug**, not a question or a configuration issue.
- [x] This issue is **not** already reported on Github (I have searched for it).
- [x] Nextcloud Server and Desktop Client are **up to date**. See [Server Maintenance and Release Schedule](https://github.com/nextcloud/server/wiki/Maintenance-and-Release-Schedule) and [Desktop Releases](https://nextcloud.com/install/#install-clients) for supported versions.
- [x] I agree to follow Nextcloud's [Code of Conduct](https://nextcloud.com/contribute/code-of-conduct/)
### Bug description
On Windows, the desktop client resolves local files through ordinary Win32 paths. Win32
strips trailing spaces and trailing periods from a path before it reaches the filesystem,
so a file whose name ends in either character cannot be addressed that way. NTFS stores
such names without complaint — only the path layer refuses to carry them.
Two failures follow from this.
**1. Trailing period — the offered remedy cannot work.**
The client reports the file properly: a named warning with a "Rename file" button. The
dialog then fails with *"Could not rename local file. Source file does not exist."* The
file does exist; the client is looking for it by a path Win32 has already trimmed.
```
[ warning nextcloud.sync.filesystem src\common\filesystembase.cpp:230 ]:
Error renaming file "D:/benth/_NcBug/trailing period." to "D:/benth/_NcBug/no trailing period"
failed: "Source file does not exist."
```
This matters because File Explorer cannot fix such a name either, for the same reason.
The button is the user's only obvious route out, and it is a dead end.
The cause is visible in `src/common/filesystembase.cpp` at v34.0.3. `FileSystem::rename()`
computes the long-path forms and then uses them only in the `.lnk` branch:
```cpp
209 QString orig = longWinPath(originFileName);
210 QString dest = longWinPath(destinationFileName);
211
212 if (isLnkFile(originFileName) || isLnkFile(destinationFileName)) {
213 success = MoveFileEx((wchar_t *)orig.utf16(), // uses them
214 (wchar_t *)dest.utf16(),
215 MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
...
219 } else
220 #endif
221 {
222 QFile orig(originFileName); // does not
223 success = orig.rename(destinationFileName);
```
`longWinPath()` is `pathtoUNC()` on Windows, i.e. the `\?\` form that turns off Win32
path parsing. For every file that is not a shortcut, lines 209-210 are dead and the raw,
trimmable strings are used instead. The same file calls `longWinPath()` correctly in nine
other places, so this reads as an overlooked call rather than a deliberate choice.
**2. Trailing space — the local file is renamed without the user being told.**
The client renames the file on disk (`ProcessDirectoryJob::maybeRenameForWindowsCompatibility`)
and uploads it under the trimmed name, which succeeds. What the GUI reports, however, is a
failure: a tray notification and a sync error reading *"Filename contains trailing spaces."*
for the original name — a name that no longer exists on disk by the time the message is
shown. Server-side Activities separately records "You created ``" as an
ordinary creation.
Nowhere does the interface state that a local file was renamed. The user is shown an error
for an operation that in fact succeeded, and a creation entry for a name they never chose;
nothing connects the two. Establishing what actually happened requires running the client
with `--logdebug` and reading the sync log, which is not a reasonable expectation of a user
whose file has just been modified.
I am aware of #7668, which specifies that the windows-compatible-filenames rename happens
without a user dialog. This report does not ask for that dialog back — only that the change
be recorded where the user can see it.
### Steps to reproduce
File Explorer cannot create these names, but an application can. Create them through the
raw path, in a folder that is a sync pair with virtual files disabled:
```powershell
$root = 'C:\path\to\syncfolder'
[System.IO.File]::WriteAllText("\?\$root\trailing space ", "x")
[System.IO.File]::WriteAllText("\?\$root\trailing period.", "x")
[System.IO.File]::WriteAllText("\?\$root\ leading space.txt", "x")
[System.IO.File]::WriteAllText("\?\$root\.leading period.txt", "x")
```
Let the client sync.
1. `trailing period.` → warning shown. Click "Rename file", enter any new name, click OK
→ *"Could not rename local file. Source file does not exist."*
2. `trailing space ` → renamed on disk to `trailing space` and uploaded successfully, while
the GUI reports *"Filename contains trailing spaces."* as a failure. No message anywhere
says the local file was renamed.
3. The two leading-character files are the control: both sync untouched.
### Expected behavior
- The "Rename file" dialog should be able to rename the file it is offered for.
- When the client renames a local file itself, it should say so — in the error/activity list
rather than only in the debug log — and the message shown should not describe a failure
when the file was in fact synced under a changed name.
### Which files are affected by this bug
`trailing period.` and `trailing space ` (also `Efterfoelgende punktum.`, `Efterfoelgende mellemrum `)
### Operating system
Windows
### Which version of the operating system you are running.
Windows 11 Pro 26200 (25H2), `LongPathsEnabled = 1`
### Installation method
Official Windows MSI
### Nextcloud Server version
33.0.9 (hosted)
### Nextcloud Desktop Client version
33.0.9 (hosted)
### Did this occur after an update or on a clean installation?
Clean desktop client installation
### Are you using the Nextcloud Server Encryption module?
No
### Are you using an external user-backend?
- [x] Default internal user-backend
- [ ] LDAP or Active Directory
- [ ] SSO - SAML
- [ ] Other
### Nextcloud Server logs
```shell
```
### Additional info
**Control test.** This is about position, not the character. `" leading space.txt"` and
`".leading period.txt"` both sync untouched and are reachable through a normal Win32 path:
```
PUT of ".../_NcBug/ leading space.txt" FINISHED WITH STATUS "OK" 201 "Created"
PUT of ".../_NcBug/.leading period.txt" FINISHED WITH STATUS "OK" 201 "Created"
```
Win32 trims only at the end of a name, so the client handles one end correctly and breaks
on the other, purely as a consequence of which path form it uses.
**Measured on the affected file:**
```powershell
[System.IO.File]::Exists('C:\path\trailing period.') # False
[System.IO.File]::Exists('\?\C:\path\trailing period.') # True
[System.IO.Path]::GetFullPath('C:\path\trailing period.') # -> 'C:\path\trailing period'
```
**Prior issues searched.** #4387, #4390, #3749, #6961, #2651 all cover trailing spaces and
are closed as completed; #6760 covered earlier defects in this same dialog and is closed;
#7668 is the design decision behind the automatic rename. #8108 (open) is a separate defect
in the same dialog — no debounce on the OK button. None of them covers the rename dialog
failing on a path Win32 has trimmed.
**Related.** The same dialog also builds its remote PROPFIND URLs without a path separator;
reported separately as #10837.
**Log.** Trimmed excerpt attached (client run with `--logdebug --logflush`).
[Nextcloud-issue-log-excerpt.log](https://github.com/user-attachments/files/32253337/Nextcloud-issue-log-excerpt.log)
Contributor guide
Assessment
This issue has not been assessed yet.