7f1ec09c9b
Changing ROOTFS owner causes a problem: created image's root directory is owned by user executing make instead of root. Changing WORKDIR owner is unnecessary because it will be removed anyways.
331 lines
8.1 KiB
Bash
Executable File
331 lines
8.1 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
|
|
*86*)
|
|
# NB: different storage modules might be needed for non-kvm
|
|
INITRD_MODULES="sd_mod ata_piix ahci virtio-scsi virtio-blk"
|
|
BLOCKDEV="/dev/sda" # might be /dev/vda for virtio
|
|
ROOTPART="1"
|
|
;;
|
|
arm*)
|
|
BOOTFSTYPE="ext2" # support expected in every sane target uboot
|
|
BLOCKDEV="/dev/mmcblk0p" # ...hopefully...
|
|
BOOTPART="1"
|
|
ROOTPART="2"
|
|
;;
|
|
e2k)
|
|
BOOTFSTYPE="ext2" # firmware knows it
|
|
BLOCKDEV="/dev/sda" # ...hopefully...
|
|
BOOTPART="1"
|
|
ROOTPART="2"
|
|
;;
|
|
mips*)
|
|
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
|
|
|
|
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 ||:
|