#!/bin/sh . loop-layer.sh . mdadm-dup.sh losetup() { /sbin/losetup "$@"; } ceil4() { local x="$1" [ $((x % 4)) -eq 0 ] || x=$((x + 4 - x % 4)) printf '%d\n' "$x" } kernel_commandline_has() { local v="$1" c read c < /proc/cmdline for c in $c do case "$c" in "$v"|"$v"=*) true; return;; esac done false } netbooted() { kernel_commandline_has BOOTIF } cdrom_has_rootfs() { if netbooted then false else bootwait samizdat-cdrom [ -d /cdrom/rootfs ] fi } losetup_layers() { if [ -e /dev/disk/by-partlabel/samizdat-rootfs ] then # TODO: prevent raciness umount /dev/disk/by-partlabel/samizdat-rootfs if [ -e /dev/disk/by-partlabel/samizdat-patchfs ] then umount /dev/disk/by-partlabel/samizdat-patchfs mountdev=/dev/disk/by-partlabel/samizdat-patchfs else mountdev=/dev/disk/by-partlabel/samizdat-rootfs fi else bootwait samizdat-nbd-dev local dev for dev in nbd0 nbd1 do dd if=/dev/zero of=/$dev.rw bs=1M count=10 dm_snapshot /dev/$dev /$dev.rw done fi } init_samizdat() { local blockdev="$1" imgfile="$2" uuid losetup_layers || return modprobe btrfs || return btrfs device scan || return if [ "$mountdev" ] then mount -t btrfs "$mountdev" /root || return else uuid=$(choose_uuid) || return [ "$uuid" ] || return mount -t btrfs UUID="$uuid" /root || return fi btrfs device add "$blockdev" /root || return mount -o rw,remount /root || return samizdat_movemounts "$imgfile" || return initialize_root_filesystem || return } movemounts() { # Move mounted filesystems to the future root filesystem local dev mp rest target while read dev mp rest; do case "$mp" in /root/*|/root|/|/proc|/dev|/dev/pts|/sys|/run) continue ;; *) target=/root/$mp ;; esac mkdir -p "$target" mount -n -o move "$mp" "$target" done /dev/null 2>&1 } make_subvolume_idem() { is_subvolume "$1" && return if [ -d "$1" ]; then rmdir "$1" || mv "$1" "$1"~ btrfs subvolume create "$1" mv "$1"~/* "$1"~/.* "$1"/ 2>&1 rmdir "$1"~ else if [ -e "$1" ]; then rm -f "$1" fi btrfs subvolume create "$1" fi } initialize_root_filesystem() { local uhome=/home/u local uhome_subs="${uhome} ${uhome}/.cache ${uhome}/.stack ${uhome}/.rustup ${uhome}/src/fsmgr/_build ${uhome}/Downloads ${uhome}/.mozilla" for d in /root /srv /var/cache/apt/archives /home $uhome_subs do make_subvolume_idem /root/${d} done chroot /root chown -R u:u ${uhome} copy_execs sbin mdadm dmsetup cryptsetup fsck.hfsplus copy_execs bin btrfs rsync # Copy these over unconditionally, because they ought to remain in sync with # the initrd. cp /bin/mdadm-dup.sh /root/sbin/ cp /bin/samizdat-eject.sh /root/sbin/ cp /bin/firstboot.service /root/etc/systemd/system/ ln -s /etc/systemd/system/firstboot.service /root/etc/systemd/system/multi-user.target.wants/ sed -i -e 's/^root:x:/root::/' /root/etc/passwd cp /patchroot/* /root/root/ true } # Get the uuid of the filesystem with the most devices, # excluding filesystems that don't incorporate loop devices. # This is used to choose the latest seed -- which should have # the most layers. choose_uuid() { local seen_loop= seen_uuid= seen_devs= btrfs filesystem show | while read line; do case "$line" in Label*) seen_uuid=${line##*uuid: } seen_devs= seen_loop= ;; *Total\ devices*) seen_devs=${line#*Total devices } seen_devs=${seen_devs%% *} ;; *path\ /dev/mapper/*) seen_loop=t;; esac [ "$seen_loop" ] && echo "$seen_devs $seen_uuid" done | uniq | sort -nr | head -n1 | (read _ x; echo $x) } num_devices() { btrfs inspect-internal dump-super "$1" | sed -ne 's/^num_devices\t\t*//p' } filesystem_incomplete() { [ "$(num_devices "$1")" != 1 ] } partition_new_hard_drive_DESTROYING_EVERYTHING() { local target="$1" sz=2910 # [ "$(parted -sm "$target" print | grep -c :)" = 1 ] || return parted "$target" -sm \ unit MiB \ mklabel gpt \ mkpart samizdat-grub-incomplete 1 8 \ set 1 bios_grub on \ mkpart samizdat-plaintext-incomplete btrfs 64 $((sz + 64)) \ mkpart samizdat-luks-encrypted-incomplete $((sz + 64)) 100% \ && udevadm settle } mark_partitions_as_complete() { local dev="$1" # TODO: Verify existing names parted "$dev" -sm \ name 1 samizdat-grub \ name 2 samizdat-plaintext \ name 3 samizdat-luks-encrypted } open_samizdat() { local blockdev=/dev/mapper/samizdatcrypt fs if filesystem_incomplete "$blockdev"; then losetup_layers fi modprobe btrfs || return btrfs device scan -u || true if ! btrfs device scan || ! btrfs device ready "$blockdev" then if ! filesystem_incomplete "$blockdev" then btrfstune -m "$blockdev" btrfs device ready "$blockdev" || : get used to disappointment fi fi mount -t btrfs "$blockdev" /root || return samizdat_movemounts "$imgfile" LoSetup -D } init_samizdat_lodev() { local imgfile="$1" megs=$(ceil4 "$2") dev truncate -s ${megs}M "$imgfile" || return dev=$(losetup -f) && losetup "$dev" "$imgfile" || return echo "$dev" } open_samizdat_blockdev_from_loop() { local imgfile="$1" keyfile="$2" dev dev=$(losetup -f) && losetup "$dev" "$imgfile" || return open_samizdat_blockdev "$dev" "$keyfile" } open_samizdat_blockdev() { local dev="$1" keyfile="$2" local cryptname=samizdatcrypt decrypted_keyfile=/luks.secret."${dev##*/}" if [ -b /dev/mapper/"$cryptname" ] then cryptsetup luksClose "$cryptname" || return fi if [ ! -e "$decrypted_keyfile" ] then echo -n secret > "$decrypted_keyfile" fi cryptsetup --key-file "$decrypted_keyfile" luksOpen "$dev" "$cryptname" || return [ -b /dev/mapper/"$cryptname" ] || return } init_samizdat_blockdev() { local dev="$1" keyfile="$2" local cryptname=samizdatcrypt [ ! -b /dev/mapper/"$cryptname" ] || return echo -n secret | cryptsetup -v luksFormat "$dev" - || return cryptsetup luksDump "$dev" >&2 echo -n secret | cryptsetup --key-file - luksOpen "$dev" "$cryptname" || return [ -b /dev/mapper/"$cryptname" ] || return } majmin() { local dev="$1" major minor eval $(stat -c 'major=%t minor=%T' "$dev") || return [ "$major" -a "$minor" ] || return printf '%d:%d\n' 0x$major 0x$minor } cryptdev_to_dev() { local dev="$1" majmin majmin=$(majmin "$dev") || return set -- /sys/dev/block/$majmin/slaves/* [ $# = 1 ] || return cryptsetup status "$dev" |while read k v; do if [ "$k" = device: ]; then echo $v; break; fi; done } cryptdev_to_backing_file() { local dev="$1" majmin result majmin="$(majmin "$dev")" || return set -- /sys/dev/block/$majmin/slaves/* [ $# = 1 ] || return read result < "$1"/loop/backing_file || return printf '%s\n' "$result" } lodev_to_file() { local result majmin dev="$1" majmin="$(majmin "$dev")" || return read result < /sys/dev/block/$majmin/loop/backing_file || return printf '%s' "$result" } mountpoint_to_dev() { local wantmp="$1" dev mp rest mountpoint -q "$wantmp" || return while read dev mp rest; do if [ "$mp" = "$wantmp" ]; then echo "$dev"; return; fi; done < /proc/mounts return 1 } get_cdrom_sizelimit() { # returns bytes local dev="$1" sectors sectors=$(blockdev --getsz "$dev") || return if dd count=2 if="$dev" bs=2048 skip=$((sectors/4 - 2)) of=/dev/null 2>/dev/null; then return else echo $(((sectors-8)*512)) fi } start_meter() { local startmsg="$*" (exec >&4 clear echo -n $startmsg set +x while sleep 2; do echo -n . done) & meterpid=$! } stop_meter() { local endmsg="$*" kill $meterpid echo " $endmsg" >&4 }