Skip to content

Commit 75dda63

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 50d8710 commit 75dda63

File tree

6 files changed

+138
-114
lines changed

6 files changed

+138
-114
lines changed

Foundation/FileHandle.swift

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

429429
internal convenience init?(path: String, flags: Int32, createMode: Int) {
430-
let fd = _CFOpenFileWithMode(path, flags, mode_t(createMode))
430+
let fsr = FileManager.default.fileSystemRepresentation(withPath: path)
431+
defer { fsr.deallocate() }
432+
let fd = _CFOpenFileWithMode(fsr, flags, mode_t(createMode))
431433
guard fd > 0 else { return nil }
432434
self.init(fileDescriptor: fd, closeOnDealloc: true)
433435
if _handle == INVALID_HANDLE_VALUE { return nil }
@@ -453,14 +455,7 @@ open class FileHandle : NSObject {
453455
#endif
454456

455457
internal convenience init?(fileSystemRepresentation: UnsafePointer<Int8>, flags: Int32, createMode: Int) {
456-
#if os(Windows)
457-
var fd: Int32 = -1
458-
if let path = String(cString: fileSystemRepresentation).cString(using: .utf16) {
459-
fd = _CFOpenFileWithMode(path, flags, mode_t(createMode))
460-
}
461-
#else
462458
let fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode))
463-
#endif
464459
guard fd > 0 else { return nil }
465460
self.init(fileDescriptor: fd, closeOnDealloc: true)
466461
}

Foundation/FileManager+Win32.swift

Lines changed: 47 additions & 77 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)
@@ -220,17 +218,17 @@ extension FileManager {
220218
try _contentsOfDir(atPath: path, { (entryName, entryType) throws in
221219
contents.append(entryName)
222220
if entryType & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY {
223-
let subPath: String = joinPath(prefix: path, suffix: entryName)
221+
let subPath: String = try joinPath(prefix: path, suffix: entryName)
224222
let entries = try subpathsOfDirectory(atPath: subPath)
225-
contents.append(contentsOf: entries.map { joinPath(prefix: entryName, suffix: $0).standardizingPath })
223+
contents.append(contentsOf: try entries.map { try joinPath(prefix: entryName, suffix: $0).standardizingPath })
226224
}
227225
})
228226
return contents
229227
}
230228

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

