Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(969)

Side by Side Diff: chrome/tools/build/mac/keystone_install.sh

Issue 341072: rsync isn't resilient about updating symbolic link times (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698