disk

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Apple Data Compression (ADC) decompressor.

ADC is the codec behind UDCO images. It is a byte-oriented LZ77: each control byte introduces either a run of literal bytes or a back-reference into what has already been produced.

Device abstraction for disk images and partitions

DMG reader with decompression support for APFS extraction Based on go-apfs/pkg/disk/dmg implementation by blacktop

Reconstruction helpers for the UDIF/DMG writer: materialise the full raw disk image (or per-block raw data) from an existing DMG so it can be losslessly re-encoded or compared byte-for-byte.

UDIF/DMG writer (encoder): the inverse of dmg_reader.go. Produces a valid UDIF DMG from a set of source blocks, and can losslessly repack an existing DMG by reconstructing its raw image layout and re-encoding it.

On-disk layout produced (all multi-byte fields BIG-ENDIAN):

[data fork: concatenated compressed chunks]
[XML plist: resource-fork -> blkx array of mish blocks]
[512-byte "koly" trailer]

Every mish block uses DataOffset=0, so each chunk's CompressedOffset is an absolute position within the data fork (which itself starts at file offset 0, DataForkOffset=0). This matches how Apple's hdiutil lays out its images and how dmg_reader.go inverts the offsets.

GPT (GUID Partition Table) parser

Package disk provides utilities for handling disk images (DMG)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecompressADC

func DecompressADC(src []byte, want int) ([]byte, error)

DecompressADC decompresses an ADC stream into exactly want bytes.

The caller always knows how long the answer is — a DMG chunk declares it — and passing it in is what makes the decoder safe. Deriving the size from the input instead means guessing a compression ratio, and a guess that is too small turns a valid stream into an out-of-range panic.

func EncodeUDIF

func EncodeUDIF(dst io.Writer, blocks []SourceBlock, opts *EncodeOptions) error

EncodeUDIF writes a complete UDIF DMG for the given source blocks to dst. Blocks are emitted in slice order; each block's chunks are written to the data fork with absolute CompressedOffsets. dst is written strictly in order (data fork, plist, koly trailer) so it need not be seekable.

func OpenWithOffset

func OpenWithOffset(filename string) (reader io.ReaderAt, offset int64, closer io.Closer, err error)

OpenWithOffset opens a disk image and returns an io.ReaderAt for the APFS container plus the byte offset at which the container starts. The image format is detected from content, never from the filename:

  1. UDIF "koly" trailer in the last 512 bytes -> DMG with on-the-fly decompression (the returned reader is already partition-relative)
  2. "NXSB" magic at offset 32 -> bare APFS container at offset 0
  3. "EFI PART" GPT at LBA 1 -> raw image; offset of the Apple_APFS partition
  4. otherwise -> assume a bare container at offset 0 (the superblock checksum will reject non-APFS data with a clear error)

func ReconstructRawImage

func ReconstructRawImage(dmgPath string) ([]byte, error)

ReconstructRawImage returns the complete raw disk image encoded by the DMG at dmgPath: every block (zero-fill and ignored materialised as zeros, others decompressed) placed at its absolute sector offset. The result is a byte-exact reproduction of the original raw image.

The entire image is held in memory, so it is only suitable for images that comfortably fit. Prefer ReconstructRawImageTo, which streams, or RepackDMG, which never assembles the image at all.

func ReconstructRawImageTo

func ReconstructRawImageTo(dst io.WriterAt, dmgPath string) (int64, error)

ReconstructRawImageTo writes the complete raw disk image encoded by the DMG at dmgPath to dst, placing every block at its absolute sector offset. Zero-fill and ignored chunks become zeros, everything else is decompressed. The result is a byte-exact reproduction of the original raw image.

Nothing larger than one chunk is held in memory, so this works on an image of any size. It returns the raw image length in bytes.

func RepackDMG

func RepackDMG(srcPath, dstPath string, opts *EncodeOptions) error

