Skip to content

Copying a symlink always fails on Windows #845

@jakepetroules

Description

@jakepetroules
Contributor

Consider the following code:

let fm = FileManager()
do {
    try fm.createFile(atPath: "test", contents: nil)
    try fm.linkItem(atPath: "test", toPath: "test2")
    precondition(!fm.fileExists(atPath: "test3"))
    try fm.copyItem(atPath: "test2", toPath: "test3")
} catch {
    print("\(error)")
    exit(EXIT_FAILURE)
}

Running this will produce the following filesystem contents:

E:\symlinktest>dir
 ...

 Directory of E:\symlinktest

...
08/12/2024  02:16 PM                 0 test
08/12/2024  02:16 PM    <SYMLINK>      test2 [test]
08/12/2024  02:16 PM                 0 test3
...

AND will throw this error from copyItem:

CocoaError(code: FoundationEssentials.CocoaError.Code(rawValue: 516), userInfo: ["NSFilePath": AnyHashable("test2"), "NSUnderlyingError": AnyHashable(FoundationEssentials.Win32Error(code: 183))])

Somehow, copyItem ALWAYS fails with ERROR_ALREADY_EXISTS when copying a symlink, even though the destination definitely did not exist prior to the call.

Swift version 6.0-dev (LLVM 31f0cdc6d5ff8a6, Swift aa0786cc7b3190c)
Microsoft Windows [Version 10.0.22631.3958]

Activity

jakepetroules

jakepetroules commented on Aug 12, 2024

@jakepetroules
ContributorAuthor

This also appears to be a regression as I don't recall observing this fail with the July snapshot of Swift 6 (but is failing with the August 7 snapshot)

jakepetroules

jakepetroules commented on Aug 12, 2024

@jakepetroules
ContributorAuthor

Also reproduces with the copyItem call replaced with the same Win32 APIs that swift-foundation is calling internally:

var ExtendedParameters: COPYFILE2_EXTENDED_PARAMETERS = .init()
ExtendedParameters.dwSize = DWORD(MemoryLayout<COPYFILE2_EXTENDED_PARAMETERS>.size)
ExtendedParameters.dwCopyFlags = DWORD(COPY_FILE_FAIL_IF_EXISTS | COPY_FILE_COPY_SYMLINK | COPY_FILE_NO_BUFFERING | COPY_FILE_OPEN_AND_COPY_REPARSE_POINT)

"test2".withCString(encodedAs: UTF16.self) { pwszSource in
    "test3".withCString(encodedAs: UTF16.self) { pwszDestination in
        let x: HRESULT = CopyFile2(pwszSource, pwszDestination, &ExtendedParameters)
        print(UInt32(bitPattern: x))
    }
}

This returns HRESULT 0x80070050 (The file exists)

So I suspect the dwCopyFlags might be invalid. Some potentially relevant docs: https://learn.microsoft.com/en-us/windows/win32/fileio/symbolic-link-effects-on-file-systems-functions

@compnerd Any idea what might be wrong here?

added a commit that references this issue on Aug 12, 2024
added a commit that references this issue on Aug 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @jakepetroules

      Issue actions

        Copying a symlink always fails on Windows · Issue #845 · swiftlang/swift-foundation