summaryrefslogtreecommitdiff
path: root/osi-mount
blob: 3b423245bc2d78165c526e8de5c74461652c3502 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env bash
# Copyright (C) 2018  Luke Shumaker
# SPDX-License-Identifier: AGPL-3.0-or-later
declare -r NAME=osi-mount
declare -r VERSION=20180812

set -euE
source ./lib/osi.sh

main() {
	needs_root

	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: %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)
			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_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 "$@"