Skip to content

Commit 5d9b008

Browse files
committed
Foundation: support _getFileSystemRepresentation on Windows
Update the `_getFileSystemRepresentation` helper to return a UTF-16 Windows style path. This enables us to properly model the file system representation of the path and the URL representation of the path.
1 parent 78c6b2f commit 5d9b008

File tree

7 files changed

+267
-134
lines changed

7 files changed

+267
-134
lines changed

CoreFoundation/URL.subproj/CFURL.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3965,10 +3965,15 @@ CF_EXPORT void __CFURLSetResourceInfoPtr(CFURLRef url, void *ptr) {
39653965
/* HFSPath<->URLPath functions at the bottom of the file */
39663966
static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) CF_RETURNS_RETAINED {
39673967
CFArrayRef tmp;
3968+
CFMutableStringRef mutablePath;
39683969
CFMutableArrayRef urlComponents = NULL;
39693970
CFIndex i=0;
39703971

3971-
tmp = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("\\"));
3972+
// Since '/' is a valid Windows path separator, we convert '/' to '\' before splitting
3973+
mutablePath = CFStringCreateMutableCopy(alloc, 0, path);
3974+
CFStringFindAndReplace(mutablePath, CFSTR("/"), CFSTR("\\"), CFRangeMake(0, CFStringGetLength(mutablePath)), 0);
3975+
tmp = CFStringCreateArrayBySeparatingStrings(alloc, mutablePath, CFSTR("\\"));
3976+
CFRelease(mutablePath);
39723977
urlComponents = CFArrayCreateMutableCopy(alloc, 0, tmp);
39733978
CFRelease(tmp);
39743979

Foundation/FileManager+Win32.swift

Lines changed: 65 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -175,22 +175,20 @@ extension FileManager {
175175
}
176176
}
177177

