Skip to content

Commit 8baa8f6

Browse files
authored
Rewind file pointer and be compatible with 3.13.13 (#84)
1 parent cf91787 commit 8baa8f6

2 files changed

Lines changed: 22 additions & 6 deletions

File tree

dissect/hypervisor/util/vmtar.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ class VisorTarInfo(tarfile.TarInfo):
2222
fixUpPgs: int | None
2323

2424
@classmethod
25-
def frombuf(cls, buf: bytes, encoding: str, errors: str) -> VisorTarInfo:
26-
obj = super().frombuf(buf, encoding, errors)
27-
25+
def _init_visor_attrs(cls, obj: VisorTarInfo, buf: bytes) -> None:
26+
"""Initialize visor-specific attributes from the raw header buffer."""
2827
obj.is_visor = buf[257:264] == b"visor "
2928
if obj.is_visor:
3029
obj.offset_data = struct.unpack("<I", buf[496:500])[0]
@@ -35,6 +34,19 @@ def frombuf(cls, buf: bytes, encoding: str, errors: str) -> VisorTarInfo:
3534
obj.textPgs = None
3635
obj.fixUpPgs = None
3736

37+
@classmethod
38+
def frombuf(cls, buf: bytes, encoding: str, errors: str) -> VisorTarInfo:
39+
obj = super().frombuf(buf, encoding, errors)
40+
cls._init_visor_attrs(obj, buf)
41+
return obj
42+
43+
@classmethod
44+
def _frombuf(cls, buf: bytes, encoding: str, errors: str, **kwargs) -> VisorTarInfo:
45+
# Python 3.13.13+ refactored tarfile to call _frombuf directly instead of
46+
# frombuf in fromtarfile and _proc_gnulong/_proc_pax. We must override _frombuf
47+
# to ensure visor-specific attributes are initialized for all code paths.
48+
obj = super()._frombuf(buf, encoding, errors, **kwargs)
49+
cls._init_visor_attrs(obj, buf)
3850
return obj
3951

4052
def _proc_member(self, tarfile: tarfile.TarFile) -> VisorTarInfo | tarfile.TarInfo:
@@ -76,6 +88,8 @@ def visoropen(cls, name: str, mode: str = "r", fileobj: BinaryIO | None = None,
7688
try:
7789
t = cls.taropen(name, mode, fileobj, **kwargs)
7890
except Exception:
91+
if fileobj is not None:
92+
fileobj.seek(0)
7993
try:
8094
fileobj = GzipFile(name, mode + "b", fileobj=fileobj)
8195
except OSError as e:
@@ -103,10 +117,11 @@ def visoropen(cls, name: str, mode: str = "r", fileobj: BinaryIO | None = None,
103117
compressed = True
104118

105119
# If we get here, we have a valid visor tar file
106-
if fileobj is not None and compressed:
107-
# Just read the entire file into memory, it's probably small
120+
if fileobj is not None:
108121
fileobj.seek(0)
109-
fileobj = BytesIO(fileobj.read())
122+
if compressed:
123+
# Read the decompressed data into a BytesIO to make random reads faster
124+
fileobj = BytesIO(fileobj.read())
110125

111126
t = cls.taropen(name, mode, fileobj, **kwargs)
112127

tests/util/test_vmtar.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def test_vmtar() -> None:
2121
# The test file has no textPgs/fixUpPgs
2222
assert all(member.is_visor for member in members.values())
2323
assert set(members.keys()) == {
24+
"test",
2425
"test/file1",
2526
"test/file2",
2627
"test/file3",

0 commit comments

Comments
 (0)