|
tether 0.0.1
|
hdiutil attach / detach equivalents A small C library (libtether) plus two CLIs — **attach-dmg** and **detach-dmg** — that mount and unmount disk images the same way hdiutil does, by calling the same underlying macOS APIs.
Named for what it does: it tethers a disk image to the running system. Attach mounts the image as a
/dev/diskNdevice; detach unmounts and releases it. That is the whole library — a tether you fasten and cast off, with no image creation or format manipulation.macOS only. This depends on
DiskArbitration.framework,CoreFoundation,IOKit, and the privateDiskImages.framework. The CMake build hard-fails on non-Apple platforms.
Attaching a .dmg is not a DiskArbitration operation. DiskArbitration only mounts/unmounts/ejects devices that already exist. Creating the /dev/diskN backing device from an image file is done by the private DiskImages.framework. So:
| Operation | Framework | Key call |
|---|---|---|
| attach (create device + mount) | DiskImages.framework (private) | DIHLDiskImageAttach |
| detach (unmount + tear down) | DiskArbitration.framework (public) | DADiskUnmount (whole) + DADiskEject |
This split is exactly how hdiutil itself is built (its detach path is literally named unmount_and_eject).
The signature was recovered from three independent sources that all agree:
hdiutil binary (Ghidra) — call site decompiles to DIHLDiskImageAttach(optionsDict, progressCb, 0, &outDict) returning int (0 = ok)._dihlDiskImageAttach((CFDictionaryRef)dict, nil, nil, (CFDictionaryRef*)&results) (callback + context may be nil; out-param is a CFDictionaryRef).Apple's open-source Darwin libsecurity_filevault (FVDIHLInterface.h) — the authoritative typedefs:
Note inStatusProc is plain void * in Apple's header, DIInitialize returns int, and DIDeinitialize returns void.
We declare these ourselves in include/diskimages_private.h and resolve them at runtime via CFBundle (see below).
| Key | Type | Notes |
|---|---|---|
main-url | CFURLRef | the image to attach (required) |
shadow-url | CFURLRef | optional shadow file (read-write overlay) |
read-only | CFBoolean | force read-only |
agent | CFString | set to framework (the value hdiutil uses on this path) |
verbose / quiet | CFBoolean | diagnostics level |
skip-verify, skip-verify-locked, skip-verify-remote | CFBoolean | skip checksum verification |
A CFDictionaryRef whose system-entities key is a CFArray of dictionaries, each describing one attached node:
| Entity key | Example |
|---|---|
dev-entry | /dev/disk4s1 |
mount-point | /Volumes/Foo |
content-hint | Apple_HFS |
On current macOS, private frameworks live in the dyld shared cache with no on-disk .tbd stub, so link-time -framework DiskImages typically fails. We therefore resolve the needed symbols (DIInitialize, DIDeinitialize, DIHLDiskImageAttach) at runtime — using **CFBundle**, exactly as Apple's own libsecurity_filevault (FVDIHLInterface.cpp) does:
We also try CFBundleGetBundleWithIdentifier(CFSTR("com.apple.DiskImagesFramework")) first (that bundle identifier is present in hdiutil's strings). This needs no private headers or SDK stubs. (hdiutil itself hard-links DiskImages — which is why its import table lists the _DIHL* symbols directly — but we can't hard-link without a stub.)
Because attach-dmg only hands main-url to DIHLDiskImageAttach, it attaches any format DiskImages recognizes, not just .dmg: UDIF (.dmg), sparse images (.sparseimage), sparse bundles (.sparsebundle, a directory — handled), ISO/CD masters (.iso, .cdr), NDIF, and encrypted images. The framework sniffs the format itself.
By default this builds only the library, build/libtether.a. The attach-dmg and detach-dmg CLIs are optional — enable them with -DBUILD_TOOLS=ON:
which additionally produces build/attach-dmg and build/detach-dmg.
inStatusProc) is plain void * in Apple's header and we always pass NULL, which all observed callers (filevault) do. hdiutil passes a real callback; if a future OS requires a non-NULL one, its prototype would need to be recovered.