mkimage-profiles/bin/tar2fs
2019-06-10 20:37:06 +03:00

326 lines
8.0 KiB
Bash
Executable File

#!/bin/bash -ex
# usage:
# tar2fs chroot.tar image.raw [size_in_bytes [fstype]]
. shell-error
export LANG=C
if [ $# -lt 2 ]; then
fatal "error: tar2fs needs at least two arguments"
fi
# this needs env_keep sudo setup to actually work
if [ -n "$GLOBAL_BUILDDIR" ]; then
WORKDIR="$GLOBAL_BUILDDIR/vmroot"
else
WORKDIR="$(mktemp --tmpdir -d vmroot-XXXXX)"
fi
[ -n "$WORKDIR" ] || fatal "couldn't come up with suitable WORKDIR"
[ -n "$GLOBAL_DEBUG" ] || message "WORKDIR: $WORKDIR"
MB=1048576 # a parted's "megabyte" in bytes is *broken*
SIZE_FACTOR=2 # multiply the sizes found by this value
BOOT_SIZE_FACTOR=2 # multiply /boot size by this value additionally
CUR_BOUNDARY=0 # align first partition at 1MB for performance (+1)
INITRD_MODULES=
BOOTFSTYPE=
BOOTPART=
ARCH="$(arch)" # NB: sudo => no GLOBAL_ will do either; mind qemu-*
case "$ARCH" in
e2k)
BOOTFSTYPE="ext2" # firmware knows it
BLOCKDEV="/dev/sda" # ...hopefully...
BOOTPART="1"
ROOTPART="2"
;;
*)
ROOTPART="1"
BLOCKDEV="/dev/sda"
;;
esac
# figure out the part taken by /boot in the given tarball
boot_size() {
if [ -n "$BOOTPART" ]; then
tar tvf "$1" \
| awk ' \
BEGIN { sum=0 }
/^-.* \.\/boot\// { sum=sum+$3 }
END { print sum }'
else
echo "0"
fi
}
# parted wrapper for convenience
parting() { parted "$LOOPDEV" --align optimal --script -- "$@"; }
# unfortunately parted is insane enough to lump alignment controls
# into unit controls so creating adjacent partitions sized in MiB
# is not as straightforward as it might be... thus "+1" padding;
# see also http://www.gnu.org/software/parted/manual/parted.html#unit
mkpart() {
# a bit different start/end semantics to handle end of device too
local start="$(($CUR_BOUNDARY + 1))" # yes, we lose a megabyte
if [ -n "$1" ]; then
CUR_BOUNDARY="$(($start + $1))"
local end="$CUR_BOUNDARY"MiB
else
local end="-1s" # last sector of the image
fi
parting mkpart primary ext2 "$start"MiB "$end"
}
# a tarball containing chroot with a kernel
TAR="$1"
[ -s "$TAR" ] || fatal "source tarball doesn't really exist"
# a path to the image to be generated
IMG="$2"
[ -d "$(dirname "$IMG")" ] || fatal "target directory doesn't exist"
# 0 means auto; if a value is specified it holds (no /boot subtracted)
ROOTSIZE="$3"
[ -n "$ROOTSIZE" -a "$ROOTSIZE" != 0 ] || unset ROOTSIZE
# image size in bytes
TARSIZE="$(stat -Lc %s "$TAR")"
# /boot size in that tarball
BOOTSIZE="$(boot_size "$TAR")"
DEFSIZE="$(($SIZE_FACTOR * ($TARSIZE - $BOOTSIZE)))" # (exact sizes)
ROOTSIZE="$((${ROOTSIZE:-$DEFSIZE} + $MB - 1))" # for ceil rounding to MB
# image and /boot sizes in megabytes
ROOTSIZEM="$(($ROOTSIZE / $MB))"
BOOTSIZEM="$((($SIZE_FACTOR * $BOOT_SIZE_FACTOR * $BOOTSIZE + $MB - 1) / $MB))"
# tested to work: ext[234], jfs
# NB: xfs doesn't have a spare sector for the bootloader
ROOTFSTYPE="${4:-ext4}"
# single root partition hardwired so far,
# add another image for home/data/swap if needed
ROOTDEV="$BLOCKDEV$ROOTPART"
# last preparations...
MKFS="mkfs.$ROOTFSTYPE ${BOOTFSTYPE:+mkfs.$BOOTFSTYPE}"
for i in losetup sfdisk parted kpartx $MKFS; do
if ! type -t "$i" >&/dev/null; then
fatal "$i required but not found in host system"
fi
done
LOOPDEV="$(losetup --find)" # would be sad about non-binary megabytes too
ROOTFS="$WORKDIR/chroot"
BOOTFS=
if [ -n "$BOOTPART" ]; then
BOOTFS="$ROOTFS/boot"
fi
exit_handler() {
rc=$?
cd /
if [ -n "$ROOTFS" ]; then
umount ${BOOTFS:+"$BOOTFS"} "$ROOTFS"{/dev,/proc,/sys,}
if [ -n "$LOOPDEV" ]; then
kpartx -d -s "$LOOPDEV" || {
sleep 10
kpartx -d -s -v "$LOOPDEV"
}
losetup --detach "$LOOPDEV"
fi
rm -r -- "$ROOTFS"
rmdir -- "$WORKDIR"
fi
exit $rc
}
# handle -e in shebang as well
trap exit_handler EXIT ERR
# prepare disk image and a filesystem inside it
rm -f -- "$IMG"
OFFSET="$(($CUR_BOUNDARY + $BOOTSIZEM + $ROOTSIZEM - 1))"
dd if=/dev/zero of="$IMG" conv=notrunc bs=$MB count=1 seek="$OFFSET"
losetup "$LOOPDEV" "$IMG"
parting mklabel msdos
if [ -n "$BOOTPART" ]; then
BOOTDEV="$BLOCKDEV$BOOTPART"
mkpart "$BOOTSIZEM"
fi
# not ROOTSIZEM but "the rest"; somewhat non-trivial arithmetics lurk in parted
mkpart
kpartx -a -s "$LOOPDEV"
LOOPROOT="/dev/mapper/$(basename "$LOOPDEV")p$ROOTPART"
mkfs."$ROOTFSTYPE" "$LOOPROOT"
if [ -n "$BOOTPART" ]; then
LOOPBOOT="/dev/mapper/$(basename "$LOOPDEV")p$BOOTPART"
mkfs."$BOOTFSTYPE" "$LOOPBOOT"
fi
ROOTUUID="$(blkid -s UUID -o value -c /dev/null "$LOOPROOT")"
if [ -n "$ROOTUUID" ]; then
ROOTDEV="UUID=$ROOTUUID"
else
ROOTDEV="$LOOPROOT"
fi
if [ -n "$BOOTPART" ]; then
BOOTUUID="$(blkid -s UUID -o value -c /dev/null "$LOOPBOOT")"
if [ -n "$ROOTUUID" ]; then
BOOTDEV="UUID=$BOOTUUID"
fi
fi
# mount and populate it
mkdir -pm755 "$ROOTFS"
mount "$LOOPROOT" "$ROOTFS"
if [ -n "$BOOTPART" ]; then
mkdir -pm700 "$BOOTFS"
mount "$LOOPBOOT" "$BOOTFS"
fi
tar -C "$ROOTFS" --numeric-owner -xf "$TAR"
for i in /dev /proc /sys; do mount --bind "$i" "$ROOTFS$i"; done
# loop device so lilo could work...
echo "$ROOTDEV / $ROOTFSTYPE relatime 1 1" >> "$ROOTFS/etc/fstab"
# target device at once
if [ -n "$BOOTPART" ]; then
echo "$BOOTDEV /boot $BOOTFSTYPE defaults 1 2" >> "$ROOTFS/etc/fstab"
fi
# Query ARCH in chroot and redefine arch-dependent variable
ARCH="$(chroot "$ROOTFS" rpm --eval '%_host_cpu')"
if [[ $ARCH = *86* ]]
then # NB: different storage modules might be needed for non-kvm
INITRD_MODULES="sd_mod ata_piix ahci virtio-scsi virtio-blk"
else
INITRD_MODULES=
fi
echo "MODULES_PRELOAD += $INITRD_MODULES $ROOTFSTYPE" >> "$ROOTFS/etc/initrd.mk"
case "$ARCH" in
*86*)
echo "FEATURES += qemu" >> "$ROOTFS/etc/initrd.mk"
;;
e2k)
echo "FEATURES += usb" >> "$ROOTFS/etc/initrd.mk"
;;
esac
# NB: don't stick BOOTFS here, it has slightly different semantics
pushd $ROOTFS/boot
# 4.9.76-elbrus-def-alt1.11.1 -> def
get_label() { echo "${1# *}" | sed -r 's,.*elbrus-([0-9a-z]+)-.*$,\1,'; }
KVERSIONS=
if [ -s .origver ]; then
while read kver; do
chroot "$ROOTFS" make-initrd -k "$kver"
KVERSIONS="$KVERSIONS $kver"
done < .origver
fi
[ -n "$KVERSIONS" ] || fatal "unable to deduce kernel version"
rm -f .origver
# ...target device too
sed -i "s,$LOOPROOT,$ROOTDEV," "$ROOTFS/etc/fstab"
echo "** KVERSIONS=[$KVERSIONS]" >&2
if [ "`echo $KVERSIONS | wc -w`" = 1 ]; then # 2+ labels
echo -e "default=`get_label $KVERSIONS`\n" >> boot.conf
fi
# FIXME: relies on particular (current) kernel package naming scheme
for v in $KVERSIONS; do
l="`get_label "$v"`"
cat >> boot.conf <<EOF
label=$l
partition=0
image=/image-$v
initrd=/initrd-$v.img
cmdline=console=ttyS0,115200 console=tty0 consoleblank=0 hardreset root=UUID=$ROOTUUID
EOF
done
echo "** start of boot.conf"
cat boot.conf
echo "** end of boot.conf"
popd
# for u-boot
if [ "$BOOTLOADER" = "uboot" ]; then
EXTLINUX_CONF="$ROOTFS/boot/extlinux/extlinux.conf"
sed -i "/LABEL=ROOT/d" "$ROOTFS/etc/fstab"
sed -i "s/LABEL=ROOT/$ROOTDEV/g" "$EXTLINUX_CONF"
fi
if [ -x "$ROOTFS"/sbin/lilo ]; then
# configure and install bootloader
REGEXP='^.*: ([0-9]+) cylinders, ([0-9]+) heads, ([0-9]+) sectors/track*$'
set -- $(sfdisk -g "$LOOPDEV" | grep -E "$REGEXP" | sed -r "s@$REGEXP@\1 \2 \3@")
LILO_COMMON="lba32
delay=1
vga=0
image=/boot/vmlinuz
initrd=/boot/initrd.img
append=\"root=$ROOTDEV rootdelay=3 console=tty1 console=ttyS0,115200n8\"
label=linux"
cat > "$ROOTFS"/etc/lilo-loop.conf <<-EOF
boot=$LOOPDEV
disk=$LOOPDEV
bios=0x80
cylinders=$1
heads=$2
sectors=$3
partition=$LOOPROOT
start=63
$LILO_COMMON
EOF
chroot "$ROOTFS" lilo -C /etc/lilo-loop.conf
cat > "$ROOTFS"/etc/lilo.conf <<-EOF
boot=$BLOCKDEV
$LILO_COMMON
EOF
elif [ -x "$ROOTFS"/usr/sbin/grub-efi-autoupdate ]; then
chroot "$ROOTFS" grub-mkconfig -o /boot/grub/grub.cfg
chroot "$ROOTFS" grub-install
elif [ -x "$ROOTFS"/usr/sbin/grub-autoupdate ]; then
chroot "$ROOTFS" grub-mkconfig -o /boot/grub/grub.cfg
chroot "$ROOTFS" grub-install --target=i386-pc "$LOOPDEV"
fi
if [ -n "$SUDO_USER" ]; then
chown "$SUDO_USER:$(id -g "$SUDO_USER")" "$IMG" ||:
fi
# maybe qemu interpreter was copied to chroot;
# this is no longer necessary, remove
rm -rf "$ROOTFS"/.host ||: