systemrescue-zfs/airootfs/usr/share/sysrescue/bin/mountall
2022-09-06 20:24:46 +02:00

340 lines
9.6 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! /usr/bin/env bash
#
# mountall - mount all suitable block devices
#
# Author: Gerd v. Egidy
# SPDX-License-Identifier: GPL-3.0-or-later
#
# see https://www.system-rescue.org/scripts/mountall/ for details
# abort on failures
set -o errexit -o pipefail -o noclobber -o nounset
print_help()
{
echo "mountall - mount all suitable block devices"
echo ""
echo "Usage:"
echo "mountall [-n|--no-bind] [-o|--ro|--readonly] [-v|--verbose]"
echo ""
echo "--no-bind Don't try to bind-mount /dev /proc and /sys when"
echo " the partition has these dirs"
echo "--readonly Mount read-only"
echo "--verbose Verbose output."
echo ""
echo "See https://www.system-rescue.org/scripts/mountall/ for details."
return
}
# error while parsing commandline parameters
argument_error()
{
echo "$1"
echo
echo "---------------------------------"
echo
print_help
exit 2
}
parse_args()
{
# adapted from https://stackoverflow.com/a/29754866 by Robert Siemer
# version edited Mar 4 '21 at 0:11, licensed under CC BY-SA 4.0 due to Stackoverflow Terms of Service
# https://creativecommons.org/licenses/by-sa/4.0/
# -allow a command to fail with !s side effect on errexit
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
! getopt --test > /dev/null
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
echo 'ERROR: `getopt --test` failed in this environment'
exit 1
fi
local OPTIONS="novh"
local LONGOPTS="no-bind,readonly,ro,verbose,help"
# option variables as globals, set to default values
declare -g BIND=1
declare -g READONLY=0
declare -g VERBOSE=0
# -regarding ! and PIPESTATUS see above
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via -- "$@" to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
# e.g. return value is 1
# then getopt has complained about wrong arguments to stdout
echo
print_help
exit 2
fi
# read getopts output this way to handle the quoting right:
eval set -- "$PARSED"
while true; do
case "$1" in
-n|--no-bind)
BIND=0
shift
;;
-o|--readonly|--ro)
READONLY=1
shift
;;
-v|--verbose)
VERBOSE=1
shift
;;
-h|--help)
print_help
exit 0
;;
--)
shift
break
;;
*)
echo "ERROR: Argument parsing logic bug"
exit 2
;;
esac
done
# we want no positional arguments
[[ $# -ne 0 ]] && argument_error "ERROR: positional arguments not allowed"
true
}
is_cryptodev()
{
local DEV="$1"
[[ $VERBOSE -eq 1 ]] && echo "Checking if $DEV is an encrypted device..."
BLKID=$(blkid "$DEV")
[[ $VERBOSE -eq 1 ]] && echo "blkid output: $BLKID"
if [[ "$BLKID" != *" TYPE="* ]] && [[ "$BLKID" != "TYPE="* ]]; then
# blkid must return a "TYPE" tag for it to be mountable at all
[[ $VERBOSE -eq 1 ]] && echo "$DEV is not mountable (no TYPE)"
false
return
fi
if [[ "$BLKID" != *"TYPE=\"crypto"* ]]; then
[[ $VERBOSE -eq 1 ]] && echo "$DEV is not not encrypted"
false
return
fi
# is the device already opened?
DEVNAME=$(basename "$DEV")
if /usr/bin/test -d /sys/devices/virtual/block/*/slaves/$DEVNAME ; then
[[ $VERBOSE -eq 1 ]] && echo "$DEV is already opened"
false
return
fi
[[ $VERBOSE -eq 1 ]] && echo "$DEV looks to be an encrypted device that could be opened"
true
}
is_mountable()
{
local DEV="$1"
[[ $VERBOSE -eq 1 ]] && echo "Checking if $DEV is mountable..."
BLKID=$(blkid "$DEV")
[[ $VERBOSE -eq 1 ]] && echo "blkid output: $BLKID"
if [[ "$BLKID" != *" TYPE="* ]] && [[ "$BLKID" != "TYPE="* ]]; then
# blkid must return a "TYPE" tag for it to be mountable at all
[[ $VERBOSE -eq 1 ]] && echo "$DEV is not mountable (no TYPE)"
false
return
fi
if [[ "$BLKID" == *"TYPE=\"linux_raid_member\""* ]] ||
[[ "$BLKID" == *"TYPE=\"LVM2_member\""* ]] ||
[[ "$BLKID" == *"TYPE=\"swap\""* ]] ||
[[ "$BLKID" == *"TYPE=\"crypto"* ]] ; then
# these are not directly mountable
[[ $VERBOSE -eq 1 ]] && echo "$DEV is not mountable (swap, RAID or LVM)"
false
return
fi
if findmnt --source "$DEV" >/dev/null 2>&1 ; then
[[ $VERBOSE -eq 1 ]] && echo "$DEV is already mounted"
false
return
fi
# special mounted check for btrfs filesystems:
# when they consist of multiple parts, you can mount them with either device name
if [[ "$BLKID" == *"TYPE=\"btrfs\""* ]] &&
/usr/bin/btrfs device stats "$DEV" >/dev/null 2>&1; then
[[ $VERBOSE -eq 1 ]] && echo "$DEV is already mounted (btrfs)"
false
return
fi
[[ $VERBOSE -eq 1 ]] && echo "$DEV looks to be mountable"
true
}
# create a suitable dir below /mnt
# tries to use the block dev name, but uses a different one if already existing
create_mountpoint()
{
local DEV="$1"
[[ $VERBOSE -eq 1 ]] && echo "looking for a suitable mountpoint for $DEV"
DEVNAME=$(basename "$DEV")
MOUNTPOINT="/mnt/$DEVNAME"
local NUMBER=1
while [[ -e "$MOUNTPOINT" ]]; do
[[ $VERBOSE -eq 1 ]] && echo "wanted mountpoint $MOUNTPOINT already existing"
# check if it is an empty directory and unmounted, then we can still use it
if [[ -d "$MOUNTPOINT" ]] && \
find "$MOUNTPOINT" -maxdepth 0 -type d -empty | grep -q "." && \
! findmnt --mountpoint "$MOUNTPOINT" >/dev/null 2>&1 ; then
[[ $VERBOSE -eq 1 ]] && echo "wanted mountpoint $MOUNTPOINT existing, but empty and nothing mounted"
break
fi
MOUNTPOINT="/mnt/${DEVNAME}_${NUMBER}"
NUMBER=$[$NUMBER+1]
[[ $VERBOSE -eq 1 ]] && echo "testing new mountpoint: $MOUNTPOINT"
done
[[ $VERBOSE -eq 1 ]] && echo "creating mountpoint $MOUNTPOINT"
if ! mkdir -p "$MOUNTPOINT"; then
echo "error creating mountpoint $MOUNTPOINT"
false
return
fi
}
# if there is /dev /proc /sys in the just mounted path, bind mount them to ours
# this allows using some commands in a chroot (like grub-install)
try_bind_mounts()
{
local MOUNTPOINT="$1"
[[ $VERBOSE -eq 1 ]] && echo "checking if to create bind-mounts below $MOUNTPOINT"
if [[ -d "${MOUNTPOINT}/dev" ]]; then
if mount --bind "/dev" "${MOUNTPOINT}/dev"; then
echo "bind mounted /dev to ${MOUNTPOINT}/dev"
else
echo "failed bind mounting /dev to ${MOUNTPOINT}/dev"
fi
fi
if [[ -d "${MOUNTPOINT}/sys" ]]; then
if mount --bind "/sys" "${MOUNTPOINT}/sys"; then
echo "bind mounted /sys to ${MOUNTPOINT}/sys"
else
echo "failed bind mounting /sys to ${MOUNTPOINT}/sys"
fi
fi
if [[ -d "${MOUNTPOINT}/proc" ]]; then
if mount --bind "/proc" "${MOUNTPOINT}/proc"; then
echo "bind mounted /proc to ${MOUNTPOINT}/proc"
else
echo "failed bind mounting /proc to ${MOUNTPOINT}/proc"
fi
fi
}
try_mount()
{
local DEV="$1"
create_mountpoint "$DEV" || return
echo -n "mounting $DEV to $MOUNTPOINT... "
local OPTIONS=""
if [[ $READONLY -eq 1 ]]; then
[[ $VERBOSE -eq 1 ]] && echo "mounting read-only"
OPTIONS="--read-only"
fi
if ! mount $OPTIONS "$DEV" "$MOUNTPOINT"; then
echo "error mounting $DEV to $MOUNTPOINT (options $OPTIONS)"
return
fi
echo "OK"
if [[ $BIND -eq 1 ]]; then
try_bind_mounts "$MOUNTPOINT"
fi
}
#################################
# execution begins here
parse_args "$@"
# loop through regular block devices
for BLKDEV in $(ls -1 "/sys/class/block"); do
# handle device mapper / lvm volumes in the 2nd loop for nice names
[[ -d "/sys/class/block/${BLKDEV}/dm" ]] && continue
if is_cryptodev "/dev/${BLKDEV}"; then
if /usr/bin/cryptsetup open "/dev/${BLKDEV}" "${BLKDEV}_crypt"; then
# we will handle the opened crypto volume in the mapper loop below
continue
else
echo "error opening ${BLKDEV}"
fi
fi
if is_mountable "/dev/${BLKDEV}"; then
try_mount "/dev/${BLKDEV}"
fi
done
# if we just opened a cryptodev we have to wait until it appears in /dev/mapper
[[ $VERBOSE -eq 1 ]] && echo "waiting until recently opened lvm devices appear in /dev/mapper"
udevadm settle --timeout=10
# loop through device mapper / lvm volumes
for LVMDEV in $(ls -1 "/dev/mapper"); do
# there is always one central control entry, skip it
[[ "$LVMDEV" == "control" ]] && continue
if is_cryptodev "/dev/mapper/${LVMDEV}"; then
if /usr/bin/cryptsetup open "/dev/mapper/${LVMDEV}" "${LVMDEV}_crypt"; then
# check if we can mount the opened device below
LVMDEV="${LVMDEV}_crypt"
else
echo "error opening ${LVMDEV}"
fi
fi
if is_mountable "/dev/mapper/${LVMDEV}"; then
try_mount "/dev/mapper/${LVMDEV}"
fi
done
exit 0