Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested mount support #67

Merged
merged 30 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cab88e2
Allow for nested mount by using mergerfs
Jun 25, 2023
4e2a113
Remove debug bash
ezrizhu Jun 25, 2023
73b9b51
Remove debug echo
ezrizhu Jun 25, 2023
092a460
mount /run with merger aswell
ezrizhu Jun 25, 2023
b95f2e4
refactor and use overlayfs via mergerfs if regular overlayfs fails
ezrizhu Jun 26, 2023
1a4ba00
Only mount /dev/{tty null zero full random urandom}
ezrizhu Jun 26, 2023
83f8e5d
improve docs, refactor from top_dir to mountpoint
ezrizhu Jun 26, 2023
6b46967
Fix mergerfs failing not showing mount log path
ezrizhu Jun 27, 2023
9c929fe
Add support for unionfs, allow user to specify unionfs helper path
ezrizhu Jun 27, 2023
74d0894
Write mountpoint on unionhelper not found message
ezrizhu Jun 27, 2023
db5539f
Merge branch 'main' into nested-mount
ezrizhu Jun 27, 2023
f2cb092
exit if findmnt not installed
ezrizhu Jun 27, 2023
4f3b766
nested mount docs
ezrizhu Jun 27, 2023
ed98863
add newlines to readme
ezrizhu Jun 27, 2023
a8427e2
grammar fix
ezrizhu Jun 27, 2023
38a34b5
Add -U option description to manpages
gliargovas Jun 28, 2023
1a29602
Add shell completion for -U option
gliargovas Jun 28, 2023
29d47f3
Change -U flag autocompletion to only suggest executables
gliargovas Jun 28, 2023
c216d0e
Install mergerfs in ci
ezrizhu Jun 28, 2023
68e5965
Try reading from /run directory before testing
gliargovas Jun 29, 2023
e69c535
Merge remote-tracking branch 'origin/main' into nested-mount
angelhof Jun 29, 2023
7d661e9
refactor
angelhof Jun 29, 2023
45f8e0b
Refactor
angelhof Jun 29, 2023
a71f715
more refactoring
angelhof Jun 29, 2023
2f5d40f
Refactor some more and unmount devices for tests to pass
angelhof Jun 29, 2023
7de1645
Add a device test
angelhof Jun 29, 2023
2fcfb17
copyedit
mgree Jun 29, 2023
57b9b8c
copyedit
mgree Jun 29, 2023
36a7b6c
Some comments and redirect a test to /dev/null
angelhof Jun 29, 2023
77f60f7
Merge branch 'nested-mount' of github.com:binpash/try into nested-mount
angelhof Jun 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
run: |
uname -a
sudo apt-get update
sudo apt-get install strace
sudo apt-get install mergerfs

- name: Checkout
uses: actions/checkout@v2
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ disks.

### Dependencies

Try relies on the following dependencies

`util-linux`

In cases where overlayfs doesn't work on nested mounts, you will need either
[mergerfs](https://github.com/trapexit/mergerfs) or [unionfs](https://github.com/rpodgorny/unionfs-fuse). Try should be able to autodetect them, but you can specify the path to mergerfs or unionfs with -U (e.g. `try -U ~/.local/bin/unionfs`)

Has been tested on the following distributions:
* `Ubuntu 20.04 LTS` or later
* `Debian 12`
Expand Down
7 changes: 5 additions & 2 deletions completions/try.bash
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ _try() {

case "${cmd}" in
try)
opts="-n -y -v -h -D summary commit"
opts="-n -y -v -h -D -U summary commit"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
Expand All @@ -31,7 +31,10 @@ _try() {
COMPREPLY=($(compgen -d "${cur}"))
return 0
;;

-U)
COMPREPLY=($(compgen -c "${cur}"))
return 0
;;
commit)
COMPREPLY=($(compgen -d "${cur}"))
return 0
Expand Down
6 changes: 5 additions & 1 deletion docs/try.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ While using *try* you can choose to commit the result to the filesystem or compl

: Specifies DIR as the overlay directory (implies -n). The use of -D also implies that *DIR* already exists.

-U *PATH*

: Use the unionfs helper implementation defined in the *PATH* (e.g., mergerfs, unionfs-fuse) instead of the default.
This option is recommended in case OverlayFS fails.
## Subcommands

try summary *DIR*
try summary *DIR*

