xref: /original-bsd/sbin/dump/main.c (revision e58c8952)
1 /*-
2  * Copyright (c) 1980, 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)main.c	8.4 (Berkeley) 04/15/94";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/time.h>
20 #ifdef sunos
21 #include <sys/vnode.h>
22 
23 #include <ufs/inode.h>
24 #include <ufs/fs.h>
25 #else
26 #include <ufs/ffs/fs.h>
27 #include <ufs/ufs/dinode.h>
28 #endif
29 
30 #include <protocols/dumprestore.h>
31 
32 #include <ctype.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <fstab.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #ifdef __STDC__
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #endif
43 
44 #include "dump.h"
45 #include "pathnames.h"
46 
47 #ifndef SBOFF
48 #define SBOFF (SBLOCK * DEV_BSIZE)
49 #endif
50 
51 int	notify = 0;	/* notify operator flag */
52 int	blockswritten = 0;	/* number of blocks written on current tape */
53 int	tapeno = 0;	/* current tape number */
54 int	density = 0;	/* density in bytes/0.1" */
55 int	ntrec = NTREC;	/* # tape blocks in each tape record */
56 int	cartridge = 0;	/* Assume non-cartridge tape */
57 long	dev_bsize = 1;	/* recalculated below */
58 long	blocksperfile;	/* output blocks per file */
59 char	*host = NULL;	/* remote host (if any) */
60 
61 static long numarg __P((int, char *, long, long, int *, char ***));
62 static __dead void missingarg __P((int, char *));
63 
64 int
65 main(argc, argv)
66 	int argc;
67 	char **argv;
68 {
69 	register ino_t ino;
70 	register int dirty;
71 	register struct dinode *dp;
72 	register struct	fstab *dt;
73 	register char *map;
74 	register char *cp;
75 	int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1;
76 	ino_t maxino;
77 
78 	spcl.c_date = 0;
79 	(void)time((time_t *)&spcl.c_date);
80 
81 	tsize = 0;	/* Default later, based on 'c' option for cart tapes */
82 	tape = _PATH_DEFTAPE;
83 	dumpdates = _PATH_DUMPDATES;
84 	temp = _PATH_DTMP;
85 	if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
86 		quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
87 	level = '0';
88 	if (argc == 1) {
89 		(void) fprintf(stderr, "Must specify a key.\n");
90 		Exit(X_ABORT);
91 	}
92 	argv++;
93 	argc -= 2;
94 	for (cp = *argv++; cp != NULL && *cp != '\0'; cp++) {
95 		switch (*cp) {
96 		case '-':
97 			break;
98 
99 		case 'w':
100 			lastdump('w');	/* tell us only what has to be done */
101 			exit(0);
102 
103 		case 'W':		/* what to do */
104 			lastdump('W');	/* tell us state of what is done */
105 			exit(0);	/* do nothing else */
106 
107 		case 'f':		/* output file */
108 			if (argc < 1)
109 				missingarg('f', "output file");
110 			tape = *argv++;
111 			argc--;
112 			break;
113 
114 		case 'd':		/* density, in bits per inch */
115 			density = numarg('d', "density",
116 			    10L, 327670L, &argc, &argv) / 10;
117 			if (density >= 625 && !bflag)
118 				ntrec = HIGHDENSITYTREC;
119 			break;
120 
121 		case 's':		/* tape size, feet */
122 			tsize = numarg('s', "size",
123 			    1L, 0L, &argc, &argv) * 12 * 10;
124 			break;
125 
126 		case 'T':		/* time of last dump */
127 			if (argc < 1)
128 				missingarg('T', "time of last dump");
129 			spcl.c_ddate = unctime(*argv);
130 			if (spcl.c_ddate < 0) {
131 				(void)fprintf(stderr, "bad time \"%s\"\n",
132 				    *argv);
133 				exit(X_ABORT);
134 			}
135 			Tflag = 1;
136 			lastlevel = '?';
137 			argc--;
138 			argv++;
139 			break;
140 
141 		case 'b':		/* blocks per tape write */
142 			ntrec = numarg('b', "number of blocks per write",
143 			    1L, 1000L, &argc, &argv);
144 			break;
145 
146 		case 'B':		/* blocks per output file */
147 			blocksperfile = numarg('B', "number of blocks per file",
148 			    1L, 0L, &argc, &argv);
149 			break;
150 
151 		case 'c':		/* Tape is cart. not 9-track */
152 			cartridge = 1;
153 			break;
154 
155 		/* dump level */
156 		case '0': case '1': case '2': case '3': case '4':
157 		case '5': case '6': case '7': case '8': case '9':
158 			level = *cp;
159 			break;
160 
161 		case 'u':		/* update /etc/dumpdates */
162 			uflag = 1;
163 			break;
164 
165 		case 'n':		/* notify operators */
166 			notify = 1;
167 			break;
168 
169 		case 'h':
170 			honorlevel = numarg('h', "honor level",
171 			    0L, 10L, &argc, &argv);
172 			break;
173 
174 		default:
175 			(void)fprintf(stderr, "bad key '%c'\n", *cp);
176 			exit(X_ABORT);
177 		}
178 	}
179 	if (argc < 1) {
180 		(void)fprintf(stderr, "Must specify disk or filesystem\n");
181 		exit(X_ABORT);
182 	}
183 	disk = *argv++;
184 	argc--;
185 	if (argc >= 1) {
186 		(void)fprintf(stderr, "Unknown arguments to dump:");
187 		while (argc--)
188 			(void)fprintf(stderr, " %s", *argv++);
189 		(void)fprintf(stderr, "\n");
190 		exit(X_ABORT);
191 	}
192 	if (Tflag && uflag) {
193 	        (void)fprintf(stderr,
194 		    "You cannot use the T and u flags together.\n");
195 		exit(X_ABORT);
196 	}
197 	if (strcmp(tape, "-") == 0) {
198 		pipeout++;
199 		tape = "standard output";
200 	}
201 
202 	if (blocksperfile)
203 		blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
204 	else {
205 		/*
206 		 * Determine how to default tape size and density
207 		 *
208 		 *         	density				tape size
209 		 * 9-track	1600 bpi (160 bytes/.1")	2300 ft.
210 		 * 9-track	6250 bpi (625 bytes/.1")	2300 ft.
211 		 * cartridge	8000 bpi (100 bytes/.1")	1700 ft.
212 		 *						(450*4 - slop)
213 		 */
214 		if (density == 0)
215 			density = cartridge ? 100 : 160;
216 		if (tsize == 0)
217 			tsize = cartridge ? 1700L*120L : 2300L*120L;
218 	}
219 
220 	if (index(tape, ':')) {
221 		host = tape;
222 		tape = index(host, ':');
223 		*tape++ = '\0';
224 #ifdef RDUMP
225 		if (rmthost(host) == 0)
226 			exit(X_ABORT);
227 #else
228 		(void)fprintf(stderr, "remote dump not enabled\n");
229 		exit(X_ABORT);
230 #endif
231 	}
232 	(void)setuid(getuid()); /* rmthost() is the only reason to be setuid */
233 
234 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
235 		signal(SIGHUP, sig);
236 	if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
237 		signal(SIGTRAP, sig);
238 	if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
239 		signal(SIGFPE, sig);
240 	if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
241 		signal(SIGBUS, sig);
242 	if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
243 		signal(SIGSEGV, sig);
244 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
245 		signal(SIGTERM, sig);
246 	if (signal(SIGINT, interrupt) == SIG_IGN)
247 		signal(SIGINT, SIG_IGN);
248 
249 	set_operators();	/* /etc/group snarfed */
250 	getfstab();		/* /etc/fstab snarfed */
251 	/*
252 	 *	disk can be either the full special file name,
253 	 *	the suffix of the special file name,
254 	 *	the special name missing the leading '/',
255 	 *	the file system name with or without the leading '/'.
256 	 */
257 	dt = fstabsearch(disk);
258 	if (dt != NULL) {
259 		disk = rawname(dt->fs_spec);
260 		(void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN);
261 		(void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN);
262 	} else {
263 		(void)strncpy(spcl.c_dev, disk, NAMELEN);
264 		(void)strncpy(spcl.c_filesys, "an unlisted file system",
265 		    NAMELEN);
266 	}
267 	(void)strcpy(spcl.c_label, "none");
268 	(void)gethostname(spcl.c_host, NAMELEN);
269 	spcl.c_level = level - '0';
270 	spcl.c_type = TS_TAPE;
271 	if (!Tflag)
272 	        getdumptime();		/* /etc/dumpdates snarfed */
273 
274 	msg("Date of this level %c dump: %s", level,
275 		spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
276  	msg("Date of last level %c dump: %s", lastlevel,
277 		spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate));
278 	msg("Dumping %s ", disk);
279 	if (dt != NULL)
280 		msgtail("(%s) ", dt->fs_file);
281 	if (host)
282 		msgtail("to %s on host %s\n", tape, host);
283 	else
284 		msgtail("to %s\n", tape);
285 
286 	if ((diskfd = open(disk, O_RDONLY)) < 0) {
287 		msg("Cannot open %s\n", disk);
288 		exit(X_ABORT);
289 	}
290 	sync();
291 	sblock = (struct fs *)sblock_buf;
292 	bread(SBOFF, (char *) sblock, SBSIZE);
293 	if (sblock->fs_magic != FS_MAGIC)
294 		quit("bad sblock magic number\n");
295 	dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1);
296 	dev_bshift = ffs(dev_bsize) - 1;
297 	if (dev_bsize != (1 << dev_bshift))
298 		quit("dev_bsize (%d) is not a power of 2", dev_bsize);
299 	tp_bshift = ffs(TP_BSIZE) - 1;
300 	if (TP_BSIZE != (1 << tp_bshift))
301 		quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
302 #ifdef FS_44INODEFMT
303 	if (sblock->fs_inodefmt >= FS_44INODEFMT)
304 		spcl.c_flags |= DR_NEWINODEFMT;
305 #endif
306 	maxino = sblock->fs_ipg * sblock->fs_ncg;
307 	mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE);
308 	usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
309 	dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char));
310 	dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
311 	tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
312 
313 	nonodump = spcl.c_level < honorlevel;
314 
315 	msg("mapping (Pass I) [regular files]\n");
316 	anydirskipped = mapfiles(maxino, &tapesize);
317 
318 	msg("mapping (Pass II) [directories]\n");
319 	while (anydirskipped) {
320 		anydirskipped = mapdirs(maxino, &tapesize);
321 	}
322 
323 	if (pipeout) {
324 		tapesize += 10;	/* 10 trailer blocks */
325 		msg("estimated %ld tape blocks.\n", tapesize);
326 	} else {
327 		double fetapes;
328 
329 		if (blocksperfile)
330 			fetapes = (double) tapesize / blocksperfile;
331 		else if (cartridge) {
332 			/* Estimate number of tapes, assuming streaming stops at
333 			   the end of each block written, and not in mid-block.
334 			   Assume no erroneous blocks; this can be compensated
335 			   for with an artificially low tape size. */
336 			fetapes =
337 			(	  tapesize	/* blocks */
338 				* TP_BSIZE	/* bytes/block */
339 				* (1.0/density)	/* 0.1" / byte */
340 			  +
341 				  tapesize	/* blocks */
342 				* (1.0/ntrec)	/* streaming-stops per block */
343 				* 15.48		/* 0.1" / streaming-stop */
344 			) * (1.0 / tsize );	/* tape / 0.1" */
345 		} else {
346 			/* Estimate number of tapes, for old fashioned 9-track
347 			   tape */
348 			int tenthsperirg = (density == 625) ? 3 : 7;
349 			fetapes =
350 			(	  tapesize	/* blocks */
351 				* TP_BSIZE	/* bytes / block */
352 				* (1.0/density)	/* 0.1" / byte */
353 			  +
354 				  tapesize	/* blocks */
355 				* (1.0/ntrec)	/* IRG's / block */
356 				* tenthsperirg	/* 0.1" / IRG */
357 			) * (1.0 / tsize );	/* tape / 0.1" */
358 		}
359 		etapes = fetapes;		/* truncating assignment */
360 		etapes++;
361 		/* count the dumped inodes map on each additional tape */
362 		tapesize += (etapes - 1) *
363 			(howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
364 		tapesize += etapes + 10;	/* headers + 10 trailer blks */
365 		msg("estimated %ld tape blocks on %3.2f tape(s).\n",
366 		    tapesize, fetapes);
367 	}
368 
369 	/*
370 	 * Allocate tape buffer.
371 	 */
372 	if (!alloctape())
373 		quit("can't allocate tape buffers - try a smaller blocking factor.\n");
374 
375 	startnewtape(1);
376 	(void)time((time_t *)&(tstart_writing));
377 	dumpmap(usedinomap, TS_CLRI, maxino - 1);
378 
379 	msg("dumping (Pass III) [directories]\n");
380 	dirty = 0;		/* XXX just to get gcc to shut up */
381 	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
382 		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
383 			dirty = *map++;
384 		else
385 			dirty >>= 1;
386 		if ((dirty & 1) == 0)
387 			continue;
388 		/*
389 		 * Skip directory inodes deleted and maybe reallocated
390 		 */
391 		dp = getino(ino);
392 		if ((dp->di_mode & IFMT) != IFDIR)
393 			continue;
394 		(void)dumpino(dp, ino);
395 	}
396 
397 	msg("dumping (Pass IV) [regular files]\n");
398 	for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
399 		int mode;
400 
401 		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
402 			dirty = *map++;
403 		else
404 			dirty >>= 1;
405 		if ((dirty & 1) == 0)
406 			continue;
407 		/*
408 		 * Skip inodes deleted and reallocated as directories.
409 		 */
410 		dp = getino(ino);
411 		mode = dp->di_mode & IFMT;
412 		if (mode == IFDIR)
413 			continue;
414 		(void)dumpino(dp, ino);
415 	}
416 
417 	spcl.c_type = TS_END;
418 	for (i = 0; i < ntrec; i++)
419 		writeheader(maxino - 1);
420 	if (pipeout)
421 		msg("DUMP: %ld tape blocks\n",spcl.c_tapea);
422 	else
423 		msg("DUMP: %ld tape blocks on %d volumes(s)\n",
424 		    spcl.c_tapea, spcl.c_volume);
425 	putdumptime();
426 	trewind();
427 	broadcast("DUMP IS DONE!\7\7\n");
428 	msg("DUMP IS DONE\n");
429 	Exit(X_FINOK);
430 	/* NOTREACHED */
431 }
432 
433 /*
434  * Pick up a numeric argument.  It must be nonnegative and in the given
435  * range (except that a vmax of 0 means unlimited).
436  */
437 static long
438 numarg(letter, meaning, vmin, vmax, pargc, pargv)
439 	int letter;
440 	char *meaning;
441 	long vmin, vmax;
442 	int *pargc;
443 	char ***pargv;
444 {
445 	register char *p;
446 	long val;
447 	char *str;
448 
449 	if (--*pargc < 0)
450 		missingarg(letter, meaning);
451 	str = *(*pargv)++;
452 	for (p = str; *p; p++)
453 		if (!isdigit(*p))
454 			goto bad;
455 	val = atol(str);
456 	if (val < vmin || (vmax && val > vmax))
457 		goto bad;
458 	return (val);
459 
460 bad:
461 	(void)fprintf(stderr, "bad '%c' (%s) value \"%s\"\n",
462 	    letter, meaning, str);
463 	exit(X_ABORT);
464 }
465 
466 static __dead void
467 missingarg(letter, meaning)
468 	int letter;
469 	char *meaning;
470 {
471 
472 	(void)fprintf(stderr, "The '%c' flag (%s) requires an argument\n",
473 	    letter, meaning);
474 	exit(X_ABORT);
475 }
476 
477 void
478 sig(signo)
479 	int signo;
480 {
481 	switch(signo) {
482 	case SIGALRM:
483 	case SIGBUS:
484 	case SIGFPE:
485 	case SIGHUP:
486 	case SIGTERM:
487 	case SIGTRAP:
488 		if (pipeout)
489 			quit("Signal on pipe: cannot recover\n");
490 		msg("Rewriting attempted as response to unknown signal.\n");
491 		(void)fflush(stderr);
492 		(void)fflush(stdout);
493 		close_rewind();
494 		exit(X_REWRITE);
495 		/* NOTREACHED */
496 	case SIGSEGV:
497 		msg("SIGSEGV: ABORTING!\n");
498 		(void)signal(SIGSEGV, SIG_DFL);
499 		(void)kill(0, SIGSEGV);
500 		/* NOTREACHED */
501 	}
502 }
503 
504 char *
505 rawname(cp)
506 	char *cp;
507 {
508 	static char rawbuf[MAXPATHLEN];
509 	char *dp = rindex(cp, '/');
510 
511 	if (dp == NULL)
512 		return (NULL);
513 	*dp = '\0';
514 	(void)strcpy(rawbuf, cp);
515 	*dp = '/';
516 	(void)strcat(rawbuf, "/r");
517 	(void)strcat(rawbuf, dp + 1);
518 	return (rawbuf);
519 }
520 
521 #ifdef sunos
522 const char *
523 strerror(errnum)
524 	int errnum;
525 {
526 	extern int sys_nerr;
527 	extern const char *const sys_errlist[];
528 
529 	if (errnum < sys_nerr)
530 		return (sys_errlist[errnum]);
531 	else
532 		return ("bogus errno in strerror");
533 }
534 #endif
535