DiskImageKit
Create, open, and manage disk images.
Overview
The DiskImageKit framework provides an API for creating, opening, and managing disk images. It’s designed primarily for use with the Virtualization framework, enabling seamless integration of disk images as storage for virtual machines.
Supported Formats
DiskImageKit supports two disk image formats:
ASIF: Apple Sparse Image Format. ASIF images support both standalone and stacked configurations.
RAW: Traditional raw disk image format. You can only use RAW images as standalone images or as base images in stacked configurations.
Stacked Disk Images
A key feature of DiskImageKit is support for stacked disk images, where multiple image layers are combined into a single logical disk. This enables powerful workflows:
Base layer: A read-only foundation image (can be shared across multiple VMs, for example).
Cache layer: Stores blocks read from the layer beneath it (useful when that layer is on slow storage).
Overlay layers: Store modifications to the base image (enables copy-on-write snapshots).
Stacked images allow you to:
Reduce storage space by sharing a common base image.
Implement snapshot and restore functionality.
Optimize performance with caching layers.
Getting started
Create a standalone ASIF image
Create a standalone disk image:
import DiskImageKit
// Create a 40 GB ASIF disk image with 512-byte blocks.
let imageURL = URL(fileURLWithPath: "/path/to/disk.asif")
let blockCount = 40 * 1000 * 1000 * 1000 / 512
let diskImage = try DiskImage(creating: .asif(
url: imageURL,
blockCount: blockCount,
blockSize: .bytes512
))Open an existing image
Open any existing disk image (standalone or layer):
let diskImage = try DiskImage(opening: .open(url: imageURL))
print("Format: \(diskImage.format)")
print("Size: \(diskImage.blockCount) blocks of \(diskImage.blockSize.rawValue) bytes")
if let layerType = diskImage.layerType {
print("Layer type: \(layerType == .cache ? "Cache" : "Overlay")")
}Create a stacked disk image
Build a multi-layer disk image:
// 1. Open an existing base image.
let baseImage = try DiskImage(opening: .open(url: baseImageURL))
// 2. Add a cache layer. This step improves performance when base is on network storage.
var stackedImage = try baseImage.appending(.asifLayer(url: cacheURL, type: .cache))
// 3. Add an overlay layer (stores all writes).
stackedImage = try stackedImage.appending(.asifLayer(url: overlayURL, type: .overlay))Use disk images with the Virtualization framework
Pass the disk image - either a standalone image or a stack - to the Virtualization framework:
import Virtualization
let storageAttachment = try VZDiskImageStorageDeviceAttachment(diskImage: diskImage)Truncate or extend disk images
You can truncate or extend disk images (this does not resize the filesystem inside the image):
let diskImage = try DiskImage(opening: .open(url: imageURL))
// Truncate to 80 GB (in 512-byte blocks).
let newBlockCount = 80 * 1000 * 1000 * 1000 / 512
try diskImage.truncate(blockCount: newBlockCount) Inspect disk images with image properties
DiskImage objects provide various properties to inspect disk images:
let diskImage = try DiskImage(opening: .open(url: imageURL))
print("Block count: \(diskImage.blockCount).")
print("Block size: \(diskImage.blockSize.rawValue) bytes.")
print("Format: \(diskImage.format)")
print("Open mode: \(diskImage.openMode).")
if let layerType = diskImage.layerType {
switch layerType {
case .cache:
print("This is a cache layer.")
case .overlay:
print("This is an overlay layer.")
}
}
// UUID tracking (for stacked images).
if let layerUUID = diskImage.layerUUID {
print("Layer UUID: \(layerUUID).")
}
if let parentUUID = diskImage.parentUUID {
print("Parent UUID: \(parentUUID).")
}
Important details about stacked disk images
Remember to keep the following details in mind when working with stack disk images:
One cache layer per stack: The framework allows only one cache layer per stacked disk image.
Layer ordering matters: Layers are processed from bottom (base) to top. The topmost layer determines the stack’s size and receives all writes.
UUID compatibility: When appending a new layer using appending(_:), the framework sets the parentUUID of the layer to the layerUUID of its parent layer (unless the parent is a raw image, which has no UUID). The layer UUID changes if the layer is written to, to prevent using stacks with incompatible layers. Appending an existing layer using appending(_:) fails if there is such a mismatch.
Topics
Essential Types
Creating disk images
init(creating:)ASIFCreationConfigurationASIFLayerCreationConfigurationDiskImage.CreationConfigurationRAWCreationConfiguration