xattr.py 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. import os
  2. from cffi import FFI
  3. from typing import Any, List
  4. # Workaround for the EON/termux build of Python having os.*xattr removed.
  5. ffi = FFI()
  6. ffi.cdef("""
  7. int setxattr(const char *path, const char *name, const void *value, size_t size, int flags);
  8. ssize_t getxattr(const char *path, const char *name, void *value, size_t size);
  9. ssize_t listxattr(const char *path, char *list, size_t size);
  10. int removexattr(const char *path, const char *name);
  11. """)
  12. libc = ffi.dlopen(None)
  13. def setxattr(path, name, value, flags=0) -> None:
  14. path = path.encode()
  15. name = name.encode()
  16. if libc.setxattr(path, name, value, len(value), flags) == -1:
  17. raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: setxattr({path}, {name}, {value}, {flags})")
  18. def getxattr(path, name, size=128):
  19. path = path.encode()
  20. name = name.encode()
  21. value = ffi.new(f"char[{size}]")
  22. l = libc.getxattr(path, name, value, size)
  23. if l == -1:
  24. # errno 61 means attribute hasn't been set
  25. if ffi.errno == 61:
  26. return None
  27. raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: getxattr({path}, {name}, {size})")
  28. return ffi.buffer(value)[:l]
  29. def listxattr(path, size=128) -> List[Any]:
  30. path = path.encode()
  31. attrs = ffi.new(f"char[{size}]")
  32. l = libc.listxattr(path, attrs, size)
  33. if l == -1:
  34. raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: listxattr({path}, {size})")
  35. # attrs is b'\0' delimited values (so chop off trailing empty item)
  36. return [a.decode() for a in ffi.buffer(attrs)[:l].split(b"\0")[0:-1]]
  37. def removexattr(path, name) -> None:
  38. path = path.encode()
  39. name = name.encode()
  40. if libc.removexattr(path, name) == -1:
  41. raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: removexattr({path}, {name})")