OLD | NEW |
1 #!/bin/bash | 1 #!/bin/bash |
2 | 2 |
3 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2009 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 # Called by the Keystone system to update the installed application with a new | 7 # Called by the Keystone system to update the installed application with a new |
8 # version from a disk image. | 8 # version from a disk image. |
9 | 9 |
10 # Return values: | 10 # Return values: |
11 # 0 Happiness | 11 # 0 Happiness |
12 # 1 Unknown failure | 12 # 1 Unknown failure |
13 # 2 Basic sanity check destination failure (e.g. ticket points to nothing) | 13 # 2 Basic sanity check destination failure (e.g. ticket points to nothing) |
14 # 3 Could not prepare existing installed version to receive update | 14 # 3 Could not prepare existing installed version to receive update |
15 # 4 rsync failed (could not assure presence of Versions directory) | 15 # 4 rsync failed (could not assure presence of Versions directory) |
16 # 5 rsync failed (could not copy new versioned directory to Versions) | 16 # 5 rsync failed (could not copy new versioned directory to Versions) |
17 # 6 rsync failed (could not update outer .app bundle) | 17 # 6 rsync failed (could not update outer .app bundle) |
18 # 7 Could not get the version, update URL, or channel after update | 18 # 7 Could not get the version, update URL, or channel after update |
19 # 8 Updated application does not have the version number from the update | 19 # 8 Updated application does not have the version number from the update |
20 # 9 ksadmin failure | 20 # 9 ksadmin failure |
21 # 10 Basic sanity check source failure (e.g. no app on disk image) | 21 # 10 Basic sanity check source failure (e.g. no app on disk image) |
22 | 22 |
23 set -e | 23 set -e |
24 | 24 |
| 25 # Returns 0 (true) if the parameter exists, is a symbolic link, and appears |
| 26 # writeable on the basis of its POSIX permissions. This is used to determine |
| 27 # writeability like test's -w primary, but -w resolves symbolic links and this |
| 28 # function does not. |
| 29 function is_writeable_symlink() { |
| 30 SYMLINK=${1} |
| 31 LINKMODE=$(stat -f %Sp "${SYMLINK}" 2> /dev/null || true) |
| 32 if [ -z "${LINKMODE}" ] || [ "${LINKMODE:0:1}" != "l" ] ; then |
| 33 return 1 |
| 34 fi |
| 35 LINKUSER=$(stat -f %u "${SYMLINK}" 2> /dev/null || true) |
| 36 LINKGROUP=$(stat -f %g "${SYMLINK}" 2> /dev/null || true) |
| 37 if [ -z "${LINKUSER}" ] || [ -z "${LINKGROUP}" ] ; then |
| 38 return 1 |
| 39 fi |
| 40 |
| 41 # If the users match, check the owner-write bit. |
| 42 if [ ${EUID} -eq ${LINKUSER} ] ; then |
| 43 if [ "${LINKMODE:2:1}" = "w" ] ; then |
| 44 return 0 |
| 45 fi |
| 46 return 1 |
| 47 fi |
| 48 |
| 49 # If the file's group matches any of the groups that this process is a |
| 50 # member of, check the group-write bit. |
| 51 GROUPMATCH= |
| 52 for group in ${GROUPS[@]} ; do |
| 53 if [ ${group} -eq ${LINKGROUP} ] ; then |
| 54 GROUPMATCH=1 |
| 55 break |
| 56 fi |
| 57 done |
| 58 if [ -n "${GROUPMATCH}" ] ; then |
| 59 if [ "${LINKMODE:5:1}" = "w" ] ; then |
| 60 return 0 |
| 61 fi |
| 62 return 1 |
| 63 fi |
| 64 |
| 65 # Check the other-write bit. |
| 66 if [ "${LINKMODE:8:1}" = "w" ] ; then |
| 67 return 0 |
| 68 fi |
| 69 return 1 |
| 70 } |
| 71 |
| 72 # If SYMLINK exists and is a symbolic link, but is not writeable according to |
| 73 # is_writeable_symlink, this function attempts to replace it with a new |
| 74 # writeable symbolic link. If FROM does not exist, is not a symbolic link, or |
| 75 # is already writeable, this function does nothing. This function always |
| 76 # returns 0 (true). |
| 77 function ensure_writeable_symlink() { |
| 78 SYMLINK=${1} |
| 79 if [ -L "${SYMLINK}" ] && ! is_writeable_symlink "${SYMLINK}" ; then |
| 80 # If ${SYMLINK} refers to a directory, doing this naively might result in |
| 81 # the new link being placed in that directory, instead of replacing the |
| 82 # existing link. ln -fhs is supposed to handle this case, but it does so |
| 83 # by unlinking (removing) the existing symbolic link before creating a new |
| 84 # one. That leaves a small window during which the symbolic link is not |
| 85 # present on disk at all. |
| 86 # |
| 87 # To avoid that possibility, a new symbolic link is created in a temporary |
| 88 # location and then swapped into place with mv. An extra temporary |
| 89 # directory is used to convince mv to replace the symbolic link: again, if |
| 90 # the existing link refers to a directory, "mv newlink oldlink" will |
| 91 # actually leave oldlink alone and place newlink into the directory. |
| 92 # "mv newlink dirname(oldlink)" works as expected, but in order to replace |
| 93 # oldlink, newlink must have the same basename, hence the temporary |
| 94 # directory. |
| 95 |
| 96 TARGET=$(readlink "${SYMLINK}" 2> /dev/null || true) |
| 97 if [ -z "${TARGET}" ] ; then |
| 98 return 0 |
| 99 fi |
| 100 |
| 101 SYMLINKDIR=$(dirname "${SYMLINK}") |
| 102 TEMPLINKDIR="${SYMLINKDIR}/.symlink_temp.${$}.${RANDOM}" |
| 103 TEMPLINK="${TEMPLINKDIR}/$(basename "${SYMLINK}")" |
| 104 |
| 105 # Don't bail out here if this fails. Something else will probably fail. |
| 106 # Let it, it'll probably be easier to understand that failure than this |
| 107 # one. |
| 108 (mkdir "${TEMPLINKDIR}" && \ |
| 109 ln -fhs "${TARGET}" "${TEMPLINK}" && \ |
| 110 chmod -h 755 "${TEMPLINK}" && \ |
| 111 mv -f "${TEMPLINK}" "${SYMLINKDIR}") || true |
| 112 rm -rf "${TEMPLINKDIR}" |
| 113 fi |
| 114 |
| 115 return 0 |
| 116 } |
| 117 |
25 # The argument should be the disk image path. Make sure it exists. | 118 # The argument should be the disk image path. Make sure it exists. |
26 if [ $# -lt 1 ] || [ ! -d "${1}" ]; then | 119 if [ $# -lt 1 ] || [ ! -d "${1}" ]; then |
27 exit 10 | 120 exit 10 |
28 fi | 121 fi |
29 | 122 |
30 # Who we are. | 123 # Who we are. |
31 PRODUCT_NAME="Google Chrome" | 124 PRODUCT_NAME="Google Chrome" |
32 APP_DIR="${PRODUCT_NAME}.app" | 125 APP_DIR="${PRODUCT_NAME}.app" |
33 FRAMEWORK_NAME="${PRODUCT_NAME} Framework" | 126 FRAMEWORK_NAME="${PRODUCT_NAME} Framework" |
34 FRAMEWORK_DIR="${FRAMEWORK_NAME}.framework" | 127 FRAMEWORK_DIR="${FRAMEWORK_NAME}.framework" |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 | 164 |
72 # See if the timestamp of what's currently on disk is newer than the update's | 165 # See if the timestamp of what's currently on disk is newer than the update's |
73 # outer .app's timestamp. rsync will copy the update's timestamp over, but | 166 # outer .app's timestamp. rsync will copy the update's timestamp over, but |
74 # if that timestamp isn't as recent as what's already on disk, the .app will | 167 # if that timestamp isn't as recent as what's already on disk, the .app will |
75 # need to be touched. | 168 # need to be touched. |
76 NEEDS_TOUCH= | 169 NEEDS_TOUCH= |
77 if [ "${DEST}" -nt "${SRC}" ] ; then | 170 if [ "${DEST}" -nt "${SRC}" ] ; then |
78 NEEDS_TOUCH=1 | 171 NEEDS_TOUCH=1 |
79 fi | 172 fi |
80 | 173 |
| 174 # In some very weird and rare cases, it is possible to wind up with a user |
| 175 # installation that contains symbolic links that the user does not have write |
| 176 # permission over. More on how that might happen later. |
| 177 # |
| 178 # If a weird and rare case like this is observed, rsync will exit with an |
| 179 # error when attempting to update the times on these symbolic links. rsync |
| 180 # may not be intelligent enough to try creating a new symbolic link in these |
| 181 # cases, but this script can be. |
| 182 # |
| 183 # This fix-up is not necessary when running as root, because root will always |
| 184 # be able to write everything needed. |
| 185 # |
| 186 # The problem occurs when an administrative user first drag-installs the |
| 187 # application to /Applications, resulting in the program's user being set to |
| 188 # the user's own ID. If, subsequently, a .pkg package is installed over that, |
| 189 # the existing directory ownership will be preserved, but file ownership will |
| 190 # be changed to whateer is specified by the package, typically root. This |
| 191 # applies to symbolic links as well. On a subsequent update, rsync will |
| 192 # be able to copy the new files into place, because the user still has |
| 193 # permission to write to the directories. If the symbolic link targets are |
| 194 # not changing, though, rsync will not replace them, and they will remain |
| 195 # owned by root. The user will not have permission to update the time on |
| 196 # the symbolic links, resulting in an rsync error. |
| 197 if [ ${EUID} -ne 0 ] ; then |
| 198 # This step isn't critical. |
| 199 set +e |
| 200 |
| 201 # Reset ${IFS} to deal with spaces in the for loop by not breaking the |
| 202 # list up when they're encountered. |
| 203 IFS_OLD="${IFS}" |
| 204 IFS=$(printf '\n\t') |
| 205 |
| 206 # Only consider symbolic links in ${SRC}. If there are any other links in |
| 207 # ${DEST} not present in ${SRC}, rsync will delete them as needed later. |
| 208 LINKS=$(cd "${SRC}" && find . -type l) |
| 209 |
| 210 for link in ${LINKS} ; do |
| 211 # ${link} is relative to ${SRC}. Prepending ${DEST} looks for the same |
| 212 # link already on disk. |
| 213 DESTLINK="${DEST}/${link}" |
| 214 ensure_writeable_symlink "${DESTLINK}" |
| 215 done |
| 216 |
| 217 # Go back to how things were. |
| 218 IFS="${IFS_OLD}" |
| 219 set -e |
| 220 fi |
| 221 |
81 # Don't use rsync -a, because -a expands to -rlptgoD. -g and -o copy owners | 222 # Don't use rsync -a, because -a expands to -rlptgoD. -g and -o copy owners |
82 # and groups, respectively, from the source, and that is undesirable in this | 223 # and groups, respectively, from the source, and that is undesirable in this |
83 # case. -D copies devices and special files; copying devices only works | 224 # case. -D copies devices and special files; copying devices only works |
84 # when running as root, so for consistency between privileged and unprivileged | 225 # when running as root, so for consistency between privileged and unprivileged |
85 # operation, this option is omitted as well. | 226 # operation, this option is omitted as well. |
86 # -c, --checksum skip based on checksum, not mod-time & size | 227 # -I, --ignore-times don't skip files that match in size and mod-time |
87 # -l, --links copy symlinks as symlinks | 228 # -l, --links copy symlinks as symlinks |
88 # -r, --recursive recurse into directories | 229 # -r, --recursive recurse into directories |
89 # -p, --perms preserve permissions | 230 # -p, --perms preserve permissions |
90 # -t, --times preserve times | 231 # -t, --times preserve times |
91 RSYNC_FLAGS="-clprt" | 232 RSYNC_FLAGS="-Ilprt" |
92 | 233 |
93 # By copying to ${DEST}, the existing application name will be preserved, even | 234 # By copying to ${DEST}, the existing application name will be preserved, even |
94 # if the user has renamed the application on disk. Respecting the user's | 235 # if the user has renamed the application on disk. Respecting the user's |
95 # changes is friendly. | 236 # changes is friendly. |
96 | 237 |
97 # Make sure that the Versions directory exists, so that it can receive the | 238 # Make sure that the Versions directory exists, so that it can receive the |
98 # versioned directory. It may not exist if updating from an older version | 239 # versioned directory. It may not exist if updating from an older version |
99 # that did not use the versioned layout on disk. An rsync that excludes all | 240 # that did not use the versioned layout on disk. An rsync that excludes all |
100 # contents is used to bring the permissions over from the update's Versions | 241 # contents is used to bring the permissions over from the update's Versions |
101 # directory, otherwise, this directory would be the only one in the entire | 242 # directory, otherwise, this directory would be the only one in the entire |
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 ([ ${OS_MAJOR} -eq 10 ] && [ ${OS_MINOR} -ge 6 ]) ; then | 474 ([ ${OS_MAJOR} -eq 10 ] && [ ${OS_MINOR} -ge 6 ]) ; then |
334 # On 10.6, xattr supports -r for recursive operation. | 475 # On 10.6, xattr supports -r for recursive operation. |
335 xattr -d -r "${QUARANTINE_ATTR}" "${DEST}" >& /dev/null | 476 xattr -d -r "${QUARANTINE_ATTR}" "${DEST}" >& /dev/null |
336 else | 477 else |
337 # On earlier systems, xattr doesn't support -r, so run xattr via find. | 478 # On earlier systems, xattr doesn't support -r, so run xattr via find. |
338 find "${DEST}" -exec xattr -d "${QUARANTINE_ATTR}" {} + >& /dev/null | 479 find "${DEST}" -exec xattr -d "${QUARANTINE_ATTR}" {} + >& /dev/null |
339 fi | 480 fi |
340 | 481 |
341 # Great success! | 482 # Great success! |
342 exit 0 | 483 exit 0 |
OLD | NEW |