-
Notifications
You must be signed in to change notification settings - Fork 1
/
mount_remap.sh
executable file
·85 lines (71 loc) · 2.42 KB
/
mount_remap.sh
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
#!/usr/bin/env bash
set -euo pipefail
# Mounts a dataset (or snapshot) into its own user & mount namespace, remapping fs uid/gid using
# bindfs and running target program without real root privileges.
# This allows backups to be so-called uid/gid-neutral and reproducible.
scriptdir="$(dirname -- "$(readlink -f "${BASH_SOURCE[0]}")")"
source "${scriptdir}/common.sh"
elevate=()
if [ "${UID}" -ne 0 ]; then
elevate+=(sudo --)
fi
if [ -z "${_IN_NS:-}" ]; then
# shellcheck disable=SC2046
exec -- "${elevate[@]}" unshare -m -f -p \
--kill-child --mount-proc \
-- \
$(propagate_env _IN_NS=1 _NS_UID="$(id -u)" _NS_GID="$(id -g)" HOME="${HOME}" XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR}") "${0}" "${@}"
fi
dataset="${1}"
if ! dataset_exists "${dataset}"; then
echo ">>> No such dataset: ${dataset}"
exit 1
fi
shift
if [ -z "${*}" ]; then
echo ">>> No command specified"
exit 1
fi
mnt_final=""
mountflags=()
if grep -q "@" <<< "${dataset}"; then
IFS=@ read -r -a _snapshot <<< "${dataset}"
_dataset="${_snapshot[0]}"
_snap="${_snapshot[1]}"
mnt_final="/tmp/zorg/${_snap}"
else
# work around zfs annoying mount mechanism
if ! [ "$(zfs get -H -o value mountpoint "${dataset}")" = "legacy" ]; then
mountflags+=(zfsutil)
fi
mountflags+=(ro)
mnt_final="/tmp/zorg/$(dataset_to_repo_name "${dataset}")"
fi
mkdir -p /tmp/zorg
mount -t tmpfs tmpfs /tmp/zorg
mnt_dataset="/tmp/zorg/.real/dataset"
mkdir -p "${mnt_dataset}" "${mnt_final}"
mount -t zfs -o "$(IFS=,; echo "${mountflags[*]}")" "${dataset}" "${mnt_dataset}"
bindfs -u "${_NS_UID}" -g "${_NS_GID}" "${mnt_dataset}" "${mnt_final}" -f &
bpid="${!}"
cleanup_mount () {
cd /
umount "${mnt_final}" || kill -9 "${bpid}"
wait "${bpid}"
}
cleanup_hooks+=(cleanup_mount)
#findmnt --poll=mount --first-only --timeout=$((10 * 1000)) --mountpoint "${mnt_final}" # TODO: crashes with double free???
while [ -z "$(awk -v "mnt=${mnt_final}" '$2 == mnt { print $2 }' /proc/mounts)" ]; do
sleep 0.5
done
# hack around users for ssh
# TODO: I'm pretty sure that there's a PAM module for that
sed -e "s#:/root:#:${HOME}:#" < /etc/passwd > /tmp/zorg/.passwd
mount --bind /tmp/zorg/.passwd /etc/passwd
if [ -d /var/run/nscd ]; then
mount -t tmpfs tmpfs /var/run/nscd
fi
# shellcheck disable=SC2046
setpriv --reuid="${_NS_UID}" --regid="${_NS_GID}" --init-groups --reset-env \
$(propagate_env) unshare -U -r --wd="${mnt_final}" --fork -- \
$(propagate_env HOME="${HOME}" XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR}") "${@}" >&2