Skip to content

Commit 2f65d91

Browse files
authored
Fix _copyDirectoryMetadata compilation error on FreeBSD (#1121)
* Fix FreeBSD build * address comments
1 parent 117041b commit 2f65d91

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

Sources/FoundationEssentials/FileManager/FileOperations.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,49 @@ enum _FileOperations {
960960
private static func _copyDirectoryMetadata(srcFD: CInt, srcPath: @autoclosure () -> String, dstFD: CInt, dstPath: @autoclosure () -> String, delegate: some LinkOrCopyDelegate) throws {
961961
#if !os(WASI) && !os(Android)
962962
// Copy extended attributes
963+
#if os(FreeBSD)
964+
// FreeBSD uses the `extattr_*` calls for setting extended attributes. Unlike like, the namespace for the extattrs are not determined by prefix of the attribute
965+
for namespace in [EXTATTR_NAMESPACE_SYSTEM, EXTATTR_NAMESPACE_USER] {
966+
// if we don't have permission to list attributes in system namespace, this returns -1 and skips it
967+
var size = extattr_list_fd(srcFD, namespace, nil, 0)
968+
if size > 0 {
969+
// we are allocating size + 1 bytes here such that we have room for the last null terminator
970+
try withUnsafeTemporaryAllocation(of: CChar.self, capacity: size + 1) { keyList in
971+
// The list of entry returns by `extattr_list_*` contains the length(1 byte) of the attribute name, follow by the Non-NULL terminated attribute name. (See exattr(2))
972+
size = extattr_list_fd(srcFD, namespace, keyList.baseAddress!, size)
973+
974+
guard size > 0 else { continue }
975+
976+
var keyLength = Int(keyList.baseAddress!.pointee)
977+
var current = keyList.baseAddress!.advanced(by: 1)
978+
let end = keyList.baseAddress!.advanced(by: size)
979+
keyList.baseAddress!.advanced(by: size).pointee = 0
980+
981+
while current < end {
982+
let nextEntry = current.advanced(by: keyLength)
983+
// get the length of next key, if this is the last entry, this points to the explicitly zerod byte at `end`.
984+
keyLength = Int(nextEntry.pointee)
985+
// zero the length field of the next name, so current name can pass in as a null-terminated string
986+
nextEntry.pointee = 0
987+
// this also set `current` to `end` after iterating all entries
988+
defer { current = nextEntry.advanced(by: 1) }
989+
990+
var valueSize = extattr_get_fd(srcFD, namespace, current, nil, 0)
991+
if valueSize >= 0 {
992+
try withUnsafeTemporaryAllocation(of: UInt8.self, capacity: valueSize) { valueBuffer in
993+
valueSize = extattr_get_fd(srcFD, namespace, current, valueBuffer.baseAddress!, valueSize)
994+
if valueSize >= 0 {
995+
if extattr_set_fd(srcFD, namespace, current, valueBuffer.baseAddress!, valueSize) != 0 {
996+
try delegate.throwIfNecessary(errno, srcPath(), dstPath())
997+
}
998+
}
999+
}
1000+
}
1001+
}
1002+
}
1003+
}
1004+
}
1005+
#else
9631006
var size = flistxattr(srcFD, nil, 0)
9641007
if size > 0 {
9651008
try withUnsafeTemporaryAllocation(of: CChar.self, capacity: size) { keyList in
@@ -985,6 +1028,7 @@ enum _FileOperations {
9851028
}
9861029
}
9871030
#endif
1031+
#endif
9881032
var statInfo = stat()
9891033
if fstat(srcFD, &statInfo) == 0 {
9901034
#if !os(WASI) // WASI doesn't have fchown for now

0 commit comments

Comments
 (0)