#!/bin/ksh # #ident "@(#)flarcreate.sh 1.15 02/01/17 SMI" # # Copyright (c) 2000-2001 by Sun Microsystems, Inc. # All rights reserved. # ######################################## # # # flarcreate -- Create a Flash Archive # # # ######################################## TEXTDOMAIN=SUNW_INSTALL_FLASH export TEXTDOMAIN # list of required utilities. If you add any to this # list, be sure to also add it to the $UTIL_LIST variable below! AWK="/usr/bin/awk" SED="/usr/bin/sed" DF="/usr/sbin/df" RM="/usr/bin/rm" CPIO="/usr/bin/cpio" COMPRESS="/usr/bin/compress" WC="/usr/bin/wc" CAT="/usr/bin/cat" UNAME="/usr/bin/uname" FIND="/usr/bin/find" TOUCH="/usr/bin/touch" MT="/usr/bin/mt" DD="/usr/bin/dd" ID="/usr/bin/id" LS="/usr/bin/ls" UTIL_LIST="$AWK $SED $DF $RM $CPIO $COMPRESS $WC $CAT $UNAME $FIND $TOUCH $MT $DD $ID $LS" ############################################################## # Function find_utils makes sure required utilies exist. ############################################################## find_utils_or_exit () { for i in $UTIL_LIST ; do if [ ! -x $i ] ; then print_error_and_exit "$(gettext "Required utility %s is not available.")" $i fi done } ############################################################## # Function print_error_and_exit prints a formatted message # to stderr prefixed with ERROR: and new-lined, and exits 1. ############################################################## print_error_and_exit () { typeset format=$1 shift typeset values=$* printf "$(gettext 'ERROR:') ${format}\n" $values >&2 exit 1 } ############################################################## # Function print_message prints a formatted message to stderr ############################################################## print_message () { typeset format=$1 shift typeset values=$* printf "${format}\n" $values >&2 } ############################################################### # Function print_usage_and_exit does just that ############################################################### print_usage_and_exit () { # Remember to update the usage in flar if you update this one typeset myname=`basename $0` print_message "$(gettext "Usage:")" print_message "$myname $(gettext '-n name [-R root] [-H] [-S] [-c] [-t [-p posn] [-b blocksize]]')" print_message " $(gettext '[-i date] [-u section [-d path ]] [-U key=value] [-m master]')" print_message " $(gettext '[-f [ list_file | - ] [-F]]')" print_message " $(gettext '[-a author] [-e descr | -E descr_file] [-T type] [-x exclude]')" print_message " $(gettext 'archive')" exit 1 } ############################################################## # Function print_error_and_usage prints a formatted message to stderr # with ERROR: and calls print_usage_and_exit ############################################################## print_error_and_usage () { typeset format=$1 shift typeset values=$* printf "$(gettext 'ERROR:') ${format}\n" $values >&2 print_usage_and_exit } ############################################################## # Functions dial(), start_dial(), stop_dial(), and cleanup() # implement a spinner of |, /. -, and \. dial() runs in the # background. Use of traps is critical to avoid spinning after # user has Control-C'd. ############################################################## dial() { typeset state=0 trap - EXIT INT # Set trap to ignore while : ; do case $state in 0) echo "|\b\c" # top-bottom state=1 ;; 1) echo "/\b\c" # upper right, lower left state=2 ;; 2) echo "-\b\c" # left-right state=3 ;; 3) echo "\\" "\b\b\c" # upper left, lower right state=0 ;; esac sleep 1 done } start_dial() { [[ $QUIET = $YES ]] && return dial & # Start in background DIALPID=$! # Get pid, for killing trap "cleanup 1" EXIT INT # Set trap } stop_dial() { [[ $QUIET = $YES ]] && return trap "" EXIT INT kill $DIALPID >/dev/null 2>&1 DIALPID= echo " \b\c" } cleanup() { typeset exitcode=$1 if [[ ! -z "$DIALPID" ]] ; then kill $DIALPID fi if [[ -x $hash_file ]] ; then ${RM} -f $hash_file fi exit $exitcode } ##################################################################### # Function get_platforms makes a string for the content_architectures # identification line. It prints the string, so the caller should # get the string as $(get_platforms). ##################################################################### get_platforms () { typeset file="var/sadm/system/admin/.platform" typeset entry= typeset line= typeset values= # if there is no $root_directory, then the following argument # to grep will be relative to /. Also, we aren't interested # in any error output from grep, all we care is that it failed, # so ignore stderr. values=$(grep "^PLATFORM_GROUP" $root_directory/$file 2> /dev/null) if [[ $? -ne 0 ]]; then print $(${UNAME} -m) return fi for entry in $values ; do [[ ! -z $line ]] && line="${line}," line="${line}${entry#PLATFORM_GROUP=}" done print $line } ############################################## # Function checks whether $2 is dirname of $1 ############################################## in_or_under() { typeset head="$1" typeset dir="$2" head=$(echo $head |${SED} 's:/*$::') expr X$dir : "^X${head}/" >/dev/null 2>&1 if [[ $? != 0 && "$dir" != "$head" ]] ; then return 1 else return 0 fi } ########################################## # Function makes root directory relative ########################################## make_root_relative() { typeset file="$1" file=${file#${root_directory}} if [[ -z "$file" ]] ; then echo . elif expr "X$file" : "X/" >/dev/null 2>&1 ; then echo ".${file}" else echo "./${file}" fi } ################################################# # Functions test if fstype parameter is ufs or # vxfs, which are neccesarily local ################################################# is_local_fstype() { typeset fstype="$1" if [[ $fstype = "ufs" || $fstype = "vxfs" || $fstype = "pcfs" ]] ; then return 0 else return 1 fi } on_local_fs() { typeset file="$1" is_local_fstype $(${DF} -n $file |${AWK} '{print $3;}') } ############################################# # Function find_interesting_filesystems ############################################# # # Description: Find the filesystems that contain files to be included in the # archive. The following rules govern filesystem selection: # 1. If $root_directory is set (isn't /), the filesystem must # be mounted under $root_directory. # 2. The filesystem must be neither the exclude directory # (if any), nor under it. # 3. The filesystem must not be under the control of vold # 4. The filesystem must be local. We assume only filesystems # of type `ufs' or `vxfs' are local. # # This function sets the global array int_fs and the global # variable int_fs_num. The latter holds the number of entries # in the former. # # It also adds semi-interesting mount points to semiint_fs # (and updates the corresponding counter semiint_fs_num). # Semi-interesting mount points are mount points of non-local # filesystems. find_interesting_filesystems() { typeset source= typeset mountpt= typeset fstype= typeset junk= typeset vold_mount= int_fs_num=0 # If an alternate root has been specified that is not the root of # a filesystem, the top of the filesystem tree headded by the # alternate root won't be included. We need to find the name of # the filesystem containing the alternate root so we can explicitly # make sure said filesystem is saved as interesting. if [[ $root_directory != "/" ]] ; then root_parent=$(${DF} -n $root_directory |${AWK} '{print $1;}') [[ $DEBUG -ge 1 ]] && echo "root_parent $root_parent" fi # If an alternate root has been specified, we need to find the # filesystem that contains the alternate root. This filesystem is # interesting, and would not have been caught if the alternate root # wasn't the root # Look for the mountpoint owned by vold (if any) while read source mountpt fstype junk ; do if expr $source : \ "^`${UNAME} -n`:vold(pid[0-9]*)$" >/dev/null 2>&1 ; then vold_mount="$mountpt" break fi done /dev/null 2>&1 ; then [[ $DEBUG -ge 1 ]] && echo "excluding vol mounted $mountpt" continue fi fi # Skip the vold mount point too if [[ -n "$vold_mount" && $mountpt = $vold_mount ]] ; then [[ $DEBUG -ge 1 ]] && echo "excluding vold $mountpt" continue fi # Local filesystems only. Since we don't know the filesystem # types for all remote filesystems, we'll punt and include only # filesystem types that we know are local is_local_fstype $fstype if [[ $? != 0 ]] ; then # This is a semi-interesting mount point. It's not # a separate local filesystem that we want to put in # the archive. Unfortunately, find was designed such # that the following won't pick up mount points: # find . -print -mount # That means we won't get mountpoints for non-local # filesystems. Here we save filesystems that would # have been printed if find did print mount points. save_semiinteresting_mount_point $mountpt [[ $DEBUG -ge 1 ]] && echo "excluding non-local $mountpt" continue fi set -A int_fs ${int_fs[*]} $mountpt int_fs_num=$((int_fs_num + 1)) [[ $DEBUG -ge 1 ]] && echo accepted $mountpt $int_fs_num done /dev/null | \ compress -c | ${WC} -c |${AWK} '{print $1}') else archive_size=$(gen_filenames |${CPIO} -oc 2>/dev/null | \ ${WC} -c |${AWK} '{print $1}') fi status=$? cd $current_dir stop_dial # Clean up if [[ $status != 0 || -z "$archive_size" || $archive_size -eq 0 ]]; then print_error_and_exit "$(gettext "Unable to find size of intended archive.")" fi # Print the size if [[ ${#archive_size} -gt 9 ]] ; then unit="GB" div=1073741824 elif [[ ${#archive_size} -gt 6 ]] ; then unit="MB" div=1048576 else unit="KB" div=1024 fi intnum=$(echo $archive_size / $div |bc) fracnum=$(echo $archive_size / \( $div / 100 \) % 100 |bc) print_message "$(gettext "The archive will be approximately %d.%02d%s.")" $intnum $fracnum $unit } ################################################################ # Function gen_ident_and_cookie outputs the cookie and the # identification lines in the output archive. Also outputs any # user-specified control sections. ################################################################ gen_ident_and_cookie() { typeset keyword typeset section typeset bad_start typeset cookie="FlAsH-aRcHiVe-1.1" typeset ident_begin="section_begin=identification" typeset hash_key="archive_id=" typeset hash="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" hash_length=${#hash} hash_offset=`expr ${#cookie} + 1 + ${#ident_begin} + 1 + ${#hash_key}` hash_file=/tmp/.flarcreate.hash.$$ ${CAT} <<-EOF $cookie $ident_begin EOF if [[ $generate_hash = $YES ]] ; then if [ -z "${COMPUTEHASH}" ] ; then print_message "$(gettext "WARNING: computehash not found; cannot generate checksums")" generate_hash=$NO else ${COMPUTEHASH} -n if [ $? != 0 ] ; then print_message "$(gettext "WARNING: Hash generation only supported on Solaris 8 and later")" generate_hash=$NO else echo "${hash_key}${hash}" fi fi fi if [[ $root_directory = "/" ]]; then creation_node=`uname -n` creation_hardware_class=`uname -m` creation_platform=`uname -i` creation_processor=`uname -p` creation_release=`uname -r` creation_os_name=`uname -s` creation_os_version=`uname -v` else creation_node=`cat ${root_directory}/etc/nodename` creation_hardware_class="UNKNOWN" creation_platform="UNKNOWN" creation_processor="UNKNOWN" creation_os_version="UNKNOWN" . ${root_directory}/var/sadm/system/admin/INST_RELEASE case $OS in Solaris ) creation_os_name=SunOS case $VERSION in 2.* ) creation_release=5.${$VERSION#*.} ;; 7 ) creation_release=5.7 ;; 8 ) creation_release=5.8 ;; 9 ) creation_release=5.9 ;; esac ;; * ) creation_os_name=UNKNOWN creation_release=UNKNOWN ;; esac fi ${CAT} <<-EOF files_archived_method=cpio creation_date=$creation_date creation_master=$creation_master content_name=$content_name creation_node=$creation_node creation_hardware_class=$creation_hardware_class creation_platform=$creation_platform creation_processor=$creation_processor creation_release=$creation_release creation_os_name=$creation_os_name creation_os_version=$creation_os_version EOF if [[ $compress_action = $YES ]]; then echo "files_compressed_method=compress" else echo "files_compressed_method=none" fi if [[ $sizing_action = $YES ]]; then echo "files_archived_size=$archive_size" fi #### TODO : files_unarchived_size if [[ ! -z $content_description ]]; then echo "content_description=$content_description" fi if [[ ! -z $content_type ]]; then echo "content_type=$content_type" fi if [[ ! -z $content_author ]]; then echo "content_author=$content_author" fi echo "content_architectures=$(get_platforms)" for keyword in $new_keywords; do if [[ $keyword = "${keyword#X-}" ]]; then print_message "$(gettext "Added keyword %s does not begin with %s.")" $keyword "X-" fi echo $keyword done echo "section_end=identification" # Write user-specified sections, if any for section in $section_list; do echo "section_begin=$section" ${CAT} $use_directory/$section echo "section_end=$section" done } ##################################################### # Function gen_archive files performs pipeline of # cpio and perhaps compress, and perhaps computehash. ##################################################### gen_archive_files() { echo "section_begin=archive" typeset gen_cmd="gen_filenames |${CPIO} -oc" if [[ $generate_hash = $YES ]] ; then gen_cmd="$gen_cmd | ${COMPUTEHASH} -f $hash_file" fi if [[ $compress_action = $YES ]] ; then gen_cmd="$gen_cmd | compress -c" fi eval ${gen_cmd} if [[ $? -eq 0 ]]; then return 0 else return 1 fi } ####################################################### # Function gen_archive performs the high-level control # of flarcreate. ####################################################### gen_archive() { cd $root_directory gen_ident_and_cookie gen_archive_files status=$? cd $current_dir if [[ $status != 0 ]] ; then print_message "$(gettext "Unable to write archive file.")" return $status fi } ####################################################### # Function determine_filesystems determines which filesystems # will be searched when generating filenames of files to be # included in the archive. This function builds a # rather large "find" command line that, when executed, # produces the filename list of files that should be included # in the archive. ####################################################### determine_filesystems() { print_message "$(gettext "Determining which filesystems will be included in the archive...")" find_interesting_filesystems if [[ $int_fs_num = 0 ]] ; then print_error_and_exit "$(gettext "Could not find any local filesystems to include.")" fi # Build the pipeline for each filesystem num=0 while [[ $num -lt $int_fs_num ]] ; do # The common beginning stuff PIPE="${FIND}" # If we're on an alternate root, and this is the filesystem that heads # the alternate root, we need to start the find at the alternate root # itself if [[ $root_directory != "/" && $root_parent = ${int_fs[$num]} ]] ; then PIPE="$PIPE ." else PIPE="$PIPE $(make_root_relative ${int_fs[$num]})" fi # More common stuff - exclude files we can't cpio PIPE="$PIPE ! -type D ! -type s" # Exclude the archive file if we're not using tape and the archive # file is on this filesystem if [[ $tape_usage = $NO ]] && on_local_fs $archive_outfile ; then if file_in_fs ${int_fs[$num]} $archive_outfile ; then PIPE="$PIPE ! -inum $archive_inode" fi fi # If we're using an exclude path and it's on this filesystem, # exclude it if [[ ! -z $exclude_path ]] ; then if file_in_fs ${int_fs[$num]} $exclude_path ; then PIPE="$PIPE ( -inum $exclude_inode " PIPE="$PIPE -prune -o -print )" else PIPE="$PIPE -print" fi else PIPE="$PIPE -print" fi # The common trailing stuff PIPE="$PIPE ! -mount -prune" # Save the pipeline eval PIPE_$num=\""$PIPE"\" [[ $DEBUG -ge 1 ]] && echo "Filesystem: ${int_fs[$num]}, Command: $PIPE" num=$(($num + 1)) done } ####################################################### # Function determine_filelist constructs a rather # large command that, when executed, produces # a list of files to be included in the archive, # *based on the file list given on the command line* ####################################################### determine_filelist() { # Build the pipeline for the file list # If file list is not coming from stdin, then we must cat it first if [[ $file_list != "-" ]] ; then LIST_PIPE="${CAT} $file_list | " fi # collapse multiple /'s into a single / LIST_PIPE="$LIST_PIPE ${SED} -n -e 's:[/]\{1,\}:/:g'" # For relative filenames, append the current root # directory. LIST_PIPE="$LIST_PIPE -e 's:^[^/]:'${root_directory}'/&:'" # again, collapse multiple /'s into a single / LIST_PIPE="$LIST_PIPE -e 's:[/]\{1,\}:/:g'" # exclude any files lying within the exclude directory (if any) if [[ ! -z $exclude_path ]] ; then LIST_PIPE="$LIST_PIPE -e '\!^'${exclude_path}'! d'" fi # now we replace any root directory prefix with './' so that # the files are stored with a relative path, and can be # extracted properly to any alternate root in the future. if [[ $root_directory = "/" ]] ; then # if root directory is /, we accept all files # and just prepend a '.'. LIST_PIPE="$LIST_PIPE -e 's:^/:./:p'" else # only print those files who lie within the alternate # root. if no root directory prefix is found, discard the # line. Replace any alternate root prefix with './' LIST_PIPE="$LIST_PIPE -e 's:^'${root_directory}'/:./:p'" fi [[ $DEBUG -ge 1 ]] && echo "File list command: $LIST_PIPE" } ##################################### # Check that user is root # (/usr/sbin/patchadd validate_uid()) ##################################### typeset -i uid uid=$(${ID} | ${SED} 's/uid=\([0-9]*\)(.*/\1/') if (( uid != 0 )) then print_error_and_exit "$(gettext "You must be root to execute this script.")" fi ######################################################### # Miscellaneous intializations and beginning-of-job steps ######################################################### current_dir=$(pwd) NO="No" YES="Yes" # make sure required binaries exist find_utils_or_exit if [[ $# -lt 3 ]]; then print_usage_and_exit fi #################################################### # Initialize defaults and parse the invoking command #################################################### content_author= compress_action=$NO creation_date=$(date -u '+''%Y''%m''%d''%H''%M''%S') content_description_path= use_directory=$PWD creation_master=$(${UNAME} -n) content_name= tape_position= tape_blocksize= file_list= exclusive_file_list=$NO tape_block_default="64k" sizing_action=$YES generate_hash=$YES hash_offset=0 hash_length=0 hash_file= section_list= new_keywords= tape_usage=$NO exclude_path= content_description= root_directory="/" content_type= archive_outfile= getopt_error=$NO DEBUG=0 QUIET=$NO COMPUTEHASH= while getopts ":a:b:cd:e:E:f:FHi:m:n:p:qR:StT:u:U:vx:" opt; do case $opt in a ) content_author=$OPTARG ;; b ) tape_blocksize=$OPTARG ;; c ) compress_action=$YES ;; d ) use_directory=$OPTARG ;; e ) content_description=$OPTARG ;; E ) content_description_path=$OPTARG ;; f ) file_list=$OPTARG ;; F ) exclusive_file_list=$YES ;; i ) creation_date=$OPTARG ;; m ) creation_master=$OPTARG ;; n ) content_name=$OPTARG ;; H ) generate_hash=$NO ;; p ) tape_position=$OPTARG ;; q ) QUIET=$YES ;; # PRIVATE R ) root_directory=$OPTARG ;; S ) sizing_action=$NO ;; t ) tape_usage=$YES ;; T ) content_type=$OPTARG ;; u ) section_list="$section_list $OPTARG" ;; U ) new_keywords="$new_keywords $OPTARG" ;; v ) DEBUG=1 ;; # PRIVATE x ) exclude_path=$OPTARG ;; \? ) print_error_and_usage "$(gettext "Option -%s is invalid.")" $OPTARG ;; \: ) print_error_and_usage "$(gettext "Option -%s has no value.")" $OPTARG ;; * ) print usage and exit ;; esac done # find a valid computehash if [ -x /usr/sbin/computehash ] ; then COMPUTEHASH=/usr/sbin/computehash elif [ -x `dirname $0`/computehash ] ; then _pwd=$PWD cd `dirname $0` COMPUTEHASH=`pwd`/computehash cd $_pwd else COMPUTEHASH= fi # Isolate outfile parameter - it should be the only argument left shift $(($OPTIND - 1)) if [[ $# != 1 ]] ; then print_usage_and_exit fi archive_outfile=$1 ############################################### # Make format, dependency, and existence checks ############################################### if [[ ! -d $root_directory ]] ; then print_error_and_usage "$(gettext "Directory %s does not exist or is not a directory.")" $root_directory fi if [[ $root_directory = ${root_directory#/} ]]; then print_error_and_usage "$(gettext "Root directory must have an absolute path.")" fi on_local_fs $root_directory if [[ $? != 0 ]] ; then print_error_and_exit "$(gettext "Root directory must be on a ufs or vxfs file system.")" fi if [[ -z $content_name ]]; then print_error_and_usage "$(gettext 'Content name (option -n) must have a value.')" fi if [[ ${#creation_date} -ne 14 ]]; then print_error_and_usage "$(gettext "Creation date %s must be 14 digits long.")" $creation_date fi if [[ $creation_date != +([0-9]) ]]; then print_error_and_usage "$(gettext "Creation date %s must be numeric.")" $creation_date fi if [[ ! -z $content_description_path ]]; then if [[ ! -z $content_description ]]; then print_error_and_usage "$(gettext "Options -E and -e are mutually exclusive.")" fi if [[ ! -r $content_description_path ]]; then print_error_and_exit "$(gettext "Content description file %s is not available.")" $content_description_path fi content_description=$(< $content_description_path) fi if [[ $use_directory != $PWD && -z $section_list ]]; then print_error_and_usage "$(gettext "Option -d is invalid in the absence of option -u.")" fi for section in $section_list; do if [[ ! -r $use_directory/$section ]]; then print_error_and_exit "$(gettext "User section %s is not available.")" $use_directory/$section fi done if [[ ! -z $tape_position ]]; then if [[ $tape_usage = $NO ]]; then print_error_and_usage "$(gettext "Option -p is invalid in the absence of option -t.")" else # $tape_usage = $YES if [[ $tape_position != +([0-9]) ]]; then print_error_and_exit "$(gettext "Tape position %s must be a number.")" $tape_position fi fi fi if [[ ! -z $tape_blocksize && $tape_usage = $NO ]]; then print_error_and_usage "$(gettext "Option -b is invalid in the absence of option -t.")" fi if [[ $tape_usage = $YES ]]; then tape_blocksize=${tape_blocksize:-$tape_block_default} if [[ $tape_blocksize != +([0-9])?(@(k|b|w)) ]]; then print_error_and_exit "$(gettext "Tape blocksize %s must be number[k|b|w].")" $tape_blocksize fi if [[ $generate_hash = $YES ]]; then print_message "$(gettext 'WARNING: hash generation disabled when using tape (-t)')" generate_hash=$NO fi fi if [[ ! -z $exclude_path ]]; then if [[ ! -z "$root_directory" ]] ; then # make exclude dir relative to alternate root exclude_path=$root_directory/${exclude_path#/} fi if [[ ! -d $exclude_path ]]; then print_error_and_exit "$(gettext "Exclusion path %s is not a directory.")" $exclude_path else # Make relative exclude path absolute. If we have an exclude # path AND an alternate root, this will never happen, as the # resulting exclude path will always be absolute after prepending # the alternate root. if [[ $exclude_path = ${exclude_path#/} ]]; then exclude_path=$(pwd)/$exclude_path fi fi # collapse multiple /'s into a single / exclude_path=`echo $exclude_path | tr -s /` [[ $DEBUG -ge 1 ]] && echo "Using exclude path: $exclude_path" exclude_inode=$(${LS} -id $exclude_path |${AWK} '{print $1}') fi if [[ -z $archive_outfile ]]; then if [[ $tape_usage = $NO ]]; then print_error_and_usage "$(gettext "Path name for new archive must be provided.")" else print_error_and_usage "$(gettext "Tape drive name for new archive must be provided.")" fi fi if [[ $tape_usage = $NO ]]; then if [[ $archive_outfile = ${archive_outfile#/} ]]; then archive_outfile=$(pwd)/$archive_outfile fi if [[ -e $archive_outfile ]]; then ${RM} -f $archive_outfile fi ${TOUCH} $archive_outfile if [[ ! -w $archive_outfile ]]; then print_error_and_exit "$(gettext "Archive file %s is not writable.")" $archive_outfile fi read archive_inode junk <<-EOF $(${LS} -i $archive_outfile) EOF fi # append current directory if filelist is relative if [[ -n "$file_list" ]] ; then if [[ $file_list = ${file_list#/} ]]; then file_list=$(pwd)/$file_list fi fi if [[ $exclusive_file_list = $YES && -z "$file_list" ]] ; then print_error_and_usage "$(gettext "Option -F invalid in the absense of option -f")" fi # we can't size the archive when doing a streamed file list, since # we can only read the file list once. if [[ "$file_list" = "-" && $sizing_action = $YES ]] ; then print_message "$(gettext "WARNING: Sizing not supported when using streamed file list")" sizing_action=$NO fi ############################################## # Determine what filesystems to archive, and # build the archive pipelines ############################################## start_dial if [ $exclusive_file_list != $YES ] ; then determine_filesystems fi if [ -n "{file_list}" ] ; then determine_filelist fi stop_dial ########################################## # If asked, find out size of archive-to-be ########################################## if [[ $sizing_action = $YES ]]; then size_archive fi ###################################### # Position magnetic tape, if requested ###################################### if [[ $tape_usage = $YES && ! -z $tape_position ]]; then print_message "$(gettext "Positioning tape drive...")" start_dial ${MT} -f $archive_outfile asf $tape_position if [[ $? -gt 0 ]]; then print_error_and_exit "$(gettext "Unable to move tape %s to position %d.")" $archive_outfile $tape_position fi stop_dial fi ################### # Write the archive ################### print_message "$(gettext "Creating the archive...")" start_dial # Use output redirection for disk, dd for tape if [[ $tape_usage = $NO ]]; then gen_archive > $archive_outfile else gen_archive | ${DD} of=$archive_outfile obs=$tape_blocksize 2>/dev/null fi status=$? # add hash, if we computed one. lets hope the hash goes near the # beginning of the file or the seek will take forever with a blocksize of 1. if [[ $generate_hash = $YES ]] ; then if [ -r $hash_file -a -f $hash_file ] ; then ${DD} if=$hash_file of=$archive_outfile bs=1 count=$hash_length \ seek=$hash_offset conv=notrunc >/dev/null 2>&1 ${RM} -f $hash_file else print_error_and_exit "$(gettext "Could not generate hash for %s")" $archive_outfile fi fi stop_dial if [[ $status != 0 ]] ; then print_error_and_exit "$(gettext "Unable to write archive.")" fi ######################################### # Perform end-of-job processing, and exit ######################################### print_message "$(gettext "Archive creation complete.")" exit 0