From b177cde43d380290dc9bb8727684d9629b0915bf Mon Sep 17 00:00:00 2001 From: Ezri Zhu Date: Sat, 2 Nov 2024 21:33:50 -0400 Subject: [PATCH 1/7] basic impl --- try | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/try b/try index 5774ae3..7dc5c62 100755 --- a/try +++ b/try @@ -72,6 +72,22 @@ try() { findmnt --real -r -o target -n >>"$DIRS_AND_MOUNTS" sort -u -o "$DIRS_AND_MOUNTS" "$DIRS_AND_MOUNTS" + # Setup excluded entries + excldir="$SANDBOX_DIR/excldir" + if [ -n "$EXCL_ENTS" ] + then + OLDIFS=$IFS + IFS=":" + mkdir "$excldir" + for exclent in $EXCL_ENTS + do + whiteout_file="$SANDBOX_DIR/excldir/$(realpath "$exclent")" + mkdir -p "$(dirname "$whiteout_file")" + mknod "$whiteout_file" c 0 0 + done + IFS=$OLDIFS + fi + # Calculate UPDATED_DIRS_AND_MOUNTS that contains the merge arguments in LOWER_DIRS UPDATED_DIRS_AND_MOUNTS="$(mktemp --suffix ".try-$EXECID")" export UPDATED_DIRS_AND_MOUNTS @@ -81,6 +97,11 @@ try() { OLDIFS=$IFS IFS=":" + if [ -d "$excldir/$mountpoint" ] + then + new_mountpoint="$excldir/$mountpoint" + fi + for lower_dir in $LOWER_DIRS do temp_mountpoint="$lower_dir/upperdir$mountpoint" @@ -614,7 +635,7 @@ NO_COMMIT="interactive" # Includes all patterns given using the `-i` flag; will be used with `grep -f` IGNORE_FILE="$(mktemp --suffix ".try-$EXECID")" -while getopts ":yvnhxi:D:U:L:" opt +while getopts ":yvnhxi:D:U:L:E:" opt do case "$opt" in (y) NO_COMMIT="commit";; @@ -632,6 +653,12 @@ do fi LOWER_DIRS="$OPTARG" NO_COMMIT="quiet";; + (E) if [ -n "$EXCL_ENTS" ] + then + error "the -E option has been specified multiple times" 2 + fi + EXCL_ENTS="$OPTARG" + NO_COMMIT="quiet";; (v) echo "$TRY_COMMAND version $TRY_VERSION" >&2 exit 0;; (U) if ! [ -x "$OPTARG" ] From 9faafdfe3a67fbb16939ad75314e8fb55316ca29 Mon Sep 17 00:00:00 2001 From: Ezri Zhu Date: Sun, 3 Nov 2024 01:52:02 -0500 Subject: [PATCH 2/7] fix nocommit quiet --- try | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/try b/try index 7dc5c62..24f1759 100755 --- a/try +++ b/try @@ -657,8 +657,7 @@ do then error "the -E option has been specified multiple times" 2 fi - EXCL_ENTS="$OPTARG" - NO_COMMIT="quiet";; + EXCL_ENTS="$OPTARG";; (v) echo "$TRY_COMMAND version $TRY_VERSION" >&2 exit 0;; (U) if ! [ -x "$OPTARG" ] From e1bf47b7544c848b4ce04baf576a2125f742c974 Mon Sep 17 00:00:00 2001 From: Ezri Zhu Date: Sun, 3 Nov 2024 01:52:12 -0500 Subject: [PATCH 3/7] add test --- test/excl_files.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 test/excl_files.sh diff --git a/test/excl_files.sh b/test/excl_files.sh new file mode 100755 index 0000000..ddda18c --- /dev/null +++ b/test/excl_files.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +TRY_TOP="${TRY_TOP:-$(git rev-parse --show-toplevel --show-superproject-working-tree 2>/dev/null || echo "${0%/*}")}" +TRY="$TRY_TOP/try" + +cleanup() { + cd .. + + if [ -d "$try_workspace" ] + then + echo running + rm -rf "$try_workspace" >/dev/null 2>&1 + fi +} + +trap 'cleanup' EXIT + +# try -E fails in /tmp on too many overlays +try_workspace="$(mktemp -d -p .)" + +cd "$try_workspace" || exit 9 + +touch hidden1 +touch nonhidden2 + +! "$TRY" -n -E hidden1 ls | grep hidden1 From cb2993fea53fd5c5344ba5c0f42f3310920360a7 Mon Sep 17 00:00:00 2001 From: Ezri Zhu Date: Sat, 11 Jan 2025 02:26:58 -0500 Subject: [PATCH 4/7] remove comment --- test/excl_files.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/test/excl_files.sh b/test/excl_files.sh index ddda18c..2652fb7 100755 --- a/test/excl_files.sh +++ b/test/excl_files.sh @@ -8,7 +8,6 @@ cleanup() { if [ -d "$try_workspace" ] then - echo running rm -rf "$try_workspace" >/dev/null 2>&1 fi } From 78b3ebd5ad6ef11a92753d923b8acc7f27befd08 Mon Sep 17 00:00:00 2001 From: Michael Greenberg Date: Wed, 20 Nov 2024 07:06:05 -0500 Subject: [PATCH 5/7] Minimize tempfile churn (#186) Closes #185. Try was making way too many tempfiles. Now we store everything in the sandbox, with a tiny bit of nuance: $IGNORE_FILE needs to be created in advance to handle the args properly. So we create that temporary unconditionally. When running a command (the try() function), we'll move $IGNORE_FILE into the sandbox. When running try commit or try summary, we just delete $IGNORE_FILE at the end. --- test/tempfiles.sh | 30 ++++++++++++++++++++++++++++++ try | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 11 deletions(-) create mode 100755 test/tempfiles.sh diff --git a/test/tempfiles.sh b/test/tempfiles.sh new file mode 100755 index 0000000..25e6adb --- /dev/null +++ b/test/tempfiles.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# shellcheck disable=SC2010,SC2126,SC2181 + +TRY_TOP="${TRY_TOP:-$(git rev-parse --show-toplevel --show-superproject-working-tree 2>/dev/null || echo "${0%/*}")}" +TRY="$TRY_TOP/try" + +workdir="$(mktemp -d)" +cd "$workdir" || exit 1 + +initial_count="$(ls "${TMPDIR-/tmp}" | grep -e "^.*\.try-[0-9]*$" | wc -l)" + +sandbox=$($TRY -n "touch $HOME/foo") +[ $? -eq 0 ] || exit 2 + +post_count="$(ls "${TMPDIR-/tmp}" | grep -e "^.*\.try-[0-9]*$" | wc -l)" + +# just one new tempfile +[ "$((initial_count + 1))" -eq "$post_count" ] || exit 3 +[ -f "$sandbox/upperdir$HOME/foo" ] || exit 4 + +# deliberately not the pattern of try sandboxes +sandbox=local +mkdir "$sandbox" || exit 5 +$TRY -D "$sandbox" "touch $HOME/bar" || exit 6 + +final_count="$(ls "${TMPDIR-/tmp}" | grep -e "^.*\.try-[0-9]*$" | wc -l)" + +# no new tempfiles! +[ "$post_count" -eq "$final_count" ] || exit 7 +[ -f "$sandbox/upperdir$HOME/bar" ] || exit 8 diff --git a/try b/try index 24f1759..5ff7054 100755 --- a/try +++ b/try @@ -34,10 +34,15 @@ try() { if [ "$SANDBOX_DIR" ] then ## If the name of a sandbox is given then we need to exit prematurely if its directory doesn't exist - ! [ -d "$SANDBOX_DIR" ] && { error "could not find sandbox directory $SANDBOX_DIR" 2; } + [ -d "$SANDBOX_DIR" ] || error "could not find sandbox directory $SANDBOX_DIR" 2 + # Force absolute path + SANDBOX_DIR="$(cd "$SANDBOX_DIR" && pwd)" + + # shellcheck disable=SC2181 + [ "$?" -eq 0 ] || error "could not find sandbox directory $SANDBOX_DIR (could not cd in)" 2 else ## Create a new sandbox if one was not given - SANDBOX_DIR=$(mktemp -d --suffix ".try-$EXECID") + SANDBOX_DIR="$(mktemp -d --suffix ".try-$EXECID")" fi ## If the sandbox is not valid we exit early @@ -50,7 +55,11 @@ try() { ## because we have already checked if it valid. export SANDBOX_DIR - try_mount_log="$(mktemp --suffix ".try-$EXECID")" + # We created "$IGNORE_FILE" up front, but now we can stash it in the sandbox. + mv "$IGNORE_FILE" "$SANDBOX_DIR"/ignore + IGNORE_FILE="$SANDBOX_DIR"/ignore + + try_mount_log="$SANDBOX_DIR"/mount.log export try_mount_log # If we're in a docker container, we want to mount tmpfs on sandbox_dir, #136 @@ -66,7 +75,7 @@ try() { mkdir -p "$SANDBOX_DIR/upperdir" "$SANDBOX_DIR/workdir" "$SANDBOX_DIR/temproot" ## Find all the directories and mounts that need to be mounted - DIRS_AND_MOUNTS="$(mktemp --suffix ".try-$EXECID")" + DIRS_AND_MOUNTS="$SANDBOX_DIR"/mounts export DIRS_AND_MOUNTS find / -maxdepth 1 >"$DIRS_AND_MOUNTS" findmnt --real -r -o target -n >>"$DIRS_AND_MOUNTS" @@ -89,7 +98,7 @@ try() { fi # Calculate UPDATED_DIRS_AND_MOUNTS that contains the merge arguments in LOWER_DIRS - UPDATED_DIRS_AND_MOUNTS="$(mktemp --suffix ".try-$EXECID")" + UPDATED_DIRS_AND_MOUNTS="$SANDBOX_DIR"/mounts.updated export UPDATED_DIRS_AND_MOUNTS while IFS="" read -r mountpoint do @@ -145,9 +154,9 @@ try() { chmod "$(stat -c %a /)" "$SANDBOX_DIR/temproot" - mount_and_execute="$(mktemp --suffix ".try-$EXECID")" - chroot_executable="$(mktemp --suffix ".try-$EXECID")" - script_to_execute="$(mktemp --suffix ".try-$EXECID")" + mount_and_execute="$SANDBOX_DIR"/mount_and_execute.sh + chroot_executable="$SANDBOX_DIR"/chroot_executable.sh + script_to_execute="$SANDBOX_DIR"/script_to_execute.sh export chroot_executable export script_to_execute @@ -248,7 +257,8 @@ do ## We can ignore this mountpoint, if the user program tries to use it, it will crash, but if not we can run normally printf "%s: Warning: Failed mounting $mountpoint as an overlay and mergerfs or unionfs not set and could not be found, see \"$try_mount_log\"\n" "$TRY_COMMAND" >&2 else - merger_dir=$(mktemp -d --suffix ".try-$EXECID") + merger_dir="$SANDBOX_DIR"/mergerdir."$(echo "$pure_mountpoint" | tr '/' '.')" + mkdir "$merger_dir" ## Create a union directory "$UNION_HELPER" $mountpoint $merger_dir 2>>"$try_mount_log" || @@ -633,6 +643,9 @@ EOF NO_COMMIT="interactive" # Includes all patterns given using the `-i` flag; will be used with `grep -f` +# +# We have to create this temporary up front. +# We move it to $SANDBOX_DIR/ignore in `try()`, but delete it when we don't move it. IGNORE_FILE="$(mktemp --suffix ".try-$EXECID")" while getopts ":yvnhxi:D:U:L:E:" opt @@ -683,9 +696,13 @@ fi TRY_EXIT_STATUS=1 case "$1" in (summary) : "${SANDBOX_DIR=$2}" - summary;; + summary + rm "$IGNORE_FILE" # we didn't move it to the sandbox, so clean up + ;; (commit) : "${SANDBOX_DIR=$2}" - commit;; + commit + rm "$IGNORE_FILE" # we didn't move it to the sandbox, so clean up + ;; (explore) : "${SANDBOX_DIR=$2}" try "$SHELL";; (--) shift From 3f8a2d1181a793e3ebf1ebd04bc8003daca6af04 Mon Sep 17 00:00:00 2001 From: Ezri Zhu Date: Mon, 13 Jan 2025 09:39:13 -0500 Subject: [PATCH 6/7] update excl file test --- test/excl_files.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/excl_files.sh b/test/excl_files.sh index 2652fb7..d987d31 100755 --- a/test/excl_files.sh +++ b/test/excl_files.sh @@ -19,7 +19,7 @@ try_workspace="$(mktemp -d -p .)" cd "$try_workspace" || exit 9 -touch hidden1 -touch nonhidden2 +echo secret > hidden1 +echo notsecret > nonhidden2 -! "$TRY" -n -E hidden1 ls | grep hidden1 +! "$TRY" -n -E hidden1 cat hidden1 | grep secret From 14abac9599f38b45f991e7181848db4f4a73512f Mon Sep 17 00:00:00 2001 From: Ezri Zhu Date: Mon, 13 Jan 2025 09:55:29 -0500 Subject: [PATCH 7/7] docs --- docs/try.1.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/try.1.md b/docs/try.1.md index d3ee1ed..be17fe8 100644 --- a/docs/try.1.md +++ b/docs/try.1.md @@ -61,6 +61,10 @@ This option is recommended in case OverlayFS fails. : Specify a colon-separated list of directories to be used as lower directories for the overlay, formatted as "dir1:dir2:...:dirn" (implies -n). +-E *PATHS* + +: Specify files to exclude from the sandbox, formatted as "file1:file2:...:filen". + ## Subcommands