#!/bin/sh : ${ROOT_MKFS_CMD:=mkfs.ext4 -q} : ${ROOT_FS_TYPE:=ext4} losetup() { /sbin/losetup "$@"; } luks_secret() { local parms=$-; # this junk keeps set -x from being too annoying set +x [ -n "$luks_secret" ] || luks_secret="$(head -c256 /dev/urandom)" printf %s "$luks_secret" case $parms in *x*) set -x; set -x ;; esac } floor4() { # Negatives round up, but aren't used. echo $(($1 / 4 * 4)) } ceil4() { local x="$1" [ $((x % 4)) -eq 0 ] || x=$((x + 4 - x % 4)) printf '%d\n' "$x" } init_samizdat() { local imgfile="$1" megs="$2" keyfile="$3" dev init_samizdat_blockdev "$imgfile" "$megs" "$keyfile" || return local blockdev=/dev/mapper/samizdatcrypt uuid for fs in /cdrom/live/*.btrfs; do losetup -f "$fs" || return done modprobe btrfs || return btrfs device scan || return uuid=$(choose_uuid) || return [ "$uuid" ] mount -t btrfs -o subvol=ROOT UUID="$uuid" /root || return btrfs device add "$blockdev" /root || return mount -o rw,remount /root || return btrfs subvolume create /root/gpg || return # TODO: Restart gpg agent with stored credentials. # TODO: Actually, if we unconditionally added a ramdisk, we could go # ahead with the boot, and then do this whole thing from the installed # system (after pivot_root). That way, there would be no need to # restart. # Really, the option to install could be identical to the option to # boot from RAM except that it would record the intent to install; or # else it could be removed entirely, with an option to persist added # as some kind of UI element. mv /gpg/gnupghome /root/gpg/ || return bootdone root-mounted } # 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/loop*) seen_loop=t;; esac [ "$seen_loop" ] && echo "$seen_devs $seen_uuid" done | uniq | sort -nr | head -n1 | (read _ x; echo $x) } open_samizdat() { open_samizdat_blockdev "$@" || return local blockdev=/dev/mapper/samizdatcrypt fs for fs in /cdrom/live/*.btrfs; do losetup -f "$fs" || return done modprobe btrfs || return btrfs device scan || return mount -t btrfs -o subvol=ROOT "$blockdev" /root || return LoSetup -D bootdone root-mounted } init_samizdat_lodev() { local imgfile="$1" megs=$(ceil4 "$2") truncate -s ${megs}M "$imgfile" || return dev=$(losetup -f) && losetup "$dev" "$imgfile" || return echo "$dev" } open_samizdat_blockdev() { local imgfile="$1" keyfile="$2" dev local cryptname=samizdatcrypt dev=$(losetup -f) && losetup "$dev" "$imgfile" || return gpg2 --verify "$keyfile" || return # The first --decrypt merely strips the signature. The option is # poorly named for that case. gpg2 --decrypt "$keyfile" | gpg2 --decrypt | cryptsetup --key-file - luksOpen "$dev" "$cryptname" || return [ -b /dev/mapper/"$cryptname" ] || return } init_samizdat_blockdev() { local imgfile="$1" megs="$2" keyfile="$3" dev local cryptname=samizdatcrypt dev=$(init_samizdat_lodev "$imgfile" "$megs") || return [ ! -b /dev/mapper/"$cryptname" ] || return luks_secret >/dev/null luks_secret | gpg2 --default-recipient-self --encrypt --armor | gpg2 --clearsign --output "$keyfile" || return luks_secret | cryptsetup luksFormat "$dev" - || return cryptsetup luksDump "$dev" >&2 luks_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 } init_gpg() { bootwait samizdat-cdrom export GNUPGHOME=/gpg/gnupghome (umask 077; rsync --exclude '/luks-key*' --ignore-existing -rpP /cdrom/samizdat/gpg/ /gpg/) if samizdat-password-agent >/var/log/samizdat-password-agent.log 2>&1; then clear true else false 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 }