@@ -178,3 +178,37 @@ deb_dl_dir_export() {
done
EOF
}
+
+# Point isar-apt at its real build-path location so host apt can access it.
+# The reproducible 'file:///isar-apt' encodes to the apt list prefix '_isar-apt',
+# the real repo path encodes to the same path with '/' replaced by '_'.
+deb_dl_dir_isar_apt_to_host() {
+ export rootfs=$1
+ export host_prefix="$(printf '%s' '${REPO_ISAR_DIR}/${DISTRO}' | tr '/' '_')"
+ run_privileged_heredoc << ' EOSUDO'
+ set -e
+ sed -i 's|file:///isar-apt|file://${REPO_ISAR_DIR}/${DISTRO}|g' \
+ "${rootfs}/etc/apt/sources.list.d/isar-apt.list"
+ for f in "${rootfs}/var/lib/apt/lists/"_isar-apt*; do
+ [ -e "$f" ] || continue
+ suffix="$(basename "$f" | sed 's|^_isar-apt||')"
+ mv "$f" "${rootfs}/var/lib/apt/lists/${host_prefix}${suffix}"
+ done
+ EOSUDO
+}
+
+# Revert the deb_dl_dir_isar_apt_to_host changes.
+deb_dl_dir_isar_apt_to_target() {
+ export rootfs=$1
+ export host_prefix="$(printf '%s' '${REPO_ISAR_DIR}/${DISTRO}' | tr '/' '_')"
+ run_privileged_heredoc << ' EOSUDO'
+ set -e
+ sed -i 's|file://${REPO_ISAR_DIR}/${DISTRO}|file:///isar-apt|g' \
+ "${rootfs}/etc/apt/sources.list.d/isar-apt.list"
+ for f in "${rootfs}/var/lib/apt/lists/${host_prefix}"*; do
+ [ -e "$f" ] || continue
+ suffix="$(basename "$f" | sed "s|^${host_prefix}||")"
+ mv "$f" "${rootfs}/var/lib/apt/lists/_isar-apt${suffix}"
+ done
+ EOSUDO
+}
@@ -65,6 +65,16 @@ ROOTFS_FEATURES:remove:focal = "generate-sbom"
# Capture all information needed for sbom generation
ROOTFS_APT_STATE = "apt-state.tar.zst"
ROOTFS_APT_ARGS="install --yes -o Debug::pkgProblemResolver=yes"
+ROOTFS_HOST_APT_OPTS = "-o Dir=${ROOTFSDIR} -o APT::Architecture=${ROOTFS_ARCH} -o DPkg::Chroot-Directory=${ROOTFSDIR}"
+
+# The host apt-get used to populate the rootfs must honor the rootfs' own apt
+# configuration fragments instead of the host's /etc/apt. A command-line '-o' is
+# applied too late (after apt has already read its config parts), so point apt at
+# the chroot via APT_CONFIG, which is parsed during apt initialization - before
+# the config parts directory is read. No host file is touched.
+ROOTFS_HOST_APT_CONFIG = "${WORKDIR}/isar-host-apt.conf"
+# Wrapper to run the host apt-get with that configuration.
+HOST_APT_CMD = "env APT_CONFIG=${ROOTFS_HOST_APT_CONFIG} /usr/bin/apt-get ${ROOTFS_HOST_APT_OPTS}"
ROOTFS_CLEAN_FILES="/etc/hostname /etc/resolv.conf"
@@ -76,7 +86,7 @@ ROOTFS_INITRD_STUBS = "update-initramfs"
ROOTFS_INITRD_STUBS += "${@ ' dracut' if bb.utils.to_boolean(d.getVar('ROOTFS_USE_DRACUT')) else '' }"
# list of <outer>:<inner> or <outer> mount entries
-ROOTFS_MOUNTS ??= "${REPO_ISAR_DIR}/${DISTRO}:/isar-apt ${WORKDIR}:/isar-work"
+ROOTFS_MOUNTS ??= "${WORKDIR}:/isar-work"
python () {
mounts = d.getVar('ROOTFS_MOUNTS', False)
@@ -102,75 +112,6 @@ export LANG ??= "C"
export LANGUAGE ??= "C"
export LC_ALL ??= "C"
-# Execute a command against a rootfs and with isar-apt bind-mounted.
-# Additional mounts may be specified using --bind <source> <target> and a
-# custom directory for the command to be executed with --chdir <dir>. The
-# command is assumed to follow the special "--" argument. This would replace
-# "sudo chroot" calls especially when a native command may be used instead of
-# chroot'ed command and without elevated privileges (the command will likely
-# take the rootfs as argument; e.g. apt-get -o Dir=${ROOTFSDIR}). If the
-# optional rootfs argument is omitted, the host rootfs will be used (e.g. to
-# run native commands): this should be used with care.
-#
-# Usage: rootfs_cmd [options] [rootfs] -- command
-#
-rootfs_cmd() {
- set -- "$@"
- bwrap_args="--bind ${REPO_ISAR_DIR}/${DISTRO} /isar-apt"
- bwrap_binds=""
- bwrap_rootfs=""
-
- while [ "${#}" -gt "0" ] && [ "$1" != "--" ]; do
- case "$1" in
- --bind)
- if [ "${#}" -lt "3" ]; then
- bbfatal "--bind requires two arguments"
- fi
- bwrap_binds="${bwrap_binds} --bind $2 $3"
- shift 3
- ;;
- --chdir)
- if [ "${#}" -lt "2" ]; then
- bbfatal "$1 requires an argument"
- fi
- bwrap_args="${bwrap_args} $1 $2"
- shift 2
- ;;
- -*)
- bbfatal "$1 is not a supported option!"
- ;;
- *)
- if [ -z "${bwrap_rootfs}" ]; then
- bwrap_rootfs="$1"
- shift
- else
- bbfatal "unexpected argument '$1'"
- fi
- ;;
- esac
- done
-
- if [ -n "${bwrap_rootfs}" ]; then
- bwrap_args="${bwrap_args} --bind ${bwrap_rootfs} /"
- fi
-
- if [ "${#}" -le "1" ] || [ "$1" != "--" ]; then
- bbfatal "no command specified (missing --)"
- fi
- shift # remove "--", command and its arguments follows
-
- # bin, lib and lib64 are only real directories on unmerged-usr rootfs
- for ro_d in bin etc lib lib64 sys usr var; do
- [ -d ${bwrap_rootfs}/${ro_d} ] && [ ! -L ${bwrap_rootfs}/${ro_d} ] || continue
- bwrap_args="${bwrap_args} --ro-bind ${bwrap_rootfs}/${ro_d} /${ro_d}"
- done
-
- bwrap --unshare-user --unshare-pid ${bwrap_args} \
- --dev-bind /dev /dev --proc /proc --tmpfs /tmp \
- ${@'--bind "${REPO_ISAR_DIR}/${DISTRO}" /isar-apt' if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''} \
- ${bwrap_binds} -- "${@}"
-}
-
rootfs_do_mounts[weight] = "3"
python rootfs_do_mounts() {
if d.getVar('ISAR_CHROOT_MODE') == 'schroot':
@@ -293,6 +234,20 @@ EOF
EOSUDO
}
+ROOTFS_CONFIGURE_COMMAND += "rootfs_redirect_isar_apt_to_host"
+rootfs_redirect_isar_apt_to_host() {
+ if [ -f "${ROOTFSDIR}/etc/apt/sources.list.d/isar-apt.list" ]; then
+ deb_dl_dir_isar_apt_to_host "${ROOTFSDIR}"
+ fi
+}
+
+# restore by latest before capturing the apt state and before sstate caching
+rootfs_redirect_isar_apt_to_target() {
+ if [ -f "${ROOTFSDIR}/etc/apt/sources.list.d/isar-apt.list" ]; then
+ deb_dl_dir_isar_apt_to_target "${ROOTFSDIR}"
+ fi
+}
+
ROOTFS_CONFIGURE_COMMAND += "rootfs_configure_apt"
rootfs_configure_apt[weight] = "2"
rootfs_configure_apt() {
@@ -315,6 +270,14 @@ rootfs_configure_apt() {
EOSUDO
}
+# Point the host apt-get at the rootfs' apt.conf.d (see ROOTFS_HOST_APT_CONFIG).
+ROOTFS_CONFIGURE_COMMAND =+ "rootfs_configure_host_apt_config"
+rootfs_configure_host_apt_config[weight] = "1"
+rootfs_configure_host_apt_config() {
+ echo 'Dir::Etc::parts "${ROOTFSDIR}/etc/apt/apt.conf.d";' \
+ > '${ROOTFS_HOST_APT_CONFIG}'
+}
+
rootfs_exclude_docs_drop() {
if [ -d '${ROOTFSDIR}/usr/share/man' ]; then
find '${ROOTFSDIR}/usr/share/man/' -mindepth 1 ! -type d -delete
@@ -371,7 +334,7 @@ rootfs_install_pkgs_update() {
run_privileged_heredoc <<'EOF'
set -e
${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
- chroot '${ROOTFSDIR}' /usr/bin/apt-get update \
+ ${HOST_APT_CMD} update \
-o Dir::Etc::SourceList="sources.list.d/isar-apt.list" \
-o Dir::Etc::SourceParts="-" \
-o APT::Get::List-Cleanup="0"
@@ -402,10 +365,14 @@ rootfs_install_pkgs_download[progress] = "custom:rootfs_progress.PkgsDownloadPro
rootfs_install_pkgs_download[isar-apt-lock] = "release-after"
rootfs_install_pkgs_download[network] = "${TASK_USE_NETWORK}"
rootfs_install_pkgs_download() {
- # download packages using apt in a non-privileged namespace
- rootfs_cmd --bind "${ROOTFSDIR}/var/cache/apt/archives" /var/cache/apt/archives \
- ${ROOTFSDIR} \
- -- /usr/bin/apt-get ${ROOTFS_APT_ARGS} -o Debug::NoLocking=1 --download-only ${ROOTFS_PACKAGES}
+ run_privileged_heredoc <<'EOF'
+ set -e
+ ${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
+ ${HOST_APT_CMD} \
+ ${ROOTFS_APT_ARGS} \
+ -o Debug::NoLocking=1 \
+ --download-only ${ROOTFS_PACKAGES}
+EOF
}
ROOTFS_INSTALL_COMMAND_BEFORE_EXPORT ??= ""
@@ -421,16 +388,15 @@ ROOTFS_INSTALL_COMMAND += "rootfs_install_pkgs_isar_download"
rootfs_install_pkgs_isar_download[weight] = "50"
rootfs_install_pkgs_isar_download[isar-apt-lock] = "acquire-before release-after"
rootfs_install_pkgs_isar_download() {
- # Command apt-get install do not cache packages from local repos
- # We can obtain non cached package URIs by recalling install command here
- # No need to export those files to dl_dir, so we can run it right after
- rootfs_cmd --bind "${ROOTFSDIR}/var/cache/apt/archives" /var/cache/apt/archives \
- --chdir "/var/cache/apt/archives" \
- ${ROOTFSDIR} \
- -- /usr/bin/sh -c 'apt-get ${ROOTFS_APT_ARGS} --print-uris ${ROOTFS_PACKAGES} | \
- sed -n "s|^.file:\(/[^'\'']*\.deb\). \([^ ]*\.deb\).*|\1 \2|p" | \
- sed ":a; s|^\([^ ]*\)%|\1\\\\x|; ta" | \
- while read -r path name; do cp -n "$(/usr/bin/printf "%b" "$path")" "$name" ; done'
+ run_privileged_heredoc <<'EOF'
+ set -e
+ ${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
+ ${HOST_APT_CMD} \
+ ${ROOTFS_APT_ARGS} --print-uris ${ROOTFS_PACKAGES} | \
+ sed -n "s|^.file:\(/[^'\'']*\.deb\). \([^ ]*\.deb\).*|\1 \2|p" | \
+ sed ":a; s|^\([^ ]*\)%|\1\\\\x|; ta" | \
+ while read -r path name; do cp -n "$(/usr/bin/printf "%b" "$path")" "${ROOTFSDIR}/var/cache/apt/archives/$name" ; done
+EOF
}
ROOTFS_INSTALL_COMMAND += "${@ 'rootfs_install_clean_files' if (d.getVar('ROOTFS_CLEAN_FILES') or '').strip() else ''}"
@@ -451,8 +417,7 @@ rootfs_install_pkgs_install() {
run_privileged_heredoc <<'EOF'
set -e
${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
- chroot "${ROOTFSDIR}" \
- /usr/bin/apt-get ${ROOTFS_APT_ARGS} --no-download ${ROOTFS_PACKAGES}
+ ${HOST_APT_CMD} ${ROOTFS_APT_ARGS} --no-download ${ROOTFS_PACKAGES}
EOF
}
@@ -472,7 +437,7 @@ rootfs_clear_initrd_symlinks() {
run_privileged rm -f ${ROOTFSDIR}/initrd.img.old
}
-ROOTFS_INSTALL_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'rootfs_capture_apt_state', '', d)}"
+ROOTFS_POSTPROCESS_COMMAND:prepend = "${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'rootfs_capture_apt_state', '', d)} "
rootfs_capture_apt_state() {
( cd ${ROOTFSDIR} && find usr/share/doc -name copyright -print0 ) | \
tar -cf ${WORKDIR}/${ROOTFS_APT_STATE} --zstd --sort=name \
@@ -485,6 +450,7 @@ rootfs_capture_apt_state() {
var/lib/apt/extended_states \
var/lib/dpkg/status
}
+ROOTFS_POSTPROCESS_COMMAND:prepend = "rootfs_redirect_isar_apt_to_target "
ROOTFS_INSTALL_DEPENDS ?= ""
@@ -583,8 +549,7 @@ cache_dbg_pkgs() {
ROOTFS_POSTPROCESS_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'clean-package-cache', 'rootfs_postprocess_clean_package_cache', '', d)}"
rootfs_postprocess_clean_package_cache() {
- run_in_chroot '${ROOTFSDIR}' \
- /usr/bin/apt-get clean
+ run_privileged ${HOST_APT_CMD} clean
# remove apt-cache folder itself (required in case rootfs is provided by sstate cache)
run_privileged find "${ROOTFSDIR}/var/cache/apt" -type f \
\( -name '*.deb' -o -name '*.bin' \) -delete
We currently use the apt inside the rootfs to operate on the rootfs. This has two major disadvantages: 1. on non-native, the apt invocations are emulated (including the extraction of the packages and downloading) 2. apt must be installed in the rootfs (which makes the rootfs larger and pulls in a lot of static-build-using dependencies related to the rust parts, which is relevant for license clearing) We change that similar to how mmdebstrap is doing it: Use the host apt to operate on the chroot, whereby dpkg itself runs inside the chroot to have proper support for the maintainer scripts. By that, apt is not emulated and the generated chroots can be much smaller. Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com> --- meta/classes-recipe/deb-dl-dir.bbclass | 34 ++++++ meta/classes-recipe/rootfs.bbclass | 145 ++++++++++--------------- 2 files changed, 89 insertions(+), 90 deletions(-)