dissect.hypervisor 3.17.dev3__tar.gz → 3.18.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/PKG-INFO +3 -2
  2. dissect_hypervisor-3.18.dev1/dissect/hypervisor/tools/vmtar.py +20 -0
  3. dissect_hypervisor-3.18.dev1/dissect/hypervisor/util/vmtar.py +122 -0
  4. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect.hypervisor.egg-info/PKG-INFO +3 -2
  5. dissect_hypervisor-3.18.dev1/dissect.hypervisor.egg-info/SOURCES.txt +86 -0
  6. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect.hypervisor.egg-info/entry_points.txt +1 -0
  7. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/pyproject.toml +3 -2
  8. {dissect_hypervisor-3.17.dev3/tests/docs → dissect_hypervisor-3.18.dev1/tests/_docs}/conf.py +2 -0
  9. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/tests/conftest.py +15 -15
  10. dissect_hypervisor-3.18.dev1/tests/descriptor/__init__.py +0 -0
  11. dissect_hypervisor-3.18.dev1/tests/disk/__init__.py +0 -0
  12. dissect_hypervisor-3.18.dev1/tests/util/__init__.py +0 -0
  13. dissect_hypervisor-3.18.dev1/tests/util/test_vmtar.py +80 -0
  14. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/tox.ini +8 -6
  15. dissect_hypervisor-3.17.dev3/dissect/hypervisor/util/vmtar.py +0 -57
  16. dissect_hypervisor-3.17.dev3/dissect.hypervisor.egg-info/SOURCES.txt +0 -82
  17. dissect_hypervisor-3.17.dev3/tests/test_vmtar.py +0 -27
  18. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/.git-blame-ignore-revs +0 -0
  19. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/COPYRIGHT +0 -0
  20. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/LICENSE +0 -0
  21. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/MANIFEST.in +0 -0
  22. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/README.md +0 -0
  23. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/__init__.py +0 -0
  24. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/descriptor/__init__.py +0 -0
  25. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/descriptor/c_hyperv.py +0 -0
  26. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/descriptor/hyperv.py +0 -0
  27. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/descriptor/ovf.py +0 -0
  28. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/descriptor/pvs.py +0 -0
  29. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/descriptor/vbox.py +0 -0
  30. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/descriptor/vmx.py +0 -0
  31. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/__init__.py +0 -0
  32. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/c_hdd.py +0 -0
  33. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/c_qcow2.py +0 -0
  34. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/c_vdi.py +0 -0
  35. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/c_vhd.py +0 -0
  36. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/c_vhdx.py +0 -0
  37. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/c_vmdk.py +0 -0
  38. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/hdd.py +0 -0
  39. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/qcow2.py +0 -0
  40. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/vdi.py +0 -0
  41. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/vhd.py +0 -0
  42. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/vhdx.py +0 -0
  43. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/disk/vmdk.py +0 -0
  44. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/exceptions.py +0 -0
  45. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/tools/__init__.py +0 -0
  46. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/tools/envelope.py +0 -0
  47. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/util/__init__.py +0 -0
  48. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect/hypervisor/util/envelope.py +0 -0
  49. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect.hypervisor.egg-info/dependency_links.txt +0 -0
  50. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect.hypervisor.egg-info/requires.txt +0 -0
  51. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/dissect.hypervisor.egg-info/top_level.txt +0 -0
  52. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/setup.cfg +0 -0
  53. {dissect_hypervisor-3.17.dev3 → dissect_hypervisor-3.18.dev1}/tests/__init__.py +0 -0
  54. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/descriptor/hyperv}/test.VMRS +0 -0
  55. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/descriptor/hyperv}/test.vmcx +0 -0
  56. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/descriptor/vmx}/encrypted.vmx +0 -0
  57. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/expanding.hdd/DiskDescriptor.xml +0 -0
  58. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/expanding.hdd/expanding.hdd +0 -0
  59. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/expanding.hdd/expanding.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  60. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/plain.hdd/DiskDescriptor.xml +0 -0
  61. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/plain.hdd/plain.hdd +0 -0
  62. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/plain.hdd/plain.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  63. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/DiskDescriptor.xml +0 -0
  64. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/split.hdd +0 -0
  65. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/split.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  66. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/split.hdd.1.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  67. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/split.hdd.2.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  68. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/split.hdd.3.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  69. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/split.hdd.4.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  70. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/hdd}/split.hdd/split.hdd.5.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  71. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/vhd}/dynamic.vhd.gz +0 -0
  72. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/vhd}/fixed.vhd.gz +0 -0
  73. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/vhdx}/differencing.avhdx.gz +0 -0
  74. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/vhdx}/dynamic.vhdx.gz +0 -0
  75. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/vhdx}/fixed.vhdx.gz +0 -0
  76. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/disk/vmdk}/sesparse.vmdk.gz +0 -0
  77. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/util/envelope}/encryption.info +0 -0
  78. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/util/envelope}/local.tgz.ve +0 -0
  79. {dissect_hypervisor-3.17.dev3/tests/data → dissect_hypervisor-3.18.dev1/tests/_data/util/vmtar}/test.vgz +0 -0
  80. {dissect_hypervisor-3.17.dev3/tests/docs → dissect_hypervisor-3.18.dev1/tests/_docs}/Makefile +0 -0
  81. {dissect_hypervisor-3.17.dev3/tests/docs → dissect_hypervisor-3.18.dev1/tests/_docs}/index.rst +0 -0
  82. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/descriptor}/test_hyperv.py +0 -0
  83. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/descriptor}/test_ovf.py +0 -0
  84. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/descriptor}/test_pvs.py +0 -0
  85. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/descriptor}/test_vbox.py +0 -0
  86. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/descriptor}/test_vmx.py +0 -0
  87. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/disk}/test_hdd.py +0 -0
  88. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/disk}/test_vhd.py +0 -0
  89. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/disk}/test_vhdx.py +0 -0
  90. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/disk}/test_vmdk.py +0 -0
  91. {dissect_hypervisor-3.17.dev3/tests → dissect_hypervisor-3.18.dev1/tests/util}/test_envelope.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: dissect.hypervisor
