]>
git.rmz.io Git - dotfiles.git/blob - bin/rsync-snapshot
2 # ----------------------------------------------------------------------
3 # created by francois scheurer on 20070323
4 # derivate from mikes handy rotating-filesystem-snapshot utility
5 # see http://www.mikerubel.org/computers/rsync_snapshots
6 # ----------------------------------------------------------------------
8 # 1) rsync -avz /src/foo /dest => ok, creates /dest/foo, like cp -a /src/foo /dest
9 # 2) rsync -avz /src/foo/ /dest/foo => ok, creates /dest/foo, like cp -a /src/foo/. /dest/foo (or like cp -a /src/foo /dest)
10 # 3) rsync -avz /src/foo/ /dest/foo/ => ok, same as 2)
11 # 4) rsync -avz /src/foo/ /dest => dangerous!!! overwrite dest content, like cp -a /src/foo/. /dest
12 # solution: remove trailing / at /src/foo/ => 1)
13 # minor problem: rsync -avz /src/foo /dest/foo => creates /dest/foo/foo, like mkdir /dest/foo && cp -a /src/foo /dest/foo
16 # -a equals -rlptgoD (no -H,-A,-X)
23 # -D --devices --specials
24 # -x --one-file-system
31 # --bwlimit=X limit disk IO to X kB/s
37 # -P equals --progress --partial
40 # -e'ssh -o ServerAliveInterval=60'
45 # -i --itemize-changes
48 # --rsh=\"ssh -p ${HOST_PORT} -i /root/.ssh/rsync_rsa -l root\"
49 # --rsync-path=\"/usr/bin/rsync\""
51 # the default behavior is to skip files with same size & mtime on destination
52 # mtime = last data write access
53 # atime = last data read access (can be ignored with noatime mount option or with chattr +A)
54 # ctime = last inode change (write access, change of permission or ownership)
55 # note that a checksum is always done after a file synchronization/transfer
56 # --modify-window=X ignore mtime differences less or equal to X sec
57 # --size-only skip files with same size on destination (ignore mtime)
58 # -c --checksum skip files with same MD5 checksum on destination (ignore size & mtime, all files are read once, then the list of files to be resynchronized is read a second time, there is a lot of disk IO but network trafic is minimal if many files are identical; log includes only different files)
59 # -I --ignore-times never skip files (all files are resynchronized, all files are read once, there is more network trafic than with --checksum but less disk IO and hence is faster than --checksum if net is fast or if most files are different; log includes all files)
60 # --link-dest does the quickcheck on another reference-directory and makes hardlinks if quickcheck succeeds
61 # (however, if mtime is different and --perms is used, the reference file is copied in a new inode)
62 # see also this link for a rsync tutorial: http://www.thegeekstuff.com/2010/09/rsync-command-examples/
64 # 'du' slow on many snapshot.X..done
65 # autokill after n minutes.
66 # if disk full, its better to replace the snapshot.001 than to cancel and have a very old backup (even if it may fail to create the snapshot and ends with 0 backups)..done
67 # rsync-snapshot for oracle redo logs..old
68 # 'find'-list with md5 signatures -> .gz file stored aside rsync.log.gz inside the snapshot.X folder; this file will be move to parent dir /backup/snapshot/localhost/ before deletion of a snapshot; this file will also be used to extract an incremental backup with tape-arch.sh..done (md5sum calculation with rsync-list.sh for acm14=18m58 and only 5m27 with a reference file. speedup is ~250-300%)
69 # realtime freedisk display with echo $(($(stat -f -c "%f" /backup/snapshot/) * 4096 / 1024))
70 # use authorized_keys with restriction of bash (command=) and set sshd_config with PermitRootLogin=forced-commands-only, see http://troy.jdmz.net/rsync/index.html http://www.snailbook.com/faq/restricted-scp.auto.html
71 # note: rsync lists all files in snapshot.X disregarding inclusion patterns, this is slow.
76 # ------------- the help page ------------------------------------------
77 if [ " $1 " == "-h" ] || [ " $1 " == "--help" ]; then
79 Version 2.01 2013-01-16
81 USAGE: rsync-snapshot.sh HOST [--recheck]
83 PURPOSE: create a snapshot backup of the whole filesystem into the folder
84 '/backup/snapshot/HOST/snapshot.001'.
85 If HOST is 'localhost' it is replaced with the local hostname.
86 If HOST is a remote host then rsync over ssh is used to transfer the files
87 with a delta-transfer algorithm to transfer only minimal parts of the files
88 and improve speed; rsync uses for this the previous backup as reference.
89 This reference is also used to create hard links instead of files when
90 possible and thus save disk space. If original and reference file have
91 identical content but different timestamps or permissions then no hard link
93 A rotation of all backups renames snapshot.X into snapshot.X+1 and removes
94 backups with X>512. About 10 backups with non-linear distribution are kept
95 in rotation; for example with X=1,2,3,4,8,16,32,64,128,256,512.
96 The snapshots folders are protected read-only against all users including
98 The --recheck option forces a sync of all files even if they have same mtime
99 & size; it is can verify a backup and fix corrupted files;
100 --recheck recalculates also the MD5 integrity signatures without using the
101 last signature-file as precalculation.
102 Some features like filter rules, MD5, chattr, bwlimit and per server retention
103 policy can be configured by modifying the scripts directly.
106 /backup/snapshot/rsync/rsync-snapshot.sh the backup script
107 /backup/snapshot/rsync/rsync-list.sh the md5 signature script
108 /backup/snapshot/rsync/rsync-include.txt the filter rules
111 (nice -5 ./rsync-snapshot.sh >log &) ; tail -f log
112 cd /backup/snapshot; for i in $(ls -A) ; do nice -10 /backup/snapshot/rsync/rsync-snapshot.sh $i ; done
120 # ------------- tuning options, file locations and constants -----------
121 SRC=" $1 " #name of backup source, may be a remote or local hostname
122 OPT=" $2 " #options (--recheck)
123 HOST_PORT=22 #port of source of backup
124 SCRIPT_PATH="/backup/snapshot/rsync"
125 SNAPSHOT_DST="/backup/snapshot" #destination folder
126 NAME="snapshot" #backup name
128 MIN_MIBSIZE=5000 # older snapshots (except snapshot.001) are removed if free disk <= MIN_MIBSIZE. the script may exit without performing a backup if free disk is still short.
129 OVERWRITE_LAST=0 # if free disk space is too small, then this option let us remove snapshot.001 as well and retry once
130 MAX_MIBSIZE=80000 # older snapshots (except snapshot.001) are removed if their size >= MAX_MIBSIZE. the script performs a backup even if their size is too big.
131 #old: SPEED=5 # 1 is slow, 100 is fast, 100000 faster and 0 does not use slow-down. this allows to avoid rsync consuming too much system performance
132 BWLIMIT=100000 # bandwidth limit in KiB/s. 0 does not use slow-down. this allows to avoid rsync consuming too much system performance
133 BACKUPSERVER="rembk" # this server connects to all other to download filesystems and create remote snapshot backups
134 MD5LIST=0 #to compute a list of md5 integrity signatures of all backuped files, need 'rsync-list.sh'
135 CHATTR=1 # to use 'chattr' command and protect the backups again modification and deletion
136 DU=1 # to use 'du' command and calculate the size of existing backups, disable it if you have many backups and it is getting too slow (for example on BACKUPSERVER)
137 SOURCE="/" #source folder to backup
139 HOST_LOCAL=" $(hostname -s) " #local hostname
140 #HOST_SRC=" ${SRC:-${HOST_LOCAL} }" #explicit source hostname, default is local hostname
141 if [ -z " ${SRC} " ] || [ " ${SRC} " == "localhost" ]; then
142 HOST_SRC=" ${HOST_LOCAL} " #explicit source hostname, default is local hostname
144 HOST_SRC=" ${SRC} " #explicit source hostname
147 if [ " ${HOST_LOCAL} " == " ${BACKUPSERVER} " ]; then #if we are on BACKUPSERVER then do some fine tuning
149 MIN_MIBSIZE=35000 #needed free space for chunk-file tape-arch.sh
151 DU=0 # NB: 'du' is currently disabled on BACKUPSERVER for performance reasons
152 elif [ " ${HOST_LOCAL} " == " ${HOST_SRC} " ]; then #else if we are on a generic server then do other some fine tuning
153 if [ " ${HOST_SRC} " == "ZRHSV-TST01" ]; then
154 MIN_MIBSIZE=500; CHATTR=0; DU=0; MD5LIST=0
161 # ------------- initialization -----------------------------------------
162 shopt -s extglob #enable extended pattern matching operators
176 --bwlimit= ${BWLIMIT} "
183 if [ " ${HOST_SRC} " != " ${HOST_LOCAL} " ]; then #option for a remote server
184 SOURCE=" ${HOST_SRC} : ${SOURCE} "
187 --rsh= \" ssh -p ${HOST_PORT} -i /root/.ssh/rsync_rsa -l root \" \
188 --rsync-path= \" /usr/bin/rsync \" "
190 if [ " ${OPT} " == "--recheck" ]; then
193 elif [ -n " ${OPT} " ]; then
194 echo "Try rsync-snapshot.sh --help ."
201 # ------------- check conditions ---------------------------------------
202 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot backup is created into ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .001 ==="
203 STARTDATE= $(date +%s)
205 # make sure we're running as root
206 if (( $(id -u) != 0)); then
207 echo "Sorry, must be root. Exiting..."
208 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot failed. ==="
212 # make sure we have a correct snapshot folder
213 if [ ! -d " ${SNAPSHOT_DST} / ${HOST_SRC} " ]; then
214 echo "Sorry, folder ${SNAPSHOT_DST} / ${HOST_SRC} is missing. Exiting..."
215 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot failed. ==="
219 # make sure we do not have started already rsync-snapshot.sh or rsync process (started by rsync-cp.sh or by a remote rsync-snapshot.sh) in the background.
220 if [ " ${HOST_LOCAL} " != " ${BACKUPSERVER} " ]; then #because BACKUPSERVER need sometimes to perform an rsync-cp.sh it must disable the check of "already started".
221 #RSYNCPID= $(pgrep -f "/bin/bash .*rsync-snapshot.sh")
222 #if ([ -n " ${RSYNCPID} " ] && [ " ${RSYNCPID} " != "$$" ]) #|| pgrep -x "rsync"
223 if pgrep -f "/bin/\w*sh \w*rsync-snapshot\.sh" | grep -qv "$$"; then
224 echo "Sorry, rsync is already running in the background. Exiting..."
225 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot failed. ==="
233 # ------------- remove some old backups --------------------------------
234 # remove certain snapshots to achieve an exponential distribution in time of the backups (1,2,4,8,...)
235 for b in 512 256 128 64 32 16 8 4; do
237 let f=0 #this flag is set to 1 when we find the 1st snapshot in the range b..a
238 for i in $(seq -f'%03g' "${b}" -1 "${a}") ; do
239 if [ -d " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " ]; then
240 if [ " ${f} " -eq 0 ]; then
243 echo " $(date +%Y-%m-%d_%H:%M:%S) Removing ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} ..."
244 [ " ${CHATTR} " -eq 1 ] && chattr -R -i " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " &>/dev/null
245 rm -rf " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} "
251 # remove additional backups if free disk space is short
253 local MIN_MIBSIZE2= $1
254 local MAX_MIBSIZE2= $2
255 for i in $(seq -f'%03g' 512 -1 001) ; do
256 if [ -d " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " ] || [ ${i} -eq 1 ]; then
257 [ ! -h " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last" ] && [ -d " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " ] && ln -s " ${NAME} . ${i} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last"
258 let d=0 #disk space used by snapshots and free disk space are ok
259 echo -n " $(date +%Y-%m-%d_%H:%M:%S) Checking free disk space... "
260 FREEDISK= $(df -m ${SNAPSHOT_DST} | tail -1 | sed -e 's/ */ /g' | cut -d" " -f4 | sed -e 's/M*//g')
261 echo -n " ${FREEDISK} MiB free. "
262 if [ ${FREEDISK} -ge ${MIN_MIBSIZE2} ]; then
263 echo "Ok, bigger than ${MIN_MIBSIZE2} MiB."
264 if [ " ${DU} " -eq 0 ]; then #avoid slow 'du'
267 echo -n " $(date +%Y-%m-%d_%H:%M:%S) Checking disk space used by ${SNAPSHOT_DST} / ${HOST_SRC} ... "
268 USEDDISK= $(du -ms "${SNAPSHOT_DST}/${HOST_SRC}/" | cut -f1)
269 echo -n " ${USEDDISK} MiB used. "
270 if [ ${USEDDISK} -le ${MAX_MIBSIZE2} ]; then
271 echo "Ok, smaller than ${MAX_MIBSIZE2} MiB."
274 let d=2 #disk space used by snapshots is too big
278 let d=1 #free disk space is too small
280 if [ ${d} -ne 0 ]; then #we need to remove snapshots
281 if [ ${i} -ne 1 ]; then
282 echo "Removing ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} ..."
283 [ " ${CHATTR} " -eq 1 ] && chattr -R -i " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " &>/dev/null
284 rm -rf " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} "
285 [ -h " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last" ] && rm -f " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last"
286 else #all snapshots except snapshot.001 are removed
287 if [ ${d} -eq 1 ]; then #snapshot.001 causes that free space is too small
288 if [ " ${OVERWRITE_LAST} " -eq 1 ]; then #last chance: remove snapshot.001 and retry once
290 echo "Warning, free disk space will be smaller than ${MIN_MIBSIZE} MiB."
291 echo " $(date +%Y-%m-%d_%H:%M:%S) OVERWRITE_LAST enabled. Removing ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .001 ..."
292 rm -rf " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .001"
293 [ -h " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last" ] && rm -f " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last"
295 for j in ${LNKDST//--link-dest=/} ; do
296 if [ -d " ${j} " ] && [ " ${CHATTR} " -eq 1 ] && [ $(lsattr -d "${j}" | cut -b5) != "i" ]; then
297 chattr -R +i " ${j} " &>/dev/null #undo unprotection that was needed to use hardlinks
300 [ ! -h " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last" ] && ln -s " ${NAME} . ${j} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last"
301 echo "Sorry, free disk space will be smaller than ${MIN_MIBSIZE} MiB. Exiting..."
302 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot failed. ==="
305 elif [ ${d} -eq 2 ]; then #snapshot.001 causes that disk space used by snapshots is too big
306 echo "Warning, disk space used by ${SNAPSHOT_DST} / ${HOST_SRC} will be bigger than ${MAX_MIBSIZE} MiB. Continuing anyway..."
314 # perform an estimation of required disk space for the new backup
315 while :; do #this loop is executed a 2nd time if OVERWRITE_LAST was ==1 and snapshot.001 got removed
316 OOVERWRITE_LAST=" ${OVERWRITE_LAST} "
317 echo -n " $(date +%Y-%m-%d_%H:%M:%S) Testing needed free disk space ..."
318 mkdir -p " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .test-free-disk-space"
319 chmod -R 775 " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .test-free-disk-space"
320 cat /dev/null >" ${SNAPSHOT_DST} / ${HOST_SRC} / ${LOG} "
321 LNKDST= $(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" -printf " --link-dest=%p")
322 for i in ${LNKDST//--link-dest=/} ; do
323 if [ -d " ${i} " ] && [ " ${CHATTR} " -eq 1 ] && [ $(lsattr -d "${i}" | cut -b5) == "i" ]; then
324 chattr -R -i " ${i} " &>/dev/null #unprotect last snapshots to use hardlinks
330 --include-from=" ${SCRIPT_PATH} /rsync-include.txt" \
332 " ${SOURCE} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .test-free-disk-space" >>" ${SNAPSHOT_DST} / ${HOST_SRC} / ${LOG} "
334 if [ " ${RES} " -ne 0 ] && [ " ${RES} " -ne 23 ] && [ " ${RES} " -ne 24 ]; then
335 echo "Sorry, error in rsync execution (value ${RES} ). Exiting..."
336 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot failed. ==="
339 let i= $(tail -100 "${SNAPSHOT_DST}/${HOST_SRC}/${LOG}" | grep 'Total transferred file size:' | cut -d " " -f5) /1048576
340 echo " ${i} MiB needed."
341 rm -rf " ${SNAPSHOT_DST} / ${HOST_SRC} / ${LOG} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .test-free-disk-space"
342 remove_snapshot $((${MIN_MIBSIZE} + ${i}) ) $((${MAX_MIBSIZE} - ${i}) )
343 if [ " ${OOVERWRITE_LAST} " == " ${OVERWRITE_LAST} " ]; then #no need to retry
351 # ------------- create the snapshot backup -----------------------------
352 # perform the filesystem backup using rsync and hard-links to the latest snapshot
354 # -rsync behaves like cp --remove-destination by default, so the destination
355 # is unlinked first. If it were not so, this would copy over the other
357 # -use --link-dest to hard-link when possible with previous snapshot,
358 # timestamps, permissions and ownerships are preserved
359 echo " $(date +%Y-%m-%d_%H:%M:%S) Creating folder ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000 ..."
360 mkdir -p " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000"
361 chmod 775 " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000"
362 cat /dev/null >" ${SNAPSHOT_DST} / ${HOST_SRC} / ${LOG} "
363 echo -n " $(date +%Y-%m-%d_%H:%M:%S) Creating backup of ${HOST_SRC} into ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000"
364 if [ -n " ${LNKDST} " ]; then
365 echo " hardlinked with ${LNKDST//--link-dest=/} ..."
367 echo " not hardlinked ..."
372 --include-from=" ${SCRIPT_PATH} /rsync-include.txt" \
374 " ${SOURCE} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000" >>" ${SNAPSHOT_DST} / ${HOST_SRC} / ${LOG} "
376 if [ " ${RES} " -ne 0 ] && [ " ${RES} " -ne 23 ] && [ " ${RES} " -ne 24 ]; then
377 echo "Sorry, error in rsync execution (value ${RES} ). Exiting..."
378 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot failed. ==="
381 for i in ${LNKDST//--link-dest=/} ; do
382 if [ -d " ${i} " ] && [ " ${CHATTR} " -eq 1 ] && [ $(lsattr -d "${i}" | cut -b5) != "i" ]; then
383 chattr -R +i " ${i} " &>/dev/null #undo unprotection that was needed to use hardlinks
386 mv " ${SNAPSHOT_DST} / ${HOST_SRC} / ${LOG} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000/ ${LOG} "
387 gzip -f " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000/ ${LOG} "
392 # ------------- create the MD5 integrity signature ---------------------
393 # create a gziped 'find'-list of all snapshot files (including md5 signatures)
394 if [ " ${MD5LIST} " -eq 1 ]; then
395 echo " $(date +%Y-%m-%d_%H:%M:%S) Computing filelist with md5 signatures of ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000 ..."
400 # let NOW ${MYTZ:0:1} =3600* ${MYTZ:1:2} +60* ${MYTZ:3:2} # convert localtime to UTC
401 # DATESTR= $(date -d "1970-01-01 $((${NOW} - 1) ) sec" "+%Y-%m-%d_%H:%M:%S") # 'now - 1s' to avoid missing files
402 DATESTR= $(date -d "1970-01-01 UTC $(($(date +%s) - 1)) seconds" "+%Y-%m-%d_%H:%M:%S") # 'now - 1s' to avoid missing files
403 REF_LIST=" $(find ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.001/ -maxdepth 1 -type f -name 'snapshot.*.list.gz' 2>/dev/null) "
404 if [ -n " ${REF_LIST} " ] && [ " ${OPT} " != "--recheck" ]; then
405 REF_LIST2="/tmp/rsync-reflist.tmp"
406 gzip -dc " ${REF_LIST} " >" ${REF_LIST2} "
407 touch -r " ${REF_LIST} " " ${REF_LIST2} "
408 ${SCRIPT_PATH} /rsync-list.sh " ${HOST_SRC} / ${NAME} .000" 0 " ${REF_LIST2} " | sort -u | gzip -c >" ${HOST_SRC} / ${NAME} . ${DATESTR} .list.gz"
411 ${SCRIPT_PATH} /rsync-list.sh " ${HOST_SRC} / ${NAME} .000" 0 | sort -u | gzip -c >" ${HOST_SRC} / ${NAME} . ${DATESTR} .list.gz"
413 touch -d " ${DATESTR/_/ } " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${DATESTR} .list.gz"
415 [ ! -d " ${SNAPSHOT_DST} / ${HOST_SRC} /md5-log" ] && mkdir -p " ${SNAPSHOT_DST} / ${HOST_SRC} /md5-log"
416 cp -al " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${DATESTR} .list.gz" " ${SNAPSHOT_DST} / ${HOST_SRC} /md5-log/ ${NAME} . ${DATESTR} .list.gz"
417 mv " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${DATESTR} .list.gz" " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000/ ${NAME} . ${DATESTR} .list.gz"
418 touch -d " ${DATESTR/_/ } " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000"
424 # ------------- finish and clean up ------------------------------------
425 # protect the backup against modification with chattr +immutable
426 if [ " ${CHATTR} " -eq 1 ]; then
427 echo " $(date +%Y-%m-%d_%H:%M:%S) Setting recursively immutable flag of ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000 ..."
428 chattr -R +i " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .000" &>/dev/null
432 if [ -d " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .512" ]; then #remove snapshot.512
433 echo "Removing ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .512 ..."
434 [ " ${CHATTR} " -eq 1 ] && chattr -R -i " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .512" &>/dev/null
435 rm -rf " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .512"
437 [ -h " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last" ] && rm -f " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last"
438 for i in $(seq -f'%03g' 511 -1 000) ; do
439 if [ -d " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " ]; then
441 j= $(printf "%.3d" "${j}")
442 echo "Renaming ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} into ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${j} ..."
443 [ " ${CHATTR} " -eq 1 ] && chattr -i " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " &>/dev/null
444 mv " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${i} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${j} "
445 [ " ${CHATTR} " -eq 1 ] && chattr +i " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} . ${j} " &>/dev/null
446 [ ! -h " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last" ] && ln -s " ${NAME} . ${j} " " ${SNAPSHOT_DST} / ${HOST_SRC} / ${NAME} .last"
450 # remove additional backups if free disk space is short
451 OVERWRITE_LAST=0 #next call of remove_snapshot() will not remove snapshot.001
452 remove_snapshot ${MIN_MIBSIZE} ${MAX_MIBSIZE}
453 echo " $(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC} : === Snapshot backup successfully done in $(($(date +%s) - ${STARTDATE} )) sec. ==="