dissect.apfs 1.2.dev1__tar.gz → 1.2.dev2__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.
- {dissect_apfs-1.2.dev1/dissect.apfs.egg-info → dissect_apfs-1.2.dev2}/PKG-INFO +1 -1
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/stream.py +14 -10
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2/dissect.apfs.egg-info}/PKG-INFO +1 -1
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect.apfs.egg-info/SOURCES.txt +1 -0
- dissect_apfs-1.2.dev2/tests/_data/large.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/test_apfs.py +25 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/COPYRIGHT +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/LICENSE +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/MANIFEST.in +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/README.md +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/__init__.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/apfs.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/c_apfs.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/c_apfs.pyi +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/cursor.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/exception.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/__init__.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/base.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/btree.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/btree_node.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/checkpoint_map.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/efi_jumpstart.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/er_recovery_block.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/er_state.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/fs.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/gbitmap.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/gbitmap_block.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/integrity_meta.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/keybag.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/nx_fusion_wbc.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/nx_fusion_wbc_list.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/nx_reap_list.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/nx_reaper.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/nx_superblock.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/omap.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/snap_meta_ext.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/spaceman.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/spaceman_bitmap.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/spaceman_cab.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/objects/spaceman_cib.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect/apfs/util.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect.apfs.egg-info/dependency_links.txt +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect.apfs.egg-info/requires.txt +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/dissect.apfs.egg-info/top_level.txt +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/pyproject.toml +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/setup.cfg +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/__init__.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/case_insensitive.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/case_insensitive_beta.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/case_sensitive.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/case_sensitive_beta.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/corrupt.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/encrypted.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/jhfs_converted.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/jhfs_encrypted.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_data/snapshot.bin.gz +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_docs/Makefile +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_docs/__init__.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_docs/conf.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/_docs/index.rst +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/conftest.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tests/test_exception.py +0 -0
- {dissect_apfs-1.2.dev1 → dissect_apfs-1.2.dev2}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dissect.apfs
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.dev2
|
|
4
4
|
Summary: A Dissect module implementing a parser for the APFS file system, a commonly used Apple file system
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -88,26 +88,30 @@ class FileStream(AlignedStream):
|
|
|
88
88
|
|
|
89
89
|
while length:
|
|
90
90
|
logical_address, physical_address, extent_length, crypto_id = self._lookup(offset)
|
|
91
|
-
|
|
91
|
+
|
|
92
|
+
offset_in_extent = offset - logical_address
|
|
93
|
+
if offset_in_extent >= extent_length:
|
|
94
|
+
raise Error(
|
|
95
|
+
f"Offset {offset:#x} is out of bounds for extent ({logical_address:#x}, {extent_length:#x})"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
block_in_extent = offset_in_extent // self.align
|
|
99
|
+
read_length = min(extent_length - offset_in_extent, length)
|
|
100
|
+
block = self.volume.container._read_block(physical_address + block_in_extent, read_length // self.align)
|
|
92
101
|
|
|
93
102
|
if self.volume.is_encrypted:
|
|
94
103
|
if not self.volume._cipher:
|
|
95
104
|
raise Error("Volume is encrypted, unlock it first")
|
|
96
105
|
|
|
97
106
|
if self.volume.is_onekey:
|
|
98
|
-
|
|
107
|
+
sector = (crypto_id + block_in_extent) * self.volume.container.sectors_per_block
|
|
108
|
+
block = self.volume._cipher.decrypt(block, sector)
|
|
99
109
|
else:
|
|
100
110
|
raise Error("Multi-key encryption is not supported yet")
|
|
101
111
|
|
|
102
|
-
if offset_in_extent := offset - logical_address:
|
|
103
|
-
block = block[offset_in_extent:]
|
|
104
|
-
|
|
105
|
-
if length < len(block):
|
|
106
|
-
block = block[:length]
|
|
107
|
-
|
|
108
112
|
result.append(block)
|
|
109
|
-
offset +=
|
|
110
|
-
length -=
|
|
113
|
+
offset += read_length
|
|
114
|
+
length -= read_length
|
|
111
115
|
|
|
112
116
|
return b"".join(result)
|
|
113
117
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dissect.apfs
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.dev2
|
|
4
4
|
Summary: A Dissect module implementing a parser for the APFS file system, a commonly used Apple file system
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
Binary file
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import gzip
|
|
4
4
|
import hashlib
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
|
+
from unittest.mock import patch
|
|
6
7
|
|
|
7
8
|
import pytest
|
|
8
9
|
|
|
@@ -379,3 +380,27 @@ def test_corrupt_checkpoints(caplog: pytest.LogCaptureFixture) -> None:
|
|
|
379
380
|
|
|
380
381
|
assert caplog.messages[0] == "Skipping superblock xid=304: invalid OMAP"
|
|
381
382
|
assert caplog.messages[1] == "Skipping superblock xid=303: Invalid nx_superblock checksum"
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def test_large_extents() -> None:
|
|
386
|
+
"""Test APFS volumes with large extents."""
|
|
387
|
+
with gzip.open(absolute_path("_data/large.bin.gz"), "rb") as fh:
|
|
388
|
+
container = APFS(fh)
|
|
389
|
+
assert len(container.volumes) == 1
|
|
390
|
+
|
|
391
|
+
volume = container.volumes[0]
|
|
392
|
+
assert volume.name == "Large"
|
|
393
|
+
|
|
394
|
+
node = volume.get("yomomma.bin")
|
|
395
|
+
assert node.size == 512 * 1024 * 1024
|
|
396
|
+
|
|
397
|
+
fh = node.open()
|
|
398
|
+
|
|
399
|
+
# First extent is 128MiB
|
|
400
|
+
assert fh._lookup(0) == (0, 1070, 128 * 1024 * 1024, 0)
|
|
401
|
+
|
|
402
|
+
with patch.object(volume.container, "_read_block", wraps=volume.container._read_block) as mock_read_block:
|
|
403
|
+
assert fh.read(512) == b"\x67" * 512
|
|
404
|
+
|
|
405
|
+
# Check that we only read one block, not the entire 128MiB extent
|
|
406
|
+
mock_read_block.assert_called_once_with(1070, 1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|