From ac715a92625e63cb61f2fd7d7015d6214254a78d Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 31 May 2021 16:18:01 +0200 Subject: [PATCH 1/4] fix the `archive_error` function It's supposed to return an exception, not raise it. --- libarchive/ffi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libarchive/ffi.py b/libarchive/ffi.py index b278354..94f3307 100644 --- a/libarchive/ffi.py +++ b/libarchive/ffi.py @@ -79,7 +79,7 @@ def _error_string(archive_p): def archive_error(archive_p, retcode): msg = _error_string(archive_p) - raise ArchiveError(msg, errno(archive_p), retcode, archive_p) + return ArchiveError(msg, errno(archive_p), retcode, archive_p) def check_null(ret, func, args): From 965f25d53435da94268463ef98515eee956eddaa Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 31 May 2021 16:19:41 +0200 Subject: [PATCH 2/4] fix a log message in `ffi.py` closes #113 --- libarchive/ffi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libarchive/ffi.py b/libarchive/ffi.py index 94f3307..00d3926 100644 --- a/libarchive/ffi.py +++ b/libarchive/ffi.py @@ -328,5 +328,5 @@ def get_write_filter_function(filter_name): except AttributeError: logger.info( f"the libarchive being used (version {version_number()}, " - f"path {ffi.libarchive_path}) doesn't support encryption" + f"path {libarchive_path}) doesn't support encryption" ) From 0323df64cdca48e5ba14315582332c241df310ae Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 31 May 2021 16:24:38 +0200 Subject: [PATCH 3/4] merge `seekable_stream_reader` into `stream_reader` --- libarchive/read.py | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/libarchive/read.py b/libarchive/read.py index c7b3c84..8cba152 100644 --- a/libarchive/read.py +++ b/libarchive/read.py @@ -57,8 +57,8 @@ def new_archive_read(format_name='all', filter_name='all', passphrase=None): @contextmanager def custom_reader( read_func, format_name='all', filter_name='all', - open_func=VOID_CB, close_func=VOID_CB, block_size=page_size, - archive_read_class=ArchiveRead, passphrase=None, + open_func=VOID_CB, seek_func=None, close_func=VOID_CB, + block_size=page_size, archive_read_class=ArchiveRead, passphrase=None, ): """Read an archive using a custom function. """ @@ -66,6 +66,8 @@ def custom_reader( read_cb = READ_CALLBACK(read_func) close_cb = CLOSE_CALLBACK(close_func) with new_archive_read(format_name, filter_name, passphrase) as archive_p: + if seek_func: + ffi.read_set_seek_callback(archive_p, SEEK_CALLBACK(seek_func)) ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) yield archive_read_class(archive_p) @@ -117,36 +119,9 @@ def stream_reader( """Read an archive from a stream. The `stream` object must support the standard `readinto` method. - """ - buf = create_string_buffer(block_size) - buf_p = cast(buf, c_void_p) - - def read_func(archive_p, context, ptrptr): - # readinto the buffer, returns number of bytes read - length = stream.readinto(buf) - # write the address of the buffer into the pointer - ptrptr = cast(ptrptr, POINTER(c_void_p)) - ptrptr[0] = buf_p - # tell libarchive how much data was written into the buffer - return length - open_cb = OPEN_CALLBACK(VOID_CB) - read_cb = READ_CALLBACK(read_func) - close_cb = CLOSE_CALLBACK(VOID_CB) - with new_archive_read(format_name, filter_name, passphrase) as archive_p: - ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) - yield ArchiveRead(archive_p) - - -@contextmanager -def seekable_stream_reader( - stream, format_name='all', filter_name='all', block_size=page_size, - passphrase=None, -): - """Read an archive from a seekable stream. - - The `stream` object must support the standard `readinto`, 'seek' and - 'tell' methods. + If `stream.seekable()` returns `True`, then an appropriate seek callback is + passed to libarchive. """ buf = create_string_buffer(block_size) buf_p = cast(buf, c_void_p) @@ -167,9 +142,12 @@ def seek_func(archive_p, context, offset, whence): open_cb = OPEN_CALLBACK(VOID_CB) read_cb = READ_CALLBACK(read_func) - seek_cb = SEEK_CALLBACK(seek_func) close_cb = CLOSE_CALLBACK(VOID_CB) with new_archive_read(format_name, filter_name, passphrase) as archive_p: - ffi.read_set_seek_callback(archive_p, seek_cb) + if stream.seekable(): + ffi.read_set_seek_callback(archive_p, SEEK_CALLBACK(seek_func)) ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) yield ArchiveRead(archive_p) + + +seekable_stream_reader = stream_reader From c931af39cf634a8c9860b7e891e1bc209bd415c6 Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 31 May 2021 16:34:00 +0200 Subject: [PATCH 4/4] replace the misleadingly named `VOID_CB` function --- libarchive/ffi.py | 4 +++- libarchive/read.py | 12 ++++++------ libarchive/write.py | 10 +++++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/libarchive/ffi.py b/libarchive/ffi.py index 00d3926..22b3711 100644 --- a/libarchive/ffi.py +++ b/libarchive/ffi.py @@ -51,7 +51,9 @@ ) OPEN_CALLBACK = CFUNCTYPE(c_int, c_void_p, c_void_p) CLOSE_CALLBACK = CFUNCTYPE(c_int, c_void_p, c_void_p) -VOID_CB = lambda *_: ARCHIVE_OK + +NO_OPEN_CB = ctypes.cast(None, OPEN_CALLBACK) +NO_CLOSE_CB = ctypes.cast(None, CLOSE_CALLBACK) # Type aliases, for readability diff --git a/libarchive/read.py b/libarchive/read.py index 8cba152..6ab0e1a 100644 --- a/libarchive/read.py +++ b/libarchive/read.py @@ -5,7 +5,7 @@ from . import ffi from .ffi import ( ARCHIVE_EOF, OPEN_CALLBACK, READ_CALLBACK, CLOSE_CALLBACK, SEEK_CALLBACK, - VOID_CB, page_size, + NO_OPEN_CB, NO_CLOSE_CB, page_size, ) from .entry import ArchiveEntry, new_archive_entry @@ -57,14 +57,14 @@ def new_archive_read(format_name='all', filter_name='all', passphrase=None): @contextmanager def custom_reader( read_func, format_name='all', filter_name='all', - open_func=VOID_CB, seek_func=None, close_func=VOID_CB, + open_func=None, seek_func=None, close_func=None, block_size=page_size, archive_read_class=ArchiveRead, passphrase=None, ): """Read an archive using a custom function. """ - open_cb = OPEN_CALLBACK(open_func) + open_cb = OPEN_CALLBACK(open_func) if open_func else NO_OPEN_CB read_cb = READ_CALLBACK(read_func) - close_cb = CLOSE_CALLBACK(close_func) + close_cb = CLOSE_CALLBACK(close_func) if close_func else NO_CLOSE_CB with new_archive_read(format_name, filter_name, passphrase) as archive_p: if seek_func: ffi.read_set_seek_callback(archive_p, SEEK_CALLBACK(seek_func)) @@ -140,9 +140,9 @@ def seek_func(archive_p, context, offset, whence): # tell libarchive the current position return stream.tell() - open_cb = OPEN_CALLBACK(VOID_CB) + open_cb = NO_OPEN_CB read_cb = READ_CALLBACK(read_func) - close_cb = CLOSE_CALLBACK(VOID_CB) + close_cb = NO_CLOSE_CB with new_archive_read(format_name, filter_name, passphrase) as archive_p: if stream.seekable(): ffi.read_set_seek_callback(archive_p, SEEK_CALLBACK(seek_func)) diff --git a/libarchive/write.py b/libarchive/write.py index 6d023b3..024f03c 100644 --- a/libarchive/write.py +++ b/libarchive/write.py @@ -5,8 +5,8 @@ from . import ffi from .entry import ArchiveEntry, new_archive_entry from .ffi import ( - OPEN_CALLBACK, WRITE_CALLBACK, CLOSE_CALLBACK, VOID_CB, REGULAR_FILE, - DEFAULT_UNIX_PERMISSION, ARCHIVE_EOF, + OPEN_CALLBACK, WRITE_CALLBACK, CLOSE_CALLBACK, NO_OPEN_CB, NO_CLOSE_CB, + REGULAR_FILE, DEFAULT_UNIX_PERMISSION, ARCHIVE_EOF, page_size, entry_sourcepath, entry_clear, read_disk_new, read_disk_open_w, read_next_header2, read_disk_descend, read_free, write_header, write_data, write_finish_entry, entry_set_size, entry_set_filetype, entry_set_perm, @@ -187,7 +187,7 @@ def new_archive_write(format_name, filter_name=None, options='', passphrase=None @contextmanager def custom_writer( write_func, format_name, filter_name=None, - open_func=VOID_CB, close_func=VOID_CB, block_size=page_size, + open_func=None, close_func=None, block_size=page_size, archive_write_class=ArchiveWrite, options='', passphrase=None, ): @@ -195,9 +195,9 @@ def write_cb_internal(archive_p, context, buffer_, length): data = cast(buffer_, POINTER(c_char * length))[0] return write_func(data) - open_cb = OPEN_CALLBACK(open_func) + open_cb = OPEN_CALLBACK(open_func) if open_func else NO_OPEN_CB write_cb = WRITE_CALLBACK(write_cb_internal) - close_cb = CLOSE_CALLBACK(close_func) + close_cb = CLOSE_CALLBACK(close_func) if close_func else NO_CLOSE_CB with new_archive_write(format_name, filter_name, options, passphrase) as archive_p: