summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2023-05-24 11:22:34 -0400
committerAndrew Cady <d@jerkface.net>2023-05-24 11:22:34 -0400
commitcf930327a1b78f3168aae700d7867af77fad5905 (patch)
tree4ab3d0ca05c2f5a015e07fae0bc8254603459d23
parent203d7252bbfc371aedf3618f23fe61a0f44c4a7f (diff)
improve retain-snapshots
-rwxr-xr-xretain-snapshots108
1 files changed, 108 insertions, 0 deletions
diff --git a/retain-snapshots b/retain-snapshots
new file mode 100755
index 0000000..86f0120
--- /dev/null
+++ b/retain-snapshots
@@ -0,0 +1,108 @@
1#!/bin/bash
2default_retain_years=7
3retain_years= # TODO: read retain_years from .propagation
4
5use_clock_time= # Delete according to clock time (WARNING: eventually deletes all data)
6
7
8# Never edit this regex.
9# Generated via: date -Ins | sed 's/[0-9]/[0-9]/g'
10datetime_regexp='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]:[0-9][0-9]'
11
12snapshot_prefix='.snapshot~'
13
14main()
15{
16 [ $# = 1 ] || exit
17 case "$1" in
18 /*) ;;
19 *) exit 1 ;;
20 esac
21
22 # Using `cd "$1"` and `find .` allows the path to contain
23 # newlines, while simple `find "$1"` would break the script.
24 # NB: Our snapshot name itself never contains whitespace.
25 cd "$1" || exit
26 find . -maxdepth 1 -type d -name "$snapshot_prefix""$datetime_regexp" | sort -rn | retain
27}
28
29is_readonly_subvolume()
30{
31 [ -d "$1" ]
32 btrfs subvolume show -- "$1" | sed -En -e'/^\tFlags:.*\breadonly\b/{q0}' -e'${q1}'
33}
34
35btrfs_subvolume_delete()
36{
37 >&2 echo btrfs subvolume delete "$@"
38}
39
40# Delete all snapshots that we do not retain.
41# The retention period is specified in a ".propagation" file.
42# If not specified it is 7 years.
43# Retain the most recent snapshot within any year of the last 7 years.
44# Retain the most recent snapshot within any month of the last 365 days.
45# Retain the most recent snapshot within any day of the last 7 days.
46# Retain the most recent snapshot within any hour of the last 24 hours.
47# Retain the most recent snapshot within any minute of the last hour.
48# Retain any snapshot created in the last minute.
49
50retain()
51{
52 2>/dev/null [ "$retain_years" -ge 1 ] || retain_years=$default_retain_years
53
54 _year= _month= _day= _hour= _minute= _second= _nanosecond=
55 now=
56 if [ "$use_clock_time" ]
57 then
58 now=$(date +%s)
59 fi
60 while read line
61 do
62 is_readonly_subvolume "$line" || break
63 dateline=${line#*/"$snapshot_prefix"}
64 [ "$dateline" != "$line" ]
65 before=$(date -d "$dateline" +%s)
66 [ "$now" ] || now=$before
67 age=$((now - before))
68
69 IFS='~-T:,' read year month day hour minute second nanosecond <<< "$dateline"
70
71 keep=
72 if [ "$year" != "$_year" ] && [ "$age" -lt $((retain_years * 366 * 24 * 60 * 60)) ]
73 then
74 keep="$year -> $line"
75 elif [ "$month" != "$_month" ] && [ "$age" -lt $((366 * 24 * 60 * 60)) ]
76 then
77 keep="$year-$month -> $line"
78 elif [ "$day" != "$_day" ] && [ "$age" -lt $((7 * 24 * 60 * 60)) ]
79 then
80 keep="$year-$month-$day -> $line"
81 elif [ "$hour" != "$_hour" ] && [ "$age" -lt $((24 * 60 * 60)) ]
82 then
83 keep="$year-$month-${day}T$hour -> $line"
84 elif [ "$minute" != "$_minute" ] && [ "$age" -lt $((60 * 60)) ]
85 then
86 keep="$year-$month-${day}T$hour:$minute -> $line"
87 elif [ "$age" -lt 60 ]
88 then
89 keep="recent -> $line"
90 fi
91
92 if [ "$keep" ]
93 then
94 echo "$keep" >&2
95 else
96 btrfs_subvolume_delete -- "$line"
97 fi
98
99 _year=$year
100 _month=$month
101 _day=$day
102 _hour=$hour
103 _minute=$minute
104 _second=$second
105 done
106}
107
108main "$@"