From a9a594f2034c0a077f4d17a3b7f0caf1cf50626e Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Sun, 1 May 2016 01:34:54 -0400 Subject: rename now-ill-named "lvm-create" --- src/initrd/btrfs-create.sh | 299 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 src/initrd/btrfs-create.sh (limited to 'src/initrd/btrfs-create.sh') diff --git a/src/initrd/btrfs-create.sh b/src/initrd/btrfs-create.sh new file mode 100644 index 0000000..d4a8bdf --- /dev/null +++ b/src/initrd/btrfs-create.sh @@ -0,0 +1,299 @@ +#!/bin/sh + +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" +} + +. loop-layer.sh + +losetup_layers() +{ + bootwait samizdat-cdrom + local fs fs_rw + for fs in /cdrom/rootfs/*.btrfs; do + fs_rw=/"${fs##*/}".rw + dd if=/dev/zero of="$fs_rw" bs=1M count=10 + losetup_snapshot "$fs" "$fs_rw" || return + done +} + +init_samizdat() +{ + local blockdev="$1" imgfile="$2" uuid + + losetup_layers || return + modprobe btrfs || return + btrfs device scan || return + + uuid=$(choose_uuid) || return + [ "$uuid" ] || return + + mount -t btrfs UUID="$uuid" /root || return + + btrfs device add "$blockdev" /root || return + mount -o rw,remount /root || return + samizdat_movemounts "$imgfile" || return + + initialize_root_filesystem || return + + bootdone root-mounted +} + +samizdat_movemounts() +{ + local imgfile="$1" mountpoint + + if [ "$imgfile" ]; then + mountpoint=$(mountpoint_of "$imgfile") || return + mkdir /root/outerfs + mount -o move "$mountpoint" /root/outerfs + fi + mkdir /root/cdrom + mount -o move /cdrom /root/cdrom + mkdir -p /run/initramfs/samizdat/log + cp /var/log/* /run/initramfs/samizdat/log + true +} + +mountpoint_of() +{ + local f="$1" + while ! mountpoint -q "$f"; do + f=$(dirname "$f") + [ "$f" != '.' ] || return 1 + done + printf '%s\n' "$f" +} + +initialize_root_filesystem() +{ + rm -r /root/root + btrfs subvolume create /root/root || return + mv /gpg/gnupghome /root/root/.gnupg || return + + rmdir /root/srv + btrfs subvolume create /root/srv + rm -r /root/var/cache/apt/archives + btrfs subvolume create /root/var/cache/apt/archives || return + + rmdir /root/home + btrfs subvolume create /root/home || return + + [ -x /root/sbin/mdadm ] || cp /sbin/mdadm /root/sbin/ + # 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/ + + 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) +} + +filesystem_incomplete() +{ + local n + n=$(btrfs filesystem show "$1" | sed -ne 's/.*Total devices \([^ ]*\) .*/\1/p') + [ "$n" != 1 ] +} + +open_samizdat() +{ + local imgfile="$1" keyfile="$2" + open_samizdat_blockdev "$imgfile" "$keyfile" || return + local blockdev=/dev/mapper/samizdatcrypt fs + + # For this part, we don't necessarily need the cdrom. + # Unfortunately the init_gpg code is still getting the GPG key there. + if filesystem_incomplete "$blockdev"; then + losetup_layers + fi + modprobe btrfs || return + btrfs device scan || return + mount -t btrfs "$blockdev" /root || return + samizdat_movemounts "$imgfile" + LoSetup -D + bootdone root-mounted +} + +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() +{ + 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 + mkdir -p "$GNUPGHOME" + (umask 077; rsync --exclude '/luks-key*' --ignore-existing -rpP /cdrom/gnupghome/ "$GNUPGHOME") + + 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 +} + -- cgit v1.2.3