RepackDMG reconstructs the raw image layout of the source DMG at srcPath and re-encodes it losslessly to dstPath, preserving every block boundary and name so both this package's reader and hdiutil handle the output exactly as they handle the input. The chunk data is recompressed per opts.

func WrapRawImageDMG

func WrapRawImageDMG(dstPath string, raw []byte, partitionHint string, opts *EncodeOptions) error

WrapRawImageDMG writes a UDIF DMG at dstPath wrapping a raw file system image as a single Apple partition block. partitionHint is the Apple partition type name embedded in the block name so the reader (and hdiutil) locate the file system, e.g. "Apple_HFSX", "Apple_HFS" or "Apple_APFS".

The whole image is held in memory. WrapRawImageDMGFrom takes the same image as an io.ReaderAt and does not.

func WrapRawImageDMGFrom

func WrapRawImageDMGFrom(dstPath string, src io.ReaderAt, size int64, partitionHint string, opts *EncodeOptions) error

WrapRawImageDMGFrom writes a UDIF DMG at dstPath wrapping the raw file system image read from src, which must cover [0, size). It is WrapRawImageDMG without holding the image in memory: nothing larger than one chunk is retained, so src may be an *os.File of any size.

The output is byte-identical to what WrapRawImageDMG produces for the same bytes; only where they come from differs.

Types

type Compression

type Compression int

Compression selects the chunk compressor used by the encoder.

const (
	// CompressionZlib compresses each non-zero chunk with zlib (UDZO chunk
	// type), falling back to raw storage when compression does not shrink the
	// chunk. It is the library default and is understood by both this package's
	// reader and hdiutil.
	CompressionZlib Compression = iota
	// CompressionNone stores every non-zero chunk raw (uncompressed).
	CompressionNone
	// CompressionLZFSE compresses each non-zero chunk with LZFSE (ULFO chunk
	// type), Apple's modern DMG codec. It gives a better ratio than zlib at
	// higher speed and is what hdiutil produces by default on recent macOS.
	// Like the others it falls back to raw storage for a chunk it cannot shrink.
	CompressionLZFSE
	// CompressionLZMA compresses each non-zero chunk with LZMA (ULMO chunk
	// type), the raw LZMA1 "alone" stream both this package's reader and hdiutil
	// accept. It gives the best ratio of the set at the cost of speed.
	CompressionLZMA
)

type DMGChunk

type DMGChunk struct {
	Type             uint32
	Comment          uint32
	DiskOffset       uint64 // Logical offset in bytes
	DiskLength       uint64 // Length in bytes
	CompressedOffset uint64 // Offset in DMG file
	CompressedLength uint64 // Compressed size in bytes
}

DMGChunk represents a compressed chunk in a DMG partition

type DMGFooter

type DMGFooter struct {
	Signature             [4]byte
	Version               uint32
	HeaderSize            uint32
	Flags                 uint32
	RunningDataForkOffset uint64
	DataForkOffset        uint64
	DataForkLength        uint64
	RsrcForkOffset        uint64
	RsrcForkLength        uint64
	SegmentNumber         uint32
	SegmentCount          uint32
	SegmentID             [16]byte
	DataChecksum          [136]byte
	PlistOffset           uint64
	PlistLength           uint64
	Reserved1             [64]byte
	CodeSignatureOffset   uint64
	CodeSignatureLength   uint64
	Reserved2             [40]byte
	MasterChecksum        [136]byte
	ImageVariant          uint32
	SectorCount           uint64
	Reserved3             uint32
	Reserved4             uint32
	Reserved5             uint32
}

DMGFooter represents the UDIF Resource File footer

type DMGPartition

type DMGPartition struct {
	Name        string
	StartSector uint64
	SectorCount uint64
	DataOffset  uint64
	Chunks      []DMGChunk
}

DMGPartition represents a partition within a DMG

type DMGReader

type DMGReader struct {
	// contains filtered or unexported fields
}

DMGReader implements io.ReaderAt for reading from compressed DMG files

func OpenDMG

func OpenDMG(filename string) (*DMGReader, error)

OpenDMG opens a DMG file and prepares it for reading

func (*DMGReader) Close

func (r *DMGReader) Close() error

Close closes the DMG reader

func (*DMGReader) ReadAt

func (r *DMGReader) ReadAt(buf []byte, off int64) (n int, err error)

ReadAt implements io.ReaderAt for decompressing DMG data on-the-fly

func (*DMGReader) Size

func (r *DMGReader) Size() int64

Size returns the size of the APFS partition in bytes

type Device

type Device interface {
	io.ReaderAt
	Size() int64
	Close() error
}

Device is a random-access, sized, closable view of a disk image or a partition within one. Implementations must return partition-relative data from ReadAt when they represent a partition (as DMGReader does for the APFS partition inside a DMG).

type EncodeOptions

type EncodeOptions struct {
	// Compression is the chunk compressor (default CompressionZlib).
	Compression Compression
	// ChunkSectors is the number of 512-byte sectors per chunk
	// (default encodeDefaultChunkSectors). Larger chunks compress slightly
	// better; smaller chunks give finer random-access granularity.
	ChunkSectors uint64
	// NoChecksums disables CRC32 checksum emission (all checksum fields are
	// written as UDIF "none", type 0). By default CRC32 (type 2) checksums
	// are emitted for each block, the data fork, and the master checksum.
	NoChecksums bool
	// ZlibLevel is the zlib compression level (zlib.DefaultCompression when 0
	// is passed via the option struct is treated as default). Accepts the
	// standard compress/zlib levels.
	ZlibLevel int
}

EncodeOptions controls how EncodeUDIF/RepackDMG produce a DMG.

type GPTHeader

type GPTHeader struct {
	Signature       gptMagic
	Revision        uint32
	HeaderSize      uint32
	CRC32           uint32
	Reserved        uint32
	HeaderStartLBA  uint64
	BackupLBA       uint64
	FirstUsableLBA  uint64
	LastUsableLBA   uint64
	DiskGUID        gptGUID
	EntriesStart    uint64
	EntriesCount    uint32
	EntriesSize     uint32
	PartitionsCRC32 uint32
	Padding         [420]byte
}

GPTHeader is a GPT header structure

func (GPTHeader) Verify

func (h GPTHeader) Verify() error

Verify verifies the GPT header

type GPTPartition

type GPTPartition struct {
	Type               gptGUID
	ID                 gptGUID
	StartingLBA        uint64
	EndingLBA          uint64
	Attributes         uint64
	PartitionNameUTF16 [72]uint8
}

GPTPartition is a GPT partition entry

func (GPTPartition) IsEmpty

func (p GPTPartition) IsEmpty() bool

IsEmpty returns if the partition is empty

func (GPTPartition) Name

func (p GPTPartition) Name() string

Name returns the partition's name

type SourceBlock

type SourceBlock struct {
	Name        string
	CFName      string
	ID          string
	Attributes  string
	StartSector uint64
	// SectorCount is the number of 512-byte sectors the block covers. When
	// Data is non-nil it must equal len(Data)/512; otherwise it is authoritative.
	SectorCount uint64
	// Data is the exact uncompressed bytes for the block, length a multiple of
	// 512. A nil Data and a nil Reader mean an all-zero block of SectorCount
	// sectors.
	Data []byte
	// Reader supplies the block's uncompressed bytes lazily, covering
	// [0, SectorCount*512) in block-relative coordinates. It is the alternative
	// to Data for a block too large to hold in memory; setting both is an error.
	//
	// The encoder reads it in ascending, chunk-sized windows and never retains
	// more than one chunk, so a block of any size costs a fixed amount of memory.
	Reader io.ReaderAt
}

SourceBlock is one blkx block to encode. Its raw bytes cover the half-open sector range [StartSector, StartSector+SectorCount).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL