Skip to content

Commit 10bc20b

Browse files
committed
Foundation: make FileManager build on Windows again
Windows should use `SetFileTime` for adjusting the file times. More importantly, it does not support `time_t` so the signature of the POSIX functions are different. Factor out the file time adjustment into a helper that is implemented in the POSIX/Win32 layer and use that instead.
1 parent 0186a12 commit 10bc20b

File tree

4 files changed

+61
-28
lines changed

4 files changed

+61
-28
lines changed

Diff for: Foundation/FileManager+POSIX.swift

+26
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,32 @@ internal func _contentsEqual(atPath path1: String, andPath path2: String) -> Boo
11591159
return nil
11601160
}
11611161
}
1162+
1163+
internal func _updateTimes(atPath path: String, withFileSystemRepresentation fsr: UnsafePointer<Int8>, creationTime: Date? = nil, accessTime: Date? = nil, modificationTime: Date? = nil) throws {
1164+
let stat = try _lstatFile(atPath: path, withFileSystemRepresentation: fsr)
1165+
1166+
let accessDate = accessTime ?? stat.lastAccessDate
1167+
let modificationDate = modificationTime ?? stat.lastModificationDate
1168+
1169+
let (accessTimeSince1970Seconds, accessTimeSince1970FractionsOfSecond) = modf(accessDate.timeIntervalSince1970)
1170+
let accessTimeval = timeval(tv_sec: time_t(accessTimeSince1970Seconds), tv_usec: suseconds_t(1.0e9 * accessTimeSince1970FractionsOfSecond))
1171+
1172+
let (modificationTimeSince1970Seconds, modificationTimeSince1970FractionsOfSecond) = modf(modificationDate.timeIntervalSince1970)
1173+
let modificationTimeval = timeval(tv_sec: time_t(modificationTimeSince1970Seconds), tv_usec: suseconds_t(1.0e9 * modificationTimeSince1970FractionsOfSecond))
1174+
1175+
let array = [accessTimeval, modificationTimeval]
1176+
let errnoValue = array.withUnsafeBufferPointer { (bytes) -> Int32? in
1177+
if utimes(fsr, bytes.baseAddress) < 0 {
1178+
return errno
1179+
} else {
1180+
return nil
1181+
}
1182+
}
1183+
1184+
if let error = errnoValue {
1185+
throw _NSErrorWithErrno(error, reading: false, path: path)
1186+
}
1187+
}
11621188
}
11631189

