#!/bin/sh # # NAME: cdfs # # Revision: 1.0 # # Modification history: # 000 banta 10/1/90 # creation # (Bill Petro) # # Copyright (c) 1988-91, Sun Microsystems, Inc. All Rights Reserved. # Sun considers its source code as an unpublished, proprietary # trade secret, and it is available only under strict license # provisions. This copyright notice is placed here only to protect # Sun in the event the source is deemed a published work. Dissassembly, # decompilation, or other means of reducing the object code to human # readable form is prohibited by the license agreement under which # this code is provided to the user or company in possession of this # copy. # # RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the # government is subject to restrictions as set forth in subparagraph # (c)(1)(ii) of the Rights in Technical Data and Computer Software # clause at DFARS 252.227-7013 (October 1988) and FAR 52.227-19 # (June 1987). # # DISCLAIMER: # # This script is not supported and is provided to you AS IS for use # at your own risk. NEITHER SUN MICROSYSTEMS, INC. NOR SUNSOFT, INC. # MAKE ANY EXPRESS OR IMPLIED WARRANTIES INCLUDING, BUT NOT LIMITED TO, # THE WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. # NEITHER SUN MICROSYSTEMS, INC. NOR SUNSOFT, INC. SHALL BE LIABLE FOR # ANY DAMAGES INCLUDING, BUT NOT LIMITED TO, CONSEQUENTIAL DAMAGES. # # DESCRIPTION: # # This script will take a clean directory structure, and create a # UFS filesystem image suitable for transfer to a CD-ROM. This script # expects either no arguments or 3 or 4 arguments. If no arguments # are present, all options will be prompted. If arguments are present, # the first argument is the path of the root of a directory structure # to be transferred to CD-ROM; the second is a character device node # of a disk partition to be used to making the filesystem. The third # is the destination of the filesystem image. An optional fourth # argument is a blocking factor for creating the image, if the image is # put on tape. # # USAGE: # cdfs [ source partition image [blocking] ] # # source - root of the source directory structure # rpart - character device node of disk partition # image - name of the target image file for this operation # blocking factor - block size for transfer to image, used if # image destination is a tape # # You must be root to run this script. # TRUE=0 FALSE=1 OK=0 FAIL=1 PATH=/usr/bin:/usr/ucb:/bin:/etc tmpmnt=/tmp/cdm$$ trap "cleanup $FAIL" 0 1 2 # # function: cleanup() # # arguments: condition to exit with # # clean up when we're done and exit with ok or failure condition # i.e. umount stuff, rm temp files and directories # cleanup () { umount $tmpmnt 2> /dev/null rmdir $tmpmnt 2> /dev/null # rm -f /tmp/cdfs*$$ exit $1 } # # function: getyn() # # arguments: prompt for question # # prompt for a y/n response, return TRUE for a 'y' answer, FALSE for # 'n' # getyn() { pline="$* (y or n):" while echo -n $pline do read ans case $ans in "y" | "Y") return $TRUE ;; "n" | "N") return $FALSE ;; *) echo "Please enter Y or N." ;; esac done } # # function: prompt() # # arguments: prompt for question # # Prompt for response to a question. Response is returned in $cmd # If response is "q", a failure result is returned. In all other # cases, true is returned. # -x sets -x for the shell for debugging purposes # +x turns it off # prompt () { pline="$* or q to quit: " while echo -n $pline do read cmd case $cmd in "q") return $FALSE ;; "") echo "Please enter a response." continue ;; "-x") set -x; continue;; "+x") set +x; continue;; *) return $TRUE ;; esac done } # # function: mountfs() # # arguments: partition to mount # # Create a mount point and mount the specified partition on on it. # Fails if the mount point can't be created or if the mount is # unsuccessful # mountfs () { _dpart=$1 # create a place to mount the filesystem [ -d $tmpmnt ] || { mkdir $tmpmnt 2>/dev/null || { echo " $progname: Couldn't create mount point for filesystem. " return $FAIL } } # mount that puppy mount $_dpart $tmpmnt || { echo " $progname: Unable to mount $_dpart on $tmpmnt " return $FAIL } # determine the amount of usable space on the partition avail_sz=`df|grep $_dpart|awk '{print $4}'` return $OK } # # function: getsize # # arguments: directory structure to snag size of # # cd to the specified directory and do a du to get the size # Fails if we cannot cd to the directory # getsize () { _curdir=`pwd` # make sure we can really cd to the src directory. We only check once, though cd $1 2> /dev/null [ `pwd` = "$1" ] || { echo " $progname: couldn't cd to $1 " cd $_curdir return $FAIL } # get the size of the directory srcdir_sz=`du -s . | awk '{print $1}'` cd $_curdir return $OK } # # function: makefs # # arguments: none # # make a filesystem on the specified partition. failure when we can't # dig up disk stats or making the fs fails horribly # makefs () { _curdir=`pwd` # count the number of files. This is close enough to an inode count for us cd $srcdir find . -print | sort > /tmp/cdfs1$$ cd $_curdir inodecnt=`wc -l /tmp/cdfs1$$ | awk '{print $1}'` # This is to make a squashed filesystem. We choose a number of sectors # that will hold the filesystem (increase it by 1/3 for UFS overhead) # and use that as the filesystem size. If you really want to use the # entire partition, comment out the following lines of script # down to the next comment below # There should really be no reason you need to do this sectcnt=`expr $srcdir_sz / 3` sectcnt=`expr $sectcnt + $srcdir_sz` [ $sectcnt -gt $dpart_sz ] && { echo" $progname: The partition $rdpart isn't large enough to hold your directory structure. Please choose a different disk partition." return $FAIL } dpart_sz=$sectcnt # comment to here sectcnt=`expr $dpart_sz \* 2` # get the number of bytes/inode needed bpi=`expr $dpart_sz \* 1024` bpi=`expr $bpi / $inodecnt` # adjust it just a little so we are never under bpi=`expr $bpi \* 8` bpi=`expr $bpi / 10` # pick a number around 200 for the number of cylinder groups, then adjust # it so cylinders are all about the same size cpg=`expr $numcyl / 200` cpg=`expr $cpg + 1` cpg=`expr $numcyl / $cpg` # make cpg a multiple of 16 cpg=`expr $cpg / 16` cpg=`expr $cpg \* 16` echo " Attempting to make the filesystem. " # # we try to make a filesystem with "ideal" parameters. Since the # filesytem may not work exactly as we hope, we can try dropping the number # cylinders per group. We can't increase bytes per inode, beacuse we're # trying to build the fewest number of inodes possible, anyway # #DEBUG echo Command used: newfs -c $cpg -d 0 -i $bpi -s $sectcnt $rdpart until newfs -c $cpg -d 0 -i $bpi -s $sectcnt $rdpart | grep -v "restricts cylinders" do cpg=`expr $cpg - 16` [ $cpg -le 0 ] && { echo " $progname: Can't make a suitable filesystem based on filesystem size and directory structure. You might try a different disk partition. " return $FAIL } #DEBUG echo Command used: newfs -c $cpg -d 0 -i $bpi -s $sectcnt $rdpart done echo " Filesystem made. " return $OK } # # function: fillfs # # arguments: none # # this copies the directory structure to the new filesystem # fillfs () { # lost+found is useless on a read-only fileystem rmdir $tmpmnt/lost+found # check the space available again # we made an attempt when we created the filesystem, but this is a # guaranteed check [ "$srcdir_sz" -ge "$avail_sz" ] && { echo " $progname: the filesystem you created isn't large enough to hold your directory structure. " return $FAIL } _curdir=`pwd` cd $srcdir # check for symbolic links. This would make the new filesystem look # ok on this system, but could cause problems when it's mounted elsewhere find . -type l -print > /tmp/linksfound linkcnt=`wc -l /tmp/linksfound | awk '{print $1}'` [ "$linkcnt" -gt 0 ] && { echo " $progname: Warning: $linkcnt symbolic links were found in your directory structure. Files may not be located the same place or may not exist on the target system. Make sure this is not a problem. A list of the linked files can be found in /tmp/linksfound. " } # fill the filesystem echo " Filling the new filesystem. This may take a while. " tar cf - . |(cd $tmpmnt; tar xf -) cd $_curdir } # # function: makeimage # # arguments: none # # Make an image of the filesystem we just created and filled. # Failure from this routine might be running out of space for your # image, but I can think of no good way to trap this condition. # makeimage () { dd if=$rdpart of=$image $obsize count=$sectcnt } # # function: testimage # # arguments: none # # check out the image we just made. This will fail if mounting the # filesystem fails or the files contained on the filesystem are # not the same as the source directory # testimage() { # make sure the partition isn't already mounted df|grep $dpart > /dev/null && { echo " $progname: The filesystem partition $dpart is currently mounted. Testing will not continue. " return $FAIL } # scrap the contents of the partition newfs $rdpart > /dev/null 2>&1 # if the image is on tape, give them a chance to get their media set [ -c "$image" ] && { echo "Ready to read your image." prompt "Make sure the image media is ready and type ok to continue" || { echo " $progname: Exiting at user request." cleanup $OK } } # copy the image back to the partition echo " Checking out your filesystem. This will take a while. " dd if=$image of=$rdpart $ibsize # try to mount the FS. Failure here is a bad sign, with no understandable # reason mountfs $dpart || { echo " $progname: Your image failed to mount successfully. Some part of the creation process failed. Please check your work and try again." return $FAIL } # compare file lists from the two directories. This should be a sufficient # check of integrity. _curdir=`pwd` [ -f /tmp/cdfs1$$ ] || { cd $srcdir find . -print | sort > /tmp/cdfs1$$ } cd $tmpmnt find . -print | sort > /tmp/cdfs2$$ cd $_curdir [ -f /tmp/cdfs1$$ -a -f /tmp/cdfs2$$ ] || { echo " $progname: Couldn't get a list of what is in the source directory or the new filesystem." return $FAIL } # compare the two lists. cmp /tmp/cdfs1$$ /tmp/cdfs2$$ || { echo " $progname: The contents of your source directory and the CD-ROM filesystem differ. Some part of the creation process failed. Please check your work and try again." return $FAIL } echo " Test of the filesystem image passed. The image is now ready to be put on CD-ROM" return $OK } # # function: usage # # tell the user how to use this script usage() { echo "Usage: $progname [ srcdir partition image [ image-blocksize ] ]" cleanup $OK } # # main() # progname=$0 iflag= ibf= #make sure we're root id|grep id=0\(root\) > /dev/null || { echo "$progname: You must be superuser to run this utility." cleanup $FAIL } # grab the arguments, checking happens below case $# in 0) iflag=yes ;; 1|2) usage ;; 3|4) srcdir=$1 rdpart=$2 image=$3 bsize=$4 ;; *) usage ;; esac # get the name of the source directory [ "$iflag" = "yes" ] && { prompt "Enter the name of the source directory structure" || cleanup $OK srcdir=$cmd } #get the size of the source directory getsize $srcdir || cleanup $FAIL # no need to make an empty FS [ $srcdir_sz = 0 ] && { echo " $progname: The source directory you specified is empty. Try again." cleanup $FAIL } dpflag=$iflag # get the device node to use while true do [ "$dpflag" = "yes" ] && { prompt "Enter the full path name of a character device node of a usable disk partition" || cleanup $OK rdpart=$cmd } # did they give us a character device node? [ -c "$rdpart" ] || { echo " $progname: $rdpart is not a character device node. " dpflag=yes continue } # grab a block device node based on what we know. rdbase=`basename $rdpart` dbase=`expr $rdbase : 'r\(.*\)'` dpart=`dirname $rdpart` dpart=$dpart/$dbase # make sure that block device node exists [ -b "$dpart" ] || { echo " $progname: Can't locate a block device node associated with $rdpart. $rdpart is not a disk partition or no block device node exists for the partition. Select another partition or make sure a block node exists for $rdpart. " dpflag=yes continue } # make sure that they didn't give us a currently mounted FS mount | grep $dpart > /dev/null && { echo " $progname: $dpart is currently mounted as a filesystem. It cannot be used for creating a CD-ROM filesystem. Please select a different partition or unmount $dpart and start again. " dpflag=yes continue } break done # while we have a good handle on the partition, get the number of sectors # and cylinders numsect=`dkinfo $dbase|tail -2|head -1|awk '{print $1}'` numcyl=`dkinfo $dbase|tail -2|head -1|awk '{print $3}'` numcyl=`expr $numcyl : '(\(.*\)'` dpart_sz=`expr "$numsect" / 2` # find out where to put the image dpflag=$iflag while true do [ "$dpflag" = "yes" ] && { prompt "Enter where to put the filesystem image" || cleanup $OK image=$cmd } # if this is a device, make sure we can write on it [ -c "$image" ] && { [ -w "$image" ] || { echo " $progname: You don't have write permission on the device $image. " dpflag="yes" continue } # get a block size for the device [ "$iflag" = "yes" ] && { echo -n "Enter the block size for transfer [default: 512] :" read $bsize } break } # see if we can create an image in a regular file touch $image 2> /dev/null [ -f "$image" ] || { echo " $progname: Can't create the filesystem image file $image. " dpflag="yes" continue } # there's a chance that image existed, so make sure we can write it [ -w "$image" ] || { echo " $progname: No permission to write on image file $image. " dpflag="yes" continue } break done # if the block size was set, make args to dd easy [ -n "$bsize" ] && { ibsize="ibs=$bsize" obsize="obs=$bsize" } partletter=`expr $dbase : '.*\(.\)'` case $partletter in a|b|c) echo " Be careful when using the a, b, or c partition of a disk drive. " ;; *) ;; esac # make sure they really know what they're doing and can trash the partition echo " The next step will create a new filesystem on $rdpart. This will destroy all data currently on this partition. " getyn "Is it ok to destroy the contents of $rdpart" || { echo " $progname: Exiting at user's request." cleanup $OK } # make a filesystem makefs $rdpart || { echo " $progname: Attempt to create filesystem on $rdpart failed. Please correct conditions listed above and try again." cleanup $FAIL } # mount the filesystem mountfs $dpart || { echo " $progname: Failed to mount $dpart. Filesystem may not be good." cleanup $FAIL } # fill the filesystem fillfs || { echo " $progname: Failed to transfer $srcdir to filesystem partition. Correct conditions above and try again." cleanup $FAIL } # unmount the filesystem until umount $tmpmnt 2> /dev/null do echo " $progname: Failed to umount the filesystem. Some other process may be working inside the directory /tmp/cdm$$.\n" prompt "Correct the situation and type ok to continue" || cleanup $OK done # make an image of the fs makeimage || { echo " $progname: A filesystem image was not successfully made. Please correct the problems listed above and try again." cleanup $FAIL } # see if they wanna test the image getyn "Do you want to test this image" || { echo " An image of the filesystem now exists in $image. Test this image before you try transferring it to CD-ROM. Putting an unusable filesystem image on CD-ROM is expensive and time consuming." cleanup $OK } # test the image testimage || { echo " Your image did not successfully check out. Correct problems and try again." cleanup $FAIL } cleanup $OK