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
pass(void (* fn)(struct dinode *),uchar_t * map)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
mark(struct dinode * ip)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
active_mark(struct dinode * ip)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
markshad(struct dinode * ip)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
estshad(struct dinode * ip)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
freeshad()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
add(struct dinode * ip)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
indir(daddr32_t d,int n,u_offset_t * filesize)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
dirdump(struct dinode * ip)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
lf_dumpmeta(struct dinode * ip)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
hasshortmeta(struct dinode ** ip)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
lf_dumpinode(struct dinode * ip)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
lf_dump(struct dinode * ip)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
lf_dmpindir(daddr32_t blk,int lvl,u_offset_t * size)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
lf_blksout(daddr32_t * blkp,u_offset_t bytes)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
bitmap(uchar_t * map,int typ)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
dsrch(daddr32_t d,ulong_t size,u_offset_t filesize)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 *
getino(ino_t ino)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
bread(diskaddr_t da,uchar_t * ba,size_t cnt)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