From e213dac839137b9a51f8349b81f2d29a6d7c27d8 Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Mon, 25 Apr 2016 20:36:49 -0400 Subject: Cdrom duplication The cdrom can now be copied into the btrfs filesystem (if there is enough space) and hot-removed. This isn't done automatically, though. In fact, the code to do it isn't available anymore after boot; it would have to be copied back onto the system to be run. --- old-school/grok-block | 4 +- old-school/mdadm-dup.sh | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 old-school/mdadm-dup.sh diff --git a/old-school/grok-block b/old-school/grok-block index 62b063f..3f61c4a 100755 --- a/old-school/grok-block +++ b/old-school/grok-block @@ -172,7 +172,9 @@ then # Recognize and mount the Samizdat if ! mountpoint -q /cdrom; then mkdir -p /cdrom - (retry_mount -t iso9660 -r "$DEVNAME" /cdrom && bootdone samizdat-cdrom) & + . mdadm-dup.sh + mount_cdrom "$DEVNAME" && bootdone samizdat-cdrom +# (retry_mount -t iso9660 -r "$DEVNAME" /cdrom && bootdone samizdat-cdrom) & fi else grok_block & diff --git a/old-school/mdadm-dup.sh b/old-school/mdadm-dup.sh new file mode 100644 index 0000000..27a39ca --- /dev/null +++ b/old-school/mdadm-dup.sh @@ -0,0 +1,165 @@ +dm_snapshot() +{ + # TODO: eliminate duplication; this function exists elsewhere in a less generalized form + local ro_file rw_file cutoff_size + ro_file=$1 + rw_file=$2 + cutoff_size=$3 + + local ro_dev rw_dev size new_dev_name persist chunksize + + if [ -b "$ro_file" ]; + then ro_dev=$ro_file + else ro_dev=$(LoSetup -r -f --show "$ro_file") || return + fi + + if [ -b "$rw_file" ]; + then rw_dev=$rw_file + else rw_dev=$(LoSetup -f --show "$rw_file") || return + fi + + if [ "$cutoff_size" -a "$cutoff_size" -gt 0 ]; then + size=$cutoff_size + else + size=$(blockdev --getsz "$ro_dev") || return + fi + + new_dev_name=${ro_dev##*/} + persist=p + chunksize=16 + dmsetup create "$new_dev_name" --table "0 $size snapshot $ro_dev $rw_dev $persist $chunksize" || return + wait_for_dm_device /dev/mapper/"$new_dev_name"; + echo /dev/mapper/"$new_dev_name" +} + +dm_snapshot_teardown() +{ + local dev="$1" + case "$dev" in + /dev/dm-*) + dmsetup table "$dev" | ( + read _ _ snapshot ro_dev rw_dev _ _ + [ "$snapshot" = snapshot ] || exit 1 + dmsetup remove "$dev" || exit 1 + # errors ignored because the loop dev can be automatically removed upon disuse already + losetup -d /dev/block/"$rw_dev" || true + eject /dev/block/"$ro_dev" || exit 1 + ) || return + ;; + *) return 1 ;; + esac +} + +wait_for_dm_device() +{ + # TODO: improve + while ! [ -e "$1" ]; do + sleep 1 + done +} + +mount_cdrom() +{ + local cdrom_dev="$1" + + local sectors md_dev=/dev/md0 cdrom_rw_file=/"${cdrom_dev##*/}".rw + + sectors=$(get_cdrom_sizelimit "$cdrom_dev") || return + + # TODO: do we even need this backing file? We do need to trick mdadm into + # thinking that this is a RW device, but previously we got away with just + # creating a loopback device. + dd if=/dev/zero of="$cdrom_rw_file" bs=1K count=32 || return + cdrom_rw_dev=$(dm_snapshot "$cdrom_dev" "$cdrom_rw_file" "$sectors") || return + mdadm_dup "$cdrom_rw_dev" "$md_dev" "$sectors" || return + mount -t iso9660 -r $md_dev /cdrom +} + +get_cdrom_sizelimit() +{ + # returns 512-byte sectors + local dev="$1" sectors + sectors=$(blockdev --getsz "$dev") || return + + # Check if we can read the last 8 sectors. With a TAO CDROM, we can't -- + # these sectors are faux, and not part of the ISO fs. If mdadm is allowed to + # read them, it will mark the device failed. + if dd count=2 if="$dev" bs=2048 skip=$((sectors/4 - 2)) of=/dev/null 2>/dev/null; then + echo $sectors + else + echo $((sectors - 8)) + fi +} + +mdadm_dup() +{ + local input_dev="$1" md_name="$2" sectors="$3" + + mdadm --build $md_name ${sectors:+--size=$((sectors / 2))} \ + --level=1 --raid-devices=1 --force --write-mostly "$input_dev" || return +} + + +mdadm_subdevices() +{ + local md_dev="$1" + mdadm -D "$md_dev" -Y | sed -ne 's/MD_DEVICE_.*_DEV=//p' +} + +Mdadm() +{ + mdadm "$@" + # r=$? + # mdadm -D "$1" + # sleep 2 + # return $r +} + +mdadm_copy_eject() # NOT INITRD; uses non-busybox "losetup" +{ + local md_dev="$1" output_file="$2" + + [ -b "$md_dev" ] || return + [ ! -e "$output_file" ] || return + + local output_dev sectors + + old_subdev=$(mdadm_subdevices "$md_dev"|head -n1) || return + [ -b "$old_subdev" ] || return + sectors=$(blockdev --getsz "$md_dev") || return + + truncate -s $((sectors * 512)) "$output_file" || return + output_dev=$(losetup -f --show "$output_file") || return + + Mdadm "$md_dev" --add "$output_dev" || return + Mdadm "$md_dev" --grow -n2 || return + + mdadm_wait_remove "$md_dev" "$old_subdev" || return + + Mdadm "$md_dev" --grow -n1 --force || return + dm_snapshot_teardown "$old_subdev" +} + +mdadm_wait_remove() +{ + # We should perhaps use mdadm --monitor's RebuildFinished event. + + local dev="$1" disk="$2" tries + if ! mdadm --wait "$dev"; then + tries=1000 + while ! mdadm --detail --test "$dev"; do + [ $tries -gt 0 ] || return 1 + sleep 1 + tries=$((tries-1)) + done + fi + + mdadm "$dev" --fail "$disk" || return 1 + tries=100 + while ! mdadm "$dev" --remove "$disk"; do + [ $tries -gt 0 ] || return 1 + sleep 1 + tries=$((tries-1)) + done + return 0 +} -- cgit v1.2.3