summaryrefslogtreecommitdiffstats
path: root/hooks/merged-usr/extract00.sh
blob: 733419190b5141c411b0edd2637b39b9c34fc32c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/bin/sh

set -eu

if [ "${MMDEBSTRAP_VERBOSITY:-1}" -ge 3 ]; then
	set -x
fi

TARGET="$1"

# can_usrmerge_symlink() and can_usrmerge_symlink() are
# Copyright 2023 Helmut Grohne <helmut@subdivi.de>
# and part of the debootstrap source in /usr/share/debootstrap/functions
# https://salsa.debian.org/installer-team/debootstrap/-/merge_requests/96
# https://bugs.debian.org/104989
can_usrmerge_symlink() {
	# Absolute symlinks can be relocated without problems.
	test "${2#/}" = "$2" || return 0
	while :; do
		if test "${2#/}" != "$2"; then
			# Handle double-slashes.
			set -- "$1" "${2#/}"
		elif test "${2#./}" != "$2"; then
			# Handle ./ inside a link target.
			set -- "$1" "${2#./}"
		elif test "$2" = ..; then
			# A parent directory symlink is ok if it does not
			# cross the top level directory.
			test "${1%/*/*}" != "$1" -a -n "${1%/*/*}"
			return $?
		elif test "${2#../}" != "$2"; then
			# Symbolic link crossing / cannot be moved safely.
			# This is prohibited by Debian Policy 10.5.
			test "${1%/*/*}" = "$1" -o -z "${1%/*/*}" && return 1
			set -- "${1%/*}" "${2#../}"
		else
			# Consider the symlink ok if its target does not
			# contain a parent directory. When we fail here,
			# the link target is non-minimal and doesn't happen
			# in the archive.
			test "${2#*/../}" = "$2"
			return $?
		fi
	done
}

merge_usr_entry() {
	# shellcheck disable=SC3043
	local entry canon
	canon="$TARGET/usr/${1#"$TARGET/"}"
	test -h "$canon" &&
		error 1 USRMERGEFAIL "cannot move %s as its destination exists as a symlink" "${1#"$TARGET"}"
	if ! test -e "$canon"; then
		mv "$1" "$canon"
		return 0
	fi
	test -d "$1" ||
		error 1 USRMERGEFAIL "cannot move non-directory %s as its destination exists" "${1#"$TARGET"}"
	test -d "$canon" ||
		error 1 USRMERGEFAIL "cannot move directory %s as its destination is not a directory" "${1#"$TARGET"}"
	for entry in "$1/"* "$1/."*; do
		# Some shells return . and .. on dot globs.
		test "${entry%/.}" != "${entry%/..}" && continue
		if test -h "$entry" && ! can_usrmerge_symlink "${entry#"$TARGET"}" "$(readlink "$entry")"; then
			error 1 USRMERGEFAIL "cannot move relative symlink crossing top-level directory" "${entry#"$TARGET"}"
		fi
		# Ignore glob match failures
		if test "${entry%'/*'}" != "${entry%'/.*'}" && ! test -e "$entry"; then
			continue
		fi
		merge_usr_entry "$entry"
	done
	rmdir "$1"
}

# This is list includes all possible multilib directories. It must be
# updated when new multilib directories are being added. Hopefully,
# all new architectures use multiarch instead, so we never get to
# update this.
for dir in bin lib lib32 lib64 libo32 libx32 sbin; do
	test -h "$TARGET/$dir" && continue
	test -e "$TARGET/$dir" || continue
	merge_usr_entry "$TARGET/$dir"
	ln -s "usr/$dir" "$TARGET/$dir"
done