aboutsummaryrefslogtreecommitdiff
path: root/tests/agent/btrfs-cpdir
diff options
context:
space:
mode:
Diffstat (limited to 'tests/agent/btrfs-cpdir')
-rwxr-xr-xtests/agent/btrfs-cpdir113
1 files changed, 113 insertions, 0 deletions
diff --git a/tests/agent/btrfs-cpdir b/tests/agent/btrfs-cpdir
new file mode 100755
index 0000000..d2dbd43
--- /dev/null
+++ b/tests/agent/btrfs-cpdir
@@ -0,0 +1,113 @@
+#! /usr/bin/env bash
+
+# Copy a directory on (the same) btrfs filesystem. Inside, subvolumes
+# are copied with btrfs subvolume snapshot and everything else with
+# cp --reflink=always.
+#
+# If the -f option is specified and <dst> exists, it is first removed by
+# calling btrfs-rmdir (which is expected to be found next to btrfs-cpdir).
+#
+# Notes:
+#
+# 1. <src> should not be a subvolume (use snapshot directly in this case).
+#
+# 2. Copying of symlinks is not supported (symlinks in submodules are ok).
+#
+# 3. Read-only subvolumes are snapshotted as read-only.
+#
+# Note also that <src> is clones as <dst>, not into <dst> (i.e., like cp -T).
+#
+usage="usage: $0 [-f] <src>/ <dst>/"
+
+owd="$(pwd)"
+trap "{ cd '$owd'; exit 1; }" ERR
+set -o errtrace # Trap in functions.
+
+function info () { echo "$*" 1>&2; }
+function error () { info "$*"; exit 1; }
+
+force=
+
+while [ "$#" -gt 0 ]; do
+ case "$1" in
+ -f)
+ shift
+ force="true"
+ ;;
+ --)
+ break
+ ;;
+ -*)
+ error "unknown option: $1"
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+src="${1%/}"
+dst="${2%/}"
+
+if [ -z "$src" -o -z "$dst" ]; then
+ error "$usage"
+fi
+
+shopt -s nullglob dotglob
+
+function cp_dir () # <src> <dst>
+{
+ local src="$1"
+ local dst="$2"
+
+ mkdir "$dst"
+
+ local s d
+ for s in "$src"/*; do
+
+ d="$dst/${s#$src/}"
+
+ if [ -f "$s" ]; then
+ cp -p --reflink=always "$s" "$d"
+ continue
+ fi
+
+ if [ -d "$s" ]; then
+
+ # See if this is a subvolume: btrfs subvolume list requires root
+ # priviliges so we use the inode number which for subvolumes is always
+ # 256.
+ #
+ if [ "$(stat --format=%i "$s")" -eq 256 ]; then
+ cp_subvol "$s" "$d"
+ else
+ cp_dir "$s" "$d"
+ fi
+ continue
+ fi
+
+ error "$s is not a file/directory, not supported"
+ done
+
+ chmod --reference="$src" "$dst"
+ chown --reference="$src" "$dst"
+}
+
+function cp_subvol () # <src> <dst>
+{
+ local src="$1"
+ local dst="$2"
+
+ local o=()
+ if [ "$(btrfs property get -ts "$src" ro)" = "ro=true" ]; then
+ o+=(-r)
+ fi
+
+ btrfs subvolume snapshot "${o[@]}" "$src" "$dst" >/dev/null
+}
+
+if [ -d "$dst" -a -n "$force" ]; then
+ "$(dirname "$(realpath ${BASH_SOURCE[0]})")/btrfs-rmdir" "$dst"
+fi
+
+cp_dir "$src" "$dst"