| #! /usr/bin/env python3 |
| # |
| # Copyright 2023 by Garmin Ltd. or its subsidiaries |
| # |
| # SPDX-License-Identifier: MIT |
| |
| import sys |
| import ctypes |
| import os |
| import errno |
| |
| libc = ctypes.CDLL("libc.so.6", use_errno=True) |
| fsencoding = sys.getfilesystemencoding() |
| |
| |
| libc.listxattr.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t] |
| libc.llistxattr.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t] |
| |
| |
| def listxattr(path, follow=True): |
| func = libc.listxattr if follow else libc.llistxattr |
| |
| os_path = os.fsencode(path) |
| |
| while True: |
| length = func(os_path, None, 0) |
| |
| if length < 0: |
| err = ctypes.get_errno() |
| raise OSError(err, os.strerror(err), str(path)) |
| |
| if length == 0: |
| return [] |
| |
| arr = ctypes.create_string_buffer(length) |
| |
| read_length = func(os_path, arr, length) |
| if read_length != length: |
| # Race! |
| continue |
| |
| return [a.decode(fsencoding) for a in arr.raw.split(b"\x00") if a] |
| |
| |
| libc.getxattr.argtypes = [ |
| ctypes.c_char_p, |
| ctypes.c_char_p, |
| ctypes.c_char_p, |
| ctypes.c_size_t, |
| ] |
| libc.lgetxattr.argtypes = [ |
| ctypes.c_char_p, |
| ctypes.c_char_p, |
| ctypes.c_char_p, |
| ctypes.c_size_t, |
| ] |
| |
| |
| def getxattr(path, name, follow=True): |
| func = libc.getxattr if follow else libc.lgetxattr |
| |
| os_path = os.fsencode(path) |
| os_name = os.fsencode(name) |
| |
| while True: |
| length = func(os_path, os_name, None, 0) |
| |
| if length < 0: |
| err = ctypes.get_errno() |
| if err == errno.ENODATA: |
| return None |
| raise OSError(err, os.strerror(err), str(path)) |
| |
| if length == 0: |
| return "" |
| |
| arr = ctypes.create_string_buffer(length) |
| |
| read_length = func(os_path, os_name, arr, length) |
| if read_length != length: |
| # Race! |
| continue |
| |
| return arr.raw |
| |
| |
| def get_all_xattr(path, follow=True): |
| attrs = {} |
| |
| names = listxattr(path, follow) |
| |
| for name in names: |
| value = getxattr(path, name, follow) |
| if value is None: |
| # This can happen if a value is erased after listxattr is called, |
| # so ignore it |
| continue |
| attrs[name] = value |
| |
| return attrs |
| |
| |
| def main(): |
| import argparse |
| from pathlib import Path |
| |
| parser = argparse.ArgumentParser() |
| parser.add_argument("path", help="File Path", type=Path) |
| |
| args = parser.parse_args() |
| |
| attrs = get_all_xattr(args.path) |
| |
| for name, value in attrs.items(): |
| try: |
| value = value.decode(fsencoding) |
| except UnicodeDecodeError: |
| pass |
| |
| print(f"{name} = {value}") |
| |
| return 0 |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |