Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 25 Aug 1997 11:23:55 -0700 (PDT)
From:      Simon Shapiro <Shimon@i-Connect.Net>
To:        "=?ISO-8859-1?Q?Dott._Davide_Tom=E8?=" <dtome@iol.it>
Cc:        hackers@FreeBSD.ORG
Subject:   RE: multiple backup on one tape
Message-ID:  <XFMail.970825112355.Shimon@i-Connect.Net>
In-Reply-To: <199708250808.KAA17675@dante.iol.it>

next in thread | previous in thread | raw e-mail | index | archive | help
This message is in MIME format
--_=XFMail.1.2-alpha.p0.FreeBSD:970825111934:1078=_
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


Hi "Dott. Davide Tomh";  On 25-Aug-97 you wrote: 
>  Hi,
>  I'd like to know how can I have backups of different
>  machines on the same tape with tar, and how to
>  restore them.

Ah, finally something I can contribute :-)

See the attachment for a program to do just that.
It backs up your system, one filesystem at a time, all on one (or
more) tape.

You restore by simply reversing the process.  On some Unixes, you
may get an error in reading so do the following instead:

dd if=/dev/nrst0 bs=64k | ${whatever_archiver_you_use}

For example, to restore the output of dat_bu, NON_DESTRUCTIVELY,

    cd /
    dd if=/dev/nrst0 bs=64k | cpio -H newc -ovdm

Repeat until you see TWO zero size files restored in a row.

BTW, this has nothing to do with FreeBSD, nor with this list the
way I understand it.  -questions would be more appropriate.  No?

Simon

--_=XFMail.1.2-alpha.p0.FreeBSD:970825111934:1078=_
Content-Disposition: attachment; filename="dat_bu"
Content-Transfer-Encoding: none
Content-Description: Multiple volumes backup on one tape
Content-Type: application/octet-stream; name=dat_bu; SizeOnDisk=16438

#!/bin/bash
#set -x
#*****************************************************************************
#                                                                            *
#       Copyright (c) 1991, 1992, 1995 by Simon Shapiro                      *
#       All Rights Reserved                                                  *
#                                                                            *
#       THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF                       *
#                        Simon Shapiro                                       *
#                                                                            *
#       The copyright notice above does not evidence any                     *
#       actual or intended publication of such source code.                  *
#                                                                            *
#*****************************************************************************/

#*****************************************************************************
#                                                                            *
#  dat_bu:       Backup on DAT, each file system in its own archive          *
#                                                                            *
#  arguments:    -e           Pre-Erase the tape before use                  *
#                -F fs_type   Specify file system type to backup             *
#                -V           Verify (ask) each file system is indeed to be  *
#                             backed up                                      *
#                -v           Be verbose about your progress                 *
#                -d  dest_dev Specify where the backup is to go              *
#                -z           Compress output (via gzip)                     *
#                -f           Forces backup to continue regardless of errors *
#                -q           Do not say anything at all unless an error     *
#                -b           Background mode on                             *
#                -p p|P       Make a partial backup of all changes since last*
#                             PARTIAL backup                                 *
#                -p c|C       Make a partial backup of all changes since last*
#                             COMPLETE backup                                *
#                -l no_days   Do a partial backup of files that changed in   *
#                             the last ``no_days'' days.                     *
#                -a           Append to end of exiting tape                  *
#                                                                            *
#  returns:      0 if all is well                                            *
#                non-zero error status                                       *
#                                                                            *
#  caveats:      Writes over /etc/.last*                                     *
#                depends on finfo                                            *
#                order of backup depends on output of dfspace(1)             *
#                Crazy filenames can cause problems                          *
#                First filename to be backed-up is the files list.           *
#                Tape block size is 64kb.                                    *
#                                                                            *
#*****************************************************************************/

#ident  "$Header: /usr/sbin/RCS/dat_bu,v 1.8 1997/01/29 18:36:23 ShimonR Exp $"

ask=false
append_tape=false
valid_options="F:mVSaqfevs:d:p:d:zl:"
force_it=false
bad_option=false
pre_erase=false
tape_dest=${BU_DEVICE:-/dev/nrst0}
tape_block=`expr 1024 '*' 64`
tmpfile=/tmp/dat_bu.${$}
root=${BU_FS:=/BackUp}
total_kilobytes=0
total_files=0
total_fs=0
shut_up=false
verbose=false
fs_list=${tmpfile}.fs_list
back_ground=false
do_partial=false
partial_type=nonsense
partial_flag=""
use_snapshots=true
mount_order=false
compress=false
fs_types=""
verbose_cpio=
last_days=0

trap 'do_cleanup; exit 2' 1 2 3 4 5

substr()
{
	local flag pat str
	local usage="usage: substr -lLrR pat string or substr string pat"

	case "$1" in
	-l | -L | -r | -R)
		flag="$1"
		pat="$2"
		shift 2
		;;
	-*)
		echo "substr: unknown option: $1"
		echo "$usage"
		return 1
		;;
	*)
		flag="-r"
		pat="$2"
		;;
	esac

	if [ "$#" -eq 0 -o "$#" -gt 2 ] ; then
		echo "substr: bad argument count"
		return 2
	fi

	str="$1"

	#
	# We don't want -f, but we don't want to turn it back on if
	# we didn't have it already
	#
	case "$-" in
	"*f*")
		;;
	*)
		fng=1
		set -f
		;;
	esac

	case "$flag" in
	-l)
		str="${str#$pat}"		# substr -l pat string
		;;
	-L)
		str="${str##$pat}"		# substr -L pat string
		;;
	-r)
		str="${str%$pat}"		# substr -r pat string
		;;
	-R)
		str="${str%%$pat}"		# substr -R pat string
		;;
	*)
		str="${str%$2}"			# substr string pat
		;;
	esac

	echo "$str"

	#
	# If we had file name generation when we started, re-enable it
	#
	if [ "$fng" = "1" ] ; then
		set +f
	fi
}

function do_cleanup
{
  trap '' 1 2 3 4 5

	echo "Terminated... Exiting..."
  rm -f ${tmpfile}*
  rm -f ${fs_list}*
  rm -f /etc/.lastbackup

  if [ ${first_backup:-x} = true ]
    then
      (mv /etc/OLD.lastbackup /etc/.lastbackup) > /dev/null 2>&1
  fi
      
  if [ ${first_partial:-x} = true ]
    then
      (mv /etc/OLD.lastpartial /etc/.lastbackup) > /dev/null 2>&1
 fi

  if [ ${do_partial} = false ]
   then
      mt -f ${tape_dest} rewind > /dev/null 2>&1
      mt -f ${tape_dest} offline > /dev/null 2>&1 &
  fi
}

function ask_bu
{
  fsn=${1}
  answer=invalid
  while  [ ${answer} = invalid ]
    do
      echo -e "Do you want to backup file system \"${fsn}\" (Y/N)? \c"
      read answer
      case ${answer} in
        y|Y)
          echo true > ${tmpfile}.ask
          ;;
        n|N)
          echo false > ${tmpfile}.ask
          ;;
        *)
          answer=invalid
          ;;
      esac
    done
}

function setup_list
{
  sul_output=${1}; shift

  > ${tmpfile}.mount

  mount | grep '^/dev/[ws]d' | cut -d ' ' -f 1,3 >> ${tmpfile}.mount
  mount | grep '^/dev/ccd' | cut -d ' ' -f 1,3 >> ${tmpfile}.mount
  while read fs_device fs
    do
      echo "${fs} ${fs_device}" >> ${sul_output}
			echo "${fs}" >> /tmp/FS-LIST
    done < ${tmpfile}.mount
}

if [ -f /proc/filesystems ]
  then
    valid_fs_types="`cat /proc/filesystems | cut -d '	' -f 2`"
  else
    valid_fs_types="ufs ffs nfs"
fi

# Parse the command line
while getopts ${valid_options} c
  do
    case ${c} in
      l)
        do_partial=true
        last_days=${OPTARG:-14}
        expr ${last_days} + 1 > /dev/null 2>&1
        if [ ${?} != 0 ]
          then
            echo "${0} ERROR: -l argument must be a decimal number!"
            exit 1
        fi
        ;;
      F)
        optarg_is_valid=false
        for fst in ${valid_fs_types}
          do
            if [ ${OPTARG} = ${fst} ]
              then
                optarg_is_valid=true
                fs_types="${fs_types} ${OPTARG}"
                break
            fi
          done

        if [ ${optarg_is_valid} = false ]
          then
            echo "\"${OPTARG}\" is not a filesystem supported by this kernel!"
            echo "Only \"${valid_fs_types}\" are supported."
            exit 1
        fi
        ;;
      z)
        compress=true
        ;;
      m )
        mount_order=true
        ;;
      V )
        ask=true
        ;;
      p )
        do_partial=true;
        partial_type=${OPTARG}
        if [ ${partial_type:=x} = 'p' -o ${partial_type} = 'P' ]
          then
            partial_type=partial
          else
            if [ ${partial_type} = 'c' -o ${partial_type} = 'C' ]
              then
                partial_type=complete
              else
                echo "Partial backup (-p) can be type c (since last Complete)"
                echo "                             or p (since last Partial)"
                exit 1
            fi
        fi
        ;;
      b )
        back_ground=true
        ;;
      q )
        shut_up=true
        ;;
      f )
        force_it=true
        ;;
      e )
        pre_erase=false
        ;;
      d )
        tape_dest=${OPTARG}
        ;;
      v )
        verbose=true
        verbose_cpio=v
        ;;
      a )
        append_tape=true
        ;;
      \? )
        bad_option=true
        ;;
    esac
  done

if [ ${bad_option} = true ]
  then
    echo "${0} ERROR:  Correct Usage: ${0} [-options]"
    echo
    echo "Options: -V     Verify (ask) intent for each file system"
		echo "         -m     Backup in mount order (default = alphabetically)"
    echo "         -a     Append to end of data (default=start snew)"
    echo "         -q     Be quite, unless errors (default=moderately verbose"
    echo "         -f     Force continuation (default=terminate on I/O errors)"
    echo "         -e     Pre-erase the tape (default=do not pre-erase)"
    echo "         -v     Be verbose about your progress"
    echo "         -d d_d Use device d_d as destination (default=${tape_dest})"
    echo "         -p p_t Partial Backup (default=Complete Backup);"
    echo "                If p_t=p then backup all since last Partial backup"
    echo "                If p_t=c then backup all since last Complete backup"
    echo "         -z     Compress via gzip"
    exit 1
fi

if [ ${fs_types:-x} = x ]
  then
    fs_types="ufs ffs ext2 nfs"
    echo "No file system types specified.  Using \"${fs_types}\"."
fi


if [ ${do_partial} = true ]
  then
    backup_type=Partial
  else
    backup_type=Complete
fi

if [ -f /etc/.lastbackup ]
  then
    first_backup=false

    if [ ${do_partial} = true ]
      then
        partial_flag="-newer /etc/.OLD.lastbackup"
    fi

    rm -f /etc/.OLD.lastbackup
    mv /etc/.lastbackup /etc/.OLD.lastbackup
  else
    first_backup=true
    if [ ${do_partial} = true ]
      then
        echo "Cannot do a partial backup.  No complete backup done before"
        exit 2
      fi
fi

if [ -f /etc/.lastpartial ]
  then
    first_partial=false
    rm -f /etc/.OLD.lastpartial
    mv /etc/.lastpartial /etc/.OLD.lastpartial 

    if [ ${partial_type} = partial ]
      then
        partial_flag="-newer /etc/.OLD.lastpartial"
    fi

  else
    first_partial=true
fi

if [ ${last_days} != 0 ]
  then
    partial_flag="-ctime ${last_days}"
fi

date > /etc/.lastbackup

if [ ${shut_up} = false ]
  then
    echo "${backup_type} system Backup Started on `date`"
fi

rm -f /tmp/FS-LIST
setup_list ${fs_list} ${fs_types}

fslist=`cat /tmp/FS-LIST`
rm -f /tmp/FS-LIST
for fs in ${fslist}
  do
    if [ ${ask} = true ]
      then
        ask_bu ${fs}
        case `cat ${tmpfile}.ask` in
          true)
            doit="Backed-Up"
            ;;
          false)
            doit=Skipped
            ;;
          *)
            doit="Invalid-Response"
        esac
      else
        doit=Backed-Up
    fi

    echo "${fs} ${doit}" >> /tmp/FS-LIST
  done

# Prepare tape drive
if [ ${back_ground} = true ]
  then
    echo
    echo "Install ${backup_type} Backup tape in drive & strike ENTER \c"
    read yes
fi

# Turn DAT compression ON
#mt -f ${tape_dest} datcompression 1 ; Linux way.
#mt -f ${tape_dest} density 19 # If you figure this out from the man page...


if [ ${append_tape} = true ]
  then
    mt -f ${tape_dest} eom

    if [ ${?} != 0 ]
      then
        echo "${0} ERROR:  Failed to advance DAT tape in drive ${tape_dest}"
        kill -5 ${$}
      else
        echo
    fi
  else
    if [ ${do_partial} = false ]
      then
        mt -f ${tape_dest} rewind
    fi
fi

if [ ${pre_erase} = true ]
  then
    if [ ${verbose}  = true ]
      then
        echo "  Pre-Erasing tape..."
    fi
    mt -f ${tape_dest} erase > /dev/null 2>&1
fi

# Backup the File System List
(cd /tmp;echo FS-LIST | cpio -H newc -o${verbose_cpio} -C 65536 -O ${tape_dest})

echo "0 0 0" > ${tmpfile}.stats

