1############################################################### smallutils
2
3smallyes() {
4	YES="${1-y}"
5	while echo "$YES" 2>/dev/null ; do : ; done
6}
7
8in_path () {
9	local OLD_IFS="$IFS"
10	IFS=":"
11	for dir in $PATH; do
12		if [ -x "$dir/$1" ]; then
13			IFS="$OLD_IFS"
14			return 0
15		fi
16	done
17	IFS="$OLD_IFS"
18	return 1
19}
20
21############################################################### interaction
22
23error () {
24	# <error code> <name> <string> <args>
25	local err name fmt
26	err="$1"
27	name="$2"
28	fmt="$3"
29	shift; shift; shift
30	if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
31		(echo "E: $name"
32		for x in "$@"; do echo "EA: $x"; done
33		echo "EF: $fmt") >&4
34	else
35		(printf "E: $fmt\n" "$@") >&4
36	fi
37	exit "$err"
38}
39
40warning () {
41	# <name> <string> <args>
42	local name fmt
43	name="$1"
44	fmt="$2"
45	shift; shift
46	if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
47		(echo "W: $name"
48		for x in "$@"; do echo "WA: $x"; done
49		echo "WF: $fmt") >&4
50	else
51		printf "W: $fmt\n" "$@" >&4
52	fi
53}
54
55info () {
56	# <name> <string> <args>
57	local name fmt
58	name="$1"
59	fmt="$2"
60	shift; shift
61	if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
62		(echo "I: $name"
63		for x in "$@"; do echo "IA: $x"; done
64		echo "IF: $fmt") >&4
65	else
66		printf "I: $fmt\n" "$@" >&4
67	fi
68}
69
70PROGRESS_NOW=0
71PROGRESS_END=0
72PROGRESS_NEXT=""
73PROGRESS_WHAT=""
74
75progress_next () {
76	PROGRESS_NEXT="$1"
77}
78
79wgetprogress () {
80	[ ! "$VERBOSE" ] && NVSWITCH="-nv"
81	local ret=0
82	if [ "$USE_DEBIANINSTALLER_INTERACTION" ] && [ "$PROGRESS_NEXT" ]; then
83		# The exit status of a pipeline is that of the last command in
84		# the pipeline, so wget's exit status must be saved in the
85		# pipeline's first command.  Since commands in a pipeline run in
86		# subshells, we have to print the exit status (on a file
87		# descriptor other than standard output, which is used by the
88		# pipeline itself) and then assign it to $ret outside of the
89		# pipeline.  The "||" is necessary due to "set -e"; otherwise, a
90		# non-zero exit status would cause the echo command to be
91		# skipped.  If wget succeeds, $ret will be "", so it then has to
92		# be set to a default value of 0.
93		ret=$({ { wget $@ 2>&1 >/dev/null || echo $? >&2; } | "$PKGDETAILS" "WGET%" "$PROGRESS_NOW" "$PROGRESS_NEXT" "$PROGRESS_END" >&3; } 2>&1)
94		: ${ret:=0}
95	else
96		wget ${NVSWITCH:+"$NVSWITCH"} "$@"
97		ret=$?
98	fi
99	return $ret
100}
101
102progress () {
103	# <now> <end> <name> <string> <args>
104	local now end name fmt
105	now="$1"
106	end="$2"
107	name="$3"
108	fmt="$4"
109	shift; shift; shift; shift
110	if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
111		PROGRESS_NOW="$now"
112		PROGRESS_END="$end"
113		PROGRESS_NEXT=""
114		(echo "P: $now $end $name"
115		for x in "$@"; do echo "PA: $x"; done
116		echo "PF: $fmt") >&3
117	fi
118}
119
120dpkg_progress () {
121	# <now> <end> <name> <desc> UNPACKING|CONFIGURING
122	local now end name desc action expect
123	now="$1"
124	end="$2"
125	name="$3"
126	desc="$4"
127	action="$5"
128	expect=""
129
130	if [ "$action" = UNPACKING ]; then
131		expect=half-installed
132	elif [ "$action" = CONFIGURING ]; then
133		expect=half-configured
134	fi
135
136	dp () {
137		now=$(($now + ${1:-1}))
138	}
139
140	exitcode=0
141	while read status pkg qstate; do
142		if [ "$status" = "EXITCODE" ]; then
143			exitcode="$pkg"
144			continue
145		fi
146		[ "$qstate" = "$expect" ] || continue
147		case $qstate in
148		    half-installed)
149			dp; progress "$now" "$end" "$name" "$desc"
150			info "$action" "Unpacking %s..." "${pkg%:}"
151			expect="unpacked"
152			;;
153		    unpacked)
154			expect="half-installed"
155			;;
156		    half-configured)
157			dp; progress "$now" "$end" "$name" "$desc"
158			info "$action" "Configuring %s..." "${pkg%:}"
159			expect="installed"
160			;;
161		    installed)
162			expect="half-configured"
163			;;
164		esac
165	done
166	return "$exitcode"
167}
168
169############################################################# set variables
170
171default_mirror () {
172	DEF_MIRROR="$1"
173}
174
175FINDDEBS_NEEDS_INDICES="false"
176finddebs_style () {
177	case "$1" in
178	    hardcoded)
179		;;
180	    from-indices)
181		FINDDEBS_NEEDS_INDICES="true"
182		;;
183	    *)
184		error 1 BADFINDDEBS "unknown finddebs style"
185		;;
186	 esac
187}
188
189mk_download_dirs () {
190	if [ "$DLDEST" = "apt_dest" ]; then
191		mkdir -p "$TARGET/$APTSTATE/lists/partial"
192		mkdir -p "$TARGET/var/cache/apt/archives/partial"
193	fi
194}
195
196download_style () {
197	case "$1" in
198	    apt)
199		if [ "$2" = "var-state" ]; then
200			APTSTATE="var/state/apt"
201		else
202			APTSTATE="var/lib/apt"
203		fi
204		DLDEST="apt_dest"
205		export APTSTATE DLDEST DEBFOR
206		;;
207	    *)
208		error 1 BADDLOAD "unknown download style"
209		;;
210	esac
211}
212
213keyring () {
214	# avoid unnecessary warning with --second-stage
215	if [ -z "$KEYRING" ] && [ "$SECOND_STAGE_ONLY" != true ]; then
216		if [ -e "$1" ]; then
217			KEYRING="$1"
218		elif [ -z "$DISABLE_KEYRING" ]; then
219			if [ -n "$DEF_HTTPS_MIRROR" ] && [ -z "$USER_MIRROR" ] && [ -z "$FORCE_KEYRING" ]; then
220				info KEYRING "Keyring file not available at %s; switching to https mirror %s" "$1" "$DEF_HTTPS_MIRROR"
221				USER_MIRROR="$DEF_HTTPS_MIRROR"
222			else
223				warning KEYRING "Cannot check Release signature; keyring file not available %s" "$1"
224				if [ -n "$FORCE_KEYRING" ]; then
225					error 1 KEYRING "Keyring-based check was requested; aborting accordingly"
226				fi
227			fi
228		fi
229	fi
230}
231
232detect_container () {
233	if [ "$container" = lxc ]; then
234		CONTAINER="lxc"
235	elif /usr/local/bin/grep -qs container=lxc-libvirt /proc/1/environ; then
236		CONTAINER="lxc-libvirt"
237	elif /usr/local/bin/grep -qs ^systemd-nspawn$ /run/systemd/container || /usr/local/bin/grep -qs systemd-nspawn /proc/1/environ || [ "$container" = "systemd-nspawn" ]; then
238		CONTAINER="systemd-nspawn"
239	elif /usr/local/bin/grep -qs '[[:space:]]/docker/.*/sys/fs/cgroup' /proc/1/mountinfo; then
240		CONTAINER="docker"
241	else
242		CONTAINER=""
243	fi
244}
245
246########################################################## variant handling
247
248doing_variant () {
249	if [ "$1" = "$VARIANT" ]; then return 0; fi
250	if [ "$1" = "-" ] && [ "$VARIANT" = "" ]; then return 0; fi
251	return 1
252}
253
254SUPPORTED_VARIANTS="-"
255variants () {
256	SUPPORTED_VARIANTS="$*"
257	for v in $*; do
258		if doing_variant "$v"; then return 0; fi
259	done
260	error 1 UNSUPPVARIANT "unsupported variant"
261}
262
263########################################################### option handling
264check_conflicting_option () {
265	if ( [ "$set_what_to_do" = --foreign ] && [ "${1%%=*}" = --unpack-tarball ] ) || \
266	   ( [ "${set_what_to_do%%=*}" = "--unpack-tarball" ] && [ "$1" = --foreign ] ); then
267		LOOSEN_CONFLICTING_RESTRICTION="true"
268	elif [ -n "$set_what_to_do" ]; then
269		error 1 ARG_CONFLICTING "$set_what_to_do is specified with $1, please use only one of those options."
270	fi
271	set_what_to_do="$1"
272}
273
274################################################# work out names for things
275
276mirror_style () {
277	case "$1" in
278	    release)
279		DOWNLOAD_INDICES="download_release_indices"
280		DOWNLOAD_DEBS="download_release"
281		;;
282	    main)
283		DOWNLOAD_INDICES="download_main_indices"
284		DOWNLOAD_DEBS="download_main"
285		;;
286	    *)
287		error 1 BADMIRROR "unknown mirror style"
288		;;
289	esac
290	export DOWNLOAD_INDICES
291	export DOWNLOAD_DEBS
292}
293
294force_md5 () {
295	DEBOOTSTRAP_CHECKSUM_FIELD=MD5SUM
296	export DEBOOTSTRAP_CHECKSUM_FIELD
297}
298
299verify_checksum () {
300	# args: dest checksum size
301	local expchecksum="$2"
302	local expsize="$3"
303	if [ "$DEBOOTSTRAP_CHECKSUM_FIELD" = "MD5SUM" ]; then
304		if in_path md5sum; then
305			relchecksum=$(md5sum < "$1" | sed 's/ .*$//')
306		elif in_path md5; then
307			relchecksum=$(md5 < "$1")
308		else
309			error 1 SIGCHECK "Cannot check md5sum"
310		fi
311	else
312		if in_path "sha${SHA_SIZE}sum"; then
313			relchecksum="$(sha${SHA_SIZE}sum < "$1" | sed 's/ .*$//')"
314		elif in_path "sha${SHA_SIZE}"; then
315			relchecksum="$(sha${SHA_SIZE} < "$1")"
316		else
317			error 1 SIGCHECK "Cannot check sha${SHA_SIZE}sum"
318		fi
319	fi
320	relsize="$(wc -c < "$1")"
321	if [ "$expsize" -ne "$relsize" ] || [ "$expchecksum" != "$relchecksum" ]; then
322		return 1
323	fi
324	return 0
325}
326
327get () {
328	# args: from dest 'nocache'
329	# args: from dest [checksum size] [alt {checksum size type}]
330	# args: from dest 'byhash' [checksum size] [alt {checksum size type}]
331	local displayname
332	local versionname
333	local from_base
334	local dest_base
335	local nocache=""
336	local byhash=""
337	from_base="$1"; shift
338	dest_base="$1"; shift
339	if [ "$1" = "nocache" ]; then
340		nocache="true"; shift
341	elif [ "$1" = "byhash" ]; then
342		byhash="true"; shift
343	fi
344	if [ "${dest_base%.deb}" != "$dest_base" ]; then
345		displayname="$(echo "$dest_base" | sed 's,^.*/,,;s,_.*$,,')"
346		versionname="$(echo "$dest_base" | sed 's,^.*/,,' | cut -d_ -f2 | sed 's/%3a/:/')"
347	else
348		displayname="$(echo "$from_base" | sed 's,^.*/,,')"
349	fi
350
351	if [ -e "$dest_base" ]; then
352		if [ -z "$1" ]; then
353			return 0
354		elif [ -n "$nocache" ]; then
355			rm -f "$dest_base"
356		else
357			info VALIDATING "Validating %s %s" "$displayname" "$versionname"
358			if verify_checksum "$dest_base" "$1" "$2"; then
359				return 0
360			else
361				rm -f "$dest_base"
362			fi
363		fi
364	fi
365
366	if [ "$#" -gt 3 ]; then
367		local st=1
368		if [ "$3" = "-" ]; then st=4; fi
369		local order="$(a=$st; while [ "$a" -le $# ]; do eval echo \"\${$(($a+1))}\" $a;
370		a=$(($a + 3)); done | sort -n | sed 's/.* //')"
371	else
372		local order=1
373	fi
374	for a in $order; do
375		local checksum siz typ from dest iters
376		checksum="$(eval echo \${$a})"
377		siz="$(eval echo \${$(( $a+1 ))})"
378		typ="$(eval echo \${$(( $a+2 ))})"
379		iters="0"
380
381		case "$typ" in
382		    xz)  from="$from_base.xz"; dest="$dest_base.xz" ;;
383		    bz2) from="$from_base.bz2"; dest="$dest_base.bz2" ;;
384		    gz)  from="$from_base.gz"; dest="$dest_base.gz" ;;
385		    *)   from="$from_base"; dest="$dest_base" ;;
386		esac
387
388		if [ ! -z "$CACHE_DIR" ]; then
389			dest="${dest%%*/}"
390		elif [ "${dest#/}" = "$dest" ]; then
391			dest="./$dest"
392		fi
393		local dest2="$dest"
394		if [ -d "${dest2%/*}/partial" ]; then
395			dest2="${dest2%/*}/partial/${dest2##*/}"
396		fi
397
398		while [ "$iters" -lt 10 ]; do
399			local from2=""
400			info RETRIEVING "Retrieving %s %s" "$displayname" "$versionname"
401			if [ "$checksum" != "" ] && [ "$byhash" != "" ]; then
402				# assume we don't mix acquire-by-hash and md5
403				from2="$(dirname "$from")/by-hash/SHA${SHA_SIZE}/$checksum"
404			fi
405			if [ ! -e "$dest2" ]; then
406				if [ -z "$from2" ] || ! just_get "$from2" "$dest2"; then
407					if ! just_get "$from" "$dest2"; then continue 2; fi
408				fi
409			fi
410			if [ "$checksum" != "" ]; then
411				info VALIDATING "Validating %s %s" "$displayname" "$versionname"
412				if verify_checksum "$dest2" "$checksum" "$siz"; then
413					checksum=""
414				fi
415			fi
416			if [ -z "$checksum" ]; then
417				[ "$dest2" = "$dest" ] || mv "$dest2" "$dest"
418				case "$typ" in
419				    gz)  gunzip "$dest" ;;
420				    bz2) bunzip2 "$dest" ;;
421				    xz)  unxz "$dest" ;;
422				esac
423				return 0
424			else
425				rm -f "$dest2"
426				warning RETRYING "Retrying failed download of %s" "$from"
427				iters=$(($iters + 1))
428			fi
429		done
430		warning CORRUPTFILE "%s was corrupt" "$from"
431	done
432	return 1
433}
434
435just_get () {
436	# args: from dest
437	local from="$1"
438	local dest="$2"
439	mkdir -p "${dest%/*}"
440	if [ "${from#null:}" != "$from" ]; then
441		error 1 NOTPREDL "%s was not pre-downloaded" "${from#null:}"
442	elif [ "${from#http://}" != "$from" ] || [ "${from#https://}" != "$from" ] || [ "${from#ftp://}" != "$from" ]; then
443		# http/https/ftp mirror
444		if wgetprogress ${CHECKCERTIF:+"$CHECKCERTIF"} ${CERTIFICATE:+"$CERTIFICATE"} ${PRIVATEKEY:+"$PRIVATEKEY"} -O "$dest" "$from"; then
445			return 0
446		else
447			rm -f "$dest"
448			return 1
449		fi
450	elif [ "${from#file:}" != "$from" ]; then
451		local base="${from#file:}"
452		if [ "${base#//}" != "$base" ]; then
453			base="/${from#file://*/}"
454		fi
455		if [ -e "$base" ]; then
456			cp "$base" "$dest"
457			return 0
458		else
459			return 1
460		fi
461	elif [ "${from#ssh:}" != "$from" ]; then
462		local ssh_dest="$(echo "$from" | sed -e 's#ssh://##' -e 's#/#:/#')"
463		if [ -n "$ssh_dest" ]; then
464			scp "$ssh_dest" "$dest"
465			return 0
466		else
467			return 1
468		fi
469	else
470		error 1 UNKNOWNLOC "unknown location %s" "$from"
471	fi
472}
473
474download () {
475	mk_download_dirs
476	"$DOWNLOAD_DEBS" "$(echo "$@" | tr ' ' '\n' | sort)"
477}
478
479download_indices () {
480	mk_download_dirs
481	"$DOWNLOAD_INDICES" "$(echo "$@" | tr ' ' '\n' | sort)"
482}
483
484debfor () {
485	(while read pkg path; do
486		for p in "$@"; do
487			[ "$p" = "$pkg" ] || continue;
488			echo "$path"
489		done
490	 done <"$TARGET/debootstrap/debpaths"
491	)
492}
493
494apt_dest () {
495	# args:
496	#   deb package version arch mirror path
497	#   pkg suite component arch mirror path
498	#   rel suite mirror path
499	case "$1" in
500	    deb)
501		echo "/var/cache/apt/archives/${2}_${3}_${4}.deb" | sed 's/:/%3a/'
502		;;
503	    pkg)
504		local m="$5"
505		printf "%s" "$APTSTATE/lists/"
506		echo "${m}_$6" | sed -e 's,^[^:]\+://,,' -e 's/\//_/g'
507		;;
508	    rel)
509		local m="$3"
510		printf "%s" "$APTSTATE/lists/"
511		echo "${m}_$4" | sed -e 's,^[^:]\+://,,' -e 's/\//_/g'
512		;;
513	esac
514}
515
516################################################################## download
517
518get_release_checksum () {
519	local reldest path
520	reldest="$1"
521	path="$2"
522	if [ "$DEBOOTSTRAP_CHECKSUM_FIELD" = MD5SUM ]; then
523		local match="^[Mm][Dd]5[Ss][Uu][Mm]"
524	else
525		local match="^[Ss][Hh][Aa]$SHA_SIZE:"
526	fi
527	sed -n "/$match/,/^[^ ]/p" < "$reldest" | \
528		while read a b c; do
529			if [ "$c" = "$path" ]; then echo "$a $b"; fi
530		done | head -n 1
531}
532
533extract_release_components () {
534	local reldest="$1"; shift
535	TMPCOMPONENTS="$(sed -n 's/Components: *//p' "$reldest")"
536	for c in $TMPCOMPONENTS ; do
537		eval "
538		case \"\$c\" in
539		    $USE_COMPONENTS)
540			COMPONENTS=\"\$COMPONENTS \$c\"
541			;;
542		esac
543		"
544	done
545
546	if [ -z "$COMPONENTS" ]; then
547		mv "$reldest" "$reldest.malformed"
548		error 1 INVALIDREL "Invalid Release file, no valid components"
549	fi
550}
551
552CODENAME=""
553validate_suite () {
554	local reldest suite
555	reldest="$1"
556
557	CODENAME=$(sed -n "s/^Codename: *//p" "$reldest")
558	suite=$(sed -n "s/^Suite: *//p" "$reldest")
559
560	for s in $SUITE $EXTRA_SUITES; do
561		if [ "$s" = "$suite" ] || [ "$s" = "$CODENAME" ]; then
562			return 0
563		fi
564	done
565	if [ "$EXTRA_SUITES" = "" ]; then
566		error 1 WRONGSUITE "Asked to install suite %s, but got %s (codename: %s) from mirror" "$SUITE" "$suite" "$CODENAME"
567	else
568		error 1 WRONGSUITE "Asked to install suites %s %s, but got %s (codename: %s) from mirror" "$SUITE" "$EXTRA_SUITES" "$suite" "$CODENAME"
569	fi
570}
571
572split_inline_sig () {
573	local inreldest reldest relsigdest
574	inreldest="$1"
575	reldest="$2"
576	relsigdest="$3"
577
578	# Note: InRelease files are fun since one needs to remove the
579	# last newline from the PGP SIGNED MESSAGE part, while keeping
580	# the PGP SIGNATURE part intact. This shell implementation
581	# should work on most if not all systems, instead of trying to
582	# sed/tr/head, etc.
583	rm -f "$reldest" "$relsigdest"
584	nl=""
585	state="pre-begin"
586	while IFS= read -r line; do
587		case "${state}" in
588		    pre-begin)
589			if [ "x${line}" = "x-----BEGIN PGP SIGNED MESSAGE-----" ]; then
590				state="begin"
591			fi
592			;;
593		    begin)
594			if [ "x${line}" = "x" ]; then
595				state="data"
596			fi
597			;;
598		    data)
599			if [ "x${line}" = "x-----BEGIN PGP SIGNATURE-----" ]; then
600				printf "%s\n" "${line}" > "$relsigdest"
601				state="signature"
602			else
603				printf "${nl}%s" "${line}" >> "$reldest"
604				nl="\n"
605			fi
606			;;
607		    signature)
608			printf "%s\n" "${line}" >> "$relsigdest"
609			if [ "x${line}" = "x-----END PGP SIGNATURE-----" ]; then
610				break
611			fi
612		esac
613	done < "$inreldest"
614}
615
616download_release_sig () {
617	local m1 suite inreldest reldest relsigdest
618	m1="$1"
619	suite="$2"
620	inreldest="$3"
621	reldest="$4"
622	relsigdest="$5"
623
624	progress 0 100 DOWNREL "Downloading Release file"
625	progress_next 100
626	if get "$m1/dists/$suite/InRelease" "$inreldest" nocache; then
627		split_inline_sig "$inreldest" "$reldest" "$relsigdest"
628		progress 100 100 DOWNREL "Downloading Release file"
629	else
630		get "$m1/dists/$suite/Release" "$reldest" nocache ||
631			error 1 NOGETREL "Failed getting release file %s" "$m1/dists/$suite/Release"
632		progress 100 100 DOWNREL "Downloading Release file"
633	fi
634	if [ -n "$KEYRING" ] && [ -z "$DISABLE_KEYRING" ]; then
635		progress 0 100 DOWNRELSIG "Downloading Release file signature"
636		if ! [ -f "$relsigdest" ]; then
637			progress_next 50
638			get "$m1/dists/$suite/Release.gpg" "$relsigdest" nocache ||
639				error 1 NOGETRELSIG "Failed getting release signature file %s" \
640				"$m1/dists/$suite/Release.gpg"
641			progress 50 100 DOWNRELSIG "Downloading Release file signature"
642		fi
643
644		info RELEASESIG "Checking Release signature"
645		# Don't worry about the exit status from gpgv2; parsing the output will
646		# take care of that.
647		(gpgv2 --status-fd 1 --keyring "$KEYRING" --ignore-time-conflict \
648		 "$relsigdest" "$reldest" || true) | read_gpg_status
649		progress 100 100 DOWNRELSIG "Downloading Release file signature"
650	fi
651}
652
653download_release_indices () {
654	local m1 inreldest reldest relsigdest totalpkgs \
655	      subpath xzi bz2i gzi normi i ext \
656	      donepkgs pkgdest acquirebyhash
657	m1="${MIRRORS%% *}"
658	for s in $SUITE $EXTRA_SUITES; do
659		inreldest="$TARGET/$($DLDEST rel "$s" "$m1" "dists/$s/InRelease")"
660		reldest="$TARGET/$($DLDEST rel "$s" "$m1" "dists/$s/Release")"
661		relsigdest="$TARGET/$($DLDEST rel "$s" "$m1" "dists/$s/Release.gpg")"
662
663		download_release_sig "$m1" "$s" "$inreldest" "$reldest" "$relsigdest"
664
665		validate_suite "$reldest"
666
667		extract_release_components "$reldest"
668
669		acquirebyhash=$(/usr/local/bin/grep "^Acquire-By-Hash: yes$" "$reldest" || true)
670		totalpkgs=0
671		for c in $COMPONENTS; do
672			subpath="$c/binary-$ARCH/Packages"
673			xzi="$(get_release_checksum "$reldest" "$subpath.xz")"
674			bz2i="$(get_release_checksum "$reldest" "$subpath.bz2")"
675			gzi="$(get_release_checksum "$reldest" "$subpath.gz")"
676			normi="$(get_release_checksum "$reldest" "$subpath")"
677			if [ "$normi" != "" ]; then
678				i="$normi"
679			elif in_path bunzip2 && [ "$bz2i" != "" ]; then
680				i="$bz2i"
681			elif in_path unxz && [ "$xzi" != "" ]; then
682				i="$xzi"
683			elif in_path gunzip && [ "$gzi" != "" ]; then
684				i="$gzi"
685			fi
686			if [ "$i" != "" ]; then
687				totalpkgs=$(( $totalpkgs + ${i#* } ))
688			else
689				mv "$reldest" "$reldest.malformed"
690				error 1 MISSINGRELENTRY "Invalid Release file, no entry for %s" "$subpath"
691			fi
692		done
693
694		donepkgs=0
695		progress 0 $totalpkgs DOWNPKGS "Downloading Packages files"
696		for c in $COMPONENTS; do
697			subpath="$c/binary-$ARCH/Packages"
698			path="dists/$s/$subpath"
699			xzi="$(get_release_checksum "$reldest" "$subpath.xz")"
700			bz2i="$(get_release_checksum "$reldest" "$subpath.bz2")"
701			gzi="$(get_release_checksum "$reldest" "$subpath.gz")"
702			normi="$(get_release_checksum "$reldest" "$subpath")"
703			ext=""
704			if [ "$acquirebyhash" != "" ]; then
705				ext="$ext byhash"
706			fi
707			if [ "$normi" != "" ]; then
708				ext="$ext $normi ."
709				i="$normi"
710			fi
711			if in_path unxz && [ "$xzi" != "" ]; then
712				ext="$ext $xzi xz"
713				i="${i:-$xzi}"
714			fi
715			if in_path bunzip2 && [ "$bz2i" != "" ]; then
716				ext="$ext $bz2i bz2"
717				i="${i:-$bz2i}"
718			fi
719			if in_path gunzip && [ "$gzi" != "" ]; then
720				ext="$ext $gzi gz"
721				i="${i:-$gzi}"
722			fi
723			progress_next $(($donepkgs + ${i#* }))
724			for m in $MIRRORS; do
725				pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m" "$path")"
726				if get "$m/$path" "$pkgdest" $ext; then break; fi
727			done
728			if [ ! -f "$pkgdest" ]; then
729				error 1 COULDNTDL "Couldn't download %s" "$m/$path"
730			fi
731			donepkgs=$(($donepkgs + ${i#* }))
732			progress $donepkgs $totalpkgs DOWNPKGS "Downloading Packages files"
733		done
734	done
735}
736
737get_package_sizes () {
738	# mirror pkgdest debs..
739	local m pkgdest
740	m="$1"; shift
741	pkgdest="$1"; shift
742	$PKGDETAILS PKGS "$m" "$pkgdest" "$@" | (
743		newleft=""
744		totaldebs=0
745		countdebs=0
746		while read p details; do
747			if [ "$details" = "-" ]; then
748				newleft="$newleft $p"
749			else
750				size="${details##* }";
751				totaldebs=$(($totaldebs + $size))
752				countdebs=$(($countdebs + 1))
753			fi
754		done
755		echo "$countdebs $totaldebs$newleft"
756	)
757}
758
759# note, leftovers come back on fd5 !!
760download_debs () {
761	local m pkgdest debdest debcache
762	m="$1"
763	pkgdest="$2"
764	shift; shift
765
766	"$PKGDETAILS" PKGS "$m" "$pkgdest" "$@" | (
767		leftover=""
768		while read p ver arc mdup fil checksum size; do
769			if [ "$ver" = "-" ]; then
770				leftover="$leftover $p"
771			else
772				progress_next $(($dloaddebs + $size))
773				debdest="$($DLDEST deb "$p" "$ver" "$arc" "$m" "$fil")"
774				debcache="$(echo "$p"_"$ver"_"$arc".deb | sed 's/:/%3a/')"
775				if [ -z "$CACHE_DIR" ] && get "$m/$fil" "$TARGET/$debdest" "$checksum" "$size"; then
776					dloaddebs=$(($dloaddebs + $size))
777					echo >>"$TARGET/debootstrap/deburis" "$p $ver $m/$fil"
778					echo >>"$TARGET/debootstrap/debpaths" "$p $debdest"
779				elif [ -d "$CACHE_DIR" ] && get "$m/$fil" "$CACHE_DIR/$debcache" "$checksum" "$size"; then
780					dloaddebs=$(($dloaddebs + $size))
781					echo >>"$TARGET/debootstrap/deburis" "$p $ver $m/$fil"
782					echo >>"$TARGET/debootstrap/debpaths" "$p $debdest"
783					cp "$CACHE_DIR/$debcache" "$TARGET/$debdest"
784				else
785					warning COULDNTDL "Couldn't download package %s (ver %s arch %s) at %s" "$p" "$ver" "$arc" "$m/$fil"
786					leftover="$leftover $p"
787				fi
788			fi
789		done
790		echo >&5 ${leftover# }
791	)
792}
793
794download_release () {
795	local m1 numdebs countdebs totaldebs leftoverdebs path pkgdest dloaddebs
796	m1="${MIRRORS%% *}"
797
798	numdebs="$#"
799
800	countdebs=0
801	progress "$countdebs" "$numdebs" SIZEDEBS "Finding package sizes"
802
803	totaldebs=0
804	leftoverdebs="$*"
805
806	# Fix possible duplicate package names, which would screw up counts:
807	leftoverdebs=$(printf "$leftoverdebs"|tr ' ' '\n'|sort -u|tr '\n' ' ')
808	numdebs=$(printf "$leftoverdebs"|wc -w)
809
810	for s in $SUITE $EXTRA_SUITES; do
811		for c in $COMPONENTS; do
812			if [ "$countdebs" -ge "$numdebs" ]; then break; fi
813
814			path="dists/$s/$c/binary-$ARCH/Packages"
815			pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m1" "$path")"
816			if [ ! -e "$pkgdest" ]; then continue; fi
817
818			info CHECKINGSIZES "Checking component %s on %s..." "$c" "$m1"
819
820			leftoverdebs="$(get_package_sizes "$m1" "$pkgdest" $leftoverdebs)"
821
822			countdebs=$(($countdebs + ${leftoverdebs%% *}))
823			leftoverdebs=${leftoverdebs#* }
824
825			totaldebs=${leftoverdebs%% *}
826			leftoverdebs=${leftoverdebs#* }
827
828			progress "$countdebs" "$numdebs" SIZEDEBS "Finding package sizes"
829		done
830	done
831
832	if [ "$countdebs" -ne "$numdebs" ]; then
833		error 1 LEFTOVERDEBS "Couldn't find these debs: %s" "$leftoverdebs"
834	fi
835
836	dloaddebs=0
837
838	progress "$dloaddebs" "$totaldebs" DOWNDEBS "Downloading packages"
839	:>"$TARGET/debootstrap/debpaths"
840
841	pkgs_to_get="$*"
842	for s in $SUITE $EXTRA_SUITES; do
843		for c in $COMPONENTS; do
844			path="dists/$s/$c/binary-$ARCH/Packages"
845			for m in $MIRRORS; do
846				pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m" "$path")"
847				if [ ! -e "$pkgdest" ]; then continue; fi
848				pkgs_to_get="$(download_debs "$m" "$pkgdest" $pkgs_to_get 5>&1 1>&6)"
849				if [ -z "$pkgs_to_get" ]; then break; fi
850			done 6>&1
851			if [ -z "$pkgs_to_get" ]; then break; fi
852		done
853		if [ -z "$pkgs_to_get" ]; then break; fi
854	done
855	progress "$dloaddebs" "$totaldebs" DOWNDEBS "Downloading packages"
856	if [ "$pkgs_to_get" != "" ]; then
857		error 1 COULDNTDLPKGS "Couldn't download packages: %s" "$pkgs_to_get"
858	fi
859}
860
861download_main_indices () {
862	local m1 comp path pkgdest
863	m1="${MIRRORS%% *}"
864	comp="${USE_COMPONENTS}"
865	progress 0 100 DOWNMAINPKGS "Downloading Packages file"
866	progress_next 100
867
868	if [ -z "$comp" ]; then comp=main; fi
869	COMPONENTS="$(echo $comp | tr '|' ' ')"
870
871	export COMPONENTS
872	for m in $MIRRORS; do
873		for s in $SUITE $EXTRA_SUITES; do
874			for c in $COMPONENTS; do
875				path="dists/$s/$c/binary-$ARCH/Packages"
876				pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m" "$path")"
877				if in_path gunzip && get "$m/${path}.gz" "${pkgdest}.gz"; then
878					rm -f "$pkgdest"
879					gunzip "$pkgdest.gz"
880				elif get "$m/$path" "$pkgdest"; then
881					true
882				fi
883			done
884		done
885	done
886	progress 100 100 DOWNMAINPKGS "Downloading Packages file"
887}
888
889download_main () {
890	local m1 path pkgdest debdest
891	m1="${MIRRORS%% *}"
892
893	:>"$TARGET/debootstrap/debpaths"
894	for p in "$@"; do
895		for s in $SUITE $EXTRA_SUITES; do
896			for c in $COMPONENTS; do
897				local details=""
898				for m in $MIRRORS; do
899					path="dists/$s/$c/binary-$ARCH/Packages"
900					pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m" "$path")"
901					if [ ! -e "$pkgdest" ]; then continue; fi
902					details="$($PKGDETAILS PKGS "$m" "$pkgdest" "$p")"
903					if [ "$details" = "$p -" ]; then
904						details=""
905						continue
906					fi
907					size="${details##* }"; details="${details% *}"
908					checksum="${details##* }"; details="${details% *}"
909					debdest="$($DLDEST deb $details)"
910					if get "$m/${details##* }" "$TARGET/$debdest" "$checksum" "$size"; then
911						echo >>"$TARGET/debootstrap/debpaths" "$p $debdest"
912						details="done"
913						break
914					fi
915				done
916				if [ "$details" != "" ]; then
917					break
918				fi
919			done
920			if [ "$details" != "" ]; then
921				break
922			fi
923		done
924		if [ "$details" != "done" ]; then
925			error 1 COULDNTDL "Couldn't download %s" "$p"
926		fi
927	done
928}
929
930###################################################### deb choosing support
931
932get_debs () {
933	local field m1 c path pkgdest
934	field="$1"
935	shift
936	for m1 in $MIRRORS; do
937		for s in $SUITE $EXTRA_SUITES; do
938			for c in $COMPONENTS; do
939				path="dists/$s/$c/binary-$ARCH/Packages"
940				pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m1" "$path")"
941				echo "$("$PKGDETAILS" FIELD "$field" "$m1" "$pkgdest" "$@" | sed 's/ .*//')"
942			done
943		done
944	done
945}
946
947################################################################ extraction
948
949EXTRACTORS_SUPPORTED="dpkg-deb ar"
950EXTRACT_DEB_TAR_OPTIONS=
951
952# Native dpkg-deb based extractors
953extract_dpkg_deb_field () {
954	local pkg field
955	pkg="$1"
956	field="$2"
957
958	dpkg-deb -f "$pkg" "$field"
959}
960
961extract_dpkg_deb_data () {
962	local pkg="$1"
963
964	dpkg-deb --fsys-tarfile "$pkg" | tar $EXTRACT_DEB_TAR_OPTIONS -xf - || error 1 FILEEXIST "Tried to extract package, but file already exists. Exit..."
965}
966
967# Raw .deb extractors
968extract_ar_deb_field () {
969	local pkg field tarball
970	pkg="$1"
971	field="$2"
972	tarball=$(ar -t "$pkg" | /usr/local/bin/grep "^control\.tar")
973
974	case "$tarball" in
975		control.tar.gz) cat_cmd=zcat ;;
976		control.tar.xz) cat_cmd=xzcat ;;
977		control.tar)    cat_cmd=cat ;;
978		*) error 1 UNKNOWNCONTROLCOMP "Unknown compression type for %s in %s" "$tarball" "$pkg" ;;
979	esac
980
981	if in_path $cat_cmd; then
982		ar -p "$pkg" "$tarball" | $cat_cmd |
983		    tar -O -xf - control ./control 2>/dev/null |
984		    /usr/local/bin/grep -i "^$field:" | sed -e 's/[^:]*: *//' | head -n 1
985	else
986		error 1 UNPACKCMDUNVL "Extracting %s requires the %s command, which is not available" "$pkg" "$cat_cmd"
987	fi
988}
989
990extract_ar_deb_data () {
991	local pkg tarball
992	pkg="$1"
993	tarball="$(ar -t "$pkg" | /usr/local/bin/grep "^data.tar")"
994
995	case "$tarball" in
996		data.tar.gz)  cat_cmd=zcat ;;
997		data.tar.bz2) cat_cmd=bzcat ;;
998		data.tar.xz)  cat_cmd=xzcat ;;
999		data.tar)     cat_cmd=cat ;;
1000		*) error 1 UNKNOWNDATACOMP "Unknown compression type for %s in %s" "$tarball" "$pkg" ;;
1001	esac
1002
1003	if in_path "$cat_cmd"; then
1004		ar -p "$pkg" "$tarball" | "$cat_cmd" | tar $EXTRACT_DEB_TAR_OPTIONS -xf -
1005	else
1006		error 1 UNPACKCMDUNVL "Extracting %s requires the %s command, which is not available" "$pkg" "$cat_cmd"
1007	fi
1008}
1009
1010valid_extractor () {
1011	local extractor="$1"
1012
1013	for E in $EXTRACTORS_SUPPORTED; do
1014		if [ "$extractor" = "$E" ]; then
1015			return 0
1016		fi
1017	done
1018
1019	return 1
1020}
1021
1022choose_extractor () {
1023	local extractor
1024
1025	if [ -n "$EXTRACTOR_OVERRIDE" ]; then
1026		extractor="$EXTRACTOR_OVERRIDE"
1027	elif in_path dpkg-deb; then
1028		extractor="dpkg-deb"
1029	else
1030		extractor="ar"
1031	fi
1032
1033	info CHOSENEXTRACTOR "Chosen extractor for .deb packages: %s" "$extractor"
1034	case "$extractor" in
1035	dpkg-deb)
1036		extract_deb_field () { extract_dpkg_deb_field "$@"; }
1037		extract_deb_data () { extract_dpkg_deb_data "$@"; }
1038		;;
1039	ar)
1040		extract_deb_field () { extract_ar_deb_field "$@"; }
1041		extract_deb_data () { extract_ar_deb_data "$@"; }
1042		;;
1043	esac
1044}
1045
1046extract () { (
1047	cd "$TARGET" || exit 1
1048	local p cat_cmd
1049	p=0
1050	for pkg in $(debfor "$@"); do
1051		p=$(($p + 1))
1052		progress "$p" "$#" EXTRACTPKGS "Extracting packages"
1053		packagename="$(echo "$pkg" | sed 's,^.*/,,;s,_.*$,,')"
1054		info EXTRACTING "Extracting %s..." "$packagename"
1055		extract_deb_data "./$pkg"
1056	done
1057); }
1058
1059in_target_nofail () {
1060	if ! PATH=/sbin:/usr/sbin:/bin:/usr/bin eval "$CHROOT_CMD \"\$@\"" 2>/dev/null; then
1061		true
1062	fi
1063	return 0
1064}
1065
1066in_target_failmsg () {
1067	local code msg arg
1068	code="$1"
1069	msg="$2"
1070	arg="$3"
1071	shift; shift; shift
1072	if ! PATH=/sbin:/usr/sbin:/bin:/usr/bin eval "$CHROOT_CMD \"\$@\""; then
1073		warning "$code" "$msg" "$arg"
1074		# Try to point user at actual failing package.
1075		msg="See %s for details"
1076		if [ -e "$TARGET/debootstrap/debootstrap.log" ]; then
1077			arg="$TARGET/debootstrap/debootstrap.log"
1078			local pkg="$(/usr/local/bin/grep '^dpkg: error processing ' "$TARGET/debootstrap/debootstrap.log" | head -n 1 | sed 's/\(error processing \)\(package \|archive \)/\1/' | cut -d ' ' -f 4)"
1079			if [ -n "$pkg" ]; then
1080				msg="$msg (possibly the package $pkg is at fault)"
1081			fi
1082		else
1083			arg="the log"
1084		fi
1085		warning "$code" "$msg" "$arg"
1086		return 1
1087	fi
1088	return 0
1089}
1090
1091in_target () {
1092	in_target_failmsg IN_TARGET_FAIL "Failure trying to run: %s" "$CHROOT_CMD $*" "$@"
1093}
1094
1095###################################################### standard setup stuff
1096
1097conditional_cp () {
1098	if [ ! -e "$2/$1" ]; then
1099		if [ -L "$1" ] && [ -e "$1" ]; then
1100			cat "$1" >"$2/$1"
1101		elif [ -e "$1" ]; then
1102			cp "$1" "$2/$1"
1103		fi
1104	fi
1105}
1106
1107
1108setup_apt_sources () {
1109	mkdir -p "$TARGET/etc/apt"
1110	for m in "$@"; do
1111		for s in $SUITE $EXTRA_SUITES; do
1112			local cs c path pkgdest
1113			for c in ${COMPONENTS:-$USE_COMPONENTS}; do
1114				path="dists/$s/$c/binary-$ARCH/Packages"
1115				pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m" "$path")"
1116				if [ -e "$pkgdest" ]; then cs="$cs $c"; fi
1117			done
1118			if [ "$cs" != "" ]; then echo "deb $m $s$cs"; fi
1119		done
1120	done > "$TARGET/etc/apt/sources.list"
1121}
1122
1123setup_etc () {
1124	mkdir -p "$TARGET/etc"
1125
1126	conditional_cp /etc/resolv.conf "$TARGET"
1127	conditional_cp /etc/hostname "$TARGET"
1128}
1129
1130UMOUNT_DIRS=
1131
1132umount_exit_function () {
1133	local realdir
1134	for dir in $UMOUNT_DIRS; do
1135		realdir="$(in_target_nofail readlink -f "$dir")"
1136		[ "$realdir" ] || continue
1137		( cd / ; umount "$TARGET/${realdir#/}" ) || true
1138	done
1139}
1140
1141umount_on_exit () {
1142	if [ "$UMOUNT_DIRS" ]; then
1143		UMOUNT_DIRS="$1 $UMOUNT_DIRS"
1144	else
1145		UMOUNT_DIRS="$1"
1146		on_exit umount_exit_function
1147	fi
1148}
1149
1150clear_mtab () {
1151	if [ -f "$TARGET/etc/mtab" ] && [ ! -h "$TARGET/etc/mtab" ]; then
1152		rm -f "$TARGET/etc/mtab"
1153	fi
1154}
1155
1156setup_proc () {
1157	case "$HOST_OS" in
1158	    *freebsd*)
1159		umount_on_exit /dev
1160		umount_on_exit /dev/fd
1161		umount_on_exit /proc
1162		umount "$TARGET/proc" 2>/dev/null || true
1163		if [ "$HOST_OS" = kfreebsd ]; then
1164			in_target mount -t linprocfs proc /proc
1165		else
1166			mount -t linprocfs proc "$TARGET/proc"
1167		fi
1168		;;
1169	    hurd*)
1170		# firmlink $TARGET/{dev,servers,proc} to the system ones.
1171		settrans -a "$TARGET/dev" /hurd/firmlink /dev
1172		settrans -a "$TARGET/servers" /hurd/firmlink /servers
1173		settrans -a "$TARGET/proc" /hurd/firmlink /proc
1174		;;
1175	    *)
1176		umount_on_exit /dev/pts
1177		umount_on_exit /dev/shm
1178		umount_on_exit /proc
1179		umount_on_exit /proc/bus/usb
1180		umount "$TARGET/proc" 2>/dev/null || true
1181
1182		# some container environment are used at second-stage, it already treats /proc and so on
1183		if [ -z "$(ls -A "$TARGET/proc")" ]; then
1184			# second-stage in docker, we cannot detect it is inside docker... just ignore warning
1185			in_target mount -t proc proc /proc || true
1186			umount_on_exit /proc
1187		fi
1188		if [ -n "$(ls -A "$TARGET/sys")" ] && \
1189			/usr/local/bin/grep -qs '[[:space:]]sysfs' "$TARGET/proc/filesystems" || \
1190                   [ "$CONTAINER" = "docker" ]; then
1191				umount_on_exit /sys
1192				umount "$TARGET/sys" 2>/dev/null || true
1193		else
1194			# second-stage in docker, we cannot detect it is inside docker... just ignore warning
1195			in_target mount -t sysfs sysfs /sys || true
1196			umount_on_exit /sys
1197		fi
1198		on_exit clear_mtab
1199		;;
1200	esac
1201	umount_on_exit /lib/init/rw
1202}
1203
1204setup_proc_symlink () {
1205	rm -rf "$TARGET/proc"
1206	ln -s /proc "$TARGET"
1207}
1208
1209# create the static device nodes
1210setup_devices () {
1211	if doing_variant fakechroot; then
1212		setup_devices_fakechroot
1213		return 0
1214	fi
1215
1216	case "$HOST_OS" in
1217	    kfreebsd*)
1218		;;
1219	    freebsd)
1220		;;
1221	    hurd*)
1222		;;
1223	    *)
1224		if [ "$CONTAINER" = "lxc" ] || [ "$CONTAINER" = "lxc-libvirt" ]; then
1225			if ! setup_devices_simple; then
1226				setup_devices_bind
1227			fi
1228			return 0
1229		fi
1230
1231		setup_devices_simple
1232		;;
1233	esac
1234}
1235
1236# enable the dynamic device nodes
1237setup_dynamic_devices () {
1238	if doing_variant fakechroot; then
1239		return 0
1240	fi
1241
1242	case "$HOST_OS" in
1243	    kfreebsd*)
1244		in_target mount -t devfs devfs /dev ;;
1245	    freebsd)
1246		mount -t devfs devfs "$TARGET/dev"
1247		mount -t fdescfs -o linrdlnk fdescfs "$TARGET/dev/fd" ;;
1248	    hurd*)
1249	        # Use the setup-translators of the hurd package
1250	        in_target /usr/lib/hurd/setup-translators -k ;;
1251	esac
1252}
1253
1254# Create a device node if it does not exist. By default, the mode is 666.
1255mknod_if_needed () {
1256	local device type major minor mode
1257	device="$1"
1258	type="$2"
1259	major="$3"
1260	minor="$4"
1261	mode="${5:-666}"
1262
1263	if [ ! -e "$device" ]; then
1264		mknod -m "$mode" "$device" "$type" "$major" "$minor"
1265	fi
1266}
1267
1268
1269setup_devices_simple () {
1270	# The list of devices that can be created in a container comes from
1271	# src/core/cgroup.c in the systemd source tree.
1272	mknod_if_needed "$TARGET/dev/null"        c 1 3
1273	mknod_if_needed "$TARGET/dev/zero"        c 1 5
1274	mknod_if_needed "$TARGET/dev/full"        c 1 7
1275	mknod_if_needed "$TARGET/dev/random"      c 1 8
1276	mknod_if_needed "$TARGET/dev/urandom"     c 1 9
1277	mknod_if_needed "$TARGET/dev/tty"         c 5 0
1278	if [ ! "$CONTAINER" = "systemd-nspawn" ]; then
1279		mknod_if_needed "$TARGET/dev/console"     c 5 1
1280	fi
1281	# To avoid pre-exist directory causes error, specify "-p" option
1282        mkdir -p "$TARGET/dev/pts/" "$TARGET/dev/shm/"
1283	# Inside a container, we might not be allowed to create /dev/ptmx.
1284	# If not, do the next best thing.
1285	if ! mknod_if_needed "$TARGET/dev/ptmx"  c 5 2; then
1286		warning MKNOD "Could not create /dev/ptmx, falling back to symlink. This chroot will require /dev/pts mounted with ptmxmode=666"
1287		ln -sf pts/ptmx "$TARGET/dev/ptmx"
1288	fi
1289	ln -sf /proc/self/fd   "$TARGET/dev/fd"
1290	ln -sf /proc/self/fd/0 "$TARGET/dev/stdin"
1291	ln -sf /proc/self/fd/1 "$TARGET/dev/stdout"
1292	ln -sf /proc/self/fd/2 "$TARGET/dev/stderr"
1293}
1294
1295setup_devices_fakechroot () {
1296	rm -rf "$TARGET/dev"
1297	ln -s /dev "$TARGET"
1298}
1299
1300setup_devices_bind () {
1301	mount -t tmpfs nodev "$TARGET/dev"
1302	umount_on_exit /dev
1303	for device in null zero full random urandom tty pts shm ptmx; do
1304		if [ -d "/dev/$device" ]; then
1305			mkdir "$TARGET/dev/$device"
1306		elif [ -c "/dev/$device" ]; then
1307			touch "$TARGET/dev/$device"
1308		else
1309			continue
1310		fi
1311		mount -o bind "/dev/$device" "$TARGET/dev/$device"
1312		umount_on_exit "/dev/$device"
1313	done
1314	ln -s /proc/self/fd   "$TARGET/dev/fd"
1315	ln -s /proc/self/fd/0 "$TARGET/dev/stdin"
1316	ln -s /proc/self/fd/1 "$TARGET/dev/stdout"
1317	ln -s /proc/self/fd/2 "$TARGET/dev/stderr"
1318}
1319
1320setup_dselect_method () {
1321	case "$1" in
1322	    apt)
1323		mkdir -p "$TARGET/var/lib/dpkg"
1324		echo "apt apt" > "$TARGET/var/lib/dpkg/cmethopt"
1325		chmod 644 "$TARGET/var/lib/dpkg/cmethopt"
1326		;;
1327	    *)
1328		error 1 UNKNOWNDSELECT "unknown dselect method"
1329		;;
1330	esac
1331}
1332
1333# Find out where the runtime dynamic linker and the shared libraries
1334# can be installed on each architecture: native, multilib and multiarch.
1335# This data can be verified by checking the files in the debian/sysdeps/
1336# directory of the glibc package.
1337#
1338# This function must be updated to support any new architecture which
1339# either installs the RTLD in a directory different from /lib or builds
1340# multilib library packages.
1341setup_merged_usr() {
1342	if doing_variant buildd && [ -z "$MERGED_USR" ]; then
1343		MERGED_USR="no"
1344	fi
1345
1346	if [ "$MERGED_USR" = "no" ]; then return 0; fi
1347
1348	local link_dir
1349	case $ARCH in
1350	    hurd-*)	return 0 ;;
1351	    amd64)	link_dir="lib32 lib64 libx32" ;;
1352	    i386)	link_dir="lib64 libx32" ;;
1353	    mips|mipsel)
1354			link_dir="lib32 lib64" ;;
1355	    mips64*|mipsn32*)
1356			link_dir="lib32 lib64 libo32" ;;
1357	    powerpc)	link_dir="lib64" ;;
1358	    ppc64)	link_dir="lib32 lib64" ;;
1359	    ppc64el)	link_dir="lib64" ;;
1360	    s390x)	link_dir="lib32" ;;
1361	    sparc)	link_dir="lib64" ;;
1362	    sparc64)	link_dir="lib32 lib64" ;;
1363	    x32)	link_dir="lib32 lib64 libx32" ;;
1364	esac
1365	link_dir="bin sbin lib $link_dir"
1366
1367	local dir
1368	for dir in $link_dir; do
1369		ln -s usr/"$dir" "$TARGET/$dir"
1370		mkdir -p "$TARGET/usr/$dir"
1371	done
1372}
1373
1374################################################################ pkgdetails
1375
1376# NOTE
1377# For the debootstrap udeb, pkgdetails is provided by the bootstrap-base
1378# udeb, so the pkgdetails API needs to be kept in sync with that.
1379
1380if in_path perl; then
1381	PKGDETAILS=pkgdetails_perl
1382
1383	# test if /usr/local/bin/grep supports --perl-regexp
1384	set +e
1385	echo x | /usr/local/bin/grep --perl-regexp . >/dev/null 2>&1
1386	if [ $? -eq 2 ]; then
1387	    gropt=-E
1388        else
1389	    gropt=--perl-regexp
1390	fi
1391	set -e
1392
1393	pkgdetails_field () {
1394		# uniq field mirror Packages values...
1395		perl -le '
1396$unique = shift @ARGV; $field = lc(shift @ARGV); $mirror = shift @ARGV;
1397%fields = map { $_, 0 } @ARGV;
1398$prevpkg = "";
1399$chksumfield = lc($ENV{DEBOOTSTRAP_CHECKSUM_FIELD}).":";
1400while (<STDIN>) {
1401	if (/^([^:]*:)\s*(.*)$/) {
1402		$f = lc($1); $v = $2;
1403		if ($f eq "package:") {
1404			$last = 0;
1405			$pkg = $v;
1406			if ($pkg ne $prevpkg) {
1407				print $output if defined $output;
1408				if ($unique && defined $output_val) {
1409					delete $fields{$output_val};
1410					$last = 1 unless keys %fields;
1411				}
1412				$prevpkg = $pkg;
1413			}
1414			undef $output;
1415			undef $output_val;
1416			last if $last;
1417		}
1418		$ver = $v if ($f eq "version:");
1419		$arc = $v if ($f eq "architecture:");
1420		$fil = $v if ($f eq "filename:");
1421		$chk = $v if ($f eq $chksumfield);
1422		$siz = $v if ($f eq "size:");
1423		$val = $v if ($f eq $field);
1424	} elsif (/^$/) {
1425		if (defined $val && defined $fields{$val}) {
1426			$output = sprintf "%s %s %s %s %s %s %s",
1427			 $pkg, $ver, $arc, $mirror, $fil, $chk, $siz;
1428			$output_val = $val;
1429		}
1430		undef $val;
1431	}
1432}
1433print $output if defined $output;
1434delete $fields{$output_val} if $unique && defined $output_val;
1435for $v (keys %fields) {
1436	printf ("%s -\n", $v) if ($unique);
1437}
1438' "$@"
1439	}
1440
1441	pkgdetails_perl () {
1442		if [ "$1" = "WGET%" ]; then
1443			shift;
1444			perl -e '
1445$v = 0;
1446$allow_percentage = 0;
1447while (read STDIN, $x, 1) {
1448	if ($x =~ m/\s/) {
1449		$allow_percentage = 1;
1450	} elsif ($allow_percentage and $x =~ m/\d/) {
1451		$v *= 10;
1452		$v += $x;
1453	} elsif ($allow_percentage and $x eq "%") {
1454		printf "P: %d %d%s\n", int($v / 100.0 * ($ARGV[1] - $ARGV[0]) + $ARGV[0]), $ARGV[2], ($#ARGV == 3 ? " $ARGV[3]" : "");
1455		$v = 0;
1456	} else {
1457		$v = 0;
1458		$allow_percentage = 0;
1459	}
1460}' "$@"
1461		elif [ "$1" = "GETDEPS" ]; then
1462			local pkgdest="$2"; shift; shift
1463LC_ALL=C /usr/local/bin/grep "$gropt" '^$|^Package:|^Depends:|^Pre-Depends:'  $pkgdest | perl -e '
1464%seen = map { $_ => 1 } @ARGV;
1465while (<STDIN>) {
1466	if (/^Package: (.*)$/) {
1467		$pkg = $1;
1468		next;
1469	} elsif (/^$/) {
1470                $in = 0;
1471                next;
1472        }
1473	$in = 1 if $seen{$pkg};
1474	if ($in and (/^Depends: (.*)$/ or /^Pre-Depends: (.*)$/)) {
1475		for $d (split /\s*,\s*/, $1) {
1476			$d =~ s/\s*[|].*$//;
1477			$d =~ s/\s*[(].*[)]\s*//;
1478			$d =~ s/:.*//;
1479			$depends{$d} = 1;
1480		}
1481	}
1482}
1483	foreach (sort keys %depends) {
1484	  print "$_\n";
1485	}
1486' "$@"
1487		elif [ "$1" = "PKGS" ]; then
1488			local m="$2"
1489			local p="$3"
1490			shift; shift; shift
1491			LC_ALL=C /usr/local/bin/grep "$gropt" '^$|^Architecture:|^Filename:|^MD5sum:|^Package:|^Priority:|^SHA256:|^Size:|^Version:|^Depends:|^Pre-Depends:' "$p" | pkgdetails_field 1 Package: "$m" "$@"
1492		elif [ "$1" = "FIELD" ]; then
1493			local f="$2"
1494			local m="$3"
1495			local p="$4"
1496			shift; shift; shift; shift
1497			LC_ALL=C /usr/local/bin/grep "$gropt" '^$|^Package:|^Priority:' "$p" | pkgdetails_field 0 "$f" "$m" "$@"
1498		elif [ "$1" = "STANZAS" ]; then
1499			local pkgdest="$2"; shift; shift
1500			perl -e '
1501my $accum = "";
1502%seen = map { $_ => 1 } @ARGV;
1503while (<STDIN>) {
1504	$accum .= $_;
1505	$in = 1 if (/^Package: (.*)$/ && $seen{$1});
1506	if ($in and /^$/) {
1507		print $accum;
1508		if (substr($accum, -1) != "\n") {
1509			print "\n\n";
1510		} elsif (substr($accum, -2, 1) != "\n") {
1511			print "\n";
1512		}
1513		$in = 0;
1514	}
1515	$accum = "" if /^$/;
1516}' <"$pkgdest" "$@"
1517		fi
1518	}
1519elif [ -e "/usr/lib/debootstrap/pkgdetails" ]; then
1520	PKGDETAILS="/usr/lib/debootstrap/pkgdetails"
1521elif [ -e "$DEBOOTSTRAP_DIR/pkgdetails" ]; then
1522	PKGDETAILS="$DEBOOTSTRAP_DIR/pkgdetails"
1523else
1524	PKGDETAILS=""
1525fi
1526
1527##################################################### dependency resolution
1528
1529resolve_deps () {
1530	local m1="${MIRRORS%% *}"
1531
1532	local PKGS="$*"
1533	local ALLPKGS="$PKGS";
1534	local ALLPKGS2="";
1535	while [ "$PKGS" != "" ]; do
1536		local NEWPKGS=""
1537		for s in $SUITE $EXTRA_SUITES; do
1538			for c in ${COMPONENTS:-$(echo ${USE_COMPONENTS} | tr '|' ' ')}; do
1539				local path="dists/$s/$c/binary-$ARCH/Packages"
1540				local pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m1" "$path")"
1541				NEWPKGS="$NEWPKGS $("$PKGDETAILS" GETDEPS "$pkgdest" $PKGS)"
1542			done
1543		done
1544		PKGS=$(echo "$PKGS $NEWPKGS" | tr ' ' '\n' | sort | uniq)
1545		local REALPKGS=""
1546		for s in $SUITE $EXTRA_SUITES; do
1547			for c in ${COMPONENTS:-$(echo ${USE_COMPONENTS} | tr '|' ' ')}; do
1548				local path="dists/$s/$c/binary-$ARCH/Packages"
1549				local pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m1" "$path")"
1550				REALPKGS="$REALPKGS $("$PKGDETAILS" PKGS REAL "$pkgdest" $PKGS | sed -n 's/ .*REAL.*$//p')"
1551			done
1552		done
1553		PKGS="$REALPKGS"
1554		ALLPKGS2=$(echo "$PKGS $ALLPKGS" | tr ' ' '\n' | sort | uniq)
1555		PKGS=$(without "$ALLPKGS2" "$ALLPKGS")
1556		ALLPKGS="$ALLPKGS2"
1557	done
1558	echo "$ALLPKGS"
1559}
1560
1561setup_available () {
1562	local m1 c path pkgdest pkg
1563	m1="${MIRRORS%% *}"
1564
1565	for s in $SUITE $EXTRA_SUITES; do
1566		for c in ${COMPONENTS:-$(echo ${USE_COMPONENTS} | tr '|' ' ')}; do
1567			path="dists/$s/$c/binary-$ARCH/Packages"
1568			pkgdest="$TARGET/$($DLDEST pkg "$s" "$c" "$ARCH" "$m1" "$path")"
1569			# XXX: What if a package is in more than one component?
1570			# -- cjwatson 2009-07-29
1571			# XXX: ...or suite?
1572			# -- jrtc27 2019-06-11
1573			"$PKGDETAILS" STANZAS "$pkgdest" "$@"
1574		done
1575	done >"$TARGET/var/lib/dpkg/available"
1576
1577	for pkg; do
1578		echo "$pkg install"
1579	done | in_target dpkg --set-selections
1580}
1581
1582get_next_predep () {
1583	local stanza="$(in_target_nofail dpkg --predep-package)"
1584	[ "$stanza" ] || return 1
1585	echo "$stanza" | /usr/local/bin/grep '^Package:' | sed 's/^Package://; s/^ *//'
1586}
1587
1588################################################################### helpers
1589
1590# Return zero if it is possible to create devices and execute programs in
1591# this directory. (Both may be forbidden by mount options, e.g. nodev and
1592# noexec respectively.)
1593check_sane_mount () {
1594	mkdir -p "$1"
1595
1596	case "$HOST_OS" in
1597	    *freebsd*|hurd*)
1598		;;
1599	    *)
1600		if ! doing_variant fakechroot; then
1601		case "$CONTAINER" in
1602		  lxc|lxc-libvirt)
1603		    ;;
1604		  *)
1605		    mknod "$1/test-dev-null" c 1 3 || return 1
1606		    if ! echo test > "$1/test-dev-null"; then
1607			rm -f "$1/test-dev-null"
1608				return 1
1609		    fi
1610		    rm -f "$1/test-dev-null"
1611		    ;;
1612		esac
1613		fi
1614	esac
1615
1616	SH="/bin/sh"
1617	[ -x "$SH" ] || SH="$(which sh)"
1618
1619	cat > "$1/test-exec" <<EOF
1620#! $SH
1621:
1622EOF
1623	chmod +x "$1/test-exec"
1624	if ! "$1/test-exec"; then
1625		rm -f "$1/test-exec"
1626		return 1
1627	fi
1628	rm -f "$1/test-exec"
1629
1630	return 0
1631}
1632
1633read_gpg_status () {
1634	local badsig unkkey validsig
1635	while read prefix keyword keyid rest; do
1636		[ "$prefix" = '[GNUPG:]' ] || continue
1637		case $keyword in
1638		    BADSIG)	badsig="$keyid" ;;
1639		    NO_PUBKEY)	unkkey="$keyid" ;;
1640		    VALIDSIG)	validsig="$keyid" ;;
1641		esac
1642	done
1643	if [ "$validsig" ]; then
1644		info VALIDRELSIG "Valid Release signature (key id %s)" "$validsig"
1645	elif [ "$badsig" ]; then
1646		error 1 BADRELSIG "Invalid Release signature (key id %s)" "$badsig"
1647	elif [ "$unkkey" ]; then
1648		error 1 UNKNOWNRELSIG "Release signed by unknown key (key id %s)\n   The specified keyring $KEYRING may be incorrect or out of date.\n   You can find the latest Debian release key at https://ftp-master.debian.org/keys.html" "$unkkey"
1649	else
1650		error 1 SIGCHECK "Error executing gpgv2 to check Release signature"
1651	fi
1652}
1653
1654without () {
1655	# usage:  without "a b c" "a d" -> "b" "c"
1656	(echo "$1" | tr ' ' '\n' | sort | uniq;
1657	 echo "$2" "$2" | tr ' ' '\n') | sort | uniq -u | tr '\n' ' '
1658	echo
1659}
1660
1661# Formerly called 'repeat', but that's a reserved word in zsh.
1662repeatn () {
1663	local n="$1"
1664	shift
1665	while [ "$n" -gt 0 ]; do
1666		if "$@"; then
1667			break
1668		else
1669			n=$(( $n - 1 ))
1670			sleep 1
1671		fi
1672	done
1673	if [ "$n" -eq 0 ]; then return 1; fi
1674	return 0
1675}
1676
1677N_EXIT_THINGS=0
1678exit_function () {
1679	local n=0
1680	while [ "$n" -lt "$N_EXIT_THINGS" ]; do
1681		(eval $(eval echo \${EXIT_THING_$n}) 2>/dev/null || true)
1682		n=$(( $n + 1 ))
1683	done
1684	N_EXIT_THINGS=0
1685}
1686
1687trap "exit_function" 0
1688trap "exit 129" 1
1689trap "error 130 INTERRUPTED \"Interrupt caught ... exiting\"" 2
1690trap "exit 131" 3
1691trap "exit 143" 15
1692
1693on_exit () {
1694	eval "$(echo EXIT_THING_${N_EXIT_THINGS}=\"$1\")"
1695	N_EXIT_THINGS=$(( $N_EXIT_THINGS + 1 ))
1696}
1697
1698############################################################## fakechroot tools
1699
1700install_fakechroot_tools () {
1701	if [ "$VARIANT" = "fakechroot" ]; then
1702		export PATH=/usr/sbin:/sbin:$PATH
1703	fi
1704
1705	mv "$TARGET/sbin/ldconfig" "$TARGET/sbin/ldconfig.REAL"
1706	echo \
1707"#!/bin/sh
1708echo
1709echo \"Warning: Fake ldconfig called, doing nothing\"" > "$TARGET/sbin/ldconfig"
1710	chmod 755 "$TARGET/sbin/ldconfig"
1711
1712	echo \
1713"/sbin/ldconfig
1714/sbin/ldconfig.REAL
1715fakechroot" >> "$TARGET/var/lib/dpkg/diversions"
1716
1717	mv "$TARGET/usr/bin/ldd" "$TARGET/usr/bin/ldd.REAL"
1718	cat << 'END' > "$TARGET/usr/bin/ldd"
1719#!/usr/bin/perl
1720
1721# fakeldd
1722#
1723# Replacement for ldd with usage of objdump
1724#
1725# (c) 2003-2005 Piotr Roszatycki <dexter@debian.org>, BSD
1726
1727
1728my %libs = ();
1729
1730my $status = 0;
1731my $dynamic = 0;
1732my $biarch = 0;
1733
1734my $ldlinuxsodir = "/lib";
1735my @ld_library_path = qw(/usr/lib /lib);
1736
1737
1738sub ldso($) {
1739	my ($lib) = @_;
1740	my @files = ();
1741
1742	if ($lib =~ /^\//) {
1743	    $libs{$lib} = $lib;
1744	    push @files, $lib;
1745	} else {
1746	    foreach my $ld_path (@ld_library_path) {
1747		next unless -f "$ld_path/$lib";
1748		my $badformat = 0;
1749		open OBJDUMP, "objdump -p $ld_path/$lib 2>/dev/null |";
1750	 	while (my $line = <OBJDUMP>) {
1751		    if ($line =~ /file format (\S*)$/) {
1752				$badformat = 1 unless $format eq $1;
1753				last;
1754		    }
1755		}
1756		close OBJDUMP;
1757		next if $badformat;
1758		$libs{$lib} = "$ld_path/$lib";
1759		push @files, "$ld_path/$lib";
1760	    }
1761	    objdump(@files);
1762	}
1763}
1764
1765
1766sub objdump(@) {
1767	my (@files) = @_;
1768	my @libs = ();
1769
1770	foreach my $file (@files) {
1771	    open OBJDUMP, "objdump -p $file 2>/dev/null |";
1772	    while (my $line = <OBJDUMP>) {
1773		$line =~ s/^\s+//;
1774		my @f = split (/\s+/, $line);
1775		if ($line =~ /file format (\S*)$/) {
1776		    if (not $format) {
1777			$format = $1;
1778			if ($unamearch eq "x86_64" and $format eq "elf32-i386") {
1779			    my $link = readlink "/lib/ld-linux.so.2";
1780			    if ($link =~ /^\/emul\/ia32-linux\//) {
1781				$ld_library_path[-2] = "/emul/ia32-linux/usr/lib";
1782				$ld_library_path[-1] = "/emul/ia32-linux/lib";
1783			    }
1784			} elsif ($unamearch =~ /^(sparc|sparc64)$/ and $format eq "elf64-sparc") {
1785			    $ldlinuxsodir = "/lib64";
1786			    $ld_library_path[-2] = "/usr/lib64";
1787			    $ld_library_path[-1] = "/lib64";
1788			}
1789		    } else {
1790			next unless $format eq $1;
1791		    }
1792		}
1793		if (not $dynamic and $f[0] eq "Dynamic") {
1794		    $dynamic = 1;
1795		}
1796		next unless $f[0] eq "NEEDED";
1797		if ($f[1] =~ /^ld-linux(\.|-)/) {
1798		    $f[1] = "$ldlinuxsodir/" . $f[1];
1799		}
1800		if (not defined $libs{$f[1]}) {
1801		    $libs{$f[1]} = undef;
1802		    push @libs, $f[1];
1803		}
1804	    }
1805	    close OBJDUMP;
1806	}
1807
1808	foreach my $lib (@libs) {
1809	    ldso($lib);
1810	}
1811}
1812
1813
1814if ($#ARGV < 0) {
1815	print STDERR "fakeldd: missing file arguments\n";
1816	exit 1;
1817}
1818
1819while ($ARGV[0] =~ /^-/) {
1820	my $arg = $ARGV[0];
1821	shift @ARGV;
1822	last if $arg eq "--";
1823}
1824
1825open LD_SO_CONF, "/etc/ld.so.conf";
1826while ($line = <LD_SO_CONF>) {
1827	chomp $line;
1828	unshift @ld_library_path, $line;
1829}
1830close LD_SO_CONF;
1831
1832unshift @ld_library_path, split(/:/, $ENV{LD_LIBRARY_PATH});
1833
1834$unamearch = "$(/bin/uname -m)";
1835chomp $unamearch;
1836
1837foreach my $file (@ARGV) {
1838	my $address;
1839	%libs = ();
1840	$dynamic = 0;
1841
1842	if ($#ARGV > 0) {
1843		print "$file:\n";
1844	}
1845
1846	if (not -f $file) {
1847		print STDERR "ldd: $file: No such file or directory\n";
1848		$status = 1;
1849		next;
1850	}
1851
1852	objdump($file);
1853
1854	if ($dynamic == 0) {
1855		print "\tnot a dynamic executable\n";
1856		$status = 1;
1857	} elsif (scalar %libs eq "0") {
1858		print "\tstatically linked\n";
1859	}
1860
1861	if ($format =~ /^elf64-/) {
1862		$address = "0x0000000000000000";
1863	} else {
1864		$address = "0x00000000";
1865	}
1866
1867	foreach $lib (keys %libs) {
1868		if ($libs{$lib}) {
1869			printf "\t%s => %s (%s)\n", $lib, $libs{$lib}, $address;
1870		} else {
1871			printf "\t%s => not found\n", $lib;
1872		}
1873	}
1874}
1875
1876exit $status;
1877END
1878	chmod 755 "$TARGET/usr/bin/ldd"
1879
1880	echo \
1881"/usr/bin/ldd
1882/usr/bin/ldd.REAL
1883fakechroot" >> "$TARGET/var/lib/dpkg/diversions"
1884
1885}
1886