3
- Version: 3.17.dev3
3
+ Version: 3.18.dev1
4
4
  Summary: A Dissect module implementing parsers for various hypervisor disk, backup and configuration files
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -31,6 +31,7 @@ Provides-Extra: dev
31
31
  Requires-Dist: dissect.hypervisor[full]; extra == "dev"
32
32
  Requires-Dist: dissect.cstruct<5.0.dev,>=4.0.dev; extra == "dev"
33
33
  Requires-Dist: dissect.util<4.0.dev,>=3.0.dev; extra == "dev"
34
+ Dynamic: license-file
34
35
 
35
36
  # dissect.hypervisor
36
37
 
@@ -0,0 +1,20 @@
1
+ import tarfile
2
+
3
+ from dissect.hypervisor.util import vmtar
4
+
5
+
6
+ def main() -> None:
7
+ # We just want to run the main function of the tarfile module, but with our VisorTarFile and is_tarfile functions
8
+ type(tarfile.main)(
9
+ tarfile.main.__code__,
10
+ tarfile.main.__globals__
11
+ | {
12
+ "TarFile": vmtar.VisorTarFile,
13
+ "is_tarfile": vmtar.is_tarfile,
14
+ "open": vmtar.open,
15
+ },
16
+ )()
17
+
18
+
19
+ if __name__ == "__main__":
20
+ main()
@@ -0,0 +1,122 @@
1
+ # References:
2
+ # - /bin/vmtar
3
+
4
+ from __future__ import annotations
5
+
6
+ import struct
7
+ import tarfile
8
+ from io import BytesIO
9
+ from typing import BinaryIO, Final
10
+
11
+
12
+ class VisorTarInfo(tarfile.TarInfo):
13
+ """Implements TarInfo for use with Visor Tar files (vmtar).
14
+
15
+ The main difference is that file data is located at the end of the tar file, on
16
+ an offset specified in the header.
17
+ """
18
+
19
+ is_visor: bool
20
+ offset_data: int | None
21
+ textPgs: int | None
22
+ fixUpPgs: int | None
23
+
24
+ @classmethod
25
+ def frombuf(cls, buf: bytes, encoding: str, errors: str) -> VisorTarInfo:
26
+ obj = super().frombuf(buf, encoding, errors)
27
+
28
+ obj.is_visor = buf[257:264] == b"visor "
29
+ if obj.is_visor:
30
+ obj.offset_data = struct.unpack("<I", buf[496:500])[0]
31
+ obj.textPgs = struct.unpack("<I", buf[504:508])[0]
32
+ obj.fixUpPgs = struct.unpack("<I", buf[508:512])[0]
33
+ else:
34
+ obj.offset_data = None
35
+ obj.textPgs = None
36
+ obj.fixUpPgs = None
37
+
38
+ return obj
39
+
40
+ def _proc_member(self, tarfile: tarfile.TarFile) -> VisorTarInfo | tarfile.TarInfo:
41
+ if self.is_visor and self.offset_data:
42
+ # Don't advance the offset with the filesize
43
+ tarfile.offset = tarfile.fileobj.tell()
44
+
45
+ # Patch the TarInfo object with saved global
46
+ # header information.
47
+ self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors)
48
+
49
+ return self
50
+
51
+ return super()._proc_member(tarfile)
52
+
53
+
54
+ class VisorTarFile(tarfile.TarFile):
55
+ def __init__(self, *args, **kwargs) -> None:
56
+ super().__init__(*args, **kwargs, tarinfo=VisorTarInfo)
57
+
58
+ @classmethod
59
+ def visoropen(cls, name: str, mode: str = "r", fileobj: BinaryIO | None = None, **kwargs) -> VisorTarFile:
60
+ """Open a visor tar file for reading. Supports gzip and lzma compression."""
61
+ if mode not in ("r",):
62
+ raise tarfile.TarError("visor currently only supports read mode")
63
+
64
+ try:
65
+ from gzip import GzipFile
66
+ except ImportError:
67
+ raise tarfile.CompressionError("gzip module is not available") from None
68
+
69
+ try:
70
+ from lzma import LZMAError, LZMAFile
71
+ except ImportError:
72
+ raise tarfile.CompressionError("lzma module is not available") from None
73
+
74
+ compressed = False
75
+
76
+ try:
77
+ t = cls.taropen(name, mode, fileobj, **kwargs)
78
+ except Exception:
79
+ try:
80
+ fileobj = GzipFile(name, mode + "b", fileobj=fileobj)
81
+ except OSError as e:
82
+ if fileobj is not None and mode == "r":
83
+ raise tarfile.ReadError("not a visor file") from e
84
+ raise
85
+
86
+ try:
87
+ t = cls.taropen(name, mode, fileobj, **kwargs)
88
+ except Exception:
89
+ fileobj.seek(0)
90
+ fileobj = LZMAFile(fileobj or name, mode) # noqa: SIM115
91
+
92
+ try:
93
+ t = cls.taropen(name, mode, fileobj, **kwargs)
94
+ except (LZMAError, EOFError, OSError) as e:
95
+ fileobj.close()
96
+ if mode == "r":
97
+ raise tarfile.ReadError("not a visor file") from e
98
+ raise
99
+ except:
100
+ fileobj.close()
101
+ raise
102
+
103
+ compressed = True
104
+
105
+ # 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
108
+ fileobj.seek(0)
109
+ fileobj = BytesIO(fileobj.read())
110
+
111
+ t = cls.taropen(name, mode, fileobj, **kwargs)
112
+
113
+ t._extfileobj = False
114
+ return t
115
+
116
+ # Only allow opening visor tar files
117
+ OPEN_METH: Final[dict[str, str]] = {"visor": "visoropen"}
118
+
119
+
120
+ open = VisorTarFile.open
121
+
122
+ is_tarfile = type(tarfile.is_tarfile)(tarfile.is_tarfile.__code__, tarfile.is_tarfile.__globals__ | {"open": open})
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: dissect.hypervisor
3
- Version: 3.17.dev3
3
+ Version: 3.18.dev1
4
4
  Summary: A Dissect module implementing parsers for various hypervisor disk, backup and configuration files
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -31,6 +31,7 @@ Provides-Extra: dev
31
31
  Requires-Dist: dissect.hypervisor[full]; extra == "dev"