178-
var saAttributes: SECURITY_ATTRIBUTES =
179-
SECURITY_ATTRIBUTES(nLength: DWORD(MemoryLayout<SECURITY_ATTRIBUTES>.size),
180-
lpSecurityDescriptor: nil,
181-
bInheritHandle: false)
182-
let psaAttributes: UnsafeMutablePointer<SECURITY_ATTRIBUTES> =
183-
UnsafeMutablePointer<SECURITY_ATTRIBUTES>(&saAttributes)
184-
185-
186-
try path.withCString(encodedAs: UTF16.self) {
187-
if !CreateDirectoryW($0, psaAttributes) {
188-
// FIXME(compnerd) pass along path
189-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
178+
try FileManager.default._fileSystemRepresentation(withPath: path) { fsr in
179+
var saAttributes: SECURITY_ATTRIBUTES =
180+
SECURITY_ATTRIBUTES(nLength: DWORD(MemoryLayout<SECURITY_ATTRIBUTES>.size),
181+
lpSecurityDescriptor: nil,
182+
bInheritHandle: false)
183+
try withUnsafeMutablePointer(to: &saAttributes) {
184+
if !CreateDirectoryW(fsr, $0) {
185+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
190186
}
191-
}
192-
if let attr = attributes {
187+
}
188+
189+
if let attr = attributes {
193190
try self.setAttributes(attr, ofItemAtPath: path)
191+
}
194192
}
195193
}
196194

@@ -236,15 +234,15 @@ extension FileManager {
236234
}
237235

238236
internal func windowsFileAttributes(atPath path: String) throws -> WIN32_FILE_ATTRIBUTE_DATA {
237+
return try FileManager.default._fileSystemRepresentation(withPath: path) {
239238
var faAttributes: WIN32_FILE_ATTRIBUTE_DATA = WIN32_FILE_ATTRIBUTE_DATA()
240-
return try path.withCString(encodedAs: UTF16.self) {
241-
if !GetFileAttributesExW($0, GetFileExInfoStandard, &faAttributes) {
242-
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
243-
}
244-
return faAttributes
239+
if !GetFileAttributesExW($0, GetFileExInfoStandard, &faAttributes) {
240+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
245241
}
242+
return faAttributes
243+
}
246244
}
247-
245+
248246
internal func _attributesOfFileSystemIncludingBlockSize(forPath path: String) throws -> (attributes: [FileAttributeKey : Any], blockSize: UInt64?) {
249247
return (attributes: try _attributesOfFileSystem(forPath: path), blockSize: nil)
250248
}
@@ -292,9 +290,10 @@ extension FileManager {
292290
case .some(false):
293291
break;
294292
case .none:
295-
let resolvedDest = destPath.isAbsolutePath
296-
? destPath
297-
: try joinPath(prefix: path.deletingLastPathComponent, suffix: destPath)
293+
let resolvedDest =
294+
destPath.isAbsolutePath ? destPath
295+
: joinPath(prefix: path.deletingLastPathComponent,
296+
suffix: destPath)
298297
guard let faAttributes = try? windowsFileAttributes(atPath: resolvedDest) else {
299298
// Note: windowsfileAttributes will throw if the destPath is not found.
300299
// Since on Windows, you are required to know the type of the symlink
@@ -309,12 +308,10 @@ extension FileManager {
309308
}
310309
}
311310

312-
try path.withCString(encodedAs: UTF16.self) { name in
313-
try destPath.withCString(encodedAs: UTF16.self) { dest in
314-
guard CreateSymbolicLinkW(name, dest, dwFlags) != 0 else {
315-
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
316-
}
317-
}
311+
try FileManager.default._fileSystemRepresentation(withPath: path, andPath: destPath) {
312+
guard CreateSymbolicLinkW($0, $1, dwFlags) != 0 else {
313+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
314+
}
318315
}
319316
}
320317

@@ -324,13 +321,14 @@ extension FileManager {
324321
throw _NSErrorWithWindowsError(DWORD(ERROR_BAD_ARGUMENTS), reading: false)
325322
}
326323

327-
let handle = path.withCString(encodedAs: UTF16.self) { symlink in
328-
CreateFileW(symlink, GENERIC_READ, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
329-
nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
330-
nil)
324+
let handle: HANDLE = try FileManager.default._fileSystemRepresentation(withPath: path) {
325+
CreateFileW($0, GENERIC_READ,
326+
DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
327+
nil, DWORD(OPEN_EXISTING),
328+
DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
329+
nil)
331330
}
332-
333-
guard handle != INVALID_HANDLE_VALUE else {
331+
if handle == INVALID_HANDLE_VALUE {
334332
throw _NSErrorWithWindowsError(GetLastError(), reading: true)
335333
}
336334
defer { CloseHandle(handle) }
@@ -494,12 +492,10 @@ extension FileManager {
494492
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteFileExists.rawValue, userInfo: [NSFilePathErrorKey : NSString(dstPath)])
495493
}
496494

497-
try srcPath.withCString(encodedAs: UTF16.self) { src in
498-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
499-
if !MoveFileExW(src, dst, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
500-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
501-
}
502-
}
495+
try FileManager.default._fileSystemRepresentation(withPath: srcPath, andPath: dstPath) {
496+
if !MoveFileExW($0, $1, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
497+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
498+
}
503499
}
504500
}
505501

@@ -512,12 +508,10 @@ extension FileManager {
512508
do {
513509
switch fileType {
514510
case .typeRegular:
515-
try srcPath.withCString(encodedAs: UTF16.self) { src in
516-
try dstPath.withCString(encodedAs: UTF16.self) { dst in
517-
if !CreateHardLinkW(dst, src, nil) {
518-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
519-
}
520-
}
511+
try FileManager.default._fileSystemRepresentation(withPath: srcPath, andPath: dstPath) {
512+
if !CreateHardLinkW($1, $0, nil) {
513+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
514+
}
521515
}
522516
case .typeSymbolicLink:
523517
try _copySymlink(atPath: srcPath, toPath: dstPath)
@@ -637,12 +631,15 @@ extension FileManager {
637631
var szDirectory: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(dwLength + 1))
638632

639633
GetCurrentDirectoryW(dwLength, &szDirectory)
640-
return String(decodingCString: &szDirectory, as: UTF16.self)
634+
return String(decodingCString: &szDirectory, as: UTF16.self).standardizingPath
641635
}
642636

643637
@discardableResult
644638
internal func _changeCurrentDirectoryPath(_ path: String) -> Bool {
645-
return path.withCString(encodedAs: UTF16.self) { SetCurrentDirectoryW($0) }
639+
guard let bResult = try? FileManager.default._fileSystemRepresentation(withPath: path, {
640+
SetCurrentDirectoryW($0)
641+
}) else { return false }
642+
return bResult
646643
}
647644

648645
internal func _fileExists(atPath path: String, isDirectory: UnsafeMutablePointer<ObjCBool>?) -> Bool {
@@ -701,8 +698,8 @@ extension FileManager {
701698
return true
702699
}
703700

704-
internal func _lstatFile(atPath path: String, withFileSystemRepresentation fsRep: UnsafePointer<Int8>? = nil) throws -> stat {
705-
let _fsRep: UnsafePointer<Int8>
701+
internal func _lstatFile(atPath path: String, withFileSystemRepresentation fsRep: UnsafePointer<NativeFSRCharType>? = nil) throws -> stat {
702+
let _fsRep: UnsafePointer<NativeFSRCharType>
706703
if fsRep == nil {
707704
_fsRep = try __fileSystemRepresentation(withPath: path)
708705
} else {
@@ -714,20 +711,20 @@ extension FileManager {
714711
}
715712

716713
var statInfo = stat()
717-
let h = path.withCString(encodedAs: UTF16.self) {
718-
CreateFileW(/*lpFileName=*/$0,
719-
/*dwDesiredAccess=*/DWORD(0),
720-
/*dwShareMode=*/DWORD(FILE_SHARE_READ),
721-
/*lpSecurityAttributes=*/nil,
722-
/*dwCreationDisposition=*/DWORD(OPEN_EXISTING),
723-
/*dwFlagsAndAttributes=*/DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
714+
let handle =
715+
CreateFileW(_fsRep, /*dwDesiredAccess=*/DWORD(0),
716+
DWORD(FILE_SHARE_READ), /*lpSecurityAttributes=*/nil,
717+
DWORD(OPEN_EXISTING),
718+
DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
724719
/*hTemplateFile=*/nil)
725-
}
726-
if h == INVALID_HANDLE_VALUE {
720+
if handle == INVALID_HANDLE_VALUE {
727721
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
728722
}
723+
defer { CloseHandle(handle) }
724+
729725
var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
730-
GetFileInformationByHandle(h, &info)
726+
GetFileInformationByHandle(handle, &info)
727+
731728
// Group id is always 0 on Windows
732729
statInfo.st_gid = 0
733730
statInfo.st_atime = info.ftLastAccessTime.time_t
@@ -751,13 +748,12 @@ extension FileManager {
751748

752749
statInfo.st_mtime = info.ftLastWriteTime.time_t
753750
statInfo.st_nlink = Int16(info.nNumberOfLinks)
754-
if info.nFileSizeHigh != 0 {
751+
guard info.nFileSizeHigh == 0 else {
755752
throw _NSErrorWithErrno(EOVERFLOW, reading: true, path: path)
756753
}
757754
statInfo.st_size = Int32(info.nFileSizeLow)
758755
// Uid is always 0 on Windows systems
759756
statInfo.st_uid = 0
760-
CloseHandle(h)
761757
return statInfo
762758
}
763759

@@ -845,20 +841,13 @@ extension FileManager {
845841
}
846842

847843
internal func _appendSymlinkDestination(_ dest: String, toPath: String) -> String {
848-
var isAbsolutePath: Bool = false
849-
dest.withCString(encodedAs: UTF16.self) {
850-
isAbsolutePath = !PathIsRelativeW($0)
851-
}
852-
853-
if isAbsolutePath {
854-
return dest
855-
}
844+
if dest.isAbsolutePath { return dest }
856845
let temp = toPath._bridgeToObjectiveC().deletingLastPathComponent
857846
return temp._bridgeToObjectiveC().appendingPathComponent(dest)
858847
}
859848

860849
internal func _updateTimes(atPath path: String,
861-
withFileSystemRepresentation fsr: UnsafePointer<Int8>,
850+
withFileSystemRepresentation fsr: UnsafePointer<NativeFSRCharType>,
862851
creationTime: Date? = nil,
863852
accessTime: Date? = nil,
864853
modificationTime: Date? = nil) throws {
@@ -869,10 +858,9 @@ extension FileManager {
869858
var mtime: FILETIME =
870859
FILETIME(from: time_t((modificationTime ?? stat.lastModificationDate).timeIntervalSince1970))
871860

872-
let hFile: HANDLE = String(utf8String: fsr)!.withCString(encodedAs: UTF16.self) {
873-
CreateFileW($0, DWORD(GENERIC_WRITE), DWORD(FILE_SHARE_WRITE),
861+
let hFile: HANDLE =
862+
CreateFileW(fsr, DWORD(GENERIC_WRITE), DWORD(FILE_SHARE_WRITE),
874863
nil, DWORD(OPEN_EXISTING), 0, nil)
875-
}
876864
if hFile == INVALID_HANDLE_VALUE {
877865
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
878866
}
@@ -927,10 +915,10 @@ extension FileManager {
927915
if isDir && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) {
928916
var ffd = WIN32_FIND_DATAW()
929917
let dirPath = joinPath(prefix: _lastReturned.path, suffix: "*")
930-
let handle = dirPath.withCString(encodedAs: UTF16.self) {
918+
let handle = try? FileManager.default._fileSystemRepresentation(withPath: dirPath) {
931919
FindFirstFileW($0, &ffd)
932920
}
933-
guard handle != INVALID_HANDLE_VALUE else { return firstValidItem() }
921+
if handle == INVALID_HANDLE_VALUE { return firstValidItem() }
934922
defer { FindClose(handle) }
935923

936924
repeat {

0 commit comments

Comments
 (0)