summaryrefslogtreecommitdiff
path: root/pre-copy-rclone-hardlink.sh
blob: aa763b57b460ec87edfdd432da91346cae33cd58 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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