11641190
extension FileManager.NSPathDirectoryEnumerator {

Diff for: Foundation/FileManager+Win32.swift

+27
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,33 @@ extension FileManager {
605605
return temp._bridgeToObjectiveC().appendingPathComponent(dest)
606606
}
607607

608+
internal func _updateTimes(atPath path: String,
609+
withFileSystemRepresentation fsr: UnsafePointer<Int8>,
610+
creationTime: Date? = nil,
611+
accessTime: Date? = nil,
612+
modificationTime: Date? = nil) throws {
613+
let stat = try _lstatFile(atPath: path, withFileSystemRepresentation: fsr)
614+
615+
var atime: FILETIME =
616+
FILETIME(from: time_t((accessTime ?? stat.lastAccessDate).timeIntervalSince1970))
617+
var mtime: FILETIME =
618+
FILETIME(from: time_t((modificationTime ?? stat.lastModificationDate).timeIntervalSince1970))
619+
620+
let hFile: HANDLE = String(utf8String: fsr)!.withCString(encodedAs: UTF16.self) {
621+
CreateFileW($0, DWORD(GENERIC_WRITE), DWORD(FILE_SHARE_WRITE),
622+
nil, DWORD(OPEN_EXISTING), 0, nil)
623+
}
624+
if hFile == INVALID_HANDLE_VALUE {
625+
throw _NSErrorWithWindowsError(GetLastError(), reading: true)
626+
}
627+
defer { CloseHandle(hFile) }
628+
629+
if !SetFileTime(hFile, nil, &atime, &mtime) {
630+
throw _NSErrorWithWindowsError(GetLastError(), reading: false)
631+
}
632+
633+
}
634+
608635
internal class NSURLDirectoryEnumerator : DirectoryEnumerator {
609636
var _options : FileManager.DirectoryEnumerationOptions
610637
var _errorHandler : ((URL, Error) -> Bool)?

Diff for: Foundation/FileManager.swift

+3-25
Original file line numberDiff line numberDiff line change
@@ -371,32 +371,10 @@ open class FileManager : NSObject {
371371
}
372372
#endif
373373
}
374-
375-
// Set dates as the very last step, to avoid other operations overwriting these values:
374+
376375
if newModificationDate != nil || newAccessDate != nil {
377-
let stat = try _lstatFile(atPath: path, withFileSystemRepresentation: fsRep)
378-
379-
let accessDate = newAccessDate ?? stat.lastAccessDate
380-
let modificationDate = newModificationDate ?? stat.lastModificationDate
381-
382-
let (accessTimeSince1970Seconds, accessTimeSince1970FractionsOfSecond) = modf(accessDate.timeIntervalSince1970)
383-
let accessTimeval = timeval(tv_sec: time_t(accessTimeSince1970Seconds), tv_usec: suseconds_t(1.0e9 * accessTimeSince1970FractionsOfSecond))
384-
385-
let (modificationTimeSince1970Seconds, modificationTimeSince1970FractionsOfSecond) = modf(modificationDate.timeIntervalSince1970)
386-
let modificationTimeval = timeval(tv_sec: time_t(modificationTimeSince1970Seconds), tv_usec: suseconds_t(1.0e9 * modificationTimeSince1970FractionsOfSecond))
387-
388-
let array = [accessTimeval, modificationTimeval]
389-
let errnoValue = array.withUnsafeBufferPointer { (bytes) -> Int32? in
390-
if utimes(fsRep, bytes.baseAddress) < 0 {
391-
return errno
392-
} else {
393-
return nil
394-
}
395-
}
396-
397-
if let error = errnoValue {
398-
throw _NSErrorWithErrno(error, reading: false, path: path)
399-
}
376+
// Set dates as the very last step, to avoid other operations overwriting these values:
377+
try _updateTimes(atPath: path, withFileSystemRepresentation: fsRep, accessTime: newAccessDate, modificationTime: newModificationDate)
400378
}
401379
})
402380
}

Diff for: Foundation/NSURL.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -1799,8 +1799,10 @@ fileprivate extension URLResourceValuesStorage {
17991799
result[key] = try attribute(.size)
18001800
case .totalFileAllocatedSizeKey: fallthrough // FIXME: This should add the size of any metadata.
18011801
case .fileAllocatedSizeKey:
1802+
#if !os(Windows)
18021803
let stat = try urlStat()
18031804
result[key] = Int(stat.st_blocks) * Int(stat.st_blksize)
1805+
#endif
18041806
case .isAliasFileKey:
18051807
// swift-corelibs-foundation does not support aliases and bookmarks.
18061808
break
@@ -2042,7 +2044,7 @@ extension stat {
20422044
#elseif os(Android)
20432045
return Date(timeIntervalSince1970: st_mtime, nanoseconds: st_mtime_nsec)
20442046
#elseif os(Windows)
2045-
return Date(timeIntervalSince1970: st_mtime)
2047+
return Date(timeIntervalSince1970: TimeInterval(st_mtime))
20462048
#else
20472049
return Date(timespec: st_mtim)
20482050
#endif
@@ -2054,7 +2056,7 @@ extension stat {
20542056
#elseif os(Android)
20552057
return Date(timeIntervalSince1970: st_atime, nanoseconds: st_atime_nsec)
20562058
#elseif os(Windows)
2057-
return Date(timeIntervalSince1970: st_atime)
2059+
return Date(timeIntervalSince1970: TimeInterval(st_atime))
20582060
#else
20592061
return Date(timespec: st_atim)
20602062
#endif
@@ -2066,7 +2068,7 @@ extension stat {
20662068
#elseif os(Android)
20672069
return Date(timeIntervalSince1970: st_ctime, nanoseconds: st_ctime_nsec)
20682070
#elseif os(Windows)
2069-
return Date(timeIntervalSince1970: st_ctime)
2071+
return Date(timeIntervalSince1970: TimeInterval(st_ctime))
20702072
#else
20712073
return Date(timespec: st_ctim)
20722074
#endif

0 commit comments

Comments
 (0)