osi-tools/bin/osi-mount

162 lines
4.6 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env bash
2018-08-18 18:42:42 +00:00
# Copyright (C) 2018 Luke Shumaker
# Copyright (C) 2023-2024 Umorpha Systems
2018-08-18 18:42:42 +00:00
# SPDX-License-Identifier: AGPL-3.0-or-later
declare -r NAME=osi-mount
2023-10-23 22:19:22 +00:00
declare -r VERSION=20231023
set -euE
install_prefix="$(realpath --logical --canonicalize-missing -- "${BASH_SOURCE[0]}/../..")"
readonly install_prefix
source "${install_prefix}/lib/osi.sh"
main() {
local arg_orig=("$@")
local arg_mode=outside
local arg_user=
local arg_rwdirs=()
2023-10-30 21:14:27 +00:00
local arg_mountopt=
local args
2023-10-30 21:14:27 +00:00
if ! args="$(getopt -n "${0##*/}" -o o:hV -l inside,user,root,rwdir:,options:,help,version -- "$@")"; then
arg_mode=error
else
eval "set -- $args"
while true; do
case "$1" in
2018-08-14 20:54:09 +00:00
--inside) shift; arg_mode=inside;;
--user|--root)
if [[ -n $arg_user ]]; then
error 0 "Multiple --user/--root flags given"
arg_mode=error
fi
arg_user=${1#--}
shift
;;
--rwdir) shift; arg_rwdirs+=("$1"); shift;;
2023-10-30 21:14:27 +00:00
-o|--options) shift; arg_mountopt="$1"; shift;;
-V|--version) shift; arg_mode=version;;
-h|--help) shift; arg_mode=usage;;
--) shift; break;;
*) error $EXIT_FAILURE 'Internal error. The programmer writing this tool screwed up.';;
esac
done
case "$arg_mode" in
outside)
case "$#" in
0) error 0 "Must specify an image, mountpoint, and a command"; arg_mode=error;;
1) error 0 "Must specify a mountpoint and a command"; arg_mode=error;;
2) error 0 "Must specify a command"; arg_mode=error;;
esac
if [[ -z $arg_user ]]; then
error 0 "Must specify either --root or --user"
arg_mode=error
fi
;;
esac
2018-08-14 20:54:09 +00:00
case "$arg_mode" in
outside|inside)
arg_device=$(realpath -- "$1")
2018-08-14 20:54:09 +00:00
arg_mountpoint=$2
arg_cmd=("${@:3}")
;;
esac
fi
case "$arg_mode" in
error)
print "Try '%q --help' for more information" "${0##*/}" >&2
2024-01-05 02:13:25 +00:00
return $EXIT_INVALIDARGUMENT
;;
version)
print "%s (osi-tools) %s" "$NAME" "$VERSION"
return $EXIT_SUCCESS
;;
usage)
2023-11-08 04:39:42 +00:00
print 'Usage: sudo %s [OPTIONS] --<user|root> FILENAME.img MOUNTPOINT COMMAND...' "${0##*/}"
print 'Operating System Image: Mount'
echo
# 000000000011111111112222222222333333333344444444445555555555666666666677777777778
# 012345678901234567890123456789012345678901234567890123456789012345678901234567890
2023-11-08 04:39:42 +00:00
print 'Run COMMAND with FILENAME.img mounted at MOUNTPOINT.'
echo
print 'OPTIONS:'
# --inside is internal-only; undocumented
2023-10-30 21:14:27 +00:00
print ' --user mount the image RO, run COMMAND as SUDO_USER'
print ' --root mount the image RW, run COMMAND as root, protect the rest of the system'
print ' --rwdir=DIR keep DIR read+write, even when --root'
print ' -o, --options LIST comma-separated list of mount options'
echo
print ' -h, --help display this help'
print ' -V, --version output version information'
return $EXIT_SUCCESS
;;
outside)
needs_root
needs_sudo
if out="$(losetup --associated "$arg_device")" && [[ -n $out ]]; then
error 0 "Seems to already be mounted somewhere: %s" "$arg_device"
printf '%s\n' "$out"
exit $EXIT_FAILURE
fi
# I feel guilty pulling in the hugeness of systemd-run
# just for `--property=ProtectSystem=strict`, but I
# don't want to implement my own protection system.
flags=(
--pty --same-dir --wait --collect --quiet
--setenv=SUDO_UID="$SUDO_UID"
2024-01-26 01:43:54 +00:00
${SOURCE_DATE_EPOCH:+"--setenv=SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH"}
${TMPDIR:+"--setenv=TMPDIR=$TMPDIR"}
--property=PrivateMounts=true
)
case "$arg_user" in
user) :;;
root)
flags+=(
--property=ProtectSystem=strict
2024-01-05 02:23:58 +00:00
--property=ProtectHome=read-only
2023-10-30 21:14:27 +00:00
--property=ReadWritePaths="$arg_device"
"${arg_rwdirs[@]/#/--property=ReadWritePaths=}"
)
;;
esac
systemd-run "${flags[@]}" -- "${BASH_SOURCE[0]}" --inside "${arg_orig[@]}"
2018-08-14 15:37:29 +00:00
if out="$(losetup --associated "$arg_device")" && [[ -n $out ]]; then
error 0 "umount'ed, but file is still associated with a loop device: %s" "$arg_device"
printf '%s\n' "$out"
exit $EXIT_FAILURE
2018-08-13 04:47:18 +00:00
fi
;;
2018-08-14 20:54:09 +00:00
inside)
needs_root
2018-08-14 15:37:29 +00:00
needs_sudo
2018-08-14 20:54:09 +00:00
case "$arg_user" in
2023-10-30 21:14:27 +00:00
user) arg_mountopt+="${arg_mountopt:+,}ro";;
root) arg_mountopt+="${arg_mountopt:+,}rw,discard";;
2018-08-14 15:37:29 +00:00
esac
2018-08-13 04:47:18 +00:00
2023-10-30 21:14:27 +00:00
cmd=(mount --no-mtab -o "$arg_mountopt" -- "$arg_device" "$arg_mountpoint")
r=0
out=$("${cmd[@]}" 2>&1) || r=$?
2023-10-30 21:13:27 +00:00
if (( r != 0 )); then
error $r '%s: %s' "${cmd[*]@Q}" "$out"
2018-08-13 04:47:18 +00:00
fi
2018-08-14 20:54:09 +00:00
case "$arg_user" in
2024-01-26 01:43:54 +00:00
user) _sudo -u "#${SUDO_UID}" -- "${arg_cmd[@]}";;
root) command -- "${arg_cmd[@]}";;
esac
;;
*) error $EXIT_FAILURE 'Internal error. The programmer writing this tool screwed up.';;
esac
}
main "$@"