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