-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsnapshot
executable file
·142 lines (113 loc) · 3.75 KB
/
snapshot
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env bash
set -euo pipefail
DEFAULT_MAX_AGE=60
function main() {
local rootDevice; rootDevice=$(mount | grep -F 'on / type' | awk '{print $1}')
local maxAlterInTagen="$1"
ensure_root_permissions
ensure_btrfs "${rootDevice}"
local mountDir; mountDir="$(mktemp -d)"
local snapshots="${mountDir}/@snapshots"
sudo mount -o subvolid=0 "${rootDevice}" "${mountDir}"
ensure_snapshots_subvolume "$snapshots"
local newSnapshotsRoot; newSnapshotsRoot="${snapshots}/$(date +%Y-%m-%d_%H:%M:%S)"
remove_old_snapshots "${maxAlterInTagen}" "${snapshots}"
create_snapshots "${mountDir}" "${newSnapshotsRoot}"
sudo umount "${mountDir}"
rmdir "${mountDir}"
}
function parse_args() {
maxAlterInTagen=${DEFAULT_MAX_AGE}
while [[ $# -gt 0 ]]
do
case "${1}" in
-h | --help) print_usage ; exit 0 ;;
--max-age) maxAlterInTagen="$2"; shift ;;
*) echo "Unbekannte Option '$1'. Abbruch." >&2; exit 1 ;;
esac
shift
done
}
function print_usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h|--help] [--max-age <AGE>]
Script description here.
Available options:
-h, --help Print this help and exit
--max-age Maximum age of snapshots in days. Defaults to ${DEFAULT_MAX_AGE}.
EOF
}
function ensure_root_permissions() {
if ! sudo -v
then
echo Unverzichtbare Rootrechte sind nicht verfügbar. Abbruch! >&2
exit 1
fi
}
function ensure_btrfs() {
local rootDevice="$1"
# Wenn das Dateisystem auf $rootDevice nicht BtrFS ist, sind Schnappschüsse
# nicht möglich. Dies wird hier geprüft. Ggf. wird das Skript hier
# verlassen.
#
# Um das snapshot-Skript auch auf nicht-BtrFS-Systemen im distup-Alias
# verwenden zu können, ist der Exit-Code trotzdem "0".
if [[ $(lsblk -o FSTYPE "${rootDevice}" | tail -n1) != "btrfs" ]]
then
echo Gerät "${rootDevice}" ist kein BtrFS-Dateisystem. Es wird kein Snapshot gemacht! >&2
exit 0
fi
}
function ensure_snapshots_subvolume() {
local snapshots="$1"
if ! [[ -d "$snapshots" ]]
then
sudo btrfs subvol create "$snapshots"
fi
if ! sudo btrfs subvolume show "$snapshots" &> /dev/null
then
echo "Verzeichnis für Snapshots existiert, aber ist kein BtrFS-Subvolume. Fahre trotzdem fort." >&2
fi
}
function remove_old_snapshots() {
# Lösche alle Snapshots, die älter sind als $maxTage Tage.
local maxTage="$1"
local snapshots="$2"
local schwelle; schwelle=$(date --date "${maxTage} days ago" +%s)
local dateDir
find "${snapshots}" -mindepth 1 -maxdepth 1 | \
while read -r dateDir
do
local dirDate; dirDate="$(basename "${dateDir}")"
local whenCreated; whenCreated="$(date +%s -d "${dirDate/_/ }")"
if [[ "${whenCreated}" -le "${schwelle}" ]]
then
local curSnapshot
for curSnapshot in "${dateDir}"/*
do
# Es wird angenommen, dass alle Unterordner von ${dateDir}
# Snapshots sind. Dadurch vereinfacht sich diese Schleife
# ungemein.
sudo btrfs subvolume delete "${curSnapshot}" | sed "s!${mountDir}/!!g"
done
sudo rmdir "${dateDir}"
fi
done
}
function create_snapshots() {
# neue Snapshots anlegen
local mountDir="${1}"
local dest="${2}"
sudo mkdir "${dest}"
local srcSubvol
sudo btrfs subvolume list "${mountDir}" | \
grep -Ev '/|@snapshots' | \
cut -d' ' -f9 | \
while read -r srcSubvol
do
sudo btrfs subvolume "snapshot" -r "${mountDir}/${srcSubvol}" "${dest}/${srcSubvol}" |
sed "s!${mountDir}/!!g"
done
}
parse_args "$@"
main "${maxAlterInTagen}"