Subject: v08i094: Repair damaged "cpio -c" archives Newsgroups: mod.sources Approved: mirror!rs Submitted by: rtech!daveb (Dave Brower) Mod.sources: Volume 8, Issue 94 Archive-name: fixcpio [ I wrote the Makefile. --r$ ] #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If all goes well, you will see the message "End of shell archive." # Contents: README Makefile fixcpio.1 fixcpio.c PATH=/bin:/usr/bin:/usr/ucb; export PATH echo shar: extracting "'README'" '(339 characters)' if test -f 'README' ; then echo shar: will not over-write existing file "'README'" else sed 's/^X//' >README <<'@//E*O*F README//' XTue Jan 27 12:22:37 PST 1987 X XThis is a program for fixing broken cpio -c archives. It is commonly Xused to recover a backup when disk 10 from a 30 disk archive has gotten Xtrashed. X XIt is often the "help" required by "Out of phase--get help." X XTo compile, either go: X X cc -O fixcpio.c -o fixcpio X Xor on System V say: X X make fixcpio X X-dB @//E*O*F README// if test 339 -ne "`wc -c <'README'`"; then echo shar: error transmitting "'README'" '(should have been 339 characters)' fi fi # end of overwriting check echo shar: extracting "'Makefile'" '(249 characters)' if test -f 'Makefile' ; then echo shar: will not over-write existing file "'Makefile'" else sed 's/^X//' >Makefile <<'@//E*O*F Makefile//' Xfixcpio: fixcpio.c X $(CC) $(CFLAGS) -o fixcpio fixcpio.c X X# Edit appropriately. XDESTDIR = /usr/local/bin XMANDIR = /usr/man/man1 XMANPAGE = fixcpio.1 Xinstall: fixcpio X cp fixcpio $(DESTDIR) X strip $(DESTDIR)/fixcpio X cp fixcpio.1 $(MANDIR)/$(MANPAGE) @//E*O*F Makefile// if test 249 -ne "`wc -c <'Makefile'`"; then echo shar: error transmitting "'Makefile'" '(should have been 249 characters)' fi fi # end of overwriting check echo shar: extracting "'fixcpio.1'" '(2243 characters)' if test -f 'fixcpio.1' ; then echo shar: will not over-write existing file "'fixcpio.1'" else sed 's/^X//' >fixcpio.1 <<'@//E*O*F 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 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. @//E*O*F fixcpio.1// if test 2243 -ne "`wc -c <'fixcpio.1'`"; then echo shar: error transmitting "'fixcpio.1'" '(should have been 2243 characters)' fi fi # end of overwriting check echo shar: extracting "'fixcpio.c'" '(9269 characters)' if test -f 'fixcpio.c' ; then echo shar: will not over-write existing file "'fixcpio.c'" else sed 's/^X//' >fixcpio.c <<'@//E*O*F 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 */ @//E*O*F fixcpio.c// if test 9269 -ne "`wc -c <'fixcpio.c'`"; then echo shar: error transmitting "'fixcpio.c'" '(should have been 9269 characters)' fi fi # end of overwriting check echo shar: "End of shell archive." exit 0