[mod.sources] v08i094: Repair damaged "cpio -c" archives

sources-request@mirror.UUCP (03/03/87)

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 <stdio.h>
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