From 54bb712c7daaaf4f6367f50ac60ead220fd081bc Mon Sep 17 00:00:00 2001 From: mxmlnkn Date: Wed, 28 May 2025 12:26:55 +0200 Subject: [PATCH] add support for reading xattrs --- libarchive/entry.py | 19 +- libarchive/ffi.py | 4 + tests/__init__.py | 4 +- tests/data/file-with-attribute.bsd.tar | Bin 0 -> 3072 bytes tests/data/file-with-attribute.bsd.tar.json | 20 + tests/data/testtar.tar.json | 1361 +++++++++-------- tests/data/unicode.tar.json | 99 +- tests/data/unicode.zip.json | 66 +- tests/data/unicode2.zip.json | 66 +- ...\241\234\352\267\270\353\236\250.zip.json" | 66 +- tests/test_entry.py | 4 + 11 files changed, 902 insertions(+), 807 deletions(-) create mode 100644 tests/data/file-with-attribute.bsd.tar create mode 100644 tests/data/file-with-attribute.bsd.tar.json diff --git a/libarchive/entry.py b/libarchive/entry.py index 863dbc7..4ff2cba 100644 --- a/libarchive/entry.py +++ b/libarchive/entry.py @@ -1,5 +1,5 @@ from contextlib import contextmanager -from ctypes import create_string_buffer, string_at +from ctypes import byref, c_char_p, create_string_buffer, c_size_t, c_void_p, string_at from enum import IntEnum import math @@ -493,6 +493,23 @@ def set_stored_digest(self, algorithm_name, value): f"{ffi.libarchive_path}) doesn't support {algorithm_name} digests" ) from None + def get_xattrs(self): + """Returns the extended attributes. + """ + xattrs = {} + + ffi.entry_xattr_reset(self._entry_p) + xattr_next = ffi.entry_xattr_next + + key, value, value_size = c_char_p(), c_void_p(), c_size_t() + key_p, value_p, value_size_p = byref(key), byref(value), byref(value_size) + + while True: + return_code = xattr_next(self._entry_p, key_p, value_p, value_size_p) + if return_code != ffi.ARCHIVE_OK: + break + yield key.value, string_at(value.value, value_size.value) + class ConsumedArchiveEntry(ArchiveEntry): diff --git a/libarchive/ffi.py b/libarchive/ffi.py index d960b59..e6af0c5 100644 --- a/libarchive/ffi.py +++ b/libarchive/ffi.py @@ -205,6 +205,10 @@ def get_write_filter_function(filter_name): ffi('entry_uname_w', [c_archive_entry_p], c_wchar_p) ffi('entry_gname_w', [c_archive_entry_p], c_wchar_p) +ffi('entry_xattr_count', [c_archive_entry_p], c_uint) +ffi('entry_xattr_reset', [c_archive_entry_p], c_uint) +ffi('entry_xattr_next', [c_archive_entry_p, POINTER(c_char_p), POINTER(c_void_p), POINTER(c_size_t)], c_uint) + ffi('entry_set_size', [c_archive_entry_p, c_longlong], None) ffi('entry_set_filetype', [c_archive_entry_p, c_uint], None) ffi('entry_set_uid', [c_archive_entry_p, c_longlong], None) diff --git a/tests/__init__.py b/tests/__init__.py index 7a7f583..5bf445e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -63,7 +63,9 @@ def get_entries(location): 'isfifo': entry.isfifo, 'isdev': entry.isdev, 'uid': entry.uid, - 'gid': entry.gid + 'gid': entry.gid, + # Convert to str because JSON cannot differentiate between str and bytes. + 'xattrs': {key.decode('utf-8'): value.decode('utf-8') for key,value in entry.get_xattrs()}, } diff --git a/tests/data/file-with-attribute.bsd.tar b/tests/data/file-with-attribute.bsd.tar new file mode 100644 index 0000000000000000000000000000000000000000..f96d66329d3ba8712e2400ca9ef5761002c9c78b GIT binary patch literal 3072 zcmeHGQ44}F5cb($v0q?!>YP6KP!SaP5CxTwvn+xnm<#ORH|aq}7)gjwyO-N`x4Zkk z?OXC{p7TR4M#t;b>c;?p0T2rS8P&XsC_~>RNR^{BV4h0=k$Ij)s(!}NtKLe