Skip to content

Cannot extract symbolic links with extractall factory callback #664

Description

@mxmlnkn

When using extractall with the factory, see #660, there seems to be no way to handle symlinks. The read method is simply called with the symbolic link path itself instead of the file contents, resulting in the file being extracted wrong.

To Reproduce

Create file with 7z archive with relative symbolic link:

mkdir -p foo && ( cd foo && echo iriya > ufo && ln -s ufo saucer; ) &&
7z a -snl with-symlink.7z foo &&
rm -r foo/

The factory example as fixed in #660 with with-symlink.7z given as the file path:

from typing import override, Union, Optional
from py7zr import Py7zIO, SevenZipFile, WriterFactory

class MyIO(Py7zIO):
    def __init__(self, limit):
        self._limit = limit
        self._buffer = None
        self._empty = True

    @override
    def write(self, s: Union[bytes, bytearray]):
        """keep only bytes of limit"""
        if self._empty:
            self._buffer = s[:self._limit]
            self._empty = False

    @override
    def read(self, size: Optional[int] = None) -> bytes:
        size = 0 if size is None else size
        if self._empty:
            return bytes()
        return self._buffer[:size]

    @override
    def seek(self, offset: int, whence: int = 0) -> int:
        return 0

    @override
    def flush(self) -> None:
        pass

    @override
    def size(self) -> int:
        return len(self._buffer)


class MyFactory(WriterFactory):
    def __init__(self, size):
        self.size = size
        self.products = {}

    @override
    def create(self, filename: str) -> Py7zIO:
        product = MyIO(self.size)
        self.products[filename] = product
        return product


size = 10
factory = MyFactory(size)
with SevenZipFile('with-symlink.7z', 'r') as archive:
    archive.extractall(factory=factory)
for filename, fileobj in factory.products.items():
    print(f'{filename}: {fileobj.read(size)}...')

Output:

foo/saucer: bytearray(b'ufo')...
foo/ufo: bytearray(b'iriya\n')...

As can be seen, the symbolic link foo/saucer pointing to -> ufo relatively, simply gets called with Py7zrIO.write(<link contents>) and there is no way to detect that it is a symbolic link and not real file data.

Expected behavior

I think, ideally this meta information should be somehow given to the MyFactory.create call as an additional argument or such, so that a symbolic link can be created on the file system instead of creating a Py7zIO-derived file object.

Environment (please complete the following information):

  • OS: Ubuntu 24.04
  • Python: 3.12.3
  • py7zr version: 1.0.0

Test data(please attach in the report):

test-file-is-in-zip-because-of-github-upload-restrictions.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfor extractionIssue on extraction, decompression or decryptionhelp wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions