]> git.wh0rd.org - dump.git/blob - restore/main.c
-A archive file implementation
[dump.git] / restore / main.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 <stelian@popies.net>, 1999-2000
6 * Stelian Pop <stelian@popies.net> - AlcĂ´ve <www.alcove.com>, 2000-2002
7 */
8
9 /*
10 * Copyright (c) 1983, 1993
11 * The Regents of the University of California. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42 #ifndef lint
43 static const char rcsid[] =
44 "$Id: main.c,v 1.37 2002/01/25 14:59:53 stelian Exp $";
45 #endif /* not lint */
46
47 #include <config.h>
48 #include <compatlfs.h>
49 #include <sys/param.h>
50 #include <sys/stat.h>
51 #include <errno.h>
52
53 #ifdef __linux__
54 #include <sys/time.h>
55 #include <time.h>
56 #ifdef HAVE_EXT2FS_EXT2_FS_H
57 #include <ext2fs/ext2_fs.h>
58 #else
59 #include <linux/ext2_fs.h>
60 #endif
61 #include <bsdcompat.h>
62 #include <signal.h>
63 #include <string.h>
64 #else /* __linux__ */
65 #include <ufs/ufs/dinode.h>
66 #endif /* __linux__ */
67 #include <protocols/dumprestore.h>
68
69 #include <compaterr.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <unistd.h>
73
74 #ifdef __linux__
75 #include <ext2fs/ext2fs.h>
76 #include <getopt.h>
77 #endif
78
79 #include "pathnames.h"
80 #include "restore.h"
81 #include "extern.h"
82
83 int aflag = 0, bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0;
84 int hflag = 1, mflag = 1, Mflag = 0, Nflag = 0, Vflag = 0, zflag = 0;
85 int uflag = 0, lflag = 0, Lflag = 0;
86 char *Afile = NULL;
87 int dokerberos = 0;
88 char command = '\0';
89 long dumpnum = 1;
90 long volno = 0;
91 long ntrec;
92 char *dumpmap;
93 char *usedinomap;
94 dump_ino_t maxino;
95 time_t dumptime;
96 time_t dumpdate;
97 FILE *terminal;
98 char *tmpdir;
99 int compare_ignore_not_found;
100 int compare_errors;
101 char filesys[NAMELEN];
102 static const char *stdin_opt = NULL;
103 char *bot_script = NULL;
104 dump_ino_t volinfo[TP_NINOS];
105
106 #ifdef USE_QFA
107 FILE *gTapeposfp;
108 char *gTapeposfile;
109 char gTps[255];
110 long gSeekstart;
111 int tapeposflag;
112 #endif /* USE_QFA */
113
114 #ifdef __linux__
115 char *__progname;
116 #endif
117
118 static void obsolete __P((int *, char **[]));
119 static void usage __P((void));
120 static void use_stdin __P((const char *));
121
122 int
123 main(int argc, char *argv[])
124 {
125 int ch;
126 dump_ino_t ino;
127 char *inputdev = _PATH_DEFTAPE;
128 char *symtbl = "./restoresymtable";
129 char *p, name[MAXPATHLEN];
130 FILE *filelist = NULL;
131 char fname[MAXPATHLEN];
132 #ifdef USE_QFA
133 tapeposflag = 0;
134 #endif
135 #ifdef USE_QFADEBUG
136 time_t tistart, tiend, titaken;
137 #endif
138
139 /* Temp files should *not* be readable. We set permissions later. */
140 (void) umask(077);
141 filesys[0] = '\0';
142 #ifdef __linux__
143 __progname = argv[0];
144 #endif
145
146 if (argc < 2)
147 usage();
148
149 if ((inputdev = getenv("TAPE")) == NULL)
150 inputdev = _PATH_DEFTAPE;
151 if ((tmpdir = getenv("TMPDIR")) == NULL)
152 tmpdir = _PATH_TMP;
153 if ((tmpdir = strdup(tmpdir)) == NULL)
154 err(1, "malloc tmpdir");
155 for (p = tmpdir + strlen(tmpdir) - 1; p >= tmpdir && *p == '/'; p--)
156 ;
157 obsolete(&argc, &argv);
158 while ((ch = getopt(argc, argv,
159 "aA:b:CcdD:f:F:hi"
160 #ifdef KERBEROS
161 "k"
162 #endif
163 "lL:mMN"
164 #ifdef USE_QFA
165 "Q:"
166 #endif
167 "Rrs:tT:uvVxX:y")) != -1)
168 switch(ch) {
169 case 'a':
170 aflag = 1;
171 break;
172 case 'A':
173 Afile = optarg;
174 aflag = 1;
175 break;
176 case 'b':
177 /* Change default tape blocksize. */
178 bflag = 1;
179 ntrec = strtol(optarg, &p, 10);
180 if (*p)
181 errx(1, "illegal blocksize -- %s", optarg);
182 if (ntrec <= 0)
183 errx(1, "block size must be greater than 0");
184 break;
185 case 'c':
186 cvtflag = 1;
187 break;
188 case 'D':
189 strncpy(filesys, optarg, NAMELEN);
190 filesys[NAMELEN - 1] = '\0';
191 break;
192 case 'T':
193 tmpdir = optarg;
194 break;
195 case 'd':
196 dflag = 1;
197 break;
198 case 'f':
199 if( !strcmp(optarg,"-") )
200 use_stdin("-f");
201 inputdev = optarg;
202 break;
203 case 'F':
204 bot_script = optarg;
205 break;
206 case 'h':
207 hflag = 0;
208 break;
209 #ifdef KERBEROS
210 case 'k':
211 dokerberos = 1;
212 break;
213 #endif
214 case 'C':
215 case 'i':
216 case 'R':
217 case 'r':
218 case 't':
219 case 'x':
220 if (command != '\0')
221 errx(1,
222 "%c and %c options are mutually exclusive",
223 ch, command);
224 command = ch;
225 break;
226 case 'l':
227 lflag = 1;
228 break;
229 case 'L':
230 Lflag = strtol(optarg, &p, 10);
231 if (*p)
232 errx(1, "illegal limit -- %s", optarg);
233 if (Lflag < 0)
234 errx(1, "limit must be greater than 0");
235 break;
236 case 'm':
237 mflag = 0;
238 break;
239 case 'M':
240 Mflag = 1;
241 break;
242 case 'N':
243 Nflag = 1;
244 break;
245 #ifdef USE_QFA
246 case 'Q':
247 gTapeposfile = optarg;
248 tapeposflag = 1;
249 aflag = 1;
250 break;
251 #endif
252 case 's':
253 /* Dumpnum (skip to) for multifile dump tapes. */
254 dumpnum = strtol(optarg, &p, 10);
255 if (*p)
256 errx(1, "illegal dump number -- %s", optarg);
257 if (dumpnum <= 0)
258 errx(1, "dump number must be greater than 0");
259 break;
260 case 'u':
261 uflag = 1;
262 break;
263 case 'v':
264 vflag = 1;
265 break;
266 case 'V':
267 Vflag = 1;
268 break;
269 case 'X':
270 if( !strcmp(optarg,"-") ) {
271 use_stdin("-X");
272 filelist = stdin;
273 }
274 else
275 if ( !(filelist = fopen(optarg,"r")) )
276 errx(1, "can't open file for reading -- %s", optarg);
277 break;
278 case 'y':
279 yflag = 1;
280 break;
281 default:
282 usage();
283 }
284 argc -= optind;
285 argv += optind;
286
287 if (command == '\0')
288 errx(1, "none of C, i, R, r, t or x options specified");
289
290 #ifdef USE_QFA
291 if (!mflag && tapeposflag)
292 errx(1, "m and Q options are mutually exclusive");
293
294 if (tapeposflag && command != 'i' && command != 'x' && command != 't')
295 errx(1, "Q option is not valid for %c command", command);
296 #endif
297
298 if (Afile && command != 'i' && command != 'x' && command != 't')
299 errx(1, "A option is not valid for %c command", command);
300
301 if (signal(SIGINT, onintr) == SIG_IGN)
302 (void) signal(SIGINT, SIG_IGN);
303 if (signal(SIGTERM, onintr) == SIG_IGN)
304 (void) signal(SIGTERM, SIG_IGN);
305 setlinebuf(stderr);
306
307 atexit(cleanup);
308
309 if (command == 'C' && inputdev[0] != '/' && strcmp(inputdev, "-")
310 #ifdef RRESTORE
311 && !strchr(inputdev, ':')
312 #endif
313 ) {
314 /* since we chdir into the directory we are comparing
315 * to, we must retain the full tape path */
316 char wd[MAXPATHLEN], fullpathinput[MAXPATHLEN];
317 if (!getcwd(wd, MAXPATHLEN))
318 err(1, "can't get current directory");
319 snprintf(fullpathinput, MAXPATHLEN, "%s/%s", wd, inputdev);
320 fullpathinput[MAXPATHLEN - 1] = '\0';
321 setinput(fullpathinput);
322 }
323 else
324 setinput(inputdev);
325
326 if (argc == 0 && !filelist) {
327 argc = 1;
328 *--argv = ".";
329 }
330
331 #ifdef USE_QFA
332 if (tapeposflag) {
333 msg("reading QFA positions from %s\n", gTapeposfile);
334 if ((gTapeposfp = fopen(gTapeposfile, "r")) == NULL)
335 errx(1, "can't open file for reading -- %s",
336 gTapeposfile);
337 /* start reading header info */
338 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
339 errx(1, "not requested format of -- %s", gTapeposfile);
340 gTps[strlen(gTps) - 1] = 0; /* delete end of line */
341 if (strcmp(gTps, QFA_MAGIC) != 0)
342 errx(1, "not requested format of -- %s", gTapeposfile);
343 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
344 errx(1, "not requested format of -- %s", gTapeposfile);
345 gTps[strlen(gTps) - 1] = 0;
346 if (strcmp(gTps, QFA_VERSION) != 0)
347 errx(1, "not requested format of -- %s", gTapeposfile);
348 /* read dumpdate */
349 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
350 errx(1, "not requested format of -- %s", gTapeposfile);
351 gTps[strlen(gTps) - 1] = 0;
352 /* TODO: check dumpdate from QFA file with current dump file's
353 * dump date */
354 /* if not equal either output warning and continue without QFA
355 * or abort */
356 /* read empty line */
357 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
358 errx(1, "not requested format of -- %s", gTapeposfile);
359 gTps[strlen(gTps) - 1] = 0;
360 /* read table header line */
361 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
362 errx(1, "not requested format of -- %s", gTapeposfile);
363 gTps[strlen(gTps) - 1] = 0;
364 /* end reading header info */
365 /* tape position table starts here */
366 gSeekstart = ftell(gTapeposfp); /* remember for later use */
367 }
368 #endif /* USE_QFA */
369
370 switch (command) {
371 /*
372 * Compare contents of tape.
373 */
374 case 'C': {
375 struct STAT stbuf;
376
377 Vprintf(stdout, "Begin compare restore\n");
378 compare_ignore_not_found = 0;
379 compare_errors = 0;
380 setup();
381 printf("filesys = %s\n", filesys);
382 if (STAT(filesys, &stbuf) < 0)
383 err(1, "cannot stat directory %s", filesys);
384 if (chdir(filesys) < 0)
385 err(1, "cannot cd to %s", filesys);
386 compare_ignore_not_found = dumptime > 0;
387 initsymtable((char *)0);
388 extractdirs(0);
389 treescan(".", ROOTINO, nodeupdates);
390 compareleaves();
391 checkrestore();
392 if (compare_errors) {
393 printf("Some files were modified!\n");
394 exit(2);
395 }
396 break;
397 }
398
399 /*
400 * Interactive mode.
401 */
402 case 'i':
403 setup();
404 extractdirs(1);
405 initsymtable(NULL);
406 runcmdshell();
407 break;
408 /*
409 * Incremental restoration of a file system.
410 */
411 case 'r':
412 aflag = 1; /* in -r or -R mode, -a is default */
413 setup();
414 if (dumptime > 0) {
415 /*
416 * This is an incremental dump tape.
417 */
418 Vprintf(stdout, "Begin incremental restore\n");
419 initsymtable(symtbl);
420 extractdirs(1);
421 removeoldleaves();
422 Vprintf(stdout, "Calculate node updates.\n");
423 treescan(".", ROOTINO, nodeupdates);
424 findunreflinks();
425 removeoldnodes();
426 } else {
427 /*
428 * This is a level zero dump tape.
429 */
430 Vprintf(stdout, "Begin level 0 restore\n");
431 initsymtable((char *)0);
432 extractdirs(1);
433 Vprintf(stdout, "Calculate extraction list.\n");
434 treescan(".", ROOTINO, nodeupdates);
435 }
436 createleaves(symtbl);
437 createlinks();
438 setdirmodes(FORCE);
439 checkrestore();
440 if (dflag) {
441 Vprintf(stdout, "Verify the directory structure\n");
442 treescan(".", ROOTINO, verifyfile);
443 }
444 dumpsymtable(symtbl, (long)1);
445 break;
446 /*
447 * Resume an incremental file system restoration.
448 */
449 case 'R':
450 aflag = 1; /* in -r or -R mode, -a is default */
451 initsymtable(symtbl);
452 skipmaps();
453 skipdirs();
454 createleaves(symtbl);
455 createlinks();
456 setdirmodes(FORCE);
457 checkrestore();
458 dumpsymtable(symtbl, (long)1);
459 break;
460
461 /* handle file names from either text file (-X) or the command line */
462 #define NEXTFILE(p) \
463 p = NULL; \
464 if (argc) { \
465 --argc; \
466 p = *argv++; \
467 } \
468 else if (filelist) { \
469 if ((p = fgets(fname, MAXPATHLEN, filelist))) { \
470 if ( *p && *(p + strlen(p) - 1) == '\n' ) /* possible null string */ \
471 *(p + strlen(p) - 1) = '\0'; \
472 if ( !*p ) /* skip empty lines */ \
473 continue; \
474 } \
475 }
476
477 /*
478 * List contents of tape.
479 */
480 case 't':
481 setup();
482 extractdirs(0);
483 initsymtable((char *)0);
484 printvolinfo();
485 for (;;) {
486 NEXTFILE(p);
487 if (!p)
488 break;
489 canon(p, name, sizeof(name));
490 ino = dirlookup(name);
491 if (ino == 0)
492 continue;
493 treescan(name, ino, listfile);
494 }
495 break;
496 /*
497 * Batch extraction of tape contents.
498 */
499 case 'x':
500 #ifdef USE_QFADEBUG
501 tistart = time(NULL);
502 #endif
503 setup();
504 extractdirs(1);
505 initsymtable((char *)0);
506 for (;;) {
507 NEXTFILE(p);
508 if (!p)
509 break;
510 canon(p, name, sizeof(name));
511 ino = dirlookup(name);
512 if (ino == 0)
513 continue;
514 if (mflag)
515 pathcheck(name);
516 treescan(name, ino, addfile);
517 }
518 createfiles();
519 createlinks();
520 setdirmodes(0);
521 if (dflag)
522 checkrestore();
523 #ifdef USE_QFADEBUG
524 tiend = time(NULL);
525 titaken = tiend - tistart;
526 msg("restore took %d:%02d:%02d\n", titaken / 3600,
527 (titaken % 3600) / 60, titaken % 60);
528 #endif /* USE_QFADEBUG */
529 break;
530 }
531 exit(0);
532 /* NOTREACHED */
533 return 0; /* gcc shut up */
534 }
535
536 static void
537 usage(void)
538 {
539 char white[MAXPATHLEN];
540 const char *ext2ver, *ext2date;
541
542 memset(white, ' ', MAXPATHLEN);
543 white[MIN(strlen(__progname), MAXPATHLEN - 1)] = '\0';
544
545 #ifdef __linux__
546 ext2fs_get_library_version(&ext2ver, &ext2date);
547 (void)fprintf(stderr, "%s %s (using libext2fs %s of %s)\n",
548 __progname, _DUMP_VERSION, ext2ver, ext2date);
549 #else
550 (void)fprintf(stderr, "%s %s\n", __progname, _DUMP_VERSION);
551 #endif
552
553 #ifdef KERBEROS
554 #define kerbflag "k"
555 #else
556 #define kerbflag
557 #endif
558
559 #ifdef USE_QFA
560 #define qfaflag "[-Q file] "
561 #else
562 #define qfaflag
563 #endif
564
565 fprintf(stderr,
566 "usage:"
567 "\t%s -C [-c" kerbflag "lMvVy] [-b blocksize] [-D filesystem] [-f file]\n"
568 "\t%s [-F script] [-L limit] [-s fileno]\n"
569 "\t%s -i [-ach" kerbflag "lmMuvVy] [-A file] [-b blocksize] [-f file]\n"
570 "\t%s [-F script] " qfaflag "[-s fileno]\n"
571 "\t%s -r [-c" kerbflag "lMuvVy] [-b blocksize] [-f file] [-F script]\n"
572 "\t%s [-s fileno] [-T directory]\n"
573 "\t%s -R [-c" kerbflag "lMuvVy] [-b blocksize] [-f file] [-F script]\n"
574 "\t%s [-s fileno] [-T directory]\n"
575 "\t%s -t [-ch" kerbflag "lMuvVy] [-A file] [-b blocksize] [-f file]\n"
576 "\t%s [-F script] " qfaflag "[-s fileno] [-X filelist] [file ...]\n"
577 "\t%s -x [-ach" kerbflag "lmMuvVy] [-A file] [-b blocksize] [-f file]\n"
578 "\t%s [-F script] " qfaflag "[-s fileno] [-X filelist] [file ...]\n",
579 __progname, white,
580 __progname, white,
581 __progname, white,
582 __progname, white,
583 __progname, white,
584 __progname, white);
585 exit(1);
586 }
587
588 /*
589 * obsolete --
590 * Change set of key letters and ordered arguments into something
591 * getopt(3) will like.
592 */
593 static void
594 obsolete(int *argcp, char **argvp[])
595 {
596 int argc, flags;
597 char *ap, **argv, *flagsp = NULL, **nargv, *p = NULL;
598
599 /* Setup. */
600 argv = *argvp;
601 argc = *argcp;
602
603 /* Return if no arguments or first argument has leading dash. */
604 ap = argv[1];
605 if (argc == 1 || *ap == '-')
606 return;
607
608 /* Allocate space for new arguments. */
609 if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
610 (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
611 err(1, "malloc args");
612
613 *nargv++ = *argv;
614 argv += 2, argc -= 2;
615
616 for (flags = 0; *ap; ++ap) {
617 switch (*ap) {
618 case 'A':
619 case 'b':
620 case 'D':
621 case 'f':
622 case 'F':
623 case 'L':
624 case 'Q':
625 case 's':
626 case 'T':
627 case 'X':
628 if (*argv == NULL) {
629 warnx("option requires an argument -- %c", *ap);
630 usage();
631 }
632 if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
633 err(1, "malloc arg");
634 nargv[0][0] = '-';
635 nargv[0][1] = *ap;
636 (void)strcpy(&nargv[0][2], *argv);
637 ++argv;
638 ++nargv;
639 break;
640 default:
641 if (!flags) {
642 *p++ = '-';
643 flags = 1;
644 }
645 *p++ = *ap;
646 break;
647 }
648 }
649
650 /* Terminate flags. */
651 if (flags) {
652 *p = '\0';
653 *nargv++ = flagsp;
654 }
655
656 /* Copy remaining arguments. */
657 while ((*nargv++ = *argv++));
658
659 /* Update argument count. */
660 *argcp = nargv - *argvp - 1;
661 }
662
663 /*
664 * use_stdin --
665 * reserve stdin for opt (avoid conflicts)
666 */
667 void
668 use_stdin(const char *opt)
669 {
670 if (stdin_opt)
671 errx(1, "can't handle standard input for both %s and %s",
672 stdin_opt, opt);
673 stdin_opt = opt;
674 }