3 # Copyright (c) 2011 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.
7 # usage: dirpatcher.sh old_dir patch_dir new_dir
9 # dirpatcher creates new_dir from patch_dir by decompressing and copying
10 # files, and using goobspatch to apply binary diffs to files in old_dir.
12 # dirpatcher performs the inverse operation to dirdiffer. For more details,
13 # consult dirdiffer.sh.
18 # 2 Incorrect number of parameters
19 # 3 Input directories do not exist or are not directories
20 # 4 Output directory already exists
21 # 5 Parent of output directory does not exist or is not a directory
22 # 6 An input or output directories contains another
23 # 7 Could not create output directory
24 # 8 File already exists in output directory
25 # 9 Found an irregular file (non-directory, file, or symbolic link) in input
26 # 10 Could not create symbolic link
27 # 11 Unrecognized file extension
28 # 12 Attempt to patch a nonexistent or non-regular file
29 # 13 Patch application failed
30 # 14 File decompression failed
32 # 16 Could not set mode (permissions)
33 # 17 Could not set modification time
37 # Environment sanitization. Set a known-safe PATH. Clear environment variables
38 # that might impact the interpreter's operation. The |bash -p| invocation
39 # on the #! line takes the bite out of BASH_ENV, ENV, and SHELLOPTS (among
40 # other features), but clearing them here ensures that they won't impact any
41 # shell scripts used as utility programs. SHELLOPTS is read-only and can't be
42 # unset, only unexported.
43 export PATH
="/usr/bin:/bin:/usr/sbin:/sbin"
44 unset BASH_ENV CDPATH ENV GLOBIGNORE IFS POSIXLY_CORRECT
47 shopt -s dotglob nullglob
49 # find_tool looks for an executable file named |tool_name|:
50 # - in the same directory as this script,
51 # - if this script is located in a Chromium source tree, at the expected
52 # Release output location in the Mac out directory,
53 # - as above, but in the Debug output location
54 # If found in any of the above locations, the script's path is output.
55 # Otherwise, this function outputs |tool_name| as a fallback, allowing it to
56 # be found (or not) by an ordinary ${PATH} search.
58 local tool_name
="${1}"
61 script_dir
="$(dirname "${0}")"
63 local tool
="${script_dir}/${tool_name}"
64 if [[ -f "${tool}" ]] && [[ -x "${tool}" ]]; then
70 script_dir_phys
="$(cd "${script_dir}" && pwd -P)"
71 if [[ "${script_dir_phys}" =~ ^
(.
*)/src
/chrome
/installer
/mac$
]]; then
72 tool
="${BASH_REMATCH[1]}/src/out/Release/${tool_name}"
73 if [[ -f "${tool}" ]] && [[ -x "${tool}" ]]; then
78 tool
="${BASH_REMATCH[1]}/src/out/Debug/${tool_name}"
79 if [[ -f "${tool}" ]] && [[ -x "${tool}" ]]; then
88 ME
="$(basename "${0}")"
90 GOOBSPATCH
="$(find_tool goobspatch)"
92 readonly BUNZIP2
="bunzip2"
93 readonly GUNZIP
="gunzip"
94 XZDEC
="$(find_tool xzdec)"
96 readonly GBS_SUFFIX
='$gbs'
97 readonly BZ2_SUFFIX
='$bz2'
98 readonly GZ_SUFFIX
='$gz'
99 readonly XZ_SUFFIX
='$xz'
100 readonly PLAIN_SUFFIX
='$raw'
105 echo "${ME}: ${error}" >& 2
113 trap '' HUP INT QUIT TERM
115 if [[ ${status} -ge 128 ]]; then
116 err
"Caught signal $((${status} - 128))"
119 if [[ "${#g_cleanup[@]}" -gt 0 ]]; then
120 rm -rf "${g_cleanup[@]}"
126 copy_mode_and_time
() {
127 local patch_file
="${1}"
128 local new_file
="${2}"
131 mode
="$(stat "-f%OMp
%OLp
" "${patch_file}")"
132 if ! chmod -h "${mode}" "${new_file}"; then
136 if ! [[ -L "${new_file}" ]]; then
137 # Symbolic link modification times can't be copied because there's no
138 # shell tool that provides direct access to lutimes. Instead, the symbolic
139 # link was created with rsync, which already copied the timestamp with
141 if ! touch -r "${patch_file}" "${new_file}"; then
148 local old_file
="${1}"
149 local patch_file
="${2}"
150 local new_file
="${3}"
153 if [[ -L "${old_file}" ]] ||
! [[ -f "${old_file}" ]]; then
154 err
"can't patch nonexistent or irregular file ${old_file}"
158 if ! "${patcher}" "${old_file}" "${new_file}" "${patch_file}"; then
159 err
"couldn't create ${new_file} by applying ${patch_file} to ${old_file}"
165 local old_file
="${1}"
166 local patch_file
="${2}"
167 local new_file
="${3}"
168 local decompressor
="${4}"
170 if ! "${decompressor}" -c < "${patch_file}" > "${new_file}"; then
171 err
"couldn't decompress ${patch_file} to ${new_file} with ${decompressor}"
177 local old_file
="${1}"
178 local patch_file
="${2}"
179 local new_file
="${3}"
182 if ! cp "${patch_file}" "${new_file}"; then
188 local old_file
="${1}"
189 local patch_file
="${2}"
190 local new_file
="${3}"
192 local operation extra strip_length
194 if [[ "${patch_file: -${#GBS_SUFFIX}}" = "${GBS_SUFFIX}" ]]; then
195 operation
="apply_patch"
196 extra
="${GOOBSPATCH}"
197 strip_length
=${#GBS_SUFFIX}
198 elif [[ "${patch_file: -${#BZ2_SUFFIX}}" = "${BZ2_SUFFIX}" ]]; then
199 operation
="decompress_file"
201 strip_length
=${#BZ2_SUFFIX}
202 elif [[ "${patch_file: -${#GZ_SUFFIX}}" = "${GZ_SUFFIX}" ]]; then
203 operation
="decompress_file"
205 strip_length
=${#GZ_SUFFIX}
206 elif [[ "${patch_file: -${#XZ_SUFFIX}}" = "${XZ_SUFFIX}" ]]; then
207 operation
="decompress_file"
209 strip_length
=${#XZ_SUFFIX}
210 elif [[ "${patch_file: -${#PLAIN_SUFFIX}}" = "${PLAIN_SUFFIX}" ]]; then
211 operation
="copy_file"
213 strip_length
=${#PLAIN_SUFFIX}
215 err
"don't know how to operate on ${patch_file}"
219 old_file
="${old_file:0:${#old_file} - ${strip_length}}"
220 new_file
="${new_file:0:${#new_file} - ${strip_length}}"
222 if [[ -e "${new_file}" ]]; then
223 err
"${new_file} already exists"
227 "${operation}" "${old_file}" "${patch_file}" "${new_file}" "${extra}"
229 copy_mode_and_time
"${patch_file}" "${new_file}"
233 local patch_file
="${1}"
234 local new_file
="${2}"
237 # target="$(readlink "${patch_file}")"
238 # ln -s "${target}" "${new_file}"
240 # Use rsync instead of the above, as it's the only way to preserve the
241 # timestamp of a symbolic link using shell tools.
242 if ! rsync
-lt "${patch_file}" "${new_file}"; then
246 copy_mode_and_time
"${patch_file}" "${new_file}"
251 local patch_dir
="${2}"
254 if ! mkdir
"${new_dir}"; then
259 for patch_file
in "${patch_dir}/"*; do
260 local file="${patch_file:${#patch_dir} + 1}"
261 local old_file
="${old_dir}/${file}"
262 local new_file
="${new_dir}/${file}"
264 if [[ -e "${new_file}" ]]; then
265 err
"${new_file} already exists"
269 if [[ -L "${patch_file}" ]]; then
270 patch_symlink
"${patch_file}" "${new_file}"
271 elif [[ -d "${patch_file}" ]]; then
272 patch_dir
"${old_file}" "${patch_file}" "${new_file}"
273 elif ! [[ -f "${patch_file}" ]]; then
274 err
"can't handle irregular file ${patch_file}"
277 patch_file
"${old_file}" "${patch_file}" "${new_file}"
281 copy_mode_and_time
"${patch_dir}" "${new_dir}"
284 # shell_safe_path ensures that |path| is safe to pass to tools as a
285 # command-line argument. If the first character in |path| is "-", "./" is
286 # prepended to it. The possibly-modified |path| is output.
289 if [[ "${path:0:1}" = "-" ]]; then
300 if [[ "${dir1:0:${#dir2}}" = "${dir2}" ]] ||
301 [[ "${dir2:0:${#dir1}}" = "${dir1}" ]]; then
309 echo "usage: ${ME} old_dir patch_dir new_dir" >& 2
313 local old_dir patch_dir new_dir
314 old_dir
="$(shell_safe_path "${1}")"
315 patch_dir
="$(shell_safe_path "${2}")"
316 new_dir
="$(shell_safe_path "${3}")"
318 trap cleanup EXIT HUP INT QUIT TERM
320 if ! [[ -d "${old_dir}" ]] ||
! [[ -d "${patch_dir}" ]]; then
321 err
"old_dir and patch_dir must exist and be directories"
326 if [[ -e "${new_dir}" ]]; then
327 err
"new_dir must not exist"
333 new_dir_parent
="$(dirname "${new_dir}")"
334 if ! [[ -d "${new_dir_parent}" ]]; then
335 err
"new_dir parent directory must exist and be a directory"
340 local old_dir_phys patch_dir_phys new_dir_parent_phys new_dir_phys
341 old_dir_phys
="$(cd "${old_dir}" && pwd -P)"
342 patch_dir_phys
="$(cd "${patch_dir}" && pwd -P)"
343 new_dir_parent_phys
="$(cd "${new_dir_parent}" && pwd -P)"
344 new_dir_phys
="${new_dir_parent_phys}/$(basename "${new_dir}")"
346 if dirs_contained
"${old_dir_phys}" "${patch_dir_phys}" ||
347 dirs_contained
"${old_dir_phys}" "${new_dir_phys}" ||
348 dirs_contained
"${patch_dir_phys}" "${new_dir_phys}"; then
349 err
"directories must not contain one another"
354 g_cleanup
+=("${new_dir}")
356 patch_dir
"${old_dir}" "${patch_dir}" "${new_dir}"
358 unset g_cleanup
[${#g_cleanup[@]}]
362 if [[ ${#} -ne 3 ]]; then