Skip to content

Commit e9b2b30

Browse files
committed
[Windows] Replace uses of withCString with getFSR
This ensures that paths are normalized before being handed off to win32 functions.
1 parent 05cb315 commit e9b2b30

File tree

6 files changed

+146
-125
lines changed

6 files changed

+146
-125
lines changed

Foundation/FileHandle.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,9 @@ open class FileHandle : NSObject {
431431
}
432432

433433
internal convenience init?(path: String, flags: Int32, createMode: Int) {
434-
let fd = _CFOpenFileWithMode(path, flags, mode_t(createMode))
434+
let fsr = FileManager.default.fileSystemRepresentation(withPath: path)
435+
defer { fsr.deallocate() }
436+
let fd = _CFOpenFileWithMode(fsr, flags, mode_t(createMode))
435437
guard fd > 0 else { return nil }
436438
self.init(fileDescriptor: fd, closeOnDealloc: true)
437439
if _handle == INVALID_HANDLE_VALUE { return nil }
@@ -457,14 +459,7 @@ open class FileHandle : NSObject {
457459
#endif
458460

459461
internal convenience init?(fileSystemRepresentation: UnsafePointer<Int8>, flags: Int32, createMode: Int) {
460-
#if os(Windows)
461-
var fd: Int32 = -1
462-
if let path = String(cString: fileSystemRepresentation).cString(using: .utf16) {
463-
fd = _CFOpenFileWithMode(path, flags, mode_t(createMode))
464-
}
465-
#else
466462
let fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode))
467-
#endif
468463
guard fd > 0 else { return nil }
469464
self.init(fileDescriptor: fd, closeOnDealloc: true)
470465
}

Foundation/FileManager+Win32.swift

Lines changed: 57 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
import CoreFoundation
1111

