Skip to content

Fix Windows symlink handling in FileManager APIs #858

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

Merged
merged 2 commits into from
Aug 16, 2024

Conversation

jmschonfeld
Copy link
Contributor

While getting SCL-F tests up and running on Windows, I noticed that those tests expose a handful of behavioral changes that swift-foundation made compared to swift-corelibs-foundation specifically around handling symlinks on Windows. In particular:

  • contentsEqual(atPath:andPath:) would compare contents at the destination of symlinks rather than at the symlinks themselves
  • copyItem(atPath:toPath:) would fail when copying symlinks
  • copyItem(atPath:toPath:) would follow symlinks to directories and copy the directory contents rather than copying the symlinks themselves

This PR addresses these issues and adds a handful of unit tests to validate all of these behaviors.

Resolves #845

@jmschonfeld
Copy link
Contributor Author

@swift-ci please test

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

if FAILED(CopyFile2(pwszSource, pwszDestination, &ExtendedParameters)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @jakepetroules mentioned in the GitHub issue - it seems there may be an issue with CopyFile2 itself here. I was unable to figure out the issue, so instead we now just handle symlink logic separately by reading the symlink's destination and creating a new symlink at the copy destination (which is what swift-corelibs-foundation roughly did)

@jmschonfeld
Copy link
Contributor Author

@swift-ci please test

@@ -817,7 +817,16 @@ enum _FileOperations {
guard delegate.shouldPerformOnItemAtPath(src, to: dst) else { return }

try dst.withNTPathRepresentation { pwszDestination in
if faAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY {
// Check for reparse points first because symlinks to directories are reported as both reparse points and directories, and we should copy the symlink not the contents of the linked directory
if faAttributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT == FILE_ATTRIBUTE_REPARSE_POINT {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a non-symlinked directory happen to satisfy this constraint as well, and is that the desired behavior? By reading at this code, I'm not sure if fileManager.createSymbolicLink would succeed since it's not a symlink. And if it fails, we'd throw instead of proceeding to copy the content of the directory. Is that ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah plain directories won't have this flag set, but other types of reparse points that aren't supported by destinationOfSymbolicLink would likely fail here - I think we can address that later since most of the FileManager code in general likely doesn't do well with other types of reparse points

Copy link
Contributor

@itingliu itingliu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a question, but I'm not sure if that's really a supported scenario. I don't mind addressing it later if that turns out to be an issue.

@jmschonfeld jmschonfeld merged commit e555c62 into swiftlang:main Aug 16, 2024
3 checks passed
@jmschonfeld jmschonfeld deleted the windows/symlink-handling branch August 16, 2024 17:41
jmschonfeld added a commit to jmschonfeld/swift-foundation that referenced this pull request Aug 16, 2024
* Fix Windows symlink handling in FileManager APIs

* Address feedback
jmschonfeld added a commit that referenced this pull request Aug 16, 2024
* Fix Windows symlink handling in FileManager APIs

* Address feedback
cthielen pushed a commit to cthielen/swift-foundation that referenced this pull request Nov 8, 2024
* Fix Windows symlink handling in FileManager APIs

* Address feedback
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Copying a symlink always fails on Windows
3 participants