# Backup the file systems one at a time
while read fs fs_device # From ${fs_list}...
  do
    # Do we really want to back it up?
    if [ ${ask} = true ]
      then
        doit="`grep ${fs} /tmp/FS-LIST`"
        doit=`echo ${doit} | awk '{ print $2 }'`
        case ${doit} in
          Backed-Up)
            ;;
          *)
            continue
            ;;
        esac
    fi

    # We have some special cases
    case ${fs} in
      /var/tmp )
        ;;                        # We do not want a backup of this one
      /tmp )
        ;;                        # We do not want a backup of this one
      * )
        total_fs=`expr ${total_fs} + 1`

        if [ ${fs} = "/" ]
          then
            fs_name=/root
          else
            fs_name=${fs}
        fi

        if [ ${verbose} = true ]
          then
            echo "  Building Backup List for the File System \"${fs_name}\"..."
        fi

        # Walk down the file system, do not crss mountpoints, do not follow
        # Symbolic Links
        ( cd /;
           fs_path=`substr -L / ${fs}`
           find ${fs_path:-.} -depth \
                              -xdev \
                              ${partial_flag} \
                              -fstype ufs \
                              -exec ls -ds {} \; | \
           grep -v 'var/spool/news' \
                   > ${tmpfile}.raw
        )

        echo "${tmpfile}.list" > ${tmpfile}.list
        cat ${tmpfile}.raw | awk '{ print $2 }' >> ${tmpfile}.list

        # Compute sizes
        kilobytes=`cat ${tmpfile}.raw | awk '{ print $1 }' | addint`
        kilobytes=`expr ${kilobytes} / 2`
        files=`wc -l ${tmpfile}.raw`;files=`echo ${files} | cut -d ' ' -f 1`

        if [ ${files} -eq 0 ]
          then
            if [ ${verbose} = true ]
              then
                echo "Skipping backup of \"${fs_name}\".  Nothing to backup"
            fi
          else

            if [ ${verbose} = true ]
              then
                if [ ${files} -gt 999 ]
                  then
                    kf=`expr ${files} / 1000`;mf=`expr ${files} % 1000`
                    mf=`printf "%03d" ${mf}`;tf="${kf},${mf}"
                  else
                    tf=${files}
                fi

                mb=`expr ${kilobytes} / 1024`;kb=`expr ${kilobytes} % 1024`
                kb=`expr ${kb}00 / 1024`
                kb=`printf "%03d" ${kb}`;tb="${mb}.${kb} megabytes"
          
                echo "  Backup of \"${fs_name}\" will save"
                echo "  ${tf} files spanning ${tb}"
            fi

            total_files=`expr ${total_files} + ${files}`
            total_kilobytes=`expr ${total_kilobytes} + ${kilobytes}`
            ( cd /;
              if [ ${compress} = true ]
                then
                  cat ${tmpfile}.list | \
                  cpio -H newc -o${verbose_cpio} -C ${tape_block} | 
                  gzip -c --fast | dd ibs=32k of=${tape_dest} obs=1024k
                else
                  cat ${tmpfile}.list | \
                  cpio -H newc -o${verbose_cpio} -C ${tape_block} -O ${tape_dest
}
              fi
            )

            if [ ${?} -ne 0 ]
              then
                echo "Backup of \"${fs_name}\" Failed."
                if [ ${force_it} = false ]
                  then
                    kill -5 ${$}
                fi
            fi
        fi
        ;;
      esac
      echo "${total_kilobytes} ${total_files} ${total_fs}" > ${tmpfile}.stats
  done < ${fs_list}  # End of while--do...

# On complete backups, rewind & eject the tape
if [ ${do_partial} = false ]
  then
    mt -f ${tape_dest} rewind > /dev/null 2>&1
    mt -f ${tape_dest} offline > /dev/null 2>&1 &
fi

# Process overall statistics
read total_kilobytes total_files total_fs < ${tmpfile}.stats
rm -f ${tmpfile}*

if [ ${total_files} -gt 999 ]
  then
    kf=`expr ${total_files} / 1000`;mf=`expr ${total_files} % 1000`
    mf=`printf "%03d" ${mf}`;tf="${kf},${mf}"
  else
    tf=${total_files}
fi

mb=`expr ${total_kilobytes} / 1024`;kb=`expr ${total_kilobytes} % 1024`
kb=`expr ${kb}00 / 1024`
kb=`printf "%03d" ${kb}`;tb="${mb}.${kb} megabytes"
          
if [ ${shut_up} = false ]
  then
    echo "${backup_type} System Backup: ${tb} in"
    echo "                        ${tf} files on"
    echo "                        ${total_fs} file systems"
    echo "completed on `date`"
fi

exit 0


--_=XFMail.1.2-alpha.p0.FreeBSD:970825111934:1078=_--
End of MIME message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?XFMail.970825112355.Shimon>