-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix for NSKeyedArchiver.archiveRootObject api failure on Linux #1464
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
Conversation
Foundation/NSKeyedArchiver.swift
Outdated
| /// - path: The path of the file in which to write the archive. | ||
| /// - Returns: `true` if the operation was successful, otherwise `false`. | ||
| open class func archiveRootObject(_ rootObject: Any, toFile path: String) -> Bool { | ||
| var filePath = "/" + path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Path input like "Users/sdsfd/test.txt" is accepted on Darwin and a "/" is prepended to the path. Hence, prepending the path since a double "/" will not impact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the only normalisation that Darwin does? It seems unlikely to me... I would guess that there is more to it than this. Also, why not only prepend if required - allowing a double // seems a bit hacky.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think prepending with / is valid here. If I try to write to a relative path (say, "./my-archive"), we'd be making it absolute ("/./my-archive"), which is not correct.
Why is the current behavior a failure on Linux? What paths are failing? Note that in Darwin Foundation, we don't modify this path at all; the underlying code for creating the temporary file takes care of things.
| } | ||
|
|
||
| internal func _NSCreateTemporaryFile(_ filePath: String) throws -> (Int32, String) { | ||
| let template = "." + filePath + ".tmp.XXXXXX" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the "." in place, the fd returned by system library call mkstemp() is always -1 and the temporary file creation always fails. Also, since the file path sent to _NSCreateTemporaryFile() is absolute, making it relative here does not seems right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file path to _NSCreateTemporaryFile should not need to be absolute; we're indeed using it as a template to create a new file, so we should be able to make whatever modifications necessary internally to make it work.
|
@swift-ci please test |
|
@swift-ci please test |
|
The failure doesn't appear to be related to the change. I'm firing up another test, just in case we have some intermittent issue at hand. @swift-ci please test |
|
@swift-ci please test |
|
cc @itaiferber |
|
@itaiferber Could you please review this PR. |
|
@mamabusi I must've missed this; looking now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for taking the time to put this PR together, but I think this is fixing a problem at the wrong level. Call sites of _NSCreateTemporaryFile should not need to modify the path they pass in to get it to work; instead, we should figure out why the result of
let filePath = NSTemporaryDirectory() + "testdir\(NSUUID().uuidString)"
is not valid to write into when passing that in to _NSCreateTemporaryFile. Presumably there are other usages of _NSCreateTemporaryFile that would be hitting the same problem; the solution would be to fix how _NSCreateTemporaryFile creates a new file, not all of the call sites.
I don't have a Linux box to test on at the moment. What is the result of NSTemporaryDirectory() + "testdir\(NSUUID().uuidString)" on your machine? And what would the resulting path from _NSCreateTemporaryFile be? If it indeed is not a writable path, we need to fix that.
Foundation/NSKeyedArchiver.swift
Outdated
| /// - path: The path of the file in which to write the archive. | ||
| /// - Returns: `true` if the operation was successful, otherwise `false`. | ||
| open class func archiveRootObject(_ rootObject: Any, toFile path: String) -> Bool { | ||
| var filePath = "/" + path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think prepending with / is valid here. If I try to write to a relative path (say, "./my-archive"), we'd be making it absolute ("/./my-archive"), which is not correct.
Why is the current behavior a failure on Linux? What paths are failing? Note that in Darwin Foundation, we don't modify this path at all; the underlying code for creating the temporary file takes care of things.
| } | ||
|
|
||
| internal func _NSCreateTemporaryFile(_ filePath: String) throws -> (Int32, String) { | ||
| let template = "." + filePath + ".tmp.XXXXXX" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file path to _NSCreateTemporaryFile should not need to be absolute; we're indeed using it as a template to create a new file, so we should be able to make whatever modifications necessary internally to make it work.
|
@itaiferber Thanks for taking time and looking into this PR.
The above internal function is called only from the function Here’s the result of
The above result looks fine and there are no issues there. When I pass the resulting filePath What is happening internally is: Now the path looks like this: and then when the system call “mkstemp(&buf)” is made, the fd returned is always “-1” causing a failure in temporary file creation. Is there a reason why the function |
|
@mamabusi I agree in that I don't think that it should make the path relative; it doesn't do that on the Darwin side AFAICT. I think it's worth trying to figure out from the git history why (if for any reason) it was there in the first place, especially if |
|
@itaiferber On the other aspect, the reason I decided to prefix "/" to the Without adding the prefix of "/" , the above test-cases fail on Linux. This test-case: fails on both Darwin and Linux. The behaviour is the same even if Linux prefixes "/", i.e. we are not deviating from any of the behaviour of Darwin for all the above test-cases. If there is something more I need to consider, please let me know. |
|
@mamabusi Thanks for taking the time to uncover the history of this! It sounds like removing the preceding Regarding prepending import Foundation
let path = "Users/itai/Desktop/../Documents/test.txt"
let url = URL(fileURLWithPath: path)
print(url.path)
if NSKeyedArchiver.archiveRootObject("Hello, world!", toFile: path) {
print("Write succeeded")
} else {
print("Write failed")
}When I run the above from my home directory ( When I run it from In fact, that can only succeed when run when the current working directory is Given that, I don't think that making the path absolute by forcibly rooting it against |
0dccd4e to
77e436d
Compare
|
@itaiferber Thank you! I understand and agree with you on "not making the path absolute". Have made the necessary changes on the PR. |
|
@swift-ci please test |
|
@mamabusi Thanks for taking the time to fix this up! LGTM |
|
@swift-ci please test and merge |
1 similar comment
|
@swift-ci please test and merge |
Test case:
Expected Result:
trueActual result on Linux:
falseActual result on Darwin:
true