dmg — Apple UDIF/DMG
github.com/go-diskimages/dmg is a pure-Go set of helpers for the native
Apple UDIF disk-image format (.dmg). No external tools (hdiutil,
plutil) are required, and it works on all platforms — not just macOS.
Format
A UDIF file ends with a 512-byte koly trailer (big-endian) that records
the image variant, the data-fork offset/length, and the offset/length of an
embedded XML plist. The plist carries an array of blkx (mish) tables, each a
sequence of sector runs:
| Run type | Code | Description |
|---|---|---|
| RAW | 0x00000001 |
Uncompressed sectors stored verbatim |
| NOCOPY | 0x00000000 |
Zero-filled sectors (no stored data) |
| FREE | 0x7FFFFFFE |
Unallocated, treated as zeros |
| LZFSE | 0x80000004 |
LZFSE compressed (Apple, macOS 10.12+) |
| ZLIB | 0x80000005 |
zlib-compressed sectors |
| Terminator | 0xFFFFFFFF |
End-of-table marker |
Image variants:
| Name | Code | Read | Write | Description |
|---|---|---|---|---|
UDRW |
1 | ✓ | ✓ | Read-write, fixed size |
UDRO |
2 | ✓ | ✓ | Read-only (stored as RAW) |
UDCO |
3 | ✓ | — | ADC compressed (write not supported) |
UDZO |
4 | ✓ | ✓ | zlib compressed (1 MiB chunks) |
UDBZ |
5 | ✓ | — | bzip2 compressed (write not supported) |
UDSP |
11 | ✓ | ✓ | Sparse (NOCOPY runs for zero sectors) |
Public API
// DetectUDIFFormat returns "UDRW", "UDSP", … from the koly trailer.
func DetectUDIFFormat(path string) (string, error)
// IsUDIF reports whether path is a valid Apple UDIF image.
func IsUDIF(path string) bool
// ConvertUDIF reads all sectors from src and writes them to dst in dstFormat.
// Write formats: UDRW, UDRO, UDSP (sparse), UDZO (zlib). UDCO and UDBZ are
// not yet writable. CRC-32 checksums are written in the koly + blkx headers.
// Multi-segment source images are not supported.
func ConvertUDIF(src, dst, dstFormat string) error
// ResizeUDRW grows a UDIF image to newSizeBytes (rounded to a 512-byte
// boundary), preserving the variant. Shrinking is not supported.
func ResizeUDRW(path string, newSizeBytes int64) error
// UnpackToTemp extracts all sectors to a raw temp file (caller os.Removes it).
func UnpackToTemp(path string) (tmpPath string, err error)
// PackFromTemp wraps the raw file as a new UDIF UDRW image (atomic).
func PackFromTemp(tmpPath, destPath string) error
// WrapRaw converts an existing raw file into a UDIF UDRW image in place (atomic).
func WrapRaw(path string) error
Format
Format{} satisfies the interface Format contract
structurally (no import needed):
type Format struct{}
func (Format) Name() string // "dmg"
func (Format) Create(path string, sizeBytes int64) error // blank UDIF UDRW
func (Format) Detect(path string) (bool, error) // koly magic
func (Format) ToRaw(src, dst string, w io.Writer) error // extract sectors → raw
func (Format) Resize(path string, newSizeBytes int64) error // grow or shrink
Examples
import "github.com/go-diskimages/dmg"
// Detect and convert.
variant, _ := dmg.DetectUDIFFormat("disk.dmg") // e.g. "UDRW"
_ = dmg.ConvertUDIF("disk.dmg", "disk.sparse.dmg", "UDSP")
_ = dmg.ConvertUDIF("disk.dmg", "disk.z.dmg", "UDZO")
_ = dmg.ResizeUDRW("disk.dmg", 2<<30) // grow to 2 GiB
// Wrap a raw file, edit, repack.
_ = dmg.WrapRaw("disk.raw") // disk.raw → UDIF in place
tmp, _ := dmg.UnpackToTemp("disk.dmg")
defer os.Remove(tmp)
_ = dmg.PackFromTemp(tmp, "disk.dmg")