Article 244 of unix-pc.sources: Path: tut.cis.ohio-state.edu!osu-cis!killer!lll-winken!lll-lcc!lll-tis!pacbell!rtech!gonzo!daveb From: daveb@gonzo.UUCP (Dave Brower) Newsgroups: unix-pc.general,unix-pc.sources Subject: recovering UNIX PC backups - fixcpio, ff10, fread10, fwrite10 Summary: Repost of old stuff, including interesting floppy utils. Keywords: disaster protection Message-ID: <451@gonzo.UUCP> Date: 5 Nov 88 18:27:34 GMT References: <121@bsadrc.UUCP> Reply-To: daveb@gonzo.UUCP (Dave Brower) Distribution: usa Organization: Gonzo Media Group Lines: 771 Xref: tut.cis.ohio-state.edu unix-pc.general:2357 unix-pc.sources:244 In article <121@bsadrc.UUCP> usenet@bsadrc.UUCP (Darrel R. Carver) writes: >Now I know what backup is putting on each volume so if I had to scrap >a floppy from a multi volume backup I would know what I was missing. >Next question, how could I do this? cpio does not seem to have a option that >would allow you to skip a volume. Does afio or has a version of cpio >been hacked together that could? Any pointers would be appreciated. fixcpio, attached, is a filter you put between between the floppy reader and cpio that identifies/deletes archive members that would make cpio choke. It requires that you wrote them with the -c option (which almost everyone does nowadays), and that you can read multiple floppies in the input. I'm also enclosing the fread/write scripts I use, originally by fluke!corey, from long ago. The *9 scripts are mostly of historical interest, since everyone should be using 10 sectors as of 3.0. Fixcpio has been distributed before, and this is unchanged. It has already help many people recover from disaster. I highly recommend you put it and the fread10 script on an easily identificable floppy, so you can find it when disaster does strike. I am really fond of the ff* family, because I only want to hit return _once_ after I feed the damn thing a disk. Cheers, -dB #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # ff10 # ff9 # fixcpio.1 # fixcpio.c # fread10 # fread9 # fwrite10 # fwrite9 # This archive created: Sat Nov 5 10:13:33 1988 export PATH; PATH=/bin:$PATH if test -f 'ff10' then echo shar: will not over-write existing file "'ff10'" else sed 's/^X//' << \SHAR_EOF > 'ff10' X: X# Interactive 10 sector floppy formatter. X# Loops while you feed in disks. X# X# Dave Brower, rtech!gonzo!daveb X Xtrap 'echo Quitting...; dismount -f; exit' 2 X Xwhile : Xdo X echo "Press return to format 10 sector floppy, q to quit--->\c" X read junk X case $junk in X q*|Q*) break;; X *) echo 'Formatting floppy in /dev/rfp020';; X esac X X if iv -i /dev/rfp020 /usr/lib/iv/FD10nl X then X echo 'Making file system on /dev/rfp021...' 1>&2 X mkfs /dev/rfp021 > /dev/null X dismount -f 1>&2 X else X echo "Cannot format floppy disk.\nMaybe write protected or inserted in the drive wrong.\n\nCheck and press Return to continue-->\c" X fi Xdone SHAR_EOF chmod +x 'ff10' fi # end of overwriting check if test -f 'ff9' then echo shar: will not over-write existing file "'ff9'" else sed 's/^X//' << \SHAR_EOF > 'ff9' X# X# PURPOSE: Format a Floppy disk using the iv function X# REQUIRED FILES: /etc/iv, /usr/lib/ua/FD files X# X# Modified version of /usr/bin/FFloppy.sh for cmdline use. X XTERMCAP= Xexport TERMCAP X Xtrap 'echo Quitting...; dismount -f; exit' 2 X Xwhile : Xdo X echo "Press return to format 9 sector floppy, q to quit--->\c" X read junk X case $junk in X q*|Q*) break;; X *) echo 'Formatting floppy in /dev/rfp020';; X esac X X iv -i /dev/rfp020 /usr/lib/iv/FDnl X if [ "$?" != "0" ] X then X echo "Cannot format floppy disk. Possible reasons include:\n- Disk has silver write-protect tab on it (remove tab);\n- Disk is not inserted or is inserted improperly;\n- Latch is not closed or disk is bad.\nPlease check floppy and press return-->\c" X else X echo 'Making file system on /dev/rfp021...' 1>&2 X mkfs /dev/rfp021 > /dev/null X df -t /dev/fp021 X dismount -f 1>&2 X fi Xdone SHAR_EOF chmod +x 'ff9' fi # end of overwriting check if test -f 'fixcpio.1' then echo shar: will not over-write existing file "'fixcpio.1'" else sed 's/^X//' << \SHAR_EOF > 'fixcpio.1' X.\" $Header: fixcpio.1,v 1.1 87/01/05 19:51:00 source Exp $ X.TH FIXCPIO 1 "UNIX-PC" "Public Domain" "David Brower" X.ta 8n 16n 24n 32n 40n 48n 56n X.SH NAME Xfixcpio \- repair damaged cpio -c archives X.SH ORIGIN XDavid Brower, {gladys, sun, amdahl, mtxinu}!rtech!daveb, daveb@rtech.com X.SH SYNOPSIS X.B fixcpio X[ infile [ outfile ] ] X.SH DESCRIPTION X.I Fixcpio XReads the standard input (or the named \fIinfile\fP) and writes a cpio X-c archive to the standard output (or named \fIoutfile\fP). X.I Infile Xand X.I outfile Xmay be the dash character (`\-') to signify standard in or out. X.PP XThe input is presumed to be a \fIcpio -c\fP archive. While the input Xis copied to the output, X.I fixcpio Xchecks each archive member for sanity, and discards those that appear Xto be bad. The program writes the names of archive members copied on Xstderr, and says X.nf X X Skipping bad member ``filename'' X X.fi Xfor each bad record. This eliminates the cheerful ``Out of phase--get help'' Xmessage from cpio. X.PP XThe major use for X.I fixcpio Xis in recovering multiple floppy backups when one disk in the set goes Xbad. The process for the UNIX-PC is about as follows. X X.PP X1. Get images of the remaining floppies in files that are in alphabetical Xorder: X.nf X X # works with up to 99 disk backup sets. X # X # if, ibs, and count will depend on your machine and X # backup procedure. X disk=01 X while : X do X echo "Interrupt to quit, return to read disk $disk \ec:" X read answer X dd if=/dev/rfp/021 ibs=1024 count=320 of=disk-$disk X dismount -f X disk=`awk "{ printf \e"%02d\en\e", $disk + 1 }" ` X done X X.fi X.PP X2. Restore the contents of the disks with X.I fixcpio's Xhelp. X.nf X X cat disk-* | fixcpio | cpio -icdum X X.fi X.SH FILES X.br X/tmp Holds a temp file containing the archive member currently being examined. X.SH BUGS X.I Fixcpio Xdoes not understand binary cpio archives. X.PP XGetting disk images from the floppies depends on both the machine and X your backup procedures. You need to know how the floppies are X written before you start recovering, and this might be awkward if X you've lost your hard disk. X.PP XUsing a temp file is a kludge, needed because you can't seek around on Xinput from a pipe. X.PP XStatus messages should probably be toggled with a -v `verbose' flag. SHAR_EOF fi # end of overwriting check if test -f 'fixcpio.c' then echo shar: will not over-write existing file "'fixcpio.c'" else sed 's/^X//' << \SHAR_EOF > 'fixcpio.c' X X/* X** fixcpio.c -- fix troubled cpio archive by skipping trashed members. X** X** Dave Brower, 12/13/86 X** {sun, amdahl, mtxinu}!rtech!daveb X** X** Usage: fixcpio [ infile [ outfile ] ] X** X** Writes a cpio -c archive to outfile (or stdout) from the infile (stdin). X** ("-" may be used as the stdin/stdout filename.) X** X** Skips over junk members. This is how to recover when you've lost X** floppy 9 of a 30 disk backup. Eliminates "Out of phase -- get help" X*/ X X# include X X/* size blocks to write */ X X# define BLKSIZ 512 X X/* Maximum reasonable pathname in a header record */ X X# define MAXPATH 128 X Xtypedef struct X{ X /* these are ints for scanf's benefit. */ X int h_magic, X h_dev, X h_ino, X h_mode, X h_uid, X h_gid, X h_nlink, X h_rdev; X long h_longtime; X int h_namesize; X long h_longfile; X} CHARHDR; X Xtypedef struct X{ X CHARHDR h; X char h_name[ MAXPATH ]; X} CHARREC; X XCHARREC CRec = { 0 }; /* Character header */ Xchar Trailer[] = "TRAILER!!!"; /* Magic string */ Xchar Tmpfile[] = "/tmp/fixcpioXXXXXX"; /* temp file template */ Xint Debug; /* Debugging? */ X Xvoid outerr(); /* error writing output file */ Xvoid tmperr(); /* error writing temp file */ Xvoid writeerr(); /* error writing file */ Xint fprintf(); /* libc defined, -1 on error */ X X/* X** main() -- fix a cpio archive with "Out of phase -- get help" problems. X*/ Xmain(argc, argv) Xint argc; Xchar **argv; X{ X register int last; /* last char processed */ X register int this; /* current chars */ X register int nmagic; /* "07"s in magic "070707" seen */ X X register FILE *ifp = stdin; /* input stream */ X register FILE *ofp = stdout; /* output stream */ X register FILE *tfp = NULL; /* temp file */ X X int done = 0; /* all done flag */ X long nbytes = 0; /* count of bytes written */ X X char buf[ 512 ]; /* holds a trailer. */ X X char *getenv(); /* libc defined */ X FILE *efopen(); /* fopen, fatal on error */ X FILE *getmember(); /* stash a member in a temp file */ X long putmember(); /* write temp file */ X X /* Set "secret" debugging flag */ X Debug = getenv("FIXCPIO") != NULL; X X if( argc > 3 ) X { X fprintf(stderr, "Usage: fixcpio [ infile [ outfile ] ]\n"); X return( 1 ); X } X X if( --argc > 0 && strcmp( *++argv, "-" ) ) X ifp = efopen( *argv, "r" ); X X if( --argc > 0 && strcmp( *++argv, "-" ) ) X ofp = efopen( *argv, "w" ); X X /* X ** Process chars of input. When you see a magic number, try X ** to accumulate the archive member on a temp file. Write out X ** good members as they are validated, skipping trouble makers. X */ X for ( nmagic = last = this = 0 ; !done ; last = this ) X { X switch( this = getc( ifp ) ) X { X case '0': X X /* maybe a header, no special action */ X break; X X case '7': X X /* Maintain count of special "07" pairs */ X nmagic = last == '0' ? nmagic + 1 : 0; X X /* It's a magic number, try to process as a header */ X if( nmagic == 3 ) X { X nmagic = 0; X X /* stashed entry is good, write it */ X if( tfp ) X nbytes += putmember( tfp, ofp ); X X /* stash this possible entry into tfp, get CRec */ X tfp = getmember( ifp ); X } X break; X X case EOF: X X done = 1; X /* Fall into... */ X X default: X X /* Any existing entry is garbage... */ X nmagic = 0; X if( tfp ) X { X if( !strcmp( CRec.h_name, Trailer ) ) X done = 1; X else X fprintf(stderr, "Skipping bad member \"%s\"\n", X CRec.h_name ); X (void)fclose( tfp ); X tfp = NULL; X } X break; X } /* switch */ X } /* for */ X X /* flush pending good member */ X if( tfp ) X { X nbytes += putmember( tfp, ofp ); X tfp = NULL; X } X X /* Write a trailer -- remember to terminate the name string! */ X (void)sprintf( buf, "070707%06o%06o%06o%06o%06o%06o%06o%011o%06o%011o%s", X 0, 0, 0, 0, 0, 0, 0, 0, sizeof(Trailer) + 1, 0, Trailer ); X if( fprintf(ofp, "%s", buf) < 0 || putc( 0, ofp ) < 0 ) X outerr(); X nbytes += strlen( buf ) + 1; X X /* round output to an even block */ X nbytes = BLKSIZ - (nbytes % BLKSIZ); X while( nbytes-- ) X if( putc( 0, ofp ) < 0 ) X outerr(); X X if( fclose( ofp ) < 0 ) X outerr(); X return( 0 ); X} X X/* X** getmember() -- save an archive member to a temp file X** X** When positioned after the magic number in a cpio file on ifp, X** copy the member to a temp file, and return it's fp. The temp file X** contains a complete member (including magic number) and is positioned X** for catting directly to the real output file. X** X** If there are problems getting the member, return NULL. X*/ XFILE * Xgetmember( ifp ) Xregister FILE *ifp; X{ X register int c; /* character of the member name */ X register int nr; /* number read or scanned */ X register FILE *ofp; /* temp file */ X long len; /* actual member length */ X X char name[ sizeof(Tmpfile) + 1 ]; /* name of the temp file */ X X /* number of chars to read for a -c header */ X# define NCREAD ( (8 * 6) + (2 * 11) ) X X char buf[ NCREAD + 1 ]; /* raw header */ X X char *mktemp(); /* libc, make temp file name */ X char *strcpy(); /* libc, copy string */ X long ncat(); /* cat file to a length */ X X if( NCREAD != ( nr = fread( buf, 1, NCREAD, ifp ) ) ) X { X fprintf(stderr, "Couldn't read header: Wanted %d, got %d\n", X NCREAD, nr); X return (NULL); X } X X if( Debug ) X { X fprintf(stderr, X "dev |ino |mode |uid |gid |nlink|rdev |longtime |nsize|longfile\n" ); X fprintf(stderr, "%s\n", buf ); X X } X X if( 10 != ( nr = sscanf( buf, "%6o%6o%6o%6o%6o%6o%6o%11o%6o%11o", X &CRec.h.h_dev, &CRec.h.h_ino, &CRec.h.h_mode, X &CRec.h.h_uid, &CRec.h.h_gid, &CRec.h.h_nlink, X &CRec.h.h_rdev, &CRec.h.h_longtime, X &CRec.h.h_namesize , &CRec.h.h_longfile ) ) ) X { X fprintf(stderr, "Couldn't scan header: Wanted 10, got %d\n", nr); X return (NULL); X } X X if( Debug ) X { X fprintf(stderr, "dev 0%o ino 0%o mode 0%o uid %d gid %d\n", X CRec.h.h_dev, CRec.h.h_ino, CRec.h.h_mode, X CRec.h.h_uid, CRec.h.h_gid ); X fprintf(stderr, X "nlink %d rdev 0%o longtime 0%o namesize %d longfile 0%o\n", X CRec.h.h_nlink, CRec.h.h_rdev, CRec.h.h_longtime, X CRec.h.h_namesize, CRec.h.h_longfile ); X } X X /* Ridiculous name size? probably trashed entry */ X if( !CRec.h.h_namesize || CRec.h.h_namesize > sizeof( CRec.h_name ) ) X { X fprintf(stderr, "Bad namesize %d\n", CRec.h.h_namesize ); X return (NULL); X } X X /* Get the name */ X nr = 0; X while( nr < CRec.h.h_namesize && ( c = getc( ifp ) ) != EOF ) X CRec.h_name[ nr++ ] = c; X X if( c == EOF ) X { X fprintf(stderr, "Unexpected EOF reading name in header\n"); X return (NULL); X } X X if( Debug ) X fprintf(stderr, "name \"%s\"\n", CRec.h_name ); X X /* create a new temp file, and mark it for delete on close */ X (void)strcpy( name, mktemp( Tmpfile ) ); X ofp = efopen( name, "w+" ); X (void)unlink( name ); X X /* Write a header */ X fprintf( ofp, "070707%06o%06o%06o%06o%06o%06o%06o%011o%06o%011o", X CRec.h.h_dev, CRec.h.h_ino, CRec.h.h_mode, X CRec.h.h_uid, CRec.h.h_gid, CRec.h.h_nlink, X CRec.h.h_rdev, CRec.h.h_longtime, X CRec.h.h_namesize, CRec.h.h_longfile ) ; X X for( nr = 0; nr < CRec.h.h_namesize ; ) X putc( CRec.h_name[ nr++ ], ofp ); X X /* now copy the file body */ X if( CRec.h.h_longfile != (len = ncat( CRec.h.h_longfile, ifp, ofp ) ) ) X { X fprintf(stderr, "Bad member length: Should be %ld, was %ld\n", X CRec.h.h_longfile, len ); X (void)fclose( ofp ); X return( NULL ); X } X X if( fseek( ofp, 0L, 0 ) < 0L ) X tmperr(); X return( ofp ); X X} X X/* X** putmember() -- Write member, close input and return bytes written X*/ Xlong Xputmember( ifp, ofp ) Xregister FILE * ifp; Xregister FILE * ofp; X{ X register long n; X long cat(); X X fprintf(stderr, "%s\n", CRec.h_name ); X n = cat( ifp, ofp ); X (void)fclose( ifp ); X return ( n ); X} X X X X/* X** cat() -- copy one stream to another, returning n bytes copied X*/ Xlong Xcat( ifp, ofp ) Xregister FILE *ifp; Xregister FILE *ofp; X{ X register int c; X register int n; X X for( n = 0 ; ( c = getc( ifp ) ) != EOF ; n++ ) X if( putc( c, ofp ) < 0 ) X outerr(); X X return ( n ); X} X X/* X** ncat() -- copy up to n bytes from one stream to another, return actual X*/ Xlong Xncat( in, ifp, ofp ) Xregister long in; Xregister FILE *ifp; Xregister FILE *ofp; X{ X register int c; X register long on; X X for( on = 0; in-- && ( c = getc( ifp ) ) != EOF ; on++ ) X if( putc( c, ofp ) < 0 ) X tmperr(); X X return ( on ); X} X X/* X** efopen() -- fopen() that fatals on error X*/ XFILE * Xefopen( file, mode ) Xchar *file; Xchar *mode; X{ X FILE * fp; X X if( NULL == (fp = fopen( file, mode ) ) ) X { X fprintf(stderr, "Can't open \"%s\" mode \"%s\"\n", file, mode ); X perror("efopen"); X exit( 1 ); X } X return( fp ); X} X X/* X** outerr() -- handle error writing output file X*/ Xvoid Xouterr() X{ X writeerr( "output" ); X} X X/* X** tmperr() -- handle error writing temp file X*/ Xvoid Xtmperr() X{ X writeerr( "temp" ); X} X X/* X** writeerr() -- handle write errors, gracelessly. X*/ Xvoid Xwriteerr( what ) Xchar *what; X{ X fprintf(stderr, "\007Error writing %s file", what ); X perror(""); X X} X X/* end of fixcpio.c */ SHAR_EOF fi # end of overwriting check if test -f 'fread10' then echo shar: will not over-write existing file "'fread10'" else sed 's/^X//' << \SHAR_EOF > 'fread10' X: X# Read as many 10 sector Unix-PC floppy disks to standard output as are X# needed to satisfy the program reading them. (such as tar or cpio.) X# X# Sample usage: fread10 | uncompress | tar xvf - X# fread 10| uncompress | cpio -icdumv X# X# The program stops reading when the pipeline is broken, presumably because X# tar or cpio decided it found the end of the archive. X# X# This reads 99 4k blocks, or 788 512 byte blocks, for 401408 bytes/disk. X# X# Author: Corey Satten, fluke!corey Aug. 1985 X# This software is hereby officially introduced into the public domain. X# X# Modified for 10 sector by Dave Brower, rtech!gonzo!daveb X# Xtrap "/etc/dismount -f 2>/dev/null 1>&2; exit 0" 0 Xtrap "/etc/dismount -f 1>&2; exit 1" 1 2 13 15 X Xdiskno=1 Xcontinue=: Xwhile $continue Xdo X echo "To EXIT -- press followed by ." 1>&2 X echo "To read insert floppy #$diskno and press : \c" 1>&2 X choice=`head -1 /dev/null || continue=false X /etc/dismount -f 1>&2 X diskno=`expr $diskno + 1` Xdone SHAR_EOF chmod +x 'fread10' fi # end of overwriting check if test -f 'fread9' then echo shar: will not over-write existing file "'fread9'" else sed 's/^X//' << \SHAR_EOF > 'fread9' X: ' X: Read as many Unix-PC floppy disks to standard output as are needed X: to satisfy the program reading them. (such as tar or cpio.) X: X: Sample usage: fread | uncompress | tar xvf - X: fread | uncompress | cpio -icdumv X: X: Author: Corey Satten, fluke!corey Aug. 1985 X: This software is hereby officially introduced into the public domain. X: ' Xtrap "/etc/dismount -f 2>/dev/null 1>&2; exit 0" 0 Xtrap "/etc/dismount -f 1>&2; exit 1" 1 2 13 15 Xdiskno=1 Xcontinue=: Xwhile $continue Xdo X echo "To EXIT -- press followed by ." 1>&2 X echo "To read insert disk #$diskno and press the " 1>&2 X choice=`head -1 /dev/null || continue=false X /etc/dismount -f 1>&2 X diskno=`expr $diskno + 1` Xdone SHAR_EOF chmod +x 'fread9' fi # end of overwriting check if test -f 'fwrite10' then echo shar: will not over-write existing file "'fwrite10'" else sed 's/^X//' << \SHAR_EOF > 'fwrite10' X: X# Copy standard input onto as many Unix-PC floppy disks as needed, X# buffering appropriately, and prompting the user to change as necessary. X# X# Sample usage: tar cvf - . | compress | fwrite X# find . -print | cpio -oc | compress | fwrite X# X# Author: Corey Satten, fluke!corey Aug. 1985 X# This software is hereby officially introduced into the public domain. X# X# Modified for 10 sector by Dave Brower, rtech!gonzo!daveb X Xtrap "/etc/dismount -f 1>&2; exit 1" 1 2 13 15 Xdiskno=1 Xwhile : Xdo X echo "To EXIT -- press followed by ." 1>&2 X echo "To continue - insert floppy disk #$diskno and press the key." 1>&2 X choice=`head -1 &1` X if [ "$1" = "788+0" ] X then X /etc/dismount -f 1>&2 X diskno=`expr $diskno + 1` X else X /etc/dismount -f 2>/dev/null 1>&2 X break; X fi Xdone Xexit 0 SHAR_EOF chmod +x 'fwrite10' fi # end of overwriting check if test -f 'fwrite9' then echo shar: will not over-write existing file "'fwrite9'" else sed 's/^X//' << \SHAR_EOF > 'fwrite9' X: ' X: Copy standard input onto as many Unix-PC floppy disks as needed, X: buffering appropriately, and prompting the user to change as necessary. X: X: Sample usage: tar cvf - . | compress | fwrite X: find . -print | cpio -oc | compress | fwrite X: X: Author: Corey Satten, fluke!corey Aug. 1985 X: This software is hereby officially introduced into the public domain. X: ' X Xtrap "/etc/dismount -f 1>&2; exit 1" 1 2 13 15 Xdiskno=1 Xgo=true Xwhile $go ;do X set `dd of=/dev/rfp021 obs=4096 count=632 2>&1` X if test $1 = 632+0 ;then X /etc/dismount -f 1>&2 X diskno=`expr $diskno + 1` X echo "To EXIT -- press followed by ." 1>&2 X echo "To continue - insert floppy disk #$diskno and press the key." 1>&2 X choice=`head -1 /dev/null 1>&2 X go=false X fi Xdone Xexit 0 SHAR_EOF chmod +x 'fwrite9' fi # end of overwriting check # End of shell archive exit 0