Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,15 @@ extension _FileManagerImpl {
#else
var result: Int32
if followSymLinks {
result = lsetxattr(path, key, buffer.baseAddress!, buffer.count, 0)
} else {
result = setxattr(path, key, buffer.baseAddress!, buffer.count, 0)
} else {
result = lsetxattr(path, key, buffer.baseAddress!, buffer.count, 0)
}
#endif

#if os(macOS) && FOUNDATION_FRAMEWORK
// if setxaddr failed and its a permission error for a sandbox app trying to set quaratine attribute, ignore it since its not
// permitted, the attribute will be put on the file by the quaratine MAC hook
// if setxattr failed and its a permission error for a sandbox app trying to set quarantine attribute, ignore it since its not
// permitted, the attribute will be put on the file by the quarantine MAC hook
if result == -1 && errno == EPERM && _xpc_runtime_is_app_sandboxed() && strcmp(key, "com.apple.quarantine") == 0 {
return
}
Expand Down
37 changes: 37 additions & 0 deletions Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,43 @@ private struct FileManagerTests {
}
}

@Test func extendedAttributesDoNotFollowSymlinksWhenSetting() async throws {
#if os(Windows) || os(WASI) || os(OpenBSD) || canImport(Android)
throw Skip("Extended attributes not supported on this platform")
#else
let xattrKey = FileAttributeKey("NSFileExtendedAttributes")
#if canImport(Darwin)
let attrName = "com.swiftfoundation.symlinktest"
#elseif os(Linux)
// Linux requires the user.* namespace prefix for regular files
let attrName = "user.swiftfoundation.symlinktest"
#else
let attrName = "swiftfoundation.symlinktest"
#endif
let attrValue = Data([0xAA, 0xBB, 0xCC])

try await FilePlayground {
File("target", contents: Data("payload".utf8))
SymbolicLink("link", destination: "target")
}.test { fileManager in
do {
try fileManager.setAttributes([xattrKey: [attrName: attrValue]], ofItemAtPath: "link")
} catch let error as CocoaError where error.code == .featureUnsupported {
throw Skip("Extended attributes not supported on this filesystem: \(error)")
}

let linkAttrs = try fileManager.attributesOfItem(atPath: "link")
let targetAttrs = try fileManager.attributesOfItem(atPath: "target")

let linkXattrs = linkAttrs[xattrKey] as? [String : Data]
let targetXattrs = targetAttrs[xattrKey] as? [String : Data]

#expect(linkXattrs?[attrName] == attrValue, "xattr should be applied to the symlink itself")
#expect(targetXattrs?[attrName] == nil, "setAttributes must not follow symlinks when setting extended attributes")
}
#endif
}

#if !canImport(Darwin) || os(macOS)
@Test func currentUserHomeDirectory() async throws {
let userName = ProcessInfo.processInfo.userName
Expand Down