]> git.wh0rd.org Git - dump.git/blob - restore/tape.c
d7471a39b6513e9228c75b7d5959a3fb2a7c640f
[dump.git] / restore / tape.c
1 /*
2  *      Ported to Linux's Second Extended File System as part of the
3  *      dump and restore backup suit
4  *      Remy Card <card@Linux.EU.Org>, 1994-1997
5  *      Stelian Pop <pop@noos.fr>, 1999-2000
6  *      Stelian Pop <pop@noos.fr> - AlcĂ´ve <www.alcove.fr>, 2000
7  */
8
9 /*
10  * Copyright (c) 1983, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  * (c) UNIX System Laboratories, Inc.
13  * All or some portions of this file are derived from material licensed
14  * to the University of California by American Telephone and Telegraph
15  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
16  * the permission of UNIX System Laboratories, Inc.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. All advertising materials mentioning features or use of this software
27  *    must display the following acknowledgement:
28  *      This product includes software developed by the University of
29  *      California, Berkeley and its contributors.
30  * 4. Neither the name of the University nor the names of its contributors
31  *    may be used to endorse or promote products derived from this software
32  *    without specific prior written permission.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  */
46
47 #ifndef lint
48 static const char rcsid[] =
49         "$Id: tape.c,v 1.27 2001/03/19 13:22:49 stelian Exp $";
50 #endif /* not lint */
51
52 #include <config.h>
53 #include <errno.h>
54 #include <compaterr.h>
55 #include <setjmp.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include <sys/param.h>
62 #include <sys/file.h>
63 #include <sys/mtio.h>
64 #include <sys/stat.h>
65
66 #ifdef  __linux__
67 #include <sys/time.h>
68 #include <time.h>
69 #include <linux/ext2_fs.h>
70 #include <ext2fs/ext2fs.h>
71 #include <bsdcompat.h>
72 #else   /* __linux__ */
73 #include <ufs/ufs/dinode.h>
74 #endif  /* __linux__ */
75 #include <protocols/dumprestore.h>
76
77 #ifdef HAVE_ZLIB
78 #include <zlib.h>
79 #endif /* HAVE_ZLIB */
80
81 #include "restore.h"
82 #include "extern.h"
83 #include "pathnames.h"
84
85 static long     fssize = MAXBSIZE;
86 static int      mt = -1;
87 static int      pipein = 0;
88 static int      magtapein = 0;          /* input is from magtape */
89 static char     magtape[MAXPATHLEN];
90 static char     magtapeprefix[MAXPATHLEN];
91 static int      blkcnt;
92 static int      numtrec;
93 static char     *tapebuf;               /* input buffer for read */
94 static int      bufsize;                /* buffer size without prefix */
95 static char     *tbufptr = NULL;        /* active tape buffer */
96 #ifdef HAVE_ZLIB
97 static char     *comprbuf;              /* uncompress work buf */
98 static size_t   comprlen;               /* size including prefix */
99 #endif
100 static union    u_spcl endoftapemark;
101 static long     blksread;               /* blocks read since last header */
102 static long     tpblksread = 0;         /* TP_BSIZE blocks read */
103 static long     tapesread;
104 static sigjmp_buf       restart;
105 static int      gettingfile = 0;        /* restart has a valid frame */
106 static char     *host = NULL;
107
108 static int      ofile;
109 static char     *map;
110 static char     lnkbuf[MAXPATHLEN + 1];
111 static int      pathlen;
112
113 int             oldinofmt;      /* old inode format conversion required */
114 int             Bcvt;           /* Swap Bytes (for CCI or sun) */
115 static int      Qcvt;           /* Swap quads (for sun) */
116
117 #define FLUSHTAPEBUF()  blkcnt = ntrec + 1
118
119 static void      accthdr __P((struct s_spcl *));
120 static int       checksum __P((int *));
121 static void      findinode __P((struct s_spcl *));
122 static void      findtapeblksize __P((void));
123 static int       gethead __P((struct s_spcl *));
124 static void      readtape __P((char *));
125 static void      setdumpnum __P((void));
126 static u_int     swabi __P((u_int));
127 #if 0
128 static u_long    swabl __P((u_long));
129 #endif
130 static u_char   *swab64 __P((u_char *, int));
131 static u_char   *swab32 __P((u_char *, int));
132 static u_char   *swab16 __P((u_char *, int));
133 static void      terminateinput __P((void));
134 static void      xtrfile __P((char *, size_t));
135 static void      xtrlnkfile __P((char *, size_t));
136 static void      xtrlnkskip __P((char *, size_t));
137 static void      xtrmap __P((char *, size_t));
138 static void      xtrmapskip __P((char *, size_t));
139 static void      xtrskip __P((char *, size_t));
140
141 #ifdef HAVE_ZLIB
142 static void     newcomprbuf __P((long));
143 static void     readtape_set __P((char *));
144 static void     readtape_uncompr __P((char *));
145 static void     readtape_comprfile __P((char *));
146 static void     readtape_comprtape __P((char *));
147 static char     *decompress_tapebuf __P((struct tapebuf *, int));
148 static void     msg_read_error __P((char *));
149 #endif
150 static int      read_a_block __P((int, void *, size_t, long *));
151 #define PREFIXSIZE      sizeof(struct tapebuf)
152
153 #define COMPARE_ONTHEFLY 1
154
155 #if COMPARE_ONTHEFLY
156 static int      ifile;          /* input file for compare */
157 static int      cmperror;       /* compare error */
158 static void     xtrcmpfile __P((char *, size_t));
159 static void     xtrcmpskip __P((char *, size_t));
160 #endif
161
162 static int readmapflag;
163
164 /*
165  * Set up an input source. This is called from main.c before setup() is.
166  */
167 void
168 setinput(char *source)
169 {
170         FLUSHTAPEBUF();
171         if (bflag)
172                 newtapebuf(ntrec);
173         else
174                 newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
175         terminal = stdin;
176
177 #ifdef RRESTORE
178         if (strchr(source, ':')) {
179                 host = source;
180                 source = strchr(host, ':');
181                 *source++ = '\0';
182                 if (rmthost(host) == 0)
183                         exit(1);
184         } else
185 #endif
186         if (strcmp(source, "-") == 0) {
187                 /*
188                  * Since input is coming from a pipe we must establish
189                  * our own connection to the terminal.
190                  */
191                 terminal = fopen(_PATH_TTY, "r");
192                 if (terminal == NULL) {
193                         warn("cannot open %s", _PATH_TTY);
194                         terminal = fopen(_PATH_DEVNULL, "r");
195                         if (terminal == NULL)
196                                 err(1, "cannot open %s", _PATH_DEVNULL);
197                 }
198                 pipein++;
199         }
200         setuid(getuid());       /* no longer need or want root privileges */
201         if (Mflag) {
202                 strncpy(magtapeprefix, source, MAXPATHLEN);
203                 magtapeprefix[MAXPATHLEN-1] = '\0';
204                 snprintf(magtape, MAXPATHLEN, "%s%03d", source, 1);
205         }
206         else
207                 strncpy(magtape, source, MAXPATHLEN);
208         magtape[MAXPATHLEN - 1] = '\0';
209 }
210
211 void
212 newtapebuf(long size)
213 {
214         static int tapebufsize = -1;
215
216         ntrec = size;
217         bufsize = ntrec * TP_BSIZE;
218         if (size <= tapebufsize)
219                 return;
220         if (tapebuf != NULL)
221                 free(tapebuf);
222         tapebuf = malloc(size * TP_BSIZE + sizeof(struct tapebuf));
223         if (tapebuf == NULL)
224                 errx(1, "Cannot allocate space for tape buffer");
225         tapebufsize = size;
226 }
227
228 #ifdef HAVE_ZLIB
229 static void
230 newcomprbuf(long size)
231 {
232         if (size <= comprlen)
233                 return;
234         comprlen = size + sizeof(struct tapebuf);
235         if (comprbuf != NULL)
236                 free(comprbuf);
237         comprbuf = malloc(comprlen);
238         if (comprbuf == NULL)
239                 errx(1, "Cannot allocate space for decompress buffer");
240 }
241 #endif /* HAVE_ZLIB */
242
243 /*
244  * Verify that the tape drive can be accessed and
245  * that it actually is a dump tape.
246  */
247 void
248 setup(void)
249 {
250         int i, j, *ip;
251         struct stat stbuf;
252         struct mtget mt_stat;
253
254         Vprintf(stdout, "Verify tape and initialize maps\n");
255 #ifdef RRESTORE
256         if (host)
257                 mt = rmtopen(magtape, 0);
258         else
259 #endif
260         if (pipein)
261                 mt = 0;
262         else
263                 mt = open(magtape, O_RDONLY, 0);
264         if (mt < 0)
265                 err(1, "%s", magtape);
266         volno = 1;
267         if (!pipein) {
268                 /* need to know if input is really from a tape */
269 #ifdef RRESTORE
270                 if (host)
271                         magtapein = rmtioctl(MTNOP, 1) != -1;
272                 else
273 #endif
274                         magtapein = ioctl(mt, MTIOCGET, (char *) &mt_stat) == 0;
275         }
276
277         Vprintf(stdout,"Input is from %s\n", magtapein? "tape": "file/pipe");
278         setdumpnum();
279         FLUSHTAPEBUF();
280         findtapeblksize();
281         if (gethead(&spcl) == FAIL) {
282                 blkcnt--; /* push back this block */
283                 blksread--;
284                 tpblksread--;
285                 cvtflag++;
286                 if (gethead(&spcl) == FAIL)
287                         errx(1, "Tape is not a dump tape");
288                 fprintf(stderr, "Converting to new file system format.\n");
289         }
290
291         if (zflag) {
292                 fprintf(stderr, "Dump tape is compressed.\n");
293 #ifdef HAVE_ZLIB
294                 newcomprbuf(bufsize);
295 #else
296                 errx(1,"This restore version doesn't support decompression");
297 #endif /* HAVE_ZLIB */
298         }
299         if (pipein) {
300                 endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
301                 endoftapemark.s_spcl.c_type = TS_END;
302                 ip = (int *)&endoftapemark;
303                 j = sizeof(union u_spcl) / sizeof(int);
304                 i = 0;
305                 do
306                         i += *ip++;
307                 while (--j);
308                 endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
309         }
310         if (vflag || command == 't' || command == 'C')
311                 printdumpinfo();
312         if (filesys[0] == '\0') {
313                 char *dirptr;
314                 strncpy(filesys, spcl.c_filesys, NAMELEN);
315                 filesys[NAMELEN - 1] = '\0';
316                 dirptr = strstr(filesys, " (dir");
317                 if (dirptr != NULL)
318                         *dirptr = '\0';
319         }
320         dumptime = spcl.c_ddate;
321         dumpdate = spcl.c_date;
322         if (stat(".", &stbuf) < 0)
323                 err(1, "cannot stat .");
324         if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
325                 fssize = TP_BSIZE;
326         if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
327                 fssize = stbuf.st_blksize;
328         if (((fssize - 1) & fssize) != 0)
329                 errx(1, "bad block size %ld", fssize);
330         if (spcl.c_volume != 1)
331                 errx(1, "Tape is not volume 1 of the dump");
332         if (gethead(&spcl) == FAIL) {
333                 Dprintf(stdout, "header read failed at %ld blocks\n", (long)blksread);
334                 panic("no header after volume mark!\n");
335         }
336         findinode(&spcl);
337         if (spcl.c_type != TS_CLRI)
338                 errx(1, "Cannot find file removal list");
339         maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
340         Dprintf(stdout, "maxino = %ld\n", (long)maxino);
341         map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
342         if (map == NULL)
343                 errx(1, "no memory for active inode map");
344         usedinomap = map;
345         curfile.action = USING;
346         getfile(xtrmap, xtrmapskip);
347         if (spcl.c_type != TS_BITS)
348                 errx(1, "Cannot find file dump list");
349         map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
350         if (map == (char *)NULL)
351                 errx(1, "no memory for file dump list");
352         dumpmap = map;
353         curfile.action = USING;
354         getfile(xtrmap, xtrmapskip);
355         /*
356          * If there may be whiteout entries on the tape, pretend that the
357          * whiteout inode exists, so that the whiteout entries can be
358          * extracted.
359          */
360         if (oldinofmt == 0)
361                 SETINO(WINO, dumpmap);
362 }
363
364 /*
365  * Prompt user to load a new dump volume.
366  * "Nextvol" is the next suggested volume to use.
367  * This suggested volume is enforced when doing full
368  * or incremental restores, but can be overridden by
369  * the user when only extracting a subset of the files.
370  */
371 void
372 getvol(long nextvol)
373 {
374         long newvol = 0, savecnt = 0, wantnext = 0, i;
375         union u_spcl tmpspcl;
376 #       define tmpbuf tmpspcl.s_spcl
377         char buf[TP_BSIZE];
378         int haderror = 0;
379
380         if (nextvol == 1) {
381                 tapesread = 0;
382                 gettingfile = 0;
383         }
384         if (pipein) {
385                 if (nextvol != 1)
386                         panic("Changing volumes on pipe input?\n");
387                 if (volno == 1)
388                         return;
389                 goto gethdr;
390         }
391         savecnt = blksread;
392 again:
393         if (pipein)
394                 exit(1); /* pipes do not get a second chance */
395         if (command == 'R' || command == 'r' || curfile.action != SKIP) {
396                 newvol = nextvol;
397                 wantnext = 1;
398         } else {
399                 newvol = 0;
400                 wantnext = 0;
401         }
402         while (newvol <= 0) {
403                 if (tapesread == 0) {
404                         fprintf(stderr, "%s%s%s%s%s",
405                             "You have not read any tapes yet.\n",
406                             "Unless you know which volume your",
407                             " file(s) are on you should start\n",
408                             "with the last volume and work",
409                             " towards the first.\n");
410                 } else {
411                         fprintf(stderr, "You have read volumes");
412                         strcpy(buf, ": ");
413                         for (i = 1; i < 32; i++)
414                                 if (tapesread & (1 << i)) {
415                                         fprintf(stderr, "%s%ld", buf, (long)i);
416                                         strcpy(buf, ", ");
417                                 }
418                         fprintf(stderr, "\n");
419                 }
420                 do      {
421                         fprintf(stderr, "Specify next volume #: ");
422                         (void) fflush(stderr);
423                         (void) fgets(buf, TP_BSIZE, terminal);
424                 } while (!feof(terminal) && buf[0] == '\n');
425                 if (feof(terminal))
426                         exit(1);
427                 newvol = atoi(buf);
428                 if (newvol <= 0) {
429                         fprintf(stderr,
430                             "Volume numbers are positive numerics\n");
431                 }
432         }
433         if (newvol == volno) {
434                 tapesread |= 1 << volno;
435                 return;
436         }
437         closemt();
438         if (Mflag) {
439                 snprintf(magtape, MAXPATHLEN, "%s%03ld", magtapeprefix, newvol);
440                 magtape[MAXPATHLEN - 1] = '\0';
441         }
442         if (!Mflag || haderror) {
443                 haderror = 0;
444                 fprintf(stderr, "Mount tape volume %ld\n", (long)newvol);
445                 fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
446                 fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
447                 (void) fflush(stderr);
448                 (void) fgets(buf, TP_BSIZE, terminal);
449                 if (feof(terminal))
450                         exit(1);
451                 if (!strcmp(buf, "none\n")) {
452                         terminateinput();
453                         return;
454                 }
455                 if (buf[0] != '\n') {
456                         (void) strcpy(magtape, buf);
457                         magtape[strlen(magtape) - 1] = '\0';
458                 }
459         }
460 #ifdef RRESTORE
461         if (host)
462                 mt = rmtopen(magtape, 0);
463         else
464 #endif
465                 mt = open(magtape, O_RDONLY, 0);
466
467         if (mt == -1) {
468                 fprintf(stderr, "Cannot open %s\n", magtape);
469                 volno = -1;
470                 haderror = 1;
471                 goto again;
472         }
473 gethdr:
474         volno = newvol;
475         setdumpnum();
476         FLUSHTAPEBUF();
477         if (gethead(&tmpbuf) == FAIL) {
478                 Dprintf(stdout, "header read failed at %ld blocks\n", (long)blksread);
479                 fprintf(stderr, "tape is not dump tape\n");
480                 volno = 0;
481                 haderror = 1;
482                 goto again;
483         }
484         if (tmpbuf.c_volume != volno) {
485                 fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
486                 volno = 0;
487                 haderror = 1;
488                 goto again;
489         }
490         if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
491 #ifdef  __linux__
492                 fprintf(stderr, "Wrong dump date\n\tgot: %s",
493                         ctime4(&tmpbuf.c_date));
494                 fprintf(stderr, "\twanted: %s", ctime4(&dumpdate));
495 #else
496                 fprintf(stderr, "Wrong dump date\n\tgot: %s",
497                         ctime(&tmpbuf.c_date));
498                 fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
499 #endif
500                 volno = 0;
501                 haderror = 1;
502                 goto again;
503         }
504         tapesread |= 1 << volno;
505         blksread = savecnt;
506         /*
507          * If continuing from the previous volume, skip over any
508          * blocks read already at the end of the previous volume.
509          *
510          * If coming to this volume at random, skip to the beginning
511          * of the next record.
512          */
513         Dprintf(stdout, "read %ld recs, tape starts with %ld\n",
514                 tpblksread, (long)tmpbuf.c_firstrec);
515         if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
516                 if (!wantnext) {
517                         tpblksread = tmpbuf.c_firstrec;
518                         for (i = tmpbuf.c_count; i > 0; i--)
519                                 readtape(buf);
520                 } else if (tmpbuf.c_firstrec > 0 &&
521                            tmpbuf.c_firstrec < tpblksread - 1) {
522                         /*
523                          * -1 since we've read the volume header
524                          */
525                         i = tpblksread - tmpbuf.c_firstrec - 1;
526                         Dprintf(stderr, "Skipping %ld duplicate record%s.\n",
527                                 (long)i, i > 1 ? "s" : "");
528                         while (--i >= 0)
529                                 readtape(buf);
530                 }
531         }
532         if (curfile.action == USING) {
533                 if (volno == 1)
534                         panic("active file into volume 1\n");
535                 return;
536         }
537         /*
538          * Skip up to the beginning of the next record
539          */
540         if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
541                 for (i = tmpbuf.c_count; i > 0; i--)
542                         readtape(buf);
543         (void) gethead(&spcl);
544         findinode(&spcl);
545         if (gettingfile) {
546                 gettingfile = 0;
547                 siglongjmp(restart, 1);
548         }
549 }
550
551 /*
552  * Handle unexpected EOF.
553  */
554 static void
555 terminateinput(void)
556 {
557
558         if (gettingfile && curfile.action == USING) {
559                 printf("Warning: %s %s\n",
560                     "End-of-input encountered while extracting", curfile.name);
561         }
562         curfile.name = "<name unknown>";
563         curfile.action = UNKNOWN;
564         curfile.dip = NULL;
565         curfile.ino = maxino;
566         if (gettingfile) {
567                 gettingfile = 0;
568                 siglongjmp(restart, 1);
569         }
570 }
571
572 /*
573  * handle multiple dumps per tape by skipping forward to the
574  * appropriate one.
575  */
576 static void
577 setdumpnum(void)
578 {
579         struct mtop tcom;
580
581         if (dumpnum == 1 || volno != 1)
582                 return;
583         if (pipein)
584                 errx(1, "Cannot have multiple dumps on pipe input");
585         tcom.mt_op = MTFSF;
586         tcom.mt_count = dumpnum - 1;
587 #ifdef RRESTORE
588         if (host)
589                 rmtioctl(MTFSF, dumpnum - 1);
590         else
591 #endif
592                 if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
593                         warn("ioctl MTFSF");
594 }
595
596 void
597 printdumpinfo(void)
598 {
599 #ifdef  __linux__
600         fprintf(stdout, "Dump   date: %s", ctime4(&spcl.c_date));
601         fprintf(stdout, "Dumped from: %s",
602             (spcl.c_ddate == 0) ? "the epoch\n" : ctime4(&spcl.c_ddate));
603 #else
604         fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
605         fprintf(stdout, "Dumped from: %s",
606             (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
607 #endif
608         if (spcl.c_host[0] == '\0')
609                 return;
610         fprintf(stdout, "Level %d dump of %s on %s:%s\n",
611                 spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
612         fprintf(stdout, "Label: %s\n", spcl.c_label);
613 }
614
615 int
616 extractfile(char *name)
617 {
618         unsigned int flags;
619         mode_t mode;
620         struct timeval timep[2];
621         struct entry *ep;
622
623         curfile.name = name;
624         curfile.action = USING;
625 #ifdef  __linux__
626         timep[0].tv_sec = curfile.dip->di_atime.tv_sec;
627         timep[0].tv_usec = curfile.dip->di_atime.tv_usec;
628         timep[1].tv_sec = curfile.dip->di_mtime.tv_sec;
629         timep[1].tv_usec = curfile.dip->di_mtime.tv_usec;
630 #else   /* __linux__ */
631         timep[0].tv_sec = curfile.dip->di_atime;
632         timep[0].tv_usec = curfile.dip->di_atimensec / 1000;
633         timep[1].tv_sec = curfile.dip->di_mtime;
634         timep[1].tv_usec = curfile.dip->di_mtimensec / 1000;
635 #endif  /* __linux__ */
636         mode = curfile.dip->di_mode;
637         flags = curfile.dip->di_flags;
638         switch (mode & IFMT) {
639
640         default:
641                 fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
642                 skipfile();
643                 return (FAIL);
644
645         case IFSOCK:
646                 Vprintf(stdout, "skipped socket %s\n", name);
647                 skipfile();
648                 return (GOOD);
649
650         case IFDIR:
651                 if (mflag) {
652                         ep = lookupname(name);
653                         if (ep == NULL || ep->e_flags & EXTRACT)
654                                 panic("unextracted directory %s\n", name);
655                         skipfile();
656                         return (GOOD);
657                 }
658                 Vprintf(stdout, "extract file %s\n", name);
659                 return (genliteraldir(name, curfile.ino));
660
661         case IFLNK:
662         {
663 #ifdef HAVE_LCHOWN
664                 uid_t luid = curfile.dip->di_uid;
665                 gid_t lgid = curfile.dip->di_gid;
666 #endif
667                 lnkbuf[0] = '\0';
668                 pathlen = 0;
669                 getfile(xtrlnkfile, xtrlnkskip);
670                 if (pathlen == 0) {
671                         Vprintf(stdout,
672                             "%s: zero length symbolic link (ignored)\n", name);
673                         return (GOOD);
674                 }
675                 if (linkit(lnkbuf, name, SYMLINK) == FAIL)
676                         return (FAIL);
677 #ifdef HAVE_LCHOWN
678                 (void) lchown(name, luid, lgid);
679 #endif
680                 return (GOOD);
681         }
682
683         case IFIFO:
684                 Vprintf(stdout, "extract fifo %s\n", name);
685                 if (Nflag) {
686                         skipfile();
687                         return (GOOD);
688                 }
689                 if (uflag && !Nflag)
690                         (void)unlink(name);
691                 if (mkfifo(name, mode) < 0) {
692                         warn("%s: cannot create fifo", name);
693                         skipfile();
694                         return (FAIL);
695                 }
696                 (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
697                 (void) chmod(name, mode);
698                 if (flags)
699 #ifdef  __linux__
700                         (void) fsetflags(name, flags);
701 #else
702                         (void) chflags(name, flags);
703 #endif
704                 skipfile();
705                 utimes(name, timep);
706                 return (GOOD);
707
708         case IFCHR:
709         case IFBLK:
710                 Vprintf(stdout, "extract special file %s\n", name);
711                 if (Nflag) {
712                         skipfile();
713                         return (GOOD);
714                 }
715                 if (uflag)
716                         (void)unlink(name);
717                 if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
718                         warn("%s: cannot create special file", name);
719                         skipfile();
720                         return (FAIL);
721                 }
722                 (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
723                 (void) chmod(name, mode);
724                 if (flags)
725 #ifdef  __linux__
726                         {
727                         warn("%s: fsetflags called on a special file", name);
728                         (void) fsetflags(name, flags);
729                         }
730 #else
731                         (void) chflags(name, flags);
732 #endif
733                 skipfile();
734                 utimes(name, timep);
735                 return (GOOD);
736
737         case IFREG:
738                 Vprintf(stdout, "extract file %s\n", name);
739                 if (Nflag) {
740                         skipfile();
741                         return (GOOD);
742                 }
743                 if (uflag)
744                         (void)unlink(name);
745                 if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
746                     0666)) < 0) {
747                         warn("%s: cannot create file", name);
748                         skipfile();
749                         return (FAIL);
750                 }
751                 (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
752                 (void) fchmod(ofile, mode);
753                 if (flags)
754 #ifdef  __linux__
755                         (void) setflags(ofile, flags);
756 #else
757                         (void) fchflags(ofile, flags);
758 #endif
759                 getfile(xtrfile, xtrskip);
760                 (void) close(ofile);
761                 utimes(name, timep);
762                 return (GOOD);
763         }
764         /* NOTREACHED */
765 }
766
767 /*
768  * skip over bit maps on the tape
769  */
770 void
771 skipmaps(void)
772 {
773
774         while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
775                 skipfile();
776 }
777
778 /*
779  * skip over a file on the tape
780  */
781 void
782 skipfile(void)
783 {
784
785         curfile.action = SKIP;
786         getfile(xtrnull, xtrnull);
787 }
788
789 /*
790  * Extract a file from the tape.
791  * When an allocated block is found it is passed to the fill function;
792  * when an unallocated block (hole) is found, a zeroed buffer is passed
793  * to the skip function.
794  */
795 void
796 getfile(void (*fill) __P((char *, size_t)), void (*skip) __P((char *, size_t)))
797 {
798         register int i;
799         volatile int curblk = 0;
800         volatile quad_t size = spcl.c_dinode.di_size;
801         volatile int last_write_was_hole = 0;
802         quad_t origsize = size;
803         static char clearedbuf[MAXBSIZE];
804         char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
805         char junk[TP_BSIZE];
806
807         if (spcl.c_type == TS_END)
808                 panic("ran off end of tape\n");
809         if (spcl.c_magic != NFS_MAGIC)
810                 panic("not at beginning of a file\n");
811         if (!gettingfile && setjmp(restart) != 0)
812                 return;
813         gettingfile++;
814 loop:
815         for (i = 0; i < spcl.c_count; i++) {
816                 if (readmapflag || spcl.c_addr[i]) {
817                         readtape(&buf[curblk++][0]);
818                         if (curblk == fssize / TP_BSIZE) {
819                                 (*fill)((char *)buf, (size_t)(size > TP_BSIZE ?
820                                      fssize : (curblk - 1) * TP_BSIZE + size));
821                                 curblk = 0;
822                                 last_write_was_hole = 0;
823                         }
824                 } else {
825                         if (curblk > 0) {
826                                 (*fill)((char *)buf, (size_t)(size > TP_BSIZE ?
827                                      curblk * TP_BSIZE :
828                                      (curblk - 1) * TP_BSIZE + size));
829                                 curblk = 0;
830                         }
831                         (*skip)(clearedbuf, (long)(size > TP_BSIZE ?
832                                 TP_BSIZE : size));
833                         last_write_was_hole = 1;
834                 }
835                 if ((size -= TP_BSIZE) <= 0) {
836                         for (i++; i < spcl.c_count; i++)
837                                 if (readmapflag || spcl.c_addr[i])
838                                         readtape(junk);
839                         break;
840                 }
841         }
842         if (gethead(&spcl) == GOOD && size > 0) {
843                 if (spcl.c_type == TS_ADDR)
844                         goto loop;
845                 Dprintf(stdout,
846                         "Missing address (header) block for %s at %ld blocks\n",
847                         curfile.name, (long)blksread);
848         }
849         if (curblk > 0) {
850                 (*fill)((char *)buf, (size_t)(curblk * TP_BSIZE) + size);
851                 last_write_was_hole = 0;
852         }
853         if (last_write_was_hole) {
854                 ftruncate(ofile, origsize);
855         }
856         findinode(&spcl);
857         gettingfile = 0;
858 }
859
860 /*
861  * Write out the next block of a file.
862  */
863 static void
864 xtrfile(char *buf, size_t size)
865 {
866
867         if (Nflag)
868                 return;
869         if (write(ofile, buf, (int) size) == -1)
870                 err(1, "write error extracting inode %lu, name %s\nwrite",
871                         (unsigned long)curfile.ino, curfile.name);
872 }
873
874 /*
875  * Skip over a hole in a file.
876  */
877 /* ARGSUSED */
878 static void
879 xtrskip(char *buf, size_t size)
880 {
881
882         if (lseek(ofile, (off_t)size, SEEK_CUR) == -1)
883                 err(1, "seek error extracting inode %lu, name %s\nlseek",
884                         (unsigned long)curfile.ino, curfile.name);
885 }
886
887 /*
888  * Collect the next block of a symbolic link.
889  */
890 static void
891 xtrlnkfile(char *buf, size_t size)
892 {
893
894         pathlen += size;
895         if (pathlen > MAXPATHLEN)
896                 errx(1, "symbolic link name: %s->%s%s; too long %d",
897                     curfile.name, lnkbuf, buf, pathlen);
898         (void) strcat(lnkbuf, buf);
899 }
900
901 /*
902  * Skip over a hole in a symbolic link (should never happen).
903  */
904 /* ARGSUSED */
905 static void
906 xtrlnkskip(char *buf, size_t size)
907 {
908
909         errx(1, "unallocated block in symbolic link %s", curfile.name);
910 }
911
912 /*
913  * Collect the next block of a bit map.
914  */
915 static void
916 xtrmap(char *buf, size_t size)
917 {
918
919         memmove(map, buf, size);
920         map += size;
921 }
922
923 /*
924  * Skip over a hole in a bit map (should never happen).
925  */
926 /* ARGSUSED */
927 static void
928 xtrmapskip(char *buf, size_t size)
929 {
930
931         panic("hole in map\n");
932         map += size;
933 }
934
935 /*
936  * Noop, when an extraction function is not needed.
937  */
938 /* ARGSUSED */
939 void
940 xtrnull(char *buf, size_t size)
941 {
942
943         return;
944 }
945
946 #if COMPARE_ONTHEFLY
947 /*
948  * Compare the next block of a file.
949  */
950 static void
951 xtrcmpfile(char *buf, size_t size)
952 {
953         static char cmpbuf[MAXBSIZE];
954
955         if (cmperror)
956                 return;
957         
958         if (read(ifile, cmpbuf, size) != size) {
959                 fprintf(stderr, "%s: size has changed.\n", 
960                         curfile.name);
961                 cmperror = 1;
962                 return;
963         }
964         
965         if (memcmp(buf, cmpbuf, size) != 0) {
966                 fprintf(stderr, "%s: tape and disk copies are different\n",
967                         curfile.name);
968                 cmperror = 1;
969                 return;
970         }
971 }
972
973 /*
974  * Skip over a hole in a file.
975  */
976 static void
977 xtrcmpskip(char *buf, size_t size)
978 {
979         static char cmpbuf[MAXBSIZE];
980         int i;
981
982         if (cmperror)
983                 return;
984         
985         if (read(ifile, cmpbuf, size) != size) {
986                 fprintf(stderr, "%s: size has changed.\n", 
987                         curfile.name);
988                 cmperror = 1;
989                 return;
990         }
991
992         for (i = 0; i < size; ++i)
993                 if (cmpbuf[i] != '\0') {
994                         fprintf(stderr, "%s: tape and disk copies are different\n",
995                                 curfile.name);
996                         cmperror = 1;
997                         return;
998                 }
999 }
1000 #endif /* COMPARE_ONTHEFLY */
1001
1002 #if !COMPARE_ONTHEFLY
1003 static int
1004 do_cmpfiles(int fd_tape, int fd_disk, long size)
1005 {
1006         static char buf_tape[BUFSIZ];
1007         static char buf_disk[BUFSIZ];
1008         ssize_t n_tape;
1009         ssize_t n_disk;
1010
1011         while (size > 0) {
1012                 if ((n_tape = read(fd_tape, buf_tape, sizeof(buf_tape))) < 1) {
1013                         close(fd_tape), close(fd_disk);
1014                         panic("do_cmpfiles: unexpected EOF[1]");
1015                 }
1016                 if ((n_disk = read(fd_disk, buf_disk, sizeof(buf_tape))) < 1) {
1017                         close(fd_tape), close(fd_disk);
1018                         panic("do_cmpfiles: unexpected EOF[2]");
1019                 }
1020                 if (n_tape != n_disk) {
1021                         close(fd_tape), close(fd_disk);
1022                         panic("do_cmpfiles: sizes different!");
1023                 }
1024                 if (memcmp(buf_tape, buf_disk, (size_t)n_tape) != 0) return (1);
1025                 size -= n_tape;
1026         }
1027         return (0);
1028 }
1029
1030 /* for debugging compare problems */
1031 #undef COMPARE_FAIL_KEEP_FILE
1032
1033 static
1034 #ifdef COMPARE_FAIL_KEEP_FILE
1035 /* return true if tapefile should be unlinked after compare */
1036 int
1037 #else
1038 void
1039 #endif
1040 cmpfiles(char *tapefile, char *diskfile, struct stat *sbuf_disk)
1041 {
1042         struct stat sbuf_tape;
1043         int fd_tape, fd_disk;
1044
1045         if (stat(tapefile, &sbuf_tape) != 0) {
1046                 panic("Can't lstat tmp file %s: %s\n", tapefile,
1047                       strerror(errno));
1048                 compare_errors = 1;
1049         }
1050
1051         if (sbuf_disk->st_size != sbuf_tape.st_size) {
1052                 fprintf(stderr,
1053                         "%s: size changed from %ld to %ld.\n",
1054                         diskfile, (long)sbuf_tape.st_size, (long)sbuf_disk->st_size);
1055                 compare_errors = 1;
1056 #ifdef COMPARE_FAIL_KEEP_FILE
1057                 return (0);
1058 #else
1059                 return;
1060 #endif
1061         }
1062
1063         if ((fd_tape = open(tapefile, O_RDONLY)) < 0) {
1064                 panic("Can't open %s: %s\n", tapefile, strerror(errno));
1065                 compare_errors = 1;
1066         }
1067         if ((fd_disk = open(diskfile, O_RDONLY)) < 0) {
1068                 close(fd_tape);
1069                 panic("Can't open %s: %s\n", diskfile, strerror(errno));
1070                 compare_errors = 1;
1071         }
1072
1073         if (do_cmpfiles(fd_tape, fd_disk, sbuf_tape.st_size)) {
1074                 fprintf(stderr, "%s: tape and disk copies are different\n",
1075                         diskfile);
1076                 close(fd_tape);
1077                 close(fd_disk);
1078                 compare_errors = 1;
1079 #ifdef COMPARE_FAIL_KEEP_FILE
1080                 /* rename the file to live in /tmp */
1081                 /* rename `tapefile' to /tmp/<basename of diskfile> */
1082                 {
1083                         char *p = strrchr(diskfile, '/');
1084                         char newname[MAXPATHLEN];
1085                         if (!p) {
1086                                 panic("can't find / in %s\n", diskfile);
1087                         }
1088                         snprintf(newname, sizeof(newname), "%s/debug/%s", tmpdir, p + 1);
1089                         if (rename(tapefile, newname)) {
1090                                 panic("rename from %s to %s failed: %s\n",
1091                                       tapefile, newname,
1092                                       strerror(errno));
1093                         } else {
1094                                 fprintf(stderr, "*** %s saved to %s\n",
1095                                         tapefile, newname);
1096                         }
1097                 }
1098                 
1099                 /* don't unlink the file (it's not there anymore */
1100                 /* anyway) */
1101                 return (0);
1102 #else
1103                 return;
1104 #endif
1105         }
1106         close(fd_tape);
1107         close(fd_disk);
1108 #ifdef COMPARE_FAIL_KEEP_FILE
1109         return (1);
1110 #endif
1111 }
1112 #endif /* !COMPARE_ONTHEFLY */
1113
1114 #if !COMPARE_ONTHEFLY
1115 static char tmpfilename[MAXPATHLEN];
1116 #endif
1117
1118 void
1119 comparefile(char *name)
1120 {
1121         int mode;
1122         struct stat sb;
1123         int r;
1124 #if !COMPARE_ONTHEFLY
1125         static char *tmpfile = NULL;
1126         struct stat stemp;
1127 #endif
1128
1129         if ((r = lstat(name, &sb)) != 0) {
1130                 warn("%s: does not exist (%d)", name, r);
1131                 compare_errors = 1;
1132                 skipfile();
1133                 return;
1134         }
1135
1136         curfile.name = name;
1137         curfile.action = USING;
1138         mode = curfile.dip->di_mode;
1139
1140         Vprintf(stdout, "comparing %s (size: %ld, mode: 0%o)\n", name,
1141                 (long)sb.st_size, mode);
1142
1143         if (sb.st_mode != mode) {
1144                 fprintf(stderr, "%s: mode changed from 0%o to 0%o.\n",
1145                         name, mode & 07777, sb.st_mode & 07777);
1146                 compare_errors = 1;
1147         }
1148         switch (mode & IFMT) {
1149         default:
1150                 skipfile();
1151                 return;
1152
1153         case IFSOCK:
1154                 skipfile();
1155                 return;
1156
1157         case IFDIR:
1158                 skipfile();
1159                 return;
1160
1161         case IFLNK: {
1162                 char lbuf[MAXPATHLEN + 1];
1163                 int lsize;
1164
1165                 if (!(sb.st_mode & S_IFLNK)) {
1166                         fprintf(stderr, "%s: is no longer a symbolic link\n",
1167                                 name);
1168                         compare_errors = 1;
1169                         return;
1170                 }
1171                 lnkbuf[0] = '\0';
1172                 pathlen = 0;
1173                 getfile(xtrlnkfile, xtrlnkskip);
1174                 if (pathlen == 0) {
1175                         fprintf(stderr,
1176                                 "%s: zero length symbolic link (ignored)\n",
1177                                 name);
1178                         compare_errors = 1;
1179                         return;
1180                 }
1181                 if ((lsize = readlink(name, lbuf, MAXPATHLEN)) < 0) {
1182                         panic("readlink of %s failed: %s", name,
1183                               strerror(errno));
1184                         compare_errors = 1;
1185                 }
1186                 lbuf[lsize] = 0;
1187                 if (strcmp(lbuf, lnkbuf) != 0) {
1188                         fprintf(stderr,
1189                                 "%s: symbolic link changed from %s to %s.\n",
1190                                 name, lnkbuf, lbuf);
1191                         compare_errors = 1;
1192                         return;
1193                 }
1194                 return;
1195         }
1196
1197         case IFCHR:
1198         case IFBLK:
1199                 if (!(sb.st_mode & (S_IFCHR|S_IFBLK))) {
1200                         fprintf(stderr, "%s: no longer a special file\n",
1201                                 name);
1202                         compare_errors = 1;
1203                         skipfile();
1204                         return;
1205                 }
1206
1207                 if (sb.st_rdev != (int)curfile.dip->di_rdev) {
1208                         fprintf(stderr,
1209                                 "%s: device changed from %d,%d to %d,%d.\n",
1210                                 name,
1211                                 ((int)curfile.dip->di_rdev >> 8) & 0xff,
1212                                 (int)curfile.dip->di_rdev & 0xff,
1213                                 ((int)sb.st_rdev >> 8) & 0xff,
1214                                 (int)sb.st_rdev & 0xff);
1215                         compare_errors = 1;
1216                 }
1217                 skipfile();
1218                 return;
1219
1220         case IFREG:
1221 #if COMPARE_ONTHEFLY
1222                 if ((ifile = open(name, O_RDONLY)) < 0) {
1223                         panic("Can't open %s: %s\n", name, strerror(errno));
1224                         skipfile();
1225                         compare_errors = 1;
1226                 }
1227                 else {
1228                         cmperror = 0;
1229                         getfile(xtrcmpfile, xtrcmpskip);
1230                         if (!cmperror) {
1231                                 char c;
1232                                 if (read(ifile, &c, 1) != 0) {
1233                                         fprintf(stderr, "%s: size has changed.\n", 
1234                                                 name);
1235                                         cmperror = 1;
1236                                 }
1237                         }
1238                         if (cmperror)
1239                                 compare_errors = 1;
1240                         close(ifile);
1241                 }
1242 #else
1243                 if (tmpfile == NULL) {
1244                         /* argument to mktemp() must not be in RO space: */
1245                         snprintf(tmpfilename, sizeof(tmpfilename), "%s/restoreCXXXXXX", tmpdir);
1246                         tmpfile = mktemp(&tmpfilename[0]);
1247                 }
1248                 if ((stat(tmpfile, &stemp) == 0) && (unlink(tmpfile) != 0)) {
1249                         panic("cannot delete tmp file %s: %s\n",
1250                               tmpfile, strerror(errno));
1251                 }
1252                 if ((ofile = creat(tmpfile, 0600)) < 0) {
1253                         panic("cannot create file temp file %s: %s\n",
1254                               name, strerror(errno));
1255                 }
1256                 getfile(xtrfile, xtrskip);
1257                 (void) close(ofile);
1258 #ifdef COMPARE_FAIL_KEEP_FILE
1259                 if (cmpfiles(tmpfile, name, &sb))
1260                         unlink(tmpfile);
1261 #else
1262                 cmpfiles(tmpfile, name, &sb);
1263                 unlink(tmpfile);
1264 #endif
1265 #endif /* COMPARE_ONTHEFLY */
1266                 return;
1267         }
1268         /* NOTREACHED */
1269 }
1270
1271 #ifdef HAVE_ZLIB
1272 static void (*readtape_func)(char *) = readtape_set;
1273
1274 /*
1275  * Read TP_BSIZE blocks from the input.
1276  * Handle read errors, and end of media.
1277  * Decompress compressed blocks.
1278  */
1279 static void
1280 readtape(char *buf)
1281 {
1282         (*readtape_func)(buf);  /* call the actual processing routine */
1283 }
1284
1285 /*
1286  * Set function pointer for readtape() routine. zflag and magtapein must
1287  * be correctly set before the first call to readtape().
1288  */
1289 static void
1290 readtape_set(char *buf)
1291 {
1292         if (!zflag) 
1293                 readtape_func = readtape_uncompr;
1294         else {
1295                 if (magtapein)
1296                         readtape_func = readtape_comprtape;
1297                 else
1298                         readtape_func = readtape_comprfile;
1299         }
1300         readtape(buf);
1301 }
1302
1303 #endif /* HAVE_ZLIB */
1304
1305 /*
1306  * This is the original readtape(), it's used for reading uncompressed input.
1307  * Read TP_BSIZE blocks from the input.
1308  * Handle read errors, and end of media.
1309  */
1310 static void
1311 #ifdef HAVE_ZLIB
1312 readtape_uncompr(char *buf)
1313 #else
1314 readtape(char *buf)
1315 #endif
1316 {
1317         ssize_t rd, newvol, i;
1318         int cnt, seek_failed;
1319
1320         if (blkcnt < numtrec) {
1321                 memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
1322                 blksread++;
1323                 tpblksread++;
1324                 return;
1325         }
1326         tbufptr = tapebuf;
1327         for (i = 0; i < ntrec; i++)
1328                 ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
1329         if (numtrec == 0)
1330                 numtrec = ntrec;
1331         cnt = ntrec * TP_BSIZE;
1332         if (zflag)
1333                 cnt += PREFIXSIZE;
1334         rd = 0;
1335 getmore:
1336 #ifdef RRESTORE
1337         if (host)
1338                 i = rmtread(&tapebuf[rd], cnt);
1339         else
1340 #endif
1341                 i = read(mt, &tapebuf[rd], cnt);
1342
1343         /*
1344          * Check for mid-tape short read error.
1345          * If found, skip rest of buffer and start with the next.
1346          */
1347         if (!pipein && numtrec < ntrec && i > 0) {
1348                 Dprintf(stdout, "mid-media short read error.\n");
1349                 numtrec = ntrec;
1350         }
1351         /*
1352          * Handle partial block read.
1353          */
1354         if (pipein && i == 0 && rd > 0)
1355                 i = rd;
1356         else if (i > 0 && i != ntrec * TP_BSIZE) {
1357                 if (pipein) {
1358                         rd += i;
1359                         cnt -= i;
1360                         if (cnt > 0)
1361                                 goto getmore;
1362                         i = rd;
1363                 } else {
1364                         /*
1365                          * Short read. Process the blocks read.
1366                          */
1367                         if (i % TP_BSIZE != 0)
1368                                 Vprintf(stdout,
1369                                     "partial block read: %ld should be %ld\n",
1370                                     (long)i, ntrec * TP_BSIZE);
1371                         numtrec = i / TP_BSIZE;
1372                 }
1373         }
1374         /*
1375          * Handle read error.
1376          */
1377         if (i < 0) {
1378                 fprintf(stderr, "Tape read error while ");
1379                 switch (curfile.action) {
1380                 default:
1381                         fprintf(stderr, "trying to set up tape\n");
1382                         break;
1383                 case UNKNOWN:
1384                         fprintf(stderr, "trying to resynchronize\n");
1385                         break;
1386                 case USING:
1387                         fprintf(stderr, "restoring %s\n", curfile.name);
1388                         break;
1389                 case SKIP:
1390                         fprintf(stderr, "skipping over inode %lu\n",
1391                                 (unsigned long)curfile.ino);
1392                         break;
1393                 }
1394                 if (!yflag && !reply("continue"))
1395                         exit(1);
1396                 i = ntrec * TP_BSIZE;
1397                 memset(tapebuf, 0, (size_t)i);
1398 #ifdef RRESTORE
1399                 if (host)
1400                         seek_failed = (rmtseek(i, 1) < 0);
1401                 else
1402 #endif
1403                         seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
1404
1405                 if (seek_failed) {
1406                         warn("continuation failed");
1407                         if (!yflag && !reply("assume end-of-tape and continue"))
1408                                 exit(1);
1409                         i = 0;
1410                 }
1411         }
1412         /*
1413          * Handle end of tape.
1414          */
1415         if (i == 0) {
1416                 Vprintf(stdout, "End-of-tape encountered\n");
1417                 if (!pipein) {
1418                         newvol = volno + 1;
1419                         volno = 0;
1420                         numtrec = 0;
1421                         getvol(newvol);
1422                         readtape(buf);
1423                         return;
1424                 }
1425                 if (rd % TP_BSIZE != 0)
1426                         panic("partial block read: %d should be %d\n",
1427                                 rd, ntrec * TP_BSIZE);
1428                 terminateinput();
1429                 memmove(&tapebuf[rd], &endoftapemark, TP_BSIZE);
1430         }
1431         blkcnt = 0;
1432         memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
1433         blksread++;
1434         tpblksread++;
1435 }
1436
1437 #ifdef HAVE_ZLIB
1438
1439 /*
1440  * Read a compressed format block from a file or pipe and uncompress it.
1441  * Attempt to handle read errors, and end of file. 
1442  */
1443 static void
1444 readtape_comprfile(char *buf)
1445 {
1446         long rl, size, i, ret;
1447         int newvol; 
1448         struct tapebuf *tpb;
1449
1450         if (blkcnt < numtrec) {
1451                 memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
1452                 blksread++;
1453                 tpblksread++;
1454                 return;
1455         }
1456         /* need to read the next block */
1457         tbufptr = tapebuf;
1458         for (i = 0; i < ntrec; i++)
1459                 ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
1460         numtrec = ntrec;
1461         tpb = (struct tapebuf *) tapebuf;
1462
1463         /* read the block prefix */
1464         ret = read_a_block(mt, tapebuf, PREFIXSIZE, &rl);
1465         if (ret <= 0)
1466                 goto readerr;
1467
1468         /* read the data */
1469         size = tpb->length;
1470         if (size > bufsize)  {
1471                 /* something's wrong */
1472                 Vprintf(stdout, "Prefix size error, max size %d, got %ld\n",
1473                         bufsize, size);
1474                 size = bufsize;
1475                 tpb->length = bufsize;
1476         }
1477         ret = read_a_block(mt, tpb->buf, size, &rl);
1478         if (ret <= 0)
1479                 goto readerr;
1480
1481         tbufptr = decompress_tapebuf(tpb, rl + PREFIXSIZE);
1482         if (tbufptr == NULL) {
1483                 msg_read_error("File decompression error while");
1484                 if (!yflag && !reply("continue"))
1485                         exit(1);
1486                 memset(tapebuf, 0, bufsize);
1487                 tbufptr = tapebuf;
1488         }
1489
1490         blkcnt = 0;
1491         memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
1492         blksread++;
1493         tpblksread++;
1494         return;
1495
1496 readerr:
1497         /* Errors while reading from a file or pipe are catastrophic. Since
1498          * there are no block boundaries, it's impossible to bypass the
1499          * block in error and find the start of the next block.
1500          */
1501         if (ret == 0) {
1502                 /* It's possible to have multiple input files using -M
1503                  * and -f file1,file2...
1504                  */
1505                 Vprintf(stdout, "End-of-File encountered\n");
1506                 if (!pipein) {
1507                         newvol = volno + 1;
1508                         volno = 0;
1509                         numtrec = 0;
1510                         getvol(newvol);
1511                         readtape(buf);
1512                         return;
1513                 }
1514         }
1515         msg_read_error("Read error while");
1516         /* if (!yflag && !reply("continue")) */
1517                 exit(1);
1518 }
1519
1520 /*
1521  * Read compressed data from a tape and uncompress it.
1522  * Handle read errors, and end of media.
1523  * Since a tape consists of separate physical blocks, we try
1524  * to recover from errors by repositioning the tape to the next
1525  * block.
1526  */
1527 static void
1528 readtape_comprtape(char *buf)
1529 {
1530         long rl, size, i;
1531         int ret, newvol;
1532         struct tapebuf *tpb;
1533         struct mtop tcom;
1534
1535         if (blkcnt < numtrec) {
1536                 memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
1537                 blksread++;
1538                 tpblksread++;
1539                 return;
1540         }
1541         /* need to read the next block */
1542         tbufptr = tapebuf;
1543         for (i = 0; i < ntrec; i++)
1544                 ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
1545         numtrec = ntrec;
1546         tpb = (struct tapebuf *) tapebuf;
1547
1548         /* read the block */
1549         size = bufsize + PREFIXSIZE;
1550         ret = read_a_block(mt, tapebuf, size, &rl);
1551         if (ret <= 0)
1552                 goto readerr;
1553
1554         tbufptr = decompress_tapebuf(tpb, rl);
1555         if (tbufptr == NULL) {
1556                 msg_read_error("Tape decompression error while");
1557                 if (!yflag && !reply("continue"))
1558                         exit(1);
1559                 memset(tapebuf, 0, PREFIXSIZE + bufsize);
1560                 tbufptr = tapebuf;
1561         }
1562         goto moverecord;
1563
1564 readerr:
1565         /* Handle errors: EOT switches to the next volume, other errors
1566          * attempt to position the tape to the next block.
1567          */
1568         if (ret == 0) {
1569                 Vprintf(stdout, "End-of-tape encountered\n");
1570                 newvol = volno + 1;
1571                 volno = 0;
1572                 numtrec = 0;
1573                 getvol(newvol);
1574                 readtape(buf);
1575                 return;
1576         }
1577
1578         msg_read_error("Tape read error while");
1579         if (!yflag && !reply("continue"))
1580                 exit(1);
1581         memset(tapebuf, 0, PREFIXSIZE + bufsize);
1582         tbufptr = tapebuf;
1583
1584 #ifdef RRESTORE
1585         if (host)
1586                 rl = rmtioctl(MTFSR, 1);
1587         else
1588 #endif
1589         {
1590                 tcom.mt_op = MTFSR;
1591                 tcom.mt_count = 1;
1592                 rl = ioctl(mt, MTIOCTOP, &tcom);
1593         }
1594
1595         if (rl < 0) {
1596                 warn("continuation failed");
1597                 if (!yflag && !reply("assume end-of-tape and continue"))
1598                         exit(1);
1599                 ret = 0;         /* end of tape */
1600                 goto readerr;
1601         }
1602
1603 moverecord:
1604         blkcnt = 0;
1605         memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
1606         blksread++;
1607         tpblksread++;
1608 }
1609
1610 /*
1611  *  Decompress a struct tapebuf into a buffer. readsize is the size read
1612  *  from the tape/file and is used for error messages. Returns a pointer
1613  *  to the location of the uncompressed buffer or NULL on errors.
1614  *  Adjust numtrec and complain for a short block.
1615  */
1616 static char *
1617 decompress_tapebuf(struct tapebuf *tpbin, int readsize)
1618 {
1619         /* If zflag is on, all blocks have a struct tapebuf prefix */
1620         /* zflag gets set in setup() from the dump header          */
1621         int cresult, blocklen;        
1622         unsigned long worklen;
1623         char *output = NULL,*reason = NULL, *lengtherr = NULL;              
1624        
1625         /* build a length error message */
1626         blocklen = tpbin->length;
1627         if (readsize < blocklen + PREFIXSIZE)
1628                 lengtherr = "short";
1629         else
1630                 if (readsize > blocklen + PREFIXSIZE)
1631                         lengtherr = "long";
1632
1633         worklen = comprlen;
1634         cresult = Z_OK;
1635         if (tpbin->compressed) {
1636                 /* uncompress whatever we read, if it fails, complain later */
1637                 cresult = uncompress(comprbuf, &worklen, tpbin->buf, blocklen);
1638                 output = comprbuf;
1639         }
1640         else {
1641                 output = tpbin->buf;
1642                 worklen = blocklen;
1643         }
1644         switch (cresult) {
1645                 case Z_OK:
1646                         if (worklen != ntrec * TP_BSIZE) {
1647                                 /* short block, shouldn't happen, but... */
1648                                 reason = "length mismatch";
1649                                 if (worklen % TP_BSIZE == 0)
1650                                         numtrec = worklen / TP_BSIZE;
1651                         }
1652                         break;
1653                 case Z_MEM_ERROR:
1654                         reason = "not enough memory";
1655                         break;
1656                 case Z_BUF_ERROR:
1657                         reason = "buffer too small";
1658                         break;
1659                 case Z_DATA_ERROR:
1660                         reason = "data error";
1661                         break;
1662                 default:
1663                         reason = "unknown";
1664         } /*switch */
1665         if (reason) {
1666                 if (lengtherr)
1667                         fprintf(stderr, "%s compressed block: %d expected: %d\n",
1668                                 lengtherr, readsize, tpbin->length + PREFIXSIZE);
1669                 fprintf(stderr, "decompression error, block %ld: %s\n",
1670                         tpblksread+1, reason);
1671                 if (cresult != Z_OK)   output = NULL;
1672         }
1673         return output;
1674 }
1675
1676 /*
1677  * Print an error message for a read error.
1678  * This was exteracted from the original readtape().
1679  */
1680 static void
1681 msg_read_error(char *m)
1682 {
1683         switch (curfile.action) {
1684                 default:
1685                         fprintf(stderr, "%s trying to set up tape\n", m);
1686                         break;
1687                 case UNKNOWN:
1688                         fprintf(stderr, "%s trying to resynchronize\n", m);
1689                         break;
1690                 case USING:
1691                         fprintf(stderr, "%s restoring %s\n", m, curfile.name);
1692                         break;
1693                 case SKIP:
1694                         fprintf(stderr, "%s skipping over inode %lu\n", m,
1695                                 (unsigned long)curfile.ino);
1696                         break;
1697         }
1698 }
1699 #endif /* HAVE_ZLIB */
1700
1701 /*
1702  * Read the first block and set the blocksize from its length. Test
1703  * if the block looks like a compressed dump tape. setup() will make
1704  * the final determination by checking the compressed flag if gethead()
1705  * finds a valid header. The test here is necessary to offset the buffer
1706  * by the size of the compressed prefix. zflag is set here so that
1707  * readtape_set can set the correct function pointer for readtape().
1708  * Note that the first block of each tape/file will not be compressed.
1709  */ 
1710 static void
1711 findtapeblksize(void)
1712 {
1713         long i;
1714         size_t len;
1715         struct tapebuf *tpb = (struct tapebuf *) tapebuf;
1716         struct s_spcl *spclpt = (struct s_spcl *) tpb->buf;
1717
1718         for (i = 0; i < ntrec; i++)
1719                 ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
1720         blkcnt = 0;
1721         tbufptr = tapebuf;
1722         /*
1723          * For a pipe or file, read in the first record. For a tape, read
1724          * the first block.
1725          */
1726         len = magtapein ? bufsize + PREFIXSIZE: TP_BSIZE;
1727
1728         if (read_a_block(mt, tapebuf, len, &i) <= 0)
1729                 errx(1, "Tape read error on first record");
1730
1731         /*
1732          * If the input is from a file or a pipe, we read TP_BSIZE
1733          * bytes looking for a compressed dump header, we then
1734          * need to read in the rest of the record, as determined by
1735          * tpb->length or bufsize. The first block of the dump is
1736          * guaranteed to not be compressed so we look at the header.
1737          */
1738         if (!magtapein) {
1739                 if (tpb->length % TP_BSIZE == 0
1740                     && tpb->length <= bufsize
1741                     && tpb->compressed == 0
1742                     && spclpt->c_type == TS_TAPE 
1743                     && spclpt->c_flags & DR_COMPRESSED) {
1744                         /* Looks like it's a compressed dump block prefix, */
1745                         /* read in the rest of the block based on tpb->length. */
1746                         len = tpb->length - TP_BSIZE + PREFIXSIZE;
1747                         if (read_a_block(mt, tapebuf+TP_BSIZE, len, &i) <= 0
1748                             || i != len)
1749                                 errx(1,"Error reading dump file header");
1750                         tbufptr = tpb->buf;
1751                         numtrec = ntrec = tpb->length / TP_BSIZE;
1752                         zflag = 1;   
1753                 }
1754                 else {
1755                         /* read in the rest of the block based on bufsize */
1756                         len = bufsize - TP_BSIZE;
1757                         if (read_a_block(mt, tapebuf+TP_BSIZE, len, &i) <= 0
1758                             || i != len)
1759                                 errx(1,"Error reading dump file header");
1760                         tbufptr = tapebuf;
1761                         numtrec = ntrec;
1762                 }
1763                 Vprintf(stdout, "Input block size is %ld\n", ntrec);
1764                 return;
1765         }
1766
1767         /*
1768          * If the input is a tape, we tried to read PREFIXSIZE +
1769          * ntrec * TP_BSIZE bytes. If it's not a compressed dump tape
1770          * or the value of ntrec is too large, we have read less than
1771          * what we asked for; adjust the value of ntrec and test for
1772          * a compressed dump tape prefix.
1773          */
1774
1775         if (i % TP_BSIZE != 0) {
1776                 if (i % TP_BSIZE == PREFIXSIZE
1777                     && tpb->compressed == 0
1778                     && spclpt->c_type == TS_TAPE
1779                     && spclpt->c_flags & DR_COMPRESSED) {
1780
1781                         zflag = 1;
1782                         tbufptr = tpb->buf;
1783                         if (tpb->length > bufsize)
1784                                 errx(1, "Tape blocksize is too large, use "
1785                                         "\'-b %d\' ", tpb->length / TP_BSIZE);
1786                 }
1787                 else
1788                         errx(1, "Tape block size (%ld) is not a multiple of dump block size (%d)",
1789                                 i, TP_BSIZE);
1790         }
1791         ntrec = i / TP_BSIZE;
1792         numtrec = ntrec;
1793         Vprintf(stdout, "Tape block size is %ld\n", ntrec);
1794 }
1795
1796 /*
1797  * Read a block of data handling all of the messy details.
1798  */
1799 static int read_a_block(int fd, void *buf, size_t len, long *lengthread)
1800 {
1801         long i = 1, size;
1802
1803         size = len;
1804         while (size > 0) {
1805 #ifdef RRESTORE
1806                 if (host)
1807                         i = rmtread(buf, size);
1808                 else
1809 #endif
1810                         i = read(fd, buf, size);                 
1811
1812                 if (i <= 0)
1813                         break; /* EOD or error */
1814                 size -= i;
1815                 if (magtapein)
1816                         break; /* block at a time for mt */
1817                 buf += i;
1818         }
1819         *lengthread = len - size;
1820         return i;
1821 }
1822
1823 void
1824 closemt(void)
1825 {
1826
1827         if (mt < 0)
1828                 return;
1829 #ifdef RRESTORE
1830         if (host)
1831                 rmtclose();
1832         else
1833 #endif
1834                 (void) close(mt);
1835 }
1836
1837 /*
1838  * Read the next block from the tape.
1839  * Check to see if it is one of several vintage headers.
1840  * If it is an old style header, convert it to a new style header.
1841  * If it is not any valid header, return an error.
1842  */
1843 static int
1844 gethead(struct s_spcl *buf)
1845 {
1846         int32_t i;
1847         union {
1848                 quad_t  qval;
1849                 int32_t val[2];
1850         } qcvt;
1851         union u_ospcl {
1852                 char dummy[TP_BSIZE];
1853                 struct  s_ospcl {
1854                         int32_t c_type;
1855                         int32_t c_date;
1856                         int32_t c_ddate;
1857                         int32_t c_volume;
1858                         int32_t c_tapea;
1859                         u_int16_t c_inumber;
1860                         int32_t c_magic;
1861                         int32_t c_checksum;
1862                         struct odinode {
1863                                 u_int16_t odi_mode;
1864                                 u_int16_t odi_nlink;
1865                                 u_int16_t odi_uid;
1866                                 u_int16_t odi_gid;
1867                                 int32_t odi_size;
1868                                 int32_t odi_rdev;
1869                                 char    odi_addr[36];
1870                                 int32_t odi_atime;
1871                                 int32_t odi_mtime;
1872                                 int32_t odi_ctime;
1873                         } c_dinode;
1874                         int32_t c_count;
1875                         char    c_addr[256];
1876                 } s_ospcl;
1877         } u_ospcl;
1878
1879         if (!cvtflag) {
1880                 readtape((char *)buf);
1881                 if (buf->c_magic != NFS_MAGIC) {
1882                         if (swabi(buf->c_magic) != NFS_MAGIC)
1883                                 return (FAIL);
1884                         if (!Bcvt) {
1885                                 Vprintf(stdout, "Note: Doing Byte swapping\n");
1886                                 Bcvt = 1;
1887                         }
1888                 }
1889                 if (checksum((int *)buf) == FAIL)
1890                         return (FAIL);
1891                 if (Bcvt)
1892                         swabst((u_char *)"8i4s31i528bi192b2i", (u_char *)buf);
1893                 goto good;
1894         }
1895         readtape((char *)(&u_ospcl.s_ospcl));
1896         memset((char *)buf, 0, (long)TP_BSIZE);
1897         buf->c_type = u_ospcl.s_ospcl.c_type;
1898         buf->c_date = u_ospcl.s_ospcl.c_date;
1899         buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
1900         buf->c_volume = u_ospcl.s_ospcl.c_volume;
1901         buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
1902         buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
1903         buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
1904         buf->c_magic = u_ospcl.s_ospcl.c_magic;
1905         buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
1906         buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
1907         buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
1908         buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
1909         buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
1910         buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
1911 #ifdef  __linux__
1912         buf->c_dinode.di_atime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_atime;
1913         buf->c_dinode.di_mtime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime;
1914         buf->c_dinode.di_ctime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_ctime;
1915 #else   /* __linux__ */
1916         buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
1917         buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
1918         buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
1919 #endif  /* __linux__ */
1920         buf->c_count = u_ospcl.s_ospcl.c_count;
1921         memmove(buf->c_addr, u_ospcl.s_ospcl.c_addr, (long)256);
1922         if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
1923             checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
1924                 return(FAIL);
1925         buf->c_magic = NFS_MAGIC;
1926
1927 good:
1928         if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
1929             (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
1930                 qcvt.qval = buf->c_dinode.di_size;
1931                 if (qcvt.val[0] || qcvt.val[1]) {
1932                         printf("Note: Doing Quad swapping\n");
1933                         Qcvt = 1;
1934                 }
1935         }
1936         if (Qcvt) {
1937                 qcvt.qval = buf->c_dinode.di_size;
1938                 i = qcvt.val[1];
1939                 qcvt.val[1] = qcvt.val[0];
1940                 qcvt.val[0] = i;
1941                 buf->c_dinode.di_size = qcvt.qval;
1942         }
1943         readmapflag = 0;
1944
1945         switch (buf->c_type) {
1946
1947         case TS_CLRI:
1948         case TS_BITS:
1949                 /*
1950                  * Have to patch up missing information in bit map headers
1951                  */
1952                 buf->c_inumber = 0;
1953                 buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
1954                 if (buf->c_count > TP_NINDIR)
1955                         readmapflag = 1;
1956                 else 
1957                         for (i = 0; i < buf->c_count; i++)
1958                                 buf->c_addr[i]++;
1959                 break;
1960
1961         case TS_TAPE:
1962                 if ((buf->c_flags & DR_NEWINODEFMT) == 0)
1963                         oldinofmt = 1;
1964                 /* fall through */
1965         case TS_END:
1966                 buf->c_inumber = 0;
1967                 break;
1968
1969         case TS_INODE:
1970         case TS_ADDR:
1971                 break;
1972
1973         default:
1974                 panic("gethead: unknown inode type %d\n", buf->c_type);
1975                 break;
1976         }
1977         /*
1978          * If we are restoring a filesystem with old format inodes,
1979          * copy the uid/gid to the new location.
1980          */
1981         if (oldinofmt) {
1982                 buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
1983                 buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
1984         }
1985         if (dflag)
1986                 accthdr(buf);
1987         return(GOOD);
1988 }
1989
1990 /*
1991  * Check that a header is where it belongs and predict the next header
1992  */
1993 static void
1994 accthdr(struct s_spcl *header)
1995 {
1996         static ino_t previno = 0x7fffffff;
1997         static int prevtype;
1998         static long predict;
1999         long blks, i;
2000
2001         if (header->c_type == TS_TAPE) {
2002                 fprintf(stderr, "Volume header (%s inode format) ",
2003                     oldinofmt ? "old" : "new");
2004                 if (header->c_firstrec)
2005                         fprintf(stderr, "begins with record %d",
2006                                 header->c_firstrec);
2007                 fprintf(stderr, "\n");
2008                 previno = 0x7fffffff;
2009                 return;
2010         }
2011         if (previno == 0x7fffffff)
2012                 goto newcalc;
2013         switch (prevtype) {
2014         case TS_BITS:
2015                 fprintf(stderr, "Dumped inodes map header");
2016                 break;
2017         case TS_CLRI:
2018                 fprintf(stderr, "Used inodes map header");
2019                 break;
2020         case TS_INODE:
2021                 fprintf(stderr, "File header, ino %lu", (unsigned long)previno);
2022                 break;
2023         case TS_ADDR:
2024                 fprintf(stderr, "File continuation header, ino %ld", (long)previno);
2025                 break;
2026         case TS_END:
2027                 fprintf(stderr, "End of tape header");
2028                 break;
2029         }
2030         if (predict != blksread - 1)
2031                 fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
2032                         predict, blksread - 1);
2033         fprintf(stderr, "\n");
2034 newcalc:
2035         blks = 0;
2036         if (header->c_type != TS_END)
2037                 for (i = 0; i < header->c_count; i++)
2038                         if (readmapflag || header->c_addr[i] != 0)
2039                                 blks++;
2040         predict = blks;
2041         blksread = 0;
2042         prevtype = header->c_type;
2043         previno = header->c_inumber;
2044 }
2045
2046 /*
2047  * Find an inode header.
2048  * Complain if had to skip, and complain is set.
2049  */
2050 static void
2051 findinode(struct s_spcl *header)
2052 {
2053         static long skipcnt = 0;
2054         long i;
2055         char buf[TP_BSIZE];
2056
2057         curfile.name = "<name unknown>";
2058         curfile.action = UNKNOWN;
2059         curfile.dip = NULL;
2060         curfile.ino = 0;
2061         do {
2062                 if (header->c_magic != NFS_MAGIC) {
2063                         skipcnt++;
2064                         while (gethead(header) == FAIL ||
2065                             header->c_date != dumpdate)
2066                                 skipcnt++;
2067                 }
2068                 switch (header->c_type) {
2069
2070                 case TS_ADDR:
2071                         /*
2072                          * Skip up to the beginning of the next record
2073                          */
2074                         for (i = 0; i < header->c_count; i++)
2075                                 if (header->c_addr[i])
2076                                         readtape(buf);
2077                         while (gethead(header) == FAIL ||
2078                             header->c_date != dumpdate)
2079                                 skipcnt++;
2080                         break;
2081
2082                 case TS_INODE:
2083                         curfile.dip = &header->c_dinode;
2084                         curfile.ino = header->c_inumber;
2085                         break;
2086
2087                 case TS_END:
2088                         curfile.ino = maxino;
2089                         break;
2090
2091                 case TS_CLRI:
2092                         curfile.name = "<file removal list>";
2093                         break;
2094
2095                 case TS_BITS:
2096                         curfile.name = "<file dump list>";
2097                         break;
2098
2099                 case TS_TAPE:
2100                         panic("unexpected tape header\n");
2101                         /* NOTREACHED */
2102
2103                 default:
2104                         panic("unknown tape header type %d\n", spcl.c_type);
2105                         /* NOTREACHED */
2106
2107                 }
2108         } while (header->c_type == TS_ADDR);
2109         if (skipcnt > 0)
2110                 fprintf(stderr, "resync restore, skipped %ld blocks\n",
2111                     skipcnt);
2112         skipcnt = 0;
2113 }
2114
2115 static int
2116 checksum(int *buf)
2117 {
2118         register int i, j;
2119
2120         j = sizeof(union u_spcl) / sizeof(int);
2121         i = 0;
2122         if(!Bcvt) {
2123                 do
2124                         i += *buf++;
2125                 while (--j);
2126         } else {
2127                 /* What happens if we want to read restore tapes
2128                         for a 16bit int machine??? */
2129                 do
2130                         i += swabi(*buf++);
2131                 while (--j);
2132         }
2133
2134         if (i != CHECKSUM) {
2135                 fprintf(stderr, "Checksum error %o, inode %lu file %s\n", i,
2136                         (unsigned long)curfile.ino, curfile.name);
2137                 return(FAIL);
2138         }
2139         return(GOOD);
2140 }
2141
2142 #ifdef RRESTORE
2143 #ifdef __STDC__
2144 #include <stdarg.h>
2145 #else
2146 #include <varargs.h>
2147 #endif
2148
2149 void
2150 #ifdef __STDC__
2151 msg(const char *fmt, ...)
2152 #else
2153 msg(fmt, va_alist)
2154         char *fmt;
2155         va_dcl
2156 #endif
2157 {
2158         va_list ap;
2159 #ifdef __STDC__
2160         va_start(ap, fmt);
2161 #else
2162         va_start(ap);
2163 #endif
2164         (void)vfprintf(stderr, fmt, ap);
2165         va_end(ap);
2166 }
2167 #endif /* RRESTORE */
2168
2169 static u_char *
2170 swab16(u_char *sp, int n)
2171 {
2172         char c;
2173
2174         while (--n >= 0) {
2175                 c = sp[0]; sp[0] = sp[1]; sp[1] = c;
2176                 sp += 2;
2177         }
2178         return (sp);
2179 }
2180
2181 static u_char *
2182 swab32(u_char *sp, int n)
2183 {
2184         char c;
2185
2186         while (--n >= 0) {
2187                 c = sp[0]; sp[0] = sp[3]; sp[3] = c;
2188                 c = sp[1]; sp[1] = sp[2]; sp[2] = c;
2189                 sp += 4;
2190         }
2191         return (sp);
2192 }
2193
2194 static u_char *
2195 swab64(u_char *sp, int n)
2196 {
2197         char c;
2198
2199         while (--n >= 0) {
2200                 c = sp[0]; sp[0] = sp[7]; sp[7] = c;
2201                 c = sp[1]; sp[1] = sp[6]; sp[6] = c;
2202                 c = sp[2]; sp[2] = sp[5]; sp[5] = c;
2203                 c = sp[3]; sp[3] = sp[4]; sp[4] = c;
2204                 sp += 8;
2205         }
2206         return (sp);
2207 }
2208
2209 void
2210 swabst(u_char *cp, u_char *sp)
2211 {
2212         int n = 0;
2213
2214         while (*cp) {
2215                 switch (*cp) {
2216                 case '0': case '1': case '2': case '3': case '4':
2217                 case '5': case '6': case '7': case '8': case '9':
2218                         n = (n * 10) + (*cp++ - '0');
2219                         continue;
2220
2221                 case 's': case 'w': case 'h':
2222                         if (n == 0)
2223                                 n = 1;
2224                         sp = swab16(sp, n);
2225                         break;
2226
2227                 case 'i':
2228                         if (n == 0)
2229                                 n = 1;
2230                         sp = swab32(sp, n);
2231                         break;
2232
2233                 case 'l':
2234                         if (n == 0)
2235                                 n = 1;
2236                         sp = swab64(sp, n);
2237                         break;
2238
2239                 default: /* Any other character, like 'b' counts as byte. */
2240                         if (n == 0)
2241                                 n = 1;
2242                         sp += n;
2243                         break;
2244                 }
2245                 cp++;
2246                 n = 0;
2247         }
2248 }
2249
2250 static u_int
2251 swabi(u_int x)
2252 {
2253         swabst((u_char *)"i", (u_char *)&x);
2254         return (x);
2255 }
2256
2257 #if 0
2258 static u_long
2259 swabl(u_long x)
2260 {
2261         swabst((u_char *)"l", (u_char *)&x);
2262         return (x);
2263 }
2264 #endif