@@ -1046,6 +1046,60 @@ private struct FileManagerTests {
10461046 }
10471047 }
10481048
1049+ // Extended attributes are not supported on all platforms
1050+ #if !os(Windows) && !os(WASI) && !os(OpenBSD) && !canImport(Android)
1051+ @Test func extendedAttributesOnSymlinks( ) async throws {
1052+ let xattrKey = FileAttributeKey ( " NSFileExtendedAttributes " )
1053+ #if os(Linux)
1054+ // Linux requires the user.* namespace prefix for regular files
1055+ let attrName = " user.swift.foundation.symlinktest "
1056+ #else
1057+ let attrName = " org.swift.foundation.symlinktest "
1058+ #endif
1059+ let attrValue = Data ( [ 0xAA , 0xBB , 0xCC ] )
1060+ let attrValue2 = Data ( [ 0xDD , 0xEE , 0xFF ] )
1061+
1062+ try await FilePlayground {
1063+ File ( " target " , contents: Data ( " payload " . utf8) )
1064+ SymbolicLink ( " link " , destination: " target " )
1065+ } . test { fileManager in
1066+ // First, validate that reading the attribute from the link does not read the value from the target
1067+ do {
1068+ try fileManager. setAttributes ( [ xattrKey: [ attrName: attrValue] ] , ofItemAtPath: " target " )
1069+ let targetAttrs = try fileManager. attributesOfItem ( atPath: " target " )
1070+ let targetXAttrs = targetAttrs [ xattrKey] as? [ String : Data ]
1071+ #expect( targetXAttrs ? [ attrName] == attrValue)
1072+
1073+ let linkAttrs = try fileManager. attributesOfItem ( atPath: " link " )
1074+ let linkXAttrs = linkAttrs [ xattrKey] as? [ String : Data ]
1075+ #expect( linkXAttrs ? [ attrName] == nil )
1076+ }
1077+
1078+ // Attempt to set xattrs on the symlink
1079+ #if os(Linux)
1080+ // On Linux, user xattrs cannot be set on symlinks
1081+ #expect( throws: CocoaError . self) {
1082+ try fileManager. setAttributes ( [ xattrKey: [ attrName: attrValue2] ] , ofItemAtPath: " link " )
1083+ }
1084+ let expectedValue : Data ? = nil
1085+ #else
1086+ try fileManager. setAttributes ( [ xattrKey: [ attrName: attrValue2] ] , ofItemAtPath: " link " )
1087+ let expectedValue : Data ? = attrValue2
1088+ #endif
1089+
1090+ // Ensure that reading back the xattr of the link produces the expected value
1091+ let linkAttrs = try fileManager. attributesOfItem ( atPath: " link " )
1092+ let linkXAttrs = linkAttrs [ xattrKey] as? [ String : Data ]
1093+ #expect( linkXAttrs ? [ attrName] == expectedValue)
1094+
1095+ // Ensure that setting the xattr on the link did not set the xattr on the target
1096+ let targetAttrs = try fileManager. attributesOfItem ( atPath: " target " )
1097+ let targetXAttrs = targetAttrs [ xattrKey] as? [ String : Data ]
1098+ #expect( targetXAttrs ? [ attrName] == attrValue)
1099+ }
1100+ }
1101+ #endif
1102+
10491103 #if !canImport(Darwin) || os(macOS)
10501104 @Test func currentUserHomeDirectory( ) async throws {
10511105 let userName = ProcessInfo . processInfo. userName
0 commit comments