: Show the summary for the overlay in DIR

Expand Down
14 changes: 13 additions & 1 deletion test/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,27 @@ cleanup()
mkdir "$try_workspace"
}

test_read_from_run_dir()
{
ls /run/systemd > /dev/null
if [ $? -ne 0 ]; then
echo "Cannot read from /run/systemd."
return 1
fi
}

run_test()
{
cleanup
local test=$1

if [ "$(type -t $test)" != "function" ]; then
echo "$test is not a function! FAIL"
return 1
fi

# Check if we can read from /run dir
test_read_from_run_dir

echo -n "Running $test..."

Expand Down
85 changes: 57 additions & 28 deletions try
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,29 @@ TRY_VERSION="0.1.0"
try() {
START_DIR="$PWD"

if ! command -v findmnt > /dev/null; then
echo "findmnt not found, please install util-linux" 1>&2
exit 1
fi

[ "$SANDBOX_DIR" ] || SANDBOX_DIR=$(mktemp -d)
export SANDBOX_DIR
mkdir -p "$SANDBOX_DIR/upperdir" "$SANDBOX_DIR/workdir" "$SANDBOX_DIR/temproot"

# we will overlay-mount each root directory separately (instead of all at once) because some directories cannot be overlayed
# so we set up the mount points now
for top_dir in /*
#
# findmnt
# --real: only list real filesystems
# -n: no header
# -r: raw output
# -o target: only print the mount target
# then we want to exclude the root partition "/"
for mointpoint in /* $(findmnt --real -r -o target -n | grep -v "^/$")
do
## Only make the directory if the original is a directory too
if [ -d "$top_dir" ]; then
mkdir "$SANDBOX_DIR"/upperdir/"$top_dir" "$SANDBOX_DIR"/workdir"/$top_dir" "$SANDBOX_DIR"/temproot/"$top_dir"
if [ -d "$mointpoint" ]; then
mkdir -p "$SANDBOX_DIR"/upperdir/"$mointpoint" "$SANDBOX_DIR"/workdir"/$mointpoint" "$SANDBOX_DIR"/temproot/"$mointpoint"
fi
done

Expand All @@ -44,47 +56,56 @@ try() {
#!/bin/sh

# actually mount the overlays
for top_dir in /*
for mointpoint in /* $(findmnt --real -r -o target -n | grep -v "^/$")
angelhof marked this conversation as resolved.
Show resolved Hide resolved
do
## If the directory is not a mountpoint
angelhof marked this conversation as resolved.
Show resolved Hide resolved
if [ -d "$top_dir" ] && ! mountpoint -q "$top_dir"; then
## TODO: The
mount -t overlay overlay -o lowerdir=/"$top_dir",upperdir="$SANDBOX_DIR"/upperdir/"$top_dir",workdir="$SANDBOX_DIR"/workdir/"$top_dir" "$SANDBOX_DIR"/temproot/"$top_dir" 2>> "$try_mount_log" || echo "Warning: Failed mounting $top_dir as an overlay, see "$try_mount_log"" 1>&2
if [ -d "$mointpoint" ]; then
# skip if it is /dev or /proc, we will mount it later
if [ "$mointpoint" = "/dev" ] || [ "$mointpoint" = "/proc" ]; then
angelhof marked this conversation as resolved.
Show resolved Hide resolved
continue
fi
# Try mounting everything normally
mount -t overlay overlay -o lowerdir=/"$mointpoint",upperdir="$SANDBOX_DIR"/upperdir/"$mointpoint",workdir="$SANDBOX_DIR"/workdir/"$mointpoint" "$SANDBOX_DIR"/temproot/"$mointpoint" 2>> "$try_mount_log"
angelhof marked this conversation as resolved.
Show resolved Hide resolved
# If mounting everything normally fails, we try using either using mergerfs or unionfs to mount them.
if [ $? -ne 0 ]; then
# Detect if union_helper is set, if not, we try to autodetect them
if [ -z ${union_helper+x} ]; then
angelhof marked this conversation as resolved.
Show resolved Hide resolved
if command -v mergerfs > /dev/null; then
angelhof marked this conversation as resolved.
Show resolved Hide resolved
union_helper=mergerfs
elif command -v unionfs > /dev/null; then
union_helper=unionfs
else
echo "Error: Failed to mount overlayfs normally, mergerfs or unionfs not found for $mointpoint, see $try_mount_log" 1>&2
angelhof marked this conversation as resolved.
Show resolved Hide resolved
exit 1
fi
fi

# Here we use mergerfs/unionfs to be mounted into a single file system. Used to fix nested mounts #19
ezrizhu marked this conversation as resolved.
Show resolved Hide resolved
merger_dir=$(mktemp -d)
"$union_helper" $mointpoint $merger_dir 2>> "$try_mount_log" || echo "Warning: Failed to mount $mointpoint via $union_helper, see $try_mount_log" 1>&2 &&
mount -t overlay overlay -o lowerdir="$merger_dir",upperdir="$SANDBOX_DIR"/upperdir"$mointpoint",workdir="$SANDBOX_DIR"/workdir"$mointpoint" "$SANDBOX_DIR"/temproot"$mointpoint" 2>> "$try_mount_log" ||
echo "Warning: Failed mounting $mointpoint as an overlay via $union_helper, see "$try_mount_log"" 1>&2
fi
fi
done

# Now we will handle custom mounts, e.g., mounts on /home
# findmnt
# --real: only list real filesystems
# -n: no header
# -r: raw output
# -o target: only print the mount target
# then we want to exclude the root partition "/"
for mount_dir in $(findmnt --real -r -o target -n | grep -v "^/$")
for dev in tty null zero full random urandom
angelhof marked this conversation as resolved.
Show resolved Hide resolved
do
mount -t overlay overlay -o lowerdir="$mount_dir",upperdir="$SANDBOX_DIR"/upperdir"$mount_dir",workdir="$SANDBOX_DIR"/workdir"$mount_dir" "$SANDBOX_DIR"/temproot"$mount_dir" 2>> "$try_mount_log" || echo "Warning: Failed mounting $mount_dir as an overlay, see "$try_mount_log"" 1>&2
touch "$SANDBOX_DIR/temproot/dev/$dev"
mount -o bind /dev/$dev "$SANDBOX_DIR/temproot/dev/$dev"
done

## Bind the udev mount so that the containerized process has access to /dev
## KK 2023-05-06 Are there any security/safety implications by binding the whole /dev?
## Maybe we just want to bind a few files in it like /dev/null, /dev/zero?
mount --rbind /dev "$SANDBOX_DIR/temproot/dev"
## KK 2023-06-20 Redirecting to /dev/null to suppress a yet uninvestigated but
## seemingly not impactful warning.
mount --rbind --read-only /run "$SANDBOX_DIR/temproot/run" 2> /dev/null

## Check if chroot_executable exists, #29
if ! [ -f "$SANDBOX_DIR/temproot/$chroot_executable" ]; then
cp $chroot_executable "$SANDBOX_DIR/temproot/$chroot_executable"
fi


unshare --root="$SANDBOX_DIR/temproot" /bin/bash "$chroot_executable"
EOF

cat >"$chroot_executable" <<EOF
#!/bin/sh

mount -t proc proc /proc &&
cd $START_DIR &&
source "$script_to_execute"
Expand Down Expand Up @@ -226,11 +247,12 @@ ignore_changes() {
usage() {
cmd="$(basename $0)"
cat >&2 <<EOF
Usage: $cmd [-nvhy] [-D DIR] CMD [ARG ...]
Usage: $cmd [-nvhy] [-D DIR] [-U PATH] CMD [ARG ...]

-n don't prompt for commit
-y assume yes to all prompts (implies -n is not used)
-D DIR work in DIR (implies -n)
-U PATH path to unionfs helper (e.g., mergerfs, unionfs-fuse)

-v show version information (and exit)
-h show this usage message (and exit)
Expand All @@ -249,7 +271,7 @@ EOF
# "commit" - commit the result directory automatically when we're done
NO_COMMIT="interactive"

while getopts ":yvnD:" opt
while getopts ":yvnD:U:" opt
do
case "$opt" in
(y) NO_COMMIT="commit";;
Expand All @@ -263,6 +285,13 @@ do
NO_COMMIT="quiet"
;;
(v) printf "%s version $TRY_VERSION\n" "$(basename $0)" >&2; exit 0;;
(U) if ! [ -x "$OPTARG" ]
then
printf "%s: no such executable $OPTARG\n" "$(basename $0)" >&2
exit 2
fi
union_helper="$OPTARG"
;;
(h|*) usage; exit 0;;
esac
done
Expand Down