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
[SR-13300] Attempt to use FileManager.setAttributes() to set dates, fails #3974
Comments
This is Can you see if there's an underlying error? if let cocoaError = error as? CocoaError,
let underlyingError = cocoaError.underlying {
print(underlyingError)
} |
Comment by Nicholas Pisarro Jr (JIRA) My code now reads:
I got: ...$ command/1stSwift -r -v -include OSC* ../4th ../4thRestore
0 Additions, 2 Changes, 0 Deletions
Restore files (y/n)? y
File Changed: /OSC.log
Failure to set modify date of: /home/pi/projects/4thRestore/OSC.log The operation could not be completed. (NSCocoaErrorDomain error 512.)
No underlying error found.
File Changed: /OSCMultiMonitor.log
Failure to set modify date of: /home/pi/projects/4thRestore/OSCMultiMonitor.log The operation could not be completed. (NSCocoaErrorDomain error 512.)
No underlying error found. Nick |
OK, I thought there might be an underlying POSIX error from the utimes call, via the _NSErrorWithErrno function. But the failure could be from earlier than that. Setting the |
The Another way to check for the underlying error is with the if let nsError = error as? NSError,
let underlyingError = nsError.userInfo[NSUnderlyingErrorKey] {
print(underlyingError)
} If there's still no underlying POSIX error, then it's likely to be an issue with the Raspberry Pi fork of swift-corelibs-foundation. |
Comment by Nicholas Pisarro Jr (JIRA) That code shows an underlying error: command/1stSwift -r -v -include OSC* ../4th ../4thRestore
0 Additions, 2 Changes, 0 Deletions
Restore files (y/n)? y
File Changed: /OSC.log
Failure to set modify date of: /home/pi/projects/4thRestore/OSC.log The operation could not be completed. (NSCocoaErrorDomain error 512.)
Error Domain=NSPOSIXErrorDomain Code=22 "Invalid argument"
File Changed: /OSCMultiMonitor.log
Failure to set modify date of: /home/pi/projects/4thRestore/OSCMultiMonitor.log The operation could not be completed. (NSCocoaErrorDomain error 512.)
Error Domain=NSPOSIXErrorDomain Code=22 "Invalid argument" How might I set the modify date directly using a posix call? |
So the issue appears to be that utimes is failing with POSIX errno 22 (EINVAL). The documentation for POSIX is here, and the page for futimens/utimensat/utimes is here.
If you need more advice, try the Using Swift category of the forums. |
Comment by Bridger Maxwell (JIRA) Did the posix call work for setting the modification date? Is there a simpler way to set the modification date to the current time? |
Comment by Nicholas Pisarro Jr (JIRA) Haven't tried it. I missed the notification for 14 Sep 2020 9:04 PM comment. I'll try it in the next few days. |
Comment by Nicholas Pisarro Jr (JIRA) Using Posix seems to work! This code works on both Mac & Raspian… //
// SnapPosix.swift
// 1stSwift
//
// Created by Nicholas Pisarro on 10/15/20.
//
import Foundation
#if MACISH
import Darwin
import Darwin.POSIX
#else
import Glibc
#endif
// import Darwin.POSIX.fcntl
public class MyPosixFuncs
{
public static func newModifyDate(_ url:NSURL, newModifyDate:NSDate)
{
let filepath = UnsafeMutablePointer<Int8>.allocate(capacity: 512)
let result = url.getFileSystemRepresentation(filepath, maxLength: 512)
if result
{
let fd = open(filepath, O_RDONLY)
if fd > 0
{
#if !MACISH
let NSEC_PER_SEC:UInt64 = 1000000000
#endif
// Come up with a timespec.
let now = newModifyDate.timeIntervalSince1970
let nowWholeSecsFloor = floor(now)
let nowNanosOnly = now - nowWholeSecsFloor
let nowNanosFloor = floor(nowNanosOnly * Double(NSEC_PER_SEC))
let timish = UnsafeMutablePointer<timespec>.allocate(capacity: 2)
timish[0] = timespec(tv_sec: Int(nowWholeSecsFloor),
tv_nsec: Int(nowNanosFloor))
timish[1] = timespec(tv_sec: Int(nowWholeSecsFloor),
tv_nsec: Int(nowNanosFloor))
var result:Int32 = -2
#if MACISH
if #available(OSX 10.13, *) {
result = futimens(fd, timish)
}
#else
result = futimens(fd, timish)
#endif
if result != 0
{
let path = url.path
print("snap: Failed \(result): Trying to set dates of \(path!).")
}
timish.deallocate()
close(fd)
}
}
filepath.deallocate()
}
}
More testing is required. (This took about 8 hours to develop, what with figuring out UnsafeMutablePointer, how to navigate the posix documentation and getting everything in the right format.) |
If you want to set the file timestamps to the current time, the simplest method is one of: import Foundation
extension URL {
func touch_1() throws {
try withUnsafeFileSystemRepresentation { path in
guard 0 == utimes(path, nil) else {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
}
}
@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
func touch_2() throws {
try withUnsafeFileSystemRepresentation { path in
guard 0 == utimensat(AT_FDCWD, path, nil, 0) else {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
}
}
}
|
Comment by Nicholas Pisarro Jr (JIRA) This is the Restore code of an incremental backup program I'm writing. It doesn't use hard links like the Unix version or Time Machine. The label "now" is actually mislabeled, since I want to restore the original date of the backed-up file. I'll relabel it. I had trouble with {{ withUnsafeFileSystemRepresentation. #if canImport(Darwin)}} seems more robust than what I'm doing. But the real proper solution is for |
Until apple/swift-corelibs-foundation#2904 is merged, and somehow available on Raspberry Pi OS, you can avoid this bug by rounding dates to the nearest second. extension Date {
func rounded_SR_13300(
_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero
) -> Self {
Self(timeIntervalSince1970: timeIntervalSince1970.rounded(rule))
}
mutating func round_SR_13300(
_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero
) {
self = rounded_SR_13300(rule)
}
}
extension URLResourceValues {
func rounded_SR_13300(
_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero
) -> Self {
var values = self
values.contentAccessDate?.round_SR_13300(rule)
values.contentModificationDate?.round_SR_13300(rule)
return values
}
mutating func round_SR_13300(
_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero
) {
self = rounded_SR_13300(rule)
}
} Unlike the extension URL {
func resourceValues_SR_13300() throws -> URLResourceValues {
try resourceValues(forKeys: [
.contentAccessDateKey,
.contentModificationDateKey,
])
}
mutating func setResourceValues_SR_13300(
_ values: URLResourceValues
) throws {
try setResourceValues(values.rounded_SR_13300())
}
} |
I'll mark this issue as resolved: apple/swift-corelibs-foundation#2904 has been merged. Swift 5.4 will likely be released in March 2021, if it follows the pattern of previous releases. |
Environment
Raspian Linux Swift version 5.1.5-v0.1
Additional Detail from JIRA
md5: e2d8e2a7325bb243150880bad357f02f
Issue Description:
When I use the code below to try to set the Modify [or creation] date of a file, it fails with the message:
Failure to set modify date of: /home/pi/projects/4thRestore/OSC.log The operation could not be completed. (NSCocoaErrorDomain error 512.)
This works under MacOS but not with Raspian Linux. Note: running under Sudo does not help.
do {
{{ try mgr.copyItem(at: file.fileID!, to: destUrl)}}
} catch {
{{ print("File copy error during restore! " + error.localizedDescription)}}
{{ return false}}
{{}}}
// Apply the saved modify date to the file since it may have come from an incompatible file system.
if !self.setModifyDate(url: destUrl,
{{ modifyDate: file.lastModifyDate)}}
{
{{ result = false;}}
{{}}}
where...
{{ public func setModifyDate(url:URL,}}
{{ modifyDate newDate:Date) -> Bool}}
{
...
{{ let attributes = [FileAttributeKey.modificationDate: newDate]}}
{{ do {}}
{{ try mgr.setAttributes(attributes, ofItemAtPath: url.path)}}
{{ } catch {}}
{{ print("Failure to set modify date of: " + url.path + " " + error.localizedDescription)}}
{{ }}}
...
}
The text was updated successfully, but these errors were encountered: