SHOW:
|
|
- or go back to the newest paste.
| 1 | #!/bin/sh | |
| 2 | ||
| 3 | - | # btrfs-snapshots.sh 1.2 |
| 3 | + | # btrfs-snapshots.sh 1.3 |
| 4 | # Create hourly, daily, weekly, and monthly snapshots of btrfs filesystems | |
| 5 | ||
| 6 | # See https://copy.com/WI9AXqTH2nD4 or as a | |
| 7 | - | # "subversion" of http://pastebin.com/VXu6JxJw |
| 7 | + | # "subversion" of http://pastebin.com/YX8WKcsR |
| 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 | script_name=`basename "$0"` | |
| 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 | btrfs_progs_dev_path="/home/a/Copy/Computerkram/Programme/btrfs-progs.dev/bin" | |
| 58 | PATH="$btrfs_progs_dev_path:$PATH" | |
| 59 | ||
| 60 | # Find all btrfs filesystems | |
| 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.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 | tmp_mount_dir=`mktemp -d "/tmp/.btrfs.mount.$uuid.$safe_dev.XXXXXX"` | |
| 73 | # Mount the current btrfs there | |
| 74 | if ! mount -t btrfs "$dev" "$tmp_mount_dir"; then | |
| 75 | - | logger -t "$log_tag" -p "$log_fac.err" -- \ |
| 75 | + | logger -s -t "$log_tag" -p "$log_fac.err" -- \ |
| 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" |
| 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" -- \ |
| 83 | + | logger -s -t "$log_tag" -p "$log_fac.err" -- \ |
| 84 | "Error! Could not do: btrfs subv snaps -r $tmp_mount_dir $_snap_name" | |
| 85 | exit 1 | |
| 86 | else | |
| 87 | logger -t "$log_tag" -p "$log_fac.info" -- \ | |
| 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 | # - list all (read only + writable) snapshots of this filesystem | |
| 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" \ |
| 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") \ |
| 99 | + | btrfs subv list -r "$tmp_mount_dir" | grep " path \.,$snap_prefix") \ |
| 100 | | sort | uniq -u \ | |
| 101 | | while read __id IdDel __gen GenDel __top __level ToplevelDel __path PathDel; do | |
| 102 | # Delete the snapshot | |
| 103 | if ! btrfs subv del "$tmp_mount_dir/$PathDel" > /dev/null; then | |
| 104 | - | logger -t "$log_tag" -p "$log_fac.err" -- \ |
| 104 | + | logger -s -t "$log_tag" -p "$log_fac.err" -- \ |
| 105 | "Error! Could not do: btrfs subv del $tmp_mount_dir/$PathDel" | |
| 106 | exit 1 | |
| 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 | # - create a list of writable subvolumes | |
| 116 | # - sort those two lists | |
| 117 | # - list only those, which are show just once; ie. the read only subvolumes | |
| 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 | # Create the snapshot | |
| 122 | - | _snap_name="$tmp_mount_dir/$Path,$snap_prefix$snap_date" |
| 122 | + | # Figure out the name of the lowest subvolume - we're going to |
| 123 | # add "." in front of it, to "hide" the snapshot | |
| 124 | - | logger -t "$log_tag" -p "$log_fac.err" -- \ |
| 124 | + | subvolume=`basename "$Path"` |
| 125 | # Figure out the name of the "top" subvolumes | |
| 126 | topvolumes=`dirname "$Path"` | |
| 127 | # Create the name of the snapshot | |
| 128 | _snap_name="$tmp_mount_dir/$topvolumes/.$subvolume,$snap_prefix$snap_date" | |
| 129 | - | "Created snapshot $Path,$snap_prefix$snap_date for subvolume $Path" |
| 129 | + | # Actually create the snapshot |
| 130 | if ! btrfs subv snaps -r "$tmp_mount_dir/$Path" "$_snap_name" > /dev/null; then | |
| 131 | logger -s -t "$log_tag" -p "$log_fac.err" -- \ | |
| 132 | "Error! Could not do: btrfs subv snaps -r $tmp_mount_dir/$Path $_snap_name" | |
| 133 | exit 1 | |
| 134 | else | |
| 135 | logger -t "$log_tag" -p "$log_fac.info" -- \ | |
| 136 | "Created snapshot .$Path,$snap_prefix$snap_date for subvolume $Path" | |
| 137 | fi | |
| 138 | # Find old, no longer wanted snapshots with the "tag" of this subvolume | |
| 139 | - | | grep " path $Path,$snap_prefix" | tail -"$NUM_SNAPSHOTS" |
| 139 | + | |
| 140 | - | btrfs subv list -r "$tmp_mount_dir"|grep " path $Path,$snap_prefix") \ |
| 140 | + | |
| 141 | # - list only the last $NUM_SNAPSHOTS snapshots -> those are to "survive" | |
| 142 | # - again list all snapshots of current path | |
| 143 | # - sort and list only only those, which are shown just once | |
| 144 | # - -> a list of those snapshots, which are to go | |
| 145 | - | logger -t "$log_tag" -p "$log_fac.err" -- \ |
| 145 | + | |
| 146 | | grep " path $topvolumes/\.$subvolume,$snap_prefix" | tail -"$NUM_SNAPSHOTS" | |
| 147 | btrfs subv list -r "$tmp_mount_dir"|grep " path $topvolumes/\.$subvolume,$snap_prefix") \ | |
| 148 | | sort | uniq -u \ | |
| 149 | | while read __id IdDel __gen GenDel __top __level ToplevelDel __path PathDel; do | |
| 150 | # Delete the snapshot | |
| 151 | if ! btrfs subv del "$tmp_mount_dir/$PathDel" > /dev/null; then | |
| 152 | logger -s -t "$log_tag" -p "$log_fac.err" -- \ | |
| 153 | "Error! Could not do: btrfs subv del $tmp_mount_dir/$PathDel" | |
| 154 | exit 1 | |
| 155 | else | |
| 156 | - | logger -t "$log_tag" -p "$log_fac.err" -- \ |
| 156 | + | |
| 157 | "Removed snapshot $PathDel" | |
| 158 | fi | |
| 159 | done | |
| 160 | done | |
| 161 | # Unmount the filesystem | |
| 162 | if ! umount "$tmp_mount_dir"; then | |
| 163 | - | logger -t "$log_tag" -p "$log_fac.err" -- \ |
| 163 | + | logger -s -t "$log_tag" -p "$log_fac.err" -- \ |
| 164 | "Error! Could not do: umount$tmp_mount_dir" | |
| 165 | # Not fatal | |
| 166 | # exit 1 | |
| 167 | fi | |
| 168 | # Remove the directory where the filesystem was mounted | |
| 169 | if ! rmdir "$tmp_mount_dir"; then | |
| 170 | logger -s -t "$log_tag" -p "$log_fac.err" -- \ | |
| 171 | "Error! Could not do: rmdir $tmp_mount_dir" | |
| 172 | # Not fatal | |
| 173 | # exit 1 | |
| 174 | fi | |
| 175 | done | |
| 176 | ||
| 177 | # Done ☺ | |
| 178 | exit 0 | |
| 179 | # EOF |