130 lines
3.4 KiB
Bash
Executable File
130 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# 2018 Luke Shumaker
|
|
declare -r NAME=osi-mount
|
|
declare -r VERSION=20180812
|
|
|
|
set -euE
|
|
source "${BASH_SOURCE[0]%/*}/lib/osi.sh"
|
|
|
|
main() {
|
|
needs_root
|
|
|
|
local arg_mode=outside
|
|
local arg_user=
|
|
local args
|
|
if ! args="$(getopt -n "${0##*/}" -o hV -l inside:,user,root,help,version -- "$@")"; then
|
|
arg_mode=error
|
|
else
|
|
eval "args=($args)"
|
|
set -- "${args[@]}"
|
|
while true; do
|
|
case "$1" in
|
|
--inside) shift; arg_mode=$1; shift;;
|
|
--user|--root)
|
|
if [[ -n $arg_user ]]; then
|
|
error 0 "Multiple --user/--root flags given"
|
|
arg_mode=error
|
|
fi
|
|
arg_user=${1#--}
|
|
shift
|
|
;;
|
|
|
|
-V|--version) shift; arg_mode=version;;
|
|
-h|--help) shift; arg_mode=usage;;
|
|
--) shift; break;;
|
|
*) error 1 '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
|
|
fi
|
|
arg_device=$1
|
|
arg_mountpoint=$2
|
|
arg_cmd=("${@:3}")
|
|
|
|
case "$arg_mode" in
|
|
error) print "Try '%q --help' for more information" "${0##*/}" >&2; return 2;;
|
|
version)
|
|
print "%s (notsystemd) %s" "$NAME" "$VERSION"
|
|
return 0
|
|
;;
|
|
usage)
|
|
print 'Usage: %s [OPTIONS] --<user|root> FILENAME.img COMMAND...' "${0##*/}"
|
|
print 'Operating System Image: Mount'
|
|
echo
|
|
print 'OPTIONS:'
|
|
# --inside is internal-only; undocumented
|
|
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'
|
|
echo
|
|
print ' -h, --help display this help'
|
|
print ' -V, --version output version information'
|
|
return 0
|
|
;;
|
|
|
|
outside)
|
|
unshare --mount "${BASH_SOURCE[0]}" --inside="$arg_user" "${args[@]}"
|
|
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 1
|
|
fi
|
|
;;
|
|
user|root)
|
|
needs_sudo
|
|
mount --make-rslave /
|
|
case "$arg_mode" in
|
|
user)
|
|
mount_opt=ro
|
|
losetup_flags=(--read-only)
|
|
;;
|
|
root)
|
|
mount_opt=rw
|
|
losetup_flags=()
|
|
# TODO: make the rest of the system RO
|
|
;;
|
|
esac
|
|
|
|
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 1
|
|
fi
|
|
# Use manual losetup creation/desctruction
|
|
# because the "autoclear" flag is lazy, and we
|
|
# need to be eager.
|
|
loopdev=$(losetup --find --show "${losetup_flags[@]}" "$arg_device")
|
|
trap "losetup --detach ${loopdev@Q}" EXIT
|
|
(
|
|
cmd=(mount --no-mtab -o "${mount_opt},sync,discard" -- "$loopdev" "$arg_mountpoint")
|
|
r=0
|
|
out=$("${cmd[@]}" 2>&1) || r=$?
|
|
if (( r != 0)); then
|
|
error $r '%s: %s' "${cmd[*]@Q}" "$out"
|
|
fi
|
|
trap "umount --no-mtab -- ${arg_mountpoint@Q}" EXIT
|
|
|
|
case "$arg_mode" in
|
|
user) sudo -u "#${SUDO_UID}" -- "${arg_cmd[@]}";;
|
|
root) command -- "${arg_cmd[@]}";;
|
|
esac
|
|
)
|
|
;;
|
|
|
|
*) error 1 'Internal error. The programmer writing this tool screwed up.';;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|