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__
22static void lf_dmpindir(daddr32_t, int, u_offset_t *);
23static void indir(daddr32_t, int, u_offset_t *);
24static void lf_blksout(daddr32_t *, u_offset_t);
25static void lf_dumpinode(struct dinode *);
26static void dsrch(daddr32_t, ulong_t, u_offset_t);
27void lf_dump(struct dinode *);
28#else
29static void lf_dmpindir();
30static void indir();
31static void lf_blksout();
32static void dsrch();
33void lf_dump();
34#endif
35
36static	char msgbuf[256];
37
38void
39pass(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		}
68restart:
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
86void
87mark(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
120void
121active_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
152static 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};
161static struct shcount *shc = NULL;
162
163void
164markshad(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
197void
198estshad(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
222void
223freeshad()
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
242void
243add(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
294static void
295indir(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 \
314blocks 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
346void
347dirdump(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
362static u_offset_t loffset; /* current offset in file (ufsdump) */
363
364static void
365lf_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
374int
375hasshortmeta(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
390void
391lf_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
427void
428lf_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
462static void
463lf_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 \
482blocks 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
509static void
510lf_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
622void
623bitmap(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
655static void
656dsrch(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
736struct dinode *
737getino(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
796void
797bread(da, ba, cnt)
798diskaddr_t da;
799uchar_t	*ba;
800size_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