#!/usr/bin/env bash # Copyright (C) 2018 Luke Shumaker # Copyright (C) 2023 Umorpha Systems # SPDX-License-Identifier: AGPL-3.0-or-later declare -r NAME=osi-mount declare -r VERSION=20231023 set -euE source ./lib/osi.sh main() { local arg_orig=("$@") 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 "set -- $args" while true; do case "$1" in --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 ;; -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 case "$arg_mode" in outside|inside) arg_device=$1 arg_mountpoint=$2 arg_cmd=("${@:3}") ;; esac fi 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: sudo %s [OPTIONS] -- FILENAME.img COMMAND...' "${0##*/}" print 'Operating System Image: Mount' echo # 000000000011111111112222222222333333333344444444445555555555666666666677777777778 # 012345678901234567890123456789012345678901234567890123456789012345678901234567890 print 'Run COMMAND with FILENAME.img mounted.' 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) needs_root 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 unshare --mount "${BASH_SOURCE[0]}" --inside "${arg_orig[@]}" 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 ;; inside) needs_root needs_sudo mount --make-rslave / case "$arg_user" in user) mount_opt=ro ;; root) mount_opt=rw # TODO: make the rest of the system RO ;; esac cmd=(mount --no-mtab -o "${mount_opt},discard" -- "$arg_device" "$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_user" 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 "$@"