diff options
author | Andrew Cady <d@jerkface.net> | 2023-05-21 19:04:14 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2023-05-21 19:04:14 -0400 |
commit | 729b61170638365c04458963018edea1ca067e7e (patch) | |
tree | 0e622b8fa3d6bf3835cffcdfa04553891bc12224 | |
parent | cba8b36d788761e185adcf6af7cf396e66a3cd8a (diff) |
pretty good
-rwxr-xr-x | go.sh | 60 |
1 files changed, 44 insertions, 16 deletions
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | MAX_AGE_SECONDS=60 | ||
2 | 3 | ||
3 | read_config() | 4 | read_config() |
4 | { | 5 | { |
@@ -7,7 +8,7 @@ read_config() | |||
7 | 8 | ||
8 | check_dependencies() | 9 | check_dependencies() |
9 | { | 10 | { |
10 | for c in flock jq btrfs pv mv | 11 | for c in flock jq btrfs pv realpath stat date mv ln rm |
11 | do | 12 | do |
12 | command -v $c >/dev/null | 13 | command -v $c >/dev/null |
13 | done | 14 | done |
@@ -23,43 +24,70 @@ is_subvolume() | |||
23 | btrfs subvolume show -- "$1" >/dev/null 2>&1 | 24 | btrfs subvolume show -- "$1" >/dev/null 2>&1 |
24 | } | 25 | } |
25 | 26 | ||
27 | get_age() | ||
28 | { | ||
29 | now=$(date +%s) | ||
30 | then=$(stat -L -c %Y "$1") | ||
31 | echo $((now - then)) | ||
32 | } | ||
33 | |||
34 | remove_old_link() | ||
35 | { | ||
36 | if [ -L "$1" ] && [ "$(get_age "$1")" -gt "$2" ] | ||
37 | then | ||
38 | rm -- "$1" | ||
39 | fi | ||
40 | } | ||
41 | |||
26 | set -e | 42 | set -e |
27 | check_dependencies | 43 | check_dependencies |
28 | check_user_is_root | 44 | check_user_is_root |
29 | 45 | ||
30 | if [ $# != 1 ] | 46 | if [ $# = 1 ] |
31 | then | 47 | then |
48 | config_file=$1 | ||
49 | else | ||
32 | echo "Usage: $0 <config.json>" >&2 | 50 | echo "Usage: $0 <config.json>" >&2 |
33 | exit 1 | 51 | exit 1 |
34 | fi | 52 | fi |
35 | 53 | ||
36 | [ -r "$1" ] | 54 | [ -r "$config_file" ] |
37 | exec 3<>"$1" | 55 | exec 3<>"$config_file" |
38 | flock -n 3 | 56 | flock -n 3 |
39 | declare -A config | 57 | declare -A config |
40 | read_config <&3 | 58 | read_config <&3 |
41 | 59 | ||
42 | src=${config[source]} | 60 | src=${config[source]} |
43 | dst=${config[destination]} | 61 | dst=${config[destination]} |
44 | head=${config[head]} | 62 | remote_head=${config[head]} |
45 | new_snapshot=${src%/}/.snapshot~$(date -Ins) | ||
46 | 63 | ||
47 | is_subvolume "$src" | 64 | is_subvolume "$src" |
48 | if [ "$head" ] | 65 | local_head_link=${src%/}/.snapshot~HEAD |
66 | |||
67 | remove_old_link "$local_head_link" "$MAX_AGE_SECONDS" | ||
68 | |||
69 | if [ -e "$local_head_link" ] | ||
49 | then | 70 | then |
50 | is_subvolume "$head" | 71 | local_head=$(realpath -e "$local_head_link") |
72 | else | ||
73 | new_snapshot=${src%/}/.snapshot~$(date -Ins) | ||
74 | btrfs subvolume snapshot -r -- "$src" "$new_snapshot" | ||
75 | # We don't need to create this link at all. | ||
76 | ln -rs -T "$new_snapshot" "$local_head_link" | ||
77 | local_head=$new_snapshot | ||
51 | fi | 78 | fi |
52 | mkdir -p "$dst" | ||
53 | 79 | ||
54 | btrfs subvolume snapshot -r "$src" "$new_snapshot" | 80 | is_subvolume "$local_head" |
55 | btrfs send "$new_snapshot" ${head:+ -p "$head"} | pv | btrfs receive "$dst" | ||
56 | jq --arg h "$head" '. | .head=$h' <"$1" >."$1"~tmp | ||
57 | mv -T -- ."$1"~tmp "$1" | ||
58 | 81 | ||
59 | #printf "%s\n" "$new_snapshot" > "$head"~tmp | 82 | if [ "${local_head##*/}" = "${remote_head##*/}" ] |
60 | #mv -T -- "$head"~tmp "$head" | 83 | then |
84 | echo "Up-to-date." >&2 | ||
85 | exit | ||
86 | fi | ||
61 | 87 | ||
62 | # parent of $src on $dst is $parent | 88 | btrfs send ${remote_head:+ -p "$remote_head"} -- "$local_head" | pv | btrfs receive -- "$dst" |
89 | jq --arg h "$local_head" '. | .head=$h' <"$config_file" >."$config_file"~tmp | ||
90 | mv -T -- ."$config_file"~tmp "$config_file" | ||
63 | 91 | ||
64 | ### OK, basic idea: | 92 | ### OK, basic idea: |
65 | # | 93 | # |