blob: 7db1b42ff069d87e4f33f4501b6073c989d2da7f [file] [log] [blame]
#!/bin/bash -e
#
# This shell script must be run as root
SAVE_ARGS=("$@")
SUITE=buster
MIRROR=http://mirrors.kernel.org/debian
DIR=$(pwd)
ROOTDIR=$DIR/rootdir
DEBIAN_ARCH="$(dpkg --print-architecture)"
RAW_ROOT_FS="$DIR/root_fs.raw.$DEBIAN_ARCH"
ROOT_FS="$DIR/root_fs.img.$DEBIAN_ARCH"
COMPAT="-o compat=0.10"
SAVE_RAW_ROOT=no
DO_GCE=no
DO_UPDATE=no
DO_NETWORKING=no
DO_IMAGE=no
DO_DRGN=no
RESUME=0
if test $(df -k /tmp | tail -1 | awk '{print $4}') -gt 350000
then
RAW_ROOT_FS=/tmp/root_fs.raw.$$
fi
if test -f ../fstests-bld/xfstests/build-distro ; then
distro=$(cat ../fstests-bld/xfstests/build-distro)
case "$distro" in
trixie|bookworm|bullseye)
SUITE="$distro" ;;
*)
SUITE=buster ;;
esac
fi
if test -r config.custom ; then
. $(pwd)/config.custom
fi
while [ "$1" != "" ]; do
case $1 in
--save_raw_root)
SAVE_RAW_ROOT=yes;
;;
--raw_root_fs) shift
RAW_ROOT_FS="$1"
;;
--resume) shift
RESUME="$1"
;;
--suite) shift
SUITE=$1
;;
--mirror) shift
MIRROR=$1
;;
--both)
DO_IMAGE=yes;
if test -z "$OUT_TAR" ; then
OUT_TAR="$DIR/root_fs.$DEBIAN_ARCH.tar.gz"
fi
;;
--drgn)
DO_DRGN=yes;
;;
--networking)
DO_NETWORKING=yes;
;;
--update)
DO_UPDATE=yes
;;
--pause)
PAUSE_DEBUG=yes
;;
--out_tar|--out-tar)
OUT_TAR="$DIR/root_fs.$DEBIAN_ARCH.tar.gz"
;;
--out_tar=*|--out-tar=*)
OUT_TAR="$(echo $1 | sed 's/--out[-_]tar=//')"
;;
--log)
DO_LOG="$DIR/gen-image.log"
;;
--log=*)
DO_LOG="$(echo $1 | sed 's/--log=//')"
;;
--src_date) shift
SOURCE_DATE="$1"
;;
-a|--add-package) shift
PACKAGES="$(echo $PACKAGES $1 | sed -e 's/,/ /')"
;;
*)
echo "usage: gen-image [--save_raw_root] [--update] [--mirror MIRROR_LOC]"
echo " [-a|--add_packages packages] [--networking] "
echo " [--src_date DATE] [--log[=LOGFILE]]"
echo " [--suite SUITE] [--out_tar[=DIR]] [--both]"
exit 1
;;
esac
shift
done
if test -z "$OUT_TAR" ; then
DO_IMAGE=yes
fi
function stage_start () {
echo "----------------- $(date '+%Y-%m-%d %H:%M:%S'): gen-image: Starting $*"
}
if test -z "$SOURCE_DATE" ; then
export SOURCE_DATE=@$(git log -1 --pretty=%ct)
fi
if test -n "$DO_LOG" ; then
if test -z "$GEN_IMAGE_LOG" ; then
export GEN_IMAGE_LOG="$DO_LOG"
set -- "${SAVE_ARGS[@]}"
exec script -c "$0 $*" "$DO_LOG"
fi
fi
if test -n "$OUT_TAR" -a "$EUID" -ne 0 ; then
export DO_FAKECHROOT=yes
fi
if test -n "$DO_FAKECHROOT" ; then
if test "$FAKECHROOT" != "true" ; then
set -- "${SAVE_ARGS[@]}"
exec fakechroot $0 "$@"
fi
if test -z "$FAKEROOTKEY" ; then
set -- "${SAVE_ARGS[@]}"
exec fakeroot $0 "$@"
fi
PATH="/sbin:/usr/sbin:$PATH"
else
if test "$EUID" -ne 0 ; then
echo "You must run this script as root (or use --out_tar)"
exit 1;
fi
fi
LIBSSL=$(apt-cache depends libssl-dev | grep 'Depends: libssl1' |\
awk '{print $2};')
if test $DO_NETWORKING = "yes"; then
PACKAGES="$PACKAGES $(cat net-packages)"
fi
if test $DO_DRGN = "yes" ; then
PACKAGES="$PACKAGES python3-pip"
fi
PACKAGES="$PACKAGES $(cat xfstests-packages) $LIBSSL"
if test -f xfstests-packages.$SUITE ; then
PACKAGES="$PACKAGES $(cat xfstests-packages.$SUITE)"
fi
PACKAGES=$(echo $PACKAGES | sed 's/ /,/g')
case $PACKAGES in
apt,*|*,apt,*|*,apt) EXCLUDE="" ;;
*) EXCLUDE="--exclude=apt"
esac
update_xfstests()
{
tar --exclude share/man -C $ROOTDIR/root -xf ../fstests-bld/xfstests.tar.gz
tar -X kvm-exclude-files -C files \
--owner=root --group=root --mode=go+u-w -c . | tar -C $ROOTDIR -x
rsync -avH ../fstests-bld/xfstests/git-versions $ROOTDIR/root/xfstests
chown -R root:root $ROOTDIR/root
chmod -R go+u-w $ROOTDIR/root
}
fix_symlinks()
{
if test -n "$DO_FAKECHROOT"; then
symlinks -crd rootdir
fi
}
finalize_rootfs()
{
stage_start "to create image file"
# the stretch version of e2fsck will return 1 if it optimized any
# directories, which is the return value for "we changed something but
# everything is a-ok". If we fail just continue if our return value was 1,
# otherwise bail.
e2fsck -fyD $RAW_ROOT_FS || [ $? -eq 1 ] || exit 1
e2fsck -fy -E discard $RAW_ROOT_FS || [ $? -eq 1 ] || exit 1
qemu-img convert -f raw -O qcow2 $COMPAT -c $RAW_ROOT_FS $ROOT_FS
}
cleanup_package_dirs()
{
if test -z "$DO_FAKECHROOT" ; then
umount $ROOTDIR/var/cache/apt/archives
umount $ROOTDIR/var/lib/apt/lists
for i in debs imgdir ;
do
umount $ROOTDIR/$i
rmdir $ROOTDIR/$i
done
else
(cd $ROOTDIR/var/cache/apt/archives ; \
for i in * ; do \
if test ! -f $DIR/var.cache.apt.archives/$i ; then \
echo caching $i ; \
ln $i $DIR/var.cache.apt.archives/ ; \
fi ; \
done)
(cd $ROOTDIR/var/lib/apt/lists ; \
for i in * ; do \
if test ! -f $DIR/var.lib.apt.lists/$i ; then \
echo caching $i ; \
ln $i $DIR/var.lib.apt.lists/ ; \
fi ; \
done)
find $ROOTDIR/var/cache/apt/archives $ROOTDIR/var/lib/apt/lists \
$ROOTDIR/debs -type f | xargs rm
rmdir $ROOTDIR/debs
rmdir $ROOTDIR/imgdir
fi
}
unmount_rootdir()
{
if test -z "$DO_FAKECHROOT" ; then
umount $ROOTDIR
rmdir $ROOTDIR
fi
}
delete_rootdir()
{
if test -z "$DO_FAKECHROOT" ; then
if test "$SAVE_RAW_ROOT" = "yes" ; then
echo "Raw root image has been saved at" $RAW_ROOT_FS
else
rm -f $RAW_ROOT_FS
fi
else
if test "$SAVE_RAW_ROOT" = "yes" ; then
echo "Raw root directory has been saved at" $ROOTDIR
else
rm -rf $ROOTDIR
fi
fi
}
run_in_chroot()
{
echo "Running in chroot: $1"
# Note: we execute the command in a login shell rather than execute it
# directly because this makes the $PATH get set up correctly.
DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
LC_ALL=C LANGUAGE=C LANG=C chroot "$ROOTDIR" /bin/sh -l -c "$1"
}
cleanup_on_abort()
{
cleanup_package_dirs
unmount_rootdir
delete_rootdir
trap - INT TERM
}
if test -n "$OUT_TAR" ; then
if test $DO_UPDATE = "yes" ; then
echo "Incompatible options: --out-tar and --update"
exit 1
fi
rm -rf "$ROOTDIR"
fi
mkdir -p $ROOTDIR
if test $DO_UPDATE = "yes" ; then
qemu-img convert -f qcow2 -O raw $ROOT_FS $RAW_ROOT_FS
mount -t ext4 -o loop $RAW_ROOT_FS $ROOTDIR
rm -rf $ROOTDIR/xfstests
update_xfstests
umount $ROOTDIR
rmdir $ROOTDIR
finalize_rootfs
exit 0
fi
detect_foreign_chroot()
{
local BINFMT_MISC_MNT=/proc/sys/fs/binfmt_misc
if [ ! -d "$BINFMT_MISC_MNT" ]; then
# binfmt_misc disabled in kernel
return
fi
if test -n "$DO_FAKECHROOT" ; then
return
fi
if ! mountpoint "$BINFMT_MISC_MNT" &>/dev/null; then
mount binfmt_misc -t binfmt_misc "$BINFMT_MISC_MNT"
trap "umount \"$BINFMT_MISC_MNT\"" EXIT
fi
if [ "$(<"$BINFMT_MISC_MNT/status")" = "disabled" ]; then
return
fi
local arch="$(uname -m)"
case "$arch" in
armv7l|armv6l)
arch="arm"
;;
*)
esac
local binfmt="qemu-$arch"
local binfmt_file="$BINFMT_MISC_MNT/$binfmt"
if [ ! -e "$binfmt_file" ]; then
return
fi
QEMU="$(awk '/^interpreter/{print $2}' "$binfmt_file")"
FOREIGN="--foreign"
echo "Detected foreign chroot, using user-mode emulation with $QEMU"
}
QEMU=
FOREIGN=
detect_foreign_chroot
mkdir -p var.cache.apt.archives
mkdir -p var.lib.apt.lists
mkdir -p debs
DEBS_DIR=debs
if test -d debs.$distro ; then
DEBS_DIR=debs.$distro
fi
if test -z "$DO_FAKECHROOT"; then
if test "$RESUME" -le 1 ; then
stage_start "stage 1: format file system"
cp /dev/null $RAW_ROOT_FS
mke2fs -t ext4 -O ^has_journal -Fq $RAW_ROOT_FS 2g
fi
mount -t ext4 -o loop $RAW_ROOT_FS $ROOTDIR
fi
mkdir -p $ROOTDIR/var/cache/apt/archives
mkdir -p $ROOTDIR/var/lib/apt/lists
mkdir -p $ROOTDIR/debs
mkdir -p $ROOTDIR/imgdir
mkdir -p $ROOTDIR/vtmp
if test -z "$DO_FAKECHROOT"; then
mount --bind var.cache.apt.archives $ROOTDIR/var/cache/apt/archives
mount --bind var.lib.apt.lists $ROOTDIR/var/lib/apt/lists
mount --bind $DEBS_DIR $ROOTDIR/debs
mount --bind $(dirname $RAW_ROOT_FS) $ROOTDIR/imgdir
else
ln var.cache.apt.archives/* $ROOTDIR/var/cache/apt/archives
ln var.lib.apt.lists/* $ROOTDIR/var/lib/apt/lists
if ! find $DEBS_DIR -maxdepth 0 -empty | grep -q . > /dev/null ; then
ln $DEBS_DIR/* $ROOTDIR/debs
fi
export FAKECHROOT_CMD_SUBST=/usr/bin/chfn=/bin/true
fi
trap cleanup_on_abort INT TERM
if test "$RESUME" -le 2 ; then
stage_start "stage 2: debootstrap"
debootstrap --variant=minbase --include=$PACKAGES $EXCLUDE \
--components=main,contrib,non-free \
$FOREIGN $SUITE $ROOTDIR $MIRROR $DIR/debootstrap.script
else
true
fi
if test $? -ne 0 ; then
echo "Deboostrap failed, aborting."
cleanup_on_abort
exit 1
fi
if test -n "$QEMU" ; then
cp $QEMU $ROOTDIR/usr/bin/
if test "$RESUME" -le 3 ; then
stage_start "stage 3: second-stage debootstrap"
run_in_chroot "/debootstrap/debootstrap --second-stage"
fi
if test "$RESUME" -le 4 ; then
stage_start "stage 4: dpkg --configure -a"
run_in_chroot "dpkg --configure -a"
fi
mkdir -p "$ROOTDIR/run/shm"
chmod 1777 "$ROOTDIR/run/shm"
fi
if test "$RESUME" -le 5 -a -f "backport-packages-$SUITE" ; then
stage_start "stage 5: Installing backports"
./get-backports-pkgs "$SUITE" "$ROOTDIR"
if test -f "$ROOTDIR/debootstrap/debpaths" ; then
DEBS=$(while read pkg path; do echo -n "$path " ; done <"$ROOTDIR/debootstrap/debpaths")
run_in_chroot "dpkg --ignore-depends=e2fsprogs --auto-deconfigure -i $DEBS"
fi
rm -rf "$ROOTDIR/debootstrap"
fi
DEBS="$(find debs -name "*_${DEBIAN_ARCH}.deb" -o -name "*_all.deb")"
if test -n "$DEBS"
then
if test "$RESUME" -le 6 ; then
stage_start "stage 6: Installing manual debs"
run_in_chroot "dpkg --ignore-depends=e2fsprogs --auto-deconfigure -i $(echo $DEBS)"
fi
fi
update_xfstests
for i in vda vdb vdc vdd vde vdf vdi vdj results test scratch mnt/test mnt/scratch
do
mkdir -p $ROOTDIR/$i
done
stage_start "to do final VM configuration"
echo "fsgqa:x:31415:31415:fsgqa user:/home/fsgqa:/bin/bash" >> $ROOTDIR/etc/passwd
echo "fsgqa:!::0:99999:7:::" >> $ROOTDIR/etc/shadow
echo "fsgqa:x:31415:" >> $ROOTDIR/etc/group
echo "fsgqa:!::" >> $ROOTDIR/etc/gshadow
mkdir $ROOTDIR/home/fsgqa
chown 31415:31415 $ROOTDIR/home/fsgqa
echo "fsgqa2:x:31416:31416:second fsgqa user:/home/fsgqa2:/bin/bash" >> $ROOTDIR/etc/passwd
echo "fsgqa2:!::0:99999:7:::" >> $ROOTDIR/etc/shadow
echo "fsgqa2:x:31416:" >> $ROOTDIR/etc/group
echo "fsgqa2:!::" >> $ROOTDIR/etc/gshadow
mkdir $ROOTDIR/home/fsgqa2
chown 31416:31416 $ROOTDIR/home/fsgqa
echo "123456-fsgqa:x:31417:31417:numberic fsgqa user:/home/123456-fsgqa:/bin/bash" >> $ROOTDIR/etc/passwd
echo "123456-fsgqa:!::0:99999:7:::" >> $ROOTDIR/etc/shadow
echo "123456-fsgqa:x:31417:" >> $ROOTDIR/etc/group
echo "123456-fsgqa:!::" >> $ROOTDIR/etc/gshadow
mkdir $ROOTDIR/home/123456-fsgqa
chown 31417:31417 $ROOTDIR/home/123456-fsgqa
chmod 755 $ROOTDIR/root
ln -sf ../proc/self/mounts $ROOTDIR/etc/mtab
fix_symlinks
if [ -f "$ROOTDIR/lib/systemd/system/run-rpc_pipefs.mount" ]; then
sed -i -e '/Conflicts=/iConditionPathExists=/sys/module/sunrpc' \
"$ROOTDIR/lib/systemd/system/run-rpc_pipefs.mount"
fi
if [ -f "$ROOTDIR/lib/systemd/system/proc-fs-nfsd.mount" ]; then
sed -i -e '/Conflicts=/iConditionPathExists=/sys/module/nfsd' \
"$ROOTDIR/lib/systemd/system/proc-fs-nfsd.mount"
fi
cp $ROOTDIR/lib/systemd/system/serial-getty@.service \
$ROOTDIR/etc/systemd/system/telnet-getty@.service
sed -i -e '/ExecStart/s/agetty/agetty -a root/' \
-e "/ExecStart/s/-o '/-o '-f /" \
-e 's/After=rc.local.service/After=kvm-xfstests.service/' \
$ROOTDIR/lib/systemd/system/serial-getty@.service
sed -i -e '/ExecStart/s/agetty/agetty -a root/' \
-e "/ExecStart/s/-o '/-o '-f /" \
-e 's/After=rc.local.service/After=network.target/' \
$ROOTDIR/etc/systemd/system/telnet-getty@.service
run_in_chroot "systemctl enable kvm-xfstests.service"
run_in_chroot "systemctl enable telnet-getty@ttyS1.service"
run_in_chroot "systemctl enable telnet-getty@ttyS2.service"
run_in_chroot "systemctl enable telnet-getty@ttyS3.service"
run_in_chroot "systemctl mask serial-getty@hvc0.service"
run_in_chroot "systemctl disable multipathd"
run_in_chroot "systemctl disable nvmf-autoconnect"
if test $DO_NETWORKING = "yes"; then
run_in_chroot "systemctl disable nfs-server || true"
run_in_chroot "systemctl disable nfs-blkmap || true"
fi
find $ROOTDIR/usr/share/doc -type f -print0 ! -name copyright | xargs -0 rm
find $ROOTDIR/usr/share/doc -mindepth 2 -type l -print0 | xargs -0 rm
find $ROOTDIR/usr/share/doc -type d -print0 | xargs -0 rmdir --ignore-fail-on-non-empty -p
rm -rf $ROOTDIR/usr/share/man $ROOTDIR/usr/share/locale
find $ROOTDIR/var/log -type f -print0 | xargs -0 rm
if test -n "$DO_FAKECHROOT"; then
rm -f $ROOTDIR/dev $ROOTDIR/proc
mkdir $ROOTDIR/dev $ROOTDIR/proc
mknod -m 622 $ROOTDIR/dev/console c 5 1
mknod -m 666 $ROOTDIR/dev/null c 1 3
mknod -m 666 $ROOTDIR/dev/zero c 1 5
mknod -m 666 $ROOTDIR/dev/ptmx c 5 2
mknod -m 666 $ROOTDIR/dev/tty c 5 0
mknod -m 444 $ROOTDIR/dev/random c 1 8
mknod -m 444 $ROOTDIR/dev/urandom c 1 9
chown root:tty $ROOTDIR/dev/{console,ptmx,tty}
# TODO: does anything similar need to be done for arm64?
if test "$(uname -m)" = x86_64 ; then
rm $ROOTDIR/lib64/ld-linux-x86-64.so.2
mkdir -p $ROOTDIR/lib64
ln -s /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 $ROOTDIR/lib64/
fi
if test -f $ROOTDIR/sbin/ldconfig.REAL ; then
mv $ROOTDIR/sbin/ldconfig.REAL $ROOTDIR/sbin/ldconfig
ed $ROOTDIR/var/lib/dpkg/diversions <<EOF > /dev/null 2>&1
/^\/sbin\/ldconfig.REAL$/
.-1,.+1d
wq
EOF
fi
if test -f $ROOTDIR/usr/bin/ldd.REAL ; then
mv $ROOTDIR/usr/bin/ldd.REAL $ROOTDIR/usr/bin/ldd
ed $ROOTDIR/var/lib/dpkg/diversions <<EOF > /dev/null 2>&1
/^\/usr\/bin\/ldd.REAL$/
.-1,.+1d
wq
EOF
fi
fi
if test $DO_DRGN = "yes" ; then
run_in_chroot "pip3 install drgn"
fi
if test -n "$PAUSE_DEBUG"
then
${SHELL:-/bin/sh}
fi
rm -rf $ROOTDIR/var/cache/apt/archives/partial \
$ROOTDIR/var/lib/apt/lists/partial
trap - INT TERM
cleanup_package_dirs
if test -n "$OUT_TAR"; then
case "$OUT_TAR" in
*.gz) ZCMD="gzip -9n" ;;
*) ZCMD="cat" ;
esac
fix_symlinks
stage_start "to create tarfile"
(cd "$ROOTDIR" ; find . -print0 | LC_ALL=C sort -z |
tar -c --null --no-recursion -T - --numeric-owner \
--mtime="${SOURCE_DATE}" -f - | $ZCMD > "$OUT_TAR")
fi
unmount_rootdir
if test "$DO_IMAGE" = "yes" ; then
finalize_rootfs
fi
delete_rootdir
stage_start "gen-image complete"