]> git.wh0rd.org - dump.git/blob - restore/main.c
Discard newline when reading the tape path entered by the operator in restore.
[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 <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 *
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.24 2001/04/27 15:22:47 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 bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0;
84 int hflag = 1, mflag = 1, Nflag = 0, zflag = 0;
85 int uflag = 0;
86 int dokerberos = 0;
87 char command = '\0';
88 long dumpnum = 1;
89 long volno = 0;
90 long ntrec;
91 char *dumpmap;
92 char *usedinomap;
93 dump_ino_t maxino;
94 time_t dumptime;
95 time_t dumpdate;
96 FILE *terminal;
97 char *tmpdir;
98 int compare_ignore_not_found;
99 int compare_errors;
100 char filesys[NAMELEN];
101 static const char *stdin_opt = NULL;
102
103 #ifdef __linux__
104 char *__progname;
105 #endif
106
107 static void obsolete __P((int *, char **[]));
108 static void usage __P((void));
109 static void use_stdin __P((const char *));
110
111 int
112 main(int argc, char *argv[])
113 {
114 int ch;
115 dump_ino_t ino;
116 char *inputdev = _PATH_DEFTAPE;
117 char *symtbl = "./restoresymtable";
118 char *p, name[MAXPATHLEN];
119 FILE *filelist = NULL;
120 char fname[MAXPATHLEN];
121 #ifdef USE_QFA
122 tapeposflag = 0;
123 #endif
124 #ifdef USE_QFADEBUG
125 time_t tistart, tiend, titaken;
126 #endif
127
128 /* Temp files should *not* be readable. We set permissions later. */
129 (void) umask(077);
130 filesys[0] = '\0';
131 #ifdef __linux__
132 __progname = argv[0];
133 #endif
134
135 if (argc < 2)
136 usage();
137
138 if ((inputdev = getenv("TAPE")) == NULL)
139 inputdev = _PATH_DEFTAPE;
140 if ((tmpdir = getenv("TMPDIR")) == NULL)
141 tmpdir = _PATH_TMP;
142 if ((tmpdir = strdup(tmpdir)) == NULL)
143 err(1, "malloc tmpdir");
144 for (p = tmpdir + strlen(tmpdir) - 1; p >= tmpdir && *p == '/'; p--)
145 ;
146 obsolete(&argc, &argv);
147 while ((ch = getopt(argc, argv,
148 "b:CcdD:f:hi"
149 #ifdef KERBEROS
150 "k"
151 #endif
152 "mMN"
153 #ifdef USE_QFA
154 "Q:"
155 #endif
156 "Rrs:tT:uvxX:y")) != -1)
157 switch(ch) {
158 case 'b':
159 /* Change default tape blocksize. */
160 bflag = 1;
161 ntrec = strtol(optarg, &p, 10);
162 if (*p)
163 errx(1, "illegal blocksize -- %s", optarg);
164 if (ntrec <= 0)
165 errx(1, "block size must be greater than 0");
166 break;
167 case 'c':
168 cvtflag = 1;
169 break;
170 case 'D':
171 strncpy(filesys, optarg, NAMELEN);
172 filesys[NAMELEN - 1] = '\0';
173 break;
174 case 'T':
175 tmpdir = optarg;
176 break;
177 case 'd':
178 dflag = 1;
179 break;
180 case 'f':
181 if( !strcmp(optarg,"-") )
182 use_stdin("-f");
183 inputdev = optarg;
184 break;
185 case 'h':
186 hflag = 0;
187 break;
188 #ifdef KERBEROS
189 case 'k':
190 dokerberos = 1;
191 break;
192 #endif
193 case 'C':
194 case 'i':
195 case 'R':
196 case 'r':
197 case 't':
198 case 'x':
199 if (command != '\0')
200 errx(1,
201 "%c and %c options are mutually exclusive",
202 ch, command);
203 command = ch;
204 break;
205 case 'm':
206 mflag = 0;
207 break;
208 case 'M':
209 Mflag = 1;
210 break;
211 case 'N':
212 Nflag = 1;
213 break;
214 #ifdef USE_QFA
215 case 'Q':
216 gTapeposfile = optarg;
217 tapeposflag = 1;
218 break;
219 #endif
220 case 's':
221 /* Dumpnum (skip to) for multifile dump tapes. */
222 dumpnum = strtol(optarg, &p, 10);
223 if (*p)
224 errx(1, "illegal dump number -- %s", optarg);
225 if (dumpnum <= 0)
226 errx(1, "dump number must be greater than 0");
227 break;
228 case 'u':
229 uflag = 1;
230 break;
231 case 'v':
232 vflag = 1;
233 break;
234 case 'X':
235 if( !strcmp(optarg,"-") ) {
236 use_stdin("-X");
237 filelist = stdin;
238 }
239 else
240 if ( !(filelist = fopen(optarg,"r")) )
241 errx(1, "can't open file for reading -- %s", optarg);
242 break;
243 case 'y':
244 yflag = 1;
245 break;
246 default:
247 usage();
248 }
249 argc -= optind;
250 argv += optind;
251
252 if (command == '\0')
253 errx(1, "none of C, i, R, r, t or x options specified");
254
255 #ifdef USE_QFA
256 if (!mflag && tapeposflag)
257 errx(1, "m and Q options are mutually exclusive");
258 #endif
259
260 if (signal(SIGINT, onintr) == SIG_IGN)
261 (void) signal(SIGINT, SIG_IGN);
262 if (signal(SIGTERM, onintr) == SIG_IGN)
263 (void) signal(SIGTERM, SIG_IGN);
264 setlinebuf(stderr);
265
266 atexit(cleanup);
267
268 setinput(inputdev);
269
270 if (argc == 0 && !filelist) {
271 argc = 1;
272 *--argv = ".";
273 }
274
275 #ifdef USE_QFA
276 if (tapeposflag) {
277 msg("reading QFA positions from %s\n", gTapeposfile);
278 if ((gTapeposfp = fopen(gTapeposfile, "r")) == NULL)
279 errx(1, "can't open file for reading -- %s",
280 gTapeposfile);
281 /* start reading header info */
282 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
283 errx(1, "not requested format of -- %s", gTapeposfile);
284 gTps[strlen(gTps) - 1] = 0; /* delete end of line */
285 if (strcmp(gTps, QFA_MAGIC) != 0)
286 errx(1, "not requested format of -- %s", gTapeposfile);
287 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
288 errx(1, "not requested format of -- %s", gTapeposfile);
289 gTps[strlen(gTps) - 1] = 0;
290 if (strcmp(gTps, QFA_VERSION) != 0)
291 errx(1, "not requested format of -- %s", gTapeposfile);
292 /* read dumpdate */
293 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
294 errx(1, "not requested format of -- %s", gTapeposfile);
295 gTps[strlen(gTps) - 1] = 0;
296 /* TODO: check dumpdate from QFA file with current dump file's
297 * dump date */
298 /* if not equal either output warning and continue without QFA
299 * or abort */
300 /* read empty line */
301 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
302 errx(1, "not requested format of -- %s", gTapeposfile);
303 gTps[strlen(gTps) - 1] = 0;
304 /* read table header line */
305 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL)
306 errx(1, "not requested format of -- %s", gTapeposfile);
307 gTps[strlen(gTps) - 1] = 0;
308 /* end reading header info */
309 /* tape position table starts here */
310 gSeekstart = ftell(gTapeposfp); /* remember for later use */
311 }
312 #endif /* USE_QFA */
313
314 switch (command) {
315 /*
316 * Compare contents of tape.
317 */
318 case 'C': {
319 struct STAT stbuf;
320
321 Vprintf(stdout, "Begin compare restore\n");
322 compare_ignore_not_found = 0;
323 compare_errors = 0;
324 setup();
325 printf("filesys = %s\n", filesys);
326 if (STAT(filesys, &stbuf) < 0)
327 err(1, "cannot stat directory %s", filesys);
328 if (chdir(filesys) < 0)
329 err(1, "cannot cd to %s", filesys);
330 compare_ignore_not_found = dumptime > 0;
331 initsymtable((char *)0);
332 extractdirs(0);
333 treescan(".", ROOTINO, nodeupdates);
334 compareleaves();
335 checkrestore();
336 if (compare_errors) {
337 printf("Some files were modified!\n");
338 exit(2);
339 }
340 break;
341 }
342
343 /*
344 * Interactive mode.
345 */
346 case 'i':
347 setup();
348 extractdirs(1);
349 initsymtable(NULL);
350 runcmdshell();
351 break;
352 /*
353 * Incremental restoration of a file system.
354 */
355 case 'r':
356 setup();
357 if (dumptime > 0) {
358 /*
359 * This is an incremental dump tape.
360 */
361 Vprintf(stdout, "Begin incremental restore\n");
362 initsymtable(symtbl);
363 extractdirs(1);
364 removeoldleaves();
365 Vprintf(stdout, "Calculate node updates.\n");
366 treescan(".", ROOTINO, nodeupdates);
367 findunreflinks();
368 removeoldnodes();
369 } else {
370 /*
371 * This is a level zero dump tape.
372 */
373 Vprintf(stdout, "Begin level 0 restore\n");
374 initsymtable((char *)0);
375 extractdirs(1);
376 Vprintf(stdout, "Calculate extraction list.\n");
377 treescan(".", ROOTINO, nodeupdates);
378 }
379 createleaves(symtbl);
380 createlinks();
381 setdirmodes(FORCE);
382 checkrestore();
383 if (dflag) {
384 Vprintf(stdout, "Verify the directory structure\n");
385 treescan(".", ROOTINO, verifyfile);
386 }
387 dumpsymtable(symtbl, (long)1);
388 break;
389 /*
390 * Resume an incremental file system restoration.
391 */
392 case 'R':
393 initsymtable(symtbl);
394 skipmaps();
395 skipdirs();
396 createleaves(symtbl);
397 createlinks();
398 setdirmodes(FORCE);
399 checkrestore();
400 dumpsymtable(symtbl, (long)1);
401 break;
402
403 /* handle file names from either text file (-X) or the command line */
404 #define NEXTFILE(p) \
405 p = NULL; \
406 if (argc) { \
407 --argc; \
408 p = *argv++; \
409 } \
410 else if (filelist) { \
411 if ((p = fgets(fname, MAXPATHLEN, filelist))) { \
412 if ( *p && *(p + strlen(p) - 1) == '\n' ) /* possible null string */ \
413 *(p + strlen(p) - 1) = '\0'; \
414 if ( !*p ) /* skip empty lines */ \
415 continue; \
416 } \
417 }
418
419 /*
420 * List contents of tape.
421 */
422 case 't':
423 setup();
424 extractdirs(0);
425 initsymtable((char *)0);
426 for (;;) {
427 NEXTFILE(p);
428 if (!p)
429 break;
430 canon(p, name, sizeof(name));
431 ino = dirlookup(name);
432 if (ino == 0)
433 continue;
434 treescan(name, ino, listfile);
435 }
436 break;
437 /*
438 * Batch extraction of tape contents.
439 */
440 case 'x':
441 #ifdef USE_QFADEBUG
442 tistart = time(NULL);
443 #endif
444 setup();
445 extractdirs(1);
446 initsymtable((char *)0);
447 for (;;) {
448 NEXTFILE(p);
449 if (!p)
450 break;
451 canon(p, name, sizeof(name));
452 ino = dirlookup(name);
453 if (ino == 0)
454 continue;
455 if (mflag)
456 pathcheck(name);
457 treescan(name, ino, addfile);
458 }
459 createfiles();
460 createlinks();
461 setdirmodes(0);
462 if (dflag)
463 checkrestore();
464 #ifdef USE_QFADEBUG
465 tiend = time(NULL);
466 titaken = tiend - tistart;
467 msg("restore took %d:%02d:%02d\n", titaken / 3600,
468 (titaken % 3600) / 60, titaken % 60);
469 #endif /* USE_QFADEBUG */
470 break;
471 }
472 exit(0);
473 /* NOTREACHED */
474 return 0; /* gcc shut up */
475 }
476
477 static void
478 usage(void)
479 {
480 #ifdef __linux__
481 const char *ext2ver, *ext2date;
482
483 ext2fs_get_library_version(&ext2ver, &ext2date);
484 (void)fprintf(stderr, "%s %s (using libext2fs %s of %s)\n",
485 __progname, _DUMP_VERSION, ext2ver, ext2date);
486 #else
487 (void)fprintf(stderr, "%s %s\n", __progname, _DUMP_VERSION);
488 #endif
489
490 #ifdef KERBEROS
491 #define kerbflag "k"
492 #else
493 #define kerbflag
494 #endif
495
496 #ifdef USE_QFA
497 #define qfaflag "[-Q file] "
498 #else
499 #define qfaflag
500 #endif
501
502 (void)fprintf(stderr,
503 "usage:\t%s%s\n\t%s%s\n\t%s%s\n\t%s%s\n\t%s%s\n\t%s%s\n",
504 __progname, " -C [-c" kerbflag "Mvy] [-b blocksize] [-D filesystem] [-f file] [-s fileno]",
505 __progname, " -i [-ch" kerbflag "mMuvy] [-b blocksize] [-f file] " qfaflag "[-s fileno]",
506 __progname, " -r [-c" kerbflag "Muvy] [-b blocksize] [-f file] [-s fileno] [-T directory]",
507 __progname, " -R [-c" kerbflag "Muvy] [-b blocksize] [-f file] [-s fileno] [-T directory]",
508 __progname, " -t [-ch" kerbflag "Muvy] [-b blocksize] [-f file] " qfaflag "[-s fileno] [-X filelist] [file ...]",
509 __progname, " -x [-ch" kerbflag "mMuvy] [-b blocksize] [-f file] " qfaflag "[-s fileno] [-X filelist] [file ...]");
510 exit(1);
511 }
512
513 /*
514 * obsolete --
515 * Change set of key letters and ordered arguments into something
516 * getopt(3) will like.
517 */
518 static void
519 obsolete(int *argcp, char **argvp[])
520 {
521 int argc, flags;
522 char *ap, **argv, *flagsp = NULL, **nargv, *p = NULL;
523
524 /* Setup. */
525 argv = *argvp;
526 argc = *argcp;
527
528 /* Return if no arguments or first argument has leading dash. */
529 ap = argv[1];
530 if (argc == 1 || *ap == '-')
531 return;
532
533 /* Allocate space for new arguments. */
534 if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
535 (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
536 err(1, "malloc args");
537
538 *nargv++ = *argv;
539 argv += 2, argc -= 2;
540
541 for (flags = 0; *ap; ++ap) {
542 switch (*ap) {
543 case 'b':
544 case 'D':
545 case 'f':
546 case 'Q':
547 case 's':
548 case 'T':
549 case 'X':
550 if (*argv == NULL) {
551 warnx("option requires an argument -- %c", *ap);
552 usage();
553 }
554 if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
555 err(1, "malloc arg");
556 nargv[0][0] = '-';
557 nargv[0][1] = *ap;
558 (void)strcpy(&nargv[0][2], *argv);
559 ++argv;
560 ++nargv;
561 break;
562 default:
563 if (!flags) {
564 *p++ = '-';
565 flags = 1;
566 }
567 *p++ = *ap;
568 break;
569 }
570 }
571
572 /* Terminate flags. */
573 if (flags) {
574 *p = '\0';
575 *nargv++ = flagsp;
576 }
577
578 /* Copy remaining arguments. */
579 while ((*nargv++ = *argv++));
580
581 /* Update argument count. */
582 *argcp = nargv - *argvp - 1;
583 }
584
585 /*
586 * use_stdin --
587 * reserve stdin for opt (avoid conflicts)
588 */
589 void
590 use_stdin(const char *opt)
591 {
592 if (stdin_opt)
593 errx(1, "can't handle standard input for both %s and %s",
594 stdin_opt, opt);
595 stdin_opt = opt;
596 }