Ich brauche ein Linux mit verschiedenen Tools, das ich vom USB-Stick booten kann. Zusätzlich möchte ich die Tools eventuell gleichzeitig auf mehreren Rechnern sowohl mit als auch ohne UEFI verwenden.
Der Plan ist beim Booten mit zramctl
ein komprimiertes Blockdevice im RAM anzulegen und auf dieses das System zu kopieren, sodass der USB-Stick anschließend entfernt werden kann.
Zuerst erstelle ich mir einen leeren Ordner und installiere in diesem mit pacstrap
ein Archlinux.
Dazu benötige ich folgende Pakete auf meinem Rechner: sudo pacman -Sy arch-install-scripts syslinux pigz exfat-utils dosfstools gptfdisk
mkdir stick
pacstrap -d stick base vim bash-completion pv
Anschließend wechsle ich mit sudo arch-chroot stick
in das neu installierte System und richte es fertig ein.
echo myhost > /etc/hostname
echo LANG=de_DE.UTF-8 > /etc/locale.conf
echo LC_COLLATE=C >> /etc/locale.conf
echo LANGUAGE=de_DE >> /etc/locale.conf
echo KEYMAP=de-latin1 > /etc/vconsole.conf
echo FONT=lat9w-16 >> /etc/vconsole.conf
ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
vim /etc/locale.gen
locale-gen
....
Damit das System später in den RAM geladen wird, führe ich in der Initial Ramdisk ein Script aus. Dieses besteht aus zwei Teilen: Dem eigentlichen Script, welches beim Booten ausgeführt wird und einer Liste von Abhängigkeiten die bei der Generierung der Initial Ramdisk kopiert werden. Bei der Erstellung des Scripts habe ich mich an diesem AUR Paket orientiert.
Das eigentliche Script liegt in dem neu installierten System unter usr/lib/initcpio/hooks/tar-root
:
#!/bin/ash
run_hook() {
if [ "${tarroot}" ]; then
fsck_root() {
echo &>/dev/null
}
tardev=$(resolve_device "${tarroot%%:*}")
echo "tardev: $tardev"
mount -t ext4 "$tardev" /tar-dev
tarfile=${tarroot##*:}
echo "tarfile: $tarfile"
if [ ! -b "/dev/zram0" ]; then
modprobe zram num_devices=$(($(nproc)+2))
fi;
mem_size=$(free -m | awk '/Mem/ {print int($2)}');
echo "mem_size: $mem_size"
tarroot_device=$(zramctl -f -s $(($mem_size*2))M -a lzo -t $(nproc))
echo "tarroot_device: $tarroot_device"
mkfs.ext4 "$tarroot_device";
echo "mounting $tarroot_device"
mount "$tarroot_device" /tar-root;
unset mem_size;
pv "/tar-dev$tarfile" | bsdtar -xzf - -C /tar-root;
umount /tar-dev;
umount /tar-root;
export tarroot_device;
tarroot_mount() {
mount "${tarroot_device}" "$1"
}
mount_handler=tarroot_mount
fi;
}
Die Liste mit den Abhängigkeiten liegt unter usr/lib/initcpio/install/tar-root
:
#!/bin/ash
build() {
add_dir /tar-dev
add_dir /tar-root
add_binary zramctl
add_binary free
add_binary mkfs.ext4
add_binary nproc
add_binary pv
add_binary bsdtar
add_binary gzip
add_runscript
}
help() {
cat <<HELPEOF
This hoook loads a tar file into ram as rootfs
HELPEOF
}
Damit das Script beim Booten auch ausgeführt wird, muss es in der /etc/mkinitcpio.conf
in der HOOKS
liste stehen. Meine HOOKS
-liste sieht wie folgt aus: HOOKS="base udev modconf block keyboard tar-root"
. Da das gepackte System später auf einer ext4-partition liegen wird Packe ich mit MODULES="ext4"
noch das dafür benötigte Modul dazu. Anschließend erstelle ich mit mkinitcpio -p linux
die neue Initial Ramdisk und verlasse anschließend das arch-chroot
Damit der USB-Stick sowohl mit als auch ohne UEFI funktioniert habe ich mich an diesem gist orientiert. Der USB-Stick wird in den Code-Schnipseln als /dev/sdX
bezeichnet.
Auf dem USB-Stick sind am Ende drei Partitionen:
Für die Partitionierung verwende ich gdisk: sudo gdisk /dev/sdX
. Zuerst erstelle ich mit o
eine neue Partitionstabelle. Dann erstelle ich mit n
die Partitionen. Bei der ersten gebe ich beim Last Sector +512M
und beim Typ ef00
an, bei der zweiten +1536M
und 8300
und bei der dritten nichts (default) und 0700
. Anschließend wechsle ich mit x
in den Expertenmodus, setze mit a
für die erste Partition das legacy BIOS bootable
flag, erstelle mit n
den MBR und schreibe die Änderungen mit w
auf den Stick.
Dann formatiere ich die Partitionen:
sudo mkfs.fat -F 32 /dev/sdX1
sudo mkfs.ext4 -O "^has_journal" /dev/sdX2
sudo mkfs.exfat -n LABEL /dev/sdX3
Als nächstes wird der Bootloader installiert. Zuerst mounte ich die EFI-Partition nach /mnt
: sudo mount /dev/sdX1 /mnt
. Für UEFI erstelle ich den Ordner /EFI/BOOT mkdir -p /mnt/EFI/BOOT
und kopiere die syslinux-Dateien in diesen: cp -r /usr/lib/syslinux/efi64/* /mnt/EFI/BOOT/
. Zusätzlich muss noch die syslinux.efi
-Datei in BOOTX64.efi
umbenannt werden: mv /mnt/EFI/BOOT/syslinux.efi /mnt/EFI/BOOT/BOOTX64.efi
. Für den Legacy BIOS Support schreibe ich die /usr/lib/syslinux/bios/gptmbr.bin
auf den Stick: sudo dd if=/usr/lib/syslinux/bios/gptmbr.bin of=/dev/sdX
, führe sudo extlinux --install /mnt
aus und kopiere die Syslinux-Dateien nach /syslinux
:
mkdir /mnt/syslinux
cp -r /usr/lib/syslinux/bios/* /mnt/syslinux/
Für beide Boot-Methoden gibt es je eine syslinux.cfg, die sich nur durch die relativen Pfade zum Kernel und zur Initial Ramdisk unterscheiden. Zuerst finde ich mit sudo blkid
die UUID der ext4-partition heraus:
/mnt/EFI/BOOT/syslinux.cfg
:
DEFAULT arch
PROMPT 0 # Set to 1 if you always want to display the boot: prompt
TIMEOUT 50
UI menu.c32
MENU TITLE Arch Linux
MENU COLOR border 30;44 #40ffffff #a0000000 std
MENU COLOR title 1;36;44 #9033ccff #a0000000 std
MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all
MENU COLOR unsel 37;44 #50ffffff #a0000000 std
MENU COLOR help 37;40 #c0ffffff #a0000000 std
MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std
MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std
MENU COLOR msg07 37;40 #90ffffff #a0000000 std
MENU COLOR tabmsg 31;40 #30ffffff #00000000 std
LABEL arch
MENU LABEL Arch Linux
LINUX ../../vmlinuz-linux
APPEND tarroot=UUID=<UUID>:/root.tgz
INITRD ../../initramfs-linux.img
LABEL hdt
MENU LABEL HDT (Hardware Detection Tool)
COM32 hdt.c32
LABEL reboot
MENU LABEL Reboot
COM32 reboot.c32
LABEL poweroff
MENU LABEL Poweroff
COM32 poweroff.c32
/mnt/syslinux/syslinux.cfg
:
DEFAULT arch
PROMPT 0 # Set to 1 if you always want to display the boot: prompt
TIMEOUT 50
UI menu.c32
MENU TITLE Arch Linux
MENU COLOR border 30;44 #40ffffff #a0000000 std
MENU COLOR title 1;36;44 #9033ccff #a0000000 std
MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all
MENU COLOR unsel 37;44 #50ffffff #a0000000 std
MENU COLOR help 37;40 #c0ffffff #a0000000 std
MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std
MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std
MENU COLOR msg07 37;40 #90ffffff #a0000000 std
MENU COLOR tabmsg 31;40 #30ffffff #00000000 std
LABEL arch
MENU LABEL Arch Linux
LINUX ../vmlinuz-linux
APPEND tarroot=UUID=<UUID>:/root.tgz
INITRD ../initramfs-linux.img
LABEL hdt
MENU LABEL HDT (Hardware Detection Tool)
COM32 hdt.c32
LABEL reboot
MENU LABEL Reboot
COM32 reboot.c32
LABEL poweroff
MENU LABEL Poweroff
COM32 poweroff.c32
Jetzt müssen nur noch Kernel, Initial Ramdisk und das Dateisystem (root.tgz) auf den Stick kopiert werden. Hierzu verwende ich ein Script, weshalb ich erst einmal die EFI-Partition wieder aushänge: sudo umount /mnt
Da ich zum Beispiel den Pacman-cache nicht in die Tar-Datei kopieren möchte, erstelle ich mir eine Blacklist: $HOME/stick-tar-exclude
./var/cache/pacman/pkg/*
./boot/*
Um die benötigten Dateien zu erstellen und später den Stick zu updaten nutze ich folgendes Script: $HOME/update-stick.sh
#!/bin/bash
echo "Mounting boot to /mnt..."
mount /dev/disk/by-uuid/<EFI UUID> /mnt
echo "Copying Kernel and initramfs..."
cp $HOME/stick/boot/vmlinuz-linux $HOME/stick/boot/initramfs-linux.img /mnt
echo "Unmounting boot..."
umount /mnt
echo "Mounting stick to /mnt..."
mount /dev/disk/by-uuid/<ext4 UUID> /mnt
echo "Packing rootfs..."
bsdtar cpf - --exclude-from=$HOME/stick-tar-exclude -C $HOME/stick/ . | pigz > /mnt/root.tgz
echo "Sync..."
sync
echo "Unmounting stick..."
umount /mnt
echo "done"
Abschließend führe ich es aus: sudo ./update-stick.sh
Nun habe ich einen USB-Stick, der beim booten das Root-Dateisystem in den RAM schreibt und anschließend gefahrlos abgezogen werden kann.
Um das System upzudaten chroote ich in den Ordner und update das System normal mit pacman. Anschließend nutze ich das Script um die Änderungen auf den Stick zu schreiben:
sudo arch-chroot stick
pacman -Syu
exit
sudo ./update-sitck.sh