Each partition of a UDIF image has one block table, which starts with the characters
mish. It is carried in the Data of a blkx entry
in the property list, in base64. The table
says where every run of the partition’s sectors is, and how it is stored. Numbers are
big-endian. Apple has not published this layout; it comes from independent
documentation, and was checked against real images.
Header
0Signaturemishand version8 bytes8Sectors the first sector and how many16 bytes24Data offset and buffer hints16 bytes40Reserved24 bytes64Checksum a type, a size and up to 128 bytes136 bytes200Run count how many entries follow4 bytes204Runs one entry for each run40 bytes each
| Offset | Size | Field | Meaning |
|---|---|---|---|
| 0 | 4 | Signature | The characters mish |
| 4 | 4 | Version | 1 |
| 8 | 8 | First sector | The partition’s first sector on the disk |
| 16 | 8 | Sector count | How many sectors the partition has |
| 24 | 8 | Data offset | Added to each run’s compressed offset; usually 0 |
| 32 | 4 | Buffers needed | A hint for a decoder |
| 36 | 4 | Block descriptors | A value written by the tool that made the image |
| 40 | 24 | Reserved | Zeros |
| 64 | 4 | Checksum type | 0 for none; 2 for CRC-32 |
| 68 | 4 | Checksum size | Its size in bits: 32 for CRC-32 |
| 72 | 128 | Checksum | A CRC-32 of the partition’s decompressed sectors, in the first bytes of the field |
| 200 | 4 | Run count | The number of 40-byte entries that follow |
The checksum is taken over the sectors in order. Runs of type ignore count for nothing: a partition that is all ignored runs has a CRC-32 of zero.
Run entries
| Offset | Size | Field | Meaning |
|---|---|---|---|
| 0 | 4 | Type | How the run is stored, one of the values below |
| 4 | 4 | Comment | A value written by the tool that made the image |
| 8 | 8 | First sector | Where the run starts, counted from the partition’s first sector |
| 16 | 8 | Sector count | How many sectors the run holds |
| 24 | 8 | Compressed offset | Where the run’s bytes start, counted from the data fork and the table’s data offset |
| 32 | 8 | Compressed length | How many bytes of the data fork the run takes |
The last entry of a table is a terminator of type 0xFFFFFFFF; in the images
checked its first sector equals the partition’s sector count.
Run types
| Type | Name | Stored in the data fork |
|---|---|---|
0x00000000 | Zero fill | Nothing; the sectors read as zeros |
0x00000001 | Raw | The sectors as they are |
0x00000002 | Ignore | Nothing; used for unallocated space such as Apple_Free partitions, and not counted in the checksum |
0x80000004 | ADC | Apple Data Compression |
0x80000005 | zlib | A zlib stream |
0x80000006 | bzip2 | A bzip2 stream |
0x80000007 | LZFSE | An LZFSE stream |
0x80000008 | LZMA | An xz stream holding LZMA2 data |
0x7FFFFFFE | Comment | Nothing; a comment entry that holds no sectors |
0xFFFFFFFF | Terminator | Nothing; the end of the table |
See the compression page for how each method stores its data.
Finding a sector
- Find the table whose first sector and sector count cover the sector.
- Take the sector number within the partition by subtracting the table’s first sector.
- Find the run whose first sector and sector count cover it.
- Read the run’s compressed length of bytes at the data fork offset, plus the table’s data offset, plus the run’s compressed offset.
- Decompress the run to its sector count × 512 bytes, and take the sector from it.
Stored runs of an image made by hdiutil are at most 2048 sectors (1 MiB), or 8192 on the LZFSE images checked, so only one small run needs decompressing for each read.