From 62adfbc2d220412a3772b3b93b218d13577060bc Mon Sep 17 00:00:00 2001 From: Michael Chiu Date: Wed, 15 Jan 2025 04:09:34 -0800 Subject: [PATCH] Fix FreeBSD build --- .../FileManager/FileOperations.swift | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Sources/FoundationEssentials/FileManager/FileOperations.swift b/Sources/FoundationEssentials/FileManager/FileOperations.swift index 62503c95..668d2c6e 100644 --- a/Sources/FoundationEssentials/FileManager/FileOperations.swift +++ b/Sources/FoundationEssentials/FileManager/FileOperations.swift @@ -960,6 +960,45 @@ enum _FileOperations { private static func _copyDirectoryMetadata(srcFD: CInt, srcPath: @autoclosure () -> String, dstFD: CInt, dstPath: @autoclosure () -> String, delegate: some LinkOrCopyDelegate) throws { #if !os(WASI) && !os(Android) // Copy extended attributes + #if os(FreeBSD) + // FreeBSD uses the `extattr_*` calls for setting extended attributes. Unlike like, the namespace for the extattrs are not determined by prefix of the attribute + for namespace in [EXTATTR_NAMESPACE_SYSTEM, EXTATTR_NAMESPACE_USER] { + // if we don't have permission to list attributes in system namespace, this returns -1 and skips it + var size = extattr_list_fd(srcFD, namespace, nil, 0) + if size > 0 { + try withUnsafeTemporaryAllocation(of: CChar.self, capacity: size + 1) { keyList in + // 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)) + size = extattr_list_fd(srcFD, namespace, keyList.baseAddress!, size) + var keyLength = Int(keyList.baseAddress!.pointee) + var current = keyList.baseAddress!.advanced(by: 1) + let end = keyList.baseAddress!.advanced(by: keyList.count) + keyList.baseAddress!.advanced(by: size).pointee = 0 + + while current < end { + let nextEntry = current.advanced(by: keyLength) + // get the length of next key, if this is the last entry, this points to the explicitly zerod byte at `end`. + keyLength = Int(nextEntry.pointee) + // zero the length field of the next name, so current name can pass in as a null-terminated string + nextEntry.pointee = 0 + // this also set `current` to `end` after iterating all entries + defer { current = nextEntry.advanced(by: 1) } + + var valueSize = extattr_get_fd(srcFD, namespace, current, nil, 0) + if valueSize >= 0 { + try withUnsafeTemporaryAllocation(of: UInt8.self, capacity: valueSize) { valueBuffer in + valueSize = extattr_get_fd(srcFD, namespace, current, valueBuffer.baseAddress!, valueSize) + if valueSize >= 0 { + if extattr_set_fd(srcFD, namespace, current, valueBuffer.baseAddress!, valueSize) != 0 { + try delegate.throwIfNecessary(errno, srcPath(), dstPath()) + } + } + } + } + } + } + } + } + #else var size = flistxattr(srcFD, nil, 0) if size > 0 { try withUnsafeTemporaryAllocation(of: CChar.self, capacity: size) { keyList in @@ -985,6 +1024,7 @@ enum _FileOperations { } } #endif + #endif var statInfo = stat() if fstat(srcFD, &statInfo) == 0 { #if !os(WASI) // WASI doesn't have fchown for now