#! @BASH@ set -e # File: backup-files.sh # Copyright (C) 2006 Steve Langasek # portions Copyright (C) 2003, 2004, 2005, 2006 Andreas Gruenbacher # , SuSE Labs # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # Create backup files of a list of files similar to GNU patch. A path # name prefix for the backup files must be specified with the -B option. usage () { echo "Usage: $0 [-B prefix] [-f {file|-}] [-s] [-k] [-t] [-L] [-b|-r|-x] {file|-} ... Create hard linked backup copies of a list of files read from standard input. -b Create backup -r Restore the backup -x Remove backup files and empty parent directories -k When doing a restore, keep the backup files -B Path name prefix for backup files -s Silent operation; only print error messages -f Read the filenames to process from file (- = standard input) -t Touch original files after restore (update their mtimes) -L Ensure that when finished, the source file has a link count of 1 " } ensure_nolinks() { local filename="$1" local link_count tmpname link_count=$(stat @STAT_HARDLINK@ "$filename") if [ $link_count -gt 1 ]; then tmpname=$(mktemp "$filename.XXXXXX") cp -p "$filename" "$tmpname" mv "$tmpname" "$filename" fi } backup() { local file="$1" local backup="${OPT_PREFIX}${file}" local dir dir=$(dirname "$backup") [ -d "$dir" ] || mkdir -p "$dir" if [ ! -e "$file" ]; then $ECHO "New file $file" : > "$backup" else $ECHO "Copying $file" if [ -n "$OPT_NOLINKS" -a "$(stat @STAT_HARDLINK@ "$file")" = 1 ]; then cp -p "$file" "$backup" else ln "$file" "$backup" 2> /dev/null || cp -p "$file" "$backup" if [ -n "$OPT_NOLINKS" ]; then ensure_nolinks "$file" fi fi fi } restore() { local file="$1" local backup="${OPT_PREFIX}${file}" if [ ! -e "$backup" ]; then return 1 fi if [ ! -s "$backup" ]; then $ECHO "Removing $file" if [ -e "$file" ]; then rm "$file" fi else $ECHO "Restoring $file" if [ -e "$file" ]; then rm "$file" else mkdir -p "$(dirname "$file")" fi if [ -n "$OPT_NOLINKS" ]; then cp -p "$backup" "$file" else ln "$backup" "$file" 2> /dev/null || cp -p "$backup" "$file" fi if [ -n "$OPT_TOUCH" ]; then touch "$file" fi fi if [ -z "$OPT_KEEP_BACKUP" ]; then rm "$backup" rmdir -p "${backup%/*}" 2> /dev/null || true fi } remove() { local file="$1" local backup="${OPT_PREFIX}${file}" if [ -e "$backup" ]; then rm "$backup" fi rmdir -p "${backup%/*}" 2> /dev/null || true } noop() { local file="$1" if [ -e "$file" ] && [ -n "$OPT_NOLINKS" ]; then ensure_nolinks "$file" fi } ECHO=echo OPT_WHAT=noop while [ $# -gt 0 ]; do case $1 in -b) OPT_WHAT=backup ;; -r) OPT_WHAT=restore ;; -x) OPT_WHAT=remove ;; -B) OPT_PREFIX=$2 shift ;; -f) OPT_FILE=$2 shift ;; -s) ECHO=: ;; -k) OPT_KEEP_BACKUP=1 ;; -L) OPT_NOLINKS=1 ;; -t) OPT_TOUCH=1 ;; -?*) usage exit 0 ;; *) break ;; esac shift done if [ -z "$OPT_PREFIX" ]; then usage exit 1 fi if [ "${OPT_PREFIX:(-1)}" != / ]; then echo "Prefix must be a directory" >&2 exit 1 fi if [ -n "$OPT_FILE" ]; then cat "$OPT_FILE" \ | while read nextfile; do $OPT_WHAT "$nextfile" done fi while [ $# -gt 0 ]; do case $1 in -) # No backup directory? We're done [ -d "$OPT_PREFIX" ] || exit 0 find "$OPT_PREFIX" -type f -print \ | while read do $OPT_WHAT "${REPLY#$OPT_PREFIX}" done if [ ${PIPESTATUS[0]} != 0 ]; then exit 1 fi ;; *) $OPT_WHAT "$1" ;; esac shift done