Skip to content

Commit fc0edb7

Browse files
authored
Merge pull request #10860 from nextcloud/backport/10846/stable-34.0
[stable-34.0] fix(windows): Pass extended \\?\ paths to QFile for ordinary files in Filesystem::rename
2 parents 5600b49 + f6064c8 commit fc0edb7

3 files changed

Lines changed: 126 additions & 16 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1540"
4+
version = "1.7">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES"
8+
buildArchitectures = "Automatic">
9+
<BuildActionEntries>
10+
<BuildActionEntry
11+
buildForTesting = "YES"
12+
buildForRunning = "YES"
13+
buildForProfiling = "YES"
14+
buildForArchiving = "YES"
15+
buildForAnalyzing = "YES">
16+
<BuildableReference
17+
BuildableIdentifier = "primary"
18+
BlueprintIdentifier = "C2B573B01B1CD91E00303B36"
19+
BuildableName = "desktopclient.app"
20+
BlueprintName = "desktopclient"
21+
ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
22+
</BuildableReference>
23+
</BuildActionEntry>
24+
</BuildActionEntries>
25+
</BuildAction>
26+
<TestAction
27+
buildConfiguration = "Debug"
28+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
29+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
30+
shouldUseLaunchSchemeArgsEnv = "YES"
31+
shouldAutocreateTestPlan = "YES">
32+
</TestAction>
33+
<LaunchAction
34+
buildConfiguration = "Debug"
35+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
36+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
37+
launchStyle = "0"
38+
useCustomWorkingDirectory = "NO"
39+
ignoresPersistentStateOnLaunch = "NO"
40+
debugDocumentVersioning = "YES"
41+
debugServiceExtension = "internal"
42+
allowLocationSimulation = "YES">
43+
<BuildableProductRunnable
44+
runnableDebuggingMode = "0">
45+
<BuildableReference
46+
BuildableIdentifier = "primary"
47+
BlueprintIdentifier = "C2B573B01B1CD91E00303B36"
48+
BuildableName = "desktopclient.app"
49+
BlueprintName = "desktopclient"
50+
ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
51+
</BuildableReference>
52+
</BuildableProductRunnable>
53+
</LaunchAction>
54+
<ProfileAction
55+
buildConfiguration = "Release"
56+
shouldUseLaunchSchemeArgsEnv = "YES"
57+
savedToolIdentifier = ""
58+
useCustomWorkingDirectory = "NO"
59+
debugDocumentVersioning = "YES">
60+
<BuildableProductRunnable
61+
runnableDebuggingMode = "0">
62+
<BuildableReference
63+
BuildableIdentifier = "primary"
64+
BlueprintIdentifier = "C2B573B01B1CD91E00303B36"
65+
BuildableName = "desktopclient.app"
66+
BlueprintName = "desktopclient"
67+
ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
68+
</BuildableReference>
69+
</BuildableProductRunnable>
70+
</ProfileAction>
71+
<AnalyzeAction
72+
buildConfiguration = "Debug">
73+
</AnalyzeAction>
74+
<ArchiveAction
75+
buildConfiguration = "Release"
76+
revealArchiveInOrganizer = "YES">
77+
</ArchiveAction>
78+
</Scheme>

src/common/filesystembase.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -206,25 +206,24 @@ bool FileSystem::rename(const QString &originFileName,
206206
bool success = false;
207207
QString error;
208208
#ifdef Q_OS_WIN
209-
QString orig = longWinPath(originFileName);
210-
QString dest = longWinPath(destinationFileName);
211-
212-
if (isLnkFile(originFileName) || isLnkFile(destinationFileName)) {
213-
success = MoveFileEx((wchar_t *)orig.utf16(),
214-
(wchar_t *)dest.utf16(),
215-
MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
216-
if (!success) {
217-
error = Utility::formatWinError(GetLastError());
218-
}
219-
} else
220-
#endif
209+
// Use the extended paths directly so Win32 does not normalize trailing periods or spaces.
210+
const auto originPath = longWinPath(originFileName);
211+
const auto destinationPath = longWinPath(destinationFileName);
212+
success = MoveFileExW(reinterpret_cast<const wchar_t *>(originPath.utf16()),
213+
reinterpret_cast<const wchar_t *>(destinationPath.utf16()),
214+
MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
215+
if (!success) {
216+
error = Utility::formatWinError(GetLastError());
217+
}
218+
#else
221219
{
222-
QFile orig(originFileName);
223-
success = orig.rename(destinationFileName);
220+
QFile file(originFileName);
221+
success = file.rename(destinationFileName);
224222
if (!success) {
225-
error = orig.errorString();
223+
error = file.errorString();
226224
}
227225
}
226+
#endif
228227

229228
if (!success) {
230229
qCWarning(lcFileSystem) << "Error renaming file" << originFileName

test/testfilesystem.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
#include "libsync/filesystem.h"
1818

1919
#ifdef Q_OS_WIN
20-
#include <securitybaseapi.h>
2120
#include <aclapi.h>
2221
#include <sddl.h>
22+
#include <securitybaseapi.h>
2323
#endif
2424

2525
using namespace OCC;
@@ -161,6 +161,39 @@ private Q_SLOTS:
161161
}
162162

163163
#ifdef Q_OS_WIN
164+
// Regression for issue #10836: a trailing period must remain addressable during a rename.
165+
void testRenameFileWithTrailingPeriod()
166+
{
167+
QTemporaryDir tempDir;
168+
QVERIFY(tempDir.isValid());
169+
170+
const auto sourcePath = tempDir.filePath(u"trailing-period."_s);
171+
const auto destinationPath = tempDir.filePath(u"renamed-file"_s);
172+
const auto pathExists = [](const QString &path) {
173+
const auto extendedPath = FileSystem::longWinPath(path);
174+
return GetFileAttributesW(reinterpret_cast<const wchar_t *>(extendedPath.utf16())) != INVALID_FILE_ATTRIBUTES;
175+
};
176+
const auto sourcePathLong = FileSystem::longWinPath(sourcePath);
177+
178+
Utility::UniqueHandle fileHandle;
179+
fileHandle.reset(CreateFileW(reinterpret_cast<const wchar_t *>(sourcePathLong.utf16()),
180+
GENERIC_WRITE,
181+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
182+
nullptr,
183+
CREATE_NEW,
184+
FILE_ATTRIBUTE_NORMAL,
185+
nullptr));
186+
QVERIFY2(fileHandle.get() != INVALID_HANDLE_VALUE, qPrintable(Utility::formatWinError(GetLastError())));
187+
fileHandle.reset();
188+
189+
QVERIFY2(pathExists(sourcePath), qPrintable(u"Source file does not exist: "_s + sourcePath));
190+
191+
QString error;
192+
QVERIFY2(FileSystem::rename(sourcePath, destinationPath, &error), qPrintable(error));
193+
QVERIFY(!pathExists(sourcePath));
194+
QVERIFY(pathExists(destinationPath));
195+
}
196+
164197
void testAclWithManyDeniedAces()
165198
{
166199
// Regression from client versions < 4.0.2; see GH issue nextcloud/desktop#8860

0 commit comments

Comments
 (0)