Skip to content

Foundation: support _getFileSystemRepresentation on Windows #2658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CoreFoundation/URL.subproj/CFURL.c
Original file line number Diff line number Diff line change
Expand Up @@ -3965,10 +3965,15 @@ CF_EXPORT void __CFURLSetResourceInfoPtr(CFURLRef url, void *ptr) {
/* HFSPath<->URLPath functions at the bottom of the file */
static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) CF_RETURNS_RETAINED {
CFArrayRef tmp;
CFMutableStringRef mutablePath;
CFMutableArrayRef urlComponents = NULL;
CFIndex i=0;

tmp = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("\\"));
// Since '/' is a valid Windows path separator, we convert '/' to '\' before splitting
mutablePath = CFStringCreateMutableCopy(alloc, 0, path);
CFStringFindAndReplace(mutablePath, CFSTR("/"), CFSTR("\\"), CFRangeMake(0, CFStringGetLength(mutablePath)), 0);
tmp = CFStringCreateArrayBySeparatingStrings(alloc, mutablePath, CFSTR("\\"));
CFRelease(mutablePath);
urlComponents = CFArrayCreateMutableCopy(alloc, 0, tmp);
CFRelease(tmp);

Expand Down
142 changes: 65 additions & 77 deletions Foundation/FileManager+Win32.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,22 +175,20 @@ extension FileManager {
}
}

var saAttributes: SECURITY_ATTRIBUTES =
SECURITY_ATTRIBUTES(nLength: DWORD(MemoryLayout<SECURITY_ATTRIBUTES>.size),
lpSecurityDescriptor: nil,
bInheritHandle: false)
let psaAttributes: UnsafeMutablePointer<SECURITY_ATTRIBUTES> =
UnsafeMutablePointer<SECURITY_ATTRIBUTES>(&saAttributes)


try path.withCString(encodedAs: UTF16.self) {
if !CreateDirectoryW($0, psaAttributes) {
// FIXME(compnerd) pass along path
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
try FileManager.default._fileSystemRepresentation(withPath: path) { fsr in
var saAttributes: SECURITY_ATTRIBUTES =
SECURITY_ATTRIBUTES(nLength: DWORD(MemoryLayout<SECURITY_ATTRIBUTES>.size),
lpSecurityDescriptor: nil,
bInheritHandle: false)
try withUnsafeMutablePointer(to: &saAttributes) {
if !CreateDirectoryW(fsr, $0) {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
}
}
if let attr = attributes {
}

if let attr = attributes {
try self.setAttributes(attr, ofItemAtPath: path)
}
}
}

Expand Down Expand Up @@ -236,15 +234,15 @@ extension FileManager {
}

internal func windowsFileAttributes(atPath path: String) throws -> WIN32_FILE_ATTRIBUTE_DATA {
return try FileManager.default._fileSystemRepresentation(withPath: path) {
var faAttributes: WIN32_FILE_ATTRIBUTE_DATA = WIN32_FILE_ATTRIBUTE_DATA()
return try path.withCString(encodedAs: UTF16.self) {
if !GetFileAttributesExW($0, GetFileExInfoStandard, &faAttributes) {
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
}
return faAttributes
if !GetFileAttributesExW($0, GetFileExInfoStandard, &faAttributes) {
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
}
return faAttributes
}
}

internal func _attributesOfFileSystemIncludingBlockSize(forPath path: String) throws -> (attributes: [FileAttributeKey : Any], blockSize: UInt64?) {
return (attributes: try _attributesOfFileSystem(forPath: path), blockSize: nil)
}
Expand Down Expand Up @@ -292,9 +290,10 @@ extension FileManager {
case .some(false):
break;
case .none:
let resolvedDest = destPath.isAbsolutePath
? destPath
: try joinPath(prefix: path.deletingLastPathComponent, suffix: destPath)
let resolvedDest =
destPath.isAbsolutePath ? destPath
: joinPath(prefix: path.deletingLastPathComponent,
suffix: destPath)
guard let faAttributes = try? windowsFileAttributes(atPath: resolvedDest) else {
// Note: windowsfileAttributes will throw if the destPath is not found.
// Since on Windows, you are required to know the type of the symlink
Expand All @@ -309,12 +308,10 @@ extension FileManager {
}
}

try path.withCString(encodedAs: UTF16.self) { name in
try destPath.withCString(encodedAs: UTF16.self) { dest in
guard CreateSymbolicLinkW(name, dest, dwFlags) != 0 else {
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
}
}
try FileManager.default._fileSystemRepresentation(withPath: path, andPath: destPath) {
guard CreateSymbolicLinkW($0, $1, dwFlags) != 0 else {
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
}
}
}

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

let handle = path.withCString(encodedAs: UTF16.self) { symlink in
CreateFileW(symlink, GENERIC_READ, DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
nil)
let handle: HANDLE = try FileManager.default._fileSystemRepresentation(withPath: path) {
CreateFileW($0, GENERIC_READ,
DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
nil, DWORD(OPEN_EXISTING),
DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
nil)
}

guard handle != INVALID_HANDLE_VALUE else {
if handle == INVALID_HANDLE_VALUE {
throw _NSErrorWithWindowsError(GetLastError(), reading: true)
}
defer { CloseHandle(handle) }
Expand Down Expand Up @@ -494,12 +492,10 @@ extension FileManager {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteFileExists.rawValue, userInfo: [NSFilePathErrorKey : NSString(dstPath)])
}

try srcPath.withCString(encodedAs: UTF16.self) { src in
try dstPath.withCString(encodedAs: UTF16.self) { dst in
if !MoveFileExW(src, dst, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
}
}
try FileManager.default._fileSystemRepresentation(withPath: srcPath, andPath: dstPath) {
if !MoveFileExW($0, $1, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
}
}
}

Expand All @@ -512,12 +508,10 @@ extension FileManager {
do {
switch fileType {
case .typeRegular:
try srcPath.withCString(encodedAs: UTF16.self) { src in
try dstPath.withCString(encodedAs: UTF16.self) { dst in
if !CreateHardLinkW(dst, src, nil) {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
}
}
try FileManager.default._fileSystemRepresentation(withPath: srcPath, andPath: dstPath) {
if !CreateHardLinkW($1, $0, nil) {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
}
}
case .typeSymbolicLink:
try _copySymlink(atPath: srcPath, toPath: dstPath)
Expand Down Expand Up @@ -637,12 +631,15 @@ extension FileManager {
var szDirectory: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(dwLength + 1))

GetCurrentDirectoryW(dwLength, &szDirectory)
return String(decodingCString: &szDirectory, as: UTF16.self)
return String(decodingCString: &szDirectory, as: UTF16.self).standardizingPath
}

@discardableResult
internal func _changeCurrentDirectoryPath(_ path: String) -> Bool {
return path.withCString(encodedAs: UTF16.self) { SetCurrentDirectoryW($0) }
guard let bResult = try? FileManager.default._fileSystemRepresentation(withPath: path, {
SetCurrentDirectoryW($0)
}) else { return false }
return bResult
}

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

internal func _lstatFile(atPath path: String, withFileSystemRepresentation fsRep: UnsafePointer<Int8>? = nil) throws -> stat {
let _fsRep: UnsafePointer<Int8>
internal func _lstatFile(atPath path: String, withFileSystemRepresentation fsRep: UnsafePointer<NativeFSRCharType>? = nil) throws -> stat {
let _fsRep: UnsafePointer<NativeFSRCharType>
if fsRep == nil {
_fsRep = try __fileSystemRepresentation(withPath: path)
} else {
Expand All @@ -714,20 +711,20 @@ extension FileManager {
}

var statInfo = stat()
let h = path.withCString(encodedAs: UTF16.self) {
CreateFileW(/*lpFileName=*/$0,
/*dwDesiredAccess=*/DWORD(0),
/*dwShareMode=*/DWORD(FILE_SHARE_READ),
/*lpSecurityAttributes=*/nil,
/*dwCreationDisposition=*/DWORD(OPEN_EXISTING),
/*dwFlagsAndAttributes=*/DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
let handle =
CreateFileW(_fsRep, /*dwDesiredAccess=*/DWORD(0),
DWORD(FILE_SHARE_READ), /*lpSecurityAttributes=*/nil,
DWORD(OPEN_EXISTING),
DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS),
/*hTemplateFile=*/nil)
}
if h == INVALID_HANDLE_VALUE {
if handle == INVALID_HANDLE_VALUE {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [path])
}
defer { CloseHandle(handle) }

var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
GetFileInformationByHandle(h, &info)
GetFileInformationByHandle(handle, &info)

// Group id is always 0 on Windows
statInfo.st_gid = 0
statInfo.st_atime = info.ftLastAccessTime.time_t
Expand All @@ -751,13 +748,12 @@ extension FileManager {

statInfo.st_mtime = info.ftLastWriteTime.time_t
statInfo.st_nlink = Int16(info.nNumberOfLinks)
if info.nFileSizeHigh != 0 {
guard info.nFileSizeHigh == 0 else {
throw _NSErrorWithErrno(EOVERFLOW, reading: true, path: path)
}
statInfo.st_size = Int32(info.nFileSizeLow)
// Uid is always 0 on Windows systems
statInfo.st_uid = 0
CloseHandle(h)
return statInfo
}

Expand Down Expand Up @@ -845,20 +841,13 @@ extension FileManager {
}

internal func _appendSymlinkDestination(_ dest: String, toPath: String) -> String {
var isAbsolutePath: Bool = false
dest.withCString(encodedAs: UTF16.self) {
isAbsolutePath = !PathIsRelativeW($0)
}

if isAbsolutePath {
return dest
}
if dest.isAbsolutePath { return dest }
let temp = toPath._bridgeToObjectiveC().deletingLastPathComponent
return temp._bridgeToObjectiveC().appendingPathComponent(dest)
}

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

let hFile: HANDLE = String(utf8String: fsr)!.withCString(encodedAs: UTF16.self) {
CreateFileW($0, DWORD(GENERIC_WRITE), DWORD(FILE_SHARE_WRITE),
let hFile: HANDLE =
CreateFileW(fsr, DWORD(GENERIC_WRITE), DWORD(FILE_SHARE_WRITE),
nil, DWORD(OPEN_EXISTING), 0, nil)
}
if hFile == INVALID_HANDLE_VALUE {
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path])
}
Expand Down Expand Up @@ -927,10 +915,10 @@ extension FileManager {
if isDir && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) {
var ffd = WIN32_FIND_DATAW()
let dirPath = joinPath(prefix: _lastReturned.path, suffix: "*")
let handle = dirPath.withCString(encodedAs: UTF16.self) {
let handle = try? FileManager.default._fileSystemRepresentation(withPath: dirPath) {
FindFirstFileW($0, &ffd)
}
guard handle != INVALID_HANDLE_VALUE else { return firstValidItem() }
if handle == INVALID_HANDLE_VALUE { return firstValidItem() }
defer { FindClose(handle) }

repeat {
Expand Down
Loading