Home | History | Annotate | Line # | Download | only in dump
traverse.c revision 1.22
      1 /*	$NetBSD: traverse.c,v 1.22 1998/12/28 13:38:29 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1980, 1988, 1991, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 #if 0
     39 static char sccsid[] = "@(#)traverse.c	8.7 (Berkeley) 6/15/95";
     40 #else
     41 __RCSID("$NetBSD: traverse.c,v 1.22 1998/12/28 13:38:29 lukem Exp $");
     42 #endif
     43 #endif /* not lint */
     44 
     45 #include <sys/param.h>
     46 #include <sys/time.h>
     47 #include <sys/stat.h>
     48 #ifdef sunos
     49 #include <sys/vnode.h>
     50 
     51 #include <ufs/fs.h>
     52 #include <ufs/fsdir.h>
     53 #include <ufs/inode.h>
     54 #else
     55 #include <ufs/ufs/dir.h>
     56 #include <ufs/ufs/dinode.h>
     57 #include <ufs/ffs/fs.h>
     58 #include <ufs/ffs/ffs_extern.h>
     59 #endif
     60 
     61 #include <protocols/dumprestore.h>
     62 
     63 #include <ctype.h>
     64 #include <errno.h>
     65 #include <fts.h>
     66 #include <stdio.h>
     67 #ifdef __STDC__
     68 #include <string.h>
     69 #include <unistd.h>
     70 #endif
     71 
     72 #include "dump.h"
     73 
     74 #define	HASDUMPEDFILE	0x1
     75 #define	HASSUBDIRS	0x2
     76 
     77 #ifdef	FS_44INODEFMT
     78 typedef	quad_t fsizeT;
     79 #else
     80 typedef	int32_t fsizeT;
     81 #endif
     82 
     83 static	int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size));
     84 static	void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size));
     85 static	int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize));
     86 
     87 /*
     88  * This is an estimation of the number of TP_BSIZE blocks in the file.
     89  * It estimates the number of blocks in files with holes by assuming
     90  * that all of the blocks accounted for by di_blocks are data blocks
     91  * (when some of the blocks are usually used for indirect pointers);
     92  * hence the estimate may be high.
     93  */
     94 long
     95 blockest(dp)
     96 	struct dinode *dp;
     97 {
     98 	long blkest, sizeest;
     99 
    100 	/*
    101 	 * dp->di_size is the size of the file in bytes.
    102 	 * dp->di_blocks stores the number of sectors actually in the file.
    103 	 * If there are more sectors than the size would indicate, this just
    104 	 *	means that there are indirect blocks in the file or unused
    105 	 *	sectors in the last file block; we can safely ignore these
    106 	 *	(blkest = sizeest below).
    107 	 * If the file is bigger than the number of sectors would indicate,
    108 	 *	then the file has holes in it.	In this case we must use the
    109 	 *	block count to estimate the number of data blocks used, but
    110 	 *	we use the actual size for estimating the number of indirect
    111 	 *	dump blocks (sizeest vs. blkest in the indirect block
    112 	 *	calculation).
    113 	 */
    114 	blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE);
    115 	sizeest = howmany(dp->di_size, TP_BSIZE);
    116 	if (blkest > sizeest)
    117 		blkest = sizeest;
    118 	if (dp->di_size > sblock->fs_bsize * NDADDR) {
    119 		/* calculate the number of indirect blocks on the dump tape */
    120 		blkest +=
    121 			howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
    122 			TP_NINDIR);
    123 	}
    124 	return (blkest + 1);
    125 }
    126 
    127 /* Auxiliary macro to pick up files changed since previous dump. */
    128 #define	CHANGEDSINCE(dp, t) \
    129 	((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t))
    130 
    131 /* The WANTTODUMP macro decides whether a file should be dumped. */
    132 #ifdef UF_NODUMP
    133 #define	WANTTODUMP(dp) \
    134 	(CHANGEDSINCE(dp, iswap32(spcl.c_ddate)) && \
    135 	 (nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP))
    136 #else
    137 #define	WANTTODUMP(dp) CHANGEDSINCE(dp, iswap32(spcl.c_ddate))
    138 #endif
    139 
    140 /*
    141  * Determine if given inode should be dumped
    142  */
    143 void
    144 mapfileino(ino, tapesize, dirskipped)
    145 	ino_t ino;
    146 	long *tapesize;
    147 	int *dirskipped;
    148 {
    149 	int mode;
    150 	struct dinode *dp;
    151 
    152 	/*
    153 	 * Skip inode if we've already marked it for dumping
    154 	 */
    155 	if (TSTINO(ino, usedinomap))
    156 		return;
    157 	dp = getino(ino);
    158 	if ((mode = (dp->di_mode & IFMT)) == 0)
    159 		return;
    160 	SETINO(ino, usedinomap);
    161 	if (mode == IFDIR)
    162 		SETINO(ino, dumpdirmap);
    163 	if (WANTTODUMP(dp)) {
    164 		SETINO(ino, dumpinomap);
    165 		if (mode != IFREG && mode != IFDIR && mode != IFLNK)
    166 			*tapesize += 1;
    167 		else
    168 			*tapesize += blockest(dp);
    169 		return;
    170 	}
    171 	if (mode == IFDIR)
    172 		*dirskipped = 1;
    173 }
    174 
    175 /*
    176  * Dump pass 1.
    177  *
    178  * Walk the inode list for a filesystem to find all allocated inodes
    179  * that have been modified since the previous dump time. Also, find all
    180  * the directories in the filesystem.
    181  */
    182 int
    183 mapfiles(maxino, tapesize, disk, dirv)
    184 	ino_t maxino;
    185 	long *tapesize;
    186 	char *disk;
    187 	char * const *dirv;
    188 {
    189 	int anydirskipped = 0;
    190 
    191 	if (dirv != NULL) {
    192 		char	 curdir[MAXPATHLEN];
    193 		FTS	*dirh;
    194 		FTSENT	*entry;
    195 		int	 d;
    196 
    197 		if (getcwd(curdir, sizeof(curdir)) == NULL) {
    198 			msg("Can't determine cwd: %s\n", strerror(errno));
    199 			dumpabort(0);
    200 		}
    201 		if ((dirh = fts_open(dirv, FTS_PHYSICAL|FTS_SEEDOT|FTS_XDEV,
    202 		    		    NULL)) == NULL) {
    203 			msg("fts_open failed: %s\n", strerror(errno));
    204 			dumpabort(0);
    205 		}
    206 		while ((entry = fts_read(dirh)) != NULL) {
    207 			switch (entry->fts_info) {
    208 			case FTS_DNR:		/* an error */
    209 			case FTS_ERR:
    210 			case FTS_NS:
    211 				msg("Can't fts_read %s: %s\n", entry->fts_path,
    212 				    strerror(errno));
    213 			case FTS_DP:		/* already seen dir */
    214 				continue;
    215 			}
    216 			mapfileino(entry->fts_statp->st_ino, tapesize,
    217 			    &anydirskipped);
    218 		}
    219 		(void)fts_close(dirh);
    220 
    221 		/*
    222 		 * Add any parent directories
    223 		 */
    224 		for (d = 0 ; dirv[d] != NULL ; d++) {
    225 			char path[MAXPATHLEN];
    226 
    227 			if (dirv[d][0] != '/')
    228 				(void)snprintf(path, sizeof(path), "%s/%s",
    229 				    curdir, dirv[d]);
    230 			else
    231 				(void)snprintf(path, sizeof(path), "%s",
    232 				    dirv[d]);
    233 			while (strcmp(path, disk) != 0) {
    234 				char *p;
    235 				struct stat sb;
    236 
    237 				if (*path == '\0')
    238 					break;
    239 				if ((p = strrchr(path, '/')) == NULL)
    240 					break;
    241 				if (p == path)
    242 					break;
    243 				*p = '\0';
    244 				if (stat(path, &sb) == -1) {
    245 					msg("Can't stat %s: %s\n", path,
    246 					    strerror(errno));
    247 					break;
    248 				}
    249 				mapfileino(sb.st_ino, tapesize, &anydirskipped);
    250 			}
    251 		}
    252 
    253 		/*
    254 		 * Ensure that the root inode actually appears in the
    255 		 * file list for a subdir
    256 		 */
    257 		mapfileino(ROOTINO, tapesize, &anydirskipped);
    258 	} else {
    259 		ino_t ino;
    260 
    261 		for (ino = ROOTINO; ino < maxino; ino++) {
    262 			mapfileino(ino, tapesize, &anydirskipped);
    263 		}
    264 	}
    265 	/*
    266 	 * Restore gets very upset if the root is not dumped,
    267 	 * so ensure that it always is dumped.
    268 	 */
    269 	SETINO(ROOTINO, dumpinomap);
    270 	return (anydirskipped);
    271 }
    272 
    273 /*
    274  * Dump pass 2.
    275  *
    276  * Scan each directory on the filesystem to see if it has any modified
    277  * files in it. If it does, and has not already been added to the dump
    278  * list (because it was itself modified), then add it. If a directory
    279  * has not been modified itself, contains no modified files and has no
    280  * subdirectories, then it can be deleted from the dump list and from
    281  * the list of directories. By deleting it from the list of directories,
    282  * its parent may now qualify for the same treatment on this or a later
    283  * pass using this algorithm.
    284  */
    285 int
    286 mapdirs(maxino, tapesize)
    287 	ino_t maxino;
    288 	long *tapesize;
    289 {
    290 	struct	dinode *dp;
    291 	int i, isdir;
    292 	char *map;
    293 	ino_t ino;
    294 	long filesize;
    295 	int ret, change = 0;
    296 
    297 	isdir = 0;		/* XXX just to get gcc to shut up */
    298 	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
    299 		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
    300 			isdir = *map++;
    301 		else
    302 			isdir >>= 1;
    303 		if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap))
    304 			continue;
    305 		dp = getino(ino);
    306 		filesize = dp->di_size;
    307 		for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
    308 			if (dp->di_db[i] != 0)
    309 				ret |= searchdir(ino, iswap32(dp->di_db[i]),
    310 					(long)dblksize(sblock, dp, i),
    311 					filesize);
    312 			if (ret & HASDUMPEDFILE)
    313 				filesize = 0;
    314 			else
    315 				filesize -= sblock->fs_bsize;
    316 		}
    317 		for (i = 0; filesize > 0 && i < NIADDR; i++) {
    318 			if (dp->di_ib[i] == 0)
    319 				continue;
    320 			ret |= dirindir(ino, dp->di_ib[i], i, &filesize);
    321 		}
    322 		if (ret & HASDUMPEDFILE) {
    323 			SETINO(ino, dumpinomap);
    324 			*tapesize += blockest(dp);
    325 			change = 1;
    326 			continue;
    327 		}
    328 		if ((ret & HASSUBDIRS) == 0) {
    329 			if (!TSTINO(ino, dumpinomap)) {
    330 				CLRINO(ino, dumpdirmap);
    331 				change = 1;
    332 			}
    333 		}
    334 	}
    335 	return (change);
    336 }
    337 
    338 /*
    339  * Read indirect blocks, and pass the data blocks to be searched
    340  * as directories. Quit as soon as any entry is found that will
    341  * require the directory to be dumped.
    342  */
    343 static int
    344 dirindir(ino, blkno, ind_level, filesize)
    345 	ino_t ino;
    346 	daddr_t blkno;
    347 	int ind_level;
    348 	long *filesize;
    349 {
    350 	int ret = 0;
    351 	int i;
    352 	daddr_t	idblk[MAXNINDIR];
    353 
    354 	bread(fsbtodb(sblock, iswap32(blkno)), (char *)idblk,
    355 		(int)sblock->fs_bsize);
    356 	if (ind_level <= 0) {
    357 		for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
    358 			blkno = idblk[i];
    359 			if (blkno != 0)
    360 				ret |= searchdir(ino, iswap32(blkno), sblock->fs_bsize,
    361 					*filesize);
    362 			if (ret & HASDUMPEDFILE)
    363 				*filesize = 0;
    364 			else
    365 				*filesize -= sblock->fs_bsize;
    366 		}
    367 		return (ret);
    368 	}
    369 	ind_level--;
    370 	for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
    371 		blkno = idblk[i];
    372 		if (blkno != 0)
    373 			ret |= dirindir(ino, blkno, ind_level, filesize);
    374 	}
    375 	return (ret);
    376 }
    377 
    378 /*
    379  * Scan a disk block containing directory information looking to see if
    380  * any of the entries are on the dump list and to see if the directory
    381  * contains any subdirectories.
    382  */
    383 static int
    384 searchdir(ino, blkno, size, filesize)
    385 	ino_t ino;
    386 	daddr_t blkno;
    387 	long size;
    388 	long filesize;
    389 {
    390 	struct direct *dp;
    391 	long loc, ret = 0;
    392 	char dblk[MAXBSIZE];
    393 
    394 	bread(fsbtodb(sblock, blkno), dblk, (int)size);
    395 	if (filesize < size)
    396 		size = filesize;
    397 	for (loc = 0; loc < size; ) {
    398 		dp = (struct direct *)(dblk + loc);
    399 		if (dp->d_reclen == 0) {
    400 			msg("corrupted directory, inumber %d\n", ino);
    401 			break;
    402 		}
    403 		loc += iswap16(dp->d_reclen);
    404 		if (dp->d_ino == 0)
    405 			continue;
    406 		if (dp->d_name[0] == '.') {
    407 			if (dp->d_name[1] == '\0')
    408 				continue;
    409 			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
    410 				continue;
    411 		}
    412 		if (TSTINO(iswap32(dp->d_ino), dumpinomap)) {
    413 			ret |= HASDUMPEDFILE;
    414 			if (ret & HASSUBDIRS)
    415 				break;
    416 		}
    417 		if (TSTINO(iswap32(dp->d_ino), dumpdirmap)) {
    418 			ret |= HASSUBDIRS;
    419 			if (ret & HASDUMPEDFILE)
    420 				break;
    421 		}
    422 	}
    423 	return (ret);
    424 }
    425 
    426 /*
    427  * Dump passes 3 and 4.
    428  *
    429  * Dump the contents of an inode to tape.
    430  */
    431 void
    432 dumpino(dp, ino)
    433 	struct dinode *dp;
    434 	ino_t ino;
    435 {
    436 	int ind_level, cnt;
    437 	fsizeT size;
    438 	char buf[TP_BSIZE];
    439 
    440 	if (newtape) {
    441 		newtape = 0;
    442 		dumpmap(dumpinomap, TS_BITS, ino);
    443 	}
    444 	CLRINO(ino, dumpinomap);
    445 	if (needswap)
    446 		ffs_dinode_swap(dp, &spcl.c_dinode);
    447 	else
    448 		spcl.c_dinode = *dp;
    449 	spcl.c_type = iswap32(TS_INODE);
    450 	spcl.c_count = 0;
    451 	switch (dp->di_mode & IFMT) {
    452 
    453 	case 0:
    454 		/*
    455 		 * Freed inode.
    456 		 */
    457 		return;
    458 
    459 	case IFLNK:
    460 		/*
    461 		 * Check for short symbolic link.
    462 		 */
    463 		if (dp->di_size > 0 &&
    464 #ifdef FS_44INODEFMT
    465 		    (dp->di_size < sblock->fs_maxsymlinklen ||
    466 		     (sblock->fs_maxsymlinklen == 0 && dp->di_blocks == 0))) {
    467 #else
    468 		    dp->di_blocks == 0) {
    469 #endif
    470 			spcl.c_addr[0] = 1;
    471 			spcl.c_count = iswap32(1);
    472 			writeheader(ino);
    473 			memmove(buf, dp->di_shortlink, (u_long)dp->di_size);
    474 			buf[dp->di_size] = '\0';
    475 			writerec(buf, 0);
    476 			return;
    477 		}
    478 		/* fall through */
    479 
    480 	case IFDIR:
    481 	case IFREG:
    482 		if (dp->di_size > 0)
    483 			break;
    484 		/* fall through */
    485 
    486 	case IFIFO:
    487 	case IFSOCK:
    488 	case IFCHR:
    489 	case IFBLK:
    490 		writeheader(ino);
    491 		return;
    492 
    493 	default:
    494 		msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT);
    495 		return;
    496 	}
    497 	if (dp->di_size > NDADDR * sblock->fs_bsize)
    498 		cnt = NDADDR * sblock->fs_frag;
    499 	else
    500 		cnt = howmany(dp->di_size, sblock->fs_fsize);
    501 	blksout(&dp->di_db[0], cnt, ino);
    502 	if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0)
    503 		return;
    504 	for (ind_level = 0; ind_level < NIADDR; ind_level++) {
    505 		dmpindir(ino, dp->di_ib[ind_level], ind_level, &size);
    506 		if (size <= 0)
    507 			return;
    508 	}
    509 }
    510 
    511 /*
    512  * Read indirect blocks, and pass the data blocks to be dumped.
    513  */
    514 static void
    515 dmpindir(ino, blk, ind_level, size)
    516 	ino_t ino;
    517 	daddr_t blk;
    518 	int ind_level;
    519 	fsizeT *size;
    520 {
    521 	int i, cnt;
    522 	daddr_t idblk[MAXNINDIR];
    523 
    524 	if (blk != 0)
    525 		bread(fsbtodb(sblock, iswap32(blk)), (char *)idblk,
    526 			(int) sblock->fs_bsize);
    527 	else
    528 		memset(idblk, 0, (int)sblock->fs_bsize);
    529 	if (ind_level <= 0) {
    530 		if (*size < NINDIR(sblock) * sblock->fs_bsize)
    531 			cnt = howmany(*size, sblock->fs_fsize);
    532 		else
    533 			cnt = NINDIR(sblock) * sblock->fs_frag;
    534 		*size -= NINDIR(sblock) * sblock->fs_bsize;
    535 		blksout(&idblk[0], cnt, ino);
    536 		return;
    537 	}
    538 	ind_level--;
    539 	for (i = 0; i < NINDIR(sblock); i++) {
    540 		dmpindir(ino, idblk[i], ind_level, size);
    541 		if (*size <= 0)
    542 			return;
    543 	}
    544 }
    545 
    546 /*
    547  * Collect up the data into tape record sized buffers and output them.
    548  */
    549 void
    550 blksout(blkp, frags, ino)
    551 	daddr_t *blkp;
    552 	int frags;
    553 	ino_t ino;
    554 {
    555 	daddr_t *bp;
    556 	int i, j, count, blks, tbperdb;
    557 
    558 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
    559 	tbperdb = sblock->fs_bsize >> tp_bshift;
    560 	for (i = 0; i < blks; i += TP_NINDIR) {
    561 		if (i + TP_NINDIR > blks)
    562 			count = blks;
    563 		else
    564 			count = i + TP_NINDIR;
    565 		for (j = i; j < count; j++)
    566 			if (blkp[j / tbperdb] != 0)
    567 				spcl.c_addr[j - i] = 1;
    568 			else
    569 				spcl.c_addr[j - i] = 0;
    570 		spcl.c_count = iswap32(count - i);
    571 		writeheader(ino);
    572 		bp = &blkp[i / tbperdb];
    573 		for (j = i; j < count; j += tbperdb, bp++)
    574 			if (*bp != 0) {
    575 				if (j + tbperdb <= count)
    576 					dumpblock(iswap32(*bp), (int)sblock->fs_bsize);
    577 				else
    578 					dumpblock(iswap32(*bp), (count - j) * TP_BSIZE);
    579 			}
    580 		spcl.c_type = iswap32(TS_ADDR);
    581 	}
    582 }
    583 
    584 /*
    585  * Dump a map to the tape.
    586  */
    587 void
    588 dumpmap(map, type, ino)
    589 	char *map;
    590 	int type;
    591 	ino_t ino;
    592 {
    593 	int i;
    594 	char *cp;
    595 
    596 	spcl.c_type = iswap32(type);
    597 	spcl.c_count = iswap32(howmany(mapsize * sizeof(char), TP_BSIZE));
    598 	writeheader(ino);
    599 	for (i = 0, cp = map; i < iswap32(spcl.c_count); i++, cp += TP_BSIZE)
    600 		writerec(cp, 0);
    601 }
    602 
    603 /*
    604  * Write a header record to the dump tape.
    605  */
    606 void
    607 writeheader(ino)
    608 	ino_t ino;
    609 {
    610 	int32_t sum, cnt, *lp;
    611 
    612 	spcl.c_inumber = iswap32(ino);
    613 	spcl.c_magic = iswap32(NFS_MAGIC);
    614 	spcl.c_checksum = 0;
    615 	lp = (int32_t *)&spcl;
    616 	sum = 0;
    617 	cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
    618 	while (--cnt >= 0) {
    619 		sum += iswap32(*lp++);
    620 		sum += iswap32(*lp++);
    621 		sum += iswap32(*lp++);
    622 		sum += iswap32(*lp++);
    623 	}
    624 	spcl.c_checksum = iswap32(CHECKSUM - sum);
    625 	writerec((char *)&spcl, 1);
    626 }
    627 
    628 struct dinode *
    629 getino(inum)
    630 	ino_t inum;
    631 {
    632 	static daddr_t minino, maxino;
    633 	static struct dinode inoblock[MAXINOPB];
    634 	int i;
    635 
    636 	curino = inum;
    637 	if (inum >= minino && inum < maxino)
    638 		return (&inoblock[inum - minino]);
    639 	bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock,
    640 	    (int)sblock->fs_bsize);
    641 	if (needswap)
    642 		for (i = 0; i < MAXINOPB; i++)
    643 			ffs_dinode_swap(&inoblock[i], &inoblock[i]);
    644 	minino = inum - (inum % INOPB(sblock));
    645 	maxino = minino + INOPB(sblock);
    646 	return (&inoblock[inum - minino]);
    647 }
    648 
    649 /*
    650  * Read a chunk of data from the disk.
    651  * Try to recover from hard errors by reading in sector sized pieces.
    652  * Error recovery is attempted at most BREADEMAX times before seeking
    653  * consent from the operator to continue.
    654  */
    655 int	breaderrors = 0;
    656 #define	BREADEMAX 32
    657 
    658 void
    659 bread(blkno, buf, size)
    660 	daddr_t blkno;
    661 	char *buf;
    662 	int size;
    663 {
    664 	int cnt, i;
    665 	extern int errno;
    666 
    667 loop:
    668 	if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
    669 		msg("bread: lseek fails\n");
    670 	if ((cnt = read(diskfd, buf, size)) == size)
    671 		return;
    672 	if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
    673 		/*
    674 		 * Trying to read the final fragment.
    675 		 *
    676 		 * NB - dump only works in TP_BSIZE blocks, hence
    677 		 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
    678 		 * It should be smarter about not actually trying to
    679 		 * read more than it can get, but for the time being
    680 		 * we punt and scale back the read only when it gets
    681 		 * us into trouble. (mkm 9/25/83)
    682 		 */
    683 		size -= dev_bsize;
    684 		goto loop;
    685 	}
    686 	if (cnt == -1)
    687 		msg("read error from %s: %s: [block %d]: count=%d\n",
    688 			disk, strerror(errno), blkno, size);
    689 	else
    690 		msg("short read error from %s: [block %d]: count=%d, got=%d\n",
    691 			disk, blkno, size, cnt);
    692 	if (++breaderrors > BREADEMAX) {
    693 		msg("More than %d block read errors from %d\n",
    694 			BREADEMAX, disk);
    695 		broadcast("DUMP IS AILING!\n");
    696 		msg("This is an unrecoverable error.\n");
    697 		if (!query("Do you want to attempt to continue?")){
    698 			dumpabort(0);
    699 			/*NOTREACHED*/
    700 		} else
    701 			breaderrors = 0;
    702 	}
    703 	/*
    704 	 * Zero buffer, then try to read each sector of buffer separately.
    705 	 */
    706 	memset(buf, 0, size);
    707 	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
    708 		if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
    709 			msg("bread: lseek2 fails!\n");
    710 		if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize)
    711 			continue;
    712 		if (cnt == -1) {
    713 			msg("read error from %s: %s: [sector %d]: count=%d\n",
    714 				disk, strerror(errno), blkno, dev_bsize);
    715 			continue;
    716 		}
    717 		msg("short read error from %s: [sector %d]: count=%d, got=%d\n",
    718 			disk, blkno, dev_bsize, cnt);
    719 	}
    720 }
    721