dumptraverse.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 1996, 1998, 2001-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "dump.h"
#include <sys/file.h>
#include <sys/mman.h>
#ifdef __STDC__
static void lf_dmpindir(daddr32_t, int, u_offset_t *);
static void indir(daddr32_t, int, u_offset_t *);
static void lf_blksout(daddr32_t *, u_offset_t);
static void lf_dumpinode(struct dinode *);
static void dsrch(daddr32_t, ulong_t, u_offset_t);
void lf_dump(struct dinode *);
#else
static void lf_dmpindir();
static void indir();
static void lf_blksout();
static void dsrch();
void lf_dump();
#endif
static char msgbuf[256];
void
pass(fn, map)
void (*fn)(struct dinode *);
uchar_t *map;
{
int bits;
ino_t maxino;
maxino = (unsigned)(sblock->fs_ipg * sblock->fs_ncg - 1);
/*
* Handle pass restarts. We don't check for UFSROOTINO just in
* case we need to restart on the root inode.
*/
if (ino != 0) {
bits = ~0;
if (map != NULL) {
/* LINTED: lint seems to think map is signed */
map += (ino / NBBY);
bits = *map++;
}
bits >>= (ino % NBBY);
resetino(ino);
goto restart;
}
while (ino < maxino) {
if ((ino % NBBY) == 0) {
bits = ~0;
if (map != NULL)
bits = *map++;
}
restart:
ino++;
/*
* Ignore any inode less than UFSROOTINO and inodes that
* we have already done on a previous pass.
*/
if ((ino >= UFSROOTINO) && (bits & 1)) {
/*
* The following test is merely an optimization
* for common case where "add" will just return.
*/
if (!(fn == add && BIT(ino, nodmap)))
(*fn)(getino(ino));
}
bits >>= 1;
}
}
void
mark(ip)
struct dinode *ip;
{
int f;
f = ip->di_mode & IFMT;
if (f == 0 || ip->di_nlink <= 0) {
/* LINTED: 32-bit to 8-bit assignment ok */
BIC(ino, clrmap);
return;
}
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(ino, clrmap);
if (f == IFDIR || f == IFATTRDIR) {
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(ino, dirmap);
}
if (ip->di_ctime >= spcl.c_ddate) {
if (f == IFSHAD)
return;
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(ino, nodmap);
/* attribute changes impact the root */
if (f == IFATTRDIR)
BIS(UFSROOTINO, nodmap);
if (f != IFREG && f != IFDIR && f != IFATTRDIR && f != IFLNK) {
o_esize += 1;
return;
}
est(ip);
}
}
void
active_mark(ip)
struct dinode *ip;
{
int f;
f = ip->di_mode & IFMT;
if (f == 0 || ip->di_nlink <= 0) {
/* LINTED: 32-bit to 8-bit assignment ok */
BIC(ino, clrmap);
return;
}
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(ino, clrmap);
if (f == IFDIR || f == IFATTRDIR) {
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(ino, dirmap);
}
if (BIT(ino, activemap)) {
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(ino, nodmap);
/* attribute changes impact the root */
if (f == IFATTRDIR)
BIS(UFSROOTINO, nodmap);
if (f != IFREG && f != IFDIR && f != IFATTRDIR && f != IFLNK) {
o_esize += 1;
return;
}
est(ip);
}
}
static struct shcount {
struct shcount *higher, *lower;
ino_t ino;
unsigned long count;
} shcounts = {
NULL, NULL,
0,
0
};
static struct shcount *shc = NULL;
void
markshad(ip)
struct dinode *ip;
{
ino_t shadow;
if (ip->di_shadow == 0)
return;
if (shc == NULL)
shc = &shcounts;
shadow = (ino_t)(unsigned)(ip->di_shadow);
while ((shadow > shc->ino) && (shc->higher))
shc = shc->higher;
while ((shadow < shc->ino) && (shc->lower))
shc = shc->lower;
if (shadow != shc->ino) {
struct shcount *new;
new = (struct shcount *)xcalloc(1, sizeof (*new));
new->higher = shc->higher;
if (shc->higher != NULL)
shc->higher->lower = new;
shc->higher = new;
new->lower = shc;
shc = new;
shc->ino = shadow;
}
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(shadow, shamap);
shc->count++;
}
void
estshad(ip)
struct dinode *ip;
{
u_offset_t esizeprime;
u_offset_t tmpesize;
if (ip->di_size <= sizeof (union u_shadow))
return;
while ((ino > shc->ino) && (shc->higher))
shc = shc->higher;
while ((ino < shc->ino) && (shc->lower))
shc = shc->lower;
if (ino != shc->ino)
return; /* xxx panic? complain? */
tmpesize = (o_esize + f_esize);
esizeprime = tmpesize;
est(ip);
esizeprime = tmpesize - esizeprime;
esizeprime *= shc->count - 1;
f_esize += esizeprime;
}
void
freeshad()
{
if (shc == NULL)
return;
while (shc->higher)
shc = shc->higher;
while (shc->lower) {
shc = shc->lower;
if (shc->higher) /* else panic? */
(void) free(shc->higher);
}
/*
* This should be unnecessary, but do it just to be safe.
* Note that shc might be malloc'd or static, so can't free().
*/
bzero(shc, sizeof (*shc));
}
void
add(ip)
struct dinode *ip;
{
int i;
u_offset_t filesize;
if (BIT(ino, nodmap))
return;
if ((ip->di_mode & IFMT) != IFDIR &&
(ip->di_mode & IFMT) != IFATTRDIR) {
(void) snprintf(msgbuf, sizeof (msgbuf), gettext(
"Warning - directory at inode `%lu' vanished!\n"), ino);
msg(msgbuf);
/* LINTED: 32-bit to 8-bit assignment ok */
BIC(ino, dirmap);
return;
}
nsubdir = 0;
dadded = 0;
filesize = ip->di_size;
for (i = 0; i < NDADDR; i++) {
if (ip->di_db[i] != 0)
/* LINTED dblksize/blkoff does a safe cast here */
dsrch(ip->di_db[i], (ulong_t)dblksize(sblock, ip, i),
filesize);
filesize -= (unsigned)(sblock->fs_bsize);
}
for (i = 0; i < NIADDR; i++) {
if (ip->di_ib[i] != 0)
indir(ip->di_ib[i], i, &filesize);
}
if (dadded) {
nadded++;
if (!BIT(ino, nodmap)) {
/* LINTED: 32-bit to 8-bit assignment ok */
BIS(ino, nodmap);
if ((ip->di_mode & IFMT) == IFATTRDIR) {
/* attribute changes "auto-percolate" to root */
BIS(UFSROOTINO, nodmap);
}
est(ip);
}
}
if (nsubdir == 0) {
if (!BIT(ino, nodmap)) {
/* LINTED: 32-bit to 8-bit assignment ok */
BIC(ino, dirmap);
}
}
}
static void
indir(d, n, filesize)
daddr32_t d;
int n;
u_offset_t *filesize;
{
int i;
daddr32_t idblk[MAXNINDIR];
if ((unsigned)(sblock->fs_bsize) > sizeof (idblk)) {
msg(gettext(
"Inconsistency detected: filesystem block size larger than valid maximum.\n"));
dumpabort();
/*NOTREACHED*/
}
if ((unsigned)NINDIR(sblock) > MAXNINDIR) {
/*CSTYLED*/
msg(gettext(
"Inconsistency detected: inode has more indirect \
blocks than valid maximum.\n"));
dumpabort();
/*NOTREACHED*/
}
if (dadded || *filesize == 0)
return;
#ifdef lint
idblk[0] = '\0';
#endif /* lint */
/* xxx sanity check sblock contents before trusting them */
bread(fsbtodb(sblock, d), (uchar_t *)idblk, (size_t)sblock->fs_bsize);
if (n <= 0) {
for (i = 0; i < NINDIR(sblock); i++) {
d = idblk[i];
if (d != 0)
dsrch(d, (ulong_t)(uint32_t)sblock->fs_bsize,
*filesize);
*filesize -= (unsigned)(sblock->fs_bsize);
}
} else {
n--;
for (i = 0; i < NINDIR(sblock); i++) {
d = idblk[i];
if (d != 0)
indir(d, n, filesize);
}
}
}
void
dirdump(ip)
struct dinode *ip;
{
/* watchout for dir inodes deleted and maybe reallocated */
if (((ip->di_mode & IFMT) != IFDIR &&
(ip->di_mode & IFMT) != IFATTRDIR) || ip->di_nlink < 2) {
(void) snprintf(msgbuf, sizeof (msgbuf), gettext(
"Warning - directory at inode `%lu' vanished!\n"),
ino);
msg(msgbuf);
return;
}
lf_dump(ip);
}
static u_offset_t loffset; /* current offset in file (ufsdump) */
static void
lf_dumpmeta(ip)
struct dinode *ip;
{
if ((ip->di_shadow == 0) || shortmeta)
return;
lf_dumpinode(getino((ino_t)(unsigned)(ip->di_shadow)));
}
int
hasshortmeta(ip)
struct dinode **ip;
{
ino_t savino;
int rc;
if ((*ip)->di_shadow == 0)
return (0);
savino = ino;
*ip = getino((ino_t)(unsigned)((*ip)->di_shadow));
rc = ((*ip)->di_size <= sizeof (union u_shadow));
*ip = getino(ino = savino);
return (rc);
}
void
lf_dumpinode(ip)
struct dinode *ip;
{
int i;
u_offset_t size;
i = ip->di_mode & IFMT;
if (i == 0 || ip->di_nlink <= 0)
return;
spcl.c_dinode = *ip;
spcl.c_count = 0;
if ((i != IFDIR && i != IFATTRDIR && i != IFREG && i != IFLNK &&
i != IFSHAD) || ip->di_size == 0) {
toslave(dospcl, ino);
return;
}
size = NDADDR * (unsigned)(sblock->fs_bsize);
if (size > ip->di_size)
size = ip->di_size;
lf_blksout(&ip->di_db[0], size);
size = ip->di_size - size;
if (size > 0) {
for (i = 0; i < NIADDR; i++) {
lf_dmpindir(ip->di_ib[i], i, &size);
if (size == 0)
break;
}
}
}
void
lf_dump(ip)
struct dinode *ip;
{
if ((!BIT(ino, nodmap)) && (!BIT(ino, shamap)))
return;
shortmeta = hasshortmeta(&ip);
if (shortmeta) {
ip = getino((ino_t)(unsigned)(ip->di_shadow));
/* assume spcl.c_shadow is smaller than 1 block */
bread(fsbtodb(sblock, ip->di_db[0]),
(uchar_t *)spcl.c_shadow.c_shadow, sizeof (spcl.c_shadow));
spcl.c_flags |= DR_HASMETA;
} else {
spcl.c_flags &= ~DR_HASMETA;
}
ip = getino(ino);
loffset = 0;
if (newtape) {
spcl.c_type = TS_TAPE;
} else if (pos)
spcl.c_type = TS_ADDR;
else
spcl.c_type = TS_INODE;
newtape = 0;
lf_dumpinode(ip);
lf_dumpmeta(ip);
pos = 0;
}
static void
lf_dmpindir(blk, lvl, size)
daddr32_t blk;
int lvl;
u_offset_t *size;
{
int i;
u_offset_t cnt;
daddr32_t idblk[MAXNINDIR];
if ((unsigned)(sblock->fs_bsize) > sizeof (idblk)) {
msg(gettext(
"Inconsistency detected: filesystem block size larger than valid maximum.\n"));
dumpabort();
/*NOTREACHED*/
}
if ((unsigned)NINDIR(sblock) > MAXNINDIR) {
msg(gettext(
"Inconsistency detected: inode has more indirect \
blocks than valid maximum.\n"));
dumpabort();
/*NOTREACHED*/
}
if (blk != 0)
bread(fsbtodb(sblock, blk), (uchar_t *)idblk,
(size_t)sblock->fs_bsize);
else
bzero((char *)idblk, (size_t)sblock->fs_bsize);
if (lvl <= 0) {
cnt = (u_offset_t)(unsigned)NINDIR(sblock) *
(u_offset_t)(unsigned)(sblock->fs_bsize);
if (cnt > *size)
cnt = *size;
*size -= cnt;
lf_blksout(&idblk[0], cnt);
return;
}
lvl--;
for (i = 0; i < NINDIR(sblock); i++) {
lf_dmpindir(idblk[i], lvl, size);
if (*size == 0)
return;
}
}
static void
lf_blksout(blkp, bytes)
daddr32_t *blkp;
u_offset_t bytes;
{
u_offset_t i;
u_offset_t tbperfsb = (unsigned)(sblock->fs_bsize / tp_bsize);
u_offset_t j, k, count;
u_offset_t bytepos, diff;
u_offset_t bytecnt = 0;
off_t byteoff = 0; /* bytes to skip within first f/s block */
off_t fragoff = 0; /* frags to skip within first f/s block */
u_offset_t tpblkoff = 0; /* tape blocks to skip in first f/s block */
u_offset_t tpblkskip = 0; /* total tape blocks to skip */
u_offset_t skip; /* tape blocks to skip this pass */
if (pos) {
/*
* We get here if a slave throws a signal to the
* master indicating a partially dumped file.
* Begin by figuring out what was undone.
*/
bytepos = (offset_t)pos * tp_bsize;
if ((loffset + bytes) <= bytepos) {
/* This stuff was dumped already, forget it. */
loffset += (u_offset_t)tp_bsize *
/* LINTED: spurious complaint on sign-extending */
d_howmany(bytes, (u_offset_t)tp_bsize);
return;
}
if (loffset < bytepos) {
/*
* Some of this was dumped, some wasn't.
* Figure out what was done and skip it.
*/
diff = bytepos - loffset;
/* LINTED: spurious complaint on sign-extending */
tpblkskip = d_howmany(diff, (u_offset_t)tp_bsize);
/* LINTED room after EOT is only a few MB */
blkp += (int)(diff / sblock->fs_bsize);
bytecnt = diff % (unsigned)(sblock->fs_bsize);
/* LINTED: result fits, due to modulus */
byteoff = bytecnt % (off_t)(sblock->fs_fsize);
/* LINTED: spurious complaint on sign-extending */
tpblkoff = d_howmany(bytecnt,
(u_offset_t)(unsigned)tp_bsize);
/* LINTED: result fits, due to modulus */
fragoff = bytecnt / (off_t)(sblock->fs_fsize);
bytecnt = (unsigned)(sblock->fs_bsize) - bytecnt;
}
}
loffset += bytes;
while (bytes > 0) {
if (bytes < TP_NINDIR*tp_bsize)
/* LINTED: spurious complaint on sign-extending */
count = d_howmany(bytes, (u_offset_t)tp_bsize);
else
count = TP_NINDIR;
if (tpblkskip) {
if (tpblkskip < TP_NINDIR) {
bytes -= (tpblkskip * (u_offset_t)tp_bsize);
skip = tpblkskip;
tpblkskip = 0;
} else {
bytes -= (offset_t)TP_NINDIR*tp_bsize;
tpblkskip -= TP_NINDIR;
continue;
}
} else
skip = 0;
assert(tbperfsb >= tpblkoff);
assert((count - skip) <= TP_NINDIR);
for (j = 0, k = 0; j < count - skip; j++, k++) {
spcl.c_addr[j] = (blkp[k] != 0);
for (i = tbperfsb - tpblkoff; --i > 0; j++)
spcl.c_addr[j+1] = spcl.c_addr[j];
tpblkoff = 0;
}
/* LINTED (count - skip) will always fit into an int32_t */
spcl.c_count = count - skip;
toslave(dospcl, ino);
bytecnt = MIN(bytes, bytecnt ?
bytecnt : (unsigned)(sblock->fs_bsize));
j = 0;
while (j < count - skip) {
if (*blkp != 0) {
/* LINTED: fragoff fits into 32 bits */
dmpblk(*blkp+(int32_t)fragoff,
/* LINTED: bytecnt fits into 32 bits */
(size_t)bytecnt, byteoff);
}
blkp++;
bytes -= bytecnt;
/* LINTED: spurious complaint on sign-extending */
j += d_howmany(bytecnt, (u_offset_t)tp_bsize);
bytecnt = MIN(bytes, (unsigned)(sblock->fs_bsize));
byteoff = 0;
fragoff = 0;
}
spcl.c_type = TS_ADDR;
bytecnt = 0;
}
pos = 0;
}
void
bitmap(map, typ)
uchar_t *map;
int typ;
{
int i;
u_offset_t count;
uchar_t *cp;
if (!newtape)
spcl.c_type = typ;
else
newtape = 0;
for (i = 0; i < TP_NINDIR; i++)
spcl.c_addr[i] = 1;
/* LINTED: spurious complaint on sign-extending */
count = d_howmany(msiz * sizeof (map[0]), tp_bsize) - pos;
for (cp = &map[pos * tp_bsize]; count > 0;
count -= (u_offset_t)(unsigned)spcl.c_count) {
if (leftover) {
spcl.c_count = leftover;
leftover = 0;
} else {
/* LINTED value always less than INT32_MAX */
spcl.c_count = count > TP_NINDIR ? TP_NINDIR : count;
}
spclrec();
for (i = 0; i < spcl.c_count; i++, cp += tp_bsize)
taprec(cp, 0, tp_bsize);
spcl.c_type = TS_ADDR;
}
}
static void
dsrch(d, size, filesize)
daddr32_t d;
ulong_t size; /* block size */
u_offset_t filesize;
{
struct direct *dp;
struct dinode *ip;
ulong_t loc;
char dblk[MAXBSIZE];
if (dadded || filesize == 0)
return;
if (filesize > (u_offset_t)size)
filesize = (u_offset_t)size;
if (sizeof (dblk) < roundup(filesize, DEV_BSIZE)) {
msg(gettext(
"Inconsistency detected: filesystem block size larger than valid maximum.\n"));
dumpabort();
/*NOTREACHED*/
}
#ifdef lint
dblk[0] = '\0';
#endif /* lint */
/* LINTED ufs disk addresses always fit into 32 bits */
bread(fsbtodb(sblock, d), (uchar_t *)dblk,
/* LINTED from sizeof check above, roundup() <= max(size_t) */
(size_t)(roundup(filesize, DEV_BSIZE)));
loc = 0;
while ((u_offset_t)loc < filesize) {
/*LINTED [dblk is char[], loc (dp->d_reclen) % 4 == 0]*/
dp = (struct direct *)(dblk + loc);
if (dp->d_reclen == 0) {
(void) snprintf(msgbuf, sizeof (msgbuf), gettext(
"Warning - directory at inode `%lu' is corrupted\n"),
ino);
msg(msgbuf);
break;
}
loc += dp->d_reclen;
if (dp->d_ino == 0)
continue;
if (dp->d_name[0] == '.') {
if (dp->d_name[1] == '\0') {
if ((ino_t)(dp->d_ino) != ino) {
(void) snprintf(msgbuf, sizeof (msgbuf),
gettext(
"Warning - directory at inode `%lu' is corrupted:\n\
\t\".\" points to inode `%lu' - run fsck\n"),
ino, dp->d_ino);
msg(msgbuf);
}
continue;
}
if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') {
if (!BIT(dp->d_ino, dirmap) &&
((ip = getino(ino)) == NULL ||
(ip->di_mode & IFMT) != IFATTRDIR)) {
(void) snprintf(msgbuf, sizeof (msgbuf),
gettext(
"Warning - directory at inode `%lu' is corrupted:\n\
\t\"..\" points to non-directory inode `%lu' - run fsck\n"),
ino, dp->d_ino);
msg(msgbuf);
}
continue;
}
}
if (BIT(dp->d_ino, nodmap)) {
dadded++;
return;
}
if (BIT(dp->d_ino, dirmap))
nsubdir++;
}
}
#define CACHESIZE 32
struct dinode *
getino(ino)
ino_t ino;
{
static ino_t minino, maxino;
static struct dinode itab[MAXINOPB];
static struct dinode icache[CACHESIZE];
static ino_t icacheval[CACHESIZE], lasti = 0;
static int cacheoff = 0;
int i;
if (ino >= minino && ino < maxino) {
lasti = ino;
return (&itab[ino - minino]);
}
/* before we do major i/o, check for a secondary cache hit */
for (i = 0; i < CACHESIZE; i++)
if (icacheval[i] == ino)
return (icache + i);
/* we need to do major i/o. throw the last inode retrieved into */
/* the cache. note: this copies garbage the first time it is */
/* used, but no harm done. */
icacheval[cacheoff] = lasti;
bcopy(itab + (lasti - minino), icache + cacheoff, sizeof (itab[0]));
lasti = ino;
if (++cacheoff >= CACHESIZE)
cacheoff = 0;
#define INOPERDB (DEV_BSIZE / sizeof (struct dinode))
minino = ino &~ (INOPERDB - 1);
maxino = ((itog(sblock, ino) + 1) * (unsigned)(sblock->fs_ipg));
if (maxino > minino + MAXINOPB)
maxino = minino + MAXINOPB;
bread(
/* LINTED: can't make up for broken system macros here */
(fsbtodb(sblock, itod(sblock, ino)) + itoo(sblock, ino) / INOPERDB),
/* LINTED: (max - min) * size fits into a size_t */
(uchar_t *)itab, (size_t)((maxino - minino) * sizeof (*itab)));
return (&itab[ino - minino]);
}
#define BREADEMAX 32
#ifdef NO__LONGLONG__
#define DEV_LSEEK(fd, offset, whence) \
lseek((fd), (((off_t)(offset))*DEV_BSIZE), (whence))
#else
#define DEV_LSEEK(fd, offset, whence) \
llseek((fd), (((offset_t)(((unsigned)offset)))*DEV_BSIZE), (whence))
#endif
#define BREAD_FAIL(buf, size) { \
breaderrors += 1; \
bzero(buf, (size_t)size); \
}
void
bread(da, ba, cnt)
diskaddr_t da;
uchar_t *ba;
size_t cnt;
{
caddr_t maddr;
uchar_t *dest;
int saverr;
int n;
size_t len;
off64_t filoff;
off64_t mapoff;
off64_t displacement;
static size_t pagesize = 0;
static int breaderrors = 0;
/* mechanics for caching small bread requests. these are */
/* often small ACLs that are used over and over. */
static uchar_t bcache[DEV_BSIZE * CACHESIZE];
static diskaddr_t bcacheval[CACHESIZE];
static int cacheoff = 0;
int i;
if ((cnt >= DEV_BSIZE) && (mapfd != -1)) {
if (pagesize == 0)
pagesize = getpagesize();
/*
* We depend on mmap(2)'s guarantee that mapping a
* partial page will cause the remainder of the page
* to be zero-filled.
*/
filoff = ((off64_t)da) * DEV_BSIZE;
displacement = filoff & (pagesize - 1);
mapoff = filoff - displacement;
/* LINTED offset will fit into 32 bits */
len = (size_t)roundup(cnt + (filoff - mapoff), pagesize);
maddr = mmap64(NULL, len, PROT_READ, MAP_SHARED, mapfd, mapoff);
if (maddr != MAP_FAILED) {
(void) memcpy(ba, maddr + displacement, cnt);
(void) munmap(maddr, len);
return;
}
}
if (DEV_LSEEK(fi, da, L_SET) < 0) {
saverr = errno;
msg(gettext("bread: dev_seek error: %s\n"), strerror(saverr));
/* Don't know where we are, return the least-harmful data */
BREAD_FAIL(ba, cnt);
return;
}
if (read(fi, ba, (size_t)cnt) == (size_t)cnt)
return;
while (cnt != 0) {
if (da >= fsbtodb(sblock, sblock->fs_size)) {
msg(gettext(
"Warning - block %llu is beyond the end of `%s'\n"),
da, disk);
BREAD_FAIL(ba, cnt);
break;
}
if (DEV_LSEEK(fi, da, L_SET) < 0) {
msg(gettext("%s: %s error\n"), "bread", "DEV_LSEEK2");
BREAD_FAIL(ba, cnt);
break;
}
if (cnt < DEV_BSIZE) {
/* small read. check for cache hit. */
for (i = 0; i < CACHESIZE; i++)
if (bcacheval[i] == da) {
bcopy(bcache + (i * DEV_BSIZE),
ba, cnt);
return;
}
/* no cache hit; throw this one into the cache... */
len = cnt;
dest = bcache + (cacheoff * DEV_BSIZE);
bcacheval[cacheoff] = da;
if (++cacheoff >= CACHESIZE)
cacheoff = 0;
} else {
len = DEV_BSIZE;
dest = ba;
}
n = read(fi, dest, DEV_BSIZE);
if (n != DEV_BSIZE) {
n = MAX(n, 0);
bzero(dest+n, DEV_BSIZE-n);
breaderrors += 1;
msg(gettext(
"Warning - cannot read sector %llu of `%s'\n"),
da, disk);
}
if (dest != ba)
bcopy(dest, ba, len);
da++;
/* LINTED character pointers aren't signed */
ba += len;
cnt -= len;
}
if (breaderrors > BREADEMAX) {
msg(gettext(
"More than %d block read errors from dump device `%s'\n"),
BREADEMAX, disk);
dumpailing();
breaderrors = 0;
}
}