Skip to content

Commit 03ff071

Browse files
feat(handler): add APFS container handler
1 parent d113957 commit 03ff071

13 files changed

Lines changed: 124 additions & 0 deletions

File tree

docs/handlers.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
| [`7-ZIP`](#7-zip) | ARCHIVE | :octicons-check-16: |
55
| [`ANDROID EROFS`](#android-erofs) | FILESYSTEM | :octicons-check-16: |
66
| [`ANDROID SPARSE`](#android-sparse) | FILESYSTEM | :octicons-check-16: |
7+
| [`APFS`](#apfs) | FILESYSTEM | :octicons-check-16: |
78
| [`AR`](#ar) | ARCHIVE | :octicons-check-16: |
89
| [`ARC`](#arc) | ARCHIVE | :octicons-check-16: |
910
| [`ARJ`](#arj) | ARCHIVE | :octicons-check-16: |
@@ -139,6 +140,22 @@
139140

140141
- [Android Sparse Image Format Documentation](https://formats.kaitai.io/android_sparse/){ target="_blank" }
141142
- [simg2img Tool](https://github.com/anestisb/android-simg2img){ target="_blank" }
143+
## APFS
144+
145+
!!! success "Fully supported"
146+
147+
=== "Description"
148+
149+
Apple File System (APFS) is Apple's proprietary filesystem introduced in macOS High Sierra and iOS 10.3, replacing HFS+. It features copy-on-write semantics, space sharing between volumes, native encryption, snapshots, and sparse files.
150+
151+
---
152+
153+
- **Handler type:** FileSystem
154+
- **Vendor:** Apple
155+
156+
=== "References"
157+
158+
- [Apple File System Reference](https://developer.apple.com/support/downloads/Apple-File-System-Reference.pdf){ target="_blank" }
142159
## AR
143160

144161
!!! success "Fully supported"

python/unblob/handlers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
)
4343
from .executable import elf, xalz
4444
from .filesystem import (
45+
apfs,
4546
btrfs_stream,
4647
cramfs,
4748
extfs,
@@ -59,6 +60,7 @@
5960
from .filesystem.android import erofs, sparse
6061

6162
BUILTIN_HANDLERS: Handlers = (
63+
apfs.APFSHandler,
6264
cramfs.CramFSHandler,
6365
deafbead.DeafBeadHandler,
6466
extfs.EXTHandler,
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from unblob.extractors import Command
2+
from unblob.file_utils import InvalidInputFormat
3+
from unblob.models import (
4+
File,
5+
HandlerDoc,
6+
HandlerType,
7+
HexString,
8+
Reference,
9+
StructHandler,
10+
ValidChunk,
11+
)
12+
13+
_NXSB_MAGIC_OFFSET = 32 # bytes from container start to the NXSB magic
14+
15+
_MIN_BLOCK_SIZE = 512
16+
_MAX_BLOCK_SIZE = 1024 * 1024 # 1 MB sanity cap
17+
18+
19+
class APFSHandler(StructHandler):
20+
NAME = "apfs"
21+
22+
PATTERNS = [HexString("4e 58 53 42")] # "NXSB"
23+
24+
# NXSB sits 32 bytes into the container superblock; shift start back to block 0.
25+
PATTERN_MATCH_OFFSET = -_NXSB_MAGIC_OFFSET
26+
27+
# APFS container superblock layout (all fields little-endian):
28+
# offset 0: obj_phys_t (32 bytes) — Fletcher-64 checksum + oid + xid + type + subtype
29+
# offset 32: nx_magic uint32 "NXSB" (0x4253584e)
30+
# offset 36: nx_block_size uint32
31+
# offset 40: nx_block_count uint64
32+
C_DEFINITIONS = r"""
33+
typedef struct apfs_nx_superblock {
34+
char o_cksum[8]; // Fletcher-64 checksum
35+
uint64 o_oid; // object identifier
36+
uint64 o_xid; // transaction identifier
37+
uint32 o_type; // object type
38+
uint32 o_subtype; // object subtype
39+
char nx_magic[4]; // "NXSB"
40+
uint32 nx_block_size; // block size in bytes
41+
uint64 nx_block_count; // number of blocks in the container
42+
} apfs_nx_superblock_t;
43+
"""
44+
HEADER_STRUCT = "apfs_nx_superblock_t"
45+
46+
EXTRACTOR = Command("7z", "x", "-y", "{inpath}", "-o{outdir}")
47+
48+
DOC = HandlerDoc(
49+
name="APFS",
50+
description="Apple File System (APFS) is Apple's proprietary filesystem introduced in macOS High Sierra and iOS 10.3, replacing HFS+. It features copy-on-write semantics, space sharing between volumes, native encryption, snapshots, and sparse files.",
51+
handler_type=HandlerType.FILESYSTEM,
52+
vendor="Apple",
53+
references=[
54+
Reference(
55+
title="Apple File System Reference",
56+
url="https://developer.apple.com/support/downloads/Apple-File-System-Reference.pdf",
57+
),
58+
],
59+
limitations=[],
60+
)
61+
62+
def calculate_chunk(self, file: File, start_offset: int) -> ValidChunk | None:
63+
header = self.parse_header(file)
64+
65+
if not (_MIN_BLOCK_SIZE <= header.nx_block_size <= _MAX_BLOCK_SIZE):
66+
raise InvalidInputFormat(
67+
f"APFS block_size out of range: {header.nx_block_size:#x}"
68+
)
69+
if header.nx_block_count == 0:
70+
raise InvalidInputFormat("APFS block_count is zero")
71+
72+
return ValidChunk(
73+
start_offset=start_offset,
74+
end_offset=start_offset + header.nx_block_size * header.nx_block_count,
75+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:7930b3dcb6a198cea696b7bd44f1ceea2491e17f0d686af759242b20d59d9d01
3+
size 8388608
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:1cdc3b9050fb182a5ba445763659c051fb6428bab94095cafafa3d71b2d1a5f5
3+
size 8389120
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:303980bcb9e9e6cdec515230791af8b0ab1aaa244b58a8d99152673aa22197d0
3+
size 6
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:5a81483d96b0bc15ad19af7f5a662e14b275729fbc05579b18513e7f550016b1
3+
size 7
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:86baf3529da550a44b0681ffa031b6b676e620e9e06dc5ac1119d0cd21cbcf55
3+
size 7
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:076a27c79e5ace2a3d47f9dd2e83e4ff6ea8872b3c2218f66c92b89b55f36560
3+
size 512
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:7930b3dcb6a198cea696b7bd44f1ceea2491e17f0d686af759242b20d59d9d01
3+
size 8388608

0 commit comments

Comments
 (0)