Skip to content

Instantly share code, notes, and snippets.

@timsutton
Created November 6, 2014 22:13
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 timsutton/9a6e45ab668e9a81ba45 to your computer and use it in GitHub Desktop.
Save timsutton/9a6e45ab668e9a81ba45 to your computer and use it in GitHub Desktop.
convert_mavericks_image from Parallels 10
#!/bin/bash
#
# This script converts a Mavericks app bundle to a bootable dmg image file
# in a few steps:
# - Converts the BaseSystem.dmg image file to a writable format and resizes it
# to contain additional data.
# - Replaces the Packages symlink with the Packages folder from the
# InstallESD.dmg image file.
# - Adds the BaseSystem.dmg and BaseSystem.chunklist
source_app="$1"
boot_dmg="$2"
ERR_INVALID_ARG=1
ERR_FILE_EXISTS=2
ERR_UNEXPECTED=3
# Check input params
if [[ -z "$source_app" ]]; then
echo "Please specify the Mavericks app bundle."
exit $ERR_INVALID_ARG
fi
if [[ ! -r "$source_app" ]]; then
echo "'$source_app' is not accessible."
exit $ERR_INVALID_ARG
fi
if [[ -z "$boot_dmg" ]]; then
echo "Please specify where to save the resulting dmg image file."
exit $ERR_INVALID_ARG
fi
if [[ -e "$boot_dmg" ]]; then
if [[ -n "$3" && "$3" == "--overwrite" ]]; then
echo "The file '$boot_dmg' already exists and will be removed"
rm "$boot_dmg" || exit $ERR_UNEXPECTED
else
echo "The file '$boot_dmg' already exists. Please choose another \
filename and try again."
exit $ERR_FILE_EXISTS
fi
fi
temp_dir="$(mktemp -d -t 'Mavericks_Boot_Image')"
install_esd_mnt="${temp_dir}/install_esd"
boot_image_mnt="${temp_dir}/boot_image"
tmp_binaries="${temp_dir}/tmp_binaries"
install_esd_dmg="${source_app}/Contents/SharedSupport/InstallESD.dmg"
base_system_dmg="${install_esd_mnt}/BaseSystem.dmg"
base_system_chunklist="${install_esd_mnt}/BaseSystem.chunklist"
input_packages="${install_esd_mnt}/Packages"
output_packages="${boot_image_mnt}/System/Installation/Packages"
boot_image_ready="no"
set -x
trap "cleanup; exit $ERR_UNEXPECTED" ERR
trap "cleanup; exit $ERR_UNEXPECTED" SIGHUP SIGINT SIGTERM
terminate() {
local PID=$1
if [ -n "$PID" ]; then
kill -SIGTERM "$PID"
wait "$PID"
fi
}
wait_for_process() {
local PID=$1
trap "terminate $PID; cleanup; exit $ERR_UNEXPECTED" SIGHUP SIGINT SIGTERM
wait $PID
res="$?"
trap "cleanup; exit $ERR_UNEXPECTED" SIGHUP SIGINT SIGTERM
echo "Wait finished with code $res"
return $res
}
execute_in_background() {
"$@" &
local PID=$!
wait_for_process $PID
}
cleanup() {
# Wait for unfinished processes if script was cancelled before
# trap for termination was set.
wait
umount_path "$install_esd_mnt"
umount_path "$boot_image_mnt"
rm -rf "$tmp_binaries"
rmdir "$temp_dir"
if [[ -e "$boot_dmg" && "$boot_image_ready" == "no" ]]; then
rm "$boot_dmg"
fi
}
mount_dmg() {
local dmg="$1"
local mnt="$2"
execute_in_background hdiutil attach "$dmg" -mountpoint "$mnt" -nobrowse
return $?
}
umount_path() {
if [[ ! -d "$1" ]]; then
return
fi
for i in `seq 1 10`; do
hdiutil detach "$1"
if [[ $? == 0 ]]; then
break
fi
sleep 1
done
}
convert_dmg() {
local src_dmg="$1"
local dst_dmg="$2"
execute_in_background hdiutil convert "$src_dmg" -o "$dst_dmg" -format UDRW
return $?
}
get_size() {
local path="$1"
BLOCKSIZE=512 du -s "$path" | awk '{print $1}'
return $?
}
get_dmg_size() {
local dmg="$1"
hdiutil resize -limits "$dmg" | awk '{print $2}'
return $?
}
resize_dmg() {
local dmg="$1"
local size="$2"
execute_in_background hdiutil resize "$dmg" -sectors "$size"
return $?
}
extract_from_pkg() {
local src_pkg="$1"
local dst_path="$2"
local file="$3"
mkdir "$dst_path" || return 1
( cd "$dst_path" && \
execute_in_background xar -xf "$src_pkg" -v --exclude Bom --exclude PackageInfo && \
execute_in_background bunzip2 -c Payload | cpio -id "./$3" ; \
rm Payload )
[ -e "${dst_path}/${file}" ]
return $?
}
mount_dmg "${install_esd_dmg}" "$install_esd_mnt"
convert_dmg "${base_system_dmg}" "${boot_dmg}"
current_size="$(get_dmg_size "${boot_dmg}")"
packages_size="$(get_size "${install_esd_mnt}/Packages")"
base_system_size="$(get_size "${base_system_dmg}")"
base_system_chunklist_size="$(get_size "${base_system_chunklist}")"
resize_dmg "${boot_dmg}" "$(( $current_size + $packages_size + $base_system_size + $base_system_chunklist_size))"
mount_dmg "${boot_dmg}" "${boot_image_mnt}"
rm "${output_packages}"
execute_in_background cp -r "${input_packages}" "${output_packages}"
execute_in_background cp -r "${base_system_dmg}" "${boot_image_mnt}"
cp -r "${base_system_chunklist}" "${boot_image_mnt}"
boot_image_ready="yes"
trap '' ERR
trap '' SIGHUP SIGINT SIGTERM
cleanup
stat -f "%z %N" "${boot_dmg}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment