Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
=== rmw ChangeLog ===

2025-12-29

* bugfix: Get path to 'mv' using meson (fixes #509)

2025-11-06

* Release v0.9.4

2025-10-20
* (bugfix) Allow rmw to move directories to waste folder on btrfs filesystems
* bugfix: Allow rmw to move directories to waste folder on btrfs filesystems
(#497)

2024-11-07
Expand Down
9 changes: 5 additions & 4 deletions src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,13 +517,14 @@ safe_mv_via_exec(const char *src, const char *dst, int *out_errno)

if (pid == 0)
{
/* child: exec /bin/mv directly (argv[0] is "mv") */
/* child: exec mv, searching PATH at runtime so this works on
non-FHS systems (e.g. NixOS) and in AppImages */
char *const argv_mv[] = { "mv", (char *) src, (char *) dst, NULL };
execv("/bin/mv", argv_mv);
/* if execv fails, set errno and exit with a distinct code */
_exit(127);
execvp("mv", argv_mv);
_exit(127); /* only reached on execvp failure */
}


/* parent: wait for child */
if (waitpid(pid, &status, 0) < 0)
{
Expand Down
121 changes: 80 additions & 41 deletions test/test_btrfs_clone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ else
. "${MESON_SOURCE_ROOT}/test/COMMON"
fi

BTRFS_IMAGE_MOUNTPOINT="/tmp/rmw-loop"
IMAGE_PATH="${MESON_SOURCE_ROOT}/test/rmw-btrfs-test.img"
BTRFS_MOUNTPOINT="/tmp/rmw-loop"
BTRFS_IMAGE="${MESON_SOURCE_ROOT}/test/rmw-btrfs-test.img"

if [ ! -f "$IMAGE_PATH" ]; then
if [ ! -f "$BTRFS_IMAGE" ]; then
echo "Test image not found; skipping."
exit 0
fi
Expand All @@ -20,63 +20,102 @@ if ! sudo -n true 2>/dev/null; then
exit 0
fi

if [ ! -d "$BTRFS_IMAGE_MOUNTPOINT" ]; then
sudo mkdir "$BTRFS_IMAGE_MOUNTPOINT"
if [ ! -d "$BTRFS_MOUNTPOINT" ]; then
sudo mkdir "$BTRFS_MOUNTPOINT"
fi
IS_BTRFS_MOUNTED="$(mount | grep rmw-btrfs)" || true
if [ -z "$IS_BTRFS_MOUNTED" ]; then
sudo mount -o loop "$IMAGE_PATH" \
"$BTRFS_IMAGE_MOUNTPOINT"
sudo chown "$(id -u)" -R "$BTRFS_IMAGE_MOUNTPOINT"

if ! mount | grep -q rmw-btrfs; then
sudo mount -o loop "$BTRFS_IMAGE" "$BTRFS_MOUNTPOINT"
sudo chown "$(id -u)" -R "$BTRFS_MOUNTPOINT"
fi

cd "$BTRFS_IMAGE_MOUNTPOINT"
RMW_TEST_CMD_STRING="$BIN_DIR/rmw -c ${MESON_SOURCE_ROOT}/test/conf/btrfs_img.testrc"
cd "$BTRFS_MOUNTPOINT"
BTRFS_RMW_CMD="$BIN_DIR/rmw -c ${MESON_SOURCE_ROOT}/test/conf/btrfs_img.testrc"

SUBVOLUME_USED="/tmp/rmw-loop/@two"
BTRFS_SUBVOLUME="$BTRFS_MOUNTPOINT/@two"
BTRFS_WASTE_DIR="$BTRFS_SUBVOLUME/Waste"

WASTE_USED="$SUBVOLUME_USED/Waste"
if [ -d "$WASTE_USED" ]; then
rm -rf "$WASTE_USED"
if [ -d "$BTRFS_WASTE_DIR" ]; then
rm -rf "$BTRFS_WASTE_DIR"
fi

TEST_DIR="$BTRFS_IMAGE_MOUNTPOINT/test_dir"
if [ -d "$TEST_DIR" ]; then
rm -rf "$TEST_DIR"
# --- Test: move a directory with contents across btrfs subvolumes ---
echo "== Test: move a directory with contents across btrfs subvolumes"
BTRFS_TEST_DIR="$BTRFS_MOUNTPOINT/test_dir"
if [ -d "$BTRFS_TEST_DIR" ]; then
rm -rf "$BTRFS_TEST_DIR"
fi

mkdir "$TEST_DIR"
touch "$TEST_DIR/bar"
$RMW_TEST_CMD_STRING "$TEST_DIR"
test ! -d "$TEST_DIR"

$RMW_TEST_CMD_STRING -u
test -d "$TEST_DIR"

mkdir "$BTRFS_TEST_DIR"
touch "$BTRFS_TEST_DIR/bar"
$BTRFS_RMW_CMD "$BTRFS_TEST_DIR"
test ! -d "$BTRFS_TEST_DIR"
test -d "$BTRFS_WASTE_DIR/files/test_dir"
test -f "$BTRFS_WASTE_DIR/files/test_dir/bar"
test -f "$BTRFS_WASTE_DIR/info/test_dir.trashinfo"

echo "== Test: restore the moved directory"
$BTRFS_RMW_CMD -u
test -d "$BTRFS_TEST_DIR"
test -f "$BTRFS_TEST_DIR/bar"
test ! -f "$BTRFS_WASTE_DIR/info/test_dir.trashinfo"

# --- Test: move a deeply nested directory across btrfs subvolumes ---
echo "== Test: move a deeply nested directory across btrfs subvolumes"
BTRFS_NESTED_DIR="$BTRFS_MOUNTPOINT/nested_dir"
if [ -d "$BTRFS_NESTED_DIR" ]; then
rm -rf "$BTRFS_NESTED_DIR"
fi
mkdir -p "$BTRFS_NESTED_DIR/a/b/c"
touch "$BTRFS_NESTED_DIR/a/b/c/deep_file"
$BTRFS_RMW_CMD "$BTRFS_NESTED_DIR"
test ! -d "$BTRFS_NESTED_DIR"
test -d "$BTRFS_WASTE_DIR/files/nested_dir"
test -f "$BTRFS_WASTE_DIR/files/nested_dir/a/b/c/deep_file"
test -f "$BTRFS_WASTE_DIR/info/nested_dir.trashinfo"

echo "== Test: restore the nested directory"
$BTRFS_RMW_CMD -u
test -d "$BTRFS_NESTED_DIR/a/b/c"
test -f "$BTRFS_NESTED_DIR/a/b/c/deep_file"
test ! -f "$BTRFS_WASTE_DIR/info/nested_dir.trashinfo"

# --- Test: move a file across btrfs subvolumes ---
echo "== Test: move a file across btrfs subvolumes"
touch foo
$RMW_TEST_CMD_STRING foo
test -f "$WASTE_USED/files/foo"
test -f "$WASTE_USED/info/foo.trashinfo"
$BTRFS_RMW_CMD foo
test -f "$BTRFS_WASTE_DIR/files/foo"
test -f "$BTRFS_WASTE_DIR/info/foo.trashinfo"
test ! -f foo
$RMW_TEST_CMD_STRING -u
test -f foo

RMW_FAKE_YEAR=true $RMW_TEST_CMD_STRING foo
test -f "$WASTE_USED/files/foo"
$RMW_TEST_CMD_STRING -g
test ! -f "$WASTE_USED/files/foo"

echo "== Test: restore the moved file"
$BTRFS_RMW_CMD -u
test -f foo
test ! -f "$BTRFS_WASTE_DIR/info/foo.trashinfo"

# --- Test: purge an expired file from btrfs waste ---
echo "== Test: purge an expired file from btrfs waste"
RMW_FAKE_YEAR=true $BTRFS_RMW_CMD foo
test -f "$BTRFS_WASTE_DIR/files/foo"
test -f "$BTRFS_WASTE_DIR/info/foo.trashinfo"
$BTRFS_RMW_CMD -g
test ! -f "$BTRFS_WASTE_DIR/files/foo"
test ! -f "$BTRFS_WASTE_DIR/info/foo.trashinfo"

# --- Test: cross-subvolume move from a non-btrfs home directory ---
echo "== Test: move file from non-btrfs home to btrfs waste"
cd "$RMW_FAKE_HOME"
touch foo
$RMW_TEST_CMD_STRING foo -v
$BTRFS_RMW_CMD foo -v
test ! -f foo
$RMW_TEST_CMD_STRING -u

echo "== Test: restore file to non-btrfs home from btrfs waste"
$BTRFS_RMW_CMD -u
test -f foo

cd

if mount | grep -q rmw-btrfs; then
sudo umount "$BTRFS_IMAGE_MOUNTPOINT"
sudo umount "$BTRFS_MOUNTPOINT"
fi

exit 0
Loading