xref: /freebsd/sbin/dump/traverse.c (revision a3e8fd0b7f663db7eafff527d5c3ca3bcfa8a537)
1 /*-
2  * Copyright (c) 1980, 1988, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)traverse.c	8.7 (Berkeley) 6/15/95";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41 
42 #include <sys/param.h>
43 #include <sys/stat.h>
44 
45 #include <ufs/ufs/dir.h>
46 #include <ufs/ufs/dinode.h>
47 #include <ufs/ffs/fs.h>
48 
49 #include <protocols/dumprestore.h>
50 
51 #include <ctype.h>
52 #include <errno.h>
53 #include <inttypes.h>
54 #include <limits.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <timeconv.h>
59 #include <unistd.h>
60 
61 #include "dump.h"
62 
63 union dinode {
64 	struct ufs1_dinode dp1;
65 	struct ufs2_dinode dp2;
66 };
67 #define	DIP(dp, field) \
68 	((sblock->fs_magic == FS_UFS1_MAGIC) ? \
69 	(dp)->dp1.field : (dp)->dp2.field)
70 
71 #define	HASDUMPEDFILE	0x1
72 #define	HASSUBDIRS	0x2
73 
74 static	int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size,
75     long *tapesize, int nodump);
76 static	void dmpindir(ino_t ino, ufs2_daddr_t blk, int level, off_t *size);
77 static	int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize,
78     long *tapesize, int nodump);
79 
80 /*
81  * This is an estimation of the number of TP_BSIZE blocks in the file.
82  * It estimates the number of blocks in files with holes by assuming
83  * that all of the blocks accounted for by di_blocks are data blocks
84  * (when some of the blocks are usually used for indirect pointers);
85  * hence the estimate may be high.
86  */
87 long
88 blockest(union dinode *dp)
89 {
90 	long blkest, sizeest;
91 
92 	/*
93 	 * dp->di_size is the size of the file in bytes.
94 	 * dp->di_blocks stores the number of sectors actually in the file.
95 	 * If there are more sectors than the size would indicate, this just
96 	 *	means that there are indirect blocks in the file or unused
97 	 *	sectors in the last file block; we can safely ignore these
98 	 *	(blkest = sizeest below).
99 	 * If the file is bigger than the number of sectors would indicate,
100 	 *	then the file has holes in it.	In this case we must use the
101 	 *	block count to estimate the number of data blocks used, but
102 	 *	we use the actual size for estimating the number of indirect
103 	 *	dump blocks (sizeest vs. blkest in the indirect block
104 	 *	calculation).
105 	 */
106 	blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE);
107 	sizeest = howmany(DIP(dp, di_size), TP_BSIZE);
108 	if (blkest > sizeest)
109 		blkest = sizeest;
110 	if (DIP(dp, di_size) > sblock->fs_bsize * NDADDR) {
111 		/* calculate the number of indirect blocks on the dump tape */
112 		blkest +=
113 			howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
114 			TP_NINDIR);
115 	}
116 	return (blkest + 1);
117 }
118 
119 /* Auxiliary macro to pick up files changed since previous dump. */
120 #define	CHANGEDSINCE(dp, t) \
121 	(DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t))
122 
123 /* The WANTTODUMP macro decides whether a file should be dumped. */
124 #ifdef UF_NODUMP
125 #define	WANTTODUMP(dp) \
126 	(CHANGEDSINCE(dp, spcl.c_ddate) && \
127 	 (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP))
128 #else
129 #define	WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
130 #endif
131 
132 /*
133  * Dump pass 1.
134  *
135  * Walk the inode list for a file system to find all allocated inodes
136  * that have been modified since the previous dump time. Also, find all
137  * the directories in the file system.
138  */
139 int
140 mapfiles(ino_t maxino, long *tapesize)
141 {
142 	int mode;
143 	ino_t ino;
144 	union dinode *dp;
145 	int anydirskipped = 0;
146 
147 	for (ino = ROOTINO; ino < maxino; ino++) {
148 		dp = getino(ino, &mode);
149 		if (mode == 0)
150 			continue;
151 		/*
152 		 * Everything must go in usedinomap so that a check
153 		 * for "in dumpdirmap but not in usedinomap" to detect
154 		 * dirs with nodump set has a chance of succeeding
155 		 * (this is used in mapdirs()).
156 		 */
157 		SETINO(ino, usedinomap);
158 		if (mode == IFDIR)
159 			SETINO(ino, dumpdirmap);
160 		if (WANTTODUMP(dp)) {
161 			SETINO(ino, dumpinomap);
162 			if (mode != IFREG && mode != IFDIR && mode != IFLNK)
163 				*tapesize += 1;
164 			else
165 				*tapesize += blockest(dp);
166 			continue;
167 		}
168 		if (mode == IFDIR) {
169 			if (!nonodump && (DIP(dp, di_flags) & UF_NODUMP))
170 				CLRINO(ino, usedinomap);
171 			anydirskipped = 1;
172 		}
173 	}
174 	/*
175 	 * Restore gets very upset if the root is not dumped,
176 	 * so ensure that it always is dumped.
177 	 */
178 	SETINO(ROOTINO, dumpinomap);
179 	return (anydirskipped);
180 }
181 
182 /*
183  * Dump pass 2.
184  *
185  * Scan each directory on the file system to see if it has any modified
186  * files in it. If it does, and has not already been added to the dump
187  * list (because it was itself modified), then add it. If a directory
188  * has not been modified itself, contains no modified files and has no
189  * subdirectories, then it can be deleted from the dump list and from
190  * the list of directories. By deleting it from the list of directories,
191  * its parent may now qualify for the same treatment on this or a later
192  * pass using this algorithm.
193  */
194 int
195 mapdirs(ino_t maxino, long *tapesize)
196 {
197 	union dinode *dp;
198 	int i, isdir, nodump;
199 	char *map;
200 	ino_t ino;
201 	union dinode di;
202 	long filesize;
203 	int ret, change = 0;
204 
205 	isdir = 0;		/* XXX just to get gcc to shut up */
206 	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
207 		if (((ino - 1) % CHAR_BIT) == 0)	/* map is offset by 1 */
208 			isdir = *map++;
209 		else
210 			isdir >>= 1;
211 		/*
212 		 * If a directory has been removed from usedinomap, it
213 		 * either has the nodump flag set, or has inherited
214 		 * it.  Although a directory can't be in dumpinomap if
215 		 * it isn't in usedinomap, we have to go through it to
216 		 * propagate the nodump flag.
217 		 */
218 		nodump = !nonodump && (TSTINO(ino, usedinomap) == 0);
219 		if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump))
220 			continue;
221 		dp = getino(ino, &i);
222 		/*
223 		 * inode buf may change in searchdir().
224 		 */
225 		if (sblock->fs_magic == FS_UFS1_MAGIC)
226 			di.dp1 = dp->dp1;
227 		else
228 			di.dp2 = dp->dp2;
229 		filesize = DIP(&di, di_size);
230 		for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
231 			if (DIP(&di, di_db[i]) != 0)
232 				ret |= searchdir(ino, DIP(&di, di_db[i]),
233 				    (long)sblksize(sblock, DIP(dp, di_size), i),
234 				    filesize, tapesize, nodump);
235 			if (ret & HASDUMPEDFILE)
236 				filesize = 0;
237 			else
238 				filesize -= sblock->fs_bsize;
239 		}
240 		for (i = 0; filesize > 0 && i < NIADDR; i++) {
241 			if (DIP(&di, di_ib[i]) == 0)
242 				continue;
243 			ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize,
244 			    tapesize, nodump);
245 		}
246 		if (ret & HASDUMPEDFILE) {
247 			SETINO(ino, dumpinomap);
248 			*tapesize += blockest(dp);
249 			change = 1;
250 			continue;
251 		}
252 		if (nodump) {
253 			if (ret & HASSUBDIRS)
254 				change = 1;	/* subdirs inherit nodump */
255 			CLRINO(ino, dumpdirmap);
256 		} else if ((ret & HASSUBDIRS) == 0)
257 			if (!TSTINO(ino, dumpinomap)) {
258 				CLRINO(ino, dumpdirmap);
259 				change = 1;
260 			}
261 	}
262 	return (change);
263 }
264 
265 /*
266  * Read indirect blocks, and pass the data blocks to be searched
267  * as directories. Quit as soon as any entry is found that will
268  * require the directory to be dumped.
269  */
270 static int
271 dirindir(
272 	ino_t ino,
273 	ufs2_daddr_t blkno,
274 	int ind_level,
275 	long *filesize,
276 	long *tapesize,
277 	int nodump)
278 {
279 	union {
280 		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
281 		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
282 	} idblk;
283 	int ret = 0;
284 	int i;
285 
286 	bread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize);
287 	if (ind_level <= 0) {
288 		for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
289 			if (sblock->fs_magic == FS_UFS1_MAGIC)
290 				blkno = idblk.ufs1[i];
291 			else
292 				blkno = idblk.ufs2[i];
293 			if (blkno != 0)
294 				ret |= searchdir(ino, blkno, sblock->fs_bsize,
295 					*filesize, tapesize, nodump);
296 			if (ret & HASDUMPEDFILE)
297 				*filesize = 0;
298 			else
299 				*filesize -= sblock->fs_bsize;
300 		}
301 		return (ret);
302 	}
303 	ind_level--;
304 	for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
305 		if (sblock->fs_magic == FS_UFS1_MAGIC)
306 			blkno = idblk.ufs1[i];
307 		else
308 			blkno = idblk.ufs2[i];
309 		if (blkno != 0)
310 			ret |= dirindir(ino, blkno, ind_level, filesize,
311 			    tapesize, nodump);
312 	}
313 	return (ret);
314 }
315 
316 /*
317  * Scan a disk block containing directory information looking to see if
318  * any of the entries are on the dump list and to see if the directory
319  * contains any subdirectories.
320  */
321 static int
322 searchdir(
323 	ino_t ino,
324 	ufs2_daddr_t blkno,
325 	long size,
326 	long filesize,
327 	long *tapesize,
328 	int nodump)
329 {
330 	int mode;
331 	struct direct *dp;
332 	union dinode *ip;
333 	long loc, ret = 0;
334 	static caddr_t dblk;
335 
336 	if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL)
337 		quit("searchdir: cannot allocate indirect memory.\n");
338 	bread(fsbtodb(sblock, blkno), dblk, (int)size);
339 	if (filesize < size)
340 		size = filesize;
341 	for (loc = 0; loc < size; ) {
342 		dp = (struct direct *)(dblk + loc);
343 		if (dp->d_reclen == 0) {
344 			msg("corrupted directory, inumber %d\n", ino);
345 			break;
346 		}
347 		loc += dp->d_reclen;
348 		if (dp->d_ino == 0)
349 			continue;
350 		if (dp->d_name[0] == '.') {
351 			if (dp->d_name[1] == '\0')
352 				continue;
353 			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
354 				continue;
355 		}
356 		if (nodump) {
357 			ip = getino(dp->d_ino, &mode);
358 			if (TSTINO(dp->d_ino, dumpinomap)) {
359 				CLRINO(dp->d_ino, dumpinomap);
360 				*tapesize -= blockest(ip);
361 			}
362 			/*
363 			 * Add back to dumpdirmap and remove from usedinomap
364 			 * to propagate nodump.
365 			 */
366 			if (mode == IFDIR) {
367 				SETINO(dp->d_ino, dumpdirmap);
368 				CLRINO(dp->d_ino, usedinomap);
369 				ret |= HASSUBDIRS;
370 			}
371 		} else {
372 			if (TSTINO(dp->d_ino, dumpinomap)) {
373 				ret |= HASDUMPEDFILE;
374 				if (ret & HASSUBDIRS)
375 					break;
376 			}
377 			if (TSTINO(dp->d_ino, dumpdirmap)) {
378 				ret |= HASSUBDIRS;
379 				if (ret & HASDUMPEDFILE)
380 					break;
381 			}
382 		}
383 	}
384 	return (ret);
385 }
386 
387 /*
388  * Dump passes 3 and 4.
389  *
390  * Dump the contents of an inode to tape.
391  */
392 void
393 dumpino(union dinode *dp, ino_t ino)
394 {
395 	int ind_level, cnt;
396 	off_t size;
397 	char buf[TP_BSIZE];
398 
399 	if (newtape) {
400 		newtape = 0;
401 		dumpmap(dumpinomap, TS_BITS, ino);
402 	}
403 	CLRINO(ino, dumpinomap);
404 	if (sblock->fs_magic == FS_UFS1_MAGIC) {
405 		spcl.c_mode = dp->dp1.di_mode;
406 		spcl.c_size = dp->dp1.di_size;
407 		spcl.c_atime = _time32_to_time(dp->dp1.di_atime);
408 		spcl.c_atimensec = dp->dp1.di_atimensec;
409 		spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime);
410 		spcl.c_mtimensec = dp->dp1.di_mtimensec;
411 		spcl.c_birthtime = 0;
412 		spcl.c_birthtimensec = 0;
413 		spcl.c_rdev = dp->dp1.di_rdev;
414 		spcl.c_file_flags = dp->dp1.di_flags;
415 		spcl.c_uid = dp->dp1.di_uid;
416 		spcl.c_gid = dp->dp1.di_gid;
417 	} else {
418 		spcl.c_mode = dp->dp2.di_mode;
419 		spcl.c_size = dp->dp2.di_size;
420 		spcl.c_atime = _time64_to_time(dp->dp2.di_atime);
421 		spcl.c_atimensec = dp->dp2.di_atimensec;
422 		spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime);
423 		spcl.c_mtimensec = dp->dp2.di_mtimensec;
424 		spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime);
425 		spcl.c_birthtimensec = dp->dp2.di_birthnsec;
426 		spcl.c_rdev = dp->dp2.di_rdev;
427 		spcl.c_file_flags = dp->dp2.di_flags;
428 		spcl.c_uid = dp->dp2.di_uid;
429 		spcl.c_gid = dp->dp2.di_gid;
430 	}
431 	spcl.c_type = TS_INODE;
432 	spcl.c_count = 0;
433 	switch (DIP(dp, di_mode) & S_IFMT) {
434 
435 	case 0:
436 		/*
437 		 * Freed inode.
438 		 */
439 		return;
440 
441 	case S_IFLNK:
442 		/*
443 		 * Check for short symbolic link.
444 		 */
445 #ifdef FS_44INODEFMT
446 		if (DIP(dp, di_size) > 0 &&
447 		    DIP(dp, di_size) < sblock->fs_maxsymlinklen) {
448 			spcl.c_addr[0] = 1;
449 			spcl.c_count = 1;
450 			writeheader(ino);
451 			if (sblock->fs_magic == FS_UFS1_MAGIC)
452 				memmove(buf, (caddr_t)dp->dp1.di_db,
453 				    (u_long)DIP(dp, di_size));
454 			else
455 				memmove(buf, (caddr_t)dp->dp2.di_db,
456 				    (u_long)DIP(dp, di_size));
457 			buf[DIP(dp, di_size)] = '\0';
458 			writerec(buf, 0);
459 			return;
460 		}
461 #endif
462 		/* FALLTHROUGH */
463 
464 	case S_IFDIR:
465 	case S_IFREG:
466 		if (DIP(dp, di_size) > 0)
467 			break;
468 		/* FALLTHROUGH */
469 
470 	case S_IFIFO:
471 	case S_IFSOCK:
472 	case S_IFCHR:
473 	case S_IFBLK:
474 		writeheader(ino);
475 		return;
476 
477 	default:
478 		msg("Warning: undefined file type 0%o\n",
479 		    DIP(dp, di_mode) & IFMT);
480 		return;
481 	}
482 	if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize)
483 		cnt = NDADDR * sblock->fs_frag;
484 	else
485 		cnt = howmany(DIP(dp, di_size), sblock->fs_fsize);
486 	if (sblock->fs_magic == FS_UFS1_MAGIC)
487 		ufs1_blksout(&dp->dp1.di_db[0], cnt, ino);
488 	else
489 		ufs2_blksout(&dp->dp2.di_db[0], cnt, ino);
490 	if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0)
491 		return;
492 	for (ind_level = 0; ind_level < NIADDR; ind_level++) {
493 		dmpindir(ino, DIP(dp, di_ib[ind_level]), ind_level, &size);
494 		if (size <= 0)
495 			return;
496 	}
497 }
498 
499 /*
500  * Read indirect blocks, and pass the data blocks to be dumped.
501  */
502 static void
503 dmpindir(ino_t ino, ufs2_daddr_t blk, int ind_level, off_t *size)
504 {
505 	union {
506 		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
507 		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
508 	} idblk;
509 	int i, cnt;
510 
511 	if (blk != 0)
512 		bread(fsbtodb(sblock, blk), (char *)&idblk,
513 		    (int)sblock->fs_bsize);
514 	else
515 		memset(&idblk, 0, sblock->fs_bsize);
516 	if (ind_level <= 0) {
517 		if (*size < NINDIR(sblock) * sblock->fs_bsize)
518 			cnt = howmany(*size, sblock->fs_fsize);
519 		else
520 			cnt = NINDIR(sblock) * sblock->fs_frag;
521 		*size -= NINDIR(sblock) * sblock->fs_bsize;
522 		if (sblock->fs_magic == FS_UFS1_MAGIC)
523 			ufs1_blksout(idblk.ufs1, cnt, ino);
524 		else
525 			ufs2_blksout(idblk.ufs2, cnt, ino);
526 		return;
527 	}
528 	ind_level--;
529 	for (i = 0; i < NINDIR(sblock); i++) {
530 		if (sblock->fs_magic == FS_UFS1_MAGIC)
531 			dmpindir(ino, idblk.ufs1[i], ind_level, size);
532 		else
533 			dmpindir(ino, idblk.ufs2[i], ind_level, size);
534 		if (*size <= 0)
535 			return;
536 	}
537 }
538 
539 /*
540  * Collect up the data into tape record sized buffers and output them.
541  */
542 void
543 ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino)
544 {
545 	ufs1_daddr_t *bp;
546 	int i, j, count, blks, tbperdb;
547 
548 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
549 	tbperdb = sblock->fs_bsize >> tp_bshift;
550 	for (i = 0; i < blks; i += TP_NINDIR) {
551 		if (i + TP_NINDIR > blks)
552 			count = blks;
553 		else
554 			count = i + TP_NINDIR;
555 		for (j = i; j < count; j++)
556 			if (blkp[j / tbperdb] != 0)
557 				spcl.c_addr[j - i] = 1;
558 			else
559 				spcl.c_addr[j - i] = 0;
560 		spcl.c_count = count - i;
561 		writeheader(ino);
562 		bp = &blkp[i / tbperdb];
563 		for (j = i; j < count; j += tbperdb, bp++)
564 			if (*bp != 0) {
565 				if (j + tbperdb <= count)
566 					dumpblock(*bp, (int)sblock->fs_bsize);
567 				else
568 					dumpblock(*bp, (count - j) * TP_BSIZE);
569 			}
570 		spcl.c_type = TS_ADDR;
571 	}
572 }
573 
574 /*
575  * Collect up the data into tape record sized buffers and output them.
576  */
577 void
578 ufs2_blksout(ufs2_daddr_t *blkp, int frags, ino_t ino)
579 {
580 	ufs2_daddr_t *bp;
581 	int i, j, count, blks, tbperdb;
582 
583 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
584 	tbperdb = sblock->fs_bsize >> tp_bshift;
585 	for (i = 0; i < blks; i += TP_NINDIR) {
586 		if (i + TP_NINDIR > blks)
587 			count = blks;
588 		else
589 			count = i + TP_NINDIR;
590 		for (j = i; j < count; j++)
591 			if (blkp[j / tbperdb] != 0)
592 				spcl.c_addr[j - i] = 1;
593 			else
594 				spcl.c_addr[j - i] = 0;
595 		spcl.c_count = count - i;
596 		writeheader(ino);
597 		bp = &blkp[i / tbperdb];
598 		for (j = i; j < count; j += tbperdb, bp++)
599 			if (*bp != 0) {
600 				if (j + tbperdb <= count)
601 					dumpblock(*bp, (int)sblock->fs_bsize);
602 				else
603 					dumpblock(*bp, (count - j) * TP_BSIZE);
604 			}
605 		spcl.c_type = TS_ADDR;
606 	}
607 }
608 
609 /*
610  * Dump a map to the tape.
611  */
612 void
613 dumpmap(char *map, int type, ino_t ino)
614 {
615 	int i;
616 	char *cp;
617 
618 	spcl.c_type = type;
619 	spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
620 	writeheader(ino);
621 	for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
622 		writerec(cp, 0);
623 }
624 
625 /*
626  * Write a header record to the dump tape.
627  */
628 void
629 writeheader(ino_t ino)
630 {
631 	int32_t sum, cnt, *lp;
632 
633 	spcl.c_inumber = ino;
634 	spcl.c_magic = FS_UFS2_MAGIC;
635 	spcl.c_checksum = 0;
636 	lp = (int32_t *)&spcl;
637 	sum = 0;
638 	cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
639 	while (--cnt >= 0) {
640 		sum += *lp++;
641 		sum += *lp++;
642 		sum += *lp++;
643 		sum += *lp++;
644 	}
645 	spcl.c_checksum = CHECKSUM - sum;
646 	writerec((char *)&spcl, 1);
647 }
648 
649 union dinode *
650 getino(ino_t inum, int *modep)
651 {
652 	static ino_t minino, maxino;
653 	static caddr_t inoblock;
654 	struct ufs1_dinode *dp1;
655 	struct ufs2_dinode *dp2;
656 
657 	if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL)
658 		quit("cannot allocate inode memory.\n");
659 	curino = inum;
660 	if (inum >= minino && inum < maxino)
661 		goto gotit;
662 	bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock,
663 	    (int)sblock->fs_bsize);
664 	minino = inum - (inum % INOPB(sblock));
665 	maxino = minino + INOPB(sblock);
666 gotit:
667 	if (sblock->fs_magic == FS_UFS1_MAGIC) {
668 		dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino];
669 		*modep = (dp1->di_mode & IFMT);
670 		return ((union dinode *)dp1);
671 	}
672 	dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino];
673 	*modep = (dp2->di_mode & IFMT);
674 	return ((union dinode *)dp2);
675 }
676 
677 /*
678  * Read a chunk of data from the disk.
679  * Try to recover from hard errors by reading in sector sized pieces.
680  * Error recovery is attempted at most BREADEMAX times before seeking
681  * consent from the operator to continue.
682  */
683 int	breaderrors = 0;
684 #define	BREADEMAX 32
685 
686 void
687 bread(ufs2_daddr_t blkno, char *buf, int size)
688 {
689 	int cnt, i;
690 
691 loop:
692 	if ((cnt = pread(diskfd, buf, size, ((off_t)blkno << dev_bshift))) ==
693 						size)
694 		return;
695 	if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
696 		/*
697 		 * Trying to read the final fragment.
698 		 *
699 		 * NB - dump only works in TP_BSIZE blocks, hence
700 		 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
701 		 * It should be smarter about not actually trying to
702 		 * read more than it can get, but for the time being
703 		 * we punt and scale back the read only when it gets
704 		 * us into trouble. (mkm 9/25/83)
705 		 */
706 		size -= dev_bsize;
707 		goto loop;
708 	}
709 	if (cnt == -1)
710 		msg("read error from %s: %s: [block %jd]: count=%d\n",
711 			disk, strerror(errno), (intmax_t)blkno, size);
712 	else
713 		msg("short read error from %s: [block %jd]: count=%d, got=%d\n",
714 			disk, (intmax_t)blkno, size, cnt);
715 	if (++breaderrors > BREADEMAX) {
716 		msg("More than %d block read errors from %s\n",
717 			BREADEMAX, disk);
718 		broadcast("DUMP IS AILING!\n");
719 		msg("This is an unrecoverable error.\n");
720 		if (!query("Do you want to attempt to continue?")){
721 			dumpabort(0);
722 			/*NOTREACHED*/
723 		} else
724 			breaderrors = 0;
725 	}
726 	/*
727 	 * Zero buffer, then try to read each sector of buffer separately.
728 	 */
729 	memset(buf, 0, size);
730 	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
731 		if ((cnt = pread(diskfd, buf, (int)dev_bsize,
732 		    ((off_t)blkno << dev_bshift))) == dev_bsize)
733 			continue;
734 		if (cnt == -1) {
735 			msg("read error from %s: %s: [sector %jd]: count=%ld\n",
736 			    disk, strerror(errno), (intmax_t)blkno, dev_bsize);
737 			continue;
738 		}
739 		msg("short read from %s: [sector %jd]: count=%ld, got=%d\n",
740 		    disk, (intmax_t)blkno, dev_bsize, cnt);
741 	}
742 }
743