3 # This script is a wrapper to the other download backends.
4 # Its role is to ensure atomicity when saving downloaded files
5 # back to BR2_DL_DIR, and not clutter BR2_DL_DIR with partial,
8 # Call it with -h to see some help.
10 # To avoid cluttering BR2_DL_DIR, we download to a trashable
11 # location, namely in $(BUILD_DIR).
12 # Then, we move the downloaded file to a temporary file in the
13 # same directory as the final output file.
14 # This allows us to finally atomically rename it to its final
16 # If anything goes wrong, we just remove all the temporaries
19 # We want to catch any unexpected failure, and exit immediately.
22 export BR_BACKEND_DL_GETOPTS=":hc:d:o:n:N:H:ru:qf:e"
26 local backend output hfile recurse quiet rc
29 # Parse our options; anything after '--' is for the backend
30 while getopts ":hc:d:D:o:n:N:H:rf:u:q" OPT; do
34 d) dl_dir="${OPTARG}";;
35 D) old_dl_dir="${OPTARG}";;
36 o) output="${OPTARG}";;
37 n) raw_base_name="${OPTARG}";;
38 N) base_name="${OPTARG}";;
39 H) hfile="${OPTARG}";;
41 f) filename="${OPTARG}";;
42 u) uris+=( "${OPTARG}" );;
44 :) error "option '%s' expects a mandatory argument\n" "${OPTARG}";;
45 \?) error "unknown option '%s'\n" "${OPTARG}";;
49 # Forget our options, and keep only those for the backend
52 if [ -z "${output}" ]; then
53 error "no output specified, use -o\n"
56 # Legacy handling: check if the file already exists in the global
57 # download directory. If it does, hard-link it. If it turns out it
58 # was an incorrect download, we'd still check it below anyway.
59 # If we can neither link nor copy, fallback to doing a download.
60 # NOTE! This is not atomic, is subject to TOCTTOU, but the whole
61 # dl-wrapper runs under an flock, so we're safe.
62 if [ ! -e "${output}" -a -e "${old_dl_dir}/${filename}" ]; then
63 ln "${old_dl_dir}/${filename}" "${output}" || \
64 cp "${old_dl_dir}/${filename}" "${output}" || \
68 # If the output file already exists and:
69 # - there's no .hash file: do not download it again and exit promptly
70 # - matches all its hashes: do not download it again and exit promptly
71 # - fails at least one of its hashes: force a re-download
72 # - there's no hash (but a .hash file): consider it a hard error
73 if [ -e "${output}" ]; then
74 if support/download/check-hash ${quiet} "${hfile}" "${output}" "${output##*/}"; then
76 elif [ ${?} -ne 2 ]; then
77 # Do not remove the file, otherwise it might get re-downloaded
78 # from a later location (i.e. primary -> upstream -> mirror).
79 # Do not print a message, check-hash already did.
83 warn "Re-downloading '%s'...\n" "${output##*/}"
86 # Look through all the uris that we were given to download the package
90 for uri in "${uris[@]}"; do
93 git|svn|cvs|bzr|file|scp|hg) ;;
98 urlencode=${backend#*|}
99 # urlencode must be "urlencode"
100 [ "${urlencode}" != "urlencode" ] && urlencode=""
102 # tmpd is a temporary directory in which backends may store
103 # intermediate by-products of the download.
104 # tmpf is the file in which the backends should put the downloaded
106 # tmpd is located in $(BUILD_DIR), so as not to clutter the (precious)
108 # We let the backends create tmpf, so they are able to set whatever
109 # permission bits they want (although we're only really interested in
110 # the executable bit.)
111 tmpd="$(mktemp -d "${BUILD_DIR}/.${output##*/}.XXXXXX")"
112 tmpf="${tmpd}/output"
114 # Helpers expect to run in a directory that is *really* trashable, so
115 # they are free to create whatever files and/or sub-dirs they might need.
116 # Doing the 'cd' here rather than in all backends is easier.
119 # If the backend fails, we can just remove the content of the temporary
120 # directory to remove all the cruft it may have left behind, and try
121 # the next URI until it succeeds. Once out of URI to try, we need to
123 if ! "${OLDPWD}/support/download/${backend}" \
124 $([ -n "${urlencode}" ] && printf %s '-e') \
127 -n "${raw_base_name}" \
132 ${quiet} ${recurse} -- "${@}"
134 # cd back to keep path coherence
140 # cd back to free the temp-dir, so we can remove it later
143 # Check if the downloaded file is sane, and matches the stored hashes
145 if support/download/check-hash ${quiet} "${hfile}" "${tmpf}" "${output##*/}"; then
148 if [ ${?} -ne 3 ]; then
153 # the hash file exists and there was no hash to check the file
161 # We tried every URI possible, none seems to work or to check against the
162 # available hash. *ABORT MISSION*
163 if [ "${download_and_check}" -eq 0 ]; then
168 # tmp_output is in the same directory as the final output, so we can
169 # later move it atomically.
170 tmp_output="$(mktemp "${output}.XXXXXX")"
172 # 'mktemp' creates files with 'go=-rwx', so the files are not accessible
173 # to users other than the one doing the download (and root, of course).
174 # This can be problematic when a shared BR2_DL_DIR is used by different
175 # users (e.g. on a build server), where all users may write to the shared
176 # location, since other users would not be allowed to read the files
177 # another user downloaded.
178 # So, we restore the 'go' access rights to a more sensible value, while
179 # still abiding by the current user's umask. We must do that before the
180 # final 'mv', so just do it now.
181 # Some backends (cp and scp) may create executable files, so we need to
182 # carry the executable bit if needed.
183 [ -x "${tmpf}" ] && new_mode=755 || new_mode=644
184 new_mode=$(printf "%04o" $((0${new_mode} & ~0$(umask))))
185 chmod ${new_mode} "${tmp_output}"
187 # We must *not* unlink tmp_output, otherwise there is a small window
188 # during which another download process may create the same tmp_output
189 # name (very, very unlikely; but not impossible.)
190 # Using 'cp' is not reliable, since 'cp' may unlink the destination file
191 # if it is unable to open it with O_WRONLY|O_TRUNC; see:
192 # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cp.html
193 # Since the destination filesystem can be anything, it might not support
194 # O_TRUNC, so 'cp' would unlink it first.
195 # Use 'cat' and append-redirection '>>' to save to the final location,
196 # since that is the only way we can be 100% sure of the behaviour.
197 if ! cat "${tmpf}" >>"${tmp_output}"; then
198 rm -rf "${tmpd}" "${tmp_output}"
203 # tmp_output and output are on the same filesystem, so POSIX guarantees
204 # that 'mv' is atomic, because it then uses rename() that POSIX mandates
206 # http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html
207 if ! mv -f "${tmp_output}" "${output}"; then
208 rm -f "${tmp_output}"
218 ${my_name} - download wrapper for Buildroot
221 ${my_name} [OPTION]... -- [BACKEND OPTION]...
224 Wrapper script around different download mechanisms. Ensures that
225 concurrent downloads do not conflict, that partial downloads are
226 properly evicted without leaving temporary files, and that access
227 rights are maintained.
232 The URI to get the file from, the URI must respect the format given in
234 You may give as many '-u URI' as you want, the script will stop at the
235 frist successful download.
237 Example: backend+URI; git+http://example.com or http+http://example.com
240 Store the downloaded archive in FILE.
243 Use FILE to read hashes from, and check them against the downloaded
253 The path to Buildroot's build dir
257 trace() { local msg="${1}"; shift; printf "%s: ${msg}" "${my_name}" "${@}"; }
258 warn() { trace "${@}" >&2; }
259 errorN() { local ret="${1}"; shift; warn "${@}"; exit ${ret}; }
260 error() { errorN 1 "${@}"; }