├── .gitignore ├── Create_osx_install_iso ├── LICENSE ├── README.md └── create_osx_install_iso.sh └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Create_osx_install_iso/LICENSE: -------------------------------------------------------------------------------- 1 | Script for building bootable .iso images from downloaded OS X upgrade 2 | Copyright (C) 2015 Karlson2k (Evgeny Grin) 3 | 4 | You can run, copy, modify, publish and do whatever you want with this 5 | script as long as this message and copyright string above are preserved. 6 | You are also explicitly allowed to reuse this script under any LGPL or 7 | GPL license or under any BSD-style license. -------------------------------------------------------------------------------- /Create_osx_install_iso/README.md: -------------------------------------------------------------------------------- 1 | Bash script for building bootable .ISO image from downloaded application for OS X upgrade. 2 | Resulting .ISO can be used for installation on virtual machines (virtual machine must be running on host with Apple hardware). -------------------------------------------------------------------------------- /Create_osx_install_iso/create_osx_install_iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script for building bootable .iso images from downloaded macOS upgrade 4 | # Copyright (C) 2015-2017 Karlson2k (Evgeny Grin) 5 | # 6 | # You can run, copy, modify, publish and do whatever you want with this 7 | # script as long as this message and copyright string above are preserved. 8 | # You are also explicitly allowed to reuse this script under any LGPL or 9 | # GPL license or under any BSD-style license. 10 | # 11 | # 12 | # Latest version: 13 | # https://raw.githubusercontent.com/Karlson2k/k2k-OSX-Tools/master/Create_osx_install_iso/create_osx_install_iso.sh 14 | # 15 | # Version 1.0.6 16 | 17 | readonly script_org_name='create_osx_install_iso.sh' || exit 127 18 | unset work_dir script_name tmp_dir OSX_inst_name OSX_inst_inst_dmg_mnt \ 19 | OSX_inst_img_rw_mnt OSX_inst_img_rw_dev || exit 127 20 | work_dir="$PWD" 21 | save_IFS="$IFS" || exit 127 22 | export LANG='en_US.UTF-8' || exit 127 # prevent localization of output, not really required 23 | 24 | [[ `ps -o comm -p $$ | tail -n1 2>/dev/null` =~ bash$ ]] || { 25 | echo "Script is designed to be run only with bash" 26 | exit 127 27 | } 28 | [[ "$(uname -s)" == Darwin ]] || { 29 | echo "Script can be run only on Mac OS X" 30 | exit 127 31 | } 32 | 33 | cleanup() { 34 | trap - SIGHUP SIGTERM SIGQUIT SIGINT SIGSTOP SIGTSTP EXIT 35 | if [[ -n $tmp_dir ]] && [[ -e "$tmp_dir" ]]; then 36 | if [[ -e "$OSX_inst_img_rw_dev" ]]; then 37 | echo "Unmounting writable image..." 38 | hdiutil detach "$OSX_inst_img_rw_dev" -force 39 | fi 40 | if [[ -e "$OSX_inst_img_rw_mnt" ]]; then 41 | echo "Unmounting writable image..." 42 | hdiutil detach "$OSX_inst_img_rw_mnt" -force 43 | fi 44 | if [[ -e "$OSX_inst_inst_dmg_mnt" ]]; then 45 | echo "Unmounting temporary mounted source image..." 46 | hdiutil detach "$OSX_inst_inst_dmg_mnt" -force 47 | fi 48 | echo "Removing temporary files..." 49 | rm -fdR "$tmp_dir" 50 | fi 51 | } 52 | 53 | trap '{ exit_code="$?"; cleanup; exit $exit_code; }' EXIT 54 | 55 | echo_term_ansi_m() { 56 | local n_param='' 57 | if [[ "$1" == "-n" ]]; then 58 | n_param="$1" 59 | shift 60 | elif [[ -z "$1" ]]; then shift 61 | fi 62 | local m_code="$1" 63 | shift 64 | if [[ -t 1 ]]; then 65 | echo $n_param $'\e['"${m_code}m$@"$'\e[0m' 66 | else 67 | echo $n_param "$@" 68 | fi 69 | } 70 | 71 | echo_neutral() { 72 | echo "$@" 73 | } 74 | 75 | echo_enh() { 76 | echo_term_ansi_m '1;97' "$@" 77 | } 78 | 79 | echo_enh_n() { 80 | echo_term_ansi_m -n '1;97' "$@" 81 | } 82 | 83 | echo_positive() { 84 | echo_term_ansi_m '1;92' "$@" 85 | } 86 | 87 | echo_positive_n() { 88 | echo_term_ansi_m -n '1;92' "$@" 89 | } 90 | 91 | echo_warning() { 92 | echo_term_ansi_m '1;93' "$@" 93 | } 94 | 95 | echo_warning_n() { 96 | echo_term_ansi_m -n '1;93' "$@" 97 | } 98 | 99 | echo_error() { 100 | echo_term_ansi_m '1;91' "$@" 1>&2 101 | } 102 | echo_error_n() { 103 | echo_term_ansi_m -n '1;91' "$@" 1>&2 104 | } 105 | 106 | exit_with_error() { 107 | trap - SIGHUP SIGTERM SIGQUIT SIGINT SIGSTOP SIGTSTP EXIT 108 | if [[ -n $1 ]]; then 109 | echo_error "Error: $1" 110 | else 111 | echo_error "Error." 112 | fi 113 | cleanup 114 | [[ $2 > 0 ]] && exit $2 115 | exit 1 116 | } 117 | 118 | trap '{ exit_with_error "unexpected interrupt at line $LINENO"; exit 255; }' SIGHUP SIGTERM SIGQUIT SIGINT SIGSTOP SIGTSTP 119 | 120 | # trap 'echo "Line number: $LINENO"; read -p "\"Enter\" to continue" ' DEBUG 121 | 122 | stage_start() { 123 | echo_enh_n "$@... " 124 | } 125 | 126 | stage_start_nl() { 127 | stage_start "$@" 128 | echo '' 129 | } 130 | 131 | stage_end_ok() { 132 | if [[ -z "$@" ]]; then 133 | echo_positive "OK" 134 | else 135 | echo_positive "$@" 136 | fi 137 | } 138 | 139 | stage_end_warn() { 140 | if [[ -z "$@" ]]; then 141 | echo_warning "OK, but with warnings" 142 | else 143 | echo_warning "$@" 144 | fi 145 | } 146 | 147 | is_answer_valid() { 148 | local answ="$1" 149 | shift 150 | while [[ -n $1 ]]; do 151 | [[ "$answ" == "$1" ]] && return 0 152 | shift 153 | done 154 | return 1 155 | } 156 | 157 | script_name="$(basename "${BASH_SOURCE[0]}" 2>/dev/null)" 158 | [[ -n "$script_name" ]] || script_name="${0##*/}" # fallback 159 | [[ -n "$script_name" ]] || script_name="${script_org_name}" # second fallback 160 | 161 | script_version="$(sed -n -e '\|^# Version| {s|^# Version \(.*$\)|\1|p; q;}' "${BASH_SOURCE[0]}" 2>/dev/null)" || unset script_version 162 | [[ -n "$script_version" ]] || script_version="Unknown" 163 | 164 | print_help() { 165 | echo "\ 166 | Script for creating .iso images from downloaded macOS upgrade application. 167 | Usage:" 168 | echo_enh_n " $script_name"; echo " [options] 169 | 170 | Valid options are: 171 | -a, --app[lication] 172 | Path and name of macOS upgrade application. 173 | Path can be omitted if application is located at 174 | default path. 175 | -i, --iso 176 | Path with optional name for output .iso 177 | -m, --method 178 | Use method number D to create installation image: 179 | Method 1 create image that most close to Apple's image, 180 | but potentially less compatible with some BIOSes/EFI. 181 | Method 2 create more BIOS/EFI-friendly images, but 182 | require more disk space for conversion. 183 | Method 3 can produce bootable images without super 184 | user rights. 185 | -n, --nosudo 186 | Do not use sudo command 187 | -v, --verify 188 | Do not skip verifications (slow down image creation) 189 | -h, --help Print this message and exit 190 | -V, --version 191 | Print version information and exit" 192 | 193 | } 194 | 195 | print_version() { 196 | echo "${script_org_name} version $script_version" 197 | } 198 | 199 | exit_with_cmd_err() { 200 | echo_error "$@" 201 | print_help 1>&2 202 | exit 32 203 | } 204 | 205 | unset cmd_par_app cmd_par_iso test_name ver_opt cr_method || exit_with_error "Can't unset variable" 206 | allow_sudo='yes' && ver_opt='--noverify' || exit_with_error "Can't set variable" 207 | while [[ -n "$1" ]]; do 208 | case "$1" in 209 | -a | --app | --application ) cmd_par_app="$2" 210 | [[ -n "$cmd_par_app" ]] && [[ "$cmd_par_app" != "--iso" ]] || exit_with_cmd_err "No Application name given for $1" 211 | shift 2 ;; 212 | -i | --iso ) cmd_par_iso="$2" 213 | [[ -n "$cmd_par_iso" ]] && [[ "$cmd_par_iso" != "--app" ]] || exit_with_cmd_err "No .iso name given for $1" 214 | shift 2 ;; 215 | -m | --method ) [[ -z "$2" ]] && exit_with_cmd_err "Method not specified for $1" 216 | cr_method="method${2}" 217 | shift 2 ;; 218 | -m* ) cr_method="method${1#-m}"; shift ;; 219 | --method* ) cr_method="method${1#--method}"; shift ;; 220 | -n | --nosudo ) allow_sudo='no'; shift ;; 221 | -v | --verify ) unset ver_opt; shift ;; 222 | -h | --h | --help ) print_help; exit 0 ;; 223 | -V | --version ) print_version; exit 0 ;; 224 | *) exit_with_cmd_err "Unknown option \"$1\"" 225 | esac 226 | done 227 | 228 | [[ "${cr_method-notset}" == "notset" ]] || [[ "$cr_method" =~ ^"method"[1-3]$ ]] || exit_with_cmd_err "Unknown creation method specified: ${cr_method#method}" 229 | 230 | check_intall_app() { 231 | [[ -n "$1" ]] || return 3 232 | [[ -d "$1" ]] || return 2 233 | [[ -e "$1/Contents/SharedSupport/InstallESD.dmg" ]] || return 1 234 | return 0 235 | } 236 | 237 | if [[ -z "$cmd_par_app" ]]; then 238 | stage_start "Looking for downloaded OS upgrades" 239 | unset test_name || exit_with_error 240 | IFS=$'\n' 241 | dirlist=(`find /Applications -maxdepth 1 -mindepth 1 \( -name 'Install OS X *.app' -or -name 'Install macOS *.app' \)`) || exit_with_error "Can't find downloaded macOS upgrade" 242 | IFS="$save_IFS" 243 | [[ ${#dirlist[@]} -eq 0 ]] && exit_with_error "Can't find downloaded macOS upgrade" 244 | stage_end_ok "found" 245 | if [[ ${#dirlist[@]} -gt 1 ]]; then 246 | echo "Several OS upgrades were found." 247 | echo "Which one OS upgrade do you want to use?" 248 | valid_answers=() 249 | unset test_name || exit_with_error 250 | for ((i=0;i<${#dirlist[@]};i++)); do 251 | test_name="${dirlist[$i]#/Applications/Install }" 252 | echo "$((i+1))) ${test_name%.app}" 253 | valid_answers[$i]="$((i+1))" 254 | done 255 | read -n 1 -p "[1-$i, q for quit]: " answer 256 | echo '' 257 | until is_answer_valid $answer ${valid_answers[@]} 'q'; do 258 | echo "'$answer' is incorrect response" 259 | read -n 1 -p "Select ""$(seq -s ', ' -t '\b\b' 1 $i)"" or q for quit: " answer 260 | echo '' 261 | done 262 | [[ "$answer" == "q" ]] && { echo_warning "Aborted."; exit 2; } 263 | OSX_inst_app="${dirlist[$((answer-1))]}" 264 | else 265 | OSX_inst_app="${dirlist[0]}" 266 | fi 267 | echo_enh "Using \"$OSX_inst_app\"." 268 | else 269 | stage_start "Checking for specified OS upgrade" 270 | unset OSX_inst_app || exit_with_error 271 | if check_intall_app "${cmd_par_app%/}"; then 272 | # direct location with path 273 | if [[ "${cmd_par_app:0:1}" == "/" ]]; then 274 | OSX_inst_app="${cmd_par_app%/}" # absolute path 275 | else 276 | OSX_inst_app="$(pwd)/${cmd_par_app%/}" # relative path 277 | test_name="$(cd "$OSX_inst_app/" 2>/dev/null && pwd)" || unset test_name || exit_with_error 278 | [[ -n "$test_name" ]] && OSX_inst_app="$test_name" # use absolute path if possible 279 | fi 280 | elif [[ "${cmd_par_app%%/*}" == "${cmd_par_app%/}" ]]; then 281 | # check /Applications 282 | test_name="${cmd_par_app%/}" 283 | test_name="${test_name%.app}.app" 284 | if check_intall_app "/Applications/${test_name}"; then 285 | OSX_inst_app="/Applications/${test_name}" 286 | elif check_intall_app "/Applications/Install ${test_name}"; then 287 | OSX_inst_app="/Applications/Install ${test_name}" 288 | elif check_intall_app "/Applications/Install OS X ${test_name}"; then 289 | OSX_inst_app="/Applications/Install OS X ${test_name}" 290 | elif check_intall_app "/Applications/Install macOS ${test_name}"; then 291 | OSX_inst_app="/Applications/Install macOS ${test_name}" 292 | fi 293 | fi 294 | [[ -n "$OSX_inst_app" ]] || exit_with_error "\"$cmd_par_app\" is not valid macOS Install application" 295 | stage_end_ok "found" 296 | echo_enh "Using \"$OSX_inst_app\"." 297 | fi 298 | 299 | stage_start "Detecting macOS name for installation" 300 | unset test_name OSX_inst_prt_name || exit_with_error 301 | test_name=$(sed -n -e '\|CFBundleDisplayName| { N; s|^.*\(.\{1,\}\).*$|\1|p; q; }' \ 302 | "$OSX_inst_app/Contents/Info.plist" 2>/dev/null) || unset test_name 303 | if [[ -n "$test_name" ]]; then 304 | OSX_inst_name="${test_name#Install }" 305 | OSX_inst_prt_name="Install $OSX_inst_name" 306 | stage_end_ok "$OSX_inst_name" 307 | else 308 | OSX_inst_name=$(echo "$OSX_inst_app"|sed -n -e's|^.*Install \(\(macOS|OS X\) .\{1,\}\)\.app.*$|\1|p' 2>/dev/null) || unset OSX_inst_name || exit_with_error 309 | [[ -z "$OSX_inst_name" ]] && OSX_inst_name="macOS" 310 | OSX_inst_prt_name="Install $OSX_inst_name" 311 | stage_end_warn "guessed \"$OSX_inst_name\"" 312 | fi 313 | 314 | stage_start "Creating temporary directory" 315 | tmp_dir="$(mktemp -d -t osx_iso_tmpdir)" || exit_with_error "Can't create tmp directory" 316 | # mkdir "tmp-tmp" 317 | # tmp_dir=$(cd tmp-tmp && pwd) || exit_with_error "Can't create tmp directory" 318 | stage_end_ok "succeed" 319 | 320 | stage_start_nl "Mounting InstallESD.dmg" 321 | OSX_inst_inst_dmg="$OSX_inst_app"'/Contents/SharedSupport/InstallESD.dmg' 322 | OSX_inst_inst_dmg_mnt="$tmp_dir/InstallESD_dmg_mnt" 323 | hdiutil attach "$OSX_inst_inst_dmg" -kernel -readonly -nobrowse ${ver_opt+-noverify} -mountpoint "$OSX_inst_inst_dmg_mnt" || exit_with_error "Can't mount installation image. Reboot recommended before retry." 324 | OSX_inst_base_dmg="$OSX_inst_inst_dmg_mnt/BaseSystem.dmg" || exit_with_error 325 | stage_end_ok "Mounting succeed" 326 | 327 | stage_start "Calculating required image size" 328 | unset OSX_inst_inst_dmg_used_size OSX_inst_base_dmg_real_size OSX_inst_base_dmg_size || exit_with_error "Can't unset variables" 329 | OSX_inst_inst_dmg_used_size=$(hdiutil imageinfo "$OSX_inst_inst_dmg" -plist | \ 330 | sed -En -e '\|Total Non-Empty Bytes| { N; s|^.*(.+).*$|\1|p; q; }') || unset OSX_inst_inst_dmg_used_size 331 | OSX_inst_base_dmg_real_size=$(hdiutil imageinfo "$OSX_inst_base_dmg" -plist | \ 332 | sed -En -e '\|Total Bytes| { N; s|^.*(.+).*$|\1|p; q; }') || unset OSX_inst_base_dmg_real_size 333 | OSX_inst_base_dmg_size=$(stat -f %z "$OSX_inst_base_dmg") || unset OSX_inst_base_dmg_size 334 | ((OSX_inst_base_dmg_size=(OSX_inst_base_dmg_size/512)*512)) # round to sector bound 335 | if !((OSX_inst_inst_dmg_used_size)) || !((OSX_inst_base_dmg_real_size)) || !((OSX_inst_base_dmg_size)); then 336 | ((OSX_inst_img_rw_size=10*1024*1024*1024)) 337 | stage_end_warn "Can't calculate, will use $OSX_inst_img_rw_size ($((OSX_inst_img_rw_size/(1024*1024))) MiB)" 338 | else 339 | ((OSX_inst_img_rw_size=OSX_inst_base_dmg_real_size+(OSX_inst_inst_dmg_used_size-OSX_inst_base_dmg_size) )) 340 | ((OSX_inst_img_rw_size+=OSX_inst_img_rw_size/10)) # add 10% for overhead, no need to be precise 341 | ((OSX_inst_img_rw_size=(OSX_inst_img_rw_size/512 + 1)*512)) # round to sector bound 342 | stage_end_ok "$OSX_inst_img_rw_size ($((OSX_inst_img_rw_size/(1024*1024))) MiB)" 343 | fi 344 | 345 | stage_start "Checking for available disk space" 346 | unset tmp_dir_free_space || exit_with_error 347 | tmp_dir_free_space="$(df -bi "$tmp_dir" | \ 348 | sed -nE -e 's|^.+[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+([0-9]+)[[:space:]]+[0-9]{1,3}%[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]{1,3}%[[:space:]]+/.*$|\1|p' )" || unset tmp_dir_free_space 349 | if [[ "${tmp_dir_free_space-notset}" == "notset" ]] || ( [[ -n "$tmp_dir_free_space" ]] && !((tmp_dir_free_space)) ); then 350 | tmp_dir_free_space='0' 351 | stage_end_warn "Can't determinate" 352 | else 353 | ((tmp_dir_free_space*=512)) 354 | if ((tmp_dir_free_space < OSX_inst_img_rw_size)); then 355 | stage_end_warn "$tmp_dir_free_space ($((tmp_dir_free_space/(1024*1024))) MiB), image creation may fail" 356 | else 357 | stage_end_ok "$tmp_dir_free_space ($((tmp_dir_free_space/(1024*1024))) MiB)" 358 | fi 359 | fi 360 | 361 | stage_start "Checking for super user rights" 362 | unset have_su_rights use_sudo sudo_prf || exit_with_error "Can't unset variables" 363 | if [[ `id -u` != '0' ]]; then 364 | have_su_rights='no' 365 | else 366 | have_su_rights='yes' 367 | fi 368 | if [[ "$have_su_rights" == "yes" ]] || [[ "$allow_sudo" != "yes" ]]; then 369 | use_sudo='no' 370 | sudo_prf='' 371 | else 372 | use_sudo='yes' 373 | sudo_prf='sudo' 374 | fi 375 | if [[ "$have_su_rights" == "yes" ]]; then 376 | stage_end_ok 'Owned' 377 | else 378 | stage_end_warn "Not owned" 379 | fi 380 | 381 | stage_start "Choosing creation method" 382 | if [[ -n "$cr_method" ]]; then 383 | stage_end_ok "Method ${cr_method#method}, specified on command line" 384 | if [[ "$cr_method" != "method3" ]] && [[ "$have_su_rights" != "yes" ]] && [[ "$allow_sudo" != "yes" ]]; then 385 | echo_warning "Resulting image probably will be unbootable as method ${cr_method#method} require super user rights and sudo was disabled by command line" 386 | fi 387 | elif [[ "$have_su_rights" != 'yes' ]]; then 388 | cr_method="method3" 389 | stage_end_ok "Method 3 as safest without super user right" 390 | elif ((tmp_dir_free_space < OSX_inst_img_rw_size*3)); then 391 | cr_method="method1" 392 | stage_end_ok "Method 1 due to limited disk space" 393 | else 394 | cr_method="method2" 395 | stage_end_ok "Method 2" 396 | fi 397 | 398 | unset img_bootable || exit_with_error 399 | if [[ "$cr_method" == "method1" ]] || [[ "$cr_method" == "method2" ]]; then 400 | if [[ "$cr_method" == "method1" ]]; then 401 | stage_start_nl "Converting BaseSystem.dmg to writable image" 402 | OSX_inst_img_rw="$tmp_dir/OS_X_Install.sparsebundle" 403 | hdiutil convert "$OSX_inst_base_dmg" -format UDSB -o "$OSX_inst_img_rw" -pmap || exit_with_error "Can't convert to writable image" 404 | stage_end_ok "Converting succeed" 405 | elif [[ "$cr_method" == "method2" ]]; then 406 | stage_start_nl "Creating installation image from BaseSystem.dmg" 407 | OSX_inst_img_dmg_tmp="$tmp_dir/OS_X_Install.dmg" || exit_with_error 408 | hdiutil create "${OSX_inst_img_dmg_tmp}" -srcdevice "$OSX_inst_base_dmg" -layout ISOCD || exit_with_error "Can't create writable image" 409 | stage_end_ok "Creating succeed" 410 | 411 | stage_start_nl "Converting installation image to writeable format" 412 | OSX_inst_img_rw="$tmp_dir/OS_X_Install.sparsebundle" 413 | hdiutil convert "$OSX_inst_img_dmg_tmp" -format UDSB -o "$OSX_inst_img_rw" -pmap || exit_with_error "Can't convert to writable image" 414 | rm -f "$OSX_inst_img_dmg_tmp" 415 | stage_end_ok "Converting succeed" 416 | fi 417 | 418 | stage_start "Resizing writable image" 419 | hdiutil resize -size "$OSX_inst_img_rw_size" "$OSX_inst_img_rw" -nofinalgap || exit_with_error "Can't resize writable image" 420 | stage_end_ok "Resizing succeed" 421 | 422 | stage_start_nl "Mounting writable image" 423 | OSX_inst_img_rw_mnt="$tmp_dir/OS_X_Install_img_rw_mnt" 424 | hdiutil attach "$OSX_inst_img_rw" -readwrite -nobrowse -mountpoint "$OSX_inst_img_rw_mnt" ${ver_opt+-noverify} || exit_with_error "Can't mount writable image" 425 | stage_end_ok "Mounting succeed" 426 | elif [[ "$cr_method" == "method3" ]]; then 427 | stage_start_nl "Creating blank writable image" 428 | OSX_inst_img_rw="$tmp_dir/OS_X_Install.sparsebundle" 429 | OSX_inst_img_rw_tmp_name="$OSX_inst_prt_name" || exit_with_error 430 | hdiutil create -size "$OSX_inst_img_rw_size" "$OSX_inst_img_rw" -type SPARSEBUNDLE -fs HFS+ -layout ISOCD -volname "$OSX_inst_img_rw_tmp_name" || exit_with_error "Can't create writable image" 431 | stage_end_ok "Creating succeed" 432 | 433 | stage_start_nl "Mounting writable image" 434 | OSX_inst_img_rw_mnt="$tmp_dir/OS_X_Install_img_rw_mnt" 435 | hdiutil attach "$OSX_inst_img_rw" -readwrite -nobrowse -mountpoint "$OSX_inst_img_rw_mnt" ${ver_opt+-noverify} || exit_with_error "Can't mount writable image" 436 | stage_end_ok "Mounting succeed" 437 | 438 | stage_start "Detecting mounted image device node" 439 | OSX_inst_img_rw_dev=`diskutil info -plist "$OSX_inst_img_rw_mnt" | sed -n -e '\|DeviceIdentifier| { N; s|^.*\(.\{1,\}\).*$|/dev/\1|p; q; }'` && \ 440 | [[ -n "$OSX_inst_img_rw_dev" ]] || exit_with_error "Can't find device node" 441 | stage_end_ok "$OSX_inst_img_rw_dev" 442 | 443 | stage_start_nl "Restoring BaseSystem.dmg to writable image" 444 | asr restore --source "$OSX_inst_base_dmg" --target "$OSX_inst_img_rw_dev" --erase --noprompt $ver_opt --buffers 1 --buffersize 64m || exit_with_error "Can't restore BaseSystem.dmg to writable image" 445 | unset OSX_inst_img_rw_mnt || exit_with_error # OSX_inst_img_rw_mnt is no valid anymore as image was remounted to different mountpoint 446 | img_bootable='yes' 447 | stage_end_ok "Restoring succeed" 448 | 449 | stage_start "Detecting re-mounted image volume name" 450 | unset OSX_inst_img_rw_volname || exit_with_error 451 | OSX_inst_img_rw_volname=`diskutil info -plist "$OSX_inst_img_rw_dev" | sed -n -e '\|VolumeName| { N; s|^.*\(.\{1,\}\).*$|\1|p; q; }'` || unset OSX_inst_img_rw_folname 452 | if [[ -z "$OSX_inst_img_rw_volname" ]]; then 453 | stage_end_warn "can't detect" 454 | else 455 | osascript -e "Tell application \"Finder\" to close the window \"$OSX_inst_img_rw_volname\"" &>/dev/null 456 | stage_end_ok "$OSX_inst_img_rw_volname" 457 | fi 458 | 459 | stage_start_nl "Remounting writable image to predefined mointpoint" 460 | hdiutil detach "$OSX_inst_img_rw_dev" -force || exit_with_error "Can't unmount image" 461 | unset OSX_inst_img_rw_dev 462 | OSX_inst_img_rw_mnt="$tmp_dir/OS_X_Install_img_rw_mnt" 463 | hdiutil attach "$OSX_inst_img_rw" -readwrite -nobrowse -mountpoint "$OSX_inst_img_rw_mnt" ${ver_opt+-noverify} || exit_with_error "Can't mount writable image" 464 | stage_end_ok "Remounting succeed" 465 | else 466 | exit_with_error "Unknown creation method" 467 | fi 468 | 469 | stage_start "Detecting macOS version on image" 470 | unset OSX_inst_ver || exit_with_error "Can't unset variable" 471 | OSX_inst_img_rw_ver_file="$OSX_inst_img_rw_mnt/System/Library/CoreServices/SystemVersion.plist" || exit_with_error "Can't set variable" 472 | OSX_inst_ver=`sed -n -e '\|ProductUserVisibleVersion| { N; s|^.*\(.\{1,\}\).*$|\1|p; q; }' "$OSX_inst_img_rw_ver_file"` || unset OSX_inst_ver 473 | if [[ -z "$OSX_inst_ver" ]]; then 474 | stage_end_warn "not detected" 475 | else 476 | stage_end_ok "$OSX_inst_ver" 477 | fi 478 | 479 | [[ "$OSX_inst_ver" =~ ^10.11($|.[1-4]$)|^10.12($|.[1-5]$) ]] || \ 480 | echo_warning "Warning! This script is tested only with images of macOS versions 10.11.0-10.11.4 and 10.12.0-10.12.5. Use with your own risk!" 481 | 482 | stage_start_nl "Renaming partition on writeable image" 483 | if ! diskutil rename "$OSX_inst_img_rw_mnt" "$OSX_inst_prt_name"; then 484 | stage_end_warn "Partition was not renamed" 485 | else 486 | unset OSX_inst_img_rw_volname 487 | stage_end_ok "Renamed to \"$OSX_inst_prt_name\"" 488 | fi 489 | 490 | stage_start "Copying BaseSystem.dmg to writeable image" 491 | #rsync -aIWEh --cache --progress "$OSX_inst_base_dmg" "$OSX_inst_img_rw_mnt/" || exit_with_error "Copying BaseSystem.dmg failed" 492 | cp -p "$OSX_inst_base_dmg" "$OSX_inst_img_rw_mnt/" || exit_with_error "Copying BaseSystem.dmg failed" 493 | cp -p "${OSX_inst_base_dmg%.dmg}.chunklist" "$OSX_inst_img_rw_mnt/" || exit_with_error "Copying BaseSystem.chunklist failed" 494 | stage_end_ok 495 | 496 | stage_start "Replacing Packages symlink with real files" 497 | rm -f "$OSX_inst_img_rw_mnt/System/Installation/Packages" || exit_with_error "Deleting Packages symlink failed" 498 | cp -pPR "$OSX_inst_inst_dmg_mnt/Packages" "$OSX_inst_img_rw_mnt/System/Installation/" || exit_with_error "Copying Packages failed" 499 | stage_end_ok 500 | 501 | stage_start "Configuring image as bootable" 502 | OSX_inst_img_rw_CoreSrv="$OSX_inst_img_rw_mnt/System/Library/CoreServices" || exit_with_error 503 | if bless --folder "$OSX_inst_img_rw_CoreSrv" \ 504 | --file "$OSX_inst_img_rw_CoreSrv/boot.efi" --openfolder "$OSX_inst_img_rw_mnt" --label "Install $OSX_inst_name"; then 505 | stage_end_ok 506 | else 507 | stage_end_warn "Failed, image may not be bootable" 508 | fi 509 | 510 | stage_start_nl "Unmounting InstallESD.dmg" 511 | hdiutil detach "$OSX_inst_inst_dmg_mnt" -force || exit_with_error "Can't unmount InstallESD.dmg" 512 | unset OSX_inst_img_rw_dev 513 | stage_end_ok "Unmointing succeed" 514 | 515 | stage_start_nl "Unmounting writable images" 516 | hdiutil detach "$OSX_inst_img_rw_mnt" -force || exit_with_error "Can't unmount writable image" 517 | unset OSX_inst_img_rw_dev 518 | stage_end_ok "Unmointing succeed" 519 | 520 | insert_version_into_name() { 521 | local name="$1" 522 | local version="$2" 523 | [[ -z "$name" ]] && return 1 524 | [[ -z "$version" ]] && { echo "$name"; return 0; } 525 | local result 526 | local ins_aft 527 | if [[ "$name" =~ (^|[[:space:]])"OS X"($|[[:space:]]) ]]; then 528 | ins_aft="OS X" 529 | elif [[ "$name" =~ (^|[[:space:]])"MacOS X"($|[[:space:]]) ]]; then 530 | ins_aft="MacOS X" 531 | elif [[ "$name" =~ (^|[[:space:]])"macOS"($|[[:space:]]) ]]; then 532 | ins_aft="macOS" 533 | fi 534 | if [[ -n "$ins_aft" ]]; then 535 | result=$(echo -n "$name" | sed -n -e 's|^\(.*[[:<:]]'"$ins_aft"'[[:>:]]\).*$|\1|p') || return 2 536 | [[ -z "$result" ]] && return 2 537 | result+=" $version" # allow any regex/special symbols in $version 538 | result+=$(echo -n "$name" | sed -n -e 's|^.*[[:<:]]'"$ins_aft"'[[:>:]]\(.*\)$|\1|p') || return 2 539 | else 540 | result="$name (macOS $version)" 541 | fi 542 | [[ -z "$result" ]] && return 1 543 | echo "$result" 544 | return 0 545 | } 546 | 547 | stage_start "Checking for output directory and image name" 548 | unset iso_name out_dir test_name || exit_with_error 549 | if [[ -z "$cmd_par_iso" ]]; then 550 | iso_name="$(insert_version_into_name "$OSX_inst_name" "$OSX_inst_ver")" || exit_with_error "Script internal error" 551 | iso_name="Install_${iso_name// /_}.iso" 552 | if [[ -z "$work_dir" ]] || [[ ! -w "$work_dir/" ]]; then 553 | [[ -n "$HOME" ]] && out_dir="$HOME/Desktop" # use Desktop as fallback 554 | if [[ -z "$out_dir" ]] || [[ ! -w "$out_dir/" ]]; then 555 | # use script location directory as fallback 556 | script_path="$(dirname "${BASH_SOURCE[0]}" 2>/dev/null)" 557 | [[ -n "$script_path" ]] || script_path="${0%/*}" 558 | [[ -n "$script_path" ]] && out_dir="$(cd "$script_path"2 2>/dev/null && pwd)" 559 | fi 560 | [[ -n "$out_dir" ]] && [[ -w "$out_dir/" ]] || out_dir="${0%/*}" 561 | [[ -n "$out_dir" ]] && [[ -w "$out_dir/" ]] || exit_with_error "Can't find writable output directory" 562 | stage_end_warn "Directory \"$work_dir\" seems to be unwritable, \"$out_dir/$iso_name\" will be used for output" 563 | else 564 | out_dir="$work_dir" 565 | stage_end_ok "$work_dir/$iso_name" 566 | fi 567 | else 568 | test_name="${cmd_par_iso}" 569 | [[ "${test_name:0:1}" == "/" ]] || test_name="$work_dir/$test_name" 570 | if [[ -d "$test_name" ]] || [[ "${test_name%/}" != "${test_name}" ]]; then 571 | # cmd_par_iso is output directory without filename 572 | out_dir="${cmd_par_iso%/}" 573 | else 574 | iso_name="${cmd_par_iso##*/}" 575 | if [[ "$iso_name" == "$cmd_par_iso" ]]; then 576 | out_dir="$work_dir" 577 | else 578 | out_dir="${cmd_par_iso%/*}" 579 | fi 580 | fi 581 | 582 | if [[ -z "$iso_name" ]]; then 583 | iso_name="$(insert_version_into_name "$OSX_inst_name" "$OSX_inst_ver")" || exit_with_error "Script internal error" 584 | iso_name="Install_${OSX_inst_name// /_}.iso" 585 | fi 586 | iso_name="${iso_name%.iso}.iso" 587 | 588 | [[ "${out_dir:0:1}" == "/" ]] || [[ -z "$out_dir" ]] || out_dir="$work_dir/${out_dir}" # relative path 589 | [[ -d "$out_dir/" ]] || mkdir "$out_dir/" || exit_with_error "Can't create specified output directory." 590 | unset test_name || exit_with_error 591 | test_name="$(cd "$out_dir/" 2>/dev/null && pwd)" 592 | [[ -n "$test_name" ]] && out_dir="$test_name" # replace with absolute path if possible 593 | stage_end_ok "specified on command line: \"$out_dir/$iso_name\"" 594 | fi 595 | 596 | stage_start_nl "Converting writeable image to .iso" 597 | unset iso_created || exit_with_error 598 | OSX_inst_result_image_ro="$out_dir/$iso_name" || exit_with_error 599 | OSX_inst_result_flag="$tmp_dir/output_image_is_ready" || exit_with_error 600 | rm -f "$OSX_inst_result_flag" || exit_with_error 601 | [[ -e "$OSX_inst_result_image_ro" ]] && exit_with_error "\"$OSX_inst_result_image_ro\" already exist" 602 | makehybrid_errout="$tmp_dir/hdiutil_makehybrid_erroutput" || exit_with_error 603 | { { hdiutil makehybrid -o "$OSX_inst_result_image_ro" "$OSX_inst_img_rw" -hfs -udf -default-volume-name "$OSX_inst_prt_name" 2>&1 1>&3 && \ 604 | touch "$OSX_inst_result_flag"; } | tee "$makehybrid_errout"; } 3>&1 1>&2 # output stderr to stderr and save it to file at the same time 605 | if ! [[ -e "$OSX_inst_result_flag" ]]; then 606 | if fgrep -Fiqs -e 'Operation not permitted' "$makehybrid_errout" && [[ "$have_su_rights" != "yes" ]]; then 607 | echo_warning "Creation of optimal .iso image failed without super user rights." 608 | if [[ "$allow_sudo" == "yes" ]]; then 609 | rm -f "$OSX_inst_result_image_ro" 610 | echo_warning "Next command will be executed with sudo, you may be asked for password." 611 | $sudo_prf hdiutil makehybrid -o "$OSX_inst_result_image_ro" "$OSX_inst_img_rw" -hfs -udf -default-volume-name "$OSX_inst_prt_name" && touch "$OSX_inst_result_flag" 612 | else 613 | echo_warning "Usage of sudo was disabled by command parameter" 614 | fi 615 | fi 616 | fi 617 | if [[ -e "$OSX_inst_result_flag" ]]; then 618 | img_bootable='yes' 619 | stage_end_ok "Converting succeed" 620 | else 621 | rm -f "$OSX_inst_result_image_ro" 622 | stage_end_warn "Creation of optimal .iso was failed, will try to use workarounds to build usable .iso" 623 | [[ "$img_bootable" != 'yes' ]] && echo_warning "Resulting image may not be bootable" 624 | 625 | stage_start "Shrinking image" 626 | if hdiutil resize -sectors min "$OSX_inst_img_rw" -nofinalgap; then 627 | stage_end_ok "succeed" 628 | else 629 | stage_end_warn "failed, image remains larger than required" 630 | fi 631 | 632 | stage_start_nl "Converting image to .iso-like format" 633 | OSX_inst_result_tmp_image="${OSX_inst_result_image_ro%.iso}.cdr" || exit_with_error 634 | [[ -e "$OSX_inst_result_tmp_image" ]] && OSX_inst_result_tmp_image="$tmp_dir/tmp_cdr_img.cdr" 635 | hdiutil convert "$OSX_inst_img_rw" -format UDTO -o "$OSX_inst_result_tmp_image" && \ 636 | mv -vn "$OSX_inst_result_tmp_image" "$OSX_inst_result_image_ro" && iso_created='yes' 637 | if [[ "$iso_created" != "yes" ]]; then 638 | rm -f "$OSX_inst_result_tmp_image" 639 | rm -f "$OSX_inst_result_image_ro" 640 | exit_with_error "Image converting failed" 641 | fi 642 | stage_end_ok "Converting succeed" 643 | fi 644 | 645 | echo_enh " 646 | Resulting .iso location:" 647 | echo "$OSX_inst_result_image_ro 648 | " 649 | [[ "$img_bootable" != 'yes' ]] && echo_warning "Resulting .iso may not be bootable" 650 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Repository k2k-OSX-Tools 2 | Contains Karlson2k's various tools for Mac OS X operating system. 3 | --------------------------------------------------------------------------------