SUMMARY: getting rest of files off bad tar tape

From: Sheila Hollenbaugh (shollen@valhalla.cs.wright.edu)
Date: Wed May 26 1993 - 06:34:20 CDT


My original question was:

>
> I am pretty sure I have seen this here before, if anyone has the
> summary I would be most grateful:
>
> We periodically remove student accounts after doing a tar backup to
> 6250 BPI 9-track reel to reel tape. The worst has finally happened,
> we have a tape which reports a hard error near the beginning of the
> tape, and a student who wants files from farther on in the tape. Is there
> a way to skip over the bad spot in the tape and retrieve the rest of the
> files?

There were three main categories of replies.

First there were variations on using mt to somehow get around the bad
spot. We had already tried this, but since the tape contained a single,
very large tar file, fsf did not help. What we really needed was
something that allowed tar to recover from tape errors.

Second were suggestions to use dd with the conv=error flag to copy the
tar file. We did not try this, but several people suggested it, so
there is reason to believe that it might work.

Third were recommendations for more forgiving tar programs. GNU tar was
recommended several times, tarx was also mentioned. But the winner was
Harold A. Miller <Hal.Miller@mel.dit.csiro.au> who sent us badtar.c.
It compiled fine, and we were able to recover the files with it. We
plan to get GNU tar in the future, but for a quick fix, badtar.c did the
job (Thanks Hal!)

In addition, a few replies combined some form of dd and tar, and one
suggested manually placing a BOT marker over the bad spot in the tape.

In the interest of not wasting bandwidth, I am appending selections from
some replies, eliminating duplication as much as possible.

Thanks to all the following (and to others whose replies have not come
in yet!

Tommy Reingold <boole.att.com!tommy>
odinba!jeff@uunet.UU.NET (Jeff Tate)
long-morrow@CS.YALE.EDU (H Morrow Long)
dob@inel.gov (Dave Brooks)
strombrg@hydra.acs.uci.edu
Geert Jan de Groot <geertj@ica.philips.nl>
"Harold A. Miller" <Hal.Miller@mel.dit.csiro.au>
"Jim Davis" <jdavis@cs.arizona.edu>
Douglas Myhre <doug@seas.marine.usf.edu>
turtle@sciences.sdsu.edu (Andrew Scherpbier)
***********************************************************

You might try using dd to get the tar file off the tape. You may be
able to say "dd if=/dev/rmt8 conv=error of=diskfile" and probably you
need blocksize as well. Conv=error means continue after errors. Then
we can hope you can make use of the damaged tar file on disk.

*************************************************

The exact mechanism was described in an early edition of root. Approximately, here goes.

1/ take a backup tape of the exact same directory structure.
2/ use dd to copy N blocks from it to pleas-let-this-work
3/ take the tape
4/ use dd to skip N blocks and then append the rest to pleas-let-this-work
5/ try and restore.

To be fair, the root example described the case where you go to restore to an empty
sirectory structure and type tar cvf instead of tar xvf (funny how proximate x & c are)
with an unprotected tape

PS. The current SysAdmin Magazine folks inherited Root, so they may have a back issue or
a copy of that article

************************************

The way I do this is with GNU tar. It is able to skip over bad spots and
resynch itself.

*****************

Also, you might dd off the file, using options like
"conv=sync,noerror", and then try tar/gtar on the result. There's
also something on the net, which you can use as a filter into tar...
I no longer recall its name, but will hunt a little on request. (I
imagine someone else will probably remember, but...)

**********************

What does dd if=/dev/nrmt8 bs=20b conv=noerror | tar xvf -
do for you?

*******************************
Sheila, try this: (badtar.c)

/* read tar tapes with bad blocks Mike Williams */
#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>

#define TBLOCK 512
#define NAMSIZ 100
#define TAPEBLOCK 10240

/* see tar (5) in the manual */
union hblock {
        char dummy[TBLOCK] ;
        struct header {
                char name[NAMSIZ] ;
                char mode[8] ;
                char uid[8] ;
                char gid[8] ;
                char size[12] ;
                char mtime[12] ;
                char chksum[8] ;
                char linkflag ;
                char linkname[NAMSIZ] ;
        } dbuf ;
} ;

char pad[TBLOCK] ;

/* check the check sum for the header block */
check_sum(c)
union hblock *c ;
{
        int i, j ;
        char *cp ;

        cp = c->dummy ;
        i = 0 ;
        for (j=0; j<TBLOCK; j++) i += *cp++ ;
        for (j=0; j<8; j++) i -= c->dbuf.chksum[j] ;
        for (j=0; j<8; j++) i += ' ' ;
        return(i) ;
}

char buf[TAPEBLOCK] ;
char xbuf[TAPEBLOCK] ;
int bpos = TAPEBLOCK - TBLOCK, eot, bad, fdl ;
FILE *logf ;

/* get the next TBLOCK chars from the tape */
char *
get_next()
{
        int res ;

        if (bpos == TAPEBLOCK - TBLOCK) {
                bcopy(xbuf, buf, TAPEBLOCK) ;
                res = read(fdl, buf, TAPEBLOCK) ;
                if (res == 0) eot = 1 ;
                if (res < TAPEBLOCK) {
                        bad = 1 ;
                        fprintf(logf, "***Bad block on tape!!\n") ;
                }
                else {
                        if (bad) fprintf(logf, "***End of bad block(s) on tape\n") ;
                        bad = 0 ;
                }
                bpos = 0 ;
        }
        else bpos += TBLOCK ;
        return(&buf[bpos]) ;
}

main(argc, argv)
char **argv ;
int argc ;
{
        int i, size, chksum, fblocks, eot_block ;
        union hblock *hp ;
        char tape[20] ;

        strcpy(tape, "/dev/rmt0") ; /* default */
        logf = stderr ;
        i = 1 ;

        /* get arguments */
        while (argc > i && argv[i][0] == '-') {
                switch (argv[i][1]) {
                        case 'f' :
                                if (argc > i+1) {
                                        strcpy(tape, argv[++i]) ;
                                }
                                else {
                                        fprintf(stderr, "No tape drive name given\n") ;
                                        exit(10) ;
                                }
                                break ;
                        case 'l' :
                                if (argc > i+1) {
                                        if ((logf = fopen(argv[++i], "w")) == NULL) {
                                                perror("Can't open log file\n") ;
                                                exit(11) ;
                                        }
                                }
                                else {
                                        fprintf(stderr, "No log file name given\n") ;
                                        exit(12) ;
                                }
                                break ;
                        default:
                                fprintf(stderr, "usage %s [-l logfile] [-f tape drive]\n",
                                        argv[0]) ;
                                exit(13) ;
                }
                i++ ;
        }

/* first char cannot be a 0 */
        pad[0] = 'x' ;

/* don't quite know what the tape driver will return, so fill buffer
 * with non zero rubbish
 */
        for (i=0; i<TAPEBLOCK; i++) xbuf[i] = 'x' ;

/* open the tape drive */
        if ((fdl = open(tape, O_RDONLY, 0)) < 0) {
                perror("can't open tape") ;
                exit(1) ;
        }
        while (1) {
                hp = (union hblock *) get_next() ;
                /* tar tests the first char to see if it is an end of tape (eot) block
                 * or not. Can't see why it doesn't use tape marks. Maybe they
                 * weren't invented when it was written?
                 */
                /* get a tar block */
                if (hp->dbuf.name[0] == '\0' && !bad && !fblocks) {
                        /* skip possible eot block (there are two of them) */
                        fprintf(logf, "***End of tape block\n") ;
                        eot_block++ ;
                        if (eot_block < 2 ) continue ;
                        eot = 1 ;
                }
        /* note if the last block read is bad, there may be rubbish (old info
         * from the last write at the end of it). This may cause some of the
         * last files which are extracted to be partially overwritten. There
         * is very little one can do about this (except pray).
         */
        /* end of tape ?? */
                if (eot) {
                        if (fblocks) {
                                fprintf(logf, "***Last file Truncated. File padded!!\n") ;
                                while (fblocks--) write(1, pad, TBLOCK) ;
                        }
                        /* write two blank (eot) blocks */
                        pad[0] = '\0' ;
                        write(1, pad, TBLOCK) ;
                        write(1, pad, TBLOCK) ;
                        exit(1) ;
                }

                eot_block = 0 ;

                if (fblocks && !bad) { /* all ok in the middle of a file */
                        write(1, hp, TBLOCK) ;
                        fblocks-- ;
                        continue ;
                }

                /* have we got a header?? */
                sscanf(hp->dbuf.size, "%lo", &size) ;
                sscanf(hp->dbuf.chksum, "%o", &chksum) ;
                if (check_sum(hp) == chksum && hp->dbuf.name[0] != '\0') {
                        /* we have a header */
                        if (fblocks) {
                                fprintf(logf, "***Truncated!! File padded!!\n") ;
                                while (fblocks--) write(1, pad, TBLOCK) ;
                        }
                        fblocks = (size%TBLOCK) ? size/TBLOCK + 1 : size/TBLOCK;
                        fprintf(logf, "%s\n", hp->dbuf.name) ;
                        write(1, hp, TBLOCK) ;
                        continue ;
                }
                /* not a header */
                if (fblocks == 0) {
                        /* throw it away! */
                        fprintf(logf, "***Deleted block!!\n") ;
                        continue ;
                }
                fblocks-- ;
                write(1, hp, TBLOCK) ;
        }
}

-- 
|Hal.Miller@mel.dit.CSIRO.AU      (HAM10) |  Facilities and Network Manager |
|CSIRO Division of Information Technology |  Facilities Management Project  |
|723 Swanston Street                      |  (TEL) +61 3 282 2628           |
|Carlton, VIC 3053, Australia             |  (FAX) +61 3 282 2600           |

***************************

With a nine-track, there is a very effective way.

The tape has two metal reflector strips, one near the start of the reel (10' or so into the tape) and one near the end. When you load the tape, the drive hardware winds the tape forward until it senses the first metal reflector strip. It uses that strip to position the tape at BOT (Beginning of Tape). It ignores anything recorded before BOT.

Now you can buy rolls of the reflector strips -- they're gummy on one side. Just find the bad spot, skip a little bit more, and stick on a new reflector. Since BOT is now after the bad spot, the drive will blithely ignore it!

***************************

If you can get past the bad spot by skipping a record or so, then you can do something like this:

dd if=$TAPE bs=20b skip=1 | tar xvBfb - 20 filenames

This will skip the first tared file (which is 1 record long) on the tape and continue taring from there. The key will be to find a way to physically get past the bad spot on the tape. The skip=1 may work, or a "mt fsr 1".

********************************

Sounds like you are in need of 'tarx' I have used this program MANY times and it has saved us from a lot of disasters! If you can't find tarx through archie, let me know and I'll send it to you.

----------------------------------------------------------- Sheila Hollenbaugh Sr. Computer Systems Engineer Wright State University College of Engineering & Computer Science Dayton, OH 45435 Voice: (513) 873-5077 FAX: (513) 873-5009 shollen@cs.wright.edu or shollen@valhalla.cs.wright.edu



This archive was generated by hypermail 2.1.2 : Fri Sep 28 2001 - 23:07:53 CDT