SHOW:
|
|
- or go back to the newest paste.
1 | #!/bin/sh | |
2 | ||
3 | - | # btrfs-snapshots.sh 1.0 |
3 | + | # btrfs-snapshots.sh 1.1 |
4 | # Create hourly, daily, weekly, and monthly snapshots of btrfs filesystems | |
5 | ||
6 | - | # See https://copy.com/WI9AXqTH2nD4 or as a "subversion" of http://pastebin.com/Rz49ANTU |
6 | + | # See https://copy.com/WI9AXqTH2nD4 or as a "subversion" of http://pastebin.com/YfZUnytT |
7 | ||
8 | # Based somewhat on http://article.gmane.org/gmane.comp.file-systems.btrfs/12609 | |
9 | # Here's my crontab: | |
10 | # 00,15,30,45 * * * * /usr/local/bin/btrfs-snapshots.sh frequently 4 | |
11 | # 38 * * * * /usr/local/bin/btrfs-snapshots.sh hourly 24 | |
12 | # 08 00 * * * /usr/local/bin/btrfs-snapshots.sh daily 7 | |
13 | # 08 12 * * 0 /usr/local/bin/btrfs-snapshots.sh weekly 4 | |
14 | ||
15 | if [ "$#" -ne 2 ]; then | |
16 | echo "Usage: $0 SNAPSHOT_TAG NUM_SNAPSHOTS | |
17 | Create hourly, daily, weekly, and monthly snapshots of btrfs filesystems. | |
18 | ||
19 | Based somewhat on http://article.gmane.org/gmane.comp.file-systems.btrfs/12609 | |
20 | Here's my crontab: | |
21 | 00,15,30,45 * * * * $0 frequently 4 | |
22 | 38 * * * * $0 hourly 24 | |
23 | 08 00 * * * $0 daily 7 | |
24 | 08 12 * * 0 $0 weekly 4" | |
25 | exit 1 | |
26 | fi | |
27 | ||
28 | # "Tag" to use for snapshots | |
29 | SNAPSHOT_TAG="$1" | |
30 | # How many of those snapshots to keep | |
31 | NUM_SNAPSHOTS="$2" | |
32 | ||
33 | # Helper veriables | |
34 | # A "prefix" for all the snapshots | |
35 | snap_prefix="snapshot:$SNAPSHOT_TAG:" | |
36 | # "Now" | |
37 | snap_date="`date +%Y-%m-%d--%H.%M.%S.%N`" | |
38 | # Name of the script | |
39 | script_name=`basename "$0"` | |
40 | ||
41 | # Messages are logged to syslog | |
42 | # facility: auth, authpriv (for security information of a sensitive nature), cron, daemon, ftp, kern (can't be generated from user process), lpr, mail, news, security (deprecated synonym for auth), syslog, user, uucp, and local0 to local7 | |
43 | log_fac="local5" | |
44 | # tag | |
45 | log_tag="$script_name" | |
46 | ||
47 | # Add path to btrfs-progs dev version to beginning of $PATH | |
48 | # Needs a post 2013-02-01 15:55:06 (commit 64edc851da59c47b92ee6830101be0854add7f09) | |
49 | # version of btrfs-progs from http://git.kernel.org/cgit/linux/kernel/git/mason/btrfs-progs.git/commit/ | |
50 | # because of issues with "btrfs subv list" | |
51 | btrfs_progs_dev_path="/home/a/Copy/Computerkram/Programme/btrfs-progs.dev/bin" | |
52 | PATH="$btrfs_progs_dev_path:$PATH" | |
53 | ||
54 | # Find all btrfs filesystems | |
55 | btrfs fi show 2>/dev/null | awk '/ path / {print $NF}' | while read dev; do | |
56 | # Find uuid and label | |
57 | - | set -- `sudo btrfs fi show 2>/dev/null | grep -B2 " path $dev" | grep "Label:" | sed 's,.*: \(.*\) uuid: \(.*\),\1 \2,'` |
57 | + | set -- `btrfs fi show 2>/dev/null | grep -B2 " path $dev" | grep "Label:" | sed 's,.*: \(.*\) uuid: \(.*\),\1 \2,'` |
58 | label="$1" | |
59 | uuid="$2" | |
60 | logger -t "$log_tag" -p "$log_fac.info" -- "Processing filesystem with label $label and uuid $uuid on $dev" | |
61 | # Change the "dev" name ("path") so, that it is usable; ie. / -> . | |
62 | safe_dev=`echo "$dev" | tr / .` | |
63 | # Create a directory for the temporary mountpoint | |
64 | tmp_mount_dir=`mktemp -d "/tmp/.btrfs.mount.$uuid.$safe_dev.XXXXXX"` | |
65 | # Mount the current btrfs there | |
66 | if ! mount -t btrfs "$dev" "$tmp_mount_dir"; then | |
67 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: mount -t btrfs $dev $tmp_mount_dir" | |
68 | exit 1 | |
69 | fi | |
70 | ||
71 | # Create snapshots for the "root" volume | |
72 | if ! btrfs subv snaps -r "$tmp_mount_dir" "$tmp_mount_dir/,$snap_prefix$snap_date" > /dev/null; then | |
73 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv snaps -r $tmp_mount_dir $tmp_mount_dir/,$snap_prefix$snap_date" | |
74 | exit 1 | |
75 | else | |
76 | logger -t "$log_tag" -p "$log_fac.info" -- "Created snapshot $Path,$snap_prefix$snap_date for root volume of fs with uuid $uuid" | |
77 | fi | |
78 | # Find old, no longer wanted snapshots with the "tag" of this subvolume | |
79 | # - list all (read only + writable) snapshots of this filesystem | |
80 | # - list only those for current "path" (-> subvolume) and prefix | |
81 | # - list only the last $NUM_SNAPSHOTS snapshots -> those are to "survive" | |
82 | # - again list all snapshots of current path | |
83 | # - sort and list only only those, which are shown just once | |
84 | # - -> a list of those snapshots, which are to go | |
85 | (btrfs subv list -r "$tmp_mount_dir" | grep " path ,$snap_prefix" | tail -"$NUM_SNAPSHOTS"; btrfs subv list -r "$tmp_mount_dir" | grep " path ,$snap_prefix") | sort | uniq -u | while read __id IdDel __gen GenDel __top __level ToplevelDel __path PathDel; do | |
86 | # Delete the snapshot | |
87 | if ! btrfs subv del "$tmp_mount_dir/$PathDel" > /dev/null; then | |
88 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv del $tmp_mount_dir/$PathDel" | |
89 | exit 1 | |
90 | else | |
91 | logger -t "$log_tag" -p "$log_fac.info" -- "Removed snapshot $PathDel" | |
92 | fi | |
93 | done | |
94 | ||
95 | # List all non read-only (ie. writable) subvolumes | |
96 | # - create a list of ALL subvolumes (read only + writable) | |
97 | # - create a list of writable subvolumes | |
98 | # - sort those two lists | |
99 | # - list only those, which are show just once; ie. the read only subvolumes | |
100 | (btrfs subv list -ar $tmp_mount_dir; btrfs subv list -a $tmp_mount_dir) | sort | uniq -u | while read _id Id _gen Gen _top _level Toplevel _path Path; do | |
101 | # Create the snapshot | |
102 | if ! btrfs subv snaps -r "$tmp_mount_dir/$Path" "$tmp_mount_dir/$Path,$snap_prefix$snap_date" > /dev/null; then | |
103 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv snaps -r $tmp_mount_dir/$Path $tmp_mount_dir/$Path,$snap_prefix$snap_date" | |
104 | exit 1 | |
105 | else | |
106 | logger -t "$log_tag" -p "$log_fac.info" -- "Created snapshot $Path,$snap_prefix$snap_date for subvolume $Path" | |
107 | fi | |
108 | # Find old, no longer wanted snapshots with the "tag" of this subvolume | |
109 | # - list all (read only + writable) snapshots of this filesystem | |
110 | # - list only those for current "path" (-> subvolume) and prefix | |
111 | # - list only the last $NUM_SNAPSHOTS snapshots -> those are to "survive" | |
112 | # - again list all snapshots of current path | |
113 | # - sort and list only only those, which are shown just once | |
114 | # - -> a list of those snapshots, which are to go | |
115 | (btrfs subv list -r "$tmp_mount_dir" | grep " path $Path,$snap_prefix" | tail -"$NUM_SNAPSHOTS"; btrfs subv list -r "$tmp_mount_dir" | grep " path $Path,$snap_prefix") | sort | uniq -u | while read __id IdDel __gen GenDel __top __level ToplevelDel __path PathDel; do | |
116 | # Delete the snapshot | |
117 | if ! btrfs subv del "$tmp_mount_dir/$PathDel" > /dev/null; then | |
118 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv del $tmp_mount_dir/$PathDel" | |
119 | exit 1 | |
120 | else | |
121 | logger -t "$log_tag" -p "$log_fac.info" -- "Removed snapshot $PathDel" | |
122 | fi | |
123 | done | |
124 | done | |
125 | # Unmount the filesystem | |
126 | if ! umount "$tmp_mount_dir"; then | |
127 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: umount$tmp_mount_dir" | |
128 | # Not fatal | |
129 | # exit 1 | |
130 | fi | |
131 | # Remove the directory where the filesystem was mounted | |
132 | if ! rmdir "$tmp_mount_dir"; then | |
133 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: rmdir $tmp_mount_dir" | |
134 | # Not fatal | |
135 | # exit 1 | |
136 | fi | |
137 | done | |
138 | ||
139 | # Done ☺ | |
140 | exit 0 | |
141 | # EOF |