hdiutil / hdik port plan — four options

Decision document for the hdiutil/hdik/DiskImages workstream on freebsd-launchd-mach. Spun out of the v3 userland-cmds plan §16 deferred scoping after research into Darling's reimplementation revealed it's a partial-surface, copyleft-licensed substitute — not the drop-in replacement the original v3 deferral assumed. Four implementation paths are laid out below for later decision. No decision is being made in this document; it's for review and selection.

Decision status: PENDING — reviewer to pick Option A, B, C, or D in §9 below. Until selected, the live ISO uses FreeBSD's mdconfig/mdmfs from /usr/src (transitional gap-filler manifest per the v3 plan).

Contents

  1. What we're solving
  2. What Apple's hdiutil/hdik actually do
  3. What Darling reimplemented (and didn't)
  4. What FreeBSD provides today
  5. Option A — Clean-room fresh-write (MIT/BSD)
  6. Option B — Vendor darling-dmg as-is (GPL-3.0)
  7. Option C — Vendor darling-dmg + extend to full surface
  8. Option D — Defer indefinitely
  9. Decision matrix
  10. Cross-cutting concerns
  11. Staged scope (if A or C selected)

1. What we're solving

freebsd-launchd-mach is dropping FreeBSD-runtime + FreeBSD-utilities and replacing the userland with Apple-source ports. Per the userland-cmds v3 plan, every Apple userland repo we have open-source for gets vendored + ported. hdiutil/hdik are the gap — Apple-closed, no source to vendor, yet expected to be present at Apple-canonical paths.

The function this fills on the live ISO: create, attach, manipulate disk images — both the daily-driver "mount a downloaded DMG" use case AND the install-image production case (creating system images, converting between sparse/compressed/RW formats, etc.). FreeBSD's existing mdconfig/mdmfs cover the underlying memory-disk plumbing but expose a totally different CLI and don't speak DMG format at all.

2. What Apple's hdiutil/hdik actually do

Per the macOS 14 hdiutil(1) man page, ~30 subcommands:

CategorySubcommandsWhat it does
Attach / detachattach, detach, eject, mount, unmount, mountvolBind a disk image to a synthetic /dev/disk* device and mount its filesystem(s).
Create / formatcreate, partitiondisk, erasedisk, fdiskMake a new disk image (sized, sparse, sparse bundle, encrypted, with specific format).
Convertconvert, makehybridTransform between formats: UDIF, UDZO, UDBZ, UDRO, ULFO, sparse, sparse bundle, ISO, IMG, NDIF, DC42, etc. makehybrid produces ISO/HFS+ hybrid images for cross-platform optical media.
Informationinfo, imageinfo, isencrypted, plugins, pmap, fsid, checksum, verifyMetadata inspection, partition-map dump, integrity check.
Maintenancecompact, resize, segment, chpassShrink unused space, grow image, split into segments, change encryption password.
Resource-fork toolsudifrez, udifderezGet/set UDIF resource entries inside a DMG.
Optical-mediaburnBurn a disk image to CD/DVD via Apple's burning infrastructure.
Web-distributioninternet-enableToggle the Safari "auto-open after download" flag.
Low-levelconversion, mediakitMediaKit framework introspection.

hdik is the lower-level worker that hdiutil attach shells out to. It does the actual disk-image attach (creates the /dev/disk* node via IOHDIXController kext), without all of hdiutil's format conversion and metadata logic. On macOS today most attach paths go through hdiutil; hdik is the in-process helper.

Source availability: all of the above is closed-source. DiskImages.framework (which both hdiutil and hdik link) has never been published. The kext (IOHDIXController) is also closed in modern macOS. Apple's opensource.apple.com has no DiskImages, no hdiutil, no hdik, no IOHDIXController. Public references: the hdiutil(1) man page, Apple Technote TN1150 (HFS+), and the UDIF format reverse-engineering work done by the open-source community (notably by Jonathan Levin and the Darling team).

3. What Darling reimplemented (and didn't)

Two research deep-dives (2026-05-26, this session) confirmed the following coverage in the Darling project:

Darling artifactWhat it providesStatus
darling-dmg (standalone repo)FUSE-based, read-only hdiutil attach + detach. Parses UDIF containers (UDZO/UDBZ/UDRO/ULFO/Raw/ADC). From-scratch HFS+ reader (catalog B-tree, extents, attributes, resource forks, transparent zlib compression). Reads partition maps (APM, GPT). ~9 KB hdiutil shim + ~15 KB HFS+ B-tree + ~38 KB total source.Working but narrow
darling-dmg licenseGPL-3.0 (full FSF text in LICENSE)Copyleft
hdik binaryNothingNever reimplemented
DiskImages.framework~38 KB of void* foo(void) { return NULL; } stubs to satisfy dynamic link references. No functional behavior.Link-stub only
diskutil999-byte shell script. One verb (eject → shells to hdiutil detach). All others print "did not recognize verb".Effectively nothing
asr (Apple Software Restore)NothingCompletely missing
hdiejectd, diskimages-helper, IOHDIXController, vsdbutil, MediaKit.frameworkNothingAll completely missing

Darling's hdiutil coverage vs Apple's:

Apple subcommand categorydarling-dmg coverage
Attach/detach2 of 6 subcommands (attach, detach); read-only; ~60% of attach's flags
Create/format0 of 4
Convert0 of 2
Information0 of 8 (BLKX checksums are parsed but never validated)
Maintenance0 of 4
Resource-fork0 of 2
Optical-media0 of 1
Web-distribution0 of 1
Low-level0 of 2
Total coverage~7% of subcommand surface (2/30)

Functional coverage for the common case "open a downloaded UDZO/ULFO DMG, read files out of the HFS+ volume" is high. That one path is the project's entire goal.

Architecture: pure userland FUSE 2.x. No kernel helper, no /dev/disk* node creation. hdiutil attach daemonizes darling-dmg, which mounts the HFS+ volume via FUSE; hdiutil detach shells to fusermount -u.

FreeBSD portability: excellent. The source already has #ifdef __FreeBSD__ branches in src/be.h; no Linux-specific headers (linux/fs.h, sysfs, /proc, FIEMAP) anywhere in src/; only <sys/stat.h> + POSIX. FreeBSD ships fusefs-libs (libfuse 2.9.x, ABI-compatible) and the fusefs(5) kmod. darling-dmg should compile out of the box after pkg install fusefs-libs icu libxml2 openssl. The Darling-specific shim (main-hdiutil.cpp) needs replacing with a ~150-line native wrapper (no <elfcalls.h>, no __darling_vchroot_expand).

4. What FreeBSD provides today

ToolWhat it doesComparable to Apple's...
mdconfig(8)Create/destroy md(4) memory disks (vnode-backed, malloc-backed, swap-backed). Returns /dev/md* node.The attach primitive only. No format parsing, no compression, no encryption.
mdmfs(8)Wrapper: mdconfig + newfs + mount in one step to make a memory-disk-backed UFS volume.Roughly hdiutil attach -nomount followed by Disk Utility format.
fdisk, gpart, bsdlabelPartition-table tooling.hdiutil pmap, hdiutil partitiondisk partially.
(missing)DMG/UDIF format parsingMost of hdiutil's value.
(missing)HFS+ filesystem reader (no kmod, no fuse module shipped)Required to mount Mac DMGs.
(missing)APFS readerRequired for modern Mac DMGs (post-10.13 system images).

FreeBSD's md(4) is the kernel-side analogue of Apple's IOHDIXController in terms of "expose a file as a block device," but it doesn't parse any disk-image format — it takes a raw file or already-formatted volume and exposes it. All format/compression/encryption logic on Apple lives in userland hdiutil + DiskImages.framework. So the Apple-shape "hdiutil attach foo.dmg" requires: (1) a DMG parser to find the embedded HFS+/APFS volume, (2) an HFS+/APFS reader to actually mount it, (3) the binding of (2)'s output to a synthetic /dev/disk* via md(4). FreeBSD has only (3).

5. Option A — Clean-room fresh-write (MIT/BSD)

Apple-shape hdiutil written from scratch into src/hdiutil/

What it is: implement hdiutil's CLI surface from scratch in C/C++, written against Apple's published man page + Apple Technote TN1150 (HFS+) + the public UDIF format documentation. License under MIT or BSD-2-Clause to align with the rest of our src/ tree. No GPL'd code anywhere in the lineage; Darling sources used only as reference for confirming behavior, not copied.

What we deliver:

Surface staged in phases (see §11 for the phased breakdown). Phase 1 = read-only attach/detach with UDZO/UDRO/ULFO and HFS+. Phase 2 = info/imageinfo/verify/checksum. Phase 3 = create and write support. Phase 4 = convert + sparse formats. Phase 5+ = the rest.

Effort estimate:

Tradeoffs:

6. Option B — Vendor darling-dmg as-is (GPL-3.0)

Pull darling-dmg into src/darling-dmg/; ship as /usr/bin/hdiutil

What it is: git-submodule or copy darling-dmg's source into our tree under src/darling-dmg/. Build it as-is against FreeBSD's fusefs-libs. Install the FUSE binary at /usr/libexec/darling-dmg; install the (rewritten) hdiutil shim at /usr/bin/hdiutil. License the vendored tree (and our shim) under GPL-3.0.

What we deliver:

Surface delivered: ~7% of Apple's hdiutil. attach + detach only; read-only; UDIF formats only; HFS+/HFSX volumes only (no APFS).

Effort estimate:

Tradeoffs:

7. Option C — Vendor darling-dmg + extend to full surface

Start with darling-dmg, write the missing 93% on top

What it is: Option B as the starting point, then incrementally add the missing 28 subcommands (create, convert, info, verify, compact, resize, etc.) on top of darling-dmg's existing UDIF parser and HFS+ reader. Because GPL-3.0 is copyleft, our extensions also become GPL-3.0.

What we deliver:

Effort estimate: Phase 1 = Option B (1–2 weeks). Phase 2–5 broadly comparable to Option A's later phases (~6–12 months of part-time work to cover the breadth of create/convert/compact/etc.).

Tradeoffs:

8. Option D — Defer indefinitely

Keep mdconfig/mdmfs from /usr/src; no hdiutil shipped

What it is: the current v3 plan position. Live ISO continues to use FreeBSD's mdconfig/mdmfs from the gap-filler manifest. No hdiutil binary is installed at Apple-canonical paths. DMG files have to be manually unpacked with third-party tools (e.g., dmg2img from pkg) if needed; Mac-source workflows that expect hdiutil just fail.

What we deliver: nothing. Status quo.

Effort estimate: zero.

Tradeoffs:

9. Decision matrix

Dimension A: Clean-room B: Vendor as-is C: Vendor + extend D: Defer
Time to first working hdiutil attach Months 1–2 weeks 1–2 weeks Never
Eventual subcommand coverage Phased; targetable to ~100% ~7% (forever, unless reclassified to C) Phased; targetable to ~100% 0%
License MIT/BSD-2 GPL-3.0 GPL-3.0 n/a
Apple-divergence (rule 3) Aligned (no GPLv3, like Apple) Divergent (Apple avoids GPLv3) Divergent Aligned by absence
Total write effort ~10K LOC + ongoing ~150 LOC shim ~150 LOC shim + ~10K LOC extensions 0
Linking-constraint risk None libdmg.so can't be linked into non-GPL binaries Same; more surface to police None
Re-licensability of our work Full None (vendored is GPL-3.0) None (extensions inherit GPL-3.0) n/a
APFS support path Phase 5+; we control it Never (darling-dmg has no APFS) Future GPL-3.0 work Never
Write/create support Phase 3+ Never Future GPL-3.0 work Never

10. Cross-cutting concerns

10.1. GPL-3.0 in FreeBSD/Apple-shape userland — is it actually a problem?

FreeBSD precedent: the base system has shipped GPLv2 (gcc 4.2.1, groff, some diff tools) for decades. GDB was in base and was GPLv3 at one point. The "BSD only" framing is aspirational, not absolute; FreeBSD does ship GPLv3 components when there's no alternative.

Apple precedent: Apple explicitly avoids GPLv3. They froze on gcc 4.2.1 — the last GPLv2 release — rather than upgrade to a GPLv3 gcc. They eventually replaced GCC entirely with clang/LLVM (Apache 2.0). They also avoid GPLv3 versions of bash (frozen on bash 3.2), readline, etc. The reasons are widely understood to be the GPLv3 patent-grant clause and the anti-tivoization clause.

Net read for this project: shipping GPLv3 inside an otherwise Apple-shape userland is a knowingly-divergent choice. It's not a legal block, but it's a posture mismatch. Per rule 3 ("don't invent things Apple doesn't do"), the lean is toward avoiding GPL-3.0 if there's a reasonable alternative — and Option A is a reasonable alternative. The question is whether the time-to-delivery benefit of Options B/C outweighs the posture mismatch.

10.2. The libdmg.so linking constraint (Options B and C)

GPL-3.0 is copyleft. Any binary that links (statically or dynamically) against libdmg.so is "based on" GPL-3.0 work and itself becomes subject to GPL-3.0 distribution requirements (source must be made available, etc.). Practically this means: only hdiutil and hdik binaries (which we're OK with being GPL-3.0) can link libdmg.so. Any other tool that wants DMG-parsing capability would have to either (a) become GPL-3.0 too, or (b) shell out to hdiutil rather than linking.

This is a permanent operational discipline. Easy to enforce with a CI check (ldd on each non-GPL binary, fail if libdmg.so appears). But it's a permanent constraint that doesn't exist under Option A.

10.3. hdik as a separate binary

On Apple, hdik is the lower-level attach worker that hdiutil shells to. Under our options:

10.4. APFS

Modern Mac DMGs (post-macOS 10.13) often contain APFS volumes, not HFS+. APFS is open-spec but Apple's reference implementation is closed. Third-party readers exist (libapfs, apfs-fuse) but coverage is partial and quality varies.

OptionAPFS path
AAdd APFS reader as a later phase; pick from MIT-compatible libraries or write our own.
BNo APFS, ever — darling-dmg has none.
CAdd APFS as GPL-3.0 extension; could vendor apfs-fuse (GPL-2 or GPL-3) under our broader GPL-3 envelope.
Dn/a.

10.5. md(4) binding for synthetic /dev/disk*

Apple's hdiutil attach creates a /dev/disk* node via IOHDIXController. Our equivalent under any option is binding to FreeBSD's md(4) — vnode-backed memory disk creates /dev/md*. We'd either (a) symlink or alias /dev/disk*/dev/md* for Apple-shape paths, or (b) name disagree with Apple and accept it. This is independent of the option chosen above; it's the actual attach mechanism.

For the FUSE-based Options B and C: no /dev/disk* at all — the mount target is a normal FUSE mountpoint. Apps that look for /dev/disk* won't find it. This is a structural divergence from Apple's model that darling-dmg accepts; we'd inherit it.

11. Staged scope (if Option A or C selected)

Phasing for incremental delivery. Each phase ships independently with its own CI marker.

PhaseWhat landsCI markerApple coverage gain
1 attach + detach for UDZO/UDRO/ULFO + HFS+; read-only HDIUTIL-ATTACH-OK ~7% (matches darling-dmg)
2 info, imageinfo, checksum, verify, pmap, isencrypted, plugins HDIUTIL-INFO-OK ~25%
3 create (sparse + sparse-bundle), write support to HFS+ filesystem creation HDIUTIL-CREATE-OK ~45%
4 convert (UDIF↔sparse↔sparse-bundle), compact, resize, segment HDIUTIL-CONVERT-OK ~65%
5 APFS reader (mount-only first; write later if needed) HDIUTIL-APFS-OK ~75%
6 Encryption (chpass, attach with password, sparse-encrypted) HDIUTIL-ENCRYPT-OK ~85%
7+ Long tail: burn, internet-enable, makehybrid, udifrez/udifderez, NDIF read, … (per-feature) ~100%

Realistic mileage: phases 1–3 are the load-bearing feature set for any actual ISO-production workflow. Phases 4–6 cover the daily-driver "I downloaded a DMG" experience. Phase 7+ is optional polish that may never need shipping.

Drafted 2026-05-26. Companion to the freebsd-apple-userland-cmds v3 plan §16 deferred scoping. Source data: two parallel research agent passes against darlinghq/darling-dmg and darlinghq/darling source trees, plus reference to Apple's hdiutil(1) man page and Technote TN1150. Decision pending in §9.