1#!/bin/bash -p
2
3# Copyright (c) 2010 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7# Called by the application to install in a new location.  Generally, this
8# means that the application is running from a disk image and wants to be
9# copied to /Applications.  The application, when running from the disk image,
10# will call this script to perform the copy.
11#
12# This script will be run as root if the application determines that it would
13# not otherwise have permission to perform the copy.
14#
15# When running as root, this script will be invoked with the real user ID set
16# to the user's ID, but the effective user ID set to 0 (root).  bash -p is
17# used on the first line to prevent bash from setting the effective user ID to
18# the real user ID (dropping root privileges).
19
20set -e
21
22# This script may run as root, so be paranoid about things like ${PATH}.
23export PATH="/usr/bin:/usr/sbin:/bin:/sbin"
24
25# If running as root, output the pid to stdout before doing anything else.
26# See chrome/browser/cocoa/authorization_util.h.
27if [ ${EUID} -eq 0 ] ; then
28  echo "${$}"
29fi
30
31if [ ${#} -ne 2 ] ; then
32  echo "usage: ${0} SRC DEST" >& 2
33  exit 2
34fi
35
36SRC=${1}
37DEST=${2}
38
39# Make sure that SRC is an absolute path and that it exists.
40if [ -z "${SRC}" ] || [ "${SRC:0:1}" != "/" ] || [ ! -d "${SRC}" ] ; then
41  echo "${0}: source ${SRC} sanity check failed" >& 2
42  exit 3
43fi
44
45# Make sure that DEST is an absolute path and that it doesn't yet exist.
46if [ -z "${DEST}" ] || [ "${DEST:0:1}" != "/" ] || [ -e "${DEST}" ] ; then
47  echo "${0}: destination ${DEST} sanity check failed" >& 2
48  exit 4
49fi
50
51# Do the copy.
52rsync -lrpt "${SRC}/" "${DEST}"
53
54# The remaining steps are not considered critical.
55set +e
56
57# Notify LaunchServices.
58/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister "${DEST}"
59
60# If this script is not running as root and the application is installed
61# somewhere under /Applications, try to make it writable by all admin users.
62# This will allow other admin users to update the application from their own
63# user Keystone instances even if the Keystone ticket is not promoted to
64# system level.
65#
66# If the script is not running as root and the application is not installed
67# under /Applications, it might not be in a system-wide location, and it
68# probably won't be something that other users on the system are running, so
69# err on the side of safety and don't make it group-writable.
70#
71# If this script is running as root, a Keystone ticket promotion is expected,
72# and future updates can be expected to be applied as root, so
73# admin-writeability is not a concern.  Set the entire thing to be owned by
74# root in that case, regardless of where it's installed, and drop any group
75# and other write permission.
76#
77# If this script is running as a user that is not a member of the admin group,
78# the chgrp operation will not succeed.  Tolerate that case, because it's
79# better than the alternative, which is to make the application
80# world-writable.
81CHMOD_MODE="a+rX,u+w,go-w"
82if [ ${EUID} -ne 0 ] ; then
83  if [ "${DEST:0:14}" = "/Applications/" ] &&
84     chgrp -Rh admin "${DEST}" >& /dev/null ; then
85    CHMOD_MODE="a+rX,ug+w,o-w"
86  fi
87else
88  chown -Rh root:wheel "${DEST}" >& /dev/null
89fi
90
91chmod -R "${CHMOD_MODE}" "${DEST}" >& /dev/null
92
93# On the Mac, or at least on HFS+, symbolic link permissions are significant,
94# but chmod -R and -h can't be used together.  Do another pass to fix the
95# permissions on any symbolic links.
96find "${DEST}" -type l -exec chmod -h "${CHMOD_MODE}" {} + >& /dev/null
97
98# Host OS version check, to be able to take advantage of features on newer
99# systems and fall back to slow ways of doing things on older systems.
100OS_VERSION=$(sw_vers -productVersion)
101OS_MAJOR=$(sed -Ene 's/^([0-9]+).*/\1/p' <<< ${OS_VERSION})
102OS_MINOR=$(sed -Ene 's/^([0-9]+)\.([0-9]+).*/\2/p' <<< ${OS_VERSION})
103
104# Because this script is launched by the application itself, the installation
105# process inherits the quarantine bit (LSFileQuarantineEnabled).  Any files or
106# directories created during the update will be quarantined in that case,
107# which may cause Launch Services to display quarantine UI.  That's bad,
108# especially if it happens when the outer .app launches a quarantined inner
109# helper.  Since the user approved the application launch if quarantined, it
110# it can be assumed that the installed copy should not be quarantined.  Use
111# xattr to drop the quarantine attribute.
112QUARANTINE_ATTR=com.apple.quarantine
113if [ ${OS_MAJOR} -gt 10 ] ||
114   ([ ${OS_MAJOR} -eq 10 ] && [ ${OS_MINOR} -ge 6 ]) ; then
115  # On 10.6, xattr supports -r for recursive operation.
116  xattr -d -r "${QUARANTINE_ATTR}" "${DEST}" >& /dev/null
117else
118  # On earlier systems, xattr doesn't support -r, so run xattr via find.
119  find "${DEST}" -exec xattr -d "${QUARANTINE_ATTR}" {} + >& /dev/null
120fi
121
122# Great success!
123exit 0
124