fixcpio -- correct "Out of Phase -- get help" errors
Dave Brower
daveb at rtech.UUCP
Fri Dec 26 05:57:51 AEST 1986
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
More information about the Comp.sources.unix
mailing list