Created
November 6, 2014 22:13
-
-
Save timsutton/9a6e45ab668e9a81ba45 to your computer and use it in GitHub Desktop.
convert_mavericks_image from Parallels 10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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