Skip to content

Fix FileManager's extended attribute symlink handling#1623

Open
SiliconA-Z wants to merge 1 commit intoswiftlang:mainfrom
SiliconA-Z:mistake
Open

Fix FileManager's extended attribute symlink handling#1623
SiliconA-Z wants to merge 1 commit intoswiftlang:mainfrom
SiliconA-Z:mistake

Conversation

@SiliconA-Z
Copy link
Copy Markdown
Contributor

@SiliconA-Z SiliconA-Z commented Dec 5, 2025

The if statement appears inverted: setxattr follows simlinks: lsetxattr does not.

Additionally, _extendedAttributes was ignoring simlinks entirely.

Both of these issues have been addressed.

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

@jmschonfeld ping?

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

@jmschonfeld @compnerd ping?

@jmschonfeld
Copy link
Copy Markdown
Contributor

@jmschonfeld @compnerd ping?

It's on my list to circle back and review when I have a moment - no need to ping 🙂. Will be working my way through the handful of PRs open to review

@jmschonfeld
Copy link
Copy Markdown
Contributor

@swift-ci please test

Copy link
Copy Markdown
Member

@compnerd compnerd left a comment

Choose a reason for hiding this comment

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

The code change looks fine, needs fixes for the tests.

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

I fixed it, but also noticed we weren't passing followSymlinks to _extendedAttributes when the test failed on Darwin so. @compnerd

@SiliconA-Z SiliconA-Z force-pushed the mistake branch 3 times, most recently from 406328d to e1179f0 Compare December 9, 2025 21:36
@SiliconA-Z SiliconA-Z force-pushed the mistake branch 2 times, most recently from 5505996 to 0c68c6a Compare December 10, 2025 15:36
@SiliconA-Z SiliconA-Z force-pushed the mistake branch 2 times, most recently from bd02eaa to b179951 Compare December 10, 2025 17:44
@SiliconA-Z SiliconA-Z force-pushed the mistake branch 3 times, most recently from 8956f2e to 979d225 Compare December 10, 2025 21:57
@SiliconA-Z SiliconA-Z changed the title Fix FileManager extended attribute symlink handling Fix FileManager's extended attribute symlink handling Dec 10, 2025
@SiliconA-Z SiliconA-Z force-pushed the mistake branch 2 times, most recently from 037f442 to 9e17700 Compare December 10, 2025 22:35
@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

@jmschonfeld Can we please test and merge?

var extendedAttrs: [String : Data] = [:]
var current = keyList.baseAddress!
let end = keyList.baseAddress!.advanced(by: keyList.count)
let end = keyList.baseAddress!.advanced(by: size)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It looks like this was changed in a recent commit (hard to determine with force pushes rather than follow-on commits which are easier to review). Is there a reason you're switching to use the passed in size here rather than using the size of the allocation returned by the allocate function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes — the size variable gets updated by the second listxattr/extattr_list_* call to reflect the actual number of bytes written.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In that case if size is potentially different (if the file system changes and the number of attributes are removed) can we add an assertion that size <= keyList.count? In most cases I assume this shouldn't be an issue, but I'm wary that trusting this value could in some edge case inadvertently introduce a buffer overread here if size somehow becomes something larger than our allocation after it is overwritten.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If anything I'd say the opposite is more likely to happen: if keyList.count changes between allocation and read then this can happen.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think I'm fully following - in what situation would keyList.count change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe this is best for another PR. I'll revert it.

if error.code == .featureUnsupported { return }
guard let posix = error.underlying as? POSIXError else { throw error }
#if canImport(Darwin)
guard posix.code == .ENOTSUP || posix.code == .EOPNOTSUPP else { throw error }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there a reason why ENOTSUP is guarded for Darwin only here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because ENOTSUP is not an error code on Linux.

I tried and it didn't compile.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ENOTSUP is an error code on Linux as far as I can tell if I'm reading the man pages correctly, but it looks like it has the same value as EOPNOTSUPP so there's no distinction. It looks like we also don't have a POSIXErrorCode.EOPNOTSUP for it which is likely the compilation error you were seeing (either intentionally because it's not necessary or just because nobody has ended up needing it). If that's the case, can you leave a comment about why this distinction is here so it's clear that ENOTSUP is not distinct from EOPNOTSUPP on non-Darwin platforms?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

I think this is as heavy as change now as it should get. We should merge this

I'll save the count and size replacement for another PR. That has been reverted back @jmschonfeld

@jmschonfeld
Copy link
Copy Markdown
Contributor

@swift-ci test

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

Test failed because of a permission error on linux. @jmschonfeld what to do? How am I supposed to fix that in the test

@SiliconA-Z SiliconA-Z force-pushed the mistake branch 4 times, most recently from fd46df6 to 3adb585 Compare December 12, 2025 18:26
@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

Ugh I had to fix the tests to use rawvalue. The previous version passed locally, but not on the CI. My bad...

The if statement appears inverted:  setxattr follows simlinks: lsetxattr does not.

Additionally, _extendedAttributes was ignoring simlinks entirely.

Both of these issues have been addressed.
@jmschonfeld
Copy link
Copy Markdown
Contributor

@swift-ci please test

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

It passed @jmschonfeld

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

@jmschonfeld Is this good to go?

@jmschonfeld
Copy link
Copy Markdown
Contributor

Apologies for the delay here. I was thinking on this behavior a bit more and I realized I may have misunderstood the base of the problem here. Today it looks like the behavior is reading xattrs always follows symlinks and setting xattrs does not follow symlinks on Darwin but does follow symlinks on Linux, all because it either ignored followSymlinks: or inverted it. Your change originally seemed reasonable which updates the functions to respect the followSymlinks: parameter which is always passed in as false meaning that these will now always not follow symlinks.

However, the rest of setAttributes/attributesForItem where these are called from will always traverse symlinks. In general, most of the FileManager APIs traverse symlinks (compared to URL APIs which typically do not traverse symlinks). So I think while this change is headed in the right direction by respecting the followSymlinks: flag, I think it uncovers another issue: that we are passing followSymlinks: false. This corrects the behavior of this function, but due to this I think it will introduce an inconsistency in APIs like setAttributes/attributesForItem.

What do you think about changing the call sites to followSymlinks: true now that the function behaves correctly? That means the behavior will be consistent with the rest of the FileManager APIs, but will require updating the tests. Thoughts?

@SiliconA-Z
Copy link
Copy Markdown
Contributor Author

I am for it

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.

3 participants