#!/bin/bash # Loopback disks for exercising the multi-disk install and storage paths. # # scripts/dev/lp-testdisk up [count] [size] # default 2 disks, 30G sparse # scripts/dev/lp-testdisk status # scripts/dev/lp-testdisk down # # Why loopback rather than a spare partition: the code paths that matter care # that a location sits on a DIFFERENT filesystem — appDir resolution, the # storage registry's device numbers, `st_dev` comparisons in the installer's # disk picker, and app-adopt's same-device mv vs cross-device copy. A loop # device gives all of that with a real ext4 superblock, its own st_dev and its # own free-space figures, and can be thrown away between runs. # # Sparse-allocated, so a 30G disk costs what is written to it, not 30G. set -u BACKING_DIR="${LP_TESTDISK_DIR:-/var/lib/lp-testdisks}" MNT_PREFIX="${LP_TESTDISK_MNT:-/mnt/lptest}" _need_root() { [[ $EUID -eq 0 ]] || { echo "lp-testdisk: run with sudo" >&2; exit 1; }; } up() { _need_root local count="${1:-2}" size="${2:-30G}" mkdir -p "$BACKING_DIR" local i img mnt for (( i=1; i<=count; i++ )); do img="$BACKING_DIR/disk$i.img" mnt="${MNT_PREFIX}$i" if ! [[ -f "$img" ]]; then truncate -s "$size" "$img" mkfs.ext4 -q -L "lptest$i" "$img" echo " created $img ($size, sparse)" fi mkdir -p "$mnt" if mountpoint -q "$mnt"; then echo " already mounted: $mnt" else mount -o loop "$img" "$mnt" || { echo " FAILED to mount $img" >&2; return 1; } # An install writes here as root and then hands ownership over, so # the mount point itself only needs to be traversable. chmod 0755 "$mnt" echo " mounted $img -> $mnt" fi done status } status() { local mnt printf ' %-16s %-10s %-8s %-8s %s\n' MOUNT DEV SIZE AVAIL "st_dev" for mnt in "${MNT_PREFIX}"*; do [[ -d "$mnt" ]] || continue if mountpoint -q "$mnt"; then printf ' %-16s %-10s %-8s %-8s %s\n' "$mnt" \ "$(findmnt -no SOURCE "$mnt" 2>/dev/null | xargs -r basename)" \ "$(findmnt -no SIZE "$mnt" 2>/dev/null)" \ "$(findmnt -no AVAIL "$mnt" 2>/dev/null)" \ "$(stat -c '%d' "$mnt" 2>/dev/null)" else printf ' %-16s %s\n' "$mnt" "(not mounted)" fi done printf ' %-16s %-10s %-8s %-8s %s\n' "/" \ "$(findmnt -no SOURCE --target / | tail -1 | xargs -r basename)" \ "$(findmnt -no SIZE --target / | tail -1)" \ "$(findmnt -no AVAIL --target / | tail -1)" \ "$(stat -c '%d' / 2>/dev/null)" } down() { _need_root local mnt for mnt in "${MNT_PREFIX}"*; do [[ -d "$mnt" ]] || continue if mountpoint -q "$mnt"; then umount "$mnt" 2>/dev/null || umount -l "$mnt" 2>/dev/null \ || { echo " busy, still mounted: $mnt" >&2; continue; } echo " unmounted $mnt" fi rmdir "$mnt" 2>/dev/null done } destroy() { _need_root down [[ -d "$BACKING_DIR" ]] && { rm -rf "$BACKING_DIR"; echo " removed $BACKING_DIR"; } } case "${1:-status}" in up) shift; up "$@" ;; status) status ;; down) down ;; destroy) destroy ;; *) echo "usage: lp-testdisk {up [count] [size]|status|down|destroy}" >&2; exit 2 ;; esac