Skip to content

Instantly share code, notes, and snippets.

@cirocosta
Created March 15, 2019 12:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cirocosta/1a3e69e78dd6927a7161d9a54f6f899b to your computer and use it in GitHub Desktop.
Save cirocosta/1a3e69e78dd6927a7161d9a54f6f899b to your computer and use it in GitHub Desktop.
a "baggageclaim-driver"-a-like script leveraging `hdiutil` and `clonefile(2)`
#!/bin/bash
# apfs - a "baggageclaim-driver"-a-like script leveraging `hdiutil` and
# `clonefile(2)`.
#
# Usage: apfs.sh (create|cow|destroy)
#
# Commands:
# create vol-id desc: creates a volume
# cow vol-id parent-vol-id desc: creates a cow layer based on `parent-vol-id`
# destroy vol-id desc: destroys a volume
#
set -e -x
readonly IMAGES_PATH="/tmp/images"
readonly VOLUMES_PATH="/tmp/volumes"
main() {
case $1 in
create)
create_volume $2
;;
destroy)
destroy_volume $2
;;
cow)
create_cow $2 $3
;;
*)
echo "Error: Unknown argument '$1'."
echo "Usage: $0 (create|cow|destroy)"
exit 1
;;
esac
}
# creates an underlying image (sparse file) to back the volume, then attaches
# such image to a `/dev` device which gets mounted to a mountpoint.
#
# /images/uuid1.sparseimage (1G sparse file)
# |
# *--> /dev/disk27s1 (block device)
# |
# *--> /volumes/uuid1 (mountpoint)
#
create_volume() {
local handle=$1
local volume_path disk_image
if [[ -z $1 ]]; then
echo "create_volume(handle): arg must not be empty"
return 1
fi
disk_image="$IMAGES_PATH/${handle}.sparseimage"
volume_path="$VOLUMES_PATH/${handle}"
hdiutil create \
-size 1g \
-type SPARSE \
-fs APFS \
-volname $handle \
$disk_image
hdiutil attach \
-mountpoint $volume_path \
$disk_image
}
# clones the backing image of the parent volume and then mounts that
# to a different location, allowing us to modify the contents of the
# parent volume without interfering with the contents of the first
# (multi-file cow semantics!)
#
#
# /images/uuid1.sparseimage (parent's 1G sparse file)
# |
# |
# *-> clone this thing: clonefile(src,dst, flags)
# |
# *-> produces /volume/uuid12.sparseimage
# |
# *-> create disk & attach to it
# |
# mount to a volume location <-*
#
#
create_cow() {
local handle=$1
local handle_parent=$2
local volume_path disk_image disk_image_parent
if [[ -z $handle || -z $handle_parent ]]; then
echo "create_cow(handle, handle_parent): both args must not be empty"
return 1
fi
disk_image="$IMAGES_PATH/${handle}.sparseimage"
disk_image_parent="$IMAGES_PATH/${handle_parent}.sparseimage"
volume_path="$VOLUMES_PATH/${handle}"
cp -c $disk_image_parent $disk_image
hdiutil attach \
-mountpoint $volume_path \
$disk_image
}
# destroys a volume by detaching it an removing the underlying disk image.
destroy_volume() {
local handle=$1
local volume_path disk_image
if [[ -z $handle ]]; then
echo "destroy_volume(handle): arg must not be empty"
return 1
fi
disk_image="$IMAGES_PATH/${handle}.sparseimage"
volume_path="$VOLUMES_PATH/${handle}"
hdiutil detach $volume_path
rm -f $disk_image
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment