@@ -117,7 +117,7 @@ function load_root_links() {
117117 done < <( ls /)
118118}
119119
120- # file_to_package identifies the debian package that provided the file $1
120+ # file_to_package identifies the debian package(s) that provided the file $1
121121function file_to_package() {
122122 local file=" $1 "
123123
@@ -166,40 +166,48 @@ function file_to_package() {
166166
167167 # `dpkg-query --search $file-pattern` outputs lines with the format: "$package: $file-path"
168168 # where $file-path belongs to $package. Sometimes it has lines that say
169- # "diversion" but there's no documented grammar I can find.
170- echo " ${result} " | grep -v " diversion" | cut -d' :' -f1
169+ # "diversion" but there's no documented grammar I can find. Multiple
170+ # packages can own one file, in which case multiple lines are output.
171+ echo " ${result} " | (grep -v " diversion" || true) | cut -d' :' -f1 | sed ' s/,//g'
171172}
172173
173- function ensure_dir_in_staging() {
174+ # stage_one_file stages the filepath $2 to $1, respecting symlinks
175+ function stage_one_file() {
174176 local staging=" $1 "
175- local dir =" $2 "
177+ local file =" $2 "
176178
177- if [[ ! -e " ${staging} /${dir} " ]]; then
178- # Stript the leading /
179- local rel=" ${dir/# \/ / } "
180- tar -C / -c --no-recursion --dereference " ${rel} " | tar -C " ${staging} " -x
179+ if [ -e " ${staging}${file} " ]; then
180+ return
181181 fi
182- }
183182
184- # stage_one_file stages the filepath $2 to $1, following symlinks
185- function stage_one_file() {
186- local staging=" $1 "
187- local file=" $2 "
183+ # This will break the path into elements, so we can handle symlinks at any
184+ # level.
185+ local elems=()
186+ IFS=' /' read -r -a elems <<< " ${file}"
187+ # [0] is empty because of leading /
188+ if [[ " ${elems[0]} " == " " ]]; then
189+ elems=(" ${elems[@]: 1} " )
190+ fi
188191
189- # copy the real form of the named path
190- local real
191- real=" $( realpath " ${file} " ) "
192- cp -a --parents " ${real} " " ${staging} "
193-
194- # recreate symlinks, even on intermediate path elements
195- if [[ " ${file} " != " ${real} " ]]; then
196- if [[ ! -e " ${staging} /${file} " ]]; then
197- local dir
198- dir=" $( dirname " ${file} " ) "
199- ensure_dir_in_staging " ${staging} " " ${dir} "
200- ln -s " ${real} " " ${staging} /${file} "
192+ local path=" "
193+ for elem in " ${elems[@]} " ; do
194+ path=" ${path} /${elem} "
195+ if [[ ! -e " ${staging}${path} " ]]; then
196+ if [[ -d " ${path} " && ! -L " ${path} " ]]; then
197+ # strip the leading / and use tar, which preserves everything
198+ local rel=" ${path/# \/ / } "
199+ tar -C / -c --no-recursion " ${rel} " | tar -C " ${staging} " -x
200+ else
201+ # preserves hardlinks, symlinks, permissions, timestamps
202+ cp -lpP " ${path} " " ${staging}${path} "
203+
204+ # if it is a symlink, also stage the target
205+ if [[ -L " ${path} " ]]; then
206+ stage_one_file " ${staging} " " $( realpath " ${path} " ) "
207+ fi
208+ fi
201209 fi
202- fi
210+ done
203211}
204212
205213# stage_file_and_deps stages the filepath $2 to $1, following symlinks and
@@ -214,21 +222,25 @@ function stage_file_and_deps() {
214222 fi
215223
216224 # get the package so we can stage package metadata as well
217- local package
218- package=" $( file_to_package " ${file} " ) "
219- DBG " staging file ${file} from pkg ${package} "
225+ local packages
226+ packages=" $( file_to_package " ${file} " ) "
227+ if [[ -z " ${packages} " ]]; then
228+ return 0 # no package(s), but no error either
229+ fi
230+ DBG " staging file ${file} from pkg(s) ${packages} "
220231
221- stage_one_file " ${staging} " " $file "
232+ stage_one_file " ${staging} " " ${ file} "
222233
223234 # stage dependencies of binaries
224- if [[ -x " $file " ]]; then
235+ if [[ -x " ${ file} " && ! -d " ${file} " ]]; then
225236 DBG " staging deps of file ${file} "
226237 while read -r lib; do
227238 indent stage_file_and_deps " ${staging} " " ${lib} "
228239 done < <( binary_to_libraries " ${file} " )
229240 fi
230241
231- if [[ -n " ${package} " ]]; then
242+ local package
243+ for package in ${packages} ; do
232244 # stage the copyright for the file, if it exists
233245 local copyright_src=" /usr/share/doc/${package} /copyright"
234246 local copyright_dst=" ${staging} /copyright/${package} /copyright.gz"
@@ -242,53 +254,31 @@ function stage_file_and_deps() {
242254 # https://github.com/bazelbuild/rules_docker/commit/f5432b813e0a11491cf2bf83ff1a923706b36420
243255 mkdir -p " ${staging} /var/lib/dpkg/status.d/"
244256 dpkg -s " ${package} " > " ${staging} /var/lib/dpkg/status.d/${package} "
245- fi
257+ done
246258}
247259
248260function stage_one_package() {
249261 local staging=" $1 "
250262 local pkg=" $2 "
251263
252- local names=()
253- local sums=()
254264 while read -r file; do
255- if [[ -f " ${file} " ]]; then
256- local found=" "
257- if [[ ! -L " ${file} " ]]; then
258- sum=" $( md5sum " ${file} " | cut -f1 -d' ' ) "
259- local i=0
260- for s in " ${sums[@]} " ; do
261- if [[ " ${sum} " == " ${s} " ]]; then
262- local dir
263- dir=" $( dirname " ${file} " ) "
264- ensure_dir_in_staging " ${staging} " " $( dirname " ${file} " ) "
265- ln -s " ${names[$i]} " " ${staging} /${file} "
266- found=" true"
267- break
268- fi
269- i=$(( i+ 1 ))
270- done
271- fi
272- if [[ -z " ${found} " ]]; then
273- names+=(" ${file} " )
274- sums+=(" ${sum} " )
275- indent stage_file_and_deps " ${staging} " " ${file} "
276- fi
277- fi
265+ indent stage_file_and_deps " ${staging} " " ${file} "
278266 done < <( dpkg -L " ${pkg} " \
279267 | grep_allow_nomatch -vE ' (/\.|/usr/share/(man|doc|.*-completion))' )
280268}
281269
282270function get_dependent_packages() {
283271 local pkg=" $1 "
284- # There's no documented grammar for the output of this. Sometimes it says:
272+ # There's no easily found documented grammar for the output of this.
273+ # Sometimes it says:
285274 # Depends: package
286275 # ...and other times it says:
287276 # Depends <package>
288- # ...but those don't really seem to be required. There's also "PreDepends"
289- # which are something else.
277+ # ...but those don't really seem to be required.
278+ # There's also "PreDepends" which is like Depends but has semantic
279+ # differences that don't matter here.
290280 apt-cache depends " ${pkg} " \
291- | grep_allow_nomatch ' ^ *Depends: [a-zA-Z0-9]' \
281+ | grep_allow_nomatch ' ^ *\(Pre\)\? Depends: [a-zA-Z0-9]' \
292282 | awk -F ' :' ' {print $2}'
293283}
294284
0 commit comments