mirror of
https://github.com/nchevsky/systemrescue-zfs.git
synced 2025-12-06 07:12:01 +01:00
340 lines
9.6 KiB
Bash
Executable file
340 lines
9.6 KiB
Bash
Executable file
#! /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 getopt’s 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
|