15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/bin/bash -p
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# usage: dmgdiffer.sh product_name old_dmg new_dmg patch_dmg
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# dmgdiffer creates a disk image containing a binary update able to patch
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# a product originally distributed in old_dmg to the version in new_dmg. Much
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# of this script is generic, but the make_patch_fs function is specific to
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# a product: in this case, Google Chrome.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This script operates by mounting old_dmg and new_dmg, creating a new
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# filesystem structure containing dirpatches generated by dirdiffer and
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# goobsdiff (which should be located in the same directory as this script),
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# and producing a disk image from that structure.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The Chrome make_patch_fs function produces an disk image that is able to
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# update a single old version on any Keystone channel to a new version on a
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# specific Keystone channel (the Keystone channel associated with new_dmg).
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Chrome's updates are split into two dirpatches: one updates the old
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# versioned directory to the new one, and the other updates the remainder of
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the application. The versioned directory is split out from the rest because
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# it contains the bulk of the application and its name changes from version to
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# version, and dirdiffer/dirpatcher do not directly handle name changes. This
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# approach also allows the versioned directory dirpatch to be applied in-place
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# in most cases during an update, rather than relying on a temporary
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# directory. In order to allow a single update dmg to apply to an old version
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# on any Keystone channel, several small files are never distributed as diffs,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# and only as full (possibly compressed) versions of the new files. These
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# files include the outer application's Info.plist which contains Keystone
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# channel information, and anything created or modified by code-signing the
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# outer application.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Application of update disk images produced by this script is
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# product-specific. With updates managed by Keystone, the update disk images
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# can contain a .keystone_install script that is able to locate and update
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the installed product.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Exit codes:
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  0  OK
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  1  Unknown failure
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  2  Incorrect number of parameters
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  3  Input disk images do not exist
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  4  Output disk image already exists
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  5  Parent of output directory does not exist or is not a directory
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  6  Could not mount old_dmg
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  7  Could not mount new_dmg
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  8  Could not create temporary patch filesystem directory
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  9  Could not create disk image
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 10  Could not read old application data
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 11  Could not read new application data
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 12  Old or new application sanity check failure
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 13  Could not write the patch
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Exit codes in the range 21-40 are mapped to codes 1-20 as returned by the
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# first dirdiffer invocation. Codes 41-60 are mapped to codes 1-20 as returned
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# by the second.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set -eu
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Environment sanitization. Set a known-safe PATH. Clear environment variables
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# that might impact the interpreter's operation. The |bash -p| invocation
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# on the #! line takes the bite out of BASH_ENV, ENV, and SHELLOPTS (among
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# other features), but clearing them here ensures that they won't impact any
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# shell scripts used as utility programs. SHELLOPTS is read-only and can't be
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# unset, only unexported.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)export PATH="/usr/bin:/bin:/usr/sbin:/sbin"
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)unset BASH_ENV CDPATH ENV GLOBIGNORE IFS POSIXLY_CORRECT
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)export -n SHELLOPTS
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ME="$(basename "${0}")"
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly ME
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SCRIPT_DIR="$(dirname "${0}")"
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly SCRIPT_DIR
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly DIRDIFFER="${SCRIPT_DIR}/dirdiffer.sh"
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly PKG_DMG="${SCRIPT_DIR}/pkg-dmg"
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)err() {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local error="${1}"
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "${ME}: ${error}" >& 2
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)declare -a g_cleanup g_cleanup_mount_points
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cleanup() {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local status=${?}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap - EXIT
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap '' HUP INT QUIT TERM
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${status} -ge 128 ]]; then
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "Caught signal $((${status} - 128))"
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${#g_cleanup_mount_points[@]}" -gt 0 ]]; then
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local mount_point
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for mount_point in "${g_cleanup_mount_points[@]}"; do
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hdiutil detach "${mount_point}" -force >& /dev/null || true
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    done
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${#g_cleanup[@]}" -gt 0 ]]; then
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -rf "${g_cleanup[@]}"
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit ${status}
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)mount_dmg() {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local dmg="${1}"
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local mount_point="${2}"
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! hdiutil attach "${1}" -mountpoint "${2}" \
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             -nobrowse -owners off > /dev/null; then
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # set -e is in effect. return ${?} so that the caller can check the return
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # code if desired, perhaps to print a more useful error message or to exit
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # with a more precise status than would be possible here.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ${?}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# make_patch_fs is responsible for comparing the old and new disk images
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# mounted at old_fs and new_fs, respectively, and populating patch_fs with the
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# contents of what will become a disk image able to update old_fs to new_fs.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# It then outputs a string which will be used as the volume name of the
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# patch_dmg.
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The entire patch contents are placed into a .patch directory to hide them
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# from ordinary view. The disk image will be given a volume name like
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# "Google Chrome 5.0.375.55-5.0.375.70" as an identifying aid, although
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# uniqueness is not important and users will never interact directly with
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# them.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)make_patch_fs() {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local product_name="${1}"
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_fs="${2}"
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_fs="${3}"
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_fs="${4}"
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_NAME="${product_name}.app"
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_NAME_RE="${product_name}\\.app"
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_PLIST="Contents/Info"
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_VERSION_KEY="CFBundleShortVersionString"
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_BUNDLEID_KEY="CFBundleIdentifier"
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_VERSION_KEY="KSVersion"
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_PRODUCT_KEY="KSProductID"
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_CHANNEL_KEY="KSChannelID"
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly VERSIONS_DIR="Contents/Versions"
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly BUILD_RE="^[0-9]+\\.[0-9]+\\.([0-9]+)\\.[0-9]+\$"
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly MIN_BUILD=434
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local product_url="http://www.google.com/chrome/"
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${product_name}" = "Google Chrome Canary" ]]; then
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    product_url="http://tools.google.com/dlpage/chromesxs"
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_app_path="${old_fs}/${APP_NAME}"
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_app_plist="${old_app_path}/${APP_PLIST}"
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_app_version
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! old_app_version="$(defaults read "${old_app_plist}" \
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        "${APP_VERSION_KEY}")"; then
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not read old app version"
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 10
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! [[ "${old_app_version}" =~ ${BUILD_RE} ]]; then
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "old app version not of expected format"
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 10
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_app_version_build="${BASH_REMATCH[1]}"
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_app_bundleid
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! old_app_bundleid="$(defaults read "${old_app_plist}" \
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         "${APP_BUNDLEID_KEY}")"; then
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not read old app bundle ID"
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 10
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_ks_plist="${old_app_plist}"
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_ks_version
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! old_ks_version="$(defaults read "${old_ks_plist}" \
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       "${KS_VERSION_KEY}")"; then
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not read old Keystone version"
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 10
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_app_path="${new_fs}/${APP_NAME}"
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_app_plist="${new_app_path}/${APP_PLIST}"
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_app_version
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! new_app_version="$(defaults read "${new_app_plist}" \
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      "${APP_VERSION_KEY}")"; then
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not read new app version"
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 11
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! [[ "${new_app_version}" =~ ${BUILD_RE} ]]; then
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "new app version not of expected format"
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 11
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_app_version_build="${BASH_REMATCH[1]}"
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_ks_plist="${new_app_plist}"
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_ks_version
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! new_ks_version="$(defaults read "${new_ks_plist}" \
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       "${KS_VERSION_KEY}")"; then
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not read new Keystone version"
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 11
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_ks_product
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! new_ks_product="$(defaults read "${new_app_plist}" \
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       "${KS_PRODUCT_KEY}")"; then
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not read new Keystone product ID"
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 11
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${old_app_version_build} -lt ${MIN_BUILD} ]] ||
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [[ ${new_app_version_build} -lt ${MIN_BUILD} ]]; then
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "old and new versions must be build ${MIN_BUILD} or newer"
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 12
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_ks_channel
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_ks_channel="$(defaults read "${new_app_plist}" \
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    "${KS_CHANNEL_KEY}" 2> /dev/null || true)"
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local name_extra
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${new_ks_channel}" = "beta" ]]; then
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name_extra=" Beta"
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ "${new_ks_channel}" = "dev" ]]; then
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name_extra=" Dev"
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ "${new_ks_channel}" = "canary" ]]; then
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name_extra=
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ -n "${new_ks_channel}" ]]; then
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name_extra=" ${new_ks_channel}"
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_versioned_dir="${old_app_path}/${VERSIONS_DIR}/${old_app_version}"
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_versioned_dir="${new_app_path}/${VERSIONS_DIR}/${new_app_version}"
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! cp -p "${SCRIPT_DIR}/keystone_install.sh" \
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "${patch_fs}/.keystone_install"; then
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not copy .keystone_install"
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 13
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_keychain_reauthorize_dir="${patch_fs}/.keychain_reauthorize"
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! mkdir "${patch_keychain_reauthorize_dir}"; then
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not mkdir patch_keychain_reauthorize_dir"
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 13
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! cp -p "${SCRIPT_DIR}/.keychain_reauthorize/${old_app_bundleid}" \
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "${patch_keychain_reauthorize_dir}/${old_app_bundleid}"; then
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not copy keychain_reauthorize"
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 13
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_dotpatch_dir="${patch_fs}/.patch"
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! mkdir "${patch_dotpatch_dir}"; then
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not mkdir patch_dotpatch_dir"
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 13
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! cp -p "${SCRIPT_DIR}/dirpatcher.sh" \
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "${SCRIPT_DIR}/goobspatch" \
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "${SCRIPT_DIR}/liblzma_decompress.dylib" \
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "${SCRIPT_DIR}/xzdec" \
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "${patch_dotpatch_dir}/"; then
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not copy patching tools"
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 13
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! echo "${new_ks_product}" > "${patch_dotpatch_dir}/ks_product" ||
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ! echo "${old_app_version}" > "${patch_dotpatch_dir}/old_app_version" ||
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ! echo "${new_app_version}" > "${patch_dotpatch_dir}/new_app_version" ||
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ! echo "${old_ks_version}" > "${patch_dotpatch_dir}/old_ks_version" ||
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ! echo "${new_ks_version}" > "${patch_dotpatch_dir}/new_ks_version"; then
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not write patch product or version information"
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 13
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_ks_channel_file="${patch_dotpatch_dir}/ks_channel"
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${new_ks_channel}" ]]; then
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! echo "${new_ks_channel}" > "${patch_ks_channel_file}"; then
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "could not write Keystone channel information"
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 13
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! touch "${patch_ks_channel_file}"; then
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "could not write empty Keystone channel information"
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 13
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The only visible contents of the disk image will be a README file that
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # explains the image's purpose.
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_app_version_extra="${new_app_version}${name_extra}"
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cat > "${patch_fs}/README.txt" << __EOF__ || \
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (err "could not write README.txt" && exit 13)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This disk image contains a differential updater that can update
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)${product_name} from version ${old_app_version} to ${new_app_version_extra}.
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This image is part of the auto-update system and is not independently
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)useful.
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)To install ${product_name}, please visit
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<${product_url}>.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)__EOF__
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_versioned_dir="\
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)${patch_dotpatch_dir}/version_${old_app_version}_${new_app_version}.dirpatch"
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! "${DIRDIFFER}" "${old_versioned_dir}" \
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      "${new_versioned_dir}" \
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      "${patch_versioned_dir}"; then
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local status=${?}
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not create a dirpatch for the versioned directory"
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit $((${status} + 20))
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Set DIRDIFFER_EXCLUDE to exclude the contents of the Versions directory,
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # but to include an empty Versions directory. The versioned directory was
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # already addressed in the preceding dirpatch.
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  export DIRDIFFER_EXCLUDE="/${APP_NAME_RE}/Contents/Versions/"
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Set DIRDIFFER_NO_DIFF to exclude files introduced by or modified by
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Keystone channel and brand tagging and subsequent code signing.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  export DIRDIFFER_NO_DIFF="\
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/${APP_NAME_RE}/Contents/\
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)(CodeResources|Info\\.plist|MacOS/${product_name}|_CodeSignature/.*)$"
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_app_dir="${patch_dotpatch_dir}/application.dirpatch"
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! "${DIRDIFFER}" "${old_app_path}" \
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      "${new_app_path}" \
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      "${patch_app_dir}"; then
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local status=${?}
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not create a dirpatch for the application directory"
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit $((${status} + 40))
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unset DIRDIFFER_EXCLUDE DIRDIFFER_NO_DIFF
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "${product_name} ${old_app_version}-${new_app_version_extra} Update"
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# package_patch_dmg creates a disk image at patch_dmg with the contents of
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# patch_fs. The disk image's volume name is taken from volume_name. temp_dir
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# is a work directory such as /tmp for the packager's use.
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)package_patch_dmg() {
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_fs="${1}"
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_dmg="${2}"
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local volume_name="${3}"
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local temp_dir="${4}"
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Because most of the contents of ${patch_fs} are already compressed, the
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # overall compression on the disk image is mostly used to minimize the sizes
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # of the filesystem structures. In the presence of so much
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # already-compressed data, zlib performs better than bzip2, so use UDZO.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! "${PKG_DMG}" \
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           --verbosity 0 \
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           --source "${patch_fs}" \
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           --target "${patch_dmg}" \
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           --tempdir "${temp_dir}" \
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           --format UDZO \
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           --volname "${volume_name}" \
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           --config "openfolder_bless=0"; then
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "disk image creation failed"
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 9
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# make_patch_dmg mounts old_dmg and new_dmg, invokes make_patch_fs to prepare
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# a patch filesystem, and then hands the patch filesystem to package_patch_dmg
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# to create patch_dmg.
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)make_patch_dmg() {
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local product_name="${1}"
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_dmg="${2}"
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_dmg="${3}"
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_dmg="${4}"
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local temp_dir
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  temp_dir="$(mktemp -d -t "${ME}")"
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_cleanup+=("${temp_dir}")
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_mount_point="${temp_dir}/old"
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_cleanup_mount_points+=("${old_mount_point}")
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! mount_dmg "${old_dmg}" "${old_mount_point}"; then
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not mount old_dmg ${old_dmg}"
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 6
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_mount_point="${temp_dir}/new"
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_cleanup_mount_points+=("${new_mount_point}")
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! mount_dmg "${new_dmg}" "${new_mount_point}"; then
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not mount new_dmg ${new_dmg}"
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 7
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_fs="${temp_dir}/patch"
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! mkdir "${patch_fs}"; then
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "could not mkdir patch_fs ${patch_fs}"
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 8
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local volume_name
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  volume_name="$(make_patch_fs "${product_name}" \
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               "${old_mount_point}" \
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               "${new_mount_point}" \
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               "${patch_fs}")"
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hdiutil detach "${new_mount_point}" > /dev/null
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unset g_cleanup_mount_points[${#g_cleanup_mount_points[@]}]
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hdiutil detach "${old_mount_point}" > /dev/null
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unset g_cleanup_mount_points[${#g_cleanup_mount_points[@]}]
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  package_patch_dmg "${patch_fs}" "${patch_dmg}" "${volume_name}" "${temp_dir}"
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rm -rf "${temp_dir}"
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unset g_cleanup[${#g_cleanup[@]}]
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# shell_safe_path ensures that |path| is safe to pass to tools as a
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# command-line argument. If the first character in |path| is "-", "./" is
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# prepended to it. The possibly-modified |path| is output.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)shell_safe_path() {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local path="${1}"
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${path:0:1}" = "-" ]]; then
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo "./${path}"
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    echo "${path}"
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)usage() {
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "usage: ${ME} product_name old_dmg new_dmg patch_dmg" >& 2
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)main() {
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local product_name old_dmg new_dmg patch_dmg
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  product_name="${1}"
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  old_dmg="$(shell_safe_path "${2}")"
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_dmg="$(shell_safe_path "${3}")"
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  patch_dmg="$(shell_safe_path "${4}")"
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap cleanup EXIT HUP INT QUIT TERM
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! [[ -f "${old_dmg}" ]] || ! [[ -f "${new_dmg}" ]]; then
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "old_dmg and new_dmg must exist and be files"
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 3
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -e "${patch_dmg}" ]]; then
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "patch_dmg must not exist"
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 4
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_dmg_parent
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  patch_dmg_parent="$(dirname "${patch_dmg}")"
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! [[ -d "${patch_dmg_parent}" ]]; then
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "patch_dmg parent directory must exist and be a directory"
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 5
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  make_patch_dmg "${product_name}" "${old_dmg}" "${new_dmg}" "${patch_dmg}"
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap - EXIT
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if [[ ${#} -ne 4 ]]; then
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  usage
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 2
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)fi
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)main "${@}"
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)exit ${?}
480