home *** CD-ROM | disk | FTP | other *** search
- Subject: v24i077: Tool to salvage data from damaged tar tapes
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: d94d3357 5075df9b f91b5436 4bc6db49
-
- Submitted-by: henry@zoo.toronto.edu
- Posting-number: Volume 24, Issue 77
- Archive-name: tarx
-
- Tarx is a newer, cleaner, more versatile replacement for my old targ/tarl
- programs for salvaging information from damaged tar tapes. It does a
- better job on various details and has been extensively tidied up.
-
- It uses a simplistic pattern-matching approach to identify tar header
- blocks. It will cheerfully persist despite all sorts of bad things about
- the archive (such as wrong checksums, read errors, and scraped-off
- magnetic surface...), up to a maximum of "errlimit" hard errors in a row
-
- It can be used to list the files, and extract as much of them as is
- possible.
-
- #! /bin/sh
- echo 'README':
- sed 's/^X//' >'README' <<'!'
- XTarx is a newer, cleaner, more versatile replacement for my old targ/tarl
- Xprograms for salvaging information from damaged tar tapes. It does a
- Xbetter job on various details and has been extensively tidied up.
- X
- X Henry Spencer at U of Toronto Zoology
- X henry@zoo.toronto.edu utzoo!henry
- !
- echo 'Makefile':
- sed 's/^X//' >'Makefile' <<'!'
- XCFLAGS = -O
- XLDFLAGS =
- X
- Xtarx: tarx.o
- X $(CC) $(LDFLAGS) tarx.o -o $@
- !
- echo 'tarx.1':
- sed 's/^X//' >'tarx.1' <<'!'
- X.TH TARX 1 "26 July 1990"
- X.BY Zoology
- 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.PP
- X.I Tarx
- Xallocates a read buffer of \fIblockfactor\fR*512 bytes,
- Xwhere the default \fIblockfactor\fR is 400 (to allow for the very large
- Xblocks sometimes found on cartridge tapes),
- Xand attempts a full-sized read each time.
- XIt copes properly with getting less than it expected, although it does
- Xinsist that the result be a multiple of 512 bytes.
- XWhen dealing with cranky tape drives or other special situations,
- Xor severely short of memory, or working on a 16-bit machine,
- Xit may be useful to specify a smaller \fIblockfactor\fR.
- X(Notably, on 16-bit machines 126 is often the maximum that will work.)
- XSpecifying a larger \fIblockfactor\fR may be necessary if tape blocks
- Xare truly enormous.
- 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
-
- exit 0 # Just in case...
-