almost lost changes
[unix-history] / usr / src / sbin / restore / tape.c
CommitLineData
f78e116c
KM
1/* Copyright (c) 1983 Regents of the University of California */
2
3#ifndef lint
4static char sccsid[] = "@(#)tape.c 3.1 (Berkeley) 83/02/18";
5#endif
6
7#include "restore.h"
8#include <dumprestor.h>
9#include <sys/ioctl.h>
10#include <sys/mtio.h>
11#include <setjmp.h>
12#include <stat.h>
13#include <file.h>
14
15long fssize;
16int mt = -1;
17char *magtape;
18int insetup = 0;
19int bct = NTREC+1;
20char tbf[NTREC*TP_BSIZE];
21jmp_buf restart;
22int gettingfile = 0; /* restart has a valid frame */
23
24int ofile;
25char *map;
26char lnkbuf[MAXPATHLEN + 1];
27int pathlen;
28
29/*
30 * Set up an input source
31 */
32setinput(source)
33 char *source;
34{
35#ifdef RRESTOR
36 char *host;
37 char *index();
38
39 host = source;
40 magtape = index(host, ':');
41 if (magtape == 0) {
42nohost:
43 msg("need keyletter ``f'' and device ``host:tape''");
44 done(1);
45 }
46 *magtape++ = '\0';
47 if (rmthost(host) == 0)
48 done(1);
49 setuid(getuid()); /* no longer need or want root privileges */
50#else
51 magtape = source;
52#endif RRESTOR
53}
54
55/*
56 * Verify that the tape drive can be accessed and
57 * that it actually is a dump tape.
58 */
59setup()
60{
61 struct mtop tcom;
62 struct stat stbuf;
63 extern char *ctime();
64 extern int xtrmap(), xtrmapskip();
65
66 vprintf(stdout, "Verify tape and initialize maps\n");
67 insetup = 1;
68#ifdef RRESTOR
69 if ((mt = rmtopen(magtape, 0)) < 0)
70#else
71 if ((mt = open(magtape, 0)) < 0)
72#endif
73 {
74 fprintf(stderr, "%s: cannot open tape\n", magtape);
75 done(1);
76 }
77 if (dumpnum != 1) {
78 tcom.mt_op = MTFSF;
79 tcom.mt_count = dumpnum -1;
80#ifdef RRESTOR
81 rmtioctl(MTFSF,dumpnum - 1);
82#else
83 if (ioctl(mt,MTIOCTOP,&tcom) < 0)
84 perror("ioctl MTFSF");
85#endif
86 }
87 flsht();
88 if (readhdr(&spcl) == 0) {
89 bct--; /* push back this block */
90 cvtflag++;
91 if (readhdr(&spcl) == 0) {
92 fprintf(stderr, "Tape is not a dump tape\n");
93 done(1);
94 }
95 fprintf(stderr, "Converting to new file system format.\n");
96 }
97 vprintf(stdout, "Dump date: %s", ctime(&spcl.c_date));
98 vprintf(stdout, "Dumped from: %s", ctime(&spcl.c_ddate));
99 dumptime = spcl.c_ddate;
100 if (stat(".", &stbuf) < 0) {
101 fprintf(stderr, "cannot stat .\n");
102 done(1);
103 }
104 fssize = stbuf.st_blksize;
105 if (fssize <= 0 || ((fssize - 1) & fssize) != 0) {
106 fprintf(stderr, "bad block size %d\n", fssize);
107 done(1);
108 }
109 if (checkvol(&spcl, (long)1) == 0) {
110 fprintf(stderr, "Tape is not volume 1 of the dump\n");
111 done(1);
112 }
113 if (readhdr(&spcl) == 0 || checktype(&spcl, TS_CLRI) != 1) {
114 fprintf(stderr, "Cannot find file removal list\n");
115 done(1);
116 }
117 maxino = spcl.c_count * TP_BSIZE + 1;
118 map = (char *)calloc(1, (int)howmany(maxino, NBBY));
119 if (map == (char *)NIL)
120 panic("no memory for file removal list\n");
121 curfile.action = USING;
122 getfile(xtrmap, xtrmapskip);
123 clrimap = map;
124 if (checktype(&spcl, TS_BITS) != 1) {
125 fprintf(stderr, "Cannot find file dump list\n");
126 done(1);
127 }
128 map = (char *)calloc(1, (int)howmany(maxino, NBBY));
129 if (map == (char *)NULL)
130 panic("no memory for file dump list\n");
131 curfile.action = USING;
132 getfile(xtrmap, xtrmapskip);
133 dumpmap = map;
134 insetup = 0;
135}
136
137getvol(nextvol)
138 long nextvol;
139{
140 long newvol;
141 union u_spcl tmpspcl;
142# define tmpbuf tmpspcl.s_spcl
143
144 if (dumpnum > 1) {
145 /*
146 * if this is a multi-dump tape we always start with
147 * volume 1, so as to avoid accidentally restoring
148 * from a different dump!
149 */
150 resetmt();
151 dumpnum = 1;
152 volno = 1;
153 goto rbits;
154 }
155again:
156 if (command == 'R' || command == 'r' || curfile.action != SKIP)
157 newvol = nextvol;
158 else
159 newvol = 0;
160 while (newvol <= 0) {
161 fprintf(stderr, "Specify volume #: ");
162 if (gets(tbf) == NULL)
163 return;
164 newvol = atoi(tbf);
165 if (newvol <= 0) {
166 fprintf(stderr,
167 "Volume numbers are positive numerics\n");
168 }
169 }
170 if (newvol == volno)
171 return;
172 closemt();
173 fprintf(stderr, "Mount tape volume %d then type return: ", newvol);
174 while (getchar() != '\n')
175 ;
176#ifdef RRESTOR
177 if ((mt = rmtopen(magtape, 0)) == -1)
178#else
179 if ((mt = open(magtape, 0)) == -1)
180#endif
181 {
182 fprintf(stderr, "Cannot open tape!\n");
183 goto again;
184 }
185 volno = newvol;
186 flsht();
187 if (readhdr(&tmpbuf) == 0) {
188 fprintf(stderr, "tape is not dump tape\n");
189 volno = 0;
190 goto again;
191 }
192 if (checkvol(&tmpbuf, volno) == 0) {
193 fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
194 volno = 0;
195 goto again;
196 }
197rbits:
198 if (curfile.action == USING) {
199 if (volno == 1)
200 panic("active file into volume 1\n");
201 return;
202 }
203 findinode(&spcl, curfile.action == UNKNOWN ? 1 : 0);
204 if (gettingfile) {
205 gettingfile = 0;
206 longjmp(restart, 1);
207 }
208}
209
210extractfile(name)
211 char *name;
212{
213 int mode;
214 time_t timep[2];
215 struct entry *ep;
216 extern int xtrlnkfile(), xtrlnkskip();
217 extern int xtrfile(), xtrskip();
218
219 curfile.name = name;
220 curfile.action = USING;
221 timep[0] = curfile.dip->di_atime;
222 timep[1] = curfile.dip->di_mtime;
223 mode = curfile.dip->di_mode;
224 switch (mode & IFMT) {
225
226 default:
227 fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
228 skipfile();
229 return (FAIL);
230
231 case IFDIR:
232 if (mflag) {
233 ep = lookupname(name);
234 if (ep == NIL || ep->e_flags & EXTRACT)
235 panic("unextracted directory %s\n", name);
236 skipfile();
237 return (GOOD);
238 }
239 vprintf(stdout, "extract file %s\n", name);
240 return (genliteraldir(name, curfile.ino));
241
242 case IFLNK:
243 lnkbuf[0] = '\0';
244 pathlen = 0;
245 getfile(xtrlnkfile, xtrlnkskip);
246 if (pathlen == 0) {
247 vprintf(stdout,
248 "%s: zero length symbolic link (ignored)\n", name);
249 } else if (symlink(lnkbuf, name) < 0) {
250 fprintf(stderr, "%s: cannot create symbolic link\n",
251 name);
252 return (FAIL);
253 } else
254 vprintf(stdout, "extract symbolic link %s\n", name);
255 return (GOOD);
256
257 case IFCHR:
258 case IFBLK:
259 vprintf(stdout, "extract special file %s\n", name);
260 if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
261 fprintf(stderr, "%s: cannot create special file\n",
262 name);
263 skipfile();
264 return (FAIL);
265 }
266 chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
267 chmod(name, mode);
268 skipfile();
269 utime(name, timep);
270 return (GOOD);
271
272 case IFREG:
273 vprintf(stdout, "extract file %s\n", name);
274 if ((ofile = open(name, FWRONLY|FCREATE, 0666)) < 0) {
275 fprintf(stderr, "%s: cannot create file\n", name);
276 skipfile();
277 return (FAIL);
278 }
279 fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
280 fchmod(ofile, mode);
281 getfile(xtrfile, xtrskip);
282 close(ofile);
283 utime(name, timep);
284 return (GOOD);
285 }
286 /* NOTREACHED */
287}
288
289skipfile()
290{
291 extern int null();
292
293 curfile.action = SKIP;
294 getfile(null, null);
295}
296
297/*
298 * Do the file extraction, calling the supplied functions
299 * with the blocks
300 */
301getfile(f1, f2)
302 int (*f2)(), (*f1)();
303{
304 register int i;
305 int curblk = 0;
306 off_t size = spcl.c_dinode.di_size;
307 static char clearedbuf[MAXBSIZE];
308 char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
309
310 if (checktype(&spcl, TS_END) == 1)
311 panic("ran off end of tape\n");
312 if (checktype(&spcl, TS_INODE) == 0)
313 panic("not at beginning of a file\n");
314 if (setjmp(restart) != 0)
315 return;
316 gettingfile++;
317loop:
318 for (i = 0; i < spcl.c_count; i++) {
319 if (spcl.c_addr[i]) {
320 readtape(&buf[curblk++][0]);
321 if (curblk == fssize / TP_BSIZE) {
322 (*f1)(buf, size > TP_BSIZE ?
323 (long) (fssize) :
324 (curblk - 1) * TP_BSIZE + size);
325 curblk = 0;
326 }
327 } else {
328 if (curblk > 0) {
329 (*f1)(buf, size > TP_BSIZE ?
330 (long) (curblk * TP_BSIZE) :
331 (curblk - 1) * TP_BSIZE + size);
332 curblk = 0;
333 }
334 (*f2)(clearedbuf, size > TP_BSIZE ?
335 (long) TP_BSIZE : size);
336 }
337 if ((size -= TP_BSIZE) <= 0) {
338 gethead(&spcl);
339 goto out;
340 }
341 }
342 if (gethead(&spcl) == 0 || checktype(&spcl, TS_ADDR) == 0) {
343 fprintf(stderr, "Missing address (header) block for %s\n",
344 curfile.name);
345 goto out;
346 }
347 goto loop;
348out:
349 if (curblk > 0) {
350 (*f1)(buf, (curblk * TP_BSIZE) + size);
351 }
352 findinode(&spcl, 1);
353 gettingfile = 0;
354}
355
356/*
357 * The next routines are called during file extraction to
358 * put the data into the right form and place.
359 */
360xtrfile(buf, size)
361 char *buf;
362 long size;
363{
364
365 if (write(ofile, buf, (int) size) == -1) {
366 fprintf(stderr, "write error extracting inode %d, name %s\n",
367 curfile.ino, curfile.name);
368 perror("write");
369 done(1);
370 }
371}
372
373xtrskip(buf, size)
374 char *buf;
375 long size;
376{
377
378#ifdef lint
379 buf = buf;
380#endif
381 if (lseek(ofile, size, 1) == (long)-1) {
382 fprintf(stderr, "seek error extracting inode %d, name %s\n",
383 curfile.ino, curfile.name);
384 perror("lseek");
385 done(1);
386 }
387}
388
389xtrlnkfile(buf, size)
390 char *buf;
391 long size;
392{
393
394 pathlen += size;
395 if (pathlen > MAXPATHLEN) {
396 fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
397 curfile.name, lnkbuf, buf, pathlen);
398 done(1);
399 }
400 strcat(lnkbuf, buf);
401}
402
403xtrlnkskip(buf, size)
404 char *buf;
405 long size;
406{
407
408#ifdef lint
409 buf = buf, size = size;
410#endif
411 fprintf(stderr, "unallocated block in symbolic link %s\n",
412 curfile.name);
413 done(1);
414}
415
416xtrmap(buf, size)
417 char *buf;
418 long size;
419{
420
421 bcopy(buf, map, size);
422}
423
424xtrmapskip(buf, size)
425 char *buf;
426 long size;
427{
428
429#ifdef lint
430 buf = buf;
431 size = size;
432#endif
433 panic("hole in map\n");
434}
435
436null() {;}
437
438/*
439 * Do the tape i/o, dealing with volume changes
440 * etc..
441 */
442readtape(b)
443 char *b;
444{
445 register long i;
446 long newvol;
447
448 if (bct >= NTREC) {
449 for (i = 0; i < NTREC; i++)
450 ((struct s_spcl *)&tbf[i*TP_BSIZE])->c_magic = 0;
451 bct = 0;
452#ifdef RRESTOR
453 if ((i = rmtread(tbf, NTREC*TP_BSIZE)) < 0)
454#else
455 if ((i = read(mt, tbf, NTREC*TP_BSIZE)) < 0)
456#endif
457 {
458 fprintf(stderr, "Tape read error while ");
459 switch (curfile.action) {
460 case UNKNOWN:
461 fprintf(stderr, "trying to resyncronize\n");
462 break;
463 case USING:
464 fprintf(stderr, "restoring %s\n", curfile.name);
465 break;
466 case SKIP:
467 fprintf(stderr, "skipping over inode %d\n",
468 curfile.ino);
469 break;
470 }
471 if (!yflag && !reply("continue"))
472 done(1);
473 i = NTREC*TP_BSIZE;
474 bzero(tbf, i);
475#ifdef RRESTOR
476 if (rmtseek(i, 1) < 0)
477#else
478 if (lseek(mt, i, 1) == (long)-1)
479#endif
480 {
481 fprintf(stderr, "continuation failed\n");
482 done(1);
483 }
484 }
485 if (i == 0) {
486 newvol = volno + 1;
487 volno = 0;
488 getvol(newvol);
489 readtape(b);
490 return;
491 }
492 }
493 bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
494}
495
496flsht()
497{
498
499 bct = NTREC+1;
500}
501
502resetmt()
503{
504 struct mtop tcom;
505
506 if (dumpnum > 1)
507 tcom.mt_op = MTBSF;
508 else
509 tcom.mt_op = MTREW;
510 tcom.mt_count = 1;
511 flsht();
512#ifdef RRESTOR
513 if (rmtioctl(tcom.mt_op, 1) == -1) {
514 /* kludge for disk dumps */
515 rmtseek((long)0, 0);
516 }
517#else
518 if (ioctl(mt,MTIOCTOP,&tcom) == -1) {
519 /* kludge for disk dumps */
520 (void) lseek(mt, (long)0, 0);
521 }
522#endif
523 if (dumpnum > 1) {
524#ifdef RRESTOR
525 rmtioctl(MTFSF, 1);
526#else
527 tcom.mt_op = MTFSF;
528 tcom.mt_count = 1;
529 ioctl(mt,MTIOCTOP,&tcom);
530#endif
531 }
532}
533
534closemt()
535{
536 if (mt < 0)
537 return;
538#ifdef RRESTOR
539 rmtclose();
540#else
541 close(mt);
542#endif
543}
544
545checkvol(b, t)
546 struct s_spcl *b;
547 long t;
548{
549
550 if (b->c_volume == t)
551 return(1);
552 return(0);
553}
554
555readhdr(b)
556 struct s_spcl *b;
557{
558
559 if (gethead(b) == 0)
560 return(0);
561 if (checktype(b, TS_TAPE) == 0)
562 return(0);
563 return(1);
564}
565
566/*
567 * read the tape into buf, then return whether or
568 * or not it is a header block.
569 */
570gethead(buf)
571 struct s_spcl *buf;
572{
573 union u_ospcl {
574 char dummy[TP_BSIZE];
575 struct s_ospcl {
576 int c_type;
577 time_t c_date;
578 time_t c_ddate;
579 int c_volume;
580 daddr_t c_tapea;
581 ino_t c_inumber;
582 int c_magic;
583 int c_checksum;
584 struct odinode {
585 unsigned short odi_mode;
586 short odi_nlink;
587 short odi_uid;
588 short odi_gid;
589 off_t odi_size;
590 daddr_t odi_rdev;
591 char odi_addr[36];
592 time_t odi_atime;
593 time_t odi_mtime;
594 time_t odi_ctime;
595 } c_dinode;
596 int c_count;
597 char c_addr[TP_NINDIR];
598 } s_ospcl;
599 } u_ospcl;
600
601 if (!cvtflag) {
602 readtape((char *)buf);
603 if (buf->c_magic != NFS_MAGIC || checksum((int *)buf) == 0)
604 return(0);
605 return(1);
606 }
607 readtape((char *)(&u_ospcl.s_ospcl));
608 bzero((char *)buf, (long)TP_BSIZE);
609 buf->c_type = u_ospcl.s_ospcl.c_type;
610 buf->c_date = u_ospcl.s_ospcl.c_date;
611 buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
612 buf->c_volume = u_ospcl.s_ospcl.c_volume;
613 buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
614 buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
615 buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
616 buf->c_magic = u_ospcl.s_ospcl.c_magic;
617 buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
618 buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
619 buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
620 buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
621 buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
622 buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
623 buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
624 buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
625 buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
626 buf->c_count = u_ospcl.s_ospcl.c_count;
627 bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)TP_NINDIR);
628 if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
629 checksum((int *)(&u_ospcl.s_ospcl)) == 0)
630 return(0);
631 buf->c_magic = NFS_MAGIC;
632 return(1);
633}
634
635/*
636 * Find an inode header.
637 * Complain if had to skip, and complain is set.
638 */
639findinode(header, complain)
640 struct s_spcl *header;
641 int complain;
642{
643 static int skipcnt = 0;
644
645 curfile.name = "<name unknown>";
646 curfile.action = UNKNOWN;
647 curfile.dip = (struct dinode *)NIL;
648 curfile.ino = 0;
649 if (ishead(header) == 0)
650 while (gethead(header) == 0)
651 skipcnt++;
652 for (;;) {
653 if (checktype(header, TS_INODE) == 1) {
654 curfile.dip = &header->c_dinode;
655 curfile.ino = header->c_inumber;
656 break;
657 }
658 if (checktype(header, TS_END) == 1) {
659 curfile.ino = maxino;
660 break;
661 }
662 if (insetup && checktype(header, TS_CLRI) == 1) {
663 curfile.name = "<file removal list>";
664 header->c_dinode.di_size = header->c_count * TP_BSIZE;
665 break;
666 }
667 if (insetup && checktype(header, TS_BITS) == 1) {
668 curfile.name = "<file dump list>";
669 header->c_dinode.di_size = header->c_count * TP_BSIZE;
670 break;
671 }
672 while (gethead(header) == 0)
673 skipcnt++;
674 }
675 if (skipcnt > 0 && complain)
676 fprintf(stderr, "resync restor, skipped %d blocks\n", skipcnt);
677 skipcnt = 0;
678}
679
680/*
681 * return whether or not the buffer contains a header block
682 */
683ishead(buf)
684 struct s_spcl *buf;
685{
686
687 if (buf->c_magic != NFS_MAGIC)
688 return(0);
689 return(1);
690}
691
692checktype(b, t)
693 struct s_spcl *b;
694 int t;
695{
696
697 return(b->c_type == t);
698}
699
700checksum(b)
701 register int *b;
702{
703 register int i, j;
704
705 j = sizeof(union u_spcl) / sizeof(int);
706 i = 0;
707 do
708 i += *b++;
709 while (--j);
710 if (i != CHECKSUM) {
711 fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
712 curfile.ino, curfile.name);
713 return(0);
714 }
715 return(1);
716}
717
718#ifdef RRESTOR
719msg(cp, a1, a2, a3)
720 char *cp;
721{
722
723 fprintf(stderr, cp, a1, a2, a3);
724}
725#endif RRESTOR