misc. mag tape questions

Henry Spencer henry at utzoo.uucp
Thu May 11 05:27:26 AEST 1989


In article <543 at voodoo.UUCP> tomm at voodoo.UUCP (Tom Mackey) writes:
>... If an EOT mark is written
>at the beginning of a tar tape, is there any way of salvaging the data
>following the tape mark? ...

Assuming the tape drive will go along with it, it can be done.

>Henry Spencer mentioned some tape bit twiddling programs...would you be
>willing to post a few of those, Mr. Spencer, so that those of us who would
>like to learn to salvage tapes could get a good start? ...

Here's my latest; note that this is a tidied-up version of my production
code, and the tidied version hasn't been tested well yet.  Also, there
are lots of things that could be done but aren't, yet.

echo 'tarx.1':
sed 's/^X//' >'tarx.1' <<'!'
X.TH TARX 1 local
X.DA 10 May 1989
X.SH NAME
Xtarx \- recover files from damaged tar-format archives
X.SH SYNOPSIS
X\fBtarx\fR
X[
X.B \-t
X]
X[
X.B \-b
Xblockfactor
X]
X[
X.B \-e
Xerrlimit
X] [
X.B \-E
Xeoflimit
X]
Xname ...
X.SH DESCRIPTION
X\fITarx\fR is used to list and recover files from a
Xdamaged \fItar\fR(1) archive.
XIt uses a simplistic pattern-matching
Xapproach to identify \fItar\fR header blocks.
XIt will cheerfully persist despite all sorts of bad things about the archive
X(such as wrong checksums, read errors, and scraped-off magnetic surface...),
Xup to a maximum of \fIerrlimit\fR (default 3) hard errors in a row
Xor \fIeoflimit\fR (default 2) EOFs in a row.
XSuch events are reported but don't terminate operations.
XThe \fItar\fR archive is read from standard input.
X.PP
XWith the
X.B \-t
Xoption,
X\fItarx\fR lists the file names it sees in the archive.
X.PP
XWithout the
X.B \-t
Xoption,
X\fItarx\fR takes file or directory \fIname\fRs as arguments
Xand attempts to extract them from the archive.
X(If no \fIname\fRs are given, \fItarx\fR extracts everything it can find.)
X\fITarx\fR is not willing to create directories, however,
Xso these must be made manually beforehand if they do not already exist.
XFiles are owned by the user, and have his default permissions.
X.SH EXAMPLE
X``tarx \-t </dev/rmt0 >filelist'' lists all files on the tape
Xmounted on /dev/rmt0 and places the results in ``filelist''.
X.PP
X``tarx joe/precious </dev/rmt0'' restores the file
X``joe/precious'' from the tape mounted on /dev/rmt0.
XThe directory ``joe'' must already exist.
X.SH SEE ALSO
Xtar(1)
X.SH HISTORY
XWritten by Henry Spencer, Univ. of Toronto Zoology,
Xbased on older programs by the same author.
XThis software is public domain.
XOriginal manual page by Chris Robertson.
X.SH BUGS
X\fITarx\fR should be smarter about directories and permissions.
X.PP
XArguably should use the \fItar\fR header-block checksum,
Xinstead of the slightly-arcane pattern matcher, to identify header blocks.
X.PP
XA \fItar\fR archive containing a file which is itself a \fItar\fR archive
Xwill cause considerable confusion.
!
echo 'tarx.c':
sed 's/^X//' >'tarx.c' <<'!'
X/*
X * tarx - manipulate damaged tar tapes heuristically
X */
X
X#include <stdio.h>
X#include <string.h>
X
X#define NAMSIZ 100		/* why isn't there a tar.h??? */
X#define	FLAG	(NAMSIZ+8+8+8+12+12+8)	/* offset of is-a-link flag */
Xstruct matches {
X	int offset;
X	char value;
X} matches[] = {			/* pattern-match table for header blocks */
X	NAMSIZ+6,	' ',
X	NAMSIZ+7,	'\0',
X	NAMSIZ+8+6,	' ',
X	NAMSIZ+8+7,	'\0',
X	NAMSIZ+16+6,	' ',
X	NAMSIZ+16+7,	'\0',
X	NAMSIZ+24+11,	' ',
X	NAMSIZ+36+11,	' ',
X	NAMSIZ+48+6,	'\0',
X	0,		0,
X};
X
X#ifndef MAXBLOCK
X#define	MAXBLOCK	400	/* SGI makes blocky tapes */
X#endif
Xint maxblock = MAXBLOCK;
X#define	BLOCK	512
X
Xchar *buf;			/* -> malloced buffer */
Xint nleft = 0;			/* number of blocks left in buffer */
Xint whichnow;			/* index in buffer of current block */
X
Xint nbad = 0;			/* number of consecutive bad reads */
Xint badlimit = 3;		/* limit on consecutive bads */
Xint neof = 0;			/* number of consecutive EOF marks */
Xint eoflimit = 2;		/* limit on consecutive eofs */
X
Xint opened = 0;			/* are we writing a file? */
Xint f;				/* the file descriptor, if any */
Xlong fsize;			/* number of bytes not yet written to file */
X
Xchar op = 'x';			/* what operation is being done? */
X
X#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
X
X#ifndef lint
Xstatic char RCSid[] = "$Header$";
X#endif
X
Xint debug = 0;
Xchar *progname;
X
Xextern void exit();
X#ifdef UTZOOERR
Xextern char *mkprogname();
X#else
X#define	mkprogname(a)	(a)
X#endif
X
X/*
X - main - parse arguments and handle options
X */
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	int c;
X	int errflg = 0;
X	register char *block;
X	extern char *readblock();
X	extern char *malloc();
X	extern int optind;
X	extern char *optarg;
X
X	progname = mkprogname(argv[0]);
X
X	while ((c = getopt(argc, argv, "tb:e:E:d")) != EOF)
X		switch (c) {
X		case 't':	/* just list files */
X			op = 't';
X			break;
X		case 'b':	/* set blocking factor */
X			maxblock = atoi(optarg);
X			break;
X		case 'e':	/* set error limit */
X			badlimit = atoi(optarg);
X			break;
X		case 'E':	/* set eof limit */
X			eoflimit = atoi(optarg);
X			break;
X		case 'd':	/* Debugging. */
X			debug++;
X			break;
X		case '?':
X		default:
X			errflg++;
X			break;
X		}
X	if (errflg) {
X		fprintf(stderr, "usage: %s ", progname);
X		fprintf(stderr, "[-t] [-b blockf] [-e errs] [-E eofs] [name] ...\n");
X		exit(2);
X	}
X
X	buf = malloc(maxblock*BLOCK);
X	if (buf == NULL) {
X		fprintf(stderr, "%s: cannot allocate buffer of %d blocks\n",
X							progname, maxblock);
X		exit(1);
X	}
X
X	for(;;) {
X		block = readblock(0);
X
X		if (block != NULL)
X			doblock(block, argc - optind, &argv[optind]);
X	}
X	/* NOTREACHED */
X}
X
X/*
X - readblock - read in a block and deal with error/eof
X */
Xchar *
Xreadblock(desc)
Xint desc;
X{
X	register int count;
X	extern int errno;
X	extern int sys_nerr;
X	extern char *sys_errlist[];
X
X	if (nleft > 0) {
X		whichnow++;
X		nleft--;
X		return(buf+whichnow*BLOCK);
X	}
X
X	count = read(desc, buf, maxblock*BLOCK);
X	if (count != 0 && neof > 0)
X		printf("---! %d EOF(s)\n", neof);	/* delayed EOF count */
X	if (count <= 0 || count%BLOCK != 0) {
X		if (count == 0)
X			neof++;
X		else if (count > 0) {
X			printf("---! bad block size (%d) - treated as bad\n", count);
X			nbad++;
X		} else {
X			if (errno >= 0 && errno < sys_nerr)
X				printf("---! error (%s)\n", sys_errlist[errno]);
X			else
X				printf("---! error %d\n", errno);
X			nbad++;
X		}
X		if (nbad >= badlimit)
X			exit(1);
X		if (neof >= eoflimit)
X			exit(0);
X		return(NULL);
X	}
X
X	/* successful read */
X	nbad = 0;
X	neof = 0;
X	whichnow = 0;
X	nleft = count/BLOCK - 1;	/* -1 for one we're about to return */
X	return(buf);
X}
X
X/*
X - doblock - process a block
X */
Xdoblock(block, argc, argv)
Xchar *block;
Xint argc;
Xchar **argv;
X{
X	register int count;
X	register int tar = istar(block);
X	register int ret;
X
X	if (!tar && !opened)
X		return;
X
X	if (!tar && opened) {
X		count = (fsize > BLOCK) ? BLOCK : (int)fsize;
X		ret = write(f, block, count);
X		if (ret < 0)
X			printf("---! write error in file!\n");
X		fsize -= count;
X		if (fsize <= 0) {
X			opened = 0;
X			close(f);
X			printf("--- done\n");
X		}
X		return;
X	}
X	/* it's a tar header block */
X
X	if (op == 't') {
X		printf("%s\n", block);
X		return;
X	}
X	/* op == 'x' */
X
X	if (opened) {
X		printf("---! premature end\n");
X		close(f);
X		opened = 0;
X	}
X
X	if (!match(block, argc, argv))
X		return;		/* this file is not of interest */
X
X	switch (block[FLAG]) {
X	case '0':
X	case '\0':
X		f = creat(block, 0666);
X		if (f < 0)
X			printf("---! unable to create `%s'\n", block);
X		else {
X			opened = 1;
X			ret = sscanf(block+NAMSIZ+24, "%lo", &fsize);
X			if (ret != 1) {
X				printf("---! can't read size of `%s'", block);
X				close(f);
X				opened = 0;
X			} else {
X				printf("--- reading %s %ld\n", block, fsize);
X				if (fsize <= 0) {
X					close(f);
X					opened = 0;
X					printf("--- done\n");
X				}
X			}
X		}
X		break;
X	case '1':
X		f = link(block+FLAG+1, block);
X		if (f < 0)
X			printf("---! unable to link %s to %s\n", block+FLAG+1, block);
X		else
X			printf("--- link %s to %s\n", block+FLAG+1, block);
X		break;
X	case '2':
X		f = symlink(block+FLAG+1, block);
X		if (f < 0)
X			printf("---! unable to symlink %s to %s\n", block+FLAG+1, block);
X		else
X			printf("--- symlink %s to %s\n", block+FLAG+1, block);
X		break;
X	default:
X		printf("---! unknown flag value %c\n", block[FLAG]);
X		break;
X	}
X}
X
X/*
X - match - does this string match one of the arguments?
X */
Xint
Xmatch(s, argc, argv)
Xchar *s;
Xint argc;
Xchar **argv;
X{
X	register int i;
X	register int len;
X
X	if (argc == 0)
X		return(1);
X
X	for (i = 0; i < argc; i++) {
X		len = strlen(argv[i]);
X		if (strncmp(s, argv[i], len) == 0 &&
X					(s[len] == '/' || s[len] == '\0'))
X			return(1);
X	}
X	return(0);
X}
X
X/*
X - istar - is this plausibly a tar header block?
X */
Xint
Xistar(block)
Xregister char *block;
X{
X	register int loop;
X
X	for (loop = 0; matches[loop].offset != 0; loop++)
X		if (block[matches[loop].offset] != matches[loop].value)
X			return(0);
X	return(1);
X}
!
echo done
-- 
Mars in 1980s:  USSR, 2 tries, |     Henry Spencer at U of Toronto Zoology
2 failures; USA, 0 tries.      | uunet!attcan!utzoo!henry henry at zoo.toronto.edu



More information about the Comp.sys.sgi mailing list