[net.sources] fixcpio -- correct "Out of Phase -- get help" errors

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