Open Lukasa opened 1 year ago
Any update on this?
I just ran into this today wasting some time trying to find out why replaceItemAt(_:withItemAt:)
deletes the target item but leaves us with the item we want to move to the other path...
Can reproduce this in docker on swift:5.9-jammy
, swift:5.10-jammy
and swiftlang/swift:nightly-6.0-jammy
import Foundation
let testDirectory = "/tmp/test"
FileManager.default.createDirectory(atPath: testDirectory, withIntermediateDirectories: true)
print(FileManager.default.contentsOfDirectory(atPath: testDirectory))
// []
let target="\(testDirectory)/replace_me.txt"
let source = "\(testDirectory)/will_replace_you.txt"
FileManager.default.createFile(atPath: target, contents: nil)
FileManager.default.createFile(atPath: source, contents: nil)
print(FileManager.default.contentsOfDirectory(atPath: testDirectory))
// [replace_me.txt", "will_replace_you.txt"]
do {
try FileManager.default.replaceItemAt(URL(fileURLWithPath: target), withItemAt: URL(fileURLWithPath: source))
} catch {
print(error)
// Error Domain=NSCocoaErrorDomain Code=4 "The file doesn’t exist."
}
print(FileManager.default.contentsOfDirectory(atPath: testDirectory))
// ["will_replace_you.txt"]
PR at #4656.
Here's a simple test file:
When run in Ubuntu 20.04 with Swift 5.7, this prints the following output:
On Ubuntu 18.04 with Swift 5.7 this produces:
In both cases the test fails.
This appears to be for different reasons on both platforms. If we find a copy of
renameat2
then we will callrenameat2
with the flagRENAME_EXCHANGE
. This is a strange choice: no other platform appears to implementreplaceItemAt(_:with:)
with the semantic of exchanging the two files, not even the other code paths in this file. If that's not present, we callrenameat
and then, if the.usingNewMetadataOnly
option is not present, we will attempt to set the attributes to reapply on the old path, whichrenameat
just deleted.In general there appear to be a number of bugs in rename handling that have slipped through the net here. Given that the exchange semantic of
renameat2
is not maintained in any other code path or platform, I propose deleting that code altogether and falling back to regular oldrenameat
, as well as fixing the issues in the implementation.