1212
#if os(Windows)
13-
internal func joinPath(prefix: String, suffix: String) -> String {
13+
14+
internal func joinPath(prefix: String, suffix: String) throws -> String {
1415
var pszPath: PWSTR?
15-
_ = prefix.withCString(encodedAs: UTF16.self) { prefix in
16-
_ = suffix.withCString(encodedAs: UTF16.self) { suffix in
17-
PathAllocCombine(prefix, suffix, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &pszPath)
18-
}
16+
guard S_OK == (try FileManager.default._fileSystemRepresentation(withPath: prefix, andPath: suffix) {
17+
PathAllocCombine($0, $1, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &pszPath)
18+
}) else {
19+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [prefix])
1920
}
2021

2122
let path: String = String(decodingCString: pszPath!, as: UTF16.self)
@@ -177,11 +178,8 @@ extension FileManager {
177178
UnsafeMutablePointer<SECURITY_ATTRIBUTES>(&saAttributes)
178179

179180

180-
try path.withCString(encodedAs: UTF16.self) {
181-
if !CreateDirectoryW($0, psaAttributes) {
182-
// FIXME(compnerd) pass along path
183-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
184-
}
181+
guard try _fileSystemRepresentation(withPath: path, { CreateDirectoryW($0, psaAttributes) }) else {
182+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
185183
}
186184
if let attr = attributes {
187185
try self.setAttributes(attr, ofItemAtPath: path)
@@ -192,7 +190,7 @@ extension FileManager {
192190
guard path != "" else {
193191
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInvalidFileName.rawValue, userInfo: [NSFilePathErrorKey : NSString(path)])
194192
}
195-
try (path + "\\*").withCString(encodedAs: UTF16.self) {
193+
try _fileSystemRepresentation(withPath: path + "\\*") {
196194
var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW()
197195

198196
let hDirectory: HANDLE = FindFirstFileW($0, &ffd)
@@ -221,17 +219,17 @@ extension FileManager {
221219
contents.append(entryName)
222220
if entryType & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
223221
&& entryType & FILE_ATTRIBUTE_REPARSE_POINT != FILE_ATTRIBUTE_REPARSE_POINT {
224-
let subPath: String = joinPath(prefix: path, suffix: entryName)
222+
let subPath: String = try joinPath(prefix: path, suffix: entryName)
225223
let entries = try subpathsOfDirectory(atPath: subPath)
226-
contents.append(contentsOf: entries.map { joinPath(prefix: entryName, suffix: $0).standardizingPath })
224+
contents.append(contentsOf: try entries.map { try joinPath(prefix: entryName, suffix: $0).standardizingPath })
227225
}
228226
})
229227
return contents
230228
}
231229

232230
internal func windowsFileAttributes(atPath path: String) throws -> WIN32_FILE_ATTRIBUTE_DATA {
233231
var faAttributes: WIN32_FILE_ATTRIBUTE_DATA = WIN32_FILE_ATTRIBUTE_DATA()
234-
return try path.withCString(encodedAs: UTF16.self) {
232+
return try _fileSystemRepresentation(withPath: path) {
235233
if !GetFileAttributesExW($0, GetFileExInfoStandard, &faAttributes) {
236234
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
237235
}
@@ -246,7 +244,7 @@ extension FileManager {
246244
internal func _attributesOfFileSystem(forPath path: String) throws -> [FileAttributeKey : Any] {
247245
var result: [FileAttributeKey:Any] = [:]
248246

249-
try path.withCString(encodedAs: UTF16.self) {
247+
try _fileSystemRepresentation(withPath: path) {
250248
let dwLength: DWORD = GetFullPathNameW($0, 0, nil, nil)
251249
guard dwLength != 0 else {
252250
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
@@ -303,12 +301,8 @@ extension FileManager {
303301
}
304302
}
305303

306-
try path.withCString(encodedAs: UTF16.self) { name in
307-
try destPath.withCString(encodedAs: UTF16.self) { dest in
308-
guard CreateSymbolicLinkW(name, dest, dwFlags) != 0 else {
309-
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
310-
}
311-
}
304+
guard try _fileSystemRepresentation(withPath: path, andPath: destPath, { CreateSymbolicLinkW($0, $1, dwFlags) != 0 }) else {
305+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
312306
}
313307
}
314308

@@ -318,10 +312,14 @@ extension FileManager {
318312
throw _NSErrorWithWindowsError(DWORD(ERROR_BAD_ARGUMENTS), reading: false)
319313
}
320314

321-
let handle = path.withCString(encodedAs: UTF16.self) { symlink in
322-
CreateFileW(symlink, GENERIC_READ, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
323-
nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
324-
nil)
315+
let handle = try _fileSystemRepresentation(withPath: path) {
316+
CreateFileW($0,
317+
GENERIC_READ,
318+
DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
319+
nil,
320+
DWORD(OPEN_EXISTING),
321+
DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
322+
nil)
325323
}
326324

327325
guard handle != INVALID_HANDLE_VALUE else {
@@ -400,16 +398,15 @@ extension FileManager {
400398
}
401399

402400
internal func _canonicalizedPath(toFileAtPath path: String) throws -> String {
403-
var hFile: HANDLE = INVALID_HANDLE_VALUE
404-
path.withCString(encodedAs: UTF16.self) { link in
405-
// BACKUP_SEMANTICS are (confusingly) required in order to receive a
406-
// handle to a directory
407-
hFile = CreateFileW(link, 0, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
408-
nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS),
409-
nil)
401+
var hFile: HANDLE = try _fileSystemRepresentation(withPath: path) {
402+
// BACKUP_SEMANTICS are (confusingly) required in order to receive a
403+
// handle to a directory
404+
CreateFileW($0, 0, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
405+
nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS),
406+
nil)
410407
}
411408
guard hFile != INVALID_HANDLE_VALUE else {
412-
return try path.withCString(encodedAs: UTF16.self) {
409+
return try _fileSystemRepresentation(withPath: path) {
413410
var dwLength = GetFullPathNameW($0, 0, nil, nil)
414411
var szPath = Array<WCHAR>(repeating: 0, count: Int(dwLength + 1))
415412
dwLength = GetFullPathNameW($0, DWORD(szPath.count), &szPath, nil)
@@ -429,12 +426,8 @@ extension FileManager {
429426
}
430427

431428
internal func _copyRegularFile(atPath srcPath: String, toPath dstPath: String, variant: String = "Copy") throws {
432-
try srcPath.withCString(encodedAs: UTF16.self) { src in
433-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
434-
if !CopyFileW(src, dst, false) {
435-
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [srcPath, dstPath])
436-
}
437-
}
429+
guard try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, { CopyFileW($0, $1, false) }) else {
430+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [srcPath, dstPath])
438431
}
439432
}
440433

@@ -463,8 +456,8 @@ extension FileManager {
463456
}
464457

465458
while let item = enumerator.nextObject() as? String {
466-
let src = joinPath(prefix: srcPath, suffix: item)
467-
let dst = joinPath(prefix: dstPath, suffix: item)
459+
let src = try joinPath(prefix: srcPath, suffix: item)
460+
let dst = try joinPath(prefix: dstPath, suffix: item)
468461

469462
let faAttributes = try windowsFileAttributes(atPath: src)
470463
fileType = FileAttributeType(attributes: faAttributes, atPath: srcPath)
@@ -488,12 +481,10 @@ extension FileManager {
488481
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteFileExists.rawValue, userInfo: [NSFilePathErrorKey : NSString(dstPath)])
489482
}
490483

491-
try srcPath.withCString(encodedAs: UTF16.self) { src in
492-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
493-
if !MoveFileExW(src, dst, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
494-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
495-
}
496-
}
484+
guard try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, {
485+
MoveFileExW($0, $1, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH))
486+
}) else {
487+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
497488
}
498489
}
499490

@@ -506,12 +497,8 @@ extension FileManager {
506497
do {
507498
switch fileType {
508499
case .typeRegular:
509-
try srcPath.withCString(encodedAs: UTF16.self) { src in
510-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
511-
if !CreateHardLinkW(dst, src, nil) {
512-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
513-
}
514-
}
500+
guard try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, { CreateHardLinkW($1, $0, nil) }) else {
501+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
515502
}
516503
case .typeSymbolicLink:
517504
try _copySymlink(atPath: srcPath, toPath: dstPath)
@@ -531,50 +518,37 @@ extension FileManager {
531518
return
532519
}
533520

534-
guard path != "" else {
535-
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInvalidFileName.rawValue, userInfo: [NSFilePathErrorKey : NSString(path)])
536-
}
537-
538-
let url = URL(fileURLWithPath: path)
539-
var fsrBuf: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(MAX_PATH))
540-
_CFURLGetWideFileSystemRepresentation(url._cfObject, false, &fsrBuf, Int(MAX_PATH))
541-
let length = wcsnlen_s(&fsrBuf, fsrBuf.count)
542-
let fsrPath = String(utf16CodeUnits: &fsrBuf, count: length)
543-
544-
let faAttributes = try windowsFileAttributes(atPath: fsrPath)
545-
521+
let faAttributes = try windowsFileAttributes(atPath: path)
546522
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY {
547523
let readableAttributes = faAttributes.dwFileAttributes & DWORD(bitPattern: ~FILE_ATTRIBUTE_READONLY)
548-
guard fsrPath.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, readableAttributes) }) else {
524+
guard try _fileSystemRepresentation(withPath: path, { SetFileAttributesW($0, readableAttributes) }) else {
549525
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
550526
}
551527
}
552528

553529
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == 0 {
554-
if !fsrPath.withCString(encodedAs: UTF16.self, DeleteFileW) {
530+
guard try _fileSystemRepresentation(withPath: path, DeleteFileW) else {
555531
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
556532
}
557533
return
558534
}
559-
var dirStack = [fsrPath]
535+
var dirStack = [path]
560536
var itemPath = ""
561537
while let currentDir = dirStack.popLast() {
562538
do {
563539
itemPath = currentDir
564540
guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else {
565541
continue
566542
}
567-
guard !itemPath.withCString(encodedAs: UTF16.self, RemoveDirectoryW) else {
543+
guard !(try _fileSystemRepresentation(withPath: itemPath, RemoveDirectoryW)) else {
568544
continue
569545
}
570546
guard GetLastError() == ERROR_DIR_NOT_EMPTY else {
571547
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [itemPath])
572548
}
573549
dirStack.append(itemPath)
574550
var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW()
575-
let h: HANDLE = (itemPath + "\\*").withCString(encodedAs: UTF16.self, {
576-
FindFirstFileW($0, &ffd)
577-
})
551+
let h: HANDLE = try _fileSystemRepresentation(withPath: itemPath + "\\*", { FindFirstFileW($0, &ffd) })
578552
guard h != INVALID_HANDLE_VALUE else {
579553
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [itemPath])
580554
}
@@ -589,7 +563,7 @@ extension FileManager {
589563

590564
if ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY {
591565
let readableAttributes = ffd.dwFileAttributes & DWORD(bitPattern: ~FILE_ATTRIBUTE_READONLY)
592-
guard itemPath.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, readableAttributes) }) else {
566+
guard try _fileSystemRepresentation(withPath: itemPath, { SetFileAttributesW($0, readableAttributes) }) else {
593567
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [file])
594568
}
595569
}
@@ -602,7 +576,7 @@ extension FileManager {
602576
guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else {
603577
continue
604578
}
605-
if !itemPath.withCString(encodedAs: UTF16.self, DeleteFileW) {
579+
guard try _fileSystemRepresentation(withPath: itemPath, DeleteFileW) else {
606580
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [file])
607581
}
608582
}
@@ -625,7 +599,7 @@ extension FileManager {
625599

626600
@discardableResult
627601
internal func _changeCurrentDirectoryPath(_ path: String) -> Bool {
628-
return path.withCString(encodedAs: UTF16.self) { SetCurrentDirectoryW($0) }
602+
return (try? _fileSystemRepresentation(withPath: path, SetCurrentDirectoryW)) ?? false
629603
}
630604

631605
internal func _fileExists(atPath path: String, isDirectory: UnsafeMutablePointer<ObjCBool>?) -> Bool {
@@ -636,7 +610,7 @@ extension FileManager {
636610
let contents = try destinationOfSymbolicLink(atPath: path)
637611
let resolvedPath = contents.isAbsolutePath
638612
? contents
639-
: joinPath(prefix: path.deletingLastPathComponent, suffix: contents)
613+
: try joinPath(prefix: path.deletingLastPathComponent, suffix: contents)
640614
try faAttributes = windowsFileAttributes(atPath: resolvedPath)
641615
} catch {
642616
return false
@@ -745,7 +719,7 @@ extension FileManager {
745719
}
746720

747721
internal func _contentsEqual(atPath path1: String, andPath path2: String) -> Bool {
748-
guard let path1Handle = path1.withCString(encodedAs: UTF16.self, {
722+
guard let path1Handle = try? _fileSystemRepresentation(withPath: path1, {
749723
CreateFileW($0, GENERIC_READ, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nil,
750724
DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS), nil)
751725
}), path1Handle != INVALID_HANDLE_VALUE else {
@@ -754,7 +728,7 @@ extension FileManager {
754728

755729
defer { CloseHandle(path1Handle) }
756730

757-
guard let path2Handle = path2.withCString(encodedAs: UTF16.self, {
731+
guard let path2Handle = try? _fileSystemRepresentation(withPath: path2, {
758732
CreateFileW($0, GENERIC_READ, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nil,
759733
DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS), nil)
760734
}), path2Handle != INVALID_HANDLE_VALUE else {
@@ -828,12 +802,7 @@ extension FileManager {
828802
}
829803

830804
internal func _appendSymlinkDestination(_ dest: String, toPath: String) -> String {
831-
var isAbsolutePath: Bool = false
832-
dest.withCString(encodedAs: UTF16.self) {
833-
isAbsolutePath = !PathIsRelativeW($0)
834-
}
835-
836-
if isAbsolutePath {
805+
if (try? !_fileSystemRepresentation(withPath: dest, PathIsRelativeW)) ?? false {
837806
return dest
838807
}
839808
let temp = toPath._bridgeToObjectiveC().deletingLastPathComponent
@@ -909,11 +878,11 @@ extension FileManager {
909878
&& attrs.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) == 0
910879
if isDir && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) {
911880
var ffd = WIN32_FIND_DATAW()
912-
let dirPath = joinPath(prefix: _lastReturned.path, suffix: "*")
913-
let handle = dirPath.withCString(encodedAs: UTF16.self) {
914-
FindFirstFileW($0, &ffd)
881+
guard let dirPath = try? joinPath(prefix: _lastReturned.path, suffix: "*"),
882+
let handle = try? FileManager.default._fileSystemRepresentation(withPath: dirPath, { FindFirstFileW($0, &ffd) }),
883+
handle != INVALID_HANDLE_VALUE else {
884+
return firstValidItem()
915885
}
916-
guard handle != INVALID_HANDLE_VALUE else { return firstValidItem() }
917886
defer { FindClose(handle) }
918887

919888
repeat {

0 commit comments

Comments
 (0)