@@ -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" || true) | 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,24 +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- if [[ -z " ${package } " ]]; then
220- return 0 # no package, but no error either
225+ local packages
226+ packages =" $( file_to_package " ${file} " ) "
227+ if [[ -z " ${packages } " ]]; then
228+ return 0 # no package(s) , but no error either
221229 fi
222- DBG " staging file ${file} from pkg ${package } "
230+ DBG " staging file ${file} from pkg(s) ${packages } "
223231
224- stage_one_file " ${staging} " " $file "
232+ stage_one_file " ${staging} " " ${ file} "
225233
226234 # stage dependencies of binaries
227- if [[ -x " $file " ]]; then
235+ if [[ -x " ${ file} " && ! -d " ${file} " ]]; then
228236 DBG " staging deps of file ${file} "
229237 while read -r lib; do
230238 indent stage_file_and_deps " ${staging} " " ${lib} "
231239 done < <( binary_to_libraries " ${file} " )
232240 fi
233241
234- if [[ -n " ${package} " ]]; then
242+ local package
243+ for package in ${packages} ; do
235244 # stage the copyright for the file, if it exists
236245 local copyright_src=" /usr/share/doc/${package} /copyright"
237246 local copyright_dst=" ${staging} /copyright/${package} /copyright.gz"
@@ -245,53 +254,31 @@ function stage_file_and_deps() {
245254 # https://github.com/bazelbuild/rules_docker/commit/f5432b813e0a11491cf2bf83ff1a923706b36420
246255 mkdir -p " ${staging} /var/lib/dpkg/status.d/"
247256 dpkg -s " ${package} " > " ${staging} /var/lib/dpkg/status.d/${package} "
248- fi
257+ done
249258}
250259
251260function stage_one_package() {
252261 local staging=" $1 "
253262 local pkg=" $2 "
254263
255- local names=()
256- local sums=()
257264 while read -r file; do
258- if [[ -f " ${file} " ]]; then
259- local found=" "
260- if [[ ! -L " ${file} " ]]; then
261- sum=" $( md5sum " ${file} " | cut -f1 -d' ' ) "
262- local i=0
263- for s in " ${sums[@]} " ; do
264- if [[ " ${sum} " == " ${s} " ]]; then
265- local dir
266- dir=" $( dirname " ${file} " ) "
267- ensure_dir_in_staging " ${staging} " " $( dirname " ${file} " ) "
268- ln -s " ${names[$i]} " " ${staging} /${file} "
269- found=" true"
270- break
271- fi
272- i=$(( i+ 1 ))
273- done
274- fi
275- if [[ -z " ${found} " ]]; then
276- names+=(" ${file} " )
277- sums+=(" ${sum} " )
278- indent stage_file_and_deps " ${staging} " " ${file} "
279- fi
280- fi
265+ indent stage_file_and_deps " ${staging} " " ${file} "
281266 done < <( dpkg -L " ${pkg} " \
282267 | grep_allow_nomatch -vE ' (/\.|/usr/share/(man|doc|.*-completion))' )
283268}
284269
285270function get_dependent_packages() {
286271 local pkg=" $1 "
287- # 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:
288274 # Depends: package
289275 # ...and other times it says:
290276 # Depends <package>
291- # ...but those don't really seem to be required. There's also "PreDepends"
292- # 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.
293280 apt-cache depends " ${pkg} " \
294- | grep_allow_nomatch ' ^ *Depends: [a-zA-Z0-9]' \
281+ | grep_allow_nomatch ' ^ *\(Pre\)\? Depends: [a-zA-Z0-9]' \
295282 | awk -F ' :' ' {print $2}'
296283}
297284
0 commit comments