11558Srgrimes/*-
21558Srgrimes * Copyright (c) 1980, 1988, 1991, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
301558Srgrimes#ifndef lint
3136997Scharnier#if 0
3223672Speterstatic char sccsid[] = "@(#)traverse.c	8.7 (Berkeley) 6/15/95";
3336997Scharnier#endif
3436997Scharnierstatic const char rcsid[] =
3550476Speter  "$FreeBSD$";
361558Srgrimes#endif /* not lint */
371558Srgrimes
381558Srgrimes#include <sys/param.h>
391558Srgrimes#include <sys/stat.h>
401558Srgrimes
411558Srgrimes#include <ufs/ufs/dir.h>
421558Srgrimes#include <ufs/ufs/dinode.h>
4323672Speter#include <ufs/ffs/fs.h>
441558Srgrimes
451558Srgrimes#include <protocols/dumprestore.h>
461558Srgrimes
471558Srgrimes#include <ctype.h>
4899562Siedowse#include <errno.h>
4999562Siedowse#include <inttypes.h>
50103949Smike#include <limits.h>
511558Srgrimes#include <stdio.h>
5299562Siedowse#include <stdlib.h>
531558Srgrimes#include <string.h>
5499562Siedowse#include <timeconv.h>
551558Srgrimes#include <unistd.h>
561558Srgrimes
571558Srgrimes#include "dump.h"
581558Srgrimes
5998542Smckusickunion dinode {
6098542Smckusick	struct ufs1_dinode dp1;
6198542Smckusick	struct ufs2_dinode dp2;
6298542Smckusick};
6398542Smckusick#define	DIP(dp, field) \
6498542Smckusick	((sblock->fs_magic == FS_UFS1_MAGIC) ? \
6598542Smckusick	(dp)->dp1.field : (dp)->dp2.field)
66132762Skan#define DIP_SET(dp, field, val) do {\
67132762Skan	if (sblock->fs_magic == FS_UFS1_MAGIC) \
68132762Skan		(dp)->dp1.field = (val); \
69132762Skan	else \
70132762Skan		(dp)->dp2.field = (val); \
71132762Skan	} while (0)
7298542Smckusick
731558Srgrimes#define	HASDUMPEDFILE	0x1
741558Srgrimes#define	HASSUBDIRS	0x2
751558Srgrimes
7698542Smckusickstatic	int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size,
77157660Sdwmalone    long *tapesize, int nodump, ino_t maxino);
78167011Smckusickstatic	void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int level,
79167011Smckusick    off_t *size);
80167011Smckusickstatic	void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino);
81167011Smckusickstatic	void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags,
82167011Smckusick    ino_t ino, int last);
83167011Smckusickstatic	int appendextdata(union dinode *dp);
84167011Smckusickstatic	void writeextdata(union dinode *dp, ino_t ino, int added);
8598542Smckusickstatic	int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize,
86157660Sdwmalone    long *tapesize, int nodump, ino_t maxino);
87107542Smckusickstatic	long blockest(union dinode *dp);
881558Srgrimes
891558Srgrimes/*
901558Srgrimes * This is an estimation of the number of TP_BSIZE blocks in the file.
911558Srgrimes * It estimates the number of blocks in files with holes by assuming
921558Srgrimes * that all of the blocks accounted for by di_blocks are data blocks
931558Srgrimes * (when some of the blocks are usually used for indirect pointers);
941558Srgrimes * hence the estimate may be high.
951558Srgrimes */
96107542Smckusickstatic long
9798542Smckusickblockest(union dinode *dp)
981558Srgrimes{
991558Srgrimes	long blkest, sizeest;
1001558Srgrimes
1011558Srgrimes	/*
1021558Srgrimes	 * dp->di_size is the size of the file in bytes.
1031558Srgrimes	 * dp->di_blocks stores the number of sectors actually in the file.
1041558Srgrimes	 * If there are more sectors than the size would indicate, this just
1051558Srgrimes	 *	means that there are indirect blocks in the file or unused
1061558Srgrimes	 *	sectors in the last file block; we can safely ignore these
1071558Srgrimes	 *	(blkest = sizeest below).
1081558Srgrimes	 * If the file is bigger than the number of sectors would indicate,
1091558Srgrimes	 *	then the file has holes in it.	In this case we must use the
1101558Srgrimes	 *	block count to estimate the number of data blocks used, but
1111558Srgrimes	 *	we use the actual size for estimating the number of indirect
1121558Srgrimes	 *	dump blocks (sizeest vs. blkest in the indirect block
1131558Srgrimes	 *	calculation).
1141558Srgrimes	 */
115107542Smckusick	if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0)
116107542Smckusick		return (1);
11798542Smckusick	blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE);
11898542Smckusick	sizeest = howmany(DIP(dp, di_size), TP_BSIZE);
1191558Srgrimes	if (blkest > sizeest)
1201558Srgrimes		blkest = sizeest;
12198542Smckusick	if (DIP(dp, di_size) > sblock->fs_bsize * NDADDR) {
1221558Srgrimes		/* calculate the number of indirect blocks on the dump tape */
1231558Srgrimes		blkest +=
1241558Srgrimes			howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
1251558Srgrimes			TP_NINDIR);
1261558Srgrimes	}
1271558Srgrimes	return (blkest + 1);
1281558Srgrimes}
1291558Srgrimes
1301558Srgrimes/* Auxiliary macro to pick up files changed since previous dump. */
1311558Srgrimes#define	CHANGEDSINCE(dp, t) \
13298542Smckusick	(DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t))
1331558Srgrimes
1341558Srgrimes/* The WANTTODUMP macro decides whether a file should be dumped. */
1351558Srgrimes#ifdef UF_NODUMP
1361558Srgrimes#define	WANTTODUMP(dp) \
1371558Srgrimes	(CHANGEDSINCE(dp, spcl.c_ddate) && \
13898542Smckusick	 (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP))
1391558Srgrimes#else
1401558Srgrimes#define	WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
1411558Srgrimes#endif
1421558Srgrimes
1431558Srgrimes/*
1441558Srgrimes * Dump pass 1.
1451558Srgrimes *
146102231Strhodes * Walk the inode list for a file system to find all allocated inodes
1471558Srgrimes * that have been modified since the previous dump time. Also, find all
148102231Strhodes * the directories in the file system.
1491558Srgrimes */
1501558Srgrimesint
15192837Simpmapfiles(ino_t maxino, long *tapesize)
1521558Srgrimes{
153107541Smckusick	int i, cg, mode, inosused;
154107541Smckusick	int anydirskipped = 0;
155107541Smckusick	union dinode *dp;
156107541Smckusick	struct cg *cgp;
15786473Siedowse	ino_t ino;
158145794Sdelphij	u_char *cp;
1591558Srgrimes
160107541Smckusick	if ((cgp = malloc(sblock->fs_cgsize)) == NULL)
161107541Smckusick		quit("mapfiles: cannot allocate memory.\n");
162107541Smckusick	for (cg = 0; cg < sblock->fs_ncg; cg++) {
163107541Smckusick		ino = cg * sblock->fs_ipg;
164107541Smckusick		bread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp,
165107541Smckusick		    sblock->fs_cgsize);
166107541Smckusick		if (sblock->fs_magic == FS_UFS2_MAGIC)
167107541Smckusick			inosused = cgp->cg_initediblk;
168107541Smckusick		else
169107541Smckusick			inosused = sblock->fs_ipg;
17073375Sobrien		/*
171107541Smckusick		 * If we are using soft updates, then we can trust the
172107541Smckusick		 * cylinder group inode allocation maps to tell us which
173107541Smckusick		 * inodes are allocated. We will scan the used inode map
174107541Smckusick		 * to find the inodes that are really in use, and then
175107541Smckusick		 * read only those inodes in from disk.
17673375Sobrien		 */
177107541Smckusick		if (sblock->fs_flags & FS_DOSOFTDEP) {
178107541Smckusick			if (!cg_chkmagic(cgp))
179107541Smckusick				quit("mapfiles: cg %d: bad magic number\n", cg);
180107541Smckusick			cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
181107541Smckusick			for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
182107541Smckusick				if (*cp == 0)
183107541Smckusick					continue;
184107541Smckusick				for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
185107541Smckusick					if (*cp & i)
186107541Smckusick						break;
187107541Smckusick					inosused--;
188107541Smckusick				}
189107541Smckusick				break;
190107541Smckusick			}
191107541Smckusick			if (inosused <= 0)
192107541Smckusick				continue;
1931558Srgrimes		}
194107541Smckusick		for (i = 0; i < inosused; i++, ino++) {
195107541Smckusick			if (ino < ROOTINO ||
196107541Smckusick			    (dp = getino(ino, &mode)) == NULL ||
197107541Smckusick			    (mode & IFMT) == 0)
198107541Smckusick				continue;
199157660Sdwmalone			if (ino >= maxino) {
200241013Smdf				msg("Skipping inode %ju >= maxino %ju\n",
201241013Smdf				    (uintmax_t)ino, (uintmax_t)maxino);
202157660Sdwmalone				continue;
203157660Sdwmalone			}
204107541Smckusick			/*
205107541Smckusick			 * Everything must go in usedinomap so that a check
206107541Smckusick			 * for "in dumpdirmap but not in usedinomap" to detect
207107541Smckusick			 * dirs with nodump set has a chance of succeeding
208107541Smckusick			 * (this is used in mapdirs()).
209107541Smckusick			 */
210107541Smckusick			SETINO(ino, usedinomap);
211107541Smckusick			if (mode == IFDIR)
212107541Smckusick				SETINO(ino, dumpdirmap);
213107541Smckusick			if (WANTTODUMP(dp)) {
214107541Smckusick				SETINO(ino, dumpinomap);
215107541Smckusick				if (mode != IFREG &&
216107541Smckusick				    mode != IFDIR &&
217107541Smckusick				    mode != IFLNK)
218107541Smckusick					*tapesize += 1;
219107541Smckusick				else
220107541Smckusick					*tapesize += blockest(dp);
221107541Smckusick				continue;
222107541Smckusick			}
223107541Smckusick			if (mode == IFDIR) {
224107541Smckusick				if (!nonodump &&
225107541Smckusick				    (DIP(dp, di_flags) & UF_NODUMP))
226107541Smckusick					CLRINO(ino, usedinomap);
227107541Smckusick				anydirskipped = 1;
228107541Smckusick			}
22975689Sjkh		}
2301558Srgrimes	}
2311558Srgrimes	/*
2321558Srgrimes	 * Restore gets very upset if the root is not dumped,
2331558Srgrimes	 * so ensure that it always is dumped.
2341558Srgrimes	 */
2351558Srgrimes	SETINO(ROOTINO, dumpinomap);
2361558Srgrimes	return (anydirskipped);
2371558Srgrimes}
2381558Srgrimes
2391558Srgrimes/*
2401558Srgrimes * Dump pass 2.
2411558Srgrimes *
242102231Strhodes * Scan each directory on the file system to see if it has any modified
2431558Srgrimes * files in it. If it does, and has not already been added to the dump
2441558Srgrimes * list (because it was itself modified), then add it. If a directory
2451558Srgrimes * has not been modified itself, contains no modified files and has no
2461558Srgrimes * subdirectories, then it can be deleted from the dump list and from
2471558Srgrimes * the list of directories. By deleting it from the list of directories,
2481558Srgrimes * its parent may now qualify for the same treatment on this or a later
2491558Srgrimes * pass using this algorithm.
2501558Srgrimes */
2511558Srgrimesint
25292837Simpmapdirs(ino_t maxino, long *tapesize)
2531558Srgrimes{
25498542Smckusick	union dinode *dp;
25586473Siedowse	int i, isdir, nodump;
25686473Siedowse	char *map;
25786473Siedowse	ino_t ino;
25898542Smckusick	union dinode di;
2591558Srgrimes	long filesize;
2601558Srgrimes	int ret, change = 0;
2611558Srgrimes
2621558Srgrimes	isdir = 0;		/* XXX just to get gcc to shut up */
2631558Srgrimes	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
264103949Smike		if (((ino - 1) % CHAR_BIT) == 0)	/* map is offset by 1 */
2651558Srgrimes			isdir = *map++;
2661558Srgrimes		else
2671558Srgrimes			isdir >>= 1;
26873375Sobrien		/*
26973375Sobrien		 * If a directory has been removed from usedinomap, it
27073375Sobrien		 * either has the nodump flag set, or has inherited
27173375Sobrien		 * it.  Although a directory can't be in dumpinomap if
27273375Sobrien		 * it isn't in usedinomap, we have to go through it to
27373375Sobrien		 * propagate the nodump flag.
27473375Sobrien		 */
27575689Sjkh		nodump = !nonodump && (TSTINO(ino, usedinomap) == 0);
27673375Sobrien		if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump))
2771558Srgrimes			continue;
27898542Smckusick		dp = getino(ino, &i);
27998542Smckusick		/*
28098542Smckusick		 * inode buf may change in searchdir().
28198542Smckusick		 */
28298542Smckusick		if (sblock->fs_magic == FS_UFS1_MAGIC)
28398542Smckusick			di.dp1 = dp->dp1;
28498542Smckusick		else
28598542Smckusick			di.dp2 = dp->dp2;
28698542Smckusick		filesize = DIP(&di, di_size);
2871558Srgrimes		for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
28898542Smckusick			if (DIP(&di, di_db[i]) != 0)
28998542Smckusick				ret |= searchdir(ino, DIP(&di, di_db[i]),
290122060Siedowse				    (long)sblksize(sblock, DIP(&di, di_size),
291157660Sdwmalone				    i), filesize, tapesize, nodump, maxino);
2921558Srgrimes			if (ret & HASDUMPEDFILE)
2931558Srgrimes				filesize = 0;
2941558Srgrimes			else
2951558Srgrimes				filesize -= sblock->fs_bsize;
2961558Srgrimes		}
2971558Srgrimes		for (i = 0; filesize > 0 && i < NIADDR; i++) {
29898542Smckusick			if (DIP(&di, di_ib[i]) == 0)
2991558Srgrimes				continue;
30098542Smckusick			ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize,
301157660Sdwmalone			    tapesize, nodump, maxino);
3021558Srgrimes		}
3031558Srgrimes		if (ret & HASDUMPEDFILE) {
3041558Srgrimes			SETINO(ino, dumpinomap);
305122060Siedowse			*tapesize += blockest(&di);
3061558Srgrimes			change = 1;
3071558Srgrimes			continue;
3081558Srgrimes		}
30973375Sobrien		if (nodump) {
31073375Sobrien			if (ret & HASSUBDIRS)
31173375Sobrien				change = 1;	/* subdirs inherit nodump */
31273375Sobrien			CLRINO(ino, dumpdirmap);
31373375Sobrien		} else if ((ret & HASSUBDIRS) == 0)
3141558Srgrimes			if (!TSTINO(ino, dumpinomap)) {
3151558Srgrimes				CLRINO(ino, dumpdirmap);
3161558Srgrimes				change = 1;
3171558Srgrimes			}
3181558Srgrimes	}
3191558Srgrimes	return (change);
3201558Srgrimes}
3211558Srgrimes
3221558Srgrimes/*
3231558Srgrimes * Read indirect blocks, and pass the data blocks to be searched
3241558Srgrimes * as directories. Quit as soon as any entry is found that will
3251558Srgrimes * require the directory to be dumped.
3261558Srgrimes */
3271558Srgrimesstatic int
32898542Smckusickdirindir(
32998542Smckusick	ino_t ino,
33098542Smckusick	ufs2_daddr_t blkno,
33198542Smckusick	int ind_level,
33298542Smckusick	long *filesize,
33398542Smckusick	long *tapesize,
334157660Sdwmalone	int nodump,
335157660Sdwmalone	ino_t maxino)
3361558Srgrimes{
33799626Siedowse	union {
33899626Siedowse		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
33999626Siedowse		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
34099626Siedowse	} idblk;
3411558Srgrimes	int ret = 0;
34286473Siedowse	int i;
3431558Srgrimes
34499626Siedowse	bread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize);
3451558Srgrimes	if (ind_level <= 0) {
3461558Srgrimes		for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
34798542Smckusick			if (sblock->fs_magic == FS_UFS1_MAGIC)
34899626Siedowse				blkno = idblk.ufs1[i];
34998542Smckusick			else
35099626Siedowse				blkno = idblk.ufs2[i];
3511558Srgrimes			if (blkno != 0)
3521558Srgrimes				ret |= searchdir(ino, blkno, sblock->fs_bsize,
353157660Sdwmalone					*filesize, tapesize, nodump, maxino);
3541558Srgrimes			if (ret & HASDUMPEDFILE)
3551558Srgrimes				*filesize = 0;
3561558Srgrimes			else
3571558Srgrimes				*filesize -= sblock->fs_bsize;
3581558Srgrimes		}
3591558Srgrimes		return (ret);
3601558Srgrimes	}
3611558Srgrimes	ind_level--;
3621558Srgrimes	for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
36398542Smckusick		if (sblock->fs_magic == FS_UFS1_MAGIC)
36499626Siedowse			blkno = idblk.ufs1[i];
36598542Smckusick		else
36699626Siedowse			blkno = idblk.ufs2[i];
3671558Srgrimes		if (blkno != 0)
36873375Sobrien			ret |= dirindir(ino, blkno, ind_level, filesize,
369157660Sdwmalone			    tapesize, nodump, maxino);
3701558Srgrimes	}
3711558Srgrimes	return (ret);
3721558Srgrimes}
3731558Srgrimes
3741558Srgrimes/*
3751558Srgrimes * Scan a disk block containing directory information looking to see if
3761558Srgrimes * any of the entries are on the dump list and to see if the directory
3771558Srgrimes * contains any subdirectories.
3781558Srgrimes */
3791558Srgrimesstatic int
38098542Smckusicksearchdir(
38198542Smckusick	ino_t ino,
38298542Smckusick	ufs2_daddr_t blkno,
38398542Smckusick	long size,
38498542Smckusick	long filesize,
38598542Smckusick	long *tapesize,
386157660Sdwmalone	int nodump,
387157660Sdwmalone	ino_t maxino)
3881558Srgrimes{
38998542Smckusick	int mode;
39086473Siedowse	struct direct *dp;
39198542Smckusick	union dinode *ip;
39286473Siedowse	long loc, ret = 0;
39398542Smckusick	static caddr_t dblk;
3941558Srgrimes
39598542Smckusick	if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL)
39698542Smckusick		quit("searchdir: cannot allocate indirect memory.\n");
3971558Srgrimes	bread(fsbtodb(sblock, blkno), dblk, (int)size);
3981558Srgrimes	if (filesize < size)
3991558Srgrimes		size = filesize;
4001558Srgrimes	for (loc = 0; loc < size; ) {
4011558Srgrimes		dp = (struct direct *)(dblk + loc);
4021558Srgrimes		if (dp->d_reclen == 0) {
403241013Smdf			msg("corrupted directory, inumber %ju\n",
404241013Smdf			    (uintmax_t)ino);
4051558Srgrimes			break;
4061558Srgrimes		}
4071558Srgrimes		loc += dp->d_reclen;
4081558Srgrimes		if (dp->d_ino == 0)
4091558Srgrimes			continue;
410157660Sdwmalone		if (dp->d_ino >= maxino) {
411241013Smdf			msg("corrupted directory entry, d_ino %ju >= %ju\n",
412241013Smdf			    (uintmax_t)dp->d_ino, (uintmax_t)maxino);
413157660Sdwmalone			break;
414157660Sdwmalone		}
4151558Srgrimes		if (dp->d_name[0] == '.') {
4161558Srgrimes			if (dp->d_name[1] == '\0')
4171558Srgrimes				continue;
4181558Srgrimes			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
4191558Srgrimes				continue;
4201558Srgrimes		}
42173375Sobrien		if (nodump) {
42298542Smckusick			ip = getino(dp->d_ino, &mode);
42373375Sobrien			if (TSTINO(dp->d_ino, dumpinomap)) {
42473375Sobrien				CLRINO(dp->d_ino, dumpinomap);
42573375Sobrien				*tapesize -= blockest(ip);
42673375Sobrien			}
42787413Sobrien			/*
42887413Sobrien			 * Add back to dumpdirmap and remove from usedinomap
42987413Sobrien			 * to propagate nodump.
43087413Sobrien			 */
43198542Smckusick			if (mode == IFDIR) {
43273375Sobrien				SETINO(dp->d_ino, dumpdirmap);
43387413Sobrien				CLRINO(dp->d_ino, usedinomap);
43473375Sobrien				ret |= HASSUBDIRS;
43573375Sobrien			}
43673375Sobrien		} else {
43773375Sobrien			if (TSTINO(dp->d_ino, dumpinomap)) {
43873375Sobrien				ret |= HASDUMPEDFILE;
43973375Sobrien				if (ret & HASSUBDIRS)
44073375Sobrien					break;
44173375Sobrien			}
44273375Sobrien			if (TSTINO(dp->d_ino, dumpdirmap)) {
44373375Sobrien				ret |= HASSUBDIRS;
44473375Sobrien				if (ret & HASDUMPEDFILE)
44573375Sobrien					break;
44673375Sobrien			}
4471558Srgrimes		}
4481558Srgrimes	}
4491558Srgrimes	return (ret);
4501558Srgrimes}
4511558Srgrimes
4521558Srgrimes/*
4531558Srgrimes * Dump passes 3 and 4.
4541558Srgrimes *
4551558Srgrimes * Dump the contents of an inode to tape.
4561558Srgrimes */
4571558Srgrimesvoid
45898542Smckusickdumpino(union dinode *dp, ino_t ino)
4591558Srgrimes{
460167011Smckusick	int ind_level, cnt, last, added;
46198542Smckusick	off_t size;
4621558Srgrimes	char buf[TP_BSIZE];
4631558Srgrimes
4641558Srgrimes	if (newtape) {
4651558Srgrimes		newtape = 0;
4661558Srgrimes		dumpmap(dumpinomap, TS_BITS, ino);
4671558Srgrimes	}
4681558Srgrimes	CLRINO(ino, dumpinomap);
469107430Smckusick	/*
470107430Smckusick	 * Zero out the size of a snapshot so that it will be dumped
471107430Smckusick	 * as a zero length file.
472107430Smckusick	 */
473107430Smckusick	if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) {
474132762Skan		DIP_SET(dp, di_size, 0);
475132762Skan		DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT);
476107430Smckusick	}
47798542Smckusick	if (sblock->fs_magic == FS_UFS1_MAGIC) {
47898542Smckusick		spcl.c_mode = dp->dp1.di_mode;
47998542Smckusick		spcl.c_size = dp->dp1.di_size;
480167011Smckusick		spcl.c_extsize = 0;
48198542Smckusick		spcl.c_atime = _time32_to_time(dp->dp1.di_atime);
48298542Smckusick		spcl.c_atimensec = dp->dp1.di_atimensec;
48398542Smckusick		spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime);
48498542Smckusick		spcl.c_mtimensec = dp->dp1.di_mtimensec;
485100207Smckusick		spcl.c_birthtime = 0;
486100207Smckusick		spcl.c_birthtimensec = 0;
48798542Smckusick		spcl.c_rdev = dp->dp1.di_rdev;
48898542Smckusick		spcl.c_file_flags = dp->dp1.di_flags;
48998542Smckusick		spcl.c_uid = dp->dp1.di_uid;
49098542Smckusick		spcl.c_gid = dp->dp1.di_gid;
49198542Smckusick	} else {
49298542Smckusick		spcl.c_mode = dp->dp2.di_mode;
49398542Smckusick		spcl.c_size = dp->dp2.di_size;
494167011Smckusick		spcl.c_extsize = dp->dp2.di_extsize;
49598542Smckusick		spcl.c_atime = _time64_to_time(dp->dp2.di_atime);
49698542Smckusick		spcl.c_atimensec = dp->dp2.di_atimensec;
49798542Smckusick		spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime);
49898542Smckusick		spcl.c_mtimensec = dp->dp2.di_mtimensec;
499100207Smckusick		spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime);
500100207Smckusick		spcl.c_birthtimensec = dp->dp2.di_birthnsec;
50198542Smckusick		spcl.c_rdev = dp->dp2.di_rdev;
50298542Smckusick		spcl.c_file_flags = dp->dp2.di_flags;
50398542Smckusick		spcl.c_uid = dp->dp2.di_uid;
50498542Smckusick		spcl.c_gid = dp->dp2.di_gid;
50598542Smckusick	}
5061558Srgrimes	spcl.c_type = TS_INODE;
5071558Srgrimes	spcl.c_count = 0;
50898542Smckusick	switch (DIP(dp, di_mode) & S_IFMT) {
5091558Srgrimes
5101558Srgrimes	case 0:
5111558Srgrimes		/*
5121558Srgrimes		 * Freed inode.
5131558Srgrimes		 */
5141558Srgrimes		return;
5151558Srgrimes
5161558Srgrimes	case S_IFLNK:
5171558Srgrimes		/*
5181558Srgrimes		 * Check for short symbolic link.
5191558Srgrimes		 */
52098542Smckusick		if (DIP(dp, di_size) > 0 &&
52198542Smckusick		    DIP(dp, di_size) < sblock->fs_maxsymlinklen) {
5221558Srgrimes			spcl.c_addr[0] = 1;
5231558Srgrimes			spcl.c_count = 1;
524167011Smckusick			added = appendextdata(dp);
5251558Srgrimes			writeheader(ino);
52698542Smckusick			if (sblock->fs_magic == FS_UFS1_MAGIC)
52798542Smckusick				memmove(buf, (caddr_t)dp->dp1.di_db,
52898542Smckusick				    (u_long)DIP(dp, di_size));
52998542Smckusick			else
53098542Smckusick				memmove(buf, (caddr_t)dp->dp2.di_db,
53198542Smckusick				    (u_long)DIP(dp, di_size));
53298542Smckusick			buf[DIP(dp, di_size)] = '\0';
5331558Srgrimes			writerec(buf, 0);
534167011Smckusick			writeextdata(dp, ino, added);
5351558Srgrimes			return;
5361558Srgrimes		}
537102411Scharnier		/* FALLTHROUGH */
5381558Srgrimes
5391558Srgrimes	case S_IFDIR:
5401558Srgrimes	case S_IFREG:
54198542Smckusick		if (DIP(dp, di_size) > 0)
5421558Srgrimes			break;
543102411Scharnier		/* FALLTHROUGH */
5441558Srgrimes
5451558Srgrimes	case S_IFIFO:
5461558Srgrimes	case S_IFSOCK:
5471558Srgrimes	case S_IFCHR:
5481558Srgrimes	case S_IFBLK:
549167011Smckusick		added = appendextdata(dp);
5501558Srgrimes		writeheader(ino);
551167011Smckusick		writeextdata(dp, ino, added);
5521558Srgrimes		return;
5531558Srgrimes
5541558Srgrimes	default:
55598542Smckusick		msg("Warning: undefined file type 0%o\n",
55698542Smckusick		    DIP(dp, di_mode) & IFMT);
5571558Srgrimes		return;
5581558Srgrimes	}
559167011Smckusick	if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) {
5601558Srgrimes		cnt = NDADDR * sblock->fs_frag;
561167011Smckusick		last = 0;
562167011Smckusick	} else {
56398542Smckusick		cnt = howmany(DIP(dp, di_size), sblock->fs_fsize);
564167011Smckusick		last = 1;
565167011Smckusick	}
56698542Smckusick	if (sblock->fs_magic == FS_UFS1_MAGIC)
56798542Smckusick		ufs1_blksout(&dp->dp1.di_db[0], cnt, ino);
56898542Smckusick	else
569167011Smckusick		ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last);
57098542Smckusick	if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0)
5711558Srgrimes		return;
5721558Srgrimes	for (ind_level = 0; ind_level < NIADDR; ind_level++) {
573167011Smckusick		dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size);
5741558Srgrimes		if (size <= 0)
5751558Srgrimes			return;
5761558Srgrimes	}
5771558Srgrimes}
5781558Srgrimes
5791558Srgrimes/*
5801558Srgrimes * Read indirect blocks, and pass the data blocks to be dumped.
5811558Srgrimes */
5821558Srgrimesstatic void
583167011Smckusickdmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int ind_level,
584167011Smckusick	off_t *size)
5851558Srgrimes{
58699626Siedowse	union {
58799626Siedowse		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
58899626Siedowse		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
58999626Siedowse	} idblk;
590167011Smckusick	int i, cnt, last;
5911558Srgrimes
5921558Srgrimes	if (blk != 0)
59399626Siedowse		bread(fsbtodb(sblock, blk), (char *)&idblk,
59499626Siedowse		    (int)sblock->fs_bsize);
5951558Srgrimes	else
59699626Siedowse		memset(&idblk, 0, sblock->fs_bsize);
5971558Srgrimes	if (ind_level <= 0) {
598167011Smckusick		if (*size > NINDIR(sblock) * sblock->fs_bsize) {
599167011Smckusick			cnt = NINDIR(sblock) * sblock->fs_frag;
600167011Smckusick			last = 0;
601167011Smckusick		} else {
6021558Srgrimes			cnt = howmany(*size, sblock->fs_fsize);
603167011Smckusick			last = 1;
604167011Smckusick		}
6051558Srgrimes		*size -= NINDIR(sblock) * sblock->fs_bsize;
60698542Smckusick		if (sblock->fs_magic == FS_UFS1_MAGIC)
60799626Siedowse			ufs1_blksout(idblk.ufs1, cnt, ino);
60898542Smckusick		else
609167011Smckusick			ufs2_blksout(dp, idblk.ufs2, cnt, ino, last);
6101558Srgrimes		return;
6111558Srgrimes	}
6121558Srgrimes	ind_level--;
6131558Srgrimes	for (i = 0; i < NINDIR(sblock); i++) {
61498542Smckusick		if (sblock->fs_magic == FS_UFS1_MAGIC)
615167011Smckusick			dmpindir(dp, ino, idblk.ufs1[i], ind_level, size);
61698542Smckusick		else
617167011Smckusick			dmpindir(dp, ino, idblk.ufs2[i], ind_level, size);
6181558Srgrimes		if (*size <= 0)
6191558Srgrimes			return;
6201558Srgrimes	}
6211558Srgrimes}
6221558Srgrimes
6231558Srgrimes/*
6241558Srgrimes * Collect up the data into tape record sized buffers and output them.
6251558Srgrimes */
626167011Smckusickstatic void
62798542Smckusickufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino)
6281558Srgrimes{
62998542Smckusick	ufs1_daddr_t *bp;
6301558Srgrimes	int i, j, count, blks, tbperdb;
6311558Srgrimes
6321558Srgrimes	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
6331558Srgrimes	tbperdb = sblock->fs_bsize >> tp_bshift;
6341558Srgrimes	for (i = 0; i < blks; i += TP_NINDIR) {
6351558Srgrimes		if (i + TP_NINDIR > blks)
6361558Srgrimes			count = blks;
6371558Srgrimes		else
6381558Srgrimes			count = i + TP_NINDIR;
6391558Srgrimes		for (j = i; j < count; j++)
6401558Srgrimes			if (blkp[j / tbperdb] != 0)
6411558Srgrimes				spcl.c_addr[j - i] = 1;
6421558Srgrimes			else
6431558Srgrimes				spcl.c_addr[j - i] = 0;
6441558Srgrimes		spcl.c_count = count - i;
6451558Srgrimes		writeheader(ino);
6461558Srgrimes		bp = &blkp[i / tbperdb];
6471558Srgrimes		for (j = i; j < count; j += tbperdb, bp++)
64848688Sbillf			if (*bp != 0) {
6491558Srgrimes				if (j + tbperdb <= count)
6501558Srgrimes					dumpblock(*bp, (int)sblock->fs_bsize);
6511558Srgrimes				else
6521558Srgrimes					dumpblock(*bp, (count - j) * TP_BSIZE);
65348688Sbillf			}
6541558Srgrimes		spcl.c_type = TS_ADDR;
6551558Srgrimes	}
6561558Srgrimes}
6571558Srgrimes
6581558Srgrimes/*
65998542Smckusick * Collect up the data into tape record sized buffers and output them.
66098542Smckusick */
661167011Smckusickstatic void
662167011Smckusickufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino,
663167011Smckusick	int last)
66498542Smckusick{
66598542Smckusick	ufs2_daddr_t *bp;
666167011Smckusick	int i, j, count, resid, blks, tbperdb, added;
667167011Smckusick	static int writingextdata = 0;
66898542Smckusick
669167011Smckusick	/*
670167011Smckusick	 * Calculate the number of TP_BSIZE blocks to be dumped.
671167011Smckusick	 * For filesystems with a fragment size bigger than TP_BSIZE,
672167011Smckusick	 * only part of the final fragment may need to be dumped.
673167011Smckusick	 */
67498542Smckusick	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
675167011Smckusick	if (last) {
676272867Shrs		if (writingextdata)
677272867Shrs			resid = howmany(fragoff(sblock, spcl.c_extsize),
678272867Shrs			    TP_BSIZE);
679272867Shrs		else
680272867Shrs			resid = howmany(fragoff(sblock, dp->dp2.di_size),
681272867Shrs			    TP_BSIZE);
682167011Smckusick		if (resid > 0)
683167011Smckusick			blks -= howmany(sblock->fs_fsize, TP_BSIZE) - resid;
684167011Smckusick	}
68598542Smckusick	tbperdb = sblock->fs_bsize >> tp_bshift;
68698542Smckusick	for (i = 0; i < blks; i += TP_NINDIR) {
68798542Smckusick		if (i + TP_NINDIR > blks)
68898542Smckusick			count = blks;
68998542Smckusick		else
69098542Smckusick			count = i + TP_NINDIR;
69198542Smckusick		for (j = i; j < count; j++)
69298542Smckusick			if (blkp[j / tbperdb] != 0)
69398542Smckusick				spcl.c_addr[j - i] = 1;
69498542Smckusick			else
69598542Smckusick				spcl.c_addr[j - i] = 0;
69698542Smckusick		spcl.c_count = count - i;
697167055Smckusick		if (last && count == blks && !writingextdata)
698167011Smckusick			added = appendextdata(dp);
69998542Smckusick		writeheader(ino);
70098542Smckusick		bp = &blkp[i / tbperdb];
70198542Smckusick		for (j = i; j < count; j += tbperdb, bp++)
70298542Smckusick			if (*bp != 0) {
70398542Smckusick				if (j + tbperdb <= count)
70498542Smckusick					dumpblock(*bp, (int)sblock->fs_bsize);
70598542Smckusick				else
70698542Smckusick					dumpblock(*bp, (count - j) * TP_BSIZE);
70798542Smckusick			}
70898542Smckusick		spcl.c_type = TS_ADDR;
709167011Smckusick		spcl.c_count = 0;
710167055Smckusick		if (last && count == blks && !writingextdata) {
711167011Smckusick			writingextdata = 1;
712167011Smckusick			writeextdata(dp, ino, added);
713167011Smckusick			writingextdata = 0;
714167011Smckusick		}
71598542Smckusick	}
71698542Smckusick}
71798542Smckusick
71898542Smckusick/*
719167011Smckusick * If there is room in the current block for the extended attributes
720167011Smckusick * as well as the file data, update the header to reflect the added
721167011Smckusick * attribute data at the end. Attributes are placed at the end so that
722167011Smckusick * old versions of restore will correctly restore the file and simply
723167011Smckusick * discard the extra data at the end that it does not understand.
724167011Smckusick * The attribute data is dumped following the file data by the
725167011Smckusick * writeextdata() function (below).
726167011Smckusick */
727167011Smckusickstatic int
728167011Smckusickappendextdata(union dinode *dp)
729167011Smckusick{
730167011Smckusick	int i, blks, tbperdb;
731167011Smckusick
732167011Smckusick	/*
733167011Smckusick	 * If no extended attributes, there is nothing to do.
734167011Smckusick	 */
735167011Smckusick	if (spcl.c_extsize == 0)
736167011Smckusick		return (0);
737167011Smckusick	/*
738167011Smckusick	 * If there is not enough room at the end of this block
739167011Smckusick	 * to add the extended attributes, then rather than putting
740167011Smckusick	 * part of them here, we simply push them entirely into a
741167011Smckusick	 * new block rather than putting some here and some later.
742167011Smckusick	 */
743167011Smckusick	if (spcl.c_extsize > NXADDR * sblock->fs_bsize)
744167011Smckusick		blks = howmany(NXADDR * sblock->fs_bsize, TP_BSIZE);
745167011Smckusick	else
746167011Smckusick		blks = howmany(spcl.c_extsize, TP_BSIZE);
747167011Smckusick	if (spcl.c_count + blks > TP_NINDIR)
748167011Smckusick		return (0);
749167011Smckusick	/*
750167011Smckusick	 * Update the block map in the header to indicate the added
751167011Smckusick	 * extended attribute. They will be appended after the file
752167011Smckusick	 * data by the writeextdata() routine.
753167011Smckusick	 */
754167011Smckusick	tbperdb = sblock->fs_bsize >> tp_bshift;
755167011Smckusick	for (i = 0; i < blks; i++)
756167011Smckusick		if (&dp->dp2.di_extb[i / tbperdb] != 0)
757167011Smckusick				spcl.c_addr[spcl.c_count + i] = 1;
758167011Smckusick			else
759167011Smckusick				spcl.c_addr[spcl.c_count + i] = 0;
760167011Smckusick	spcl.c_count += blks;
761167011Smckusick	return (blks);
762167011Smckusick}
763167011Smckusick
764167011Smckusick/*
765167011Smckusick * Dump the extended attribute data. If there was room in the file
766167011Smckusick * header, then all we need to do is output the data blocks. If there
767167011Smckusick * was not room in the file header, then an additional TS_ADDR header
768167011Smckusick * is created to hold the attribute data.
769167011Smckusick */
770167011Smckusickstatic void
771167011Smckusickwriteextdata(union dinode *dp, ino_t ino, int added)
772167011Smckusick{
773167011Smckusick	int i, frags, blks, tbperdb, last;
774167011Smckusick	ufs2_daddr_t *bp;
775167011Smckusick	off_t size;
776167011Smckusick
777167011Smckusick	/*
778167011Smckusick	 * If no extended attributes, there is nothing to do.
779167011Smckusick	 */
780167011Smckusick	if (spcl.c_extsize == 0)
781167011Smckusick		return;
782167011Smckusick	/*
783167011Smckusick	 * If there was no room in the file block for the attributes,
784167011Smckusick	 * dump them out in a new block, otherwise just dump the data.
785167011Smckusick	 */
786167011Smckusick	if (added == 0) {
787167011Smckusick		if (spcl.c_extsize > NXADDR * sblock->fs_bsize) {
788167011Smckusick			frags = NXADDR * sblock->fs_frag;
789167011Smckusick			last = 0;
790167011Smckusick		} else {
791167011Smckusick			frags = howmany(spcl.c_extsize, sblock->fs_fsize);
792167011Smckusick			last = 1;
793167011Smckusick		}
794167011Smckusick		ufs2_blksout(dp, &dp->dp2.di_extb[0], frags, ino, last);
795167011Smckusick	} else {
796167011Smckusick		if (spcl.c_extsize > NXADDR * sblock->fs_bsize)
797167011Smckusick			blks = howmany(NXADDR * sblock->fs_bsize, TP_BSIZE);
798167011Smckusick		else
799167011Smckusick			blks = howmany(spcl.c_extsize, TP_BSIZE);
800167011Smckusick		tbperdb = sblock->fs_bsize >> tp_bshift;
801167011Smckusick		for (i = 0; i < blks; i += tbperdb) {
802167011Smckusick			bp = &dp->dp2.di_extb[i / tbperdb];
803167011Smckusick			if (*bp != 0) {
804167011Smckusick				if (i + tbperdb <= blks)
805167011Smckusick					dumpblock(*bp, (int)sblock->fs_bsize);
806167011Smckusick				else
807167011Smckusick					dumpblock(*bp, (blks - i) * TP_BSIZE);
808167011Smckusick			}
809167011Smckusick		}
810167011Smckusick
811167011Smckusick	}
812167011Smckusick	/*
813167011Smckusick	 * If an indirect block is added for extended attributes, then
814167011Smckusick	 * di_exti below should be changed to the structure element
815167011Smckusick	 * that references the extended attribute indirect block. This
816167011Smckusick	 * definition is here only to make it compile without complaint.
817167011Smckusick	 */
818167011Smckusick#define di_exti di_spare[0]
819167011Smckusick	/*
820167011Smckusick	 * If the extended attributes fall into an indirect block,
821167011Smckusick	 * dump it as well.
822167011Smckusick	 */
823167011Smckusick	if ((size = spcl.c_extsize - NXADDR * sblock->fs_bsize) > 0)
824167011Smckusick		dmpindir(dp, ino, dp->dp2.di_exti, 0, &size);
825167011Smckusick}
826167011Smckusick
827167011Smckusick/*
8281558Srgrimes * Dump a map to the tape.
8291558Srgrimes */
8301558Srgrimesvoid
83192837Simpdumpmap(char *map, int type, ino_t ino)
8321558Srgrimes{
83386473Siedowse	int i;
8341558Srgrimes	char *cp;
8351558Srgrimes
8361558Srgrimes	spcl.c_type = type;
8371558Srgrimes	spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
8381558Srgrimes	writeheader(ino);
8391558Srgrimes	for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
8401558Srgrimes		writerec(cp, 0);
8411558Srgrimes}
8421558Srgrimes
8431558Srgrimes/*
8441558Srgrimes * Write a header record to the dump tape.
8451558Srgrimes */
8461558Srgrimesvoid
84792837Simpwriteheader(ino_t ino)
8481558Srgrimes{
84986473Siedowse	int32_t sum, cnt, *lp;
8501558Srgrimes
851179267Smckusick	if (rsync_friendly >= 2) {
852179267Smckusick		/* don't track changes to access time */
853179267Smckusick		spcl.c_atime = spcl.c_mtime;
854179267Smckusick		spcl.c_atimensec = spcl.c_mtimensec;
855179267Smckusick	}
8561558Srgrimes	spcl.c_inumber = ino;
85798542Smckusick	spcl.c_magic = FS_UFS2_MAGIC;
8581558Srgrimes	spcl.c_checksum = 0;
85940668Sdima	lp = (int32_t *)&spcl;
8601558Srgrimes	sum = 0;
86140668Sdima	cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
8621558Srgrimes	while (--cnt >= 0) {
8631558Srgrimes		sum += *lp++;
8641558Srgrimes		sum += *lp++;
8651558Srgrimes		sum += *lp++;
8661558Srgrimes		sum += *lp++;
8671558Srgrimes	}
8681558Srgrimes	spcl.c_checksum = CHECKSUM - sum;
8691558Srgrimes	writerec((char *)&spcl, 1);
8701558Srgrimes}
8711558Srgrimes
87298542Smckusickunion dinode *
87398542Smckusickgetino(ino_t inum, int *modep)
8741558Srgrimes{
87598542Smckusick	static ino_t minino, maxino;
87698542Smckusick	static caddr_t inoblock;
87798542Smckusick	struct ufs1_dinode *dp1;
87898542Smckusick	struct ufs2_dinode *dp2;
8791558Srgrimes
88098542Smckusick	if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL)
88198542Smckusick		quit("cannot allocate inode memory.\n");
8821558Srgrimes	curino = inum;
8831558Srgrimes	if (inum >= minino && inum < maxino)
88498542Smckusick		goto gotit;
88598542Smckusick	bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock,
8861558Srgrimes	    (int)sblock->fs_bsize);
8871558Srgrimes	minino = inum - (inum % INOPB(sblock));
8881558Srgrimes	maxino = minino + INOPB(sblock);
88998542Smckusickgotit:
89098542Smckusick	if (sblock->fs_magic == FS_UFS1_MAGIC) {
89198542Smckusick		dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino];
89298542Smckusick		*modep = (dp1->di_mode & IFMT);
89398542Smckusick		return ((union dinode *)dp1);
89498542Smckusick	}
89598542Smckusick	dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino];
89698542Smckusick	*modep = (dp2->di_mode & IFMT);
89798542Smckusick	return ((union dinode *)dp2);
8981558Srgrimes}
8991558Srgrimes
9001558Srgrimes/*
9011558Srgrimes * Read a chunk of data from the disk.
9021558Srgrimes * Try to recover from hard errors by reading in sector sized pieces.
9031558Srgrimes * Error recovery is attempted at most BREADEMAX times before seeking
9041558Srgrimes * consent from the operator to continue.
9051558Srgrimes */
9068871Srgrimesint	breaderrors = 0;
9071558Srgrimes#define	BREADEMAX 32
9081558Srgrimes
9091558Srgrimesvoid
91098542Smckusickbread(ufs2_daddr_t blkno, char *buf, int size)
9111558Srgrimes{
912114810Smckusick	int secsize, bytes, resid, xfer, base, cnt, i;
913114810Smckusick	static char *tmpbuf;
914114810Smckusick	off_t offset;
9151558Srgrimes
9161558Srgrimesloop:
917114810Smckusick	offset = blkno << dev_bshift;
918114810Smckusick	secsize = sblock->fs_fsize;
919114810Smckusick	base = offset % secsize;
920114810Smckusick	resid = size % secsize;
921114810Smckusick	/*
922114810Smckusick	 * If the transfer request starts or ends on a non-sector
923114810Smckusick	 * boundary, we must read the entire sector and copy out
924114810Smckusick	 * just the part that we need.
925114810Smckusick	 */
926114810Smckusick	if (base == 0 && resid == 0) {
927114810Smckusick		cnt = cread(diskfd, buf, size, offset);
928114810Smckusick		if (cnt == size)
929114810Smckusick			return;
930114810Smckusick	} else {
931114810Smckusick		if (tmpbuf == NULL && (tmpbuf = malloc(secsize)) == 0)
932114810Smckusick			quit("buffer malloc failed\n");
933114810Smckusick		xfer = 0;
934114810Smckusick		bytes = size;
935114810Smckusick		if (base != 0) {
936114810Smckusick			cnt = cread(diskfd, tmpbuf, secsize, offset - base);
937114810Smckusick			if (cnt != secsize)
938114810Smckusick				goto bad;
939168392Sthomas			xfer = MIN(secsize - base, size);
940114810Smckusick			offset += xfer;
941114810Smckusick			bytes -= xfer;
942114810Smckusick			resid = bytes % secsize;
943114810Smckusick			memcpy(buf, &tmpbuf[base], xfer);
944114810Smckusick		}
945114810Smckusick		if (bytes >= secsize) {
946114810Smckusick			cnt = cread(diskfd, &buf[xfer], bytes - resid, offset);
947114810Smckusick			if (cnt != bytes - resid)
948114810Smckusick				goto bad;
949114810Smckusick			xfer += cnt;
950114810Smckusick			offset += cnt;
951114810Smckusick		}
952114810Smckusick		if (resid == 0)
953114810Smckusick			return;
954114810Smckusick		cnt = cread(diskfd, tmpbuf, secsize, offset);
955114810Smckusick		if (cnt == secsize) {
956114810Smckusick			memcpy(&buf[xfer], tmpbuf, resid);
957114810Smckusick			return;
958114810Smckusick		}
959114810Smckusick	}
960114810Smckusickbad:
9611558Srgrimes	if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
9621558Srgrimes		/*
9631558Srgrimes		 * Trying to read the final fragment.
9641558Srgrimes		 *
9651558Srgrimes		 * NB - dump only works in TP_BSIZE blocks, hence
9661558Srgrimes		 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
9671558Srgrimes		 * It should be smarter about not actually trying to
9681558Srgrimes		 * read more than it can get, but for the time being
9691558Srgrimes		 * we punt and scale back the read only when it gets
9701558Srgrimes		 * us into trouble. (mkm 9/25/83)
9711558Srgrimes		 */
9721558Srgrimes		size -= dev_bsize;
9731558Srgrimes		goto loop;
9741558Srgrimes	}
9751558Srgrimes	if (cnt == -1)
97699562Siedowse		msg("read error from %s: %s: [block %jd]: count=%d\n",
97799562Siedowse			disk, strerror(errno), (intmax_t)blkno, size);
9781558Srgrimes	else
97999562Siedowse		msg("short read error from %s: [block %jd]: count=%d, got=%d\n",
98099562Siedowse			disk, (intmax_t)blkno, size, cnt);
9811558Srgrimes	if (++breaderrors > BREADEMAX) {
98299530Siedowse		msg("More than %d block read errors from %s\n",
9831558Srgrimes			BREADEMAX, disk);
9841558Srgrimes		broadcast("DUMP IS AILING!\n");
9851558Srgrimes		msg("This is an unrecoverable error.\n");
9861558Srgrimes		if (!query("Do you want to attempt to continue?")){
9871558Srgrimes			dumpabort(0);
9881558Srgrimes			/*NOTREACHED*/
9891558Srgrimes		} else
9901558Srgrimes			breaderrors = 0;
9911558Srgrimes	}
9921558Srgrimes	/*
993109187Sdillon	 * Zero buffer, then try to read each sector of buffer separately,
994109187Sdillon	 * and bypass the cache.
9951558Srgrimes	 */
99623672Speter	memset(buf, 0, size);
9971558Srgrimes	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
99879039Smikeh		if ((cnt = pread(diskfd, buf, (int)dev_bsize,
99979039Smikeh		    ((off_t)blkno << dev_bshift))) == dev_bsize)
10001558Srgrimes			continue;
10011558Srgrimes		if (cnt == -1) {
100299562Siedowse			msg("read error from %s: %s: [sector %jd]: count=%ld\n",
100399562Siedowse			    disk, strerror(errno), (intmax_t)blkno, dev_bsize);
10041558Srgrimes			continue;
10051558Srgrimes		}
100699562Siedowse		msg("short read from %s: [sector %jd]: count=%ld, got=%d\n",
100799562Siedowse		    disk, (intmax_t)blkno, dev_bsize, cnt);
10081558Srgrimes	}
10091558Srgrimes}
1010