synology_apm_repo.sdk.units.content.disk_fs package¶
DiskFilesystem parses the partition table and filesystem(s)
inside a VM/PC/PS disk image via the Dissect framework (Fox-IT/NCC
Group’s DFIR toolkit, dissect.* PyPI packages), so units/device.py
can offer a read-only, per-file browsing/export view alongside the
whole-image cat/export it already supports, wired in as a new,
additional sibling node next to each disk-image leaf — existing
disk-image refs are completely unaffected.
Dissect’s packages are pure Python (py3-none-any wheels, no
per-platform native build) and cover partition tables (dissect.volume,
auto-detecting MBR/GPT/Apple Partition Map/BSD disklabel) plus
NTFS/ext2-4/XFS/Btrfs/FAT/APFS content (HFS+ and ISO9660 disks show the
same “no filesystem recognized” diagnostic as any other unsupported
format). A disk with no recognized partition table (Disk(...) raises)
falls back to treating the whole image as one filesystem candidate — a
bare, unpartitioned APFS container is the common real-world case. Each
format’s own detection/listing/sizing lives in its own sibling module
(_ntfs, _apfs, _posix_formats for ext2/3/4+XFS+Btrfs+FAT);
_disk_filesystem.py holds the shared partition-table/APFS-container
wiring (DiskFilesystem), and _content_source.py holds the two
ContentSource classes every format’s own opened file reads through.
Every Dissect filesystem object exposes get(path) (NTFS via its root
MftRecord; ext/FAT/APFS directly on the opened volume object) that
re-resolves an absolute, forward-slash path from scratch — so a node’s
own stable identifier is simply that path string, resolved fresh
whenever DiskFilesystem.list_dir/open_file needs it, including a
canonical ref pasted into a brand new process with no prior listing in
this instance.
The whole dissect.* stack is always installed (a required dependency of
this SDK, not split per format since every package involved is equally
well-packaged — pure-Python universal wheels; dissect.apfs’s one native
dependency, pycryptodome for FileVault decryption, ships broad prebuilt
wheels of its own) but imported lazily, so importing units/device.py
doesn’t pay the real cost of importing seven packages for a caller who
never browses into a disk’s filesystem.
Each format also has its own content_unavailable check for a file
whose real content this SDK cannot produce, rather than genuinely
corrupt: a cloud-sync placeholder with no local data (e.g. an evicted
OneDrive/iCloud file), or (NTFS only) an EFS-encrypted file this SDK
has no key material for.
Every dissect.* call is synchronous while this SDK is async-native
throughout, so every call here runs inside asyncio.to_thread — except
_disk_filesystem._build_bridge’s stream adapter, whose worker thread
briefly reads back across the loop boundary to fetch real bytes.
- class synology_apm_repo.sdk.units.content.disk_fs.DiskFilesystem(content, loop)¶
Bases:
objectOne already-open disk image’s own partition table and filesystem(s)/volume(s), lazily parsed. Build with
open, never the constructor directly.A disk image can (and typically does) contain more than one partition/volume (a Windows “System Reserved” boot partition alongside the real NTFS volume, for instance) — every one Dissect can open is kept, not just the first/largest, matching what a real disk-mount tool would show. A partition/volume Dissect can’t open (unsupported format, encrypted without a key, or genuinely not a filesystem at all) is silently skipped, not reported as an error — this mirrors the project’s existing principle of resolving real state lazily and only reporting what was actually found, not guessing at why something wasn’t.
Not a
UnitProvider— likeVirtualDiskContentSource, it is a disk-content helperDeviceProviderdrives directly, building its ownNode/RestorableUnitobjects from the plain tuples this class returns.device.pyonly ever talks to this class’s three public methods (partitions,list_dir,open_file).- async classmethod open(content)¶
Returns
Nonewhen no partition/volume on this disk yields anything Dissect can open at all (encrypted, unsupported, or genuinely not a partitioned/filesystem-bearing image) — callers show a diagnostic node for that case, the same shapeunits/device_pcps.py’s own_diagnostic_nodealready uses for other “resolvable in principle, nothing found” cases.- Raises:
DiskFilesystemUnavailableError – None of this package’s
dissect.*dependencies are importable — calldisk_fs_availablefirst to avoid this in the common case (nothing installed); this path exists for the rarer “find_specfound something, but the real import still fails” case (a broken/partial install).
- partitions()¶
(partition_addr, human_label)for every filesystem/volume successfully opened — stable ordering (insertion order:dissect.volume’s own partition-table order, each APFS container’s volumes in their own container order).
- async list_dir(partition_addr, path)¶
One
_DirEntryper directory’s real entries —path="/"means the partition/volume’s own filesystem root.pathis resolved fresh via the underlying format’s ownget(path)every call (paths, not inode numbers, are the stable identifier here) — dot/dot-dot and each format’s own synthetic bookkeeping entries are filtered out by that format’s own_Format.iterdir, never surfaced as browsable content.file_stateis alwaysFileState.NORMALfor a directory, or for a format with no cloud-sync/encryption concept.mtimeisNonewhen a format/entry has no reliable value.
- async open_file(partition_addr, path)¶
Opens
pathon the filesystem mounted atpartition_addr.- Raises:
ContentUnavailableError – this file is a cloud-sync placeholder with no local data, or (NTFS only) is EFS-encrypted with no key material available.
Bases:
ExceptionRaised by
DiskFilesystem.openwhen none of this package’sdissect.*dependencies are importable in the current environment. Deliberately notUnsupportedDataFormatError(that means “this repository’s own data doesn’t support this” — here the repository data may be perfectly fine, it’s this environment’s owndissect.*install that’s broken or missing).
- class synology_apm_repo.sdk.units.content.disk_fs.DissectFileContentSource(entry, size)¶
Bases:
objectBacked by one already-resolved Dissect filesystem entry (an NTFS
MftRecord, an extINode, a FATDirectoryEntry, or an APFSDirectoryEntry) — reads through that entry’s own.open()stream rather than a format-specific random-access method, since that’s the one thing all four formats’ entries agree on (unlike read-at-offset method names/arg orders, which differ per format).- async read(offset=0, length=None)¶
- stream(block=8388608)¶
- async export_to(dst, *, sparse=True, progress=None)¶
- synology_apm_repo.sdk.units.content.disk_fs.disk_fs_available()¶
Cheap presence check for a working Dissect install — checks the import system’s own module registry, never actually imports any of them. True if at least one of the packages in
_DISSECT_PACKAGESis present; callers use this to decide whether to even offer the “(filesystem)” sibling node at all.