32
32
  Requires-Dist: dissect.cstruct<5.0.dev,>=4.0.dev; extra == "dev"
33
33
  Requires-Dist: dissect.util<4.0.dev,>=3.0.dev; extra == "dev"
34
+ Dynamic: license-file
34
35
 
35
36
  # dissect.hypervisor
36
37
 
@@ -0,0 +1,86 @@
1
+ .git-blame-ignore-revs
2
+ COPYRIGHT
3
+ LICENSE
4
+ MANIFEST.in
5
+ README.md
6
+ pyproject.toml
7
+ tox.ini
8
+ dissect.hypervisor.egg-info/PKG-INFO
9
+ dissect.hypervisor.egg-info/SOURCES.txt
10
+ dissect.hypervisor.egg-info/dependency_links.txt
11
+ dissect.hypervisor.egg-info/entry_points.txt
12
+ dissect.hypervisor.egg-info/requires.txt
13
+ dissect.hypervisor.egg-info/top_level.txt
14
+ dissect/hypervisor/__init__.py
15
+ dissect/hypervisor/exceptions.py
16
+ dissect/hypervisor/descriptor/__init__.py
17
+ dissect/hypervisor/descriptor/c_hyperv.py
18
+ dissect/hypervisor/descriptor/hyperv.py
19
+ dissect/hypervisor/descriptor/ovf.py
20
+ dissect/hypervisor/descriptor/pvs.py
21
+ dissect/hypervisor/descriptor/vbox.py
22
+ dissect/hypervisor/descriptor/vmx.py
23
+ dissect/hypervisor/disk/__init__.py
24
+ dissect/hypervisor/disk/c_hdd.py
25
+ dissect/hypervisor/disk/c_qcow2.py
26
+ dissect/hypervisor/disk/c_vdi.py
27
+ dissect/hypervisor/disk/c_vhd.py
28
+ dissect/hypervisor/disk/c_vhdx.py
29
+ dissect/hypervisor/disk/c_vmdk.py
30
+ dissect/hypervisor/disk/hdd.py
31
+ dissect/hypervisor/disk/qcow2.py
32
+ dissect/hypervisor/disk/vdi.py
33
+ dissect/hypervisor/disk/vhd.py
34
+ dissect/hypervisor/disk/vhdx.py
35
+ dissect/hypervisor/disk/vmdk.py
36
+ dissect/hypervisor/tools/__init__.py
37
+ dissect/hypervisor/tools/envelope.py
38
+ dissect/hypervisor/tools/vmtar.py
39
+ dissect/hypervisor/util/__init__.py
40
+ dissect/hypervisor/util/envelope.py
41
+ dissect/hypervisor/util/vmtar.py
42
+ tests/__init__.py
43
+ tests/conftest.py
44
+ tests/_data/descriptor/hyperv/test.VMRS
45
+ tests/_data/descriptor/hyperv/test.vmcx
46
+ tests/_data/descriptor/vmx/encrypted.vmx
47
+ tests/_data/disk/hdd/expanding.hdd/DiskDescriptor.xml
48
+ tests/_data/disk/hdd/expanding.hdd/expanding.hdd
49
+ tests/_data/disk/hdd/expanding.hdd/expanding.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
50
+ tests/_data/disk/hdd/plain.hdd/DiskDescriptor.xml
51
+ tests/_data/disk/hdd/plain.hdd/plain.hdd
52
+ tests/_data/disk/hdd/plain.hdd/plain.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
53
+ tests/_data/disk/hdd/split.hdd/DiskDescriptor.xml
54
+ tests/_data/disk/hdd/split.hdd/split.hdd
55
+ tests/_data/disk/hdd/split.hdd/split.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
56
+ tests/_data/disk/hdd/split.hdd/split.hdd.1.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
57
+ tests/_data/disk/hdd/split.hdd/split.hdd.2.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
58
+ tests/_data/disk/hdd/split.hdd/split.hdd.3.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
59
+ tests/_data/disk/hdd/split.hdd/split.hdd.4.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
60
+ tests/_data/disk/hdd/split.hdd/split.hdd.5.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
61
+ tests/_data/disk/vhd/dynamic.vhd.gz
62
+ tests/_data/disk/vhd/fixed.vhd.gz
63
+ tests/_data/disk/vhdx/differencing.avhdx.gz
64
+ tests/_data/disk/vhdx/dynamic.vhdx.gz
65
+ tests/_data/disk/vhdx/fixed.vhdx.gz
66
+ tests/_data/disk/vmdk/sesparse.vmdk.gz
67
+ tests/_data/util/envelope/encryption.info
68
+ tests/_data/util/envelope/local.tgz.ve
69
+ tests/_data/util/vmtar/test.vgz
70
+ tests/_docs/Makefile
71
+ tests/_docs/conf.py
72
+ tests/_docs/index.rst
73
+ tests/descriptor/__init__.py
74
+ tests/descriptor/test_hyperv.py
75
+ tests/descriptor/test_ovf.py
76
+ tests/descriptor/test_pvs.py
77
+ tests/descriptor/test_vbox.py
78
+ tests/descriptor/test_vmx.py
79
+ tests/disk/__init__.py
80
+ tests/disk/test_hdd.py
81
+ tests/disk/test_vhd.py
82
+ tests/disk/test_vhdx.py
83
+ tests/disk/test_vmdk.py
84
+ tests/util/__init__.py
85
+ tests/util/test_envelope.py
86
+ tests/util/test_vmtar.py
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  envelope-decrypt = dissect.hypervisor.tools.envelope:main
3
+ vmtar = dissect.hypervisor.tools.vmtar:main
@@ -48,10 +48,11 @@ dev = [
48
48
 
49
49
  [project.scripts]
50
50
  envelope-decrypt = "dissect.hypervisor.tools.envelope:main"
51
+ vmtar = "dissect.hypervisor.tools.vmtar:main"
51
52
 
52
53
  [tool.ruff]
53
54
  line-length = 120
54
- required-version = ">=0.9.0"
55
+ required-version = ">=0.11.0"
55
56
 
56
57
  [tool.ruff.format]
57
58
  docstring-code-format = true
@@ -94,7 +95,7 @@ select = [
94
95
  ignore = ["E203", "B904", "UP024", "ANN002", "ANN003", "ANN204", "ANN401", "SIM105", "TRY003"]
95
96
 
96
97
  [tool.ruff.lint.per-file-ignores]
97
- "tests/docs/**" = ["INP001"]
98
+ "tests/_docs/**" = ["INP001"]
98
99
 
99
100
  [tool.ruff.lint.isort]
100
101
  known-first-party = ["dissect.hypervisor"]
@@ -1,3 +1,5 @@
1
+ project = "dissect.hypervisor"
2
+
1
3
  extensions = [
2
4
  "autoapi.extension",
3
5
  "sphinx.ext.autodoc",
@@ -26,74 +26,74 @@ def open_file_gz(name: str, mode: str = "rb") -> Iterator[BinaryIO]:
26
26
 
27
27
  @pytest.fixture
28
28
  def encrypted_vmx() -> Iterator[BinaryIO]:
29
- yield from open_file("data/encrypted.vmx")
29
+ yield from open_file("_data/descriptor/vmx/encrypted.vmx")
30
30
 
31
31
 
32
32
  @pytest.fixture
33
33
  def vmcx() -> Iterator[BinaryIO]:
34
- yield from open_file("data/test.vmcx")
34
+ yield from open_file("_data/descriptor/hyperv/test.vmcx")
35
35
 
36
36
 
37
37
  @pytest.fixture
38
38
  def vmrs() -> Iterator[BinaryIO]:
39
- yield from open_file("data/test.VMRS")
39
+ yield from open_file("_data/descriptor/hyperv/test.VMRS")
40
40
 
41
41
 
42
42
  @pytest.fixture
43
43
  def fixed_vhd() -> Iterator[BinaryIO]:
44
- yield from open_file_gz("data/fixed.vhd.gz")
44
+ yield from open_file_gz("_data/disk/vhd/fixed.vhd.gz")
45
45
 
46
46
 
47
47
  @pytest.fixture
48
48
  def dynamic_vhd() -> Iterator[BinaryIO]:
49
- yield from open_file_gz("data/dynamic.vhd.gz")
49
+ yield from open_file_gz("_data/disk/vhd/dynamic.vhd.gz")
50
50
 
51
51
 
52
52
  @pytest.fixture
53
53
  def fixed_vhdx() -> Iterator[BinaryIO]:
54
- yield from open_file_gz("data/fixed.vhdx.gz")
54
+ yield from open_file_gz("_data/disk/vhdx/fixed.vhdx.gz")
55
55
 
56
56
 
57
57
  @pytest.fixture
58
58
  def dynamic_vhdx() -> Iterator[BinaryIO]:
59
- yield from open_file_gz("data/dynamic.vhdx.gz")
59
+ yield from open_file_gz("_data/disk/vhdx/dynamic.vhdx.gz")
60
60
 
61
61
 
62
62
  @pytest.fixture
63
63
  def differencing_vhdx() -> Iterator[BinaryIO]:
64
- yield from open_file_gz("data/differencing.avhdx.gz")
64
+ yield from open_file_gz("_data/disk/vhdx/differencing.avhdx.gz")
65
65
 
66
66
 
67
67
  @pytest.fixture
68
68
  def sesparse_vmdk() -> Iterator[BinaryIO]:
69
- yield from open_file_gz("data/sesparse.vmdk.gz")
69
+ yield from open_file_gz("_data/disk/vmdk/sesparse.vmdk.gz")
70
70
 
71
71
 
72
72
  @pytest.fixture
73
73
  def plain_hdd() -> Iterator[str]:
74
- return absolute_path("data/plain.hdd")
74
+ return absolute_path("_data/disk/hdd/plain.hdd")
75
75
 
76
76
 
77
77
  @pytest.fixture
78
78
  def expanding_hdd() -> Iterator[str]:
79
- return absolute_path("data/expanding.hdd")
79
+ return absolute_path("_data/disk/hdd/expanding.hdd")
80
80
 
81
81
 
82
82
  @pytest.fixture
83
83
  def split_hdd() -> Iterator[str]:
84
- return absolute_path("data/split.hdd")
84
+ return absolute_path("_data/disk/hdd/split.hdd")
85
85
 
86
86
 
87
87
  @pytest.fixture
88
88
  def envelope() -> Iterator[BinaryIO]:
89
- yield from open_file("data/local.tgz.ve")
89
+ yield from open_file("_data/util/envelope/local.tgz.ve")
90
90
 
91
91
 
92
92
  @pytest.fixture
93
93
  def keystore() -> Iterator[TextIO]:
94
- yield from open_file("data/encryption.info", "r")
94
+ yield from open_file("_data/util/envelope/encryption.info", "r")
95
95
 
96
96
 
97
97
  @pytest.fixture
98
98
  def vgz() -> Iterator[BinaryIO]:
99
- yield from open_file("data/test.vgz")
99
+ yield from open_file("_data/util/vmtar/test.vgz")
File without changes
File without changes
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, BinaryIO
4
+
5
+ from dissect.hypervisor.tools.vmtar import main as vmtar_main
6
+ from dissect.hypervisor.util import vmtar
7
+ from tests.conftest import absolute_path
8
+
9
+ if TYPE_CHECKING:
10
+ from pathlib import Path
11
+
12
+ import pytest
13
+
14
+
15
+ def test_vmtar(vgz: BinaryIO) -> None:
16
+ tar = vmtar.open(fileobj=vgz)
17
+
18
+ members = {member.name: member for member in tar.getmembers()}
19
+
20
+ # The test file has no textPgs/fixUpPgs
21
+ assert all(member.is_visor for member in members.values())
22
+ assert set(members.keys()) == {
23
+ "test/file1",
24
+ "test/file2",
25
+ "test/file3",
26
+ "test/subdir",
27
+ "test/subdir/file4",
28
+ }
29
+
30
+ assert tar.extractfile(members["test/file1"]).read() == (b"a" * 512) + b"\n"
31
+ assert tar.extractfile(members["test/file2"]).read() == (b"b" * 1024) + b"\n"
32
+ assert tar.extractfile(members["test/file3"]).read() == (b"c" * 2048) + b"\n"
33
+ assert tar.extractfile(members["test/subdir/file4"]).read() == (b"f" * 2048) + b"\n"
34
+
35
+
36
+ def test_vmtar_tool(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture) -> None:
37
+ vgz_path = absolute_path("_data/util/vmtar/test.vgz")
38
+
39
+ with monkeypatch.context() as m:
40
+ m.setattr("sys.argv", ["vmtar", "-l", str(vgz_path)])
41
+
42
+ vmtar_main()
43
+
44
+ out, _ = capsys.readouterr()
45
+ assert out.splitlines() == [
46
+ "test/ ",
47
+ "test/file3 ",
48
+ "test/file2 ",
49
+ "test/subdir/ ",
50
+ "test/subdir/file4 ",
51
+ "test/file1 ",
52
+ ]
53
+
54
+ with monkeypatch.context() as m:
55
+ m.setattr("sys.argv", ["vmtar", "-t", str(vgz_path)])
56
+
57
+ vmtar_main()
58
+
59
+ _, err = capsys.readouterr()
60
+ assert err.startswith("[<VisorTarInfo 'test'")
61
+
62
+ with monkeypatch.context() as m:
63
+ m.setattr("sys.argv", ["vmtar", "-e", str(vgz_path), str(tmp_path)])
64
+
65
+ vmtar_main()
66
+
67
+ for path in (
68
+ "test",
69
+ "test/file1",
70
+ "test/file2",
71
+ "test/file3",
72
+ "test/subdir",
73
+ "test/subdir/file4",
74
+ ):
75
+ assert tmp_path.joinpath(path).exists()
76
+
77
+ assert tmp_path.joinpath("test/file1").read_text() == (b"a" * 512).decode() + "\n"
78
+ assert tmp_path.joinpath("test/file2").read_text() == (b"b" * 1024).decode() + "\n"
79
+ assert tmp_path.joinpath("test/file3").read_text() == (b"c" * 2048).decode() + "\n"
80
+ assert tmp_path.joinpath("test/subdir/file4").read_text() == (b"f" * 2048).decode() + "\n"
@@ -32,16 +32,18 @@ commands =
32
32
  [testenv:fix]
33
33
  package = skip
34
34
  deps =
35
- ruff==0.9.2
35
+ ruff==0.11.10
36
36
  commands =
37
37
  ruff format dissect tests
38
+ ruff check --fix dissect tests
38
39
 
39
40
  [testenv:lint]
40
41
  package = skip
41
42
  deps =
42
- ruff==0.9.2
43
+ ruff==0.11.10
43
44
  vermin
44
45
  commands =
46
+ ruff format --check dissect tests
45
47
  ruff check dissect tests
46
48
  vermin -t=3.9- --no-tips --lint dissect tests
47
49
 
@@ -55,12 +57,12 @@ deps =
55
57
  sphinx-design
56
58
  furo
57
59
  commands =
58
- make -C tests/docs clean
59
- make -C tests/docs html
60
+ make -C tests/_docs clean
61
+ make -C tests/_docs html
60
62
 
61
63
  [testenv:docs-linkcheck]
62
64
  allowlist_externals = make
63
65
  deps = {[testenv:docs-build]deps}
64
66
  commands =
65
- make -C tests/docs clean
66
- make -C tests/docs linkcheck
67
+ make -C tests/_docs clean
68
+ make -C tests/_docs linkcheck
@@ -1,57 +0,0 @@
1
- # References:
2
- # - /bin/vmtar
3
-
4
- from __future__ import annotations
5
-
6
- import struct
7
- import tarfile
8
-
9
-
10
- class VisorTarInfo(tarfile.TarInfo):
11
- """Implements TarInfo for use with Visor Tar files (vmtar).
12
-
13
- The main difference is that file data is located at the end of the tar file, on
14
- an offset specified in the header.
15
- """
16
-
17
- is_visor: bool
18
- offset_data: int | None
19
- textPgs: int | None
20
- fixUpPgs: int | None
21
-
22
- @classmethod
23
- def frombuf(cls, buf: bytes, encoding: str, errors: str) -> VisorTarInfo:
24
- obj = super().frombuf(buf, encoding, errors)
25
-
26
- obj.is_visor = buf[257:264] == b"visor "
27
- if obj.is_visor:
28
- obj.offset_data = struct.unpack("<I", buf[496:500])[0]
29
- obj.textPgs = struct.unpack("<I", buf[504:508])[0]
30
- obj.fixUpPgs = struct.unpack("<I", buf[508:512])[0]
31
- else:
32
- obj.offset_data = None
33
- obj.textPgs = None
34
- obj.fixUpPgs = None
35
-
36
- return obj
37
-
38
- def _proc_member(self, tarfile: tarfile.TarFile) -> VisorTarInfo | tarfile.TarInfo:
39
- if self.is_visor and self.offset_data:
40
- # Don't advance the offset with the filesize
41
- tarfile.offset = tarfile.fileobj.tell()
42
-
43
- # Patch the TarInfo object with saved global
44
- # header information.
45
- self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors)
46
-
47
- return self
48
-
49
- return super()._proc_member(tarfile)
50
-
51
-
52
- def VisorTarFile(*args, **kwargs) -> tarfile.TarFile:
53
- return tarfile.TarFile(*args, **kwargs, tarinfo=VisorTarInfo)
54
-
55
-
56
- def open(*args, **kwargs) -> tarfile.TarFile:
57
- return tarfile.open(*args, **kwargs, tarinfo=VisorTarInfo)
@@ -1,82 +0,0 @@
1
- .git-blame-ignore-revs
2
- COPYRIGHT
3
- LICENSE
4
- MANIFEST.in
5
- README.md
6
- pyproject.toml
7
- tox.ini
8
- dissect.hypervisor.egg-info/PKG-INFO
9
- dissect.hypervisor.egg-info/SOURCES.txt
10
- dissect.hypervisor.egg-info/dependency_links.txt
11
- dissect.hypervisor.egg-info/entry_points.txt
12
- dissect.hypervisor.egg-info/requires.txt
13
- dissect.hypervisor.egg-info/top_level.txt
14
- dissect/hypervisor/__init__.py
15
- dissect/hypervisor/exceptions.py
16
- dissect/hypervisor/descriptor/__init__.py
17
- dissect/hypervisor/descriptor/c_hyperv.py
18
- dissect/hypervisor/descriptor/hyperv.py
19
- dissect/hypervisor/descriptor/ovf.py
20
- dissect/hypervisor/descriptor/pvs.py
21
- dissect/hypervisor/descriptor/vbox.py
22
- dissect/hypervisor/descriptor/vmx.py
23
- dissect/hypervisor/disk/__init__.py
24
- dissect/hypervisor/disk/c_hdd.py
25
- dissect/hypervisor/disk/c_qcow2.py
26
- dissect/hypervisor/disk/c_vdi.py
27
- dissect/hypervisor/disk/c_vhd.py
28
- dissect/hypervisor/disk/c_vhdx.py
29
- dissect/hypervisor/disk/c_vmdk.py
30
- dissect/hypervisor/disk/hdd.py
31
- dissect/hypervisor/disk/qcow2.py
32
- dissect/hypervisor/disk/vdi.py
33
- dissect/hypervisor/disk/vhd.py
34
- dissect/hypervisor/disk/vhdx.py
35
- dissect/hypervisor/disk/vmdk.py
36
- dissect/hypervisor/tools/__init__.py
37
- dissect/hypervisor/tools/envelope.py
38
- dissect/hypervisor/util/__init__.py
39
- dissect/hypervisor/util/envelope.py
40
- dissect/hypervisor/util/vmtar.py
41
- tests/__init__.py
42
- tests/conftest.py
43
- tests/test_envelope.py
44
- tests/test_hdd.py
45
- tests/test_hyperv.py
46
- tests/test_ovf.py
47
- tests/test_pvs.py
48
- tests/test_vbox.py
49
- tests/test_vhd.py
50
- tests/test_vhdx.py
51
- tests/test_vmdk.py
52
- tests/test_vmtar.py
53
- tests/test_vmx.py
54
- tests/data/differencing.avhdx.gz
55
- tests/data/dynamic.vhd.gz
56
- tests/data/dynamic.vhdx.gz
57
- tests/data/encrypted.vmx
58
- tests/data/encryption.info
59
- tests/data/fixed.vhd.gz
60
- tests/data/fixed.vhdx.gz
61
- tests/data/local.tgz.ve
62
- tests/data/sesparse.vmdk.gz
63
- tests/data/test.VMRS
64
- tests/data/test.vgz
65
- tests/data/test.vmcx
66
- tests/data/expanding.hdd/DiskDescriptor.xml
67
- tests/data/expanding.hdd/expanding.hdd
68
- tests/data/expanding.hdd/expanding.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
69
- tests/data/plain.hdd/DiskDescriptor.xml
70
- tests/data/plain.hdd/plain.hdd
71
- tests/data/plain.hdd/plain.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
72
- tests/data/split.hdd/DiskDescriptor.xml
73
- tests/data/split.hdd/split.hdd
74
- tests/data/split.hdd/split.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
75
- tests/data/split.hdd/split.hdd.1.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
76
- tests/data/split.hdd/split.hdd.2.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
77
- tests/data/split.hdd/split.hdd.3.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
78
- tests/data/split.hdd/split.hdd.4.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
79
- tests/data/split.hdd/split.hdd.5.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz
80
- tests/docs/Makefile
81
- tests/docs/conf.py
82
- tests/docs/index.rst
@@ -1,27 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import BinaryIO
4
-
5
- from dissect.hypervisor.util import vmtar
6
-
7
-
8
- def test_vmtar(vgz: BinaryIO) -> None:
9
- tar = vmtar.open(fileobj=vgz)
10
-
11
- members = {member.name: member for member in tar.getmembers()}
12
-
13
- # The test file has no textPgs/fixUpPgs
14
- assert all(member.is_visor for member in members.values())
15
- assert set(members.keys()) == {
16
- "test",
17
- "test/file1",
18
- "test/file2",
19
- "test/file3",
20
- "test/subdir",
21
- "test/subdir/file4",
22
- }
23
-
24
- assert tar.extractfile(members["test/file1"]).read() == (b"a" * 512) + b"\n"
25
- assert tar.extractfile(members["test/file2"]).read() == (b"b" * 1024) + b"\n"
26
- assert tar.extractfile(members["test/file3"]).read() == (b"c" * 2048) + b"\n"
27
- assert tar.extractfile(members["test/subdir/file4"]).read() == (b"f" * 2048) + b"\n"