Skip to content

Commit dff1eda

Browse files
committed
SWBUtil: use FileManager for more operations
`unlink` is deprecated on Windows, and `RemoveFileW` should be preferred. However, that would limit the path to `MAX_PATH` (261) characters. Prefer to use `FileManager to remove the file to avoid the path limit. Adopt the fileManager path in more locations.
1 parent 09c975a commit dff1eda

File tree

1 file changed

+4
-150
lines changed

1 file changed

+4
-150
lines changed

Diff for: Sources/SWBUtil/FSProxy.swift

+4-150
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ class LocalFS: FSProxy, @unchecked Sendable {
362362
/// Check whether a given path is a symlink.
363363
/// - parameter destinationExists: If the path is a symlink, then this `inout` parameter will be set to `true` if the destination exists. Otherwise it will be set to `false`.
364364
func isSymlink(_ path: Path, _ destinationExists: inout Bool) -> Bool {
365-
#if os(Windows)
366365
do {
367366
let destination = try fileManager.destinationOfSymbolicLink(atPath: path.str)
368367
destinationExists = exists((path.isAbsolute ? path.dirname : Path.currentDirectory).join(destination))
@@ -371,22 +370,6 @@ class LocalFS: FSProxy, @unchecked Sendable {
371370
destinationExists = false
372371
return false
373372
}
374-
#else
375-
destinationExists = false
376-
var statBuf = stat()
377-
if lstat(path.str, &statBuf) < 0 {
378-
return false
379-
}
380-
guard createFileInfo(statBuf).isSymlink else {
381-
return false
382-
}
383-
statBuf = stat()
384-
if stat(path.str, &statBuf) < 0 {
385-
return true
386-
}
387-
destinationExists = true
388-
return true
389-
#endif
390373
}
391374

392375
func listdir(_ path: Path) throws -> [String] {
@@ -397,70 +380,11 @@ class LocalFS: FSProxy, @unchecked Sendable {
397380
/// - parameter recursive: If `false`, then the parent directory at `path` must already exist in order to create the directory. If it doesn't, then it will return without creating the directory (it will not throw an exception). If `true`, then the directory hierarchy of `path` will be created if possible.
398381
func createDirectory(_ path: Path, recursive: Bool) throws {
399382
// Try to create the directory.
400-
#if os(Windows)
401383
do {
402384
return try fileManager.createDirectory(atPath: path.str, withIntermediateDirectories: recursive)
403385
} catch {
404386
throw StubError.error("Could not create directory at path '\(path.str)': \(error)")
405387
}
406-
#else
407-
let result = mkdir(path.str, S_IRWXU | S_IRWXG | S_IRWXO)
408-
409-
// If it succeeded, we are done.
410-
if result == 0 {
411-
return
412-
}
413-
414-
// If the failure was because something exists at this path, then we examine it to see whether it means we're okay.
415-
if errno == EEXIST {
416-
var destinationExists = false
417-
if isDirectory(path) {
418-
// If the item at the path is a directory, then we're good. This includes if it's a symlink which points to a directory.
419-
return
420-
}
421-
else if isSymlink(path, &destinationExists) {
422-
// If the item at the path is a symlink, then we check whether it's a broken symlink or points to something that is not a directory.
423-
if destinationExists {
424-
// The destination does exist, so it's not a directory.
425-
throw StubError.error("File is a symbolic link which references a path which is not a directory: \(path.str)")
426-
}
427-
else {
428-
// The destination does not exist - throw an exception because we have a broken symlink.
429-
throw StubError.error("File is a broken symbolic link: \(path.str)")
430-
}
431-
}
432-
else {
433-
/// The path exists but is not a directory
434-
throw StubError.error("File exists but is not a directory: \(path.str)")
435-
}
436-
}
437-
438-
// If we are recursive and not the root path, then...
439-
if recursive && !path.isRoot {
440-
// If it failed due to ENOENT (e.g., a missing parent), then attempt to create the parent and retry.
441-
if errno == ENOENT {
442-
// Attempt to create the parent.
443-
guard path.isAbsolute else {
444-
throw StubError.error("Cannot recursively create directory at non-absolute path: \(path.str)")
445-
}
446-
try createDirectory(path.dirname, recursive: true)
447-
448-
// Re-attempt creation, non-recursively.
449-
try createDirectory(path)
450-
451-
// We are done.
452-
return
453-
}
454-
455-
// If our parent is not a directory, then report that.
456-
if !isDirectory(path.dirname) {
457-
throw StubError.error("File exists but is not a directory: \(path.dirname.str)")
458-
}
459-
}
460-
461-
// Otherwise, we failed due to some other error. Report it.
462-
throw POSIXError(errno, context: "mkdir", path.str, "S_IRWXU | S_IRWXG | S_IRWXO")
463-
#endif
464388
}
465389

466390
func createTemporaryDirectory(parent: Path) throws -> Path {
@@ -562,24 +486,12 @@ class LocalFS: FSProxy, @unchecked Sendable {
562486
}
563487

564488
func remove(_ path: Path) throws {
565-
guard unlink(path.str) == 0 else {
566-
throw POSIXError(errno, context: "unlink", path.str)
567-
}
489+
try fileManager.removeItem(atPath: path.str)
568490
}
569491

570492
func removeDirectory(_ path: Path) throws {
571493
if isDirectory(path) {
572-
#if os(Windows)
573494
try fileManager.removeItem(atPath: path.str)
574-
#else
575-
var paths = [path]
576-
try traverse(path) { paths.append($0) }
577-
for path in paths.reversed() {
578-
guard SWBLibc.remove(path.str) == 0 else {
579-
throw POSIXError(errno, context: "remove", path.str)
580-
}
581-
}
582-
#endif
583495
}
584496
}
585497

@@ -608,69 +520,11 @@ class LocalFS: FSProxy, @unchecked Sendable {
608520
}
609521

610522
func touch(_ path: Path) throws {
611-
#if os(Windows)
612-
let handle: HANDLE = path.withPlatformString {
613-
CreateFileW($0, DWORD(GENERIC_WRITE), DWORD(FILE_SHARE_READ), nil,
614-
DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS), nil)
615-
}
616-
if handle == INVALID_HANDLE_VALUE {
617-
throw StubError.error("Failed to update file time")
618-
}
619-
try handle.closeAfter {
620-
var ft = FILETIME()
621-
var st = SYSTEMTIME()
622-
GetSystemTime(&st)
623-
SystemTimeToFileTime(&st, &ft)
624-
if !SetFileTime(handle, nil, &ft, &ft) {
625-
throw StubError.error("Failed to update file time")
626-
}
627-
}
628-
#else
629-
try eintrLoop {
630-
guard utimensat(AT_FDCWD, path.str, nil, 0) == 0 else {
631-
throw POSIXError(errno, context: "utimensat", "AT_FDCWD", path.str)
632-
}
633-
}
634-
#endif
523+
try fileManager.setAttributes([.modificationDate:Date()], ofItemAtPath: path.str)
635524
}
636525

637526
func setFileTimestamp(_ path: Path, timestamp: Int) throws {
638-
#if os(Windows)
639-
let handle: HANDLE = path.withPlatformString {
640-
CreateFileW($0, DWORD(GENERIC_WRITE), DWORD(FILE_SHARE_READ), nil,
641-
DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS), nil)
642-
}
643-
if handle == INVALID_HANDLE_VALUE {
644-
throw StubError.error("Failed to update file time")
645-
}
646-
try handle.closeAfter {
647-
// Number of 100ns intervals between 1601 and 1970 epochs
648-
let delta = 116444736000000000
649-
650-
let ll = UInt64((timestamp * 10000000) + delta)
651-
652-
var timeInt = ULARGE_INTEGER()
653-
timeInt.QuadPart = ll
654-
655-
var ft = FILETIME()
656-
ft.dwLowDateTime = timeInt.LowPart
657-
ft.dwHighDateTime = timeInt.HighPart
658-
if !SetFileTime(handle, nil, &ft, &ft) {
659-
throw StubError.error("Failed to update file time")
660-
}
661-
}
662-
#else
663-
try eintrLoop {
664-
#if os(Linux) || os(Android)
665-
let UTIME_OMIT = 1073741822
666-
#endif
667-
let atime = timespec(tv_sec: 0, tv_nsec: Int(UTIME_OMIT))
668-
let mtime = timespec(tv_sec: timestamp, tv_nsec: 0)
669-
guard utimensat(AT_FDCWD, path.str, [atime, mtime], 0) == 0 else {
670-
throw POSIXError(errno, context: "utimensat", "AT_FDCWD", path.str, String(timestamp))
671-
}
672-
}
673-
#endif
527+
try fileManager.setAttributes([.modificationDate:Date(timeIntervalSince1970: Double(timestamp))], ofItemAtPath: path.str)
674528
}
675529

676530
func getFileInfo(_ path: Path) throws -> FileInfo {
@@ -711,7 +565,7 @@ class LocalFS: FSProxy, @unchecked Sendable {
711565
#if os(Windows)
712566
try eintrLoop {
713567
guard stat(path.str, &buf) == 0 else {
714-
throw POSIXError(errno, context: "lstat", path.str)
568+
throw POSIXError(errno, context: "stat", path.str)
715569
}
716570
}
717571

0 commit comments

Comments
 (0)