summaryrefslogtreecommitdiff
path: root/retain-snapshots
blob: 86f01208c6e4ae31f42c240e7f90653817bf0a9b (plain)
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/bin/bash
default_retain_years=7
retain_years= # TODO: read retain_years from .propagation

use_clock_time= # Delete according to clock time (WARNING: eventually deletes all data)


# Never edit this regex.
# Generated via: date -Ins | sed 's/[0-9]/[0-9]/g'
datetime_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]'

snapshot_prefix='.snapshot~'

main()
{
        [ $# = 1 ] || exit
        case "$1" in
                /*) ;;
                *) exit 1 ;;
        esac

        # Using `cd "$1"` and `find .` allows the path to contain
        # newlines, while simple `find "$1"` would break the script.
        # NB: Our snapshot name itself never contains whitespace.
        cd "$1" || exit
        find . -maxdepth 1 -type d -name "$snapshot_prefix""$datetime_regexp" | sort -rn | retain
}

is_readonly_subvolume()
{
        [ -d "$1" ]
        btrfs subvolume show -- "$1" | sed -En -e'/^\tFlags:.*\breadonly\b/{q0}' -e'${q1}'
}

btrfs_subvolume_delete()
{
        >&2 echo btrfs subvolume delete "$@"
}

# Delete all snapshots that we do not retain.
# The retention period is specified in a ".propagation" file.
# If not specified it is 7 years.
# Retain the most recent snapshot within any     year   of the   last 7 years.
# Retain the most recent snapshot within any    month   of the   last 365 days.
# Retain the most recent snapshot within any      day   of the   last 7 days.
# Retain the most recent snapshot within any     hour   of the   last 24 hours.
# Retain the most recent snapshot within any   minute   of the   last hour.
# Retain any snapshot created in the last minute.

retain()
{
        2>/dev/null [ "$retain_years" -ge 1 ] || retain_years=$default_retain_years

        _year= _month= _day= _hour= _minute= _second= _nanosecond=
        now=
        if [ "$use_clock_time" ]
        then
                now=$(date +%s)
        fi
        while read line
        do
                is_readonly_subvolume "$line" || break
                dateline=${line#*/"$snapshot_prefix"}
                [ "$dateline" != "$line" ]
                before=$(date -d "$dateline" +%s)
                [ "$now" ] || now=$before
                age=$((now - before))

                IFS='~-T:,' read year month day hour minute second nanosecond <<< "$dateline"

                keep=
                if [ "$year" != "$_year" ] && [ "$age" -lt $((retain_years * 366 * 24 * 60 * 60)) ]
                then
                        keep="$year -> $line"
                elif [ "$month" != "$_month" ] && [ "$age" -lt $((366 * 24 * 60 * 60)) ]
                then
                        keep="$year-$month -> $line"
                elif [ "$day" != "$_day" ] && [ "$age" -lt $((7 * 24 * 60 * 60)) ]
                then
                        keep="$year-$month-$day -> $line"
                elif [ "$hour" != "$_hour" ] && [ "$age" -lt $((24 * 60 * 60)) ]
                then
                        keep="$year-$month-${day}T$hour -> $line"
                elif [ "$minute" != "$_minute" ] && [ "$age" -lt $((60 * 60)) ]
                then
                        keep="$year-$month-${day}T$hour:$minute -> $line"
                elif [ "$age" -lt 60 ]
                then
                        keep="recent -> $line"
                fi

                if [ "$keep" ]
                then
                        echo "$keep" >&2
                else
                        btrfs_subvolume_delete -- "$line"
                fi

                _year=$year
                _month=$month
                _day=$day
                _hour=$hour
                _minute=$minute
                _second=$second
        done
}

main "$@"