248-
try path.withCString(encodedAs: UTF16.self) {
246+
try _fileSystemRepresentation(withPath: path) {
249247
let dwLength: DWORD = GetFullPathNameW($0, 0, nil, nil)
250248
guard dwLength != 0 else {
251249
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
@@ -291,18 +289,14 @@ extension FileManager {
291289
dwFlags |= DWORD(SYMBOLIC_LINK_FLAG_DIRECTORY)
292290
}
293291

294-
try path.withCString(encodedAs: UTF16.self) { name in
295-
try destPath.withCString(encodedAs: UTF16.self) { dest in
296-
guard CreateSymbolicLinkW(name, dest, dwFlags) != 0 else {
297-
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
298-
}
299-
}
292+
guard try _fileSystemRepresentation(withPath: path, andPath: destPath, { CreateSymbolicLinkW($0, $1, dwFlags) != 0 }) else {
293+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
300294
}
301295
}
302296

303297
internal func _destinationOfSymbolicLink(atPath path: String) throws -> String {
304-
let handle = path.withCString(encodedAs: UTF16.self) { symlink in
305-
CreateFileW(symlink,
298+
let handle = try _fileSystemRepresentation(withPath: path) {
299+
CreateFileW($0,
306300
GENERIC_READ,
307301
DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
308302
nil,
@@ -355,16 +349,15 @@ extension FileManager {
355349
}
356350

357351
internal func _canonicalizedPath(toFileAtPath path: String) throws -> String {
358-
var hFile: HANDLE = INVALID_HANDLE_VALUE
359-
path.withCString(encodedAs: UTF16.self) { link in
360-
// BACKUP_SEMANTICS are (confusingly) required in order to receive a
361-
// handle to a directory
362-
hFile = CreateFileW(link, 0, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
363-
nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS),
364-
nil)
352+
var hFile: HANDLE = try _fileSystemRepresentation(withPath: path) {
353+
// BACKUP_SEMANTICS are (confusingly) required in order to receive a
354+
// handle to a directory
355+
CreateFileW($0, 0, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
356+
nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS),
357+
nil)
365358
}
366359
guard hFile != INVALID_HANDLE_VALUE else {
367-
return try path.withCString(encodedAs: UTF16.self) {
360+
return try _fileSystemRepresentation(withPath: path) {
368361
var dwLength = GetFullPathNameW($0, 0, nil, nil)
369362
var szPath = Array<WCHAR>(repeating: 0, count: Int(dwLength + 1))
370363
dwLength = GetFullPathNameW($0, DWORD(szPath.count), &szPath, nil)
@@ -384,12 +377,8 @@ extension FileManager {
384377
}
385378

386379
internal func _copyRegularFile(atPath srcPath: String, toPath dstPath: String, variant: String = "Copy") throws {
387-
try srcPath.withCString(encodedAs: UTF16.self) { src in
388-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
389-
if !CopyFileW(src, dst, false) {
390-
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [srcPath, dstPath])
391-
}
392-
}
380+
guard try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, { CopyFileW($0, $1, false) }) else {
381+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [srcPath, dstPath])
393382
}
394383
}
395384

@@ -420,8 +409,8 @@ extension FileManager {
420409
}
421410

422411
while let item = enumerator.nextObject() as? String {
423-
let src = joinPath(prefix: srcPath, suffix: item)
424-
let dst = joinPath(prefix: dstPath, suffix: item)
412+
let src = try joinPath(prefix: srcPath, suffix: item)
413+
let dst = try joinPath(prefix: dstPath, suffix: item)
425414

426415
let faAttributes = try windowsFileAttributes(atPath: src)
427416
fileType = FileAttributeType(attributes: faAttributes, atPath: srcPath)
@@ -445,12 +434,10 @@ extension FileManager {
445434
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteFileExists.rawValue, userInfo: [NSFilePathErrorKey : NSString(dstPath)])
446435
}
447436

448-
try srcPath.withCString(encodedAs: UTF16.self) { src in
449-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
450-
if !MoveFileExW(src, dst, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
451-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
452-
}
453-
}
437+
guard try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, {
438+
MoveFileExW($0, $1, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH))
439+
}) else {
440+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
454441
}
455442
}
456443

@@ -463,12 +450,8 @@ extension FileManager {
463450
do {
464451
switch fileType {
465452
case .typeRegular:
466-
try srcPath.withCString(encodedAs: UTF16.self) { src in
467-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
468-
if !CreateHardLinkW(dst, src, nil) {
469-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
470-
}
471-
}
453+
guard try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, { CreateHardLinkW($1, $0, nil) }) else {
454+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
472455
}
473456
case .typeSymbolicLink:
474457
try _copySymlink(atPath: srcPath, toPath: dstPath)
@@ -488,50 +471,37 @@ extension FileManager {
488471
return
489472
}
490473

491-
guard path != "" else {
492-
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInvalidFileName.rawValue, userInfo: [NSFilePathErrorKey : NSString(path)])
493-
}
494-
495-
let url = URL(fileURLWithPath: path)
496-
var fsrBuf: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(MAX_PATH))
497-
_CFURLGetWideFileSystemRepresentation(url._cfObject, false, &fsrBuf, Int(MAX_PATH))
498-
let length = wcsnlen_s(&fsrBuf, fsrBuf.count)
499-
let fsrPath = String(utf16CodeUnits: &fsrBuf, count: length)
500-
501-
let faAttributes = try windowsFileAttributes(atPath: fsrPath)
502-
474+
let faAttributes = try windowsFileAttributes(atPath: path)
503475
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY {
504476
let readableAttributes = faAttributes.dwFileAttributes & DWORD(bitPattern: ~FILE_ATTRIBUTE_READONLY)
505-
guard fsrPath.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, readableAttributes) }) else {
477+
guard try _fileSystemRepresentation(withPath: path, { SetFileAttributesW($0, readableAttributes) }) else {
506478
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
507479
}
508480
}
509481

510482
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == 0 {
511-
if !fsrPath.withCString(encodedAs: UTF16.self, DeleteFileW) {
483+
guard try _fileSystemRepresentation(withPath: path, DeleteFileW) else {
512484
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
513485
}
514486
return
515487
}
516-
var dirStack = [fsrPath]
488+
var dirStack = [path]
517489
var itemPath = ""
518490
while let currentDir = dirStack.popLast() {
519491
do {
520492
itemPath = currentDir
521493
guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else {
522494
continue
523495
}
524-
guard !itemPath.withCString(encodedAs: UTF16.self, RemoveDirectoryW) else {
496+
guard !(try _fileSystemRepresentation(withPath: itemPath, RemoveDirectoryW)) else {
525497
continue
526498
}
527499
guard GetLastError() == ERROR_DIR_NOT_EMPTY else {
528500
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [itemPath])
529501
}
530502
dirStack.append(itemPath)
531503
var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW()
532-
let h: HANDLE = (itemPath + "\\*").withCString(encodedAs: UTF16.self, {
533-
FindFirstFileW($0, &ffd)
534-
})
504+
let h: HANDLE = try _fileSystemRepresentation(withPath: itemPath + "\\*", { FindFirstFileW($0, &ffd) })
535505
guard h != INVALID_HANDLE_VALUE else {
536506
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [itemPath])
537507
}
@@ -546,7 +516,7 @@ extension FileManager {
546516

547517
if ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY {
548518
let readableAttributes = ffd.dwFileAttributes & DWORD(bitPattern: ~FILE_ATTRIBUTE_READONLY)
549-
guard file.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, readableAttributes) }) else {
519+
guard try _fileSystemRepresentation(withPath: file, { SetFileAttributesW($0, readableAttributes) }) else {
550520
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [file])
551521
}
552522
}
@@ -559,7 +529,7 @@ extension FileManager {
559529
guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else {
560530
continue
561531
}
562-
if !itemPath.withCString(encodedAs: UTF16.self, DeleteFileW) {
532+
guard try _fileSystemRepresentation(withPath: itemPath, DeleteFileW) else {
563533
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [file])
564534
}
565535
}
@@ -582,7 +552,7 @@ extension FileManager {
582552

583553
@discardableResult
584554
internal func _changeCurrentDirectoryPath(_ path: String) -> Bool {
585-
return path.withCString(encodedAs: UTF16.self) { SetCurrentDirectoryW($0) }
555+
return (try? _fileSystemRepresentation(withPath: path, SetCurrentDirectoryW)) ?? false
586556
}
587557

588558
internal func _fileExists(atPath path: String, isDirectory: UnsafeMutablePointer<ObjCBool>?) -> Bool {
@@ -775,11 +745,11 @@ extension FileManager {
775745

776746
if isDir.boolValue && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) {
777747
var ffd = WIN32_FIND_DATAW()
778-
let dirPath = joinPath(prefix: _lastReturned.path, suffix: "*")
779-
let handle = dirPath.withCString(encodedAs: UTF16.self) {
780-
FindFirstFileW($0, &ffd)
748+
guard let dirPath = try? joinPath(prefix: _lastReturned.path, suffix: "*"),
749+
let handle = try? FileManager.default._fileSystemRepresentation(withPath: dirPath, { FindFirstFileW($0, &ffd) }),
750+
handle != INVALID_HANDLE_VALUE else {
751+
return firstValidItem()
781752
}
782-
guard handle != INVALID_HANDLE_VALUE else { return firstValidItem() }
783753
defer { FindClose(handle) }
784754

785755
repeat {

Foundation/FileManager.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ open class FileManager : NSObject {
436436
let hiddenAttrs = isHidden
437437
? attrs | DWORD(FILE_ATTRIBUTE_HIDDEN)
438438
: attrs & DWORD(bitPattern: ~FILE_ATTRIBUTE_HIDDEN)
439-
guard path.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, hiddenAttrs) }) else {
439+
guard try _fileSystemRepresentation(withPath: path, { SetFileAttributesW($0, hiddenAttrs) }) else {
440440
fatalError("Couldn't set \(path) to be hidden")
441441
}
442442
#else
@@ -1264,24 +1264,28 @@ public struct FileAttributeType : RawRepresentable, Equatable, Hashable {
12641264
self = .typeCharacterSpecial
12651265
} else if attributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) == DWORD(FILE_ATTRIBUTE_REPARSE_POINT) {
12661266
// A reparse point may or may not actually be a symbolic link, we need to read the reparse tag
1267-
let fileHandle = path.withCString(encodedAs: UTF16.self) {
1267+
let fileHandle = try? FileManager.default._fileSystemRepresentation(withPath: path) {
12681268
CreateFileW(/*lpFileName=*/$0,
12691269
/*dwDesiredAccess=*/DWORD(0),
12701270
/*dwShareMode=*/DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE),
12711271
/*lpSecurityAttributes=*/nil,
12721272
/*dwCreationDisposition=*/DWORD(OPEN_EXISTING),
12731273
/*dwFlagsAndAttributes=*/DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
12741274
/*hTemplateFile=*/nil)
1275+
} ?? INVALID_HANDLE_VALUE
1276+
guard fileHandle != INVALID_HANDLE_VALUE else {
1277+
self = .typeUnknown
1278+
return
12751279
}
12761280
defer { CloseHandle(fileHandle) }
12771281
var tagInfo = FILE_ATTRIBUTE_TAG_INFO()
12781282
guard GetFileInformationByHandleEx(fileHandle, FileAttributeTagInfo, &tagInfo, DWORD(MemoryLayout<FILE_ATTRIBUTE_TAG_INFO>.size)) else {
1279-
self = .typeUnknown
1280-
return
1283+
self = .typeUnknown
1284+
return
12811285
}
12821286
self = tagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK ? .typeSymbolicLink : .typeRegular
12831287
} else {
1284-
self = .typeRegular
1288+
self = .typeRegular
12851289
}
12861290
}
12871291
#else

0 commit comments

Comments
 (0)