daveb@rtech.UUCP (Dave Brower) (12/25/86)
I wrote the enclosed program when I lost a middle floppy from 11 from a 23 disk cpio -c backup. It snips out archive members that have been corrupted, and can be used to work around the notorious "Out of Phase -- Get help" message. Because you are likely to be using this when recovering from disaster, it tries to be very good about detecting errors. I'll write up a man page and send this to mod.sources later. If you encounter problems or add anything interesting (like handling binary archives), send it to me asap. -dB -------------------------------- cut here -------------------------------- /* ** fixcpio.c -- fix troubled cpio archive by skipping trashed members. ** ** Dave Brower, 12/13/86 ** {sun, amdahl, mtxinu}!rtech!daveb ** ** Usage: fixcpio [ infile [ outfile ] ] ** ** Writes a cpio -c archive to outfile (or stdout) from the infile (stdin). ** ("-" may be used as the stdin/stdout filename.) ** ** Skips over junk members. This is how to recover when you've lost ** floppy 9 of a 30 disk backup. Eliminates "Out of phase -- get help" */ # include <stdio.h> /* size blocks to write */ # define BLKSIZ 512 /* Maximum reasonable pathname in a header record */ # define MAXPATH 128 typedef struct { /* these are ints for scanf's benefit. */ int h_magic, h_dev, h_ino, h_mode, h_uid, h_gid, h_nlink, h_rdev; long h_longtime; int h_namesize; long h_longfile; } CHARHDR; typedef struct { CHARHDR h; char h_name[ MAXPATH ]; } CHARREC; CHARREC CRec = { 0 }; /* Character header */ char Trailer[] = "TRAILER!!!"; /* Magic string */ char Tmpfile[] = "/tmp/fixcpioXXXXXX"; /* temp file template */ int Debug; /* Debugging? */ void outerr(); /* error writing output file */ void tmperr(); /* error writing temp file */ void writeerr(); /* error writing file */ int fprintf(); /* libc defined, -1 on error */ /* ** main() -- fix a cpio archive with "Out of phase -- get help" problems. */ main(argc, argv) int argc; char **argv; { register int last; /* last char processed */ register int this; /* current chars */ register int nmagic; /* "07"s in magic "070707" seen */ register FILE *ifp = stdin; /* input stream */ register FILE *ofp = stdout; /* output stream */ register FILE *tfp = NULL; /* temp file */ int done = 0; /* all done flag */ long nbytes = 0; /* count of bytes written */ char buf[ 512 ]; /* holds a trailer. */ char *getenv(); /* libc defined */ FILE *efopen(); /* fopen, fatal on error */ FILE *getmember(); /* stash a member in a temp file */ long putmember(); /* write temp file */ /* Set "secret" debugging flag */ Debug = getenv("FIXCPIO") != NULL; if( argc > 3 ) { fprintf(stderr, "Usage: fixcpio [ infile [ outfile ] ]\n"); return( 1 ); } if( --argc > 0 && strcmp( *++argv, "-" ) ) ifp = efopen( *argv, "r" ); if( --argc > 0 && strcmp( *++argv, "-" ) ) ofp = efopen( *argv, "w" ); /* ** Process chars of input. When you see a magic number, try ** to accumulate the archive member on a temp file. Write out ** good members as they are validated, skipping trouble makers. */ for ( nmagic = last = this = 0 ; !done ; last = this ) { switch( this = getc( ifp ) ) { case '0': /* maybe a header, no special action */ break; case '7': /* Maintain count of special "07" pairs */ nmagic = last == '0' ? nmagic + 1 : 0; /* It's a magic number, try to process as a header */ if( nmagic == 3 ) { nmagic = 0; /* stashed entry is good, write it */ if( tfp ) nbytes += putmember( tfp, ofp ); /* stash this possible entry into tfp, get CRec */ tfp = getmember( ifp ); } break; case EOF: done = 1; /* Fall into... */ default: /* Any existing entry is garbage... */ nmagic = 0; if( tfp ) { if( !strcmp( CRec.h_name, Trailer ) ) done = 1; else fprintf(stderr, "Skipping bad member \"%s\"\n", CRec.h_name ); (void)fclose( tfp ); tfp = NULL; } break; } /* switch */ } /* for */ /* flush pending good member */ if( tfp ) { nbytes += putmember( tfp, ofp ); tfp = NULL; } /* Write a trailer -- remember to terminate the name string! */ (void)sprintf( buf, "070707%06o%06o%06o%06o%06o%06o%06o%011o%06o%011o%s", 0, 0, 0, 0, 0, 0, 0, 0, sizeof(Trailer) + 1, 0, Trailer ); if( fprintf(ofp, "%s", buf) < 0 || putc( 0, ofp ) < 0 ) outerr(); nbytes += strlen( buf ) + 1; /* round output to an even block */ nbytes = BLKSIZ - (nbytes % BLKSIZ); while( nbytes-- ) if( putc( 0, ofp ) < 0 ) outerr(); if( fclose( ofp ) < 0 ) outerr(); return( 0 ); } /* ** getmember() -- save an archive member to a temp file ** ** When positioned after the magic number in a cpio file on ifp, ** copy the member to a temp file, and return it's fp. The temp file ** contains a complete member (including magic number) and is positioned ** for catting directly to the real output file. ** ** If there are problems getting the member, return NULL. */ FILE * getmember( ifp ) register FILE *ifp; { register int c; /* character of the member name */ register int nr; /* number read or scanned */ register FILE *ofp; /* temp file */ long len; /* actual member length */ char name[ sizeof(Tmpfile) + 1 ]; /* name of the temp file */ /* number of chars to read for a -c header */ # define NCREAD ( (8 * 6) + (2 * 11) ) char buf[ NCREAD + 1 ]; /* raw header */ char *mktemp(); /* libc, make temp file name */ char *strcpy(); /* libc, copy string */ long ncat(); /* cat file to a length */ if( NCREAD != ( nr = fread( buf, 1, NCREAD, ifp ) ) ) { fprintf(stderr, "Couldn't read header: Wanted %d, got %d\n", NCREAD, nr); return (NULL); } if( Debug ) { fprintf(stderr, "dev |ino |mode |uid |gid |nlink|rdev |longtime |nsize|longfile\n" ); fprintf(stderr, "%s\n", buf ); } if( 10 != ( nr = sscanf( buf, "%6o%6o%6o%6o%6o%6o%6o%11o%6o%11o", &CRec.h.h_dev, &CRec.h.h_ino, &CRec.h.h_mode, &CRec.h.h_uid, &CRec.h.h_gid, &CRec.h.h_nlink, &CRec.h.h_rdev, &CRec.h.h_longtime, &CRec.h.h_namesize , &CRec.h.h_longfile ) ) ) { fprintf(stderr, "Couldn't scan header: Wanted 10, got %d\n", nr); return (NULL); } if( Debug ) { fprintf(stderr, "dev 0%o ino 0%o mode 0%o uid %d gid %d\n", CRec.h.h_dev, CRec.h.h_ino, CRec.h.h_mode, CRec.h.h_uid, CRec.h.h_gid ); fprintf(stderr, "nlink %d rdev 0%o longtime 0%o namesize %d longfile 0%o\n", CRec.h.h_nlink, CRec.h.h_rdev, CRec.h.h_longtime, CRec.h.h_namesize, CRec.h.h_longfile ); } /* Ridiculous name size? probably trashed entry */ if( !CRec.h.h_namesize || CRec.h.h_namesize > sizeof( CRec.h_name ) ) { fprintf(stderr, "Bad namesize %d\n", CRec.h.h_namesize ); return (NULL); } /* Get the name */ nr = 0; while( nr < CRec.h.h_namesize && ( c = getc( ifp ) ) != EOF ) CRec.h_name[ nr++ ] = c; if( c == EOF ) { fprintf(stderr, "Unexpected EOF reading name in header\n"); return (NULL); } if( Debug ) fprintf(stderr, "name \"%s\"\n", CRec.h_name ); /* create a new temp file, and mark it for delete on close */ (void)strcpy( name, mktemp( Tmpfile ) ); ofp = efopen( name, "w+" ); (void)unlink( name ); /* Write a header */ fprintf( ofp, "070707%06o%06o%06o%06o%06o%06o%06o%011o%06o%011o", CRec.h.h_dev, CRec.h.h_ino, CRec.h.h_mode, CRec.h.h_uid, CRec.h.h_gid, CRec.h.h_nlink, CRec.h.h_rdev, CRec.h.h_longtime, CRec.h.h_namesize, CRec.h.h_longfile ) ; for( nr = 0; nr < CRec.h.h_namesize ; ) putc( CRec.h_name[ nr++ ], ofp ); /* now copy the file body */ if( CRec.h.h_longfile != (len = ncat( CRec.h.h_longfile, ifp, ofp ) ) ) { fprintf(stderr, "Bad member length: Should be %ld, was %ld\n", CRec.h.h_longfile, len ); (void)fclose( ofp ); return( NULL ); } if( fseek( ofp, 0L, 0 ) < 0L ) tmperr(); return( ofp ); } /* ** putmember() -- Write member, close input and return bytes written */ long putmember( ifp, ofp ) register FILE * ifp; register FILE * ofp; { register long n; long cat(); fprintf(stderr, "%s\n", CRec.h_name ); n = cat( ifp, ofp ); (void)fclose( ifp ); return ( n ); } /* ** cat() -- copy one stream to another, returning n bytes copied */ long cat( ifp, ofp ) register FILE *ifp; register FILE *ofp; { register int c; register int n; for( n = 0 ; ( c = getc( ifp ) ) != EOF ; n++ ) if( putc( c, ofp ) < 0 ) outerr(); return ( n ); } /* ** ncat() -- copy up to n bytes from one stream to another, return actual */ long ncat( in, ifp, ofp ) register long in; register FILE *ifp; register FILE *ofp; { register int c; register long on; for( on = 0; in-- && ( c = getc( ifp ) ) != EOF ; on++ ) if( putc( c, ofp ) < 0 ) tmperr(); return ( on ); } /* ** efopen() -- fopen() that fatals on error */ FILE * efopen( file, mode ) char *file; char *mode; { FILE * fp; if( NULL == (fp = fopen( file, mode ) ) ) { fprintf(stderr, "Can't open \"%s\" mode \"%s\"\n", file, mode ); perror("efopen"); exit( 1 ); } return( fp ); } /* ** outerr() -- handle error writing output file */ void outerr() { writeerr( "output" ); } /* ** tmperr() -- handle error writing temp file */ void tmperr() { writeerr( "temp" ); } /* ** writeerr() -- handle write errors, gracelessly. */ void writeerr( what ) char *what; { fprintf(stderr, "\007Error writing %s file", what ); perror(""); } /* end of fixcpio.c */ -- Kill Tree. Kill Turkey. Merry Xmas. {amdahl, sun, mtxinu, cbosgd}!rtech!daveb
daveb@rtech.UUCP (Dave Brower) (12/25/86)
In article <563@rtech.UUCP> I write: > I wrote the enclosed program when I lost a middle floppy from 11 from a 23 > disk cpio -c backup. It snips out archive members that have been corrupted, What I meant to say was: > I wrote the enclosed program when I lost floppy 11 from a 23 disk cpio > -c backup. It snips out archive members that have been corrupted, I also meant to include this dif, and mention to WATCH THE SIGNATURE. Sorry about that. *** fixcpio.c Thu Dec 25 11:49:54 1986 --- nfix Thu Dec 25 12:01:33 1986 *************** *** 396,402 { fprintf(stderr, "\007Error writing %s file", what ); perror(""); ! } /* end of fixcpio.c */ --- 396,402 ----- { fprintf(stderr, "\007Error writing %s file", what ); perror(""); ! exit( 1 ); } /* end of fixcpio.c */ -- Kill Tree. Kill Turkey. Merry Xmas. {amdahl, sun, mtxinu, cbosgd}!rtech!daveb