Skip to content

Foundation: continue Windows file system cleanup #2659

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
6 changes: 6 additions & 0 deletions CoreFoundation/Base.subproj/CFPlatform.c
Original file line number Diff line number Diff line change
Expand Up @@ -1527,9 +1527,15 @@ CF_EXPORT char **_CFEnviron(void) {
#endif
}

#if TARGET_OS_WIN32
CF_CROSS_PLATFORM_EXPORT int _CFOpenFileWithMode(const unsigned short *path, int opts, mode_t mode) {
return _wopen(path, opts, mode);
}
#else
CF_CROSS_PLATFORM_EXPORT int _CFOpenFileWithMode(const char *path, int opts, mode_t mode) {
return open(path, opts, mode);
}
#endif

int _CFOpenFile(const char *path, int opts) {
return open(path, opts, 0);
Expand Down
4 changes: 4 additions & 0 deletions CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,11 @@ CF_CROSS_PLATFORM_EXPORT CFIndex __CFCharDigitValue(UniChar ch);

#pragma mark - File Functions

#if TARGET_OS_WIN32
CF_CROSS_PLATFORM_EXPORT int _CFOpenFileWithMode(const unsigned short *path, int opts, mode_t mode);
#else
CF_CROSS_PLATFORM_EXPORT int _CFOpenFileWithMode(const char *path, int opts, mode_t mode);
#endif
CF_CROSS_PLATFORM_EXPORT void *_CFReallocf(void *ptr, size_t size);
CF_CROSS_PLATFORM_EXPORT int _CFOpenFile(const char *path, int opts);

Expand Down
15 changes: 5 additions & 10 deletions Foundation/FileHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,10 @@ open class FileHandle : NSObject {
}

internal convenience init?(path: String, flags: Int32, createMode: Int) {
let fd = _CFOpenFileWithMode(path, flags, mode_t(createMode))
guard fd > 0 else { return nil }
guard let fd: Int32 = try? FileManager.default._fileSystemRepresentation(withPath: path, {
_CFOpenFileWithMode($0, flags, mode_t(createMode))
}), fd > 0 else { return nil }

self.init(fileDescriptor: fd, closeOnDealloc: true)
if _handle == INVALID_HANDLE_VALUE { return nil }
}
Expand All @@ -456,15 +458,8 @@ open class FileHandle : NSObject {
}
#endif

internal convenience init?(fileSystemRepresentation: UnsafePointer<Int8>, flags: Int32, createMode: Int) {
#if os(Windows)
var fd: Int32 = -1
if let path = String(cString: fileSystemRepresentation).cString(using: .utf16) {
fd = _CFOpenFileWithMode(path, flags, mode_t(createMode))
}
#else
internal convenience init?(fileSystemRepresentation: UnsafePointer<NativeFSRCharType>, flags: Int32, createMode: Int) {
let fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode))
#endif
guard fd > 0 else { return nil }
self.init(fileDescriptor: fd, closeOnDealloc: true)
}
Expand Down
140 changes: 77 additions & 63 deletions Foundation/FileManager+Win32.swift
Original file line number Diff line number Diff line change
Expand Up @@ -583,21 +583,24 @@ extension FileManager {
}
dirStack.append(itemPath)
var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW()
let h: HANDLE = (itemPath + "\\*").withCString(encodedAs: UTF16.self, {
FindFirstFileW($0, &ffd)
})
guard h != INVALID_HANDLE_VALUE else {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [itemPath])
let capacity = MemoryLayout.size(ofValue: ffd.cFileName)

let handle: HANDLE = try FileManager.default._fileSystemRepresentation(withPath: joinPath(prefix: itemPath, suffix: "*")) {
FindFirstFileW($0, &ffd)
}
defer { FindClose(h) }
if handle == INVALID_HANDLE_VALUE {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [itemPath])
}
defer { FindClose(handle) }

repeat {
let fileArr = Array<WCHAR>(
UnsafeBufferPointer(start: &ffd.cFileName.0,
count: MemoryLayout.size(ofValue: ffd.cFileName)))
let file = String(decodingCString: fileArr, as: UTF16.self)
itemPath = "\(currentDir)\\\(file)"
let file = withUnsafePointer(to: &ffd.cFileName) {
$0.withMemoryRebound(to: WCHAR.self, capacity: capacity) {
String(decodingCString: $0, as: UTF16.self)
}
}

itemPath = "\(currentDir)\\\(file)"
if ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY {
let readableAttributes = ffd.dwFileAttributes & DWORD(bitPattern: ~FILE_ATTRIBUTE_READONLY)
guard itemPath.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, readableAttributes) }) else {
Expand All @@ -617,7 +620,7 @@ extension FileManager {
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [file])
}
}
} while FindNextFileW(h, &ffd)
} while FindNextFileW(handle, &ffd)
} catch {
if !shouldProceedAfterError(error, removingItemAtPath: itemPath, isURL: isURL) {
throw error
Expand Down Expand Up @@ -646,18 +649,24 @@ extension FileManager {
var faAttributes: WIN32_FILE_ATTRIBUTE_DATA = WIN32_FILE_ATTRIBUTE_DATA()
do { faAttributes = try windowsFileAttributes(atPath: path) } catch { return false }
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) == DWORD(FILE_ATTRIBUTE_REPARSE_POINT) {
do {
let contents = try destinationOfSymbolicLink(atPath: path)
let resolvedPath = contents.isAbsolutePath
? contents
: joinPath(prefix: path.deletingLastPathComponent, suffix: contents)
try faAttributes = windowsFileAttributes(atPath: resolvedPath)
} catch {
return false
}
}
if let isDirectory = isDirectory {
guard let handle: HANDLE = try? FileManager.default._fileSystemRepresentation(withPath: path, {
CreateFileW($0, /* dwDesiredAccess= */ DWORD(0),
DWORD(FILE_SHARE_READ), /* lpSecurityAttributes= */ nil,
DWORD(OPEN_EXISTING),
DWORD(FILE_FLAG_BACKUP_SEMANTICS), /* hTemplateFile= */ nil)
}) else { return false }
if handle == INVALID_HANDLE_VALUE { return false }
defer { CloseHandle(handle) }

if let isDirectory = isDirectory {
var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
GetFileInformationByHandle(handle, &info)
isDirectory.pointee = ObjCBool(info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY))
}
} else {
if let isDirectory = isDirectory {
isDirectory.pointee = ObjCBool(faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY))
}
}
return true
}
Expand Down Expand Up @@ -758,21 +767,24 @@ extension FileManager {
}

internal func _contentsEqual(atPath path1: String, andPath path2: String) -> Bool {
guard let path1Handle = path1.withCString(encodedAs: UTF16.self, {
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)
}), path1Handle != INVALID_HANDLE_VALUE else {
return false
}

guard let path1Handle: HANDLE = try? FileManager.default._fileSystemRepresentation(withPath: path1, {
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)
}) else { return false }
if path1Handle == INVALID_HANDLE_VALUE { return false }
defer { CloseHandle(path1Handle) }

guard let path2Handle = path2.withCString(encodedAs: UTF16.self, {
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)
}), path2Handle != INVALID_HANDLE_VALUE else {
return false
}
guard let path2Handle: HANDLE = try? FileManager.default._fileSystemRepresentation(withPath: path2, {
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)
}) else { return false }
if path2Handle == INVALID_HANDLE_VALUE { return false }
defer { CloseHandle(path2Handle) }

let file1Type = GetFileType(path1Handle)
Expand Down Expand Up @@ -829,14 +841,12 @@ extension FileManager {
return true
}

let path1Fsr = fileSystemRepresentation(withPath: path1)
defer { path1Fsr.deallocate() }
let path2Fsr = fileSystemRepresentation(withPath: path2)
defer { path2Fsr.deallocate() }
return _compareFiles(withFileSystemRepresentation: path1Fsr,
andFileSystemRepresentation: path2Fsr,
size: (Int64(path1FileInfo.nFileSizeHigh) << 32) | Int64(path1FileInfo.nFileSizeLow),
bufSize: 0x1000)
return try! FileManager.default._fileSystemRepresentation(withPath: path1, andPath: path2) {
_compareFiles(withFileSystemRepresentation: $0,
andFileSystemRepresentation: $1,
size: (Int64(path1FileInfo.nFileSizeHigh) << 32) | Int64(path1FileInfo.nFileSizeLow),
bufSize: 0x1000)
}
}
}

Expand Down Expand Up @@ -891,10 +901,13 @@ extension FileManager {
func firstValidItem() -> URL? {
while let url = _stack.popLast() {
if !FileManager.default.fileExists(atPath: url.path) {
guard let handler = _errorHandler,
handler(url, _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [url.path]))
else { return nil }
continue
if let handler = _errorHandler {
if !handler(url, _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [url.path])) {
return nil
}
} else {
return nil
}
}
_lastReturned = url
return _lastReturned
Expand All @@ -910,31 +923,32 @@ extension FileManager {
return firstValidItem()
}

let isDir = attrs.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY)
&& attrs.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) == 0
let isDir = attrs.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY) &&
attrs.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) == 0
if isDir && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) {
var ffd = WIN32_FIND_DATAW()
let dirPath = joinPath(prefix: _lastReturned.path, suffix: "*")
let handle = try? FileManager.default._fileSystemRepresentation(withPath: dirPath) {
let capacity = MemoryLayout.size(ofValue: ffd.cFileName)

guard let handle = try? FileManager.default._fileSystemRepresentation(withPath: joinPath(prefix: _lastReturned.path, suffix: "*"), {
FindFirstFileW($0, &ffd)
}
}) else { return firstValidItem() }
if handle == INVALID_HANDLE_VALUE { return firstValidItem() }
defer { FindClose(handle) }

repeat {
let fileArr = Array<WCHAR>(
UnsafeBufferPointer(start: &ffd.cFileName.0,
count: MemoryLayout.size(ofValue: ffd.cFileName)))
let file = String(decodingCString: fileArr, as: UTF16.self)
if file != "." && file != ".."
&& (!_options.contains(.skipsHiddenFiles)
|| (ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_HIDDEN) == 0)) {
let relative = URL(fileURLWithPath: file, relativeTo: _lastReturned)
_stack.append(relative)
let file = withUnsafePointer(to: &ffd.cFileName) {
$0.withMemoryRebound(to: WCHAR.self, capacity: capacity) {
String(decodingCString: $0, as: UTF16.self)
}
}
if file == "." || file == ".." { continue }
if _options.contains(.skipsHiddenFiles) &&
ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_HIDDEN) == DWORD(FILE_ATTRIBUTE_HIDDEN) {
continue
}
_stack.append(URL(fileURLWithPath: file, relativeTo: _lastReturned))
} while FindNextFileW(handle, &ffd)
}

return firstValidItem()
}

Expand Down
2 changes: 1 addition & 1 deletion Foundation/FileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ open class FileManager : NSObject {
}()
#endif

internal func _compareFiles(withFileSystemRepresentation file1Rep: UnsafePointer<Int8>, andFileSystemRepresentation file2Rep: UnsafePointer<Int8>, size: Int64, bufSize: Int) -> Bool {
internal func _compareFiles(withFileSystemRepresentation file1Rep: UnsafePointer<NativeFSRCharType>, andFileSystemRepresentation file2Rep: UnsafePointer<NativeFSRCharType>, size: Int64, bufSize: Int) -> Bool {
guard let file1 = FileHandle(fileSystemRepresentation: file1Rep, flags: O_RDONLY, createMode: 0) else { return false }
guard let file2 = FileHandle(fileSystemRepresentation: file2Rep, flags: O_RDONLY, createMode: 0) else { return false }

Expand Down