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 |