杨锴
2025-04-16 09a372bc45fde16fd42257ab6f78b8deeecf720b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//
//  URL+ExtendedAttributes.swift
//  XCGLogger: https://github.com/DaveWoodCom/XCGLogger
//
//  Created by Dave Wood on 2017-04-04.
//  Copyright © 2017 Dave Wood, Cerebral Gardens.
//  Some rights reserved: https://github.com/DaveWoodCom/XCGLogger/blob/main/LICENSE.txt
//
//  Based on code by Martin-R here: https://stackoverflow.com/a/38343753/144857
 
import Foundation
 
extension URL {
 
    /// Get extended attribute.
    func extendedAttribute(forName name: String) throws -> Data? {
        let data: Data? = try self.withUnsafeFileSystemRepresentation { (fileSystemPath: (UnsafePointer<Int8>?)) -> Data? in
            // Determine attribute size
            let length: Int = getxattr(fileSystemPath, name, nil, 0, 0, 0)
            guard length >= 0 else { return nil }
 
            // Create buffer with required size
            var data: Data = Data(count: length)
 
            // Retrieve attribute
            let result: Int = data.withUnsafeMutableBytes { [count = data.count] (body: UnsafeMutableRawBufferPointer) -> Int in
                getxattr(fileSystemPath, name, body.baseAddress, count, 0, 0)
            }
            guard result >= 0 else { throw URL.posixError(errno) }
            return data
        }
 
        return data
    }
 
    /// Set extended attribute.
    func setExtendedAttribute(data: Data, forName name: String) throws {
        try self.withUnsafeFileSystemRepresentation { fileSystemPath in
            let result: Int32 = data.withUnsafeBytes { [count = data.count] (body: UnsafeRawBufferPointer) -> Int32 in
                setxattr(fileSystemPath, name, body.baseAddress, count, 0, 0)
            }
            guard result >= 0 else { throw URL.posixError(errno) }
        }
    }
 
    /// Remove extended attribute.
    func removeExtendedAttribute(forName name: String) throws {
        try self.withUnsafeFileSystemRepresentation { fileSystemPath in
            let result: Int32 = removexattr(fileSystemPath, name, 0)
            guard result >= 0 else { throw URL.posixError(errno) }
        }
    }
 
    /// Get list of all extended attributes.
    func listExtendedAttributes() throws -> [String] {
        let list: [String] = try self.withUnsafeFileSystemRepresentation { (fileSystemPath: (UnsafePointer<Int8>?)) -> [String] in
            let length: Int = listxattr(fileSystemPath, nil, 0, 0)
            guard length >= 0 else { throw URL.posixError(errno) }
 
            // Create buffer with required size
            var data: Data = Data(count: length)
 
            // Retrieve attribute list
            let result: Int = data.withUnsafeMutableBytes { [count = data.count] (body: UnsafeMutableRawBufferPointer) -> Int in
                return listxattr(fileSystemPath, UnsafeMutablePointer<Int8>(OpaquePointer(body.baseAddress)), count, 0)
            }
            guard result >= 0 else { throw URL.posixError(errno) }
 
            // Extract attribute names
            let list: [String] = data.split(separator: 0).compactMap {
                String(data: Data($0), encoding: .utf8)
            }
            return list
        }
        return list
    }
 
    /// Helper function to create an NSError from a Unix errno.
    private static func posixError(_ err: Int32) -> NSError {
        return NSError(domain: NSPOSIXErrorDomain, code: Int(err), userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))])
    }
}