xref: /illumos-gate/usr/src/cmd/backup/dump/dumptraverse.c (revision dd72704bd9e794056c558153663c739e2012d721)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved	*/
8 
9 /*
10  * Copyright (c) 1980 Regents of the University of California.
11  * All rights reserved.  The Berkeley software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #include "dump.h"
16 #include <sys/file.h>
17 #include <sys/mman.h>
18 
19 static void lf_dmpindir(daddr32_t, int, u_offset_t *);
20 static void indir(daddr32_t, int, u_offset_t *);
21 static void lf_blksout(daddr32_t *, u_offset_t);
22 static void lf_dumpinode(struct dinode *);
23 static void dsrch(daddr32_t, ulong_t, u_offset_t);
24 void lf_dump(struct dinode *);
25 
26 int	dadded;
27 int	nsubdir;
28 int	shortmeta;
29 static	char msgbuf[256];
30 
31 void
32 pass(void (*fn)(struct dinode *), uchar_t *map)
33 {
34 	int bits;
35 	ino_t maxino;
36 
37 	maxino = (unsigned)(sblock->fs_ipg * sblock->fs_ncg - 1);
38 	/*
39 	 * Handle pass restarts.  We don't check for UFSROOTINO just in
40 	 * case we need to restart on the root inode.
41 	 */
42 	if (ino != 0) {
43 		bits = ~0;
44 		if (map != NULL) {
45 			/* LINTED: lint seems to think map is signed */
46 			map += (ino / NBBY);
47 			bits = *map++;
48 		}
49 		bits >>= (ino % NBBY);
50 		resetino(ino);
51 		goto restart;
52 	}
53 	while (ino < maxino) {
54 		if ((ino % NBBY) == 0) {
55 			bits = ~0;
56 			if (map != NULL)
57 				bits = *map++;
58 		}
59 restart:
60 		ino++;
61 		/*
62 		 * Ignore any inode less than UFSROOTINO and inodes that
63 		 * we have already done on a previous pass.
64 		 */
65 		if ((ino >= UFSROOTINO) && (bits & 1)) {
66 			/*
67 			 * The following test is merely an optimization
68 			 * for common case where "add" will just return.
69 			 */
70 			if (!(fn == add && BIT(ino, nodmap)))
71 				(*fn)(getino(ino));
72 		}
73 		bits >>= 1;
74 	}
75 }
76 
77 void
78 mark(struct dinode *ip)
79 {
80 	int f;
81 
82 	f = ip->di_mode & IFMT;
83 	if (f == 0 || ip->di_nlink <= 0) {
84 		/* LINTED: 32-bit to 8-bit assignment ok */
85 		BIC(ino, clrmap);
86 		return;
87 	}
88 	/* LINTED: 32-bit to 8-bit assignment ok */
89 	BIS(ino, clrmap);
90 	if (f == IFDIR || f == IFATTRDIR) {
91 		/* LINTED: 32-bit to 8-bit assignment ok */
92 		BIS(ino, dirmap);
93 	}
94 	if (ip->di_ctime >= spcl.c_ddate) {
95 		if (f == IFSHAD)
96 			return;
97 		/* LINTED: 32-bit to 8-bit assignment ok */
98 		BIS(ino, nodmap);
99 		/* attribute changes impact the root */
100 		if (f == IFATTRDIR)
101 			BIS(UFSROOTINO, nodmap);
102 		if (f != IFREG && f != IFDIR && f != IFATTRDIR && f != IFLNK) {
103 			o_esize += 1;
104 			return;
105 		}
106 		est(ip);
107 	}
108 }
109 
110 void
111 active_mark(struct dinode *ip)
112 {
113 	int f;
114 
115 	f = ip->di_mode & IFMT;
116 	if (f == 0 || ip->di_nlink <= 0) {
117 		/* LINTED: 32-bit to 8-bit assignment ok */
118 		BIC(ino, clrmap);
119 		return;
120 	}
121 	/* LINTED: 32-bit to 8-bit assignment ok */
122 	BIS(ino, clrmap);
123 	if (f == IFDIR || f == IFATTRDIR) {
124 		/* LINTED: 32-bit to 8-bit assignment ok */
125 		BIS(ino, dirmap);
126 	}
127 	if (BIT(ino, activemap)) {
128 		/* LINTED: 32-bit to 8-bit assignment ok */
129 		BIS(ino, nodmap);
130 		/* attribute changes impact the root */
131 		if (f == IFATTRDIR)
132 			BIS(UFSROOTINO, nodmap);
133 		if (f != IFREG && f != IFDIR && f != IFATTRDIR && f != IFLNK) {
134 			o_esize += 1;
135 			return;
136 		}
137 		est(ip);
138 	}
139 }
140 
141 static struct shcount {
142 	struct shcount *higher, *lower;
143 	ino_t ino;
144 	unsigned long count;
145 } shcounts = {
146 	NULL, NULL,
147 	0,
148 	0
149 };
150 static struct shcount *shc = NULL;
151 
152 void
153 markshad(struct dinode *ip)
154 {
155 	ino_t shadow;
156 
157 	if (ip->di_shadow == 0)
158 		return;
159 	if (shc == NULL)
160 		shc = &shcounts;
161 
162 	shadow = (ino_t)(unsigned)(ip->di_shadow);
163 	while ((shadow > shc->ino) && (shc->higher))
164 		shc = shc->higher;
165 	while ((shadow < shc->ino) && (shc->lower))
166 		shc = shc->lower;
167 	if (shadow != shc->ino) {
168 		struct shcount *new;
169 
170 		new = (struct shcount *)xcalloc(1, sizeof (*new));
171 		new->higher = shc->higher;
172 		if (shc->higher != NULL)
173 			shc->higher->lower = new;
174 		shc->higher = new;
175 		new->lower = shc;
176 		shc = new;
177 		shc->ino = shadow;
178 	}
179 
180 	/* LINTED: 32-bit to 8-bit assignment ok */
181 	BIS(shadow, shamap);
182 	shc->count++;
183 }
184 
185 void
186 estshad(struct dinode *ip)
187 {
188 	u_offset_t esizeprime;
189 	u_offset_t tmpesize;
190 
191 	if (ip->di_size <= sizeof (union u_shadow))
192 		return;
193 
194 	while ((ino > shc->ino) && (shc->higher))
195 		shc = shc->higher;
196 	while ((ino < shc->ino) && (shc->lower))
197 		shc = shc->lower;
198 	if (ino != shc->ino)
199 		return; /* xxx panic? complain? */
200 
201 	tmpesize = (o_esize + f_esize);
202 	esizeprime = tmpesize;
203 	est(ip);
204 	esizeprime = tmpesize - esizeprime;
205 	esizeprime *= shc->count - 1;
206 	f_esize += esizeprime;
207 }
208 
209 void
210 freeshad()
211 {
212 	if (shc == NULL)
213 		return;
214 
215 	while (shc->higher)
216 		shc = shc->higher;
217 	while (shc->lower) {
218 		shc = shc->lower;
219 		if (shc->higher) /* else panic? */
220 			(void) free(shc->higher);
221 	}
222 	/*
223 	 * This should be unnecessary, but do it just to be safe.
224 	 * Note that shc might be malloc'd or static, so can't free().
225 	 */
226 	bzero(shc, sizeof (*shc));
227 }
228 
229 void
230 add(struct dinode *ip)
231 {
232 	int i;
233 	u_offset_t filesize;
234 
235 	if (BIT(ino, nodmap))
236 		return;
237 	if ((ip->di_mode & IFMT) != IFDIR &&
238 	    (ip->di_mode & IFMT) != IFATTRDIR) {
239 		(void) snprintf(msgbuf, sizeof (msgbuf), gettext(
240 		    "Warning - directory at inode `%lu' vanished!\n"), ino);
241 		msg(msgbuf);
242 		/* LINTED: 32-bit to 8-bit assignment ok */
243 		BIC(ino, dirmap);
244 		return;
245 	}
246 	nsubdir = 0;
247 	dadded = 0;
248 	filesize = ip->di_size;
249 	for (i = 0; i < NDADDR; i++) {
250 		if (ip->di_db[i] != 0)
251 			/* LINTED dblksize/blkoff does a safe cast here */
252 			dsrch(ip->di_db[i], (ulong_t)dblksize(sblock, ip, i),
253 			    filesize);
254 		filesize -= (unsigned)(sblock->fs_bsize);
255 	}
256 	for (i = 0; i < NIADDR; i++) {
257 		if (ip->di_ib[i] != 0)
258 			indir(ip->di_ib[i], i, &filesize);
259 	}
260 	if (dadded) {
261 		nadded++;
262 		if (!BIT(ino, nodmap)) {
263 			/* LINTED: 32-bit to 8-bit assignment ok */
264 			BIS(ino, nodmap);
265 			if ((ip->di_mode & IFMT) == IFATTRDIR) {
266 				/* attribute changes "auto-percolate" to root */
267 				BIS(UFSROOTINO, nodmap);
268 			}
269 			est(ip);
270 		}
271 	}
272 	if (nsubdir == 0) {
273 		if (!BIT(ino, nodmap)) {
274 			/* LINTED: 32-bit to 8-bit assignment ok */
275 			BIC(ino, dirmap);
276 		}
277 	}
278 }
279 
280 static void
281 indir(daddr32_t d, int n, u_offset_t *filesize)
282 {
283 	int i;
284 	daddr32_t idblk[MAXNINDIR];
285 
286 	if ((unsigned)(sblock->fs_bsize) > sizeof (idblk)) {
287 		msg(gettext(
288 "Inconsistency detected: filesystem block size larger than valid maximum.\n"));
289 		dumpabort();
290 		/*NOTREACHED*/
291 	}
292 
293 	if ((unsigned)NINDIR(sblock) > MAXNINDIR) {
294 		/*CSTYLED*/
295 		msg(gettext(
296 "Inconsistency detected: inode has more indirect \
297 blocks than valid maximum.\n"));
298 		dumpabort();
299 		/*NOTREACHED*/
300 	}
301 
302 	if (dadded || *filesize == 0)
303 		return;
304 
305 #ifdef	lint
306 	idblk[0] = '\0';
307 #endif	/* lint */
308 
309 	/* xxx sanity check sblock contents before trusting them */
310 	bread(fsbtodb(sblock, d), (uchar_t *)idblk, (size_t)sblock->fs_bsize);
311 	if (n <= 0) {
312 		for (i = 0; i < NINDIR(sblock); i++) {
313 			d = idblk[i];
314 			if (d != 0)
315 				dsrch(d, (ulong_t)(uint32_t)sblock->fs_bsize,
316 				    *filesize);
317 			*filesize -= (unsigned)(sblock->fs_bsize);
318 		}
319 	} else {
320 		n--;
321 		for (i = 0; i < NINDIR(sblock); i++) {
322 			d = idblk[i];
323 			if (d != 0)
324 				indir(d, n, filesize);
325 		}
326 	}
327 }
328 
329 void
330 dirdump(struct dinode *ip)
331 {
332 	/* watchout for dir inodes deleted and maybe reallocated */
333 	if (((ip->di_mode & IFMT) != IFDIR &&
334 	    (ip->di_mode & IFMT) != IFATTRDIR) || ip->di_nlink < 2) {
335 		(void) snprintf(msgbuf, sizeof (msgbuf), gettext(
336 		    "Warning - directory at inode `%lu' vanished!\n"), ino);
337 		msg(msgbuf);
338 		return;
339 	}
340 	lf_dump(ip);
341 }
342 
343 static u_offset_t loffset; /* current offset in file (ufsdump) */
344 
345 static void
346 lf_dumpmeta(struct dinode *ip)
347 {
348 	if ((ip->di_shadow == 0) || shortmeta)
349 		return;
350 
351 	lf_dumpinode(getino((ino_t)(unsigned)(ip->di_shadow)));
352 }
353 
354 int
355 hasshortmeta(struct dinode **ip)
356 {
357 	ino_t savino;
358 	int rc;
359 
360 	if ((*ip)->di_shadow == 0)
361 		return (0);
362 	savino = ino;
363 	*ip = getino((ino_t)(unsigned)((*ip)->di_shadow));
364 	rc = ((*ip)->di_size <= sizeof (union u_shadow));
365 	*ip = getino(ino = savino);
366 	return (rc);
367 }
368 
369 void
370 lf_dumpinode(struct dinode *ip)
371 {
372 	int i;
373 	u_offset_t size;
374 
375 	i = ip->di_mode & IFMT;
376 
377 	if (i == 0 || ip->di_nlink <= 0)
378 		return;
379 
380 	spcl.c_dinode = *ip;
381 	spcl.c_count = 0;
382 
383 	if ((i != IFDIR && i != IFATTRDIR && i != IFREG && i != IFLNK &&
384 	    i != IFSHAD) || ip->di_size == 0) {
385 		toslave(dospcl, ino);
386 		return;
387 	}
388 
389 	size = NDADDR * (unsigned)(sblock->fs_bsize);
390 	if (size > ip->di_size)
391 		size = ip->di_size;
392 
393 	lf_blksout(&ip->di_db[0], size);
394 
395 	size = ip->di_size - size;
396 	if (size > 0) {
397 		for (i = 0; i < NIADDR; i++) {
398 			lf_dmpindir(ip->di_ib[i], i, &size);
399 			if (size == 0)
400 				break;
401 		}
402 	}
403 }
404 
405 void
406 lf_dump(struct dinode *ip)
407 {
408 
409 	if ((!BIT(ino, nodmap)) && (!BIT(ino, shamap)))
410 		return;
411 
412 	shortmeta = hasshortmeta(&ip);
413 	if (shortmeta) {
414 		ip = getino((ino_t)(unsigned)(ip->di_shadow));
415 		/* assume spcl.c_shadow is smaller than 1 block */
416 		bread(fsbtodb(sblock, ip->di_db[0]),
417 		    (uchar_t *)spcl.c_shadow.c_shadow, sizeof (spcl.c_shadow));
418 		spcl.c_flags |= DR_HASMETA;
419 	} else {
420 		spcl.c_flags &= ~DR_HASMETA;
421 	}
422 	ip = getino(ino);
423 
424 	loffset = 0;
425 
426 	if (newtape) {
427 		spcl.c_type = TS_TAPE;
428 	} else if (pos)
429 		spcl.c_type = TS_ADDR;
430 	else
431 		spcl.c_type = TS_INODE;
432 
433 	newtape = 0;
434 	lf_dumpinode(ip);
435 	lf_dumpmeta(ip);
436 	pos = 0;
437 }
438 
439 static void
440 lf_dmpindir(daddr32_t blk, int lvl, u_offset_t *size)
441 {
442 	int i;
443 	u_offset_t cnt;
444 	daddr32_t idblk[MAXNINDIR];
445 
446 	if ((unsigned)(sblock->fs_bsize) > sizeof (idblk)) {
447 		msg(gettext(
448 "Inconsistency detected: filesystem block size larger than valid maximum.\n"));
449 		dumpabort();
450 		/*NOTREACHED*/
451 	}
452 
453 	if ((unsigned)NINDIR(sblock) > MAXNINDIR) {
454 		msg(gettext(
455 "Inconsistency detected: inode has more indirect \
456 blocks than valid maximum.\n"));
457 		dumpabort();
458 		/*NOTREACHED*/
459 	}
460 
461 	if (blk != 0)
462 		bread(fsbtodb(sblock, blk), (uchar_t *)idblk,
463 		    (size_t)sblock->fs_bsize);
464 	else
465 		bzero((char *)idblk, (size_t)sblock->fs_bsize);
466 	if (lvl <= 0) {
467 		cnt = (u_offset_t)(unsigned)NINDIR(sblock) *
468 		    (u_offset_t)(unsigned)(sblock->fs_bsize);
469 		if (cnt > *size)
470 			cnt = *size;
471 		*size -= cnt;
472 		lf_blksout(&idblk[0], cnt);
473 		return;
474 	}
475 	lvl--;
476 	for (i = 0; i < NINDIR(sblock); i++) {
477 		lf_dmpindir(idblk[i], lvl, size);
478 		if (*size == 0)
479 			return;
480 	}
481 }
482 
483 static void
484 lf_blksout(daddr32_t *blkp, u_offset_t bytes)
485 {
486 	u_offset_t i;
487 	u_offset_t tbperfsb = (unsigned)(sblock->fs_bsize / tp_bsize);
488 
489 	u_offset_t j, k, count;
490 
491 	u_offset_t bytepos, diff;
492 	u_offset_t bytecnt = 0;
493 	off_t byteoff = 0;	/* bytes to skip within first f/s block */
494 	off_t fragoff = 0;	/* frags to skip within first f/s block */
495 
496 	u_offset_t tpblkoff = 0; /* tape blocks to skip in first f/s block */
497 	u_offset_t tpblkskip = 0;	/* total tape blocks to skip  */
498 	u_offset_t skip;		/* tape blocks to skip this pass */
499 
500 	if (pos) {
501 		/*
502 		 * We get here if a slave throws a signal to the
503 		 * master indicating a partially dumped file.
504 		 * Begin by figuring out what was undone.
505 		 */
506 		bytepos = (offset_t)pos * tp_bsize;
507 
508 		if ((loffset + bytes) <= bytepos) {
509 			/* This stuff was dumped already, forget it. */
510 			loffset += (u_offset_t)tp_bsize *
511 			    /* LINTED: spurious complaint on sign-extending */
512 			    d_howmany(bytes, (u_offset_t)tp_bsize);
513 			return;
514 		}
515 
516 		if (loffset < bytepos) {
517 			/*
518 			 * Some of this was dumped, some wasn't.
519 			 * Figure out what was done and skip it.
520 			 */
521 			diff = bytepos - loffset;
522 			/* LINTED: spurious complaint on sign-extending */
523 			tpblkskip = d_howmany(diff, (u_offset_t)tp_bsize);
524 			/* LINTED room after EOT is only a few MB */
525 			blkp += (int)(diff / sblock->fs_bsize);
526 
527 			bytecnt = diff % (unsigned)(sblock->fs_bsize);
528 			/* LINTED: result fits, due to modulus */
529 			byteoff = bytecnt % (off_t)(sblock->fs_fsize);
530 			/* LINTED: spurious complaint on sign-extending */
531 			tpblkoff = d_howmany(bytecnt,
532 			    (u_offset_t)(unsigned)tp_bsize);
533 			/* LINTED: result fits, due to modulus */
534 			fragoff = bytecnt / (off_t)(sblock->fs_fsize);
535 			bytecnt = (unsigned)(sblock->fs_bsize) - bytecnt;
536 		}
537 	}
538 
539 	loffset += bytes;
540 
541 	while (bytes > 0) {
542 		if (bytes < TP_NINDIR*tp_bsize)
543 			/* LINTED: spurious complaint on sign-extending */
544 			count = d_howmany(bytes, (u_offset_t)tp_bsize);
545 		else
546 			count = TP_NINDIR;
547 		if (tpblkskip) {
548 			if (tpblkskip < TP_NINDIR) {
549 				bytes -= (tpblkskip * (u_offset_t)tp_bsize);
550 				skip = tpblkskip;
551 				tpblkskip = 0;
552 			} else {
553 				bytes -= (offset_t)TP_NINDIR*tp_bsize;
554 				tpblkskip -= TP_NINDIR;
555 				continue;
556 			}
557 		} else
558 			skip = 0;
559 		assert(tbperfsb >= tpblkoff);
560 		assert((count - skip) <= TP_NINDIR);
561 		for (j = 0, k = 0; j < count - skip; j++, k++) {
562 			spcl.c_addr[j] = (blkp[k] != 0);
563 			for (i = tbperfsb - tpblkoff; --i > 0; j++)
564 				spcl.c_addr[j+1] = spcl.c_addr[j];
565 			tpblkoff = 0;
566 		}
567 		/* LINTED (count - skip) will always fit into an int32_t */
568 		spcl.c_count = count - skip;
569 		toslave(dospcl, ino);
570 		bytecnt = MIN(bytes, bytecnt ?
571 		    bytecnt : (unsigned)(sblock->fs_bsize));
572 		j = 0;
573 		while (j < count - skip) {
574 			if (*blkp != 0) {
575 				/* LINTED: fragoff fits into 32 bits */
576 				dmpblk(*blkp+(int32_t)fragoff,
577 				    /* LINTED: bytecnt fits into 32 bits */
578 				    (size_t)bytecnt, byteoff);
579 			}
580 			blkp++;
581 			bytes -= bytecnt;
582 			/* LINTED: spurious complaint on sign-extending */
583 			j += d_howmany(bytecnt, (u_offset_t)tp_bsize);
584 			bytecnt = MIN(bytes, (unsigned)(sblock->fs_bsize));
585 			byteoff = 0;
586 			fragoff = 0;
587 		}
588 		spcl.c_type = TS_ADDR;
589 		bytecnt = 0;
590 	}
591 	pos = 0;
592 }
593 
594 void
595 bitmap(uchar_t *map, int typ)
596 {
597 	int i;
598 	u_offset_t count;
599 	uchar_t *cp;
600 
601 	if (!newtape)
602 		spcl.c_type = typ;
603 	else
604 		newtape = 0;
605 	for (i = 0; i < TP_NINDIR; i++)
606 		spcl.c_addr[i] = 1;
607 	/* LINTED: spurious complaint on sign-extending */
608 	count = d_howmany(msiz * sizeof (map[0]), tp_bsize) - pos;
609 	for (cp = &map[pos * tp_bsize]; count > 0;
610 	    count -= (u_offset_t)(unsigned)spcl.c_count) {
611 		if (leftover) {
612 			spcl.c_count = leftover;
613 			leftover = 0;
614 		} else {
615 			/* LINTED value always less than INT32_MAX */
616 			spcl.c_count = count > TP_NINDIR ? TP_NINDIR : count;
617 		}
618 		spclrec();
619 		for (i = 0; i < spcl.c_count; i++, cp += tp_bsize)
620 			taprec(cp, 0, tp_bsize);
621 		spcl.c_type = TS_ADDR;
622 	}
623 }
624 
625 static void
626 dsrch(daddr32_t d, ulong_t size, u_offset_t filesize)
627 {
628 	struct direct *dp;
629 	struct dinode *ip;
630 	ulong_t loc;
631 	char dblk[MAXBSIZE];
632 
633 	if (dadded || filesize == 0)
634 		return;
635 	if (filesize > (u_offset_t)size)
636 		filesize = (u_offset_t)size;
637 	if (sizeof (dblk) < roundup(filesize, DEV_BSIZE)) {
638 		msg(gettext(
639 "Inconsistency detected: filesystem block size larger than valid maximum.\n"));
640 		dumpabort();
641 		/*NOTREACHED*/
642 	}
643 
644 #ifdef	lint
645 	dblk[0] = '\0';
646 #endif	/* lint */
647 
648 	/* LINTED ufs disk addresses always fit into 32 bits */
649 	bread(fsbtodb(sblock, d), (uchar_t *)dblk,
650 	    /* LINTED from sizeof check above, roundup() <= max(size_t) */
651 	    (size_t)(roundup(filesize, DEV_BSIZE)));
652 	loc = 0;
653 	while ((u_offset_t)loc < filesize) {
654 		/*LINTED [dblk is char[], loc (dp->d_reclen) % 4 == 0]*/
655 		dp = (struct direct *)(dblk + loc);
656 		if (dp->d_reclen == 0) {
657 			(void) snprintf(msgbuf, sizeof (msgbuf), gettext(
658 			    "Warning - directory at inode `%lu' is "
659 			    "corrupted\n"), ino);
660 			msg(msgbuf);
661 			break;
662 		}
663 		loc += dp->d_reclen;
664 		if (dp->d_ino == 0)
665 			continue;
666 		if (dp->d_name[0] == '.') {
667 			if (dp->d_name[1] == '\0') {
668 				if ((ino_t)(dp->d_ino) != ino) {
669 					(void) snprintf(msgbuf, sizeof (msgbuf),
670 					    gettext(
671 			"Warning - directory at inode `%lu' is corrupted:\n\
672 \t\".\" points to inode `%lu' - run fsck\n"),
673 					    ino, dp->d_ino);
674 					msg(msgbuf);
675 				}
676 				continue;
677 			}
678 			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') {
679 				if (!BIT(dp->d_ino, dirmap) &&
680 				    ((ip = getino(ino)) == NULL ||
681 				    (ip->di_mode & IFMT) != IFATTRDIR)) {
682 					(void) snprintf(msgbuf, sizeof (msgbuf),
683 					    gettext(
684 			"Warning - directory at inode `%lu' is corrupted:\n\
685 \t\"..\" points to non-directory inode `%lu' - run fsck\n"),
686 					    ino, dp->d_ino);
687 					msg(msgbuf);
688 				}
689 				continue;
690 			}
691 		}
692 		if (BIT(dp->d_ino, nodmap)) {
693 			dadded++;
694 			return;
695 		}
696 		if (BIT(dp->d_ino, dirmap))
697 			nsubdir++;
698 	}
699 }
700 
701 #define	CACHESIZE 32
702 
703 struct dinode *
704 getino(ino_t ino)
705 {
706 	static ino_t minino, maxino;
707 	static struct dinode itab[MAXINOPB];
708 	static struct dinode icache[CACHESIZE];
709 	static ino_t icacheval[CACHESIZE], lasti = 0;
710 	static int cacheoff = 0;
711 	int i;
712 
713 	if (ino >= minino && ino < maxino) {
714 		lasti = ino;
715 		return (&itab[ino - minino]);
716 	}
717 
718 	/* before we do major i/o, check for a secondary cache hit */
719 	for (i = 0; i < CACHESIZE; i++)
720 		if (icacheval[i] == ino)
721 			return (icache + i);
722 
723 	/* we need to do major i/o.  throw the last inode retrieved into */
724 	/* the cache.  note: this copies garbage the first time it is    */
725 	/* used, but no harm done.					 */
726 	icacheval[cacheoff] = lasti;
727 	bcopy(itab + (lasti - minino), icache + cacheoff, sizeof (itab[0]));
728 	lasti = ino;
729 	if (++cacheoff >= CACHESIZE)
730 		cacheoff = 0;
731 
732 #define	INOPERDB (DEV_BSIZE / sizeof (struct dinode))
733 	minino = ino &~ (INOPERDB - 1);
734 	maxino = ((itog(sblock, ino) + 1) * (unsigned)(sblock->fs_ipg));
735 	if (maxino > minino + MAXINOPB)
736 		maxino = minino + MAXINOPB;
737 	bread(
738 	    /* LINTED: can't make up for broken system macros here */
739 	    (fsbtodb(sblock, itod(sblock, ino)) + itoo(sblock, ino) / INOPERDB),
740 	    /* LINTED: (max - min) * size fits into a size_t */
741 	    (uchar_t *)itab, (size_t)((maxino - minino) * sizeof (*itab)));
742 	return (&itab[ino - minino]);
743 }
744 
745 #define	BREADEMAX 32
746 
747 #ifdef NO__LONGLONG__
748 #define	DEV_LSEEK(fd, offset, whence) \
749 	lseek((fd), (((off_t)(offset))*DEV_BSIZE), (whence))
750 #else
751 #define	DEV_LSEEK(fd, offset, whence) \
752 	llseek((fd), (((offset_t)((offset)))*DEV_BSIZE), (whence))
753 #endif
754 
755 #define	BREAD_FAIL(buf, size)	{ \
756 		breaderrors += 1; \
757 		bzero(buf, (size_t)size); \
758 	}
759 
760 
761 
762 void
763 bread(diskaddr_t da, uchar_t *ba, size_t cnt)
764 {
765 	caddr_t maddr;
766 	uchar_t *dest;
767 	int saverr;
768 	int n;
769 	size_t len;
770 	off64_t filoff;
771 	off64_t mapoff;
772 	off64_t displacement;
773 
774 	static size_t pagesize = 0;
775 	static int breaderrors = 0;
776 
777 	/* mechanics for caching small bread requests.  these are */
778 	/* often small ACLs that are used over and over.	  */
779 	static uchar_t bcache[DEV_BSIZE * CACHESIZE];
780 	static diskaddr_t bcacheval[CACHESIZE];
781 	static int cacheoff = 0;
782 	int i;
783 
784 	if ((cnt >= DEV_BSIZE) && (mapfd != -1)) {
785 		if (pagesize == 0)
786 			pagesize = getpagesize();
787 		/*
788 		 * We depend on mmap(2)'s guarantee that mapping a
789 		 * partial page will cause the remainder of the page
790 		 * to be zero-filled.
791 		 */
792 		filoff = ((off64_t)da) * DEV_BSIZE;
793 		displacement = filoff & (pagesize - 1);
794 		mapoff = filoff - displacement;
795 		/* LINTED offset will fit into 32 bits */
796 		len = (size_t)roundup(cnt + (filoff - mapoff), pagesize);
797 		maddr = mmap64(NULL, len, PROT_READ, MAP_SHARED, mapfd, mapoff);
798 		if (maddr != MAP_FAILED) {
799 			(void) memcpy(ba, maddr + displacement, cnt);
800 			(void) munmap(maddr, len);
801 			return;
802 		}
803 	}
804 
805 	if (DEV_LSEEK(fi, da, L_SET) < 0) {
806 		saverr = errno;
807 		msg(gettext("bread: dev_seek error: %s\n"), strerror(saverr));
808 		/* Don't know where we are, return the least-harmful data */
809 		BREAD_FAIL(ba, cnt);
810 		return;
811 	}
812 
813 	if (read(fi, ba, (size_t)cnt) == (size_t)cnt)
814 		return;
815 
816 	while (cnt != 0) {
817 
818 		if (da >= fsbtodb(sblock, sblock->fs_size)) {
819 			msg(gettext(
820 			    "Warning - block %llu is beyond the end of `%s'\n"),
821 			    da, disk);
822 			BREAD_FAIL(ba, cnt);
823 			break;
824 		}
825 
826 		if (DEV_LSEEK(fi, da, L_SET) < 0) {
827 			msg(gettext("%s: %s error\n"), "bread", "DEV_LSEEK2");
828 			BREAD_FAIL(ba, cnt);
829 			break;
830 		}
831 
832 		if (cnt < DEV_BSIZE) {
833 			/* small read.  check for cache hit. */
834 			for (i = 0; i < CACHESIZE; i++)
835 				if (bcacheval[i] == da) {
836 					bcopy(bcache + (i * DEV_BSIZE),
837 					    ba, cnt);
838 					return;
839 				}
840 
841 			/* no cache hit; throw this one into the cache... */
842 			len = cnt;
843 			dest = bcache + (cacheoff * DEV_BSIZE);
844 			bcacheval[cacheoff] = da;
845 			if (++cacheoff >= CACHESIZE)
846 				cacheoff = 0;
847 		} else {
848 			len = DEV_BSIZE;
849 			dest = ba;
850 		}
851 
852 		n = read(fi, dest, DEV_BSIZE);
853 		if (n != DEV_BSIZE) {
854 			n = MAX(n, 0);
855 			bzero(dest+n, DEV_BSIZE-n);
856 			breaderrors += 1;
857 			msg(gettext(
858 			    "Warning - cannot read sector %llu of `%s'\n"),
859 			    da, disk);
860 		}
861 		if (dest != ba)
862 			bcopy(dest, ba, len);
863 
864 		da++;
865 		/* LINTED character pointers aren't signed */
866 		ba += len;
867 		cnt -= len;
868 	}
869 
870 	if (breaderrors > BREADEMAX) {
871 		msg(gettext(
872 		    "More than %d block read errors from dump device `%s'\n"),
873 		    BREADEMAX, disk);
874 		dumpailing();
875 		breaderrors = 0;
876 	}
877 }
878