From 5daeb8200fa6118734035c1b240e05c04f643d56 Mon Sep 17 00:00:00 2001 From: Vadim Troshchinskiy Date: Tue, 12 Nov 2024 13:36:01 +0100 Subject: [PATCH] Initial package contents --- .../opengnsys-libarchive-c-5.1/.gitattributes | 1 + .../.github/FUNDING.yml | 1 + .../.github/workflows/main.yml | 36 + .../opengnsys-libarchive-c-5.1/.gitignore | 8 + .../opengnsys-libarchive-c-5.1/LICENSE.md | 1 + .../opengnsys-libarchive-c-5.1/MANIFEST.in | 1 + .../opengnsys-libarchive-c-5.1/PKG-INFO | 147 ++++ .../opengnsys-libarchive-c-5.1/README.rst | 135 ++++ .../libarchive/__init__.py | 17 + .../libarchive/entry.py | 450 ++++++++++++ .../libarchive/exception.py | 12 + .../libarchive/extract.py | 88 +++ .../libarchive/ffi.py | 364 ++++++++++ .../libarchive/flags.py | 7 + .../libarchive/read.py | 176 +++++ .../libarchive/write.py | 279 ++++++++ .../opengnsys-libarchive-c-5.1/setup.cfg | 12 + .../opengnsys-libarchive-c-5.1/setup.py | 25 + .../tests/__init__.py | 136 ++++ .../tests/data/flags.tar | Bin 0 -> 10240 bytes .../tests/data/special.tar | Bin 0 -> 112640 bytes .../tests/data/tar_relative.tar | Bin 0 -> 10240 bytes .../tests/data/testtar.README | 3 + .../tests/data/testtar.tar | Bin 0 -> 435200 bytes .../tests/data/testtar.tar.json | 665 ++++++++++++++++++ .../tests/data/unicode.tar | Bin 0 -> 10240 bytes .../tests/data/unicode.tar.json | 53 ++ .../tests/data/unicode.zip | Bin 0 -> 668 bytes .../tests/data/unicode.zip.json | 36 + .../tests/data/unicode2.zip | Bin 0 -> 636 bytes .../tests/data/unicode2.zip.json | 36 + .../tests/data/프로그램.README | 3 + .../tests/data/프로그램.zip | Bin 0 -> 355 bytes .../tests/data/프로그램.zip.json | 36 + .../tests/test_atime_mtime_ctime.py | 127 ++++ .../tests/test_convert.py | 24 + .../tests/test_entry.py | 151 ++++ .../tests/test_errors.py | 40 ++ .../tests/test_rwx.py | 183 +++++ .../tests/test_security_flags.py | 36 + .../opengnsys-libarchive-c-5.1/tox.ini | 14 + .../opengnsys-libarchive-c-5.1/version.py | 45 ++ .../opengnsys-libarchive-c_5.1.debian.tar.xz | Bin 0 -> 6788 bytes 43 files changed, 3348 insertions(+) create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitattributes create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/FUNDING.yml create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/workflows/main.yml create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitignore create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/LICENSE.md create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/MANIFEST.in create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/PKG-INFO create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/README.rst create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/__init__.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/entry.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/exception.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/extract.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/ffi.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/flags.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/read.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/write.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.cfg create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/__init__.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/flags.tar create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/special.tar create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/tar_relative.tar create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/testtar.README create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/testtar.tar create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/testtar.tar.json create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.tar create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.tar.json create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.zip create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.zip.json create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode2.zip create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode2.zip.json create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.README create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.zip create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.zip.json create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_atime_mtime_ctime.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_convert.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_entry.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_errors.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_rwx.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_security_flags.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/tox.ini create mode 100644 packages/libarchive-c/opengnsys-libarchive-c-5.1/version.py create mode 100644 packages/libarchive-c/opengnsys-libarchive-c_5.1.debian.tar.xz diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitattributes b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitattributes new file mode 100644 index 0000000..f571202 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitattributes @@ -0,0 +1 @@ +version.py export-subst diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/FUNDING.yml b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/FUNDING.yml new file mode 100644 index 0000000..680194c --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/FUNDING.yml @@ -0,0 +1 @@ +liberapay: Changaco diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/workflows/main.yml b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/workflows/main.yml new file mode 100644 index 0000000..a715a9d --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.github/workflows/main.yml @@ -0,0 +1,36 @@ +name: CI +on: + # Trigger the workflow on push or pull request events but only for the master branch + push: + branches: [ master ] + pull_request: + branches: [ master ] + # Allow running this workflow manually from the Actions tab + workflow_dispatch: +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install libarchive + run: sudo apt-get install -y libarchive13 + - name: Install Python 3.11 + uses: actions/setup-python@v2 + with: + python-version: '3.11' + - name: Install Python 3.10 + uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Install Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Install Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: '3.8' + - name: Install tox + run: pip install tox + - name: Run the tests + run: tox diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitignore b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitignore new file mode 100644 index 0000000..6472f43 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/.gitignore @@ -0,0 +1,8 @@ +*.egg-info/ +/build/ +/dist/ +/env/ +/htmlcov/ +.coverage +*.pyc +.tox/ diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/LICENSE.md b/packages/libarchive-c/opengnsys-libarchive-c-5.1/LICENSE.md new file mode 100644 index 0000000..eebce25 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/LICENSE.md @@ -0,0 +1 @@ +https://creativecommons.org/publicdomain/zero/1.0/ diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/MANIFEST.in b/packages/libarchive-c/opengnsys-libarchive-c-5.1/MANIFEST.in new file mode 100644 index 0000000..2e92268 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/MANIFEST.in @@ -0,0 +1 @@ +include version.py diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/PKG-INFO b/packages/libarchive-c/opengnsys-libarchive-c-5.1/PKG-INFO new file mode 100644 index 0000000..b0fab94 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/PKG-INFO @@ -0,0 +1,147 @@ +Metadata-Version: 2.1 +Name: libarchive-c +Version: 5.1 +Summary: Python interface to libarchive +Home-page: https://github.com/Changaco/python-libarchive-c +Author: Changaco +Author-email: changaco@changaco.oy.lc +License: CC0 +Keywords: archive libarchive 7z tar bz2 zip gz +Description-Content-Type: text/x-rst +License-File: LICENSE.md + +A Python interface to libarchive. It uses the standard ctypes_ module to +dynamically load and access the C library. + +.. _ctypes: https://docs.python.org/3/library/ctypes.html + +Installation +============ + + pip install libarchive-c + +Compatibility +============= + +python +------ + +python-libarchive-c is currently tested with python 3.8, 3.9, 3.10 and 3.11. + +If you find an incompatibility with older versions you can send us a small patch, +but we won't accept big changes. + +libarchive +---------- + +python-libarchive-c may not work properly with obsolete versions of libarchive such as the ones included in MacOS. In that case you can install a recent version of libarchive (e.g. with ``brew install libarchive`` on MacOS) and use the ``LIBARCHIVE`` environment variable to point python-libarchive-c to it:: + + export LIBARCHIVE=/usr/local/Cellar/libarchive/3.3.3/lib/libarchive.13.dylib + +Usage +===== + +Import:: + + import libarchive + +Extracting archives +------------------- + +To extract an archive, use the ``extract_file`` function:: + + os.chdir('/path/to/target/directory') + libarchive.extract_file('test.zip') + +Alternatively, the ``extract_memory`` function can be used to extract from a buffer, +and ``extract_fd`` from a file descriptor. + +The ``extract_*`` functions all have an integer ``flags`` argument which is passed +directly to the C function ``archive_write_disk_set_options()``. You can import +the ``EXTRACT_*`` constants from the ``libarchive.extract`` module and see the +official description of each flag in the ``archive_write_disk(3)`` man page. + +By default, when the ``flags`` argument is ``None``, the ``SECURE_NODOTDOT``, +``SECURE_NOABSOLUTEPATHS`` and ``SECURE_SYMLINKS`` flags are passed to +libarchive, unless the current directory is the root (``/``). + +Reading archives +---------------- + +To read an archive, use the ``file_reader`` function:: + + with libarchive.file_reader('test.7z') as archive: + for entry in archive: + for block in entry.get_blocks(): + ... + +Alternatively, the ``memory_reader`` function can be used to read from a buffer, +``fd_reader`` from a file descriptor, ``stream_reader`` from a stream object +(which must support the standard ``readinto`` method), and ``custom_reader`` +from anywhere using callbacks. + +To learn about the attributes of the ``entry`` object, see the ``libarchive/entry.py`` +source code or run ``help(libarchive.entry.ArchiveEntry)`` in a Python shell. + +Displaying progress +~~~~~~~~~~~~~~~~~~~ + +If your program processes large archives, you can keep track of its progress +with the ``bytes_read`` attribute. Here's an example of a progress bar using +`tqdm `_:: + + with tqdm(total=os.stat(archive_path).st_size, unit='bytes') as pbar, \ + libarchive.file_reader(archive_path) as archive: + for entry in archive: + ... + pbar.update(archive.bytes_read - pbar.n) + +Creating archives +----------------- + +To create an archive, use the ``file_writer`` function:: + + from libarchive.entry import FileType + + with libarchive.file_writer('test.tar.gz', 'ustar', 'gzip') as archive: + # Add the `libarchive/` directory and everything in it (recursively), + # then the `README.rst` file. + archive.add_files('libarchive/', 'README.rst') + # Add a regular file defined from scratch. + data = b'foobar' + archive.add_file_from_memory('../escape-test', len(data), data) + # Add a directory defined from scratch. + early_epoch = (42, 42) # 1970-01-01 00:00:42.000000042 + archive.add_file_from_memory( + 'metadata-test', 0, b'', + filetype=FileType.DIRECTORY, permission=0o755, uid=4242, gid=4242, + atime=early_epoch, mtime=early_epoch, ctime=early_epoch, birthtime=early_epoch, + ) + +Alternatively, the ``memory_writer`` function can be used to write to a memory buffer, +``fd_writer`` to a file descriptor, and ``custom_writer`` to a callback function. + +For each of those functions, the mandatory second argument is the archive format, +and the optional third argument is the compression format (called “filter” in +libarchive). The acceptable values are listed in ``libarchive.ffi.WRITE_FORMATS`` +and ``libarchive.ffi.WRITE_FILTERS``. + +File metadata codecs +-------------------- + +By default, UTF-8 is used to read and write file attributes from and to archives. +A different codec can be specified through the ``header_codec`` arguments of the +``*_reader`` and ``*_writer`` functions. Example:: + + with libarchive.file_writer('test.tar', 'ustar', header_codec='cp037') as archive: + ... + with file_reader('test.tar', header_codec='cp037') as archive: + ... + +In addition to file paths (``pathname`` and ``linkpath``), the specified codec is +used to encode and decode user and group names (``uname`` and ``gname``). + +License +======= + +`CC0 Public Domain Dedication `_ diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/README.rst b/packages/libarchive-c/opengnsys-libarchive-c-5.1/README.rst new file mode 100644 index 0000000..64bef11 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/README.rst @@ -0,0 +1,135 @@ +A Python interface to libarchive. It uses the standard ctypes_ module to +dynamically load and access the C library. + +.. _ctypes: https://docs.python.org/3/library/ctypes.html + +Installation +============ + + pip install libarchive-c + +Compatibility +============= + +python +------ + +python-libarchive-c is currently tested with python 3.8, 3.9, 3.10 and 3.11. + +If you find an incompatibility with older versions you can send us a small patch, +but we won't accept big changes. + +libarchive +---------- + +python-libarchive-c may not work properly with obsolete versions of libarchive such as the ones included in MacOS. In that case you can install a recent version of libarchive (e.g. with ``brew install libarchive`` on MacOS) and use the ``LIBARCHIVE`` environment variable to point python-libarchive-c to it:: + + export LIBARCHIVE=/usr/local/Cellar/libarchive/3.3.3/lib/libarchive.13.dylib + +Usage +===== + +Import:: + + import libarchive + +Extracting archives +------------------- + +To extract an archive, use the ``extract_file`` function:: + + os.chdir('/path/to/target/directory') + libarchive.extract_file('test.zip') + +Alternatively, the ``extract_memory`` function can be used to extract from a buffer, +and ``extract_fd`` from a file descriptor. + +The ``extract_*`` functions all have an integer ``flags`` argument which is passed +directly to the C function ``archive_write_disk_set_options()``. You can import +the ``EXTRACT_*`` constants from the ``libarchive.extract`` module and see the +official description of each flag in the ``archive_write_disk(3)`` man page. + +By default, when the ``flags`` argument is ``None``, the ``SECURE_NODOTDOT``, +``SECURE_NOABSOLUTEPATHS`` and ``SECURE_SYMLINKS`` flags are passed to +libarchive, unless the current directory is the root (``/``). + +Reading archives +---------------- + +To read an archive, use the ``file_reader`` function:: + + with libarchive.file_reader('test.7z') as archive: + for entry in archive: + for block in entry.get_blocks(): + ... + +Alternatively, the ``memory_reader`` function can be used to read from a buffer, +``fd_reader`` from a file descriptor, ``stream_reader`` from a stream object +(which must support the standard ``readinto`` method), and ``custom_reader`` +from anywhere using callbacks. + +To learn about the attributes of the ``entry`` object, see the ``libarchive/entry.py`` +source code or run ``help(libarchive.entry.ArchiveEntry)`` in a Python shell. + +Displaying progress +~~~~~~~~~~~~~~~~~~~ + +If your program processes large archives, you can keep track of its progress +with the ``bytes_read`` attribute. Here's an example of a progress bar using +`tqdm `_:: + + with tqdm(total=os.stat(archive_path).st_size, unit='bytes') as pbar, \ + libarchive.file_reader(archive_path) as archive: + for entry in archive: + ... + pbar.update(archive.bytes_read - pbar.n) + +Creating archives +----------------- + +To create an archive, use the ``file_writer`` function:: + + from libarchive.entry import FileType + + with libarchive.file_writer('test.tar.gz', 'ustar', 'gzip') as archive: + # Add the `libarchive/` directory and everything in it (recursively), + # then the `README.rst` file. + archive.add_files('libarchive/', 'README.rst') + # Add a regular file defined from scratch. + data = b'foobar' + archive.add_file_from_memory('../escape-test', len(data), data) + # Add a directory defined from scratch. + early_epoch = (42, 42) # 1970-01-01 00:00:42.000000042 + archive.add_file_from_memory( + 'metadata-test', 0, b'', + filetype=FileType.DIRECTORY, permission=0o755, uid=4242, gid=4242, + atime=early_epoch, mtime=early_epoch, ctime=early_epoch, birthtime=early_epoch, + ) + +Alternatively, the ``memory_writer`` function can be used to write to a memory buffer, +``fd_writer`` to a file descriptor, and ``custom_writer`` to a callback function. + +For each of those functions, the mandatory second argument is the archive format, +and the optional third argument is the compression format (called “filter” in +libarchive). The acceptable values are listed in ``libarchive.ffi.WRITE_FORMATS`` +and ``libarchive.ffi.WRITE_FILTERS``. + +File metadata codecs +-------------------- + +By default, UTF-8 is used to read and write file attributes from and to archives. +A different codec can be specified through the ``header_codec`` arguments of the +``*_reader`` and ``*_writer`` functions. Example:: + + with libarchive.file_writer('test.tar', 'ustar', header_codec='cp037') as archive: + ... + with file_reader('test.tar', header_codec='cp037') as archive: + ... + +In addition to file paths (``pathname`` and ``linkpath``), the specified codec is +used to encode and decode user and group names (``uname`` and ``gname``). + +License +======= + +`CC0 Public Domain Dedication `_ diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/__init__.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/__init__.py new file mode 100644 index 0000000..bb52974 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/__init__.py @@ -0,0 +1,17 @@ +from .entry import ArchiveEntry +from .exception import ArchiveError +from .extract import extract_fd, extract_file, extract_memory +from .read import ( + custom_reader, fd_reader, file_reader, memory_reader, stream_reader, + seekable_stream_reader +) +from .write import custom_writer, fd_writer, file_writer, memory_writer + +__all__ = [x.__name__ for x in ( + ArchiveEntry, + ArchiveError, + extract_fd, extract_file, extract_memory, + custom_reader, fd_reader, file_reader, memory_reader, stream_reader, + seekable_stream_reader, + custom_writer, fd_writer, file_writer, memory_writer +)] diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/entry.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/entry.py new file mode 100644 index 0000000..70701ef --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/entry.py @@ -0,0 +1,450 @@ +from contextlib import contextmanager +from ctypes import create_string_buffer +from enum import IntEnum +import math + +from . import ffi + + +class FileType(IntEnum): + NAMED_PIPE = AE_IFIFO = 0o010000 # noqa: E221 + CHAR_DEVICE = AE_IFCHR = 0o020000 # noqa: E221 + DIRECTORY = AE_IFDIR = 0o040000 # noqa: E221 + BLOCK_DEVICE = AE_IFBLK = 0o060000 # noqa: E221 + REGULAR_FILE = AE_IFREG = 0o100000 # noqa: E221 + SYMBOLINK_LINK = AE_IFLNK = 0o120000 # noqa: E221 + SOCKET = AE_IFSOCK = 0o140000 # noqa: E221 + + +@contextmanager +def new_archive_entry(): + entry_p = ffi.entry_new() + try: + yield entry_p + finally: + ffi.entry_free(entry_p) + + +def format_time(seconds, nanos): + """ return float of seconds.nanos when nanos set, or seconds when not """ + if nanos: + return float(seconds) + float(nanos) / 1000000000.0 + return int(seconds) + + +class ArchiveEntry: + + __slots__ = ('_archive_p', '_entry_p', 'header_codec') + + def __init__(self, archive_p=None, header_codec='utf-8', **attributes): + """Allocate memory for an `archive_entry` struct. + + The `header_codec` is used to decode and encode file paths and other + attributes. + + The `**attributes` are passed to the `modify` method. + """ + self._archive_p = archive_p + self._entry_p = ffi.entry_new() + self.header_codec = header_codec + if attributes: + self.modify(**attributes) + + def __del__(self): + """Free the C struct""" + ffi.entry_free(self._entry_p) + + def __str__(self): + """Returns the file's path""" + return self.pathname + + def modify(self, header_codec=None, **attributes): + """Convenience method to modify the entry's attributes. + + Args: + filetype (int): the file's type, see the `FileType` class for values + pathname (str): the file's path + linkpath (str): the other path of the file, if the file is a link + size (int | None): the file's size, in bytes + perm (int): the file's permissions in standard Unix format, e.g. 0o640 + uid (int): the file owner's numerical identifier + gid (int): the file group's numerical identifier + uname (str | bytes): the file owner's name + gname (str | bytes): the file group's name + atime (int | Tuple[int, int] | float | None): + the file's most recent access time, + either in seconds or as a tuple (seconds, nanoseconds) + mtime (int | Tuple[int, int] | float | None): + the file's most recent modification time, + either in seconds or as a tuple (seconds, nanoseconds) + ctime (int | Tuple[int, int] | float | None): + the file's most recent metadata change time, + either in seconds or as a tuple (seconds, nanoseconds) + birthtime (int | Tuple[int, int] | float | None): + the file's creation time (for archive formats that support it), + either in seconds or as a tuple (seconds, nanoseconds) + rdev (int | Tuple[int, int]): device number, if the file is a device + rdevmajor (int): major part of the device number + rdevminor (int): minor part of the device number + """ + if header_codec: + self.header_codec = header_codec + for name, value in attributes.items(): + setattr(self, name, value) + + @property + def filetype(self): + return ffi.entry_filetype(self._entry_p) + + @filetype.setter + def filetype(self, value): + ffi.entry_set_filetype(self._entry_p, value) + + @property + def uid(self): + return ffi.entry_uid(self._entry_p) + + @uid.setter + def uid(self, uid): + ffi.entry_set_uid(self._entry_p, uid) + + @property + def gid(self): + return ffi.entry_gid(self._entry_p) + + @gid.setter + def gid(self, gid): + ffi.entry_set_gid(self._entry_p, gid) + + @property + def uname(self): + uname = ffi.entry_uname_w(self._entry_p) + if not uname: + uname = ffi.entry_uname(self._entry_p) + if uname is not None: + try: + uname = uname.decode(self.header_codec) + except UnicodeError: + pass + return uname + + @uname.setter + def uname(self, value): + if not isinstance(value, bytes): + value = value.encode(self.header_codec) + if self.header_codec == 'utf-8': + ffi.entry_update_uname_utf8(self._entry_p, value) + else: + ffi.entry_copy_uname(self._entry_p, value) + + @property + def gname(self): + gname = ffi.entry_gname_w(self._entry_p) + if not gname: + gname = ffi.entry_gname(self._entry_p) + if gname is not None: + try: + gname = gname.decode(self.header_codec) + except UnicodeError: + pass + return gname + + @gname.setter + def gname(self, value): + if not isinstance(value, bytes): + value = value.encode(self.header_codec) + if self.header_codec == 'utf-8': + ffi.entry_update_gname_utf8(self._entry_p, value) + else: + ffi.entry_copy_gname(self._entry_p, value) + + def get_blocks(self, block_size=ffi.page_size): + """Read the file's content, keeping only one chunk in memory at a time. + + Don't do anything like `list(entry.get_blocks())`, it would silently fail. + + Args: + block_size (int): the buffer's size, in bytes + """ + archive_p = self._archive_p + if not archive_p: + raise TypeError("this entry isn't linked to any content") + buf = create_string_buffer(block_size) + read = ffi.read_data + while 1: + r = read(archive_p, buf, block_size) + if r == 0: + break + yield buf.raw[0:r] + self.__class__ = ConsumedArchiveEntry + + @property + def isblk(self): + return self.filetype & 0o170000 == 0o060000 + + @property + def ischr(self): + return self.filetype & 0o170000 == 0o020000 + + @property + def isdir(self): + return self.filetype & 0o170000 == 0o040000 + + @property + def isfifo(self): + return self.filetype & 0o170000 == 0o010000 + + @property + def islnk(self): + return bool(ffi.entry_hardlink_w(self._entry_p) or + ffi.entry_hardlink(self._entry_p)) + + @property + def issym(self): + return self.filetype & 0o170000 == 0o120000 + + @property + def isreg(self): + return self.filetype & 0o170000 == 0o100000 + + @property + def isfile(self): + return self.isreg + + @property + def issock(self): + return self.filetype & 0o170000 == 0o140000 + + @property + def isdev(self): + return self.ischr or self.isblk or self.isfifo or self.issock + + @property + def atime(self): + if not ffi.entry_atime_is_set(self._entry_p): + return None + sec_val = ffi.entry_atime(self._entry_p) + nsec_val = ffi.entry_atime_nsec(self._entry_p) + return format_time(sec_val, nsec_val) + + @atime.setter + def atime(self, value): + if value is None: + ffi.entry_unset_atime(self._entry_p) + elif isinstance(value, int): + self.set_atime(value) + elif isinstance(value, tuple): + self.set_atime(*value) + else: + seconds, fraction = math.modf(value) + self.set_atime(int(seconds), int(fraction * 1_000_000_000)) + + def set_atime(self, timestamp_sec, timestamp_nsec=0): + "Kept for backward compatibility. `entry.atime = ...` is supported now." + return ffi.entry_set_atime(self._entry_p, timestamp_sec, timestamp_nsec) + + @property + def mtime(self): + if not ffi.entry_mtime_is_set(self._entry_p): + return None + sec_val = ffi.entry_mtime(self._entry_p) + nsec_val = ffi.entry_mtime_nsec(self._entry_p) + return format_time(sec_val, nsec_val) + + @mtime.setter + def mtime(self, value): + if value is None: + ffi.entry_unset_mtime(self._entry_p) + elif isinstance(value, int): + self.set_mtime(value) + elif isinstance(value, tuple): + self.set_mtime(*value) + else: + seconds, fraction = math.modf(value) + self.set_mtime(int(seconds), int(fraction * 1_000_000_000)) + + def set_mtime(self, timestamp_sec, timestamp_nsec=0): + "Kept for backward compatibility. `entry.mtime = ...` is supported now." + return ffi.entry_set_mtime(self._entry_p, timestamp_sec, timestamp_nsec) + + @property + def ctime(self): + if not ffi.entry_ctime_is_set(self._entry_p): + return None + sec_val = ffi.entry_ctime(self._entry_p) + nsec_val = ffi.entry_ctime_nsec(self._entry_p) + return format_time(sec_val, nsec_val) + + @ctime.setter + def ctime(self, value): + if value is None: + ffi.entry_unset_ctime(self._entry_p) + elif isinstance(value, int): + self.set_ctime(value) + elif isinstance(value, tuple): + self.set_ctime(*value) + else: + seconds, fraction = math.modf(value) + self.set_ctime(int(seconds), int(fraction * 1_000_000_000)) + + def set_ctime(self, timestamp_sec, timestamp_nsec=0): + "Kept for backward compatibility. `entry.ctime = ...` is supported now." + return ffi.entry_set_ctime(self._entry_p, timestamp_sec, timestamp_nsec) + + @property + def birthtime(self): + if not ffi.entry_birthtime_is_set(self._entry_p): + return None + sec_val = ffi.entry_birthtime(self._entry_p) + nsec_val = ffi.entry_birthtime_nsec(self._entry_p) + return format_time(sec_val, nsec_val) + + @birthtime.setter + def birthtime(self, value): + if value is None: + ffi.entry_unset_birthtime(self._entry_p) + elif isinstance(value, int): + self.set_birthtime(value) + elif isinstance(value, tuple): + self.set_birthtime(*value) + else: + seconds, fraction = math.modf(value) + self.set_birthtime(int(seconds), int(fraction * 1_000_000_000)) + + def set_birthtime(self, timestamp_sec, timestamp_nsec=0): + "Kept for backward compatibility. `entry.birthtime = ...` is supported now." + return ffi.entry_set_birthtime( + self._entry_p, timestamp_sec, timestamp_nsec + ) + + @property + def pathname(self): + path = ffi.entry_pathname_w(self._entry_p) + if not path: + path = ffi.entry_pathname(self._entry_p) + if path is not None: + try: + path = path.decode(self.header_codec) + except UnicodeError: + pass + return path + + @pathname.setter + def pathname(self, value): + if not isinstance(value, bytes): + value = value.encode(self.header_codec) + if self.header_codec == 'utf-8': + ffi.entry_update_pathname_utf8(self._entry_p, value) + else: + ffi.entry_copy_pathname(self._entry_p, value) + + @property + def linkpath(self): + path = ( + ( + ffi.entry_symlink_w(self._entry_p) or + ffi.entry_symlink(self._entry_p) + ) if self.issym else ( + ffi.entry_hardlink_w(self._entry_p) or + ffi.entry_hardlink(self._entry_p) + ) + ) + if isinstance(path, bytes): + try: + path = path.decode(self.header_codec) + except UnicodeError: + pass + return path + + @linkpath.setter + def linkpath(self, value): + if not isinstance(value, bytes): + value = value.encode(self.header_codec) + if self.header_codec == 'utf-8': + ffi.entry_update_link_utf8(self._entry_p, value) + else: + ffi.entry_copy_link(self._entry_p, value) + + # aliases for compatibility with the standard `tarfile` module + path = property(pathname.fget, pathname.fset, doc="alias of pathname") + name = path + linkname = property(linkpath.fget, linkpath.fset, doc="alias of linkpath") + + @property + def size(self): + if ffi.entry_size_is_set(self._entry_p): + return ffi.entry_size(self._entry_p) + + @size.setter + def size(self, value): + if value is None: + ffi.entry_unset_size(self._entry_p) + else: + ffi.entry_set_size(self._entry_p, value) + + @property + def mode(self): + return ffi.entry_mode(self._entry_p) + + @mode.setter + def mode(self, value): + ffi.entry_set_mode(self._entry_p, value) + + @property + def strmode(self): + """The file's mode as a string, e.g. '?rwxrwx---'""" + # note we strip the mode because archive_entry_strmode + # returns a trailing space: strcpy(bp, "?rwxrwxrwx "); + return ffi.entry_strmode(self._entry_p).strip() + + @property + def perm(self): + return ffi.entry_perm(self._entry_p) + + @perm.setter + def perm(self, value): + ffi.entry_set_perm(self._entry_p, value) + + @property + def rdev(self): + return ffi.entry_rdev(self._entry_p) + + @rdev.setter + def rdev(self, value): + if isinstance(value, tuple): + ffi.entry_set_rdevmajor(self._entry_p, value[0]) + ffi.entry_set_rdevminor(self._entry_p, value[1]) + else: + ffi.entry_set_rdev(self._entry_p, value) + + @property + def rdevmajor(self): + return ffi.entry_rdevmajor(self._entry_p) + + @rdevmajor.setter + def rdevmajor(self, value): + ffi.entry_set_rdevmajor(self._entry_p, value) + + @property + def rdevminor(self): + return ffi.entry_rdevminor(self._entry_p) + + @rdevminor.setter + def rdevminor(self, value): + ffi.entry_set_rdevminor(self._entry_p, value) + + +class ConsumedArchiveEntry(ArchiveEntry): + + __slots__ = () + + def get_blocks(self, **kw): + raise TypeError("the content of this entry has already been read") + + +class PassedArchiveEntry(ArchiveEntry): + + __slots__ = () + + def get_blocks(self, **kw): + raise TypeError("this entry is passed, it's too late to read its content") diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/exception.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/exception.py new file mode 100644 index 0000000..e24658c --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/exception.py @@ -0,0 +1,12 @@ + +class ArchiveError(Exception): + + def __init__(self, msg, errno=None, retcode=None, archive_p=None): + self.msg = msg + self.errno = errno + self.retcode = retcode + self.archive_p = archive_p + + def __str__(self): + p = '%s (errno=%s, retcode=%s, archive_p=%s)' + return p % (self.msg, self.errno, self.retcode, self.archive_p) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/extract.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/extract.py new file mode 100644 index 0000000..bf0c703 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/extract.py @@ -0,0 +1,88 @@ +from contextlib import contextmanager +from ctypes import byref, c_longlong, c_size_t, c_void_p +import os + +from .ffi import ( + write_disk_new, write_disk_set_options, write_free, write_header, + read_data_block, write_data_block, write_finish_entry, ARCHIVE_EOF +) +from .read import fd_reader, file_reader, memory_reader + + +EXTRACT_OWNER = 0x0001 +EXTRACT_PERM = 0x0002 +EXTRACT_TIME = 0x0004 +EXTRACT_NO_OVERWRITE = 0x0008 +EXTRACT_UNLINK = 0x0010 +EXTRACT_ACL = 0x0020 +EXTRACT_FFLAGS = 0x0040 +EXTRACT_XATTR = 0x0080 +EXTRACT_SECURE_SYMLINKS = 0x0100 +EXTRACT_SECURE_NODOTDOT = 0x0200 +EXTRACT_NO_AUTODIR = 0x0400 +EXTRACT_NO_OVERWRITE_NEWER = 0x0800 +EXTRACT_SPARSE = 0x1000 +EXTRACT_MAC_METADATA = 0x2000 +EXTRACT_NO_HFS_COMPRESSION = 0x4000 +EXTRACT_HFS_COMPRESSION_FORCED = 0x8000 +EXTRACT_SECURE_NOABSOLUTEPATHS = 0x10000 +EXTRACT_CLEAR_NOCHANGE_FFLAGS = 0x20000 + +PREVENT_ESCAPE = ( + EXTRACT_SECURE_NOABSOLUTEPATHS | + EXTRACT_SECURE_NODOTDOT | + EXTRACT_SECURE_SYMLINKS +) + + +@contextmanager +def new_archive_write_disk(flags): + archive_p = write_disk_new() + write_disk_set_options(archive_p, flags) + try: + yield archive_p + finally: + write_free(archive_p) + + +def extract_entries(entries, flags=None): + """Extracts the given archive entries into the current directory. + """ + if flags is None: + if os.getcwd() == '/': + # If the current directory is the root, then trying to prevent + # escaping is probably undesirable. + flags = 0 + else: + flags = PREVENT_ESCAPE + buff, size, offset = c_void_p(), c_size_t(), c_longlong() + buff_p, size_p, offset_p = byref(buff), byref(size), byref(offset) + with new_archive_write_disk(flags) as write_p: + for entry in entries: + write_header(write_p, entry._entry_p) + read_p = entry._archive_p + while 1: + r = read_data_block(read_p, buff_p, size_p, offset_p) + if r == ARCHIVE_EOF: + break + write_data_block(write_p, buff, size, offset) + write_finish_entry(write_p) + + +def extract_fd(fd, flags=None): + """Extracts an archive from a file descriptor into the current directory. + """ + with fd_reader(fd) as archive: + extract_entries(archive, flags) + + +def extract_file(filepath, flags=None): + """Extracts an archive from a file into the current directory.""" + with file_reader(filepath) as archive: + extract_entries(archive, flags) + + +def extract_memory(buffer_, flags=None): + """Extracts an archive from memory into the current directory.""" + with memory_reader(buffer_) as archive: + extract_entries(archive, flags) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/ffi.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/ffi.py new file mode 100644 index 0000000..1fc321a --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/ffi.py @@ -0,0 +1,364 @@ +from ctypes import ( + c_char_p, c_int, c_uint, c_long, c_longlong, c_size_t, c_int64, + c_void_p, c_wchar_p, CFUNCTYPE, POINTER, +) + +try: + from ctypes import c_ssize_t +except ImportError: + from ctypes import c_longlong as c_ssize_t + +import ctypes +from ctypes.util import find_library +import logging +import mmap +import os +import sysconfig + +from .exception import ArchiveError + + +logger = logging.getLogger('libarchive') + +page_size = mmap.PAGESIZE + +libarchive_path = os.environ.get('LIBARCHIVE') or find_library('archive') +libarchive = ctypes.cdll.LoadLibrary(libarchive_path) + + +# Constants + +ARCHIVE_EOF = 1 # Found end of archive. +ARCHIVE_OK = 0 # Operation was successful. +ARCHIVE_RETRY = -10 # Retry might succeed. +ARCHIVE_WARN = -20 # Partial success. +ARCHIVE_FAILED = -25 # Current operation cannot complete. +ARCHIVE_FATAL = -30 # No more operations are possible. + + +# Callback types + +WRITE_CALLBACK = CFUNCTYPE( + c_ssize_t, c_void_p, c_void_p, POINTER(c_void_p), c_size_t +) +READ_CALLBACK = CFUNCTYPE( + c_ssize_t, c_void_p, c_void_p, POINTER(c_void_p) +) +SEEK_CALLBACK = CFUNCTYPE( + c_longlong, c_void_p, c_void_p, c_longlong, c_int +) +OPEN_CALLBACK = CFUNCTYPE(c_int, c_void_p, c_void_p) +CLOSE_CALLBACK = CFUNCTYPE(c_int, c_void_p, c_void_p) + +NO_OPEN_CB = ctypes.cast(None, OPEN_CALLBACK) +NO_CLOSE_CB = ctypes.cast(None, CLOSE_CALLBACK) + + +# Type aliases, for readability + +c_archive_p = c_void_p +c_archive_entry_p = c_void_p + +if sysconfig.get_config_var('SIZEOF_TIME_T') == 8: + c_time_t = c_int64 +else: + c_time_t = c_long + + +# Helper functions + +def _error_string(archive_p): + msg = error_string(archive_p) + if msg is None: + return + try: + return msg.decode('ascii') + except UnicodeDecodeError: + return msg + + +def archive_error(archive_p, retcode): + msg = _error_string(archive_p) + return ArchiveError(msg, errno(archive_p), retcode, archive_p) + + +def check_null(ret, func, args): + if ret is None: + raise ArchiveError(func.__name__+' returned NULL') + return ret + + +def check_int(retcode, func, args): + if retcode >= 0: + return retcode + elif retcode == ARCHIVE_WARN: + logger.warning(_error_string(args[0])) + return retcode + else: + raise archive_error(args[0], retcode) + + +def ffi(name, argtypes, restype, errcheck=None): + f = getattr(libarchive, 'archive_'+name) + f.argtypes = argtypes + f.restype = restype + if errcheck: + f.errcheck = errcheck + globals()[name] = f + return f + + +def get_read_format_function(format_name): + function_name = 'read_support_format_' + format_name + func = globals().get(function_name) + if func: + return func + try: + return ffi(function_name, [c_archive_p], c_int, check_int) + except AttributeError: + raise ValueError('the read format %r is not available' % format_name) + + +def get_read_filter_function(filter_name): + function_name = 'read_support_filter_' + filter_name + func = globals().get(function_name) + if func: + return func + try: + return ffi(function_name, [c_archive_p], c_int, check_int) + except AttributeError: + raise ValueError('the read filter %r is not available' % filter_name) + + +def get_write_format_function(format_name): + function_name = 'write_set_format_' + format_name + func = globals().get(function_name) + if func: + return func + try: + return ffi(function_name, [c_archive_p], c_int, check_int) + except AttributeError: + raise ValueError('the write format %r is not available' % format_name) + + +def get_write_filter_function(filter_name): + function_name = 'write_add_filter_' + filter_name + func = globals().get(function_name) + if func: + return func + try: + return ffi(function_name, [c_archive_p], c_int, check_int) + except AttributeError: + raise ValueError('the write filter %r is not available' % filter_name) + + +# FFI declarations + +# library version +version_number = ffi('version_number', [], c_int, check_int) + +# archive_util + +errno = ffi('errno', [c_archive_p], c_int) +error_string = ffi('error_string', [c_archive_p], c_char_p) +ffi('filter_bytes', [c_archive_p, c_int], c_longlong) +ffi('filter_count', [c_archive_p], c_int) +ffi('filter_name', [c_archive_p, c_int], c_char_p) +ffi('format_name', [c_archive_p], c_char_p) + +# archive_entry + +ffi('entry_new', [], c_archive_entry_p, check_null) + +ffi('entry_filetype', [c_archive_entry_p], c_int) +ffi('entry_atime', [c_archive_entry_p], c_time_t) +ffi('entry_birthtime', [c_archive_entry_p], c_time_t) +ffi('entry_mtime', [c_archive_entry_p], c_time_t) +ffi('entry_ctime', [c_archive_entry_p], c_time_t) +ffi('entry_atime_nsec', [c_archive_entry_p], c_long) +ffi('entry_birthtime_nsec', [c_archive_entry_p], c_long) +ffi('entry_mtime_nsec', [c_archive_entry_p], c_long) +ffi('entry_ctime_nsec', [c_archive_entry_p], c_long) +ffi('entry_atime_is_set', [c_archive_entry_p], c_int) +ffi('entry_birthtime_is_set', [c_archive_entry_p], c_int) +ffi('entry_mtime_is_set', [c_archive_entry_p], c_int) +ffi('entry_ctime_is_set', [c_archive_entry_p], c_int) +ffi('entry_pathname', [c_archive_entry_p], c_char_p) +ffi('entry_pathname_w', [c_archive_entry_p], c_wchar_p) +ffi('entry_sourcepath', [c_archive_entry_p], c_char_p) +ffi('entry_size', [c_archive_entry_p], c_longlong) +ffi('entry_size_is_set', [c_archive_entry_p], c_int) +ffi('entry_mode', [c_archive_entry_p], c_int) +ffi('entry_strmode', [c_archive_entry_p], c_char_p) +ffi('entry_perm', [c_archive_entry_p], c_int) +ffi('entry_hardlink', [c_archive_entry_p], c_char_p) +ffi('entry_hardlink_w', [c_archive_entry_p], c_wchar_p) +ffi('entry_symlink', [c_archive_entry_p], c_char_p) +ffi('entry_symlink_w', [c_archive_entry_p], c_wchar_p) +ffi('entry_rdev', [c_archive_entry_p], c_uint) +ffi('entry_rdevmajor', [c_archive_entry_p], c_uint) +ffi('entry_rdevminor', [c_archive_entry_p], c_uint) +ffi('entry_uid', [c_archive_entry_p], c_longlong) +ffi('entry_gid', [c_archive_entry_p], c_longlong) +ffi('entry_uname', [c_archive_entry_p], c_char_p) +ffi('entry_gname', [c_archive_entry_p], c_char_p) +ffi('entry_uname_w', [c_archive_entry_p], c_wchar_p) +ffi('entry_gname_w', [c_archive_entry_p], c_wchar_p) + +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) +ffi('entry_set_gid', [c_archive_entry_p, c_longlong], None) +ffi('entry_set_mode', [c_archive_entry_p, c_int], None) +ffi('entry_set_perm', [c_archive_entry_p, c_int], None) +ffi('entry_set_atime', [c_archive_entry_p, c_time_t, c_long], None) +ffi('entry_set_mtime', [c_archive_entry_p, c_time_t, c_long], None) +ffi('entry_set_ctime', [c_archive_entry_p, c_time_t, c_long], None) +ffi('entry_set_birthtime', [c_archive_entry_p, c_time_t, c_long], None) +ffi('entry_set_rdev', [c_archive_entry_p, c_uint], None) +ffi('entry_set_rdevmajor', [c_archive_entry_p, c_uint], None) +ffi('entry_set_rdevminor', [c_archive_entry_p, c_uint], None) +ffi('entry_unset_size', [c_archive_entry_p], None) +ffi('entry_unset_atime', [c_archive_entry_p], None) +ffi('entry_unset_mtime', [c_archive_entry_p], None) +ffi('entry_unset_ctime', [c_archive_entry_p], None) +ffi('entry_unset_birthtime', [c_archive_entry_p], None) + +ffi('entry_copy_pathname', [c_archive_entry_p, c_char_p], None) +ffi('entry_update_pathname_utf8', [c_archive_entry_p, c_char_p], c_int, check_int) +ffi('entry_copy_link', [c_archive_entry_p, c_char_p], None) +ffi('entry_update_link_utf8', [c_archive_entry_p, c_char_p], c_int, check_int) +ffi('entry_copy_uname', [c_archive_entry_p, c_char_p], None) +ffi('entry_update_uname_utf8', [c_archive_entry_p, c_char_p], c_int, check_int) +ffi('entry_copy_gname', [c_archive_entry_p, c_char_p], None) +ffi('entry_update_gname_utf8', [c_archive_entry_p, c_char_p], c_int, check_int) + +ffi('entry_clear', [c_archive_entry_p], c_archive_entry_p) +ffi('entry_free', [c_archive_entry_p], None) + +# archive_read + +ffi('read_new', [], c_archive_p, check_null) + +READ_FORMATS = set(( + '7zip', 'all', 'ar', 'cab', 'cpio', 'empty', 'iso9660', 'lha', 'mtree', + 'rar', 'raw', 'tar', 'xar', 'zip', 'warc' +)) +for f_name in list(READ_FORMATS): + try: + get_read_format_function(f_name) + except ValueError as e: # pragma: no cover + logger.info(str(e)) + READ_FORMATS.remove(f_name) + +READ_FILTERS = set(( + 'all', 'bzip2', 'compress', 'grzip', 'gzip', 'lrzip', 'lzip', 'lzma', + 'lzop', 'none', 'rpm', 'uu', 'xz', 'lz4', 'zstd' +)) +for f_name in list(READ_FILTERS): + try: + get_read_filter_function(f_name) + except ValueError as e: # pragma: no cover + logger.info(str(e)) + READ_FILTERS.remove(f_name) + +ffi('read_set_seek_callback', [c_archive_p, SEEK_CALLBACK], c_int, check_int) + +ffi('read_open', + [c_archive_p, c_void_p, OPEN_CALLBACK, READ_CALLBACK, CLOSE_CALLBACK], + c_int, check_int) +ffi('read_open_fd', [c_archive_p, c_int, c_size_t], c_int, check_int) +ffi('read_open_filename_w', [c_archive_p, c_wchar_p, c_size_t], + c_int, check_int) +ffi('read_open_memory', [c_archive_p, c_void_p, c_size_t], c_int, check_int) + +ffi('read_next_header', [c_archive_p, POINTER(c_void_p)], c_int, check_int) +ffi('read_next_header2', [c_archive_p, c_void_p], c_int, check_int) + +ffi('read_close', [c_archive_p], c_int, check_int) +ffi('read_free', [c_archive_p], c_int, check_int) + +# archive_read_disk + +ffi('read_disk_new', [], c_archive_p, check_null) +ffi('read_disk_set_behavior', [c_archive_p, c_int], c_int, check_int) +ffi('read_disk_set_standard_lookup', [c_archive_p], c_int, check_int) +ffi('read_disk_open', [c_archive_p, c_char_p], c_int, check_int) +ffi('read_disk_open_w', [c_archive_p, c_wchar_p], c_int, check_int) +ffi('read_disk_descend', [c_archive_p], c_int, check_int) + +# archive_read_data + +ffi('read_data_block', + [c_archive_p, POINTER(c_void_p), POINTER(c_size_t), POINTER(c_longlong)], + c_int, check_int) +ffi('read_data', [c_archive_p, c_void_p, c_size_t], c_ssize_t, check_int) +ffi('read_data_skip', [c_archive_p], c_int, check_int) + +# archive_write + +ffi('write_new', [], c_archive_p, check_null) +ffi('write_set_options', [c_archive_p, c_char_p], c_int, check_int) + +ffi('write_disk_new', [], c_archive_p, check_null) +ffi('write_disk_set_options', [c_archive_p, c_int], c_int, check_int) + +WRITE_FORMATS = set(( + '7zip', 'ar_bsd', 'ar_svr4', 'cpio', 'cpio_newc', 'gnutar', 'iso9660', + 'mtree', 'mtree_classic', 'pax', 'pax_restricted', 'shar', 'shar_dump', + 'ustar', 'v7tar', 'xar', 'zip', 'warc' +)) +for f_name in list(WRITE_FORMATS): + try: + get_write_format_function(f_name) + except ValueError as e: # pragma: no cover + logger.info(str(e)) + WRITE_FORMATS.remove(f_name) + +WRITE_FILTERS = set(( + 'b64encode', 'bzip2', 'compress', 'grzip', 'gzip', 'lrzip', 'lzip', 'lzma', + 'lzop', 'uuencode', 'xz', 'lz4', 'zstd' +)) +for f_name in list(WRITE_FILTERS): + try: + get_write_filter_function(f_name) + except ValueError as e: # pragma: no cover + logger.info(str(e)) + WRITE_FILTERS.remove(f_name) + +ffi('write_open', + [c_archive_p, c_void_p, OPEN_CALLBACK, WRITE_CALLBACK, CLOSE_CALLBACK], + c_int, check_int) +ffi('write_open_fd', [c_archive_p, c_int], c_int, check_int) +ffi('write_open_filename', [c_archive_p, c_char_p], c_int, check_int) +ffi('write_open_filename_w', [c_archive_p, c_wchar_p], c_int, check_int) +ffi('write_open_memory', + [c_archive_p, c_void_p, c_size_t, POINTER(c_size_t)], + c_int, check_int) + +ffi('write_get_bytes_in_last_block', [c_archive_p], c_int, check_int) +ffi('write_get_bytes_per_block', [c_archive_p], c_int, check_int) +ffi('write_set_bytes_in_last_block', [c_archive_p, c_int], c_int, check_int) +ffi('write_set_bytes_per_block', [c_archive_p, c_int], c_int, check_int) + +ffi('write_header', [c_archive_p, c_void_p], c_int, check_int) +ffi('write_data', [c_archive_p, c_void_p, c_size_t], c_ssize_t, check_int) +ffi('write_data_block', [c_archive_p, c_void_p, c_size_t, c_longlong], + c_int, check_int) +ffi('write_finish_entry', [c_archive_p], c_int, check_int) + +ffi('write_fail', [c_archive_p], c_int, check_int) + +ffi('write_close', [c_archive_p], c_int, check_int) +ffi('write_free', [c_archive_p], c_int, check_int) + +# archive encryption + +try: + ffi('read_add_passphrase', [c_archive_p, c_char_p], c_int, check_int) + ffi('write_set_passphrase', [c_archive_p, c_char_p], c_int, check_int) +except AttributeError: + logger.info( + f"the libarchive being used (version {version_number()}, " + f"path {libarchive_path}) doesn't support encryption" + ) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/flags.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/flags.py new file mode 100644 index 0000000..6c5b304 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/flags.py @@ -0,0 +1,7 @@ +READDISK_RESTORE_ATIME = 0x0001 +READDISK_HONOR_NODUMP = 0x0002 +READDISK_MAC_COPYFILE = 0x0004 +READDISK_NO_TRAVERSE_MOUNTS = 0x0008 +READDISK_NO_XATTR = 0x0010 +READDISK_NO_ACL = 0x0020 +READDISK_NO_FFLAGS = 0x0040 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/read.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/read.py new file mode 100644 index 0000000..3451376 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/read.py @@ -0,0 +1,176 @@ +from contextlib import contextmanager +from ctypes import cast, c_void_p, POINTER, create_string_buffer +from os import fstat, stat + +from . import ffi +from .ffi import ( + ARCHIVE_EOF, OPEN_CALLBACK, READ_CALLBACK, CLOSE_CALLBACK, SEEK_CALLBACK, + NO_OPEN_CB, NO_CLOSE_CB, page_size, +) +from .entry import ArchiveEntry, PassedArchiveEntry + + +class ArchiveRead: + + def __init__(self, archive_p, header_codec='utf-8'): + self._pointer = archive_p + self.header_codec = header_codec + + def __iter__(self): + """Iterates through an archive's entries. + """ + archive_p = self._pointer + header_codec = self.header_codec + read_next_header2 = ffi.read_next_header2 + while 1: + entry = ArchiveEntry(archive_p, header_codec) + r = read_next_header2(archive_p, entry._entry_p) + if r == ARCHIVE_EOF: + return + yield entry + entry.__class__ = PassedArchiveEntry + + @property + def bytes_read(self): + return ffi.filter_bytes(self._pointer, -1) + + @property + def filter_names(self): + count = ffi.filter_count(self._pointer) + return [ffi.filter_name(self._pointer, i) for i in range(count - 1)] + + @property + def format_name(self): + return ffi.format_name(self._pointer) + + +@contextmanager +def new_archive_read(format_name='all', filter_name='all', passphrase=None): + """Creates an archive struct suitable for reading from an archive. + + Returns a pointer if successful. Raises ArchiveError on error. + """ + archive_p = ffi.read_new() + try: + if passphrase: + if not isinstance(passphrase, bytes): + passphrase = passphrase.encode('utf-8') + try: + ffi.read_add_passphrase(archive_p, passphrase) + except AttributeError: + raise NotImplementedError( + f"the libarchive being used (version {ffi.version_number()}, " + f"path {ffi.libarchive_path}) doesn't support encryption" + ) + ffi.get_read_filter_function(filter_name)(archive_p) + ffi.get_read_format_function(format_name)(archive_p) + yield archive_p + finally: + ffi.read_free(archive_p) + + +@contextmanager +def custom_reader( + read_func, format_name='all', filter_name='all', + open_func=None, seek_func=None, close_func=None, + block_size=page_size, archive_read_class=ArchiveRead, passphrase=None, + header_codec='utf-8', +): + """Read an archive using a custom function. + """ + 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) if close_func else NO_CLOSE_CB + seek_cb = SEEK_CALLBACK(seek_func) + with new_archive_read(format_name, filter_name, passphrase) as archive_p: + if seek_func: + ffi.read_set_seek_callback(archive_p, seek_cb) + ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) + yield archive_read_class(archive_p, header_codec) + + +@contextmanager +def fd_reader( + fd, format_name='all', filter_name='all', block_size=4096, passphrase=None, + header_codec='utf-8', +): + """Read an archive from a file descriptor. + """ + with new_archive_read(format_name, filter_name, passphrase) as archive_p: + try: + block_size = fstat(fd).st_blksize + except (OSError, AttributeError): # pragma: no cover + pass + ffi.read_open_fd(archive_p, fd, block_size) + yield ArchiveRead(archive_p, header_codec) + + +@contextmanager +def file_reader( + path, format_name='all', filter_name='all', block_size=4096, passphrase=None, + header_codec='utf-8', +): + """Read an archive from a file. + """ + with new_archive_read(format_name, filter_name, passphrase) as archive_p: + try: + block_size = stat(path).st_blksize + except (OSError, AttributeError): # pragma: no cover + pass + ffi.read_open_filename_w(archive_p, path, block_size) + yield ArchiveRead(archive_p, header_codec) + + +@contextmanager +def memory_reader( + buf, format_name='all', filter_name='all', passphrase=None, + header_codec='utf-8', +): + """Read an archive from memory. + """ + with new_archive_read(format_name, filter_name, passphrase) as archive_p: + ffi.read_open_memory(archive_p, cast(buf, c_void_p), len(buf)) + yield ArchiveRead(archive_p, header_codec) + + +@contextmanager +def stream_reader( + stream, format_name='all', filter_name='all', block_size=page_size, + passphrase=None, header_codec='utf-8', +): + """Read an archive from a stream. + + The `stream` object must support the standard `readinto` method. + + 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) + + 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 + + def seek_func(archive_p, context, offset, whence): + stream.seek(offset, whence) + # tell libarchive the current position + return stream.tell() + + open_cb = NO_OPEN_CB + read_cb = READ_CALLBACK(read_func) + close_cb = NO_CLOSE_CB + seek_cb = SEEK_CALLBACK(seek_func) + with new_archive_read(format_name, filter_name, passphrase) as archive_p: + if stream.seekable(): + ffi.read_set_seek_callback(archive_p, seek_cb) + ffi.read_open(archive_p, None, open_cb, read_cb, close_cb) + yield ArchiveRead(archive_p, header_codec) + + +seekable_stream_reader = stream_reader diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/write.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/write.py new file mode 100644 index 0000000..7ba191d --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/libarchive/write.py @@ -0,0 +1,279 @@ +from contextlib import contextmanager +from ctypes import byref, cast, c_char, c_size_t, c_void_p, POINTER +from posixpath import join +import warnings + +from . import ffi +from .entry import ArchiveEntry, FileType +from .ffi import ( + OPEN_CALLBACK, WRITE_CALLBACK, CLOSE_CALLBACK, NO_OPEN_CB, NO_CLOSE_CB, + 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, + read_disk_set_behavior +) + + +@contextmanager +def new_archive_read_disk(path, flags=0, lookup=False): + archive_p = read_disk_new() + read_disk_set_behavior(archive_p, flags) + if lookup: + ffi.read_disk_set_standard_lookup(archive_p) + read_disk_open_w(archive_p, path) + try: + yield archive_p + finally: + read_free(archive_p) + + +class ArchiveWrite: + + def __init__(self, archive_p, header_codec='utf-8'): + self._pointer = archive_p + self.header_codec = header_codec + + def add_entries(self, entries): + """Add the given entries to the archive. + """ + write_p = self._pointer + for entry in entries: + write_header(write_p, entry._entry_p) + for block in entry.get_blocks(): + write_data(write_p, block, len(block)) + write_finish_entry(write_p) + + def add_files( + self, *paths, flags=0, lookup=False, pathname=None, recursive=True, + **attributes + ): + """Read files through the OS and add them to the archive. + + Args: + paths (str): the paths of the files to add to the archive + flags (int): + passed to the C function `archive_read_disk_set_behavior`; + use the `libarchive.flags.READDISK_*` constants + lookup (bool): + when True, the C function `archive_read_disk_set_standard_lookup` + is called to enable the lookup of user and group names + pathname (str | None): + the path of the file in the archive, defaults to the source path + recursive (bool): + when False, if a path in `paths` is a directory, + only the directory itself is added. + attributes (dict): passed to `ArchiveEntry.modify()` + + Raises: + ArchiveError: if a file doesn't exist or can't be accessed, or if + adding it to the archive fails + """ + write_p = self._pointer + + block_size = ffi.write_get_bytes_per_block(write_p) + if block_size <= 0: + block_size = 10240 # pragma: no cover + + entry = ArchiveEntry(header_codec=self.header_codec) + entry_p = entry._entry_p + destination_path = attributes.pop('pathname', None) + for path in paths: + with new_archive_read_disk(path, flags, lookup) as read_p: + while 1: + r = read_next_header2(read_p, entry_p) + if r == ARCHIVE_EOF: + break + entry_path = entry.pathname + if destination_path: + if entry_path == path: + entry_path = destination_path + else: + assert entry_path.startswith(path) + entry_path = join( + destination_path, + entry_path[len(path):].lstrip('/') + ) + entry.pathname = entry_path.lstrip('/') + if attributes: + entry.modify(**attributes) + read_disk_descend(read_p) + write_header(write_p, entry_p) + if entry.isreg: + with open(entry_sourcepath(entry_p), 'rb') as f: + while 1: + data = f.read(block_size) + if not data: + break + write_data(write_p, data, len(data)) + write_finish_entry(write_p) + entry_clear(entry_p) + if not recursive: + break + + def add_file(self, path, **kw): + "Single-path alias of `add_files()`" + return self.add_files(path, **kw) + + def add_file_from_memory( + self, entry_path, entry_size, entry_data, + filetype=FileType.REGULAR_FILE, permission=0o664, + **other_attributes + ): + """"Add file from memory to archive. + + Args: + entry_path (str | bytes): the file's path + entry_size (int): the file's size, in bytes + entry_data (bytes | Iterable[bytes]): the file's content + filetype (int): see `libarchive.entry.ArchiveEntry.modify()` + permission (int): see `libarchive.entry.ArchiveEntry.modify()` + other_attributes: see `libarchive.entry.ArchiveEntry.modify()` + """ + archive_pointer = self._pointer + + if isinstance(entry_data, bytes): + entry_data = (entry_data,) + elif isinstance(entry_data, str): + raise TypeError( + "entry_data: expected bytes, got %r" % type(entry_data) + ) + + entry = ArchiveEntry( + pathname=entry_path, size=entry_size, filetype=filetype, + perm=permission, header_codec=self.header_codec, + **other_attributes + ) + write_header(archive_pointer, entry._entry_p) + + for chunk in entry_data: + if not chunk: + break + write_data(archive_pointer, chunk, len(chunk)) + + write_finish_entry(archive_pointer) + + +@contextmanager +def new_archive_write(format_name, filter_name=None, options='', passphrase=None): + archive_p = ffi.write_new() + try: + ffi.get_write_format_function(format_name)(archive_p) + if filter_name: + ffi.get_write_filter_function(filter_name)(archive_p) + if passphrase and 'encryption' not in options: + if format_name == 'zip': + warnings.warn( + "The default encryption scheme of zip archives is weak. " + "Use `options='encryption=$type'` to specify the encryption " + "type you want to use. The supported values are 'zipcrypt' " + "(the weak default), 'aes128' and 'aes256'." + ) + options += ',encryption' if options else 'encryption' + if options: + if not isinstance(options, bytes): + options = options.encode('utf-8') + ffi.write_set_options(archive_p, options) + if passphrase: + if not isinstance(passphrase, bytes): + passphrase = passphrase.encode('utf-8') + try: + ffi.write_set_passphrase(archive_p, passphrase) + except AttributeError: + raise NotImplementedError( + f"the libarchive being used (version {ffi.version_number()}, " + f"path {ffi.libarchive_path}) doesn't support encryption" + ) + yield archive_p + ffi.write_close(archive_p) + ffi.write_free(archive_p) + except Exception: + ffi.write_fail(archive_p) + ffi.write_free(archive_p) + raise + + @property + def bytes_written(self): + return ffi.filter_bytes(self._pointer, -1) + + +@contextmanager +def custom_writer( + write_func, format_name, filter_name=None, + open_func=None, close_func=None, block_size=page_size, + archive_write_class=ArchiveWrite, options='', passphrase=None, + header_codec='utf-8', +): + """Create an archive and send it in chunks to the `write_func` function. + + For formats and filters, see `WRITE_FORMATS` and `WRITE_FILTERS` in the + `libarchive.ffi` module. + """ + + 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) if open_func else NO_OPEN_CB + write_cb = WRITE_CALLBACK(write_cb_internal) + 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: + ffi.write_set_bytes_in_last_block(archive_p, 1) + ffi.write_set_bytes_per_block(archive_p, block_size) + ffi.write_open(archive_p, None, open_cb, write_cb, close_cb) + yield archive_write_class(archive_p, header_codec) + + +@contextmanager +def fd_writer( + fd, format_name, filter_name=None, + archive_write_class=ArchiveWrite, options='', passphrase=None, + header_codec='utf-8', +): + """Create an archive and write it into a file descriptor. + + For formats and filters, see `WRITE_FORMATS` and `WRITE_FILTERS` in the + `libarchive.ffi` module. + """ + with new_archive_write(format_name, filter_name, options, + passphrase) as archive_p: + ffi.write_open_fd(archive_p, fd) + yield archive_write_class(archive_p, header_codec) + + +@contextmanager +def file_writer( + filepath, format_name, filter_name=None, + archive_write_class=ArchiveWrite, options='', passphrase=None, + header_codec='utf-8', +): + """Create an archive and write it into a file. + + For formats and filters, see `WRITE_FORMATS` and `WRITE_FILTERS` in the + `libarchive.ffi` module. + """ + with new_archive_write(format_name, filter_name, options, + passphrase) as archive_p: + ffi.write_open_filename_w(archive_p, filepath) + yield archive_write_class(archive_p, header_codec) + + +@contextmanager +def memory_writer( + buf, format_name, filter_name=None, + archive_write_class=ArchiveWrite, options='', passphrase=None, + header_codec='utf-8', +): + """Create an archive and write it into a buffer. + + For formats and filters, see `WRITE_FORMATS` and `WRITE_FILTERS` in the + `libarchive.ffi` module. + """ + with new_archive_write(format_name, filter_name, options, + passphrase) as archive_p: + used = byref(c_size_t()) + buf_p = cast(buf, c_void_p) + ffi.write_open_memory(archive_p, buf_p, len(buf), used) + yield archive_write_class(archive_p, header_codec) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.cfg b/packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.cfg new file mode 100644 index 0000000..8998725 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.cfg @@ -0,0 +1,12 @@ +[wheel] +universal = 1 + +[flake8] +exclude = .?*,env*/ +ignore = E226,E731,W504 +max-line-length = 85 + +[egg_info] +tag_build = +tag_date = 0 + diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.py new file mode 100644 index 0000000..3100112 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/setup.py @@ -0,0 +1,25 @@ +import os +from os.path import join, dirname + +from setuptools import setup, find_packages + +from version import get_version + +os.umask(0o022) + +with open(join(dirname(__file__), 'README.rst'), encoding="utf-8") as f: + README = f.read() + +setup( + name='libarchive-c', + version=get_version(), + description='Python interface to libarchive', + author='Changaco', + author_email='changaco@changaco.oy.lc', + url='https://github.com/Changaco/python-libarchive-c', + license='CC0', + packages=find_packages(exclude=['tests']), + long_description=README, + long_description_content_type='text/x-rst', + keywords='archive libarchive 7z tar bz2 zip gz', +) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/__init__.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/__init__.py new file mode 100644 index 0000000..7a7f583 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/__init__.py @@ -0,0 +1,136 @@ +from contextlib import closing, contextmanager +from copy import copy +from os import chdir, getcwd, stat, walk +from os.path import abspath, dirname, join +from stat import S_ISREG +import tarfile +try: + from stat import filemode +except ImportError: # Python 2 + filemode = tarfile.filemode + +from libarchive import file_reader + + +data_dir = join(dirname(__file__), 'data') + + +def check_archive(archive, tree): + tree2 = copy(tree) + for e in archive: + epath = str(e).rstrip('/') + assert epath in tree2 + estat = tree2.pop(epath) + assert e.mtime == int(estat['mtime']) + if not e.isdir: + size = e.size + if size is not None: + assert size == estat['size'] + with open(epath, 'rb') as f: + for block in e.get_blocks(): + assert f.read(len(block)) == block + leftover = f.read() + assert not leftover + + # Check that there are no missing directories or files + assert len(tree2) == 0 + + +def get_entries(location): + """ + Using the archive file at `location`, return an iterable of name->value + mappings for each libarchive.ArchiveEntry objects essential attributes. + Paths are base64-encoded because JSON is UTF-8 and cannot handle + arbitrary binary pathdata. + """ + with file_reader(location) as arch: + for entry in arch: + # libarchive introduces prefixes such as h prefix for + # hardlinks: tarfile does not, so we ignore the first char + mode = entry.strmode[1:].decode('ascii') + yield { + 'path': surrogate_decode(entry.pathname), + 'mtime': entry.mtime, + 'size': entry.size, + 'mode': mode, + 'isreg': entry.isreg, + 'isdir': entry.isdir, + 'islnk': entry.islnk, + 'issym': entry.issym, + 'linkpath': surrogate_decode(entry.linkpath), + 'isblk': entry.isblk, + 'ischr': entry.ischr, + 'isfifo': entry.isfifo, + 'isdev': entry.isdev, + 'uid': entry.uid, + 'gid': entry.gid + } + + +def get_tarinfos(location): + """ + Using the tar archive file at `location`, return an iterable of + name->value mappings for each tarfile.TarInfo objects essential + attributes. + Paths are base64-encoded because JSON is UTF-8 and cannot handle + arbitrary binary pathdata. + """ + with closing(tarfile.open(location)) as tar: + for entry in tar: + path = surrogate_decode(entry.path or '') + if entry.isdir() and not path.endswith('/'): + path += '/' + # libarchive introduces prefixes such as h prefix for + # hardlinks: tarfile does not, so we ignore the first char + mode = filemode(entry.mode)[1:] + yield { + 'path': path, + 'mtime': entry.mtime, + 'size': entry.size, + 'mode': mode, + 'isreg': entry.isreg(), + 'isdir': entry.isdir(), + 'islnk': entry.islnk(), + 'issym': entry.issym(), + 'linkpath': surrogate_decode(entry.linkpath or None), + 'isblk': entry.isblk(), + 'ischr': entry.ischr(), + 'isfifo': entry.isfifo(), + 'isdev': entry.isdev(), + 'uid': entry.uid, + 'gid': entry.gid + } + + +@contextmanager +def in_dir(dirpath): + prev = abspath(getcwd()) + chdir(dirpath) + try: + yield + finally: + chdir(prev) + + +def stat_dict(path): + keys = set(('uid', 'gid', 'mtime')) + mode, _, _, _, uid, gid, size, _, mtime, _ = stat(path) + if S_ISREG(mode): + keys.add('size') + return {k: v for k, v in locals().items() if k in keys} + + +def treestat(d, stat_dict=stat_dict): + r = {} + for dirpath, dirnames, filenames in walk(d): + r[dirpath] = stat_dict(dirpath) + for fname in filenames: + fpath = join(dirpath, fname) + r[fpath] = stat_dict(fpath) + return r + + +def surrogate_decode(o): + if isinstance(o, bytes): + return o.decode('utf8', errors='surrogateescape') + return o diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/flags.tar b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/flags.tar new file mode 100644 index 0000000000000000000000000000000000000000..24e6c7f076601b3fec4bafc78c615381e9e97c42 GIT binary patch literal 10240 zcmeIwK@Ng25QX6$g$oo)fN~zt8i+^<3?|;*cIQS6xMA^6LS`{cXun4$k13m8()m#j zecQ~Ju75gx(c%`-O}qGE6{m0g;MCDFp=?>I(DJ%6pFNIZsl3RIHDzUsLWQ?%ZB=59 zesIs;x=1r8i5lJAZ|(h>Z|$CVmZsm+zk1Q}`78Wz_ZaZL{6qZYf8xJ}qalC*0tg_0 q00IagfB*srAb`hd literal 0 HcmV?d00001 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/special.tar b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/special.tar new file mode 100644 index 0000000000000000000000000000000000000000..8fd0c50c92a54790f66e231202f97a196e17f2f0 GIT binary patch literal 112640 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 P0fGQQfFSTsLE!%Zx!eFH literal 0 HcmV?d00001 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/tar_relative.tar b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/tar_relative.tar new file mode 100644 index 0000000000000000000000000000000000000000..0caff864506dd5c31a8b108fe68f77c2e28faf32 GIT binary patch literal 10240 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 literal 0 HcmV?d00001 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/testtar.tar.json b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/testtar.tar.json new file mode 100644 index 0000000..46e3f0b --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/testtar.tar.json @@ -0,0 +1,665 @@ +[ + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/conttype", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/regtype", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": true, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rwxr-xr-x", + "mtime": 1041808783, + "path": "ustar/dirtype/", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": true, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rwxr-xr-x", + "mtime": 1041808783, + "path": "ustar/dirtype-with-size/", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": true, + "isreg": false, + "issym": false, + "linkpath": "ustar/regtype", + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/lnktype", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": true, + "linkpath": "regtype", + "mode": "rwxrwxrwx", + "mtime": 1041808783, + "path": "ustar/symtype", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": true, + "ischr": false, + "isdev": true, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rw-rw----", + "mtime": 1041808783, + "path": "ustar/blktype", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": true, + "isdev": true, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rw-rw-rw-", + "mtime": 1041808783, + "path": "ustar/chrtype", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": true, + "isdir": false, + "isfifo": true, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/fifotype", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/sparse", + "size": 86016, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/umlauts-\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/12345/1234567/longname", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": true, + "linkpath": "../linktest1/regtype", + "mode": "rwxrwxrwx", + "mtime": 1041808783, + "path": "./ustar/linktest2/symtype", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "ustar/linktest1/regtype", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": true, + "isreg": false, + "issym": false, + "linkpath": "./ustar/linktest1/regtype", + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "./ustar/linktest2/lnktype", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": true, + "linkpath": "ustar/regtype", + "mode": "rwxrwxrwx", + "mtime": 1041808783, + "path": "symtype2", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "gnu/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/longname", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": true, + "isreg": false, + "issym": false, + "linkpath": "gnu/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/longname", + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "gnu/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/longlink", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "gnu/sparse", + "size": 86016, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "gnu/sparse-0.0", + "size": 86016, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "gnu/sparse-0.1", + "size": 86016, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "gnu/sparse-1.0", + "size": 86016, + "uid": 1000 + }, + { + "gid": 4294967295, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "gnu/regtype-gnu-uid", + "size": 7011, + "uid": 4294967295 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "misc/regtype-old-v7", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "misc/regtype-hpux-signed-chksum-\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "misc/regtype-old-v7-signed-chksum-\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": true, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rwxr-xr-x", + "mtime": 1041808783, + "path": "misc/dirtype-old-v7/", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "misc/regtype-suntar", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "misc/regtype-xstar", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/longname", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": true, + "isreg": false, + "issym": false, + "linkpath": "pax/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/longname", + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/123/longlink", + "size": 0, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/umlauts-\u00c4\u00d6\u00dc\u00e4\u00f6\u00fc\u00df", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/regtype1", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/regtype2", + "size": 7011, + "uid": 1000 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/regtype3", + "size": 7011, + "uid": 1000 + }, + { + "gid": 123, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/regtype4", + "size": 7011, + "uid": 123 + }, + { + "gid": 1000, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/bad-pax-\udce4\udcf6\udcfc", + "size": 7011, + "uid": 1000 + }, + { + "gid": 0, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "pax/hdrcharset-\udce4\udcf6\udcfc", + "size": 7011, + "uid": 0 + }, + { + "gid": 100, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-r--r--", + "mtime": 1041808783, + "path": "misc/eof", + "size": 0, + "uid": 1000 + } +] diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.tar b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.tar new file mode 100644 index 0000000000000000000000000000000000000000..bbaded7ac0663e32af100bc23452b8f4b5035c95 GIT binary patch literal 10240 zcmeIv-3h`l6o%o9k_FVHG>yFy@!lN@wpg%@v=P*49KbDH%v8M*3Rchy@jaA=972Ac zFPfTkBEDNHlvPUH#w6}W9lqPV>xB|pC$TlL);9FR-^4yp_Bc+(HsRgN3E;!qTJ+TzxN+_U>ESf{FSv)$lqF> z2&q1B_RB5EKb<^spFIUHU(yX-@P7V@v4;GI`mzoI1Q0*~0R#|0009ILKmY**5I_I{ Z1Q0*~0R#|0009ILKmY**5J2F61+K72QaAtr literal 0 HcmV?d00001 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.tar.json b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.tar.json new file mode 100644 index 0000000..85c631e --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.tar.json @@ -0,0 +1,53 @@ +[ + { + "gid": 513, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": true, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rwx------", + "mtime": 1319027321, + "path": "2859/", + "size": 0, + "uid": 500 + }, + { + "gid": 513, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rwx------", + "mtime": 1319027194, + "path": "2859/Copy of h\u00e0nz\u00ec-somefile.txt", + "size": 0, + "uid": 500 + }, + { + "gid": 513, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rwx------", + "mtime": 1319027194, + "path": "2859/h\u00e0nz\u00ec?-somefile.txt ", + "size": 0, + "uid": 500 + } +] \ No newline at end of file diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.zip b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode.zip new file mode 100644 index 0000000000000000000000000000000000000000..c3c5f3f6d7b92a6091543baac940c92ea5a015f0 GIT binary patch literal 668 zcmWIWW@h1H0D(U-`8Hq%lwe|zVMx>u4dG;9{!=&4Ti^D3NNEK(10%}|W(Ec@5dhK4 z!N9NusJDlQ<6Rab0|N;2fc2&qm7eLy(<{hJ2b)q1G^M3}o;R8)odJICyj)TsNAP-j zxC8-d0U+k#U}IollzG4_4&-nactjR6FmMZjFyp1Wb$>vDC9V-A&iT2ysd*&~44qoN zAPwxEE{-7;bEXDba~*OJsIB7osyA!*i`Xv`WlkFe*XXQSqvNB%xV9;bZB0)^=h@=4 z$r)wg+|0~3=YF4A$vtU8zk$8vs>xyc?*y-}GClwNja%$3fsFHW4qi-jS<=gGtFT@# zYyP9ifURxqioK4n7`7z#Oqp97m|=FNf?xc@Qo9C8iTg=@eZp*>2gn{u$P`cD zxzQDM|JEmL}erJFIrI<+%AI>b~IaSnRgc#dnEtsn_vYGj1~eU@}_u?2YBMBX{^t zh)<9`tNd3f?kz)Yqy4I9dl`6g+M@S(90LXu1B0ilpUXO@gr)#*MkYCCT(KbaC5N6Jj@&hh9!-l1eK_doDP5{g3*?VV1f&Ikn1_Rnfq_xx0k1fa!&%@FS3rgzWI%P*fcTrE*8}62;+a6k)cxUdg+Rc{Z=98%Vg1cj}+fozopr z0=yZS>=|&ynhIDgB=AAZM13v>1+W++g9O7muZ3HVo;b${qkI1>0P`Vc;)-dQnGHa5 uKxQTCBjO#|bOx`DrfEk`G0s(n*aD*iyjg*f%D}(`gqwi$7GU~eU;qGtLd35C literal 0 HcmV?d00001 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode2.zip.json b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode2.zip.json new file mode 100644 index 0000000..bd34813 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/unicode2.zip.json @@ -0,0 +1,36 @@ +[ + { + "gid": 0, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": true, + "isfifo": false, + "islnk": false, + "isreg": false, + "issym": false, + "linkpath": null, + "mode": "rwxrwxr-x", + "mtime": 1381752672, + "path": "a/", + "size": 0, + "uid": 0 + }, + { + "gid": 0, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-rw-r--", + "mtime": 1268681860, + "path": "a/gru\u0308n.png", + "size": 362, + "uid": 0 + } +] \ No newline at end of file diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.README b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.README new file mode 100644 index 0000000..de6ba98 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.README @@ -0,0 +1,3 @@ +Test file from borrowed from +https://github.com/libarchive/libarchive/issues/459 +http://libarchive.github.io/google-code/issue-350/comment-0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8.zip diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.zip b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.zip new file mode 100644 index 0000000000000000000000000000000000000000..5407833c668d37cc612d165a45f7fa3a6ef7e898 GIT binary patch literal 355 zcmWIWW@h1H00DgubC;HRFShUj*&xixAj6Q6nv;{SS5i?D8p6rIyi?&}5D1r6a5FHn zd}Cx_023Y{MLG)M`9(P?id+E@134HNq<{u0ero!~3p5Ob1%L*=ozn7p;hb07cf6jr z0&ebPpqU_yW-hW?ps|cha?H3~DgiW}fdS|shAoXC7LvPJA?`*q8*Us{cOn}X2s94a bB@kBwO~vBk0B=?{ko8PJxD-gQ0C5-qW$9W? literal 0 HcmV?d00001 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.zip.json b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.zip.json new file mode 100644 index 0000000..9ba3534 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/data/프로그램.zip.json @@ -0,0 +1,36 @@ +[ + { + "gid": 502, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-rw-r--", + "mtime": 1390485689, + "path": "hello.txt", + "size": 14, + "uid": 502 + }, + { + "gid": 502, + "isblk": false, + "ischr": false, + "isdev": false, + "isdir": false, + "isfifo": false, + "islnk": false, + "isreg": true, + "issym": false, + "linkpath": null, + "mode": "rw-rw-r--", + "mtime": 1390485651, + "path": "\ud504\ub85c\uadf8\ub7a8.txt", + "size": 13, + "uid": 502 + } +] \ No newline at end of file diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_atime_mtime_ctime.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_atime_mtime_ctime.py new file mode 100644 index 0000000..637da36 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_atime_mtime_ctime.py @@ -0,0 +1,127 @@ +from copy import copy +from os import stat + +from libarchive import (file_reader, file_writer, memory_reader, memory_writer) + +import pytest + +from . import treestat + + +# NOTE: zip does not support high resolution time data, but pax and others do +def check_atime_ctime(archive, tree, timefmt=int): + tree2 = copy(tree) + for entry in archive: + epath = str(entry).rstrip('/') + assert epath in tree2 + estat = tree2.pop(epath) + assert entry.atime == timefmt(estat.st_atime) + assert entry.ctime == timefmt(estat.st_ctime) + + +def stat_dict(path): + # return the raw stat output, the tuple output only returns ints + return stat(path) + + +def time_check(time_tuple, timefmt): + seconds, nanos = time_tuple + maths = float(seconds) + float(nanos) / 1000000000.0 + return timefmt(maths) + + +@pytest.mark.parametrize('archfmt,timefmt', [('zip', int), ('pax', float)]) +def test_memory_atime_ctime(archfmt, timefmt): + # Collect information on what should be in the archive + tree = treestat('libarchive', stat_dict) + + # Create an archive of our libarchive/ directory + buf = bytes(bytearray(1000000)) + with memory_writer(buf, archfmt) as archive1: + archive1.add_files('libarchive/') + + # Check the data + with memory_reader(buf) as archive2: + check_atime_ctime(archive2, tree, timefmt=timefmt) + + +@pytest.mark.parametrize('archfmt,timefmt', [('zip', int), ('pax', float)]) +def test_file_atime_ctime(archfmt, timefmt, tmpdir): + archive_path = "{0}/test.{1}".format(tmpdir.strpath, archfmt) + + # Collect information on what should be in the archive + tree = treestat('libarchive', stat_dict) + + # Create an archive of our libarchive/ directory + with file_writer(archive_path, archfmt) as archive: + archive.add_files('libarchive/') + + # Read the archive and check that the data is correct + with file_reader(archive_path) as archive: + check_atime_ctime(archive, tree, timefmt=timefmt) + + +@pytest.mark.parametrize('archfmt,timefmt', [('zip', int), ('pax', float)]) +def test_memory_time_setters(archfmt, timefmt): + has_birthtime = archfmt != 'zip' + + # Create an archive of our libarchive/ directory + buf = bytes(bytearray(1000000)) + with memory_writer(buf, archfmt) as archive1: + archive1.add_files('libarchive/') + + atimestamp = (1482144741, 495628118) + mtimestamp = (1482155417, 659017086) + ctimestamp = (1482145211, 536858081) + btimestamp = (1482144740, 495628118) + buf2 = bytes(bytearray(1000000)) + with memory_reader(buf) as archive1: + with memory_writer(buf2, archfmt) as archive2: + for entry in archive1: + entry.set_atime(*atimestamp) + entry.set_mtime(*mtimestamp) + entry.set_ctime(*ctimestamp) + if has_birthtime: + entry.set_birthtime(*btimestamp) + archive2.add_entries([entry]) + + with memory_reader(buf2) as archive2: + for entry in archive2: + assert entry.atime == time_check(atimestamp, timefmt) + assert entry.mtime == time_check(mtimestamp, timefmt) + assert entry.ctime == time_check(ctimestamp, timefmt) + if has_birthtime: + assert entry.birthtime == time_check(btimestamp, timefmt) + + +@pytest.mark.parametrize('archfmt,timefmt', [('zip', int), ('pax', float)]) +def test_file_time_setters(archfmt, timefmt, tmpdir): + has_birthtime = archfmt != 'zip' + + # Create an archive of our libarchive/ directory + archive_path = tmpdir.join('/test.{0}'.format(archfmt)).strpath + archive2_path = tmpdir.join('/test2.{0}'.format(archfmt)).strpath + with file_writer(archive_path, archfmt) as archive1: + archive1.add_files('libarchive/') + + atimestamp = (1482144741, 495628118) + mtimestamp = (1482155417, 659017086) + ctimestamp = (1482145211, 536858081) + btimestamp = (1482144740, 495628118) + with file_reader(archive_path) as archive1: + with file_writer(archive2_path, archfmt) as archive2: + for entry in archive1: + entry.set_atime(*atimestamp) + entry.set_mtime(*mtimestamp) + entry.set_ctime(*ctimestamp) + if has_birthtime: + entry.set_birthtime(*btimestamp) + archive2.add_entries([entry]) + + with file_reader(archive2_path) as archive2: + for entry in archive2: + assert entry.atime == time_check(atimestamp, timefmt) + assert entry.mtime == time_check(mtimestamp, timefmt) + assert entry.ctime == time_check(ctimestamp, timefmt) + if has_birthtime: + assert entry.birthtime == time_check(btimestamp, timefmt) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_convert.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_convert.py new file mode 100644 index 0000000..ff1bc75 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_convert.py @@ -0,0 +1,24 @@ +from libarchive import memory_reader, memory_writer + +from . import check_archive, treestat + + +def test_convert(): + + # Collect information on what should be in the archive + tree = treestat('libarchive') + + # Create an archive of our libarchive/ directory + buf = bytes(bytearray(1000000)) + with memory_writer(buf, 'gnutar', 'xz') as archive1: + archive1.add_files('libarchive/') + + # Convert the archive to another format + buf2 = bytes(bytearray(1000000)) + with memory_reader(buf) as archive1: + with memory_writer(buf2, 'zip') as archive2: + archive2.add_entries(archive1) + + # Check the data + with memory_reader(buf2) as archive2: + check_archive(archive2, tree) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_entry.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_entry.py new file mode 100644 index 0000000..419cecb --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_entry.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- + +from codecs import open +import json +import locale +from os import environ, stat +from os.path import join +import unicodedata + +import pytest + +from libarchive import memory_reader, memory_writer +from libarchive.entry import ArchiveEntry, ConsumedArchiveEntry, PassedArchiveEntry + +from . import data_dir, get_entries, get_tarinfos + + +text_type = unicode if str is bytes else str # noqa: F821 + +locale.setlocale(locale.LC_ALL, '') + +# needed for sane time stamp comparison +environ['TZ'] = 'UTC' + + +def test_entry_properties(): + + buf = bytes(bytearray(1000000)) + with memory_writer(buf, 'gnutar') as archive: + archive.add_files('README.rst') + + readme_stat = stat('README.rst') + + with memory_reader(buf) as archive: + for entry in archive: + assert entry.uid == readme_stat.st_uid + assert entry.gid == readme_stat.st_gid + assert entry.mode == readme_stat.st_mode + assert not entry.isblk + assert not entry.ischr + assert not entry.isdir + assert not entry.isfifo + assert not entry.islnk + assert not entry.issym + assert not entry.linkpath + assert entry.linkpath == entry.linkname + assert entry.isreg + assert entry.isfile + assert not entry.issock + assert not entry.isdev + assert b'rw' in entry.strmode + assert entry.pathname == entry.path + assert entry.pathname == entry.name + + +def test_check_ArchiveEntry_against_TarInfo(): + for name in ('special.tar', 'tar_relative.tar'): + path = join(data_dir, name) + tarinfos = list(get_tarinfos(path)) + entries = list(get_entries(path)) + for tarinfo, entry in zip(tarinfos, entries): + assert tarinfo == entry + assert len(tarinfos) == len(entries) + + +def test_check_archiveentry_using_python_testtar(): + check_entries(join(data_dir, 'testtar.tar')) + + +def test_check_archiveentry_with_unicode_and_binary_entries_tar(): + check_entries(join(data_dir, 'unicode.tar')) + + +def test_check_archiveentry_with_unicode_and_binary_entries_zip(): + check_entries(join(data_dir, 'unicode.zip')) + + +def test_check_archiveentry_with_unicode_and_binary_entries_zip2(): + check_entries(join(data_dir, 'unicode2.zip'), ignore='mode') + + +def test_check_archiveentry_with_unicode_entries_and_name_zip(): + check_entries(join(data_dir, '\ud504\ub85c\uadf8\ub7a8.zip')) + + +def check_entries(test_file, regen=False, ignore=''): + ignore = ignore.split() + fixture_file = test_file + '.json' + if regen: + entries = list(get_entries(test_file)) + with open(fixture_file, 'w', encoding='UTF-8') as ex: + json.dump(entries, ex, indent=2, sort_keys=True) + with open(fixture_file, encoding='UTF-8') as ex: + expected = json.load(ex) + actual = list(get_entries(test_file)) + for e1, e2 in zip(actual, expected): + for key in ignore: + e1.pop(key) + e2.pop(key) + # Normalize all unicode (can vary depending on the system) + for d in (e1, e2): + for key in d: + if isinstance(d[key], text_type): + d[key] = unicodedata.normalize('NFC', d[key]) + assert e1 == e2 + + +def test_the_life_cycle_of_archive_entries(): + """Check that `get_blocks` only works on the current entry, and only once. + """ + # Create a test archive in memory + buf = bytes(bytearray(10_000_000)) + with memory_writer(buf, 'gnutar') as archive: + archive.add_files( + 'README.rst', + 'libarchive/__init__.py', + 'libarchive/entry.py', + ) + # Read multiple entries of the test archive and check how the evolve + with memory_reader(buf) as archive: + archive_iter = iter(archive) + entry1 = next(archive_iter) + assert type(entry1) is ArchiveEntry + for block in entry1.get_blocks(): + pass + assert type(entry1) is ConsumedArchiveEntry + with pytest.raises(TypeError): + entry1.get_blocks() + entry2 = next(archive_iter) + assert type(entry2) is ArchiveEntry + assert type(entry1) is PassedArchiveEntry + with pytest.raises(TypeError): + entry1.get_blocks() + entry3 = next(archive_iter) + assert type(entry3) is ArchiveEntry + assert type(entry2) is PassedArchiveEntry + assert type(entry1) is PassedArchiveEntry + + +def test_non_ASCII_encoding_of_file_metadata(): + buf = bytes(bytearray(100_000)) + file_name = 'README.rst' + encoded_file_name = 'README.rst'.encode('cp037') + with memory_writer(buf, 'ustar', header_codec='cp037') as archive: + archive.add_file(file_name) + with memory_reader(buf) as archive: + entry = next(iter(archive)) + assert entry.pathname == encoded_file_name + with memory_reader(buf, header_codec='cp037') as archive: + entry = next(iter(archive)) + assert entry.pathname == file_name diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_errors.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_errors.py new file mode 100644 index 0000000..513c86a --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_errors.py @@ -0,0 +1,40 @@ +from errno import ENOENT + +import pytest + +from libarchive import ArchiveError, ffi, memory_writer + + +def test_add_files_nonexistent(): + with memory_writer(bytes(bytearray(4096)), 'zip') as archive: + with pytest.raises(ArchiveError) as e: + archive.add_files('nonexistent') + assert e.value.msg + assert e.value.errno == ENOENT + assert e.value.retcode == -25 + + +def test_check_int_logs_warnings(monkeypatch): + calls = [] + monkeypatch.setattr(ffi.logger, 'warning', lambda *_: calls.append(1)) + archive_p = ffi.write_new() + ffi.check_int(ffi.ARCHIVE_WARN, print, [archive_p]) + assert calls == [1] + + +def test_check_null(): + with pytest.raises(ArchiveError) as e: + ffi.check_null(None, print, []) + assert str(e) + + +def test_error_string_decoding(monkeypatch): + monkeypatch.setattr(ffi, 'error_string', lambda *_: None) + r = ffi._error_string(None) + assert r is None + monkeypatch.setattr(ffi, 'error_string', lambda *_: b'a') + r = ffi._error_string(None) + assert isinstance(r, type('')) + monkeypatch.setattr(ffi, 'error_string', lambda *_: '\xe9'.encode('utf8')) + r = ffi._error_string(None) + assert isinstance(r, bytes) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_rwx.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_rwx.py new file mode 100644 index 0000000..77c16fc --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_rwx.py @@ -0,0 +1,183 @@ +"""Test reading, writing and extracting archives.""" + +import io +import json + +import libarchive +from libarchive.entry import format_time +from libarchive.extract import EXTRACT_OWNER, EXTRACT_PERM, EXTRACT_TIME +from libarchive.write import memory_writer +from unittest.mock import patch +import pytest + +from . import check_archive, in_dir, treestat + + +def test_buffers(tmpdir): + + # Collect information on what should be in the archive + tree = treestat('libarchive') + + # Create an archive of our libarchive/ directory + buf = bytes(bytearray(1000000)) + with libarchive.memory_writer(buf, 'gnutar', 'xz') as archive: + archive.add_files('libarchive/') + + # Read the archive and check that the data is correct + with libarchive.memory_reader(buf) as archive: + check_archive(archive, tree) + assert archive.format_name == b'GNU tar format' + assert archive.filter_names == [b'xz'] + + # Extract the archive in tmpdir and check that the data is intact + with in_dir(tmpdir.strpath): + flags = EXTRACT_OWNER | EXTRACT_PERM | EXTRACT_TIME + libarchive.extract_memory(buf, flags) + tree2 = treestat('libarchive') + assert tree2 == tree + + +def test_fd(tmpdir): + archive_file = open(tmpdir.strpath+'/test.tar.bz2', 'w+b') + fd = archive_file.fileno() + + # Collect information on what should be in the archive + tree = treestat('libarchive') + + # Create an archive of our libarchive/ directory + with libarchive.fd_writer(fd, 'gnutar', 'bzip2') as archive: + archive.add_files('libarchive/') + + # Read the archive and check that the data is correct + archive_file.seek(0) + with libarchive.fd_reader(fd) as archive: + check_archive(archive, tree) + assert archive.format_name == b'GNU tar format' + assert archive.filter_names == [b'bzip2'] + + # Extract the archive in tmpdir and check that the data is intact + archive_file.seek(0) + with in_dir(tmpdir.strpath): + flags = EXTRACT_OWNER | EXTRACT_PERM | EXTRACT_TIME + libarchive.extract_fd(fd, flags) + tree2 = treestat('libarchive') + assert tree2 == tree + + +def test_files(tmpdir): + archive_path = tmpdir.strpath+'/test.tar.gz' + + # Collect information on what should be in the archive + tree = treestat('libarchive') + + # Create an archive of our libarchive/ directory + with libarchive.file_writer(archive_path, 'ustar', 'gzip') as archive: + archive.add_files('libarchive/') + + # Read the archive and check that the data is correct + with libarchive.file_reader(archive_path) as archive: + check_archive(archive, tree) + assert archive.format_name == b'POSIX ustar format' + assert archive.filter_names == [b'gzip'] + + # Extract the archive in tmpdir and check that the data is intact + with in_dir(tmpdir.strpath): + flags = EXTRACT_OWNER | EXTRACT_PERM | EXTRACT_TIME + libarchive.extract_file(archive_path, flags) + tree2 = treestat('libarchive') + assert tree2 == tree + + +def test_custom_writer_and_stream_reader(): + # Collect information on what should be in the archive + tree = treestat('libarchive') + + # Create an archive of our libarchive/ directory + stream = io.BytesIO() + with libarchive.custom_writer(stream.write, 'zip') as archive: + archive.add_files('libarchive/') + stream.seek(0) + + # Read the archive and check that the data is correct + with libarchive.stream_reader(stream, 'zip') as archive: + check_archive(archive, tree) + assert archive.format_name == b'ZIP 2.0 (deflation)' + assert archive.filter_names == [] + + +@patch('libarchive.ffi.write_fail') +def test_write_fail(write_fail_mock): + buf = bytes(bytearray(1000000)) + try: + with memory_writer(buf, 'gnutar', 'xz') as archive: + archive.add_files('libarchive/') + raise TypeError + except TypeError: + pass + assert write_fail_mock.called + + +@patch('libarchive.ffi.write_fail') +def test_write_not_fail(write_fail_mock): + buf = bytes(bytearray(1000000)) + with memory_writer(buf, 'gnutar', 'xz') as archive: + archive.add_files('libarchive/') + assert not write_fail_mock.called + + +def test_adding_nonexistent_file_to_archive(): + stream = io.BytesIO() + with libarchive.custom_writer(stream.write, 'zip') as archive: + with pytest.raises(libarchive.ArchiveError): + archive.add_files('nonexistent') + archive.add_files('libarchive/') + + +@pytest.mark.parametrize( + 'archfmt,data_bytes', + [('zip', b'content'), + ('gnutar', b''), + ('pax', json.dumps({'a': 1, 'b': 2, 'c': 3}).encode()), + ('7zip', b'lorem\0ipsum')]) +def test_adding_entry_from_memory(archfmt, data_bytes): + entry_path = 'testfile.data' + entry_data = data_bytes + entry_size = len(data_bytes) + + blocks = [] + + archfmt = 'zip' + has_birthtime = archfmt != 'zip' + + atime = (1482144741, 495628118) + mtime = (1482155417, 659017086) + ctime = (1482145211, 536858081) + btime = (1482144740, 495628118) if has_birthtime else None + + def write_callback(data): + blocks.append(data[:]) + return len(data) + + with libarchive.custom_writer(write_callback, archfmt) as archive: + archive.add_file_from_memory( + entry_path, entry_size, entry_data, + atime=atime, mtime=mtime, ctime=ctime, birthtime=btime, + uid=1000, gid=1000, + ) + + buf = b''.join(blocks) + with libarchive.memory_reader(buf) as memory_archive: + for archive_entry in memory_archive: + expected = entry_data + actual = b''.join(archive_entry.get_blocks()) + assert expected == actual + assert archive_entry.path == entry_path + assert archive_entry.atime in (atime[0], format_time(*atime)) + assert archive_entry.mtime in (mtime[0], format_time(*mtime)) + assert archive_entry.ctime in (ctime[0], format_time(*ctime)) + if has_birthtime: + assert archive_entry.birthtime in ( + btime[0], format_time(*btime) + ) + assert archive_entry.uid == 1000 + assert archive_entry.gid == 1000 diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_security_flags.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_security_flags.py new file mode 100644 index 0000000..f279eaf --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tests/test_security_flags.py @@ -0,0 +1,36 @@ +"""Test security-related extraction flags.""" + +import pytest +import os + +from libarchive import extract_file, file_reader +from libarchive.extract import ( + EXTRACT_SECURE_NOABSOLUTEPATHS, EXTRACT_SECURE_NODOTDOT, +) +from libarchive.exception import ArchiveError +from . import data_dir + + +def run_test(flags): + archive_path = os.path.join(data_dir, 'flags.tar') + try: + extract_file(archive_path, 0) + with pytest.raises(ArchiveError): + extract_file(archive_path, flags) + finally: + with file_reader(archive_path) as archive: + for entry in archive: + if os.path.exists(entry.pathname): + os.remove(entry.pathname) + + +def test_extraction_is_secure_by_default(): + run_test(None) + + +def test_explicit_no_dot_dot(): + run_test(EXTRACT_SECURE_NODOTDOT) + + +def test_explicit_no_absolute_paths(): + run_test(EXTRACT_SECURE_NOABSOLUTEPATHS) diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/tox.ini b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tox.ini new file mode 100644 index 0000000..bfa1601 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/tox.ini @@ -0,0 +1,14 @@ +[tox] +envlist=py38,py39,py310,py311 +skipsdist=True + +[testenv] +passenv = LIBARCHIVE +commands= + python -m pytest -Wd -vv --forked --cov libarchive --cov-report term-missing {toxinidir}/tests {posargs} + flake8 {toxinidir} +deps= + flake8 + pytest + pytest-cov + pytest-forked diff --git a/packages/libarchive-c/opengnsys-libarchive-c-5.1/version.py b/packages/libarchive-c/opengnsys-libarchive-c-5.1/version.py new file mode 100644 index 0000000..40656b7 --- /dev/null +++ b/packages/libarchive-c/opengnsys-libarchive-c-5.1/version.py @@ -0,0 +1,45 @@ +# Source: https://github.com/Changaco/version.py + +from os.path import dirname, isdir, join +import re +from subprocess import CalledProcessError, check_output + + +PREFIX = '' + +tag_re = re.compile(r'\btag: %s([0-9][^,]*)\b' % PREFIX) +version_re = re.compile('^Version: (.+)$', re.M) + + +def get_version(): + # Return the version if it has been injected into the file by git-archive + version = tag_re.search('$Format:%D$') + if version: + return version.group(1) + + d = dirname(__file__) + + if isdir(join(d, '.git')): + # Get the version using "git describe". + cmd = 'git describe --tags --match %s[0-9]* --dirty' % PREFIX + try: + version = check_output(cmd.split()).decode().strip()[len(PREFIX):] + except CalledProcessError: + raise RuntimeError('Unable to get version number from git tags') + + # PEP 440 compatibility + if '-' in version: + if version.endswith('-dirty'): + raise RuntimeError('The working tree is dirty') + version = '.post'.join(version.split('-')[:2]) + + else: + # Extract the version from the PKG-INFO file. + with open(join(d, 'PKG-INFO'), encoding='utf-8', errors='replace') as f: + version = version_re.search(f.read()).group(1) + + return version + + +if __name__ == '__main__': + print(get_version()) diff --git a/packages/libarchive-c/opengnsys-libarchive-c_5.1.debian.tar.xz b/packages/libarchive-c/opengnsys-libarchive-c_5.1.debian.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..ba9dca879c13e92434727edb9617572d6f667c3f GIT binary patch literal 6788 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 literal 0 HcmV?d00001