Skip to content

Commit d4a2ea5

Browse files
committed
Strip any UNC long path prefix from _wgetcwd
- This keeps any UNC long file or device prefix (\\?\) out of URL that were relative and then resolved to absolute urls.
1 parent 94ef6ab commit d4a2ea5

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed

Sources/CoreFoundation/CFPlatform.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <shellapi.h>
3636
#include <shlobj.h>
3737
#include <shlwapi.h>
38+
#include <pathcch.h>
3839
#include <WinIoCtl.h>
3940
#include <direct.h>
4041
#include <process.h>
@@ -1140,9 +1141,22 @@ CF_EXPORT char *_NS_getcwd(char *dstbuf, size_t size) {
11401141
if (!buf) {
11411142
return NULL;
11421143
}
1144+
1145+
// Strip UNC long path prefixe (\\?\) from the wide character buffer using PathCchStripPrefix
1146+
wchar_t *pathToConvert = buf;
1147+
size_t pathLen = wcslen(buf);
1148+
1149+
// Use PathCchStripPrefix to remove UNC long path prefixes
1150+
HRESULT hr = PathCchStripPrefix(buf, pathLen + 1);
1151+
if (SUCCEEDED(hr)) {
1152+
pathToConvert = buf;
1153+
} else {
1154+
// If PathCchStripPrefix fails, fall back to the original buffer
1155+
pathToConvert = buf;
1156+
}
11431157

11441158
// Convert result to UTF8
1145-
copyToNarrowFileSystemRepresentation(buf, (CFIndex)size, dstbuf);
1159+
copyToNarrowFileSystemRepresentation(pathToConvert, (CFIndex)size, dstbuf);
11461160
free(buf);
11471161
return dstbuf;
11481162
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
//
9+
10+
#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
11+
#if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC
12+
@testable import SwiftFoundation
13+
#else
14+
@testable import Foundation
15+
#endif
16+
#endif
17+
18+
import XCTest
19+
20+
#if os(Windows)
21+
// Import Windows C functions
22+
import WinSDK
23+
24+
// Declare _NS_getcwd function for testing
25+
@_silgen_name("_NS_getcwd")
26+
func _NS_getcwd(_ buffer: UnsafeMutablePointer<CChar>, _ size: Int) -> UnsafeMutablePointer<CChar>?
27+
#endif
28+
29+
class TestCFPlatformGetcwd : XCTestCase {
30+
31+
#if os(Windows)
32+
func test_NS_getcwd_UNC_prefix_stripping() {
33+
// Test that _NS_getcwd properly strips UNC long path prefixes using PathCchStripPrefix
34+
35+
// Create a temporary directory to work with
36+
let fm = FileManager.default
37+
let tempDir = fm.temporaryDirectory.appendingPathComponent("test_getcwd_\(UUID().uuidString)")
38+
39+
do {
40+
try fm.createDirectory(at: tempDir, withIntermediateDirectories: true)
41+
defer { try? fm.removeItem(at: tempDir) }
42+
43+
// Get original directory for restoration
44+
var originalBuffer = [CChar](repeating: 0, count: Int(MAX_PATH))
45+
guard _NS_getcwd(&originalBuffer, originalBuffer.count) != nil else {
46+
XCTFail("Failed to get original directory")
47+
return
48+
}
49+
let originalDir = String(cString: originalBuffer)
50+
51+
defer {
52+
// Restore original directory
53+
originalDir.withCString { _chdir($0) }
54+
}
55+
56+
// Test with UNC long path prefix \\?\
57+
let uncLongPathPrefix = "\\\\?\\" + tempDir.path
58+
let uncLongPathCString = uncLongPathPrefix.cString(using: .utf8)!
59+
let uncChdirResult = uncLongPathCString.withUnsafeBufferPointer { buffer in
60+
return _chdir(buffer.baseAddress!)
61+
}
62+
XCTAssertEqual(uncChdirResult, 0, "Failed to change directory using UNC long path prefix")
63+
64+
// Test _NS_getcwd directly after changing to UNC prefixed path
65+
var buffer = [CChar](repeating: 0, count: Int(MAX_PATH))
66+
guard let result = _NS_getcwd(&buffer, buffer.count) else {
67+
XCTFail("_NS_getcwd returned null")
68+
return
69+
}
70+
71+
let currentDir = String(cString: result)
72+
73+
// Verify that the path doesn't contain UNC prefixes (this is the key test!)
74+
XCTAssertFalse(currentDir.hasPrefix("\\\\?\\"), "Current directory path should not contain \\\\?\\ UNC prefix after stripping")
75+
76+
// Verify that we can still access the directory (it's a valid path)
77+
XCTAssertTrue(fm.fileExists(atPath: currentDir), "Current directory path should be valid and accessible")
78+
79+
// Verify the path ends with our test directory name
80+
XCTAssertTrue(currentDir.hasSuffix(tempDir.lastPathComponent), "Current directory should end with our test directory name")
81+
82+
// Test with a deeper nested directory using UNC prefix to ensure stripping works with longer paths
83+
let deepDir = tempDir.appendingPathComponent("level1").appendingPathComponent("level2").appendingPathComponent("level3")
84+
try fm.createDirectory(at: deepDir, withIntermediateDirectories: true)
85+
86+
let deepUncPath = "\\\\?\\" + deepDir.path
87+
let deepUncCString = deepUncPath.cString(using: .utf8)!
88+
let deepChdirResult = deepUncCString.withUnsafeBufferPointer { buffer in
89+
return _chdir(buffer.baseAddress!)
90+
}
91+
XCTAssertEqual(deepChdirResult, 0, "Failed to change to deep directory with UNC prefix")
92+
93+
// Test _NS_getcwd with deep UNC prefixed path
94+
var deepBuffer = [CChar](repeating: 0, count: Int(MAX_PATH))
95+
guard let deepResult = _NS_getcwd(&deepBuffer, deepBuffer.count) else {
96+
XCTFail("_NS_getcwd returned null for deep UNC path")
97+
return
98+
}
99+
100+
let deepCurrentDir = String(cString: deepResult)
101+
102+
// Verify UNC prefixes are stripped from deeper paths too
103+
XCTAssertFalse(deepCurrentDir.hasPrefix("\\\\?\\"), "Deep directory path should not contain \\\\?\\ UNC prefix after stripping")
104+
XCTAssertTrue(fm.fileExists(atPath: deepCurrentDir), "Deep directory path should be valid and accessible")
105+
XCTAssertTrue(deepCurrentDir.hasSuffix("level3"), "Deep directory should end with level3")
106+
107+
} catch {
108+
XCTFail("Failed to set up test environment: \(error)")
109+
}
110+
}
111+
112+
func test_NS_getcwd_small_buffer() {
113+
// Test that _NS_getcwd handles small buffer correctly
114+
var smallBuffer = [CChar](repeating: 0, count: 1)
115+
let result = _NS_getcwd(&smallBuffer, smallBuffer.count)
116+
// This should either return null or handle the small buffer gracefully
117+
// The exact behavior depends on the implementation, but it shouldn't crash
118+
XCTAssertTrue(result == nil || result != nil, "Function should not crash with small buffer")
119+
}
120+
#endif
121+
}

0 commit comments

Comments
 (0)