tar2fs 8.16 KB
Newer Older
1 2 3 4 5
#!/bin/bash -ex
# usage:
# tar2fs chroot.tar image.raw [size_in_bytes [fstype]]

. shell-error
6
export LANG=C
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

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*
24

25
SIZE_FACTOR=2		# multiply the sizes found by this value
26
BOOT_SIZE_FACTOR=2	# multiply /boot size by this value additionally
27 28 29 30 31 32 33

CUR_BOUNDARY=0		# align first partition at 1MB for performance (+1)

INITRD_MODULES=
BOOTFSTYPE=
BOOTPART=

34 35 36
ARCH="$(arch)"		# NB: sudo => no GLOBAL_ will do either; mind qemu-*

case "$ARCH" in
37 38
*86*)
	# NB: different storage modules might be needed for non-kvm
39
	INITRD_MODULES="sd_mod ata_piix ahci virtio-scsi virtio-blk"
40 41 42 43 44 45 46 47 48
	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"
	;;
49 50 51 52 53 54
e2k)
	BOOTFSTYPE="ext2"       # firmware knows it
	BLOCKDEV="/dev/sda"     # ...hopefully...
	BOOTPART="1"
	ROOTPART="2"
	;;
55 56 57 58
mips*)
	ROOTPART="1"
	BLOCKDEV="/dev/sda"
	;;
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
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

105
# image size in bytes
106 107 108 109
TARSIZE="$(stat -Lc %s "$TAR")"
# /boot size in that tarball
BOOTSIZE="$(boot_size "$TAR")"
DEFSIZE="$(($SIZE_FACTOR * ($TARSIZE - $BOOTSIZE)))"	# (exact sizes)
110
ROOTSIZE="$((${ROOTSIZE:-$DEFSIZE} + $MB - 1))"	# for ceil rounding to MB
111 112
# image and /boot sizes in megabytes
ROOTSIZEM="$(($ROOTSIZE / $MB))"
113
BOOTSIZEM="$((($SIZE_FACTOR * $BOOT_SIZE_FACTOR * $BOOTSIZE + $MB - 1) / $MB))"
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140

# 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=$?
141
	cd /
142 143 144 145
	if [ -n "$ROOTFS" ]; then
		umount ${BOOTFS:+"$BOOTFS"} "$ROOTFS"{/dev,/proc,/sys,}

		if [ -n "$LOOPDEV" ]; then
146 147 148 149
			kpartx -d -s "$LOOPDEV" || {
				sleep 10
				kpartx -d -s -v "$LOOPDEV"
			}
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
			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

Michael Shigorin's avatar
Michael Shigorin committed
177
kpartx -a -s "$LOOPDEV"
178 179 180 181 182 183 184 185 186
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

187 188 189 190 191 192 193 194 195 196 197 198 199 200
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

201 202 203 204 205 206 207 208 209 210 211 212 213
# 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...
214
echo "$ROOTDEV / $ROOTFSTYPE relatime 1 1" >> "$ROOTFS/etc/fstab"
215 216 217 218 219 220

# target device at once
if [ -n "$BOOTPART" ]; then
	echo "$BOOTDEV /boot $BOOTFSTYPE defaults 1 2" >> "$ROOTFS/etc/fstab"
fi

221 222 223 224 225 226 227 228 229 230
# 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

231
echo "MODULES_PRELOAD += $INITRD_MODULES $ROOTFSTYPE" >> "$ROOTFS/etc/initrd.mk"
232 233 234 235 236 237 238 239 240

case "$ARCH" in
*86*)
	echo "FEATURES += qemu" >> "$ROOTFS/etc/initrd.mk"
	;;
e2k)
	echo "FEATURES += usb"  >> "$ROOTFS/etc/initrd.mk"
	;;
esac
241

242 243
# NB: don't stick BOOTFS here, it has slightly different semantics
pushd $ROOTFS/boot
244

245 246 247
# 4.9.76-elbrus-def-alt1.11.1 -> def
get_label() { echo "${1# *}" | sed -r 's,.*elbrus-([0-9a-z]+)-.*$,\1,'; }

248
KVERSIONS=
249
if [ -s .origver ]; then
250 251
	while read kver; do
		chroot "$ROOTFS" make-initrd -k "$kver"
252
		KVERSIONS="$KVERSIONS $kver"
253
	done < .origver
254
fi
255
[ -n "$KVERSIONS" ] || fatal "unable to deduce kernel version"
256
rm -f .origver
257 258 259 260

# ...target device too
sed -i "s,$LOOPROOT,$ROOTDEV," "$ROOTFS/etc/fstab"

261 262 263 264 265 266
echo "** KVERSIONS=[$KVERSIONS]" >&2

if [ "`echo $KVERSIONS | wc -w`" = 1 ]; then # 2+ labels
	echo -e "default=`get_label $KVERSIONS`\n" >> boot.conf
fi

267 268
# FIXME: relies on particular (current) kernel package naming scheme
for v in $KVERSIONS; do
269
	l="`get_label "$v"`"
270 271 272 273 274 275
	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
276

277 278 279 280 281 282 283
EOF
done

echo "** start of boot.conf"
cat boot.conf
echo "** end of boot.conf"

284 285
popd

286
if [ -x "$ROOTFS"/sbin/lilo ]; then
287
	# configure and install bootloader
288 289
	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@")
290 291 292 293 294 295

	LILO_COMMON="lba32
delay=1
vga=0
image=/boot/vmlinuz
  initrd=/boot/initrd.img
296
  append=\"root=$ROOTDEV rootdelay=3 console=tty1 console=ttyS0,115200n8\"
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
  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
317 318 319 320 321 322
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"
323 324 325
fi

if [ -n "$SUDO_USER" ]; then
326
	chown "$SUDO_USER:$(id -g "$SUDO_USER")" "$IMG" "$ROOTFS" "$WORKDIR" ||:
327
fi
328 329 330
# maybe qemu interpreter was copied to chroot;
# this is no longer necessary, remove
rm -rf "$ROOTFS"/.host ||: