diff options
Diffstat (limited to 'bitbake/lib/bb/xattr.py')
-rwxr-xr-x | bitbake/lib/bb/xattr.py | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/bitbake/lib/bb/xattr.py b/bitbake/lib/bb/xattr.py new file mode 100755 index 0000000000..7b634944a4 --- /dev/null +++ b/bitbake/lib/bb/xattr.py | |||
@@ -0,0 +1,126 @@ | |||
1 | #! /usr/bin/env python3 | ||
2 | # | ||
3 | # Copyright 2023 by Garmin Ltd. or its subsidiaries | ||
4 | # | ||
5 | # SPDX-License-Identifier: MIT | ||
6 | |||
7 | import sys | ||
8 | import ctypes | ||
9 | import os | ||
10 | import errno | ||
11 | |||
12 | libc = ctypes.CDLL("libc.so.6", use_errno=True) | ||
13 | fsencoding = sys.getfilesystemencoding() | ||
14 | |||
15 | |||
16 | libc.listxattr.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t] | ||
17 | libc.llistxattr.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t] | ||
18 | |||
19 | |||
20 | def listxattr(path, follow=True): | ||
21 | func = libc.listxattr if follow else libc.llistxattr | ||
22 | |||
23 | os_path = os.fsencode(path) | ||
24 | |||
25 | while True: | ||
26 | length = func(os_path, None, 0) | ||
27 | |||
28 | if length < 0: | ||
29 | err = ctypes.get_errno() | ||
30 | raise OSError(err, os.strerror(err), str(path)) | ||
31 | |||
32 | if length == 0: | ||
33 | return [] | ||
34 | |||
35 | arr = ctypes.create_string_buffer(length) | ||
36 | |||
37 | read_length = func(os_path, arr, length) | ||
38 | if read_length != length: | ||
39 | # Race! | ||
40 | continue | ||
41 | |||
42 | return [a.decode(fsencoding) for a in arr.raw.split(b"\x00") if a] | ||
43 | |||
44 | |||
45 | libc.getxattr.argtypes = [ | ||
46 | ctypes.c_char_p, | ||
47 | ctypes.c_char_p, | ||
48 | ctypes.c_char_p, | ||
49 | ctypes.c_size_t, | ||
50 | ] | ||
51 | libc.lgetxattr.argtypes = [ | ||
52 | ctypes.c_char_p, | ||
53 | ctypes.c_char_p, | ||
54 | ctypes.c_char_p, | ||
55 | ctypes.c_size_t, | ||
56 | ] | ||
57 | |||
58 | |||
59 | def getxattr(path, name, follow=True): | ||
60 | func = libc.getxattr if follow else libc.lgetxattr | ||
61 | |||
62 | os_path = os.fsencode(path) | ||
63 | os_name = os.fsencode(name) | ||
64 | |||
65 | while True: | ||
66 | length = func(os_path, os_name, None, 0) | ||
67 | |||
68 | if length < 0: | ||
69 | err = ctypes.get_errno() | ||
70 | if err == errno.ENODATA: | ||
71 | return None | ||
72 | raise OSError(err, os.strerror(err), str(path)) | ||
73 | |||
74 | if length == 0: | ||
75 | return "" | ||
76 | |||
77 | arr = ctypes.create_string_buffer(length) | ||
78 | |||
79 | read_length = func(os_path, os_name, arr, length) | ||
80 | if read_length != length: | ||
81 | # Race! | ||
82 | continue | ||
83 | |||
84 | return arr.raw | ||
85 | |||
86 | |||
87 | def get_all_xattr(path, follow=True): | ||
88 | attrs = {} | ||
89 | |||
90 | names = listxattr(path, follow) | ||
91 | |||
92 | for name in names: | ||
93 | value = getxattr(path, name, follow) | ||
94 | if value is None: | ||
95 | # This can happen if a value is erased after listxattr is called, | ||
96 | # so ignore it | ||
97 | continue | ||
98 | attrs[name] = value | ||
99 | |||
100 | return attrs | ||
101 | |||
102 | |||
103 | def main(): | ||
104 | import argparse | ||
105 | from pathlib import Path | ||
106 | |||
107 | parser = argparse.ArgumentParser() | ||
108 | parser.add_argument("path", help="File Path", type=Path) | ||
109 | |||
110 | args = parser.parse_args() | ||
111 | |||
112 | attrs = get_all_xattr(args.path) | ||
113 | |||
114 | for name, value in attrs.items(): | ||
115 | try: | ||
116 | value = value.decode(fsencoding) | ||
117 | except UnicodeDecodeError: | ||
118 | pass | ||
119 | |||
120 | print(f"{name} = {value}") | ||
121 | |||
122 | return 0 | ||
123 | |||
124 | |||
125 | if __name__ == "__main__": | ||
126 | sys.exit(main()) | ||