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