summaryrefslogtreecommitdiff
path: root/pre-copy-rclone-hardlink.sh
diff options
context:
space:
mode:
Diffstat (limited to 'pre-copy-rclone-hardlink.sh')
-rwxr-xr-xpre-copy-rclone-hardlink.sh80
1 files changed, 80 insertions, 0 deletions
diff --git a/pre-copy-rclone-hardlink.sh b/pre-copy-rclone-hardlink.sh
new file mode 100755
index 0000000..01f7b6e
--- /dev/null
+++ b/pre-copy-rclone-hardlink.sh
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+#
+# Copyright 2023 David Vazgenovich Shakaryan
+
+qargs() {
+ local arg str=
+
+ for arg; do
+ [[ "${arg:-*}" =~ [^A-Za-z0-9%+./:=@_-] ]] && arg="${arg@Q}"
+ str+=" ${arg}"
+ done
+ printf '%s\n' "${str:1}"
+}
+
+temp_dest="${1}"
+link_dest="${2}"
+[[ -n "${link_dest}" ]] || exit 0
+
+[[ "${temp_dest}" =~ ^(rclone://([^:]+):)(.+) ]] || exit 1
+prefix="${BASH_REMATCH[1]}"
+remote="${BASH_REMATCH[2]}"
+path="${BASH_REMATCH[3]}"
+[[ "${link_dest}" == "${prefix}"* ]] || exit 1
+link="${link_dest#"${prefix}"}"
+
+conf="$(rclone config dump)" || exit 1
+read -rd '' JQ <<'EOF'
+.[$k]
+| select(.type == "crypt" or .type == "sftp") // error("not crypt or sftp")
+| [.type, .remote]
+| @tsv
+EOF
+IFS=$'\t' read -r r_type r_remote < <(
+ jq -rc --arg k "${remote}" "${JQ}" <<<"${conf}") || exit 1
+
+if [[ "${r_type}" == 'crypt' ]]; then
+ crypt="${remote}"
+
+ [[ "${r_remote}" =~ ^([^:]+):(.*) ]] || exit 1
+ remote="${BASH_REMATCH[1]}"
+ base="${BASH_REMATCH[2]:+${BASH_REMATCH[2]}/}"
+
+ upath=0
+ ulink=0
+ while IFS=$'\t' read -r k v; do
+ [[ "${k% }" == "${path}" ]] && path="${base}${v# }" && upath=1
+ [[ "${k% }" == "${link}" ]] && link="${base}${v# }" && ulink=1
+ done < <(rclone cryptdecode --reverse "${crypt}:" "${path}" "${link}")
+ wait "$!" || exit 1
+ ((ulink && upath)) || exit 1
+fi
+
+read -rd '' JQ <<'EOF'
+.[$k]
+| select(.type == "sftp") // error("not sftp")
+| select(.key_file_pass | not) // error("key uses passphrase")
+| [
+ (.key_file | select(.) // error("missing key")
+ | "-i", (sub("^~"; env.HOME) | @sh)),
+ (.known_hosts_file | select(.)
+ | "-o", ("UserKnownHostsFile=\(sub("^~"; env.HOME))" | @sh)),
+ (.port | select(.) | "-p", @sh),
+ ("\(.user // error("missing user"))@\(.host // error("missing host"))"
+ | @sh)
+]
+| join(" ")
+EOF
+mapfile -td '' args < <(
+ jq -rc --arg k "${remote}" "${JQ}" <<<"${conf}" | \
+ xargs -r printf '%s\0'
+ [[ "${PIPESTATUS[0]}" -eq 0 && "${PIPESTATUS[1]}" -eq 0 ]])
+wait "$!" || exit 1
+
+ssh_cmd=(ssh -o BatchMode=yes "${args[@]}")
+cmd=("${ssh_cmd[@]}"
+ "cp -al $(printf '%q' "${link}") $(printf '%q' "${path}")")
+echo "* $(qargs "${cmd[@]}")"
+"${cmd[@]}" || exit 1
+"${ssh_cmd[@]}" "touch -c ${path@Q}"
+exit 0