xref: /freebsd/sbin/dump/traverse.c (revision 32e86a82)
18fae3551SRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
48fae3551SRodney W. Grimes  * Copyright (c) 1980, 1988, 1991, 1993
58fae3551SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
68fae3551SRodney W. Grimes  *
78fae3551SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
88fae3551SRodney W. Grimes  * modification, are permitted provided that the following conditions
98fae3551SRodney W. Grimes  * are met:
108fae3551SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
118fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
128fae3551SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
138fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
148fae3551SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
168fae3551SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
178fae3551SRodney W. Grimes  *    without specific prior written permission.
188fae3551SRodney W. Grimes  *
198fae3551SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
208fae3551SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
218fae3551SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
228fae3551SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
238fae3551SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
248fae3551SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
258fae3551SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
268fae3551SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
278fae3551SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
288fae3551SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
298fae3551SRodney W. Grimes  * SUCH DAMAGE.
308fae3551SRodney W. Grimes  */
318fae3551SRodney W. Grimes 
328fae3551SRodney W. Grimes #include <sys/param.h>
338fae3551SRodney W. Grimes #include <sys/stat.h>
348fae3551SRodney W. Grimes 
358fae3551SRodney W. Grimes #include <ufs/ufs/dir.h>
368fae3551SRodney W. Grimes #include <ufs/ufs/dinode.h>
37a37c38b8SPeter Wemm #include <ufs/ffs/fs.h>
388fae3551SRodney W. Grimes 
398fae3551SRodney W. Grimes #include <protocols/dumprestore.h>
408fae3551SRodney W. Grimes 
412d518c65SWarner Losh #include <assert.h>
428fae3551SRodney W. Grimes #include <ctype.h>
4337736675SWarner Losh #include <errno.h>
44617dbd3cSIan Dowse #include <inttypes.h>
4589fdc4e1SMike Barcroft #include <limits.h>
46617dbd3cSIan Dowse #include <stdio.h>
471c85e6a3SKirk McKusick #include <stdlib.h>
48617dbd3cSIan Dowse #include <string.h>
49617dbd3cSIan Dowse #include <timeconv.h>
50617dbd3cSIan Dowse #include <unistd.h>
518fae3551SRodney W. Grimes 
528fae3551SRodney W. Grimes #include "dump.h"
538fae3551SRodney W. Grimes 
541c85e6a3SKirk McKusick union dinode {
551c85e6a3SKirk McKusick 	struct ufs1_dinode dp1;
561c85e6a3SKirk McKusick 	struct ufs2_dinode dp2;
571c85e6a3SKirk McKusick };
581c85e6a3SKirk McKusick #define	DIP(dp, field) \
591c85e6a3SKirk McKusick 	((sblock->fs_magic == FS_UFS1_MAGIC) ? \
601c85e6a3SKirk McKusick 	(dp)->dp1.field : (dp)->dp2.field)
618518a74aSAlexander Kabaev #define DIP_SET(dp, field, val) do {\
628518a74aSAlexander Kabaev 	if (sblock->fs_magic == FS_UFS1_MAGIC) \
638518a74aSAlexander Kabaev 		(dp)->dp1.field = (val); \
648518a74aSAlexander Kabaev 	else \
658518a74aSAlexander Kabaev 		(dp)->dp2.field = (val); \
668518a74aSAlexander Kabaev 	} while (0)
671c85e6a3SKirk McKusick 
688fae3551SRodney W. Grimes #define	HASDUMPEDFILE	0x1
698fae3551SRodney W. Grimes #define	HASSUBDIRS	0x2
708fae3551SRodney W. Grimes 
711c85e6a3SKirk McKusick static	int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size,
720b39291eSDavid Malone     long *tapesize, int nodump, ino_t maxino);
73772ad651SKirk McKusick static	void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int level,
74772ad651SKirk McKusick     off_t *size);
75772ad651SKirk McKusick static	void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino);
76772ad651SKirk McKusick static	void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags,
77772ad651SKirk McKusick     ino_t ino, int last);
78772ad651SKirk McKusick static	int appendextdata(union dinode *dp);
79772ad651SKirk McKusick static	void writeextdata(union dinode *dp, ino_t ino, int added);
801c85e6a3SKirk McKusick static	int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize,
810b39291eSDavid Malone     long *tapesize, int nodump, ino_t maxino);
826bfd0bdcSKirk McKusick static	long blockest(union dinode *dp);
838fae3551SRodney W. Grimes 
848fae3551SRodney W. Grimes /*
858fae3551SRodney W. Grimes  * This is an estimation of the number of TP_BSIZE blocks in the file.
868fae3551SRodney W. Grimes  * It estimates the number of blocks in files with holes by assuming
878fae3551SRodney W. Grimes  * that all of the blocks accounted for by di_blocks are data blocks
888fae3551SRodney W. Grimes  * (when some of the blocks are usually used for indirect pointers);
898fae3551SRodney W. Grimes  * hence the estimate may be high.
908fae3551SRodney W. Grimes  */
916bfd0bdcSKirk McKusick static long
blockest(union dinode * dp)921c85e6a3SKirk McKusick blockest(union dinode *dp)
938fae3551SRodney W. Grimes {
948fae3551SRodney W. Grimes 	long blkest, sizeest;
958fae3551SRodney W. Grimes 
968fae3551SRodney W. Grimes 	/*
978fae3551SRodney W. Grimes 	 * dp->di_size is the size of the file in bytes.
988fae3551SRodney W. Grimes 	 * dp->di_blocks stores the number of sectors actually in the file.
998fae3551SRodney W. Grimes 	 * If there are more sectors than the size would indicate, this just
1008fae3551SRodney W. Grimes 	 *	means that there are indirect blocks in the file or unused
1018fae3551SRodney W. Grimes 	 *	sectors in the last file block; we can safely ignore these
1028fae3551SRodney W. Grimes 	 *	(blkest = sizeest below).
1038fae3551SRodney W. Grimes 	 * If the file is bigger than the number of sectors would indicate,
1048fae3551SRodney W. Grimes 	 *	then the file has holes in it.	In this case we must use the
1058fae3551SRodney W. Grimes 	 *	block count to estimate the number of data blocks used, but
1068fae3551SRodney W. Grimes 	 *	we use the actual size for estimating the number of indirect
1078fae3551SRodney W. Grimes 	 *	dump blocks (sizeest vs. blkest in the indirect block
1088fae3551SRodney W. Grimes 	 *	calculation).
1098fae3551SRodney W. Grimes 	 */
1106bfd0bdcSKirk McKusick 	if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0)
1116bfd0bdcSKirk McKusick 		return (1);
1121c85e6a3SKirk McKusick 	blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE);
1131c85e6a3SKirk McKusick 	sizeest = howmany(DIP(dp, di_size), TP_BSIZE);
1148fae3551SRodney W. Grimes 	if (blkest > sizeest)
1158fae3551SRodney W. Grimes 		blkest = sizeest;
1161dc349abSEd Maste 	if (DIP(dp, di_size) > sblock->fs_bsize * UFS_NDADDR) {
1178fae3551SRodney W. Grimes 		/* calculate the number of indirect blocks on the dump tape */
1181dc349abSEd Maste 		blkest += howmany(sizeest -
1191dc349abSEd Maste 		    UFS_NDADDR * sblock->fs_bsize / TP_BSIZE, TP_NINDIR);
1208fae3551SRodney W. Grimes 	}
1218fae3551SRodney W. Grimes 	return (blkest + 1);
1228fae3551SRodney W. Grimes }
1238fae3551SRodney W. Grimes 
1248fae3551SRodney W. Grimes /* Auxiliary macro to pick up files changed since previous dump. */
1258fae3551SRodney W. Grimes #define	CHANGEDSINCE(dp, t) \
1261c85e6a3SKirk McKusick 	(DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t))
1278fae3551SRodney W. Grimes 
1288fae3551SRodney W. Grimes /* The WANTTODUMP macro decides whether a file should be dumped. */
1298fae3551SRodney W. Grimes #ifdef UF_NODUMP
1308fae3551SRodney W. Grimes #define	WANTTODUMP(dp) \
1318fae3551SRodney W. Grimes 	(CHANGEDSINCE(dp, spcl.c_ddate) && \
1321c85e6a3SKirk McKusick 	 (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP))
1338fae3551SRodney W. Grimes #else
1348fae3551SRodney W. Grimes #define	WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
1358fae3551SRodney W. Grimes #endif
1368fae3551SRodney W. Grimes 
1378fae3551SRodney W. Grimes /*
1388fae3551SRodney W. Grimes  * Dump pass 1.
1398fae3551SRodney W. Grimes  *
1408fae3551SRodney W. Grimes  * Walk the inode list for a file system to find all allocated inodes
1418fae3551SRodney W. Grimes  * that have been modified since the previous dump time. Also, find all
1428fae3551SRodney W. Grimes  * the directories in the file system.
1438fae3551SRodney W. Grimes  */
1448fae3551SRodney W. Grimes int
mapfiles(ino_t maxino,long * tapesize)1452db673abSWarner Losh mapfiles(ino_t maxino, long *tapesize)
1468fae3551SRodney W. Grimes {
14769becf4aSKirk McKusick 	int i, cg, mode, inosused;
1488fae3551SRodney W. Grimes 	int anydirskipped = 0;
14969becf4aSKirk McKusick 	union dinode *dp;
15069becf4aSKirk McKusick 	struct cg *cgp;
15169becf4aSKirk McKusick 	ino_t ino;
1520c40596cSXin LI 	u_char *cp;
1538fae3551SRodney W. Grimes 
15469becf4aSKirk McKusick 	if ((cgp = malloc(sblock->fs_cgsize)) == NULL)
15569becf4aSKirk McKusick 		quit("mapfiles: cannot allocate memory.\n");
15669becf4aSKirk McKusick 	for (cg = 0; cg < sblock->fs_ncg; cg++) {
15769becf4aSKirk McKusick 		ino = cg * sblock->fs_ipg;
158a770ae06SKirk McKusick 		blkread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp,
15969becf4aSKirk McKusick 		    sblock->fs_cgsize);
16069becf4aSKirk McKusick 		if (sblock->fs_magic == FS_UFS2_MAGIC)
16169becf4aSKirk McKusick 			inosused = cgp->cg_initediblk;
16269becf4aSKirk McKusick 		else
16369becf4aSKirk McKusick 			inosused = sblock->fs_ipg;
16469becf4aSKirk McKusick 		/*
16569becf4aSKirk McKusick 		 * If we are using soft updates, then we can trust the
16669becf4aSKirk McKusick 		 * cylinder group inode allocation maps to tell us which
16769becf4aSKirk McKusick 		 * inodes are allocated. We will scan the used inode map
16869becf4aSKirk McKusick 		 * to find the inodes that are really in use, and then
16969becf4aSKirk McKusick 		 * read only those inodes in from disk.
17069becf4aSKirk McKusick 		 */
17169becf4aSKirk McKusick 		if (sblock->fs_flags & FS_DOSOFTDEP) {
17269becf4aSKirk McKusick 			if (!cg_chkmagic(cgp))
17369becf4aSKirk McKusick 				quit("mapfiles: cg %d: bad magic number\n", cg);
17469becf4aSKirk McKusick 			cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
17569becf4aSKirk McKusick 			for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
17669becf4aSKirk McKusick 				if (*cp == 0)
17769becf4aSKirk McKusick 					continue;
17869becf4aSKirk McKusick 				for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
17969becf4aSKirk McKusick 					if (*cp & i)
18069becf4aSKirk McKusick 						break;
18169becf4aSKirk McKusick 					inosused--;
18269becf4aSKirk McKusick 				}
18369becf4aSKirk McKusick 				break;
18469becf4aSKirk McKusick 			}
18569becf4aSKirk McKusick 			if (inosused <= 0)
18669becf4aSKirk McKusick 				continue;
18769becf4aSKirk McKusick 		}
18869becf4aSKirk McKusick 		for (i = 0; i < inosused; i++, ino++) {
1891dc349abSEd Maste 			if (ino < UFS_ROOTINO ||
19007436eebSKirk McKusick 			    (dp = getino(ino, &mode)) == NULL ||
191d8ba45e2SEd Maste 			    (mode & IFMT) == 0)
1928fae3551SRodney W. Grimes 				continue;
1930b39291eSDavid Malone 			if (ino >= maxino) {
194e25a029eSMatthew D Fleming 				msg("Skipping inode %ju >= maxino %ju\n",
195e25a029eSMatthew D Fleming 				    (uintmax_t)ino, (uintmax_t)maxino);
1960b39291eSDavid Malone 				continue;
1970b39291eSDavid Malone 			}
198801382faSDavid E. O'Brien 			/*
1992c8094f3SJordan K. Hubbard 			 * Everything must go in usedinomap so that a check
2002c8094f3SJordan K. Hubbard 			 * for "in dumpdirmap but not in usedinomap" to detect
2012c8094f3SJordan K. Hubbard 			 * dirs with nodump set has a chance of succeeding
2022c8094f3SJordan K. Hubbard 			 * (this is used in mapdirs()).
203801382faSDavid E. O'Brien 			 */
2042c8094f3SJordan K. Hubbard 			SETINO(ino, usedinomap);
205d8ba45e2SEd Maste 			if (mode == IFDIR)
2068fae3551SRodney W. Grimes 				SETINO(ino, dumpdirmap);
2078fae3551SRodney W. Grimes 			if (WANTTODUMP(dp)) {
2088fae3551SRodney W. Grimes 				SETINO(ino, dumpinomap);
209d8ba45e2SEd Maste 				if (mode != IFREG &&
210d8ba45e2SEd Maste 				    mode != IFDIR &&
211d8ba45e2SEd Maste 				    mode != IFLNK)
2128fae3551SRodney W. Grimes 					*tapesize += 1;
2138fae3551SRodney W. Grimes 				else
2148fae3551SRodney W. Grimes 					*tapesize += blockest(dp);
2158fae3551SRodney W. Grimes 				continue;
2168fae3551SRodney W. Grimes 			}
217d8ba45e2SEd Maste 			if (mode == IFDIR) {
21869becf4aSKirk McKusick 				if (!nonodump &&
21969becf4aSKirk McKusick 				    (DIP(dp, di_flags) & UF_NODUMP))
2202c8094f3SJordan K. Hubbard 					CLRINO(ino, usedinomap);
2218fae3551SRodney W. Grimes 				anydirskipped = 1;
2228fae3551SRodney W. Grimes 			}
2232c8094f3SJordan K. Hubbard 		}
22469becf4aSKirk McKusick 	}
2258fae3551SRodney W. Grimes 	/*
2268fae3551SRodney W. Grimes 	 * Restore gets very upset if the root is not dumped,
2278fae3551SRodney W. Grimes 	 * so ensure that it always is dumped.
2288fae3551SRodney W. Grimes 	 */
2291dc349abSEd Maste 	SETINO(UFS_ROOTINO, dumpinomap);
2308fae3551SRodney W. Grimes 	return (anydirskipped);
2318fae3551SRodney W. Grimes }
2328fae3551SRodney W. Grimes 
2338fae3551SRodney W. Grimes /*
2348fae3551SRodney W. Grimes  * Dump pass 2.
2358fae3551SRodney W. Grimes  *
2368fae3551SRodney W. Grimes  * Scan each directory on the file system to see if it has any modified
2378fae3551SRodney W. Grimes  * files in it. If it does, and has not already been added to the dump
2388fae3551SRodney W. Grimes  * list (because it was itself modified), then add it. If a directory
2398fae3551SRodney W. Grimes  * has not been modified itself, contains no modified files and has no
2408fae3551SRodney W. Grimes  * subdirectories, then it can be deleted from the dump list and from
2418fae3551SRodney W. Grimes  * the list of directories. By deleting it from the list of directories,
2428fae3551SRodney W. Grimes  * its parent may now qualify for the same treatment on this or a later
2438fae3551SRodney W. Grimes  * pass using this algorithm.
2448fae3551SRodney W. Grimes  */
2458fae3551SRodney W. Grimes int
mapdirs(ino_t maxino,long * tapesize)2462db673abSWarner Losh mapdirs(ino_t maxino, long *tapesize)
2478fae3551SRodney W. Grimes {
2481c85e6a3SKirk McKusick 	union dinode *dp;
249d2334e27SIan Dowse 	int i, isdir, nodump;
250d2334e27SIan Dowse 	char *map;
251d2334e27SIan Dowse 	ino_t ino;
2521c85e6a3SKirk McKusick 	union dinode di;
2538fae3551SRodney W. Grimes 	long filesize;
2548fae3551SRodney W. Grimes 	int ret, change = 0;
2558fae3551SRodney W. Grimes 
2568fae3551SRodney W. Grimes 	isdir = 0;		/* XXX just to get gcc to shut up */
2578fae3551SRodney W. Grimes 	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
25889fdc4e1SMike Barcroft 		if (((ino - 1) % CHAR_BIT) == 0)	/* map is offset by 1 */
2598fae3551SRodney W. Grimes 			isdir = *map++;
2608fae3551SRodney W. Grimes 		else
2618fae3551SRodney W. Grimes 			isdir >>= 1;
262801382faSDavid E. O'Brien 		/*
263801382faSDavid E. O'Brien 		 * If a directory has been removed from usedinomap, it
264801382faSDavid E. O'Brien 		 * either has the nodump flag set, or has inherited
265801382faSDavid E. O'Brien 		 * it.  Although a directory can't be in dumpinomap if
266801382faSDavid E. O'Brien 		 * it isn't in usedinomap, we have to go through it to
267801382faSDavid E. O'Brien 		 * propagate the nodump flag.
268801382faSDavid E. O'Brien 		 */
2692c8094f3SJordan K. Hubbard 		nodump = !nonodump && (TSTINO(ino, usedinomap) == 0);
270801382faSDavid E. O'Brien 		if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump))
2718fae3551SRodney W. Grimes 			continue;
27207436eebSKirk McKusick 		dp = getino(ino, &i);
2731c85e6a3SKirk McKusick 		/*
2741c85e6a3SKirk McKusick 		 * inode buf may change in searchdir().
2751c85e6a3SKirk McKusick 		 */
2761c85e6a3SKirk McKusick 		if (sblock->fs_magic == FS_UFS1_MAGIC)
2771c85e6a3SKirk McKusick 			di.dp1 = dp->dp1;
2781c85e6a3SKirk McKusick 		else
2791c85e6a3SKirk McKusick 			di.dp2 = dp->dp2;
2801c85e6a3SKirk McKusick 		filesize = DIP(&di, di_size);
2811dc349abSEd Maste 		for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) {
2821c85e6a3SKirk McKusick 			if (DIP(&di, di_db[i]) != 0)
2831c85e6a3SKirk McKusick 				ret |= searchdir(ino, DIP(&di, di_db[i]),
284155ea063SIan Dowse 				    (long)sblksize(sblock, DIP(&di, di_size),
2850b39291eSDavid Malone 				    i), filesize, tapesize, nodump, maxino);
2868fae3551SRodney W. Grimes 			if (ret & HASDUMPEDFILE)
2878fae3551SRodney W. Grimes 				filesize = 0;
2888fae3551SRodney W. Grimes 			else
2898fae3551SRodney W. Grimes 				filesize -= sblock->fs_bsize;
2908fae3551SRodney W. Grimes 		}
2911dc349abSEd Maste 		for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) {
2921c85e6a3SKirk McKusick 			if (DIP(&di, di_ib[i]) == 0)
2938fae3551SRodney W. Grimes 				continue;
2941c85e6a3SKirk McKusick 			ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize,
2950b39291eSDavid Malone 			    tapesize, nodump, maxino);
2968fae3551SRodney W. Grimes 		}
2978fae3551SRodney W. Grimes 		if (ret & HASDUMPEDFILE) {
2988fae3551SRodney W. Grimes 			SETINO(ino, dumpinomap);
299155ea063SIan Dowse 			*tapesize += blockest(&di);
3008fae3551SRodney W. Grimes 			change = 1;
3018fae3551SRodney W. Grimes 			continue;
3028fae3551SRodney W. Grimes 		}
303801382faSDavid E. O'Brien 		if (nodump) {
304801382faSDavid E. O'Brien 			if (ret & HASSUBDIRS)
305801382faSDavid E. O'Brien 				change = 1;	/* subdirs inherit nodump */
306801382faSDavid E. O'Brien 			CLRINO(ino, dumpdirmap);
307801382faSDavid E. O'Brien 		} else if ((ret & HASSUBDIRS) == 0)
3088fae3551SRodney W. Grimes 			if (!TSTINO(ino, dumpinomap)) {
3098fae3551SRodney W. Grimes 				CLRINO(ino, dumpdirmap);
3108fae3551SRodney W. Grimes 				change = 1;
3118fae3551SRodney W. Grimes 			}
3128fae3551SRodney W. Grimes 	}
3138fae3551SRodney W. Grimes 	return (change);
3148fae3551SRodney W. Grimes }
3158fae3551SRodney W. Grimes 
3168fae3551SRodney W. Grimes /*
3178fae3551SRodney W. Grimes  * Read indirect blocks, and pass the data blocks to be searched
3188fae3551SRodney W. Grimes  * as directories. Quit as soon as any entry is found that will
3198fae3551SRodney W. Grimes  * require the directory to be dumped.
3208fae3551SRodney W. Grimes  */
3218fae3551SRodney W. Grimes static int
dirindir(ino_t ino,ufs2_daddr_t blkno,int ind_level,long * filesize,long * tapesize,int nodump,ino_t maxino)3221c85e6a3SKirk McKusick dirindir(
3231c85e6a3SKirk McKusick 	ino_t ino,
3241c85e6a3SKirk McKusick 	ufs2_daddr_t blkno,
3251c85e6a3SKirk McKusick 	int ind_level,
3261c85e6a3SKirk McKusick 	long *filesize,
3271c85e6a3SKirk McKusick 	long *tapesize,
3280b39291eSDavid Malone 	int nodump,
3290b39291eSDavid Malone 	ino_t maxino)
3308fae3551SRodney W. Grimes {
3317680e41cSIan Dowse 	union {
3327680e41cSIan Dowse 		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
3337680e41cSIan Dowse 		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
3347680e41cSIan Dowse 	} idblk;
3358fae3551SRodney W. Grimes 	int ret = 0;
336d2334e27SIan Dowse 	int i;
3378fae3551SRodney W. Grimes 
338a770ae06SKirk McKusick 	blkread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize);
3398fae3551SRodney W. Grimes 	if (ind_level <= 0) {
3408fae3551SRodney W. Grimes 		for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
3411c85e6a3SKirk McKusick 			if (sblock->fs_magic == FS_UFS1_MAGIC)
3427680e41cSIan Dowse 				blkno = idblk.ufs1[i];
3431c85e6a3SKirk McKusick 			else
3447680e41cSIan Dowse 				blkno = idblk.ufs2[i];
3458fae3551SRodney W. Grimes 			if (blkno != 0)
3468fae3551SRodney W. Grimes 				ret |= searchdir(ino, blkno, sblock->fs_bsize,
3470b39291eSDavid Malone 					*filesize, tapesize, nodump, maxino);
3488fae3551SRodney W. Grimes 			if (ret & HASDUMPEDFILE)
3498fae3551SRodney W. Grimes 				*filesize = 0;
3508fae3551SRodney W. Grimes 			else
3518fae3551SRodney W. Grimes 				*filesize -= sblock->fs_bsize;
3528fae3551SRodney W. Grimes 		}
3538fae3551SRodney W. Grimes 		return (ret);
3548fae3551SRodney W. Grimes 	}
3558fae3551SRodney W. Grimes 	ind_level--;
3568fae3551SRodney W. Grimes 	for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
3571c85e6a3SKirk McKusick 		if (sblock->fs_magic == FS_UFS1_MAGIC)
3587680e41cSIan Dowse 			blkno = idblk.ufs1[i];
3591c85e6a3SKirk McKusick 		else
3607680e41cSIan Dowse 			blkno = idblk.ufs2[i];
3618fae3551SRodney W. Grimes 		if (blkno != 0)
362801382faSDavid E. O'Brien 			ret |= dirindir(ino, blkno, ind_level, filesize,
3630b39291eSDavid Malone 			    tapesize, nodump, maxino);
3648fae3551SRodney W. Grimes 	}
3658fae3551SRodney W. Grimes 	return (ret);
3668fae3551SRodney W. Grimes }
3678fae3551SRodney W. Grimes 
3688fae3551SRodney W. Grimes /*
3698fae3551SRodney W. Grimes  * Scan a disk block containing directory information looking to see if
3708fae3551SRodney W. Grimes  * any of the entries are on the dump list and to see if the directory
3718fae3551SRodney W. Grimes  * contains any subdirectories.
3728fae3551SRodney W. Grimes  */
3738fae3551SRodney W. Grimes static int
searchdir(ino_t ino,ufs2_daddr_t blkno,long size,long filesize,long * tapesize,int nodump,ino_t maxino)3741c85e6a3SKirk McKusick searchdir(
3751c85e6a3SKirk McKusick 	ino_t ino,
3761c85e6a3SKirk McKusick 	ufs2_daddr_t blkno,
3771c85e6a3SKirk McKusick 	long size,
3781c85e6a3SKirk McKusick 	long filesize,
3791c85e6a3SKirk McKusick 	long *tapesize,
3800b39291eSDavid Malone 	int nodump,
3810b39291eSDavid Malone 	ino_t maxino)
3828fae3551SRodney W. Grimes {
3831c85e6a3SKirk McKusick 	int mode;
384d2334e27SIan Dowse 	struct direct *dp;
3851c85e6a3SKirk McKusick 	union dinode *ip;
386d2334e27SIan Dowse 	long loc, ret = 0;
3871c85e6a3SKirk McKusick 	static caddr_t dblk;
3888fae3551SRodney W. Grimes 
3891c85e6a3SKirk McKusick 	if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL)
3901c85e6a3SKirk McKusick 		quit("searchdir: cannot allocate indirect memory.\n");
391a770ae06SKirk McKusick 	blkread(fsbtodb(sblock, blkno), dblk, (int)size);
3928fae3551SRodney W. Grimes 	if (filesize < size)
3938fae3551SRodney W. Grimes 		size = filesize;
3948fae3551SRodney W. Grimes 	for (loc = 0; loc < size; ) {
3958fae3551SRodney W. Grimes 		dp = (struct direct *)(dblk + loc);
3968fae3551SRodney W. Grimes 		if (dp->d_reclen == 0) {
397e25a029eSMatthew D Fleming 			msg("corrupted directory, inumber %ju\n",
398e25a029eSMatthew D Fleming 			    (uintmax_t)ino);
3998fae3551SRodney W. Grimes 			break;
4008fae3551SRodney W. Grimes 		}
4018fae3551SRodney W. Grimes 		loc += dp->d_reclen;
4028fae3551SRodney W. Grimes 		if (dp->d_ino == 0)
4038fae3551SRodney W. Grimes 			continue;
4040b39291eSDavid Malone 		if (dp->d_ino >= maxino) {
405e25a029eSMatthew D Fleming 			msg("corrupted directory entry, d_ino %ju >= %ju\n",
406e25a029eSMatthew D Fleming 			    (uintmax_t)dp->d_ino, (uintmax_t)maxino);
4070b39291eSDavid Malone 			break;
4080b39291eSDavid Malone 		}
4098fae3551SRodney W. Grimes 		if (dp->d_name[0] == '.') {
4108fae3551SRodney W. Grimes 			if (dp->d_name[1] == '\0')
4118fae3551SRodney W. Grimes 				continue;
4128fae3551SRodney W. Grimes 			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
4138fae3551SRodney W. Grimes 				continue;
4148fae3551SRodney W. Grimes 		}
415801382faSDavid E. O'Brien 		if (nodump) {
41607436eebSKirk McKusick 			ip = getino(dp->d_ino, &mode);
417801382faSDavid E. O'Brien 			if (TSTINO(dp->d_ino, dumpinomap)) {
418801382faSDavid E. O'Brien 				CLRINO(dp->d_ino, dumpinomap);
419801382faSDavid E. O'Brien 				*tapesize -= blockest(ip);
420801382faSDavid E. O'Brien 			}
42101629855SDavid E. O'Brien 			/*
42201629855SDavid E. O'Brien 			 * Add back to dumpdirmap and remove from usedinomap
42301629855SDavid E. O'Brien 			 * to propagate nodump.
42401629855SDavid E. O'Brien 			 */
425d8ba45e2SEd Maste 			if (mode == IFDIR) {
426801382faSDavid E. O'Brien 				SETINO(dp->d_ino, dumpdirmap);
42701629855SDavid E. O'Brien 				CLRINO(dp->d_ino, usedinomap);
428801382faSDavid E. O'Brien 				ret |= HASSUBDIRS;
429801382faSDavid E. O'Brien 			}
430801382faSDavid E. O'Brien 		} else {
4318fae3551SRodney W. Grimes 			if (TSTINO(dp->d_ino, dumpinomap)) {
4328fae3551SRodney W. Grimes 				ret |= HASDUMPEDFILE;
4338fae3551SRodney W. Grimes 				if (ret & HASSUBDIRS)
4348fae3551SRodney W. Grimes 					break;
4358fae3551SRodney W. Grimes 			}
4368fae3551SRodney W. Grimes 			if (TSTINO(dp->d_ino, dumpdirmap)) {
4378fae3551SRodney W. Grimes 				ret |= HASSUBDIRS;
4388fae3551SRodney W. Grimes 				if (ret & HASDUMPEDFILE)
4398fae3551SRodney W. Grimes 					break;
4408fae3551SRodney W. Grimes 			}
4418fae3551SRodney W. Grimes 		}
442801382faSDavid E. O'Brien 	}
4438fae3551SRodney W. Grimes 	return (ret);
4448fae3551SRodney W. Grimes }
4458fae3551SRodney W. Grimes 
4468fae3551SRodney W. Grimes /*
4478fae3551SRodney W. Grimes  * Dump passes 3 and 4.
4488fae3551SRodney W. Grimes  *
4498fae3551SRodney W. Grimes  * Dump the contents of an inode to tape.
4508fae3551SRodney W. Grimes  */
4518fae3551SRodney W. Grimes void
dumpino(union dinode * dp,ino_t ino)4521c85e6a3SKirk McKusick dumpino(union dinode *dp, ino_t ino)
4538fae3551SRodney W. Grimes {
454772ad651SKirk McKusick 	int ind_level, cnt, last, added;
4551c85e6a3SKirk McKusick 	off_t size;
4568fae3551SRodney W. Grimes 	char buf[TP_BSIZE];
4578fae3551SRodney W. Grimes 
4588fae3551SRodney W. Grimes 	if (newtape) {
4598fae3551SRodney W. Grimes 		newtape = 0;
4608fae3551SRodney W. Grimes 		dumpmap(dumpinomap, TS_BITS, ino);
4618fae3551SRodney W. Grimes 	}
4628fae3551SRodney W. Grimes 	CLRINO(ino, dumpinomap);
463be5b1425SKirk McKusick 	/*
464be5b1425SKirk McKusick 	 * Zero out the size of a snapshot so that it will be dumped
465be5b1425SKirk McKusick 	 * as a zero length file.
466be5b1425SKirk McKusick 	 */
467be5b1425SKirk McKusick 	if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) {
4688518a74aSAlexander Kabaev 		DIP_SET(dp, di_size, 0);
4698518a74aSAlexander Kabaev 		DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT);
470be5b1425SKirk McKusick 	}
4711c85e6a3SKirk McKusick 	if (sblock->fs_magic == FS_UFS1_MAGIC) {
4721c85e6a3SKirk McKusick 		spcl.c_mode = dp->dp1.di_mode;
4731c85e6a3SKirk McKusick 		spcl.c_size = dp->dp1.di_size;
474772ad651SKirk McKusick 		spcl.c_extsize = 0;
4751c85e6a3SKirk McKusick 		spcl.c_atime = _time32_to_time(dp->dp1.di_atime);
4761c85e6a3SKirk McKusick 		spcl.c_atimensec = dp->dp1.di_atimensec;
4771c85e6a3SKirk McKusick 		spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime);
4781c85e6a3SKirk McKusick 		spcl.c_mtimensec = dp->dp1.di_mtimensec;
479fb36a3d8SKirk McKusick 		spcl.c_birthtime = 0;
480fb36a3d8SKirk McKusick 		spcl.c_birthtimensec = 0;
4811c85e6a3SKirk McKusick 		spcl.c_rdev = dp->dp1.di_rdev;
4821c85e6a3SKirk McKusick 		spcl.c_file_flags = dp->dp1.di_flags;
4831c85e6a3SKirk McKusick 		spcl.c_uid = dp->dp1.di_uid;
4841c85e6a3SKirk McKusick 		spcl.c_gid = dp->dp1.di_gid;
4851c85e6a3SKirk McKusick 	} else {
4861c85e6a3SKirk McKusick 		spcl.c_mode = dp->dp2.di_mode;
4871c85e6a3SKirk McKusick 		spcl.c_size = dp->dp2.di_size;
488772ad651SKirk McKusick 		spcl.c_extsize = dp->dp2.di_extsize;
4891c85e6a3SKirk McKusick 		spcl.c_atime = _time64_to_time(dp->dp2.di_atime);
4901c85e6a3SKirk McKusick 		spcl.c_atimensec = dp->dp2.di_atimensec;
4911c85e6a3SKirk McKusick 		spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime);
4921c85e6a3SKirk McKusick 		spcl.c_mtimensec = dp->dp2.di_mtimensec;
493fb36a3d8SKirk McKusick 		spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime);
494fb36a3d8SKirk McKusick 		spcl.c_birthtimensec = dp->dp2.di_birthnsec;
4951c85e6a3SKirk McKusick 		spcl.c_rdev = dp->dp2.di_rdev;
4961c85e6a3SKirk McKusick 		spcl.c_file_flags = dp->dp2.di_flags;
4971c85e6a3SKirk McKusick 		spcl.c_uid = dp->dp2.di_uid;
4981c85e6a3SKirk McKusick 		spcl.c_gid = dp->dp2.di_gid;
4991c85e6a3SKirk McKusick 	}
5008fae3551SRodney W. Grimes 	spcl.c_type = TS_INODE;
5018fae3551SRodney W. Grimes 	spcl.c_count = 0;
5021c85e6a3SKirk McKusick 	switch (DIP(dp, di_mode) & S_IFMT) {
5038fae3551SRodney W. Grimes 
5048fae3551SRodney W. Grimes 	case 0:
5058fae3551SRodney W. Grimes 		/*
5068fae3551SRodney W. Grimes 		 * Freed inode.
5078fae3551SRodney W. Grimes 		 */
5088fae3551SRodney W. Grimes 		return;
5098fae3551SRodney W. Grimes 
5108fae3551SRodney W. Grimes 	case S_IFLNK:
5118fae3551SRodney W. Grimes 		/*
5128fae3551SRodney W. Grimes 		 * Check for short symbolic link.
5138fae3551SRodney W. Grimes 		 */
5141c85e6a3SKirk McKusick 		if (DIP(dp, di_size) > 0 &&
5151c85e6a3SKirk McKusick 		    DIP(dp, di_size) < sblock->fs_maxsymlinklen) {
5168fae3551SRodney W. Grimes 			spcl.c_addr[0] = 1;
5178fae3551SRodney W. Grimes 			spcl.c_count = 1;
518772ad651SKirk McKusick 			added = appendextdata(dp);
5198fae3551SRodney W. Grimes 			writeheader(ino);
5205b13fa79SJessica Clarke 			memmove(buf, DIP(dp, di_shortlink),
5211c85e6a3SKirk McKusick 			    (u_long)DIP(dp, di_size));
5221c85e6a3SKirk McKusick 			buf[DIP(dp, di_size)] = '\0';
5238fae3551SRodney W. Grimes 			writerec(buf, 0);
524772ad651SKirk McKusick 			writeextdata(dp, ino, added);
5258fae3551SRodney W. Grimes 			return;
5268fae3551SRodney W. Grimes 		}
5277fed38d0SPhilippe Charnier 		/* FALLTHROUGH */
5288fae3551SRodney W. Grimes 
5298fae3551SRodney W. Grimes 	case S_IFDIR:
5308fae3551SRodney W. Grimes 	case S_IFREG:
5311c85e6a3SKirk McKusick 		if (DIP(dp, di_size) > 0)
5328fae3551SRodney W. Grimes 			break;
5337fed38d0SPhilippe Charnier 		/* FALLTHROUGH */
5348fae3551SRodney W. Grimes 
5358fae3551SRodney W. Grimes 	case S_IFIFO:
5368fae3551SRodney W. Grimes 	case S_IFSOCK:
5378fae3551SRodney W. Grimes 	case S_IFCHR:
5388fae3551SRodney W. Grimes 	case S_IFBLK:
539772ad651SKirk McKusick 		added = appendextdata(dp);
5408fae3551SRodney W. Grimes 		writeheader(ino);
541772ad651SKirk McKusick 		writeextdata(dp, ino, added);
5428fae3551SRodney W. Grimes 		return;
5438fae3551SRodney W. Grimes 
5448fae3551SRodney W. Grimes 	default:
5451c85e6a3SKirk McKusick 		msg("Warning: undefined file type 0%o\n",
546d8ba45e2SEd Maste 		    DIP(dp, di_mode) & IFMT);
5478fae3551SRodney W. Grimes 		return;
5488fae3551SRodney W. Grimes 	}
5491dc349abSEd Maste 	if (DIP(dp, di_size) > UFS_NDADDR * sblock->fs_bsize) {
5501dc349abSEd Maste 		cnt = UFS_NDADDR * sblock->fs_frag;
551772ad651SKirk McKusick 		last = 0;
552772ad651SKirk McKusick 	} else {
5531c85e6a3SKirk McKusick 		cnt = howmany(DIP(dp, di_size), sblock->fs_fsize);
554772ad651SKirk McKusick 		last = 1;
555772ad651SKirk McKusick 	}
5561c85e6a3SKirk McKusick 	if (sblock->fs_magic == FS_UFS1_MAGIC)
5571c85e6a3SKirk McKusick 		ufs1_blksout(&dp->dp1.di_db[0], cnt, ino);
5581c85e6a3SKirk McKusick 	else
559772ad651SKirk McKusick 		ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last);
5601dc349abSEd Maste 	if ((size = DIP(dp, di_size) - UFS_NDADDR * sblock->fs_bsize) <= 0)
5618fae3551SRodney W. Grimes 		return;
5621dc349abSEd Maste 	for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) {
563772ad651SKirk McKusick 		dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size);
5648fae3551SRodney W. Grimes 		if (size <= 0)
5658fae3551SRodney W. Grimes 			return;
5668fae3551SRodney W. Grimes 	}
5678fae3551SRodney W. Grimes }
5688fae3551SRodney W. Grimes 
5698fae3551SRodney W. Grimes /*
5708fae3551SRodney W. Grimes  * Read indirect blocks, and pass the data blocks to be dumped.
5718fae3551SRodney W. Grimes  */
5728fae3551SRodney W. Grimes static void
dmpindir(union dinode * dp,ino_t ino,ufs2_daddr_t blk,int ind_level,off_t * size)573772ad651SKirk McKusick dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int ind_level,
574772ad651SKirk McKusick 	off_t *size)
5758fae3551SRodney W. Grimes {
5767680e41cSIan Dowse 	union {
5777680e41cSIan Dowse 		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
5787680e41cSIan Dowse 		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
5797680e41cSIan Dowse 	} idblk;
580772ad651SKirk McKusick 	int i, cnt, last;
5818fae3551SRodney W. Grimes 
5828fae3551SRodney W. Grimes 	if (blk != 0)
583a770ae06SKirk McKusick 		blkread(fsbtodb(sblock, blk), (char *)&idblk,
5847680e41cSIan Dowse 		    (int)sblock->fs_bsize);
5858fae3551SRodney W. Grimes 	else
5867680e41cSIan Dowse 		memset(&idblk, 0, sblock->fs_bsize);
5878fae3551SRodney W. Grimes 	if (ind_level <= 0) {
588772ad651SKirk McKusick 		if (*size > NINDIR(sblock) * sblock->fs_bsize) {
5898fae3551SRodney W. Grimes 			cnt = NINDIR(sblock) * sblock->fs_frag;
590772ad651SKirk McKusick 			last = 0;
591772ad651SKirk McKusick 		} else {
592772ad651SKirk McKusick 			cnt = howmany(*size, sblock->fs_fsize);
593772ad651SKirk McKusick 			last = 1;
594772ad651SKirk McKusick 		}
5958fae3551SRodney W. Grimes 		*size -= NINDIR(sblock) * sblock->fs_bsize;
5961c85e6a3SKirk McKusick 		if (sblock->fs_magic == FS_UFS1_MAGIC)
5977680e41cSIan Dowse 			ufs1_blksout(idblk.ufs1, cnt, ino);
5981c85e6a3SKirk McKusick 		else
599772ad651SKirk McKusick 			ufs2_blksout(dp, idblk.ufs2, cnt, ino, last);
6008fae3551SRodney W. Grimes 		return;
6018fae3551SRodney W. Grimes 	}
6028fae3551SRodney W. Grimes 	ind_level--;
6038fae3551SRodney W. Grimes 	for (i = 0; i < NINDIR(sblock); i++) {
6041c85e6a3SKirk McKusick 		if (sblock->fs_magic == FS_UFS1_MAGIC)
605772ad651SKirk McKusick 			dmpindir(dp, ino, idblk.ufs1[i], ind_level, size);
6061c85e6a3SKirk McKusick 		else
607772ad651SKirk McKusick 			dmpindir(dp, ino, idblk.ufs2[i], ind_level, size);
6088fae3551SRodney W. Grimes 		if (*size <= 0)
6098fae3551SRodney W. Grimes 			return;
6108fae3551SRodney W. Grimes 	}
6118fae3551SRodney W. Grimes }
6128fae3551SRodney W. Grimes 
6138fae3551SRodney W. Grimes /*
6148fae3551SRodney W. Grimes  * Collect up the data into tape record sized buffers and output them.
6158fae3551SRodney W. Grimes  */
616772ad651SKirk McKusick static void
ufs1_blksout(ufs1_daddr_t * blkp,int frags,ino_t ino)6171c85e6a3SKirk McKusick ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino)
6188fae3551SRodney W. Grimes {
6191c85e6a3SKirk McKusick 	ufs1_daddr_t *bp;
6201c85e6a3SKirk McKusick 	int i, j, count, blks, tbperdb;
6211c85e6a3SKirk McKusick 
6221c85e6a3SKirk McKusick 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
6231c85e6a3SKirk McKusick 	tbperdb = sblock->fs_bsize >> tp_bshift;
6241c85e6a3SKirk McKusick 	for (i = 0; i < blks; i += TP_NINDIR) {
6251c85e6a3SKirk McKusick 		if (i + TP_NINDIR > blks)
6261c85e6a3SKirk McKusick 			count = blks;
6271c85e6a3SKirk McKusick 		else
6281c85e6a3SKirk McKusick 			count = i + TP_NINDIR;
6292d518c65SWarner Losh 		assert(count <= TP_NINDIR + i);
6301c85e6a3SKirk McKusick 		for (j = i; j < count; j++)
6311c85e6a3SKirk McKusick 			if (blkp[j / tbperdb] != 0)
6321c85e6a3SKirk McKusick 				spcl.c_addr[j - i] = 1;
6331c85e6a3SKirk McKusick 			else
6341c85e6a3SKirk McKusick 				spcl.c_addr[j - i] = 0;
6351c85e6a3SKirk McKusick 		spcl.c_count = count - i;
6361c85e6a3SKirk McKusick 		writeheader(ino);
6371c85e6a3SKirk McKusick 		bp = &blkp[i / tbperdb];
6381c85e6a3SKirk McKusick 		for (j = i; j < count; j += tbperdb, bp++)
6391c85e6a3SKirk McKusick 			if (*bp != 0) {
6401c85e6a3SKirk McKusick 				if (j + tbperdb <= count)
6411c85e6a3SKirk McKusick 					dumpblock(*bp, (int)sblock->fs_bsize);
6421c85e6a3SKirk McKusick 				else
6431c85e6a3SKirk McKusick 					dumpblock(*bp, (count - j) * TP_BSIZE);
6441c85e6a3SKirk McKusick 			}
6451c85e6a3SKirk McKusick 		spcl.c_type = TS_ADDR;
6461c85e6a3SKirk McKusick 	}
6471c85e6a3SKirk McKusick }
6481c85e6a3SKirk McKusick 
6491c85e6a3SKirk McKusick /*
6501c85e6a3SKirk McKusick  * Collect up the data into tape record sized buffers and output them.
6511c85e6a3SKirk McKusick  */
652772ad651SKirk McKusick static void
ufs2_blksout(union dinode * dp,ufs2_daddr_t * blkp,int frags,ino_t ino,int last)653772ad651SKirk McKusick ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino,
654772ad651SKirk McKusick 	int last)
6551c85e6a3SKirk McKusick {
6561c85e6a3SKirk McKusick 	ufs2_daddr_t *bp;
657772ad651SKirk McKusick 	int i, j, count, resid, blks, tbperdb, added;
658772ad651SKirk McKusick 	static int writingextdata = 0;
6598fae3551SRodney W. Grimes 
660772ad651SKirk McKusick 	/*
661772ad651SKirk McKusick 	 * Calculate the number of TP_BSIZE blocks to be dumped.
662772ad651SKirk McKusick 	 * For filesystems with a fragment size bigger than TP_BSIZE,
663772ad651SKirk McKusick 	 * only part of the final fragment may need to be dumped.
664772ad651SKirk McKusick 	 */
6658fae3551SRodney W. Grimes 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
666772ad651SKirk McKusick 	if (last) {
667adbc0311SHiroki Sato 		if (writingextdata)
668adbc0311SHiroki Sato 			resid = howmany(fragoff(sblock, spcl.c_extsize),
669adbc0311SHiroki Sato 			    TP_BSIZE);
670adbc0311SHiroki Sato 		else
671adbc0311SHiroki Sato 			resid = howmany(fragoff(sblock, dp->dp2.di_size),
672adbc0311SHiroki Sato 			    TP_BSIZE);
673772ad651SKirk McKusick 		if (resid > 0)
674772ad651SKirk McKusick 			blks -= howmany(sblock->fs_fsize, TP_BSIZE) - resid;
675772ad651SKirk McKusick 	}
6768fae3551SRodney W. Grimes 	tbperdb = sblock->fs_bsize >> tp_bshift;
6778fae3551SRodney W. Grimes 	for (i = 0; i < blks; i += TP_NINDIR) {
6788fae3551SRodney W. Grimes 		if (i + TP_NINDIR > blks)
6798fae3551SRodney W. Grimes 			count = blks;
6808fae3551SRodney W. Grimes 		else
6818fae3551SRodney W. Grimes 			count = i + TP_NINDIR;
6822d518c65SWarner Losh 		assert(count <= TP_NINDIR + i);
6838fae3551SRodney W. Grimes 		for (j = i; j < count; j++)
6848fae3551SRodney W. Grimes 			if (blkp[j / tbperdb] != 0)
6858fae3551SRodney W. Grimes 				spcl.c_addr[j - i] = 1;
6868fae3551SRodney W. Grimes 			else
6878fae3551SRodney W. Grimes 				spcl.c_addr[j - i] = 0;
6888fae3551SRodney W. Grimes 		spcl.c_count = count - i;
6893ec81826SKirk McKusick 		if (last && count == blks && !writingextdata)
690772ad651SKirk McKusick 			added = appendextdata(dp);
6918fae3551SRodney W. Grimes 		writeheader(ino);
6928fae3551SRodney W. Grimes 		bp = &blkp[i / tbperdb];
6938fae3551SRodney W. Grimes 		for (j = i; j < count; j += tbperdb, bp++)
69416c4e408SBill Fumerola 			if (*bp != 0) {
6958fae3551SRodney W. Grimes 				if (j + tbperdb <= count)
6968fae3551SRodney W. Grimes 					dumpblock(*bp, (int)sblock->fs_bsize);
6978fae3551SRodney W. Grimes 				else
6988fae3551SRodney W. Grimes 					dumpblock(*bp, (count - j) * TP_BSIZE);
69916c4e408SBill Fumerola 			}
7008fae3551SRodney W. Grimes 		spcl.c_type = TS_ADDR;
701772ad651SKirk McKusick 		spcl.c_count = 0;
7023ec81826SKirk McKusick 		if (last && count == blks && !writingextdata) {
703772ad651SKirk McKusick 			writingextdata = 1;
704772ad651SKirk McKusick 			writeextdata(dp, ino, added);
705772ad651SKirk McKusick 			writingextdata = 0;
7068fae3551SRodney W. Grimes 		}
7078fae3551SRodney W. Grimes 	}
708772ad651SKirk McKusick }
709772ad651SKirk McKusick 
710772ad651SKirk McKusick /*
711772ad651SKirk McKusick  * If there is room in the current block for the extended attributes
712772ad651SKirk McKusick  * as well as the file data, update the header to reflect the added
713772ad651SKirk McKusick  * attribute data at the end. Attributes are placed at the end so that
714772ad651SKirk McKusick  * old versions of restore will correctly restore the file and simply
715772ad651SKirk McKusick  * discard the extra data at the end that it does not understand.
716772ad651SKirk McKusick  * The attribute data is dumped following the file data by the
717772ad651SKirk McKusick  * writeextdata() function (below).
718772ad651SKirk McKusick  */
719772ad651SKirk McKusick static int
appendextdata(union dinode * dp)720772ad651SKirk McKusick appendextdata(union dinode *dp)
721772ad651SKirk McKusick {
722772ad651SKirk McKusick 	int i, blks, tbperdb;
723772ad651SKirk McKusick 
724772ad651SKirk McKusick 	/*
725772ad651SKirk McKusick 	 * If no extended attributes, there is nothing to do.
726772ad651SKirk McKusick 	 */
727772ad651SKirk McKusick 	if (spcl.c_extsize == 0)
728772ad651SKirk McKusick 		return (0);
729772ad651SKirk McKusick 	/*
730772ad651SKirk McKusick 	 * If there is not enough room at the end of this block
731772ad651SKirk McKusick 	 * to add the extended attributes, then rather than putting
732772ad651SKirk McKusick 	 * part of them here, we simply push them entirely into a
733772ad651SKirk McKusick 	 * new block rather than putting some here and some later.
734772ad651SKirk McKusick 	 */
7351dc349abSEd Maste 	if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize)
7361dc349abSEd Maste 		blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE);
737772ad651SKirk McKusick 	else
738772ad651SKirk McKusick 		blks = howmany(spcl.c_extsize, TP_BSIZE);
739772ad651SKirk McKusick 	if (spcl.c_count + blks > TP_NINDIR)
740772ad651SKirk McKusick 		return (0);
741772ad651SKirk McKusick 	/*
742772ad651SKirk McKusick 	 * Update the block map in the header to indicate the added
743772ad651SKirk McKusick 	 * extended attribute. They will be appended after the file
744772ad651SKirk McKusick 	 * data by the writeextdata() routine.
745772ad651SKirk McKusick 	 */
746772ad651SKirk McKusick 	tbperdb = sblock->fs_bsize >> tp_bshift;
747efe145a7SKirk McKusick 	assert(spcl.c_count + blks <= TP_NINDIR);
748772ad651SKirk McKusick 	for (i = 0; i < blks; i++)
749772ad651SKirk McKusick 		if (&dp->dp2.di_extb[i / tbperdb] != 0)
750772ad651SKirk McKusick 				spcl.c_addr[spcl.c_count + i] = 1;
751772ad651SKirk McKusick 			else
752772ad651SKirk McKusick 				spcl.c_addr[spcl.c_count + i] = 0;
753772ad651SKirk McKusick 	spcl.c_count += blks;
754772ad651SKirk McKusick 	return (blks);
755772ad651SKirk McKusick }
756772ad651SKirk McKusick 
757772ad651SKirk McKusick /*
758772ad651SKirk McKusick  * Dump the extended attribute data. If there was room in the file
759772ad651SKirk McKusick  * header, then all we need to do is output the data blocks. If there
760772ad651SKirk McKusick  * was not room in the file header, then an additional TS_ADDR header
761772ad651SKirk McKusick  * is created to hold the attribute data.
762772ad651SKirk McKusick  */
763772ad651SKirk McKusick static void
writeextdata(union dinode * dp,ino_t ino,int added)764772ad651SKirk McKusick writeextdata(union dinode *dp, ino_t ino, int added)
765772ad651SKirk McKusick {
766772ad651SKirk McKusick 	int i, frags, blks, tbperdb, last;
767772ad651SKirk McKusick 	ufs2_daddr_t *bp;
768772ad651SKirk McKusick 	off_t size;
769772ad651SKirk McKusick 
770772ad651SKirk McKusick 	/*
771772ad651SKirk McKusick 	 * If no extended attributes, there is nothing to do.
772772ad651SKirk McKusick 	 */
773772ad651SKirk McKusick 	if (spcl.c_extsize == 0)
774772ad651SKirk McKusick 		return;
775772ad651SKirk McKusick 	/*
776772ad651SKirk McKusick 	 * If there was no room in the file block for the attributes,
777772ad651SKirk McKusick 	 * dump them out in a new block, otherwise just dump the data.
778772ad651SKirk McKusick 	 */
779772ad651SKirk McKusick 	if (added == 0) {
7801dc349abSEd Maste 		if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) {
7811dc349abSEd Maste 			frags = UFS_NXADDR * sblock->fs_frag;
782772ad651SKirk McKusick 			last = 0;
783772ad651SKirk McKusick 		} else {
784772ad651SKirk McKusick 			frags = howmany(spcl.c_extsize, sblock->fs_fsize);
785772ad651SKirk McKusick 			last = 1;
786772ad651SKirk McKusick 		}
787772ad651SKirk McKusick 		ufs2_blksout(dp, &dp->dp2.di_extb[0], frags, ino, last);
788772ad651SKirk McKusick 	} else {
7891dc349abSEd Maste 		if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize)
7901dc349abSEd Maste 			blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE);
791772ad651SKirk McKusick 		else
792772ad651SKirk McKusick 			blks = howmany(spcl.c_extsize, TP_BSIZE);
793772ad651SKirk McKusick 		tbperdb = sblock->fs_bsize >> tp_bshift;
794772ad651SKirk McKusick 		for (i = 0; i < blks; i += tbperdb) {
795772ad651SKirk McKusick 			bp = &dp->dp2.di_extb[i / tbperdb];
796772ad651SKirk McKusick 			if (*bp != 0) {
797772ad651SKirk McKusick 				if (i + tbperdb <= blks)
798772ad651SKirk McKusick 					dumpblock(*bp, (int)sblock->fs_bsize);
799772ad651SKirk McKusick 				else
800772ad651SKirk McKusick 					dumpblock(*bp, (blks - i) * TP_BSIZE);
801772ad651SKirk McKusick 			}
802772ad651SKirk McKusick 		}
803772ad651SKirk McKusick 
804772ad651SKirk McKusick 	}
805772ad651SKirk McKusick 	/*
806772ad651SKirk McKusick 	 * If an indirect block is added for extended attributes, then
807772ad651SKirk McKusick 	 * di_exti below should be changed to the structure element
808772ad651SKirk McKusick 	 * that references the extended attribute indirect block. This
809772ad651SKirk McKusick 	 * definition is here only to make it compile without complaint.
810772ad651SKirk McKusick 	 */
811772ad651SKirk McKusick #define di_exti di_spare[0]
812772ad651SKirk McKusick 	/*
813772ad651SKirk McKusick 	 * If the extended attributes fall into an indirect block,
814772ad651SKirk McKusick 	 * dump it as well.
815772ad651SKirk McKusick 	 */
8161dc349abSEd Maste 	if ((size = spcl.c_extsize - UFS_NXADDR * sblock->fs_bsize) > 0)
817772ad651SKirk McKusick 		dmpindir(dp, ino, dp->dp2.di_exti, 0, &size);
818772ad651SKirk McKusick }
8198fae3551SRodney W. Grimes 
8208fae3551SRodney W. Grimes /*
8218fae3551SRodney W. Grimes  * Dump a map to the tape.
8228fae3551SRodney W. Grimes  */
8238fae3551SRodney W. Grimes void
dumpmap(char * map,int type,ino_t ino)8242db673abSWarner Losh dumpmap(char *map, int type, ino_t ino)
8258fae3551SRodney W. Grimes {
826d2334e27SIan Dowse 	int i;
8278fae3551SRodney W. Grimes 	char *cp;
8288fae3551SRodney W. Grimes 
8298fae3551SRodney W. Grimes 	spcl.c_type = type;
8308fae3551SRodney W. Grimes 	spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
8318fae3551SRodney W. Grimes 	writeheader(ino);
8328fae3551SRodney W. Grimes 	for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
8338fae3551SRodney W. Grimes 		writerec(cp, 0);
8348fae3551SRodney W. Grimes }
8358fae3551SRodney W. Grimes 
8368fae3551SRodney W. Grimes /*
8378fae3551SRodney W. Grimes  * Write a header record to the dump tape.
8388fae3551SRodney W. Grimes  */
8398fae3551SRodney W. Grimes void
writeheader(ino_t ino)8402db673abSWarner Losh writeheader(ino_t ino)
8418fae3551SRodney W. Grimes {
842d2334e27SIan Dowse 	int32_t sum, cnt, *lp;
8438fae3551SRodney W. Grimes 
844693c40a3SKirk McKusick 	if (rsync_friendly >= 2) {
845693c40a3SKirk McKusick 		/* don't track changes to access time */
846693c40a3SKirk McKusick 		spcl.c_atime = spcl.c_mtime;
847693c40a3SKirk McKusick 		spcl.c_atimensec = spcl.c_mtimensec;
848693c40a3SKirk McKusick 	}
8498fae3551SRodney W. Grimes 	spcl.c_inumber = ino;
8501c85e6a3SKirk McKusick 	spcl.c_magic = FS_UFS2_MAGIC;
8518fae3551SRodney W. Grimes 	spcl.c_checksum = 0;
85297b465b1SDima Ruban 	lp = (int32_t *)&spcl;
8538fae3551SRodney W. Grimes 	sum = 0;
85497b465b1SDima Ruban 	cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
8558fae3551SRodney W. Grimes 	while (--cnt >= 0) {
8568fae3551SRodney W. Grimes 		sum += *lp++;
8578fae3551SRodney W. Grimes 		sum += *lp++;
8588fae3551SRodney W. Grimes 		sum += *lp++;
8598fae3551SRodney W. Grimes 		sum += *lp++;
8608fae3551SRodney W. Grimes 	}
8618fae3551SRodney W. Grimes 	spcl.c_checksum = CHECKSUM - sum;
8628fae3551SRodney W. Grimes 	writerec((char *)&spcl, 1);
8638fae3551SRodney W. Grimes }
8648fae3551SRodney W. Grimes 
8651c85e6a3SKirk McKusick union dinode *
getino(ino_t inum,int * modep)86607436eebSKirk McKusick getino(ino_t inum, int *modep)
8678fae3551SRodney W. Grimes {
8681c85e6a3SKirk McKusick 	static ino_t minino, maxino;
8691c85e6a3SKirk McKusick 	static caddr_t inoblock;
8701c85e6a3SKirk McKusick 	struct ufs1_dinode *dp1;
8711c85e6a3SKirk McKusick 	struct ufs2_dinode *dp2;
8728fae3551SRodney W. Grimes 
8731c85e6a3SKirk McKusick 	if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL)
8741c85e6a3SKirk McKusick 		quit("cannot allocate inode memory.\n");
8758fae3551SRodney W. Grimes 	curino = inum;
8768fae3551SRodney W. Grimes 	if (inum >= minino && inum < maxino)
8771c85e6a3SKirk McKusick 		goto gotit;
878a770ae06SKirk McKusick 	blkread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock,
8798fae3551SRodney W. Grimes 	    (int)sblock->fs_bsize);
8808fae3551SRodney W. Grimes 	minino = inum - (inum % INOPB(sblock));
8818fae3551SRodney W. Grimes 	maxino = minino + INOPB(sblock);
8821c85e6a3SKirk McKusick gotit:
8831c85e6a3SKirk McKusick 	if (sblock->fs_magic == FS_UFS1_MAGIC) {
8841c85e6a3SKirk McKusick 		dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino];
885d8ba45e2SEd Maste 		*modep = (dp1->di_mode & IFMT);
8861c85e6a3SKirk McKusick 		return ((union dinode *)dp1);
8871c85e6a3SKirk McKusick 	}
8881c85e6a3SKirk McKusick 	dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino];
889d8ba45e2SEd Maste 	*modep = (dp2->di_mode & IFMT);
8901c85e6a3SKirk McKusick 	return ((union dinode *)dp2);
8918fae3551SRodney W. Grimes }
8928fae3551SRodney W. Grimes 
8938fae3551SRodney W. Grimes /*
8948fae3551SRodney W. Grimes  * Read a chunk of data from the disk.
8958fae3551SRodney W. Grimes  * Try to recover from hard errors by reading in sector sized pieces.
8968fae3551SRodney W. Grimes  * Error recovery is attempted at most BREADEMAX times before seeking
8978fae3551SRodney W. Grimes  * consent from the operator to continue.
8988fae3551SRodney W. Grimes  */
8998fae3551SRodney W. Grimes int	breaderrors = 0;
9008fae3551SRodney W. Grimes #define	BREADEMAX 32
9018fae3551SRodney W. Grimes 
9028fae3551SRodney W. Grimes void
blkread(ufs2_daddr_t blkno,char * buf,int size)903a770ae06SKirk McKusick blkread(ufs2_daddr_t blkno, char *buf, int size)
9048fae3551SRodney W. Grimes {
905924a7003SKirk McKusick 	int secsize, bytes, resid, xfer, base, cnt, i;
906924a7003SKirk McKusick 	static char *tmpbuf;
907924a7003SKirk McKusick 	off_t offset;
9088fae3551SRodney W. Grimes 
9098fae3551SRodney W. Grimes loop:
910924a7003SKirk McKusick 	offset = blkno << dev_bshift;
911924a7003SKirk McKusick 	secsize = sblock->fs_fsize;
912924a7003SKirk McKusick 	base = offset % secsize;
913924a7003SKirk McKusick 	resid = size % secsize;
914924a7003SKirk McKusick 	/*
915924a7003SKirk McKusick 	 * If the transfer request starts or ends on a non-sector
916924a7003SKirk McKusick 	 * boundary, we must read the entire sector and copy out
917924a7003SKirk McKusick 	 * just the part that we need.
918924a7003SKirk McKusick 	 */
919924a7003SKirk McKusick 	if (base == 0 && resid == 0) {
920924a7003SKirk McKusick 		cnt = cread(diskfd, buf, size, offset);
9215941e412SMatthew Dillon 		if (cnt == size)
9228fae3551SRodney W. Grimes 			return;
923924a7003SKirk McKusick 	} else {
9240b410d9cSMarcelo Araujo 		if (tmpbuf == NULL && (tmpbuf = malloc(secsize)) == NULL)
925924a7003SKirk McKusick 			quit("buffer malloc failed\n");
926924a7003SKirk McKusick 		xfer = 0;
927924a7003SKirk McKusick 		bytes = size;
928924a7003SKirk McKusick 		if (base != 0) {
929924a7003SKirk McKusick 			cnt = cread(diskfd, tmpbuf, secsize, offset - base);
930924a7003SKirk McKusick 			if (cnt != secsize)
931924a7003SKirk McKusick 				goto bad;
932993425eeSThomas Quinot 			xfer = MIN(secsize - base, size);
933924a7003SKirk McKusick 			offset += xfer;
934924a7003SKirk McKusick 			bytes -= xfer;
935924a7003SKirk McKusick 			resid = bytes % secsize;
936924a7003SKirk McKusick 			memcpy(buf, &tmpbuf[base], xfer);
937924a7003SKirk McKusick 		}
938924a7003SKirk McKusick 		if (bytes >= secsize) {
939924a7003SKirk McKusick 			cnt = cread(diskfd, &buf[xfer], bytes - resid, offset);
940924a7003SKirk McKusick 			if (cnt != bytes - resid)
941924a7003SKirk McKusick 				goto bad;
942924a7003SKirk McKusick 			xfer += cnt;
943924a7003SKirk McKusick 			offset += cnt;
944924a7003SKirk McKusick 		}
945924a7003SKirk McKusick 		if (resid == 0)
946924a7003SKirk McKusick 			return;
947924a7003SKirk McKusick 		cnt = cread(diskfd, tmpbuf, secsize, offset);
948924a7003SKirk McKusick 		if (cnt == secsize) {
949924a7003SKirk McKusick 			memcpy(&buf[xfer], tmpbuf, resid);
950924a7003SKirk McKusick 			return;
951924a7003SKirk McKusick 		}
952924a7003SKirk McKusick 	}
953924a7003SKirk McKusick bad:
9548fae3551SRodney W. Grimes 	if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
9558fae3551SRodney W. Grimes 		/*
9568fae3551SRodney W. Grimes 		 * Trying to read the final fragment.
9578fae3551SRodney W. Grimes 		 *
9588fae3551SRodney W. Grimes 		 * NB - dump only works in TP_BSIZE blocks, hence
9598fae3551SRodney W. Grimes 		 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
9608fae3551SRodney W. Grimes 		 * It should be smarter about not actually trying to
9618fae3551SRodney W. Grimes 		 * read more than it can get, but for the time being
9628fae3551SRodney W. Grimes 		 * we punt and scale back the read only when it gets
9638fae3551SRodney W. Grimes 		 * us into trouble. (mkm 9/25/83)
9648fae3551SRodney W. Grimes 		 */
9658fae3551SRodney W. Grimes 		size -= dev_bsize;
9668fae3551SRodney W. Grimes 		goto loop;
9678fae3551SRodney W. Grimes 	}
9688fae3551SRodney W. Grimes 	if (cnt == -1)
969617dbd3cSIan Dowse 		msg("read error from %s: %s: [block %jd]: count=%d\n",
970617dbd3cSIan Dowse 			disk, strerror(errno), (intmax_t)blkno, size);
9718fae3551SRodney W. Grimes 	else
972617dbd3cSIan Dowse 		msg("short read error from %s: [block %jd]: count=%d, got=%d\n",
973617dbd3cSIan Dowse 			disk, (intmax_t)blkno, size, cnt);
9748fae3551SRodney W. Grimes 	if (++breaderrors > BREADEMAX) {
975325167c3SIan Dowse 		msg("More than %d block read errors from %s\n",
9768fae3551SRodney W. Grimes 			BREADEMAX, disk);
9778fae3551SRodney W. Grimes 		broadcast("DUMP IS AILING!\n");
9788fae3551SRodney W. Grimes 		msg("This is an unrecoverable error.\n");
9798fae3551SRodney W. Grimes 		if (!query("Do you want to attempt to continue?")){
9808fae3551SRodney W. Grimes 			dumpabort(0);
9818fae3551SRodney W. Grimes 			/*NOTREACHED*/
9828fae3551SRodney W. Grimes 		} else
9838fae3551SRodney W. Grimes 			breaderrors = 0;
9848fae3551SRodney W. Grimes 	}
9858fae3551SRodney W. Grimes 	/*
9865941e412SMatthew Dillon 	 * Zero buffer, then try to read each sector of buffer separately,
9875941e412SMatthew Dillon 	 * and bypass the cache.
9888fae3551SRodney W. Grimes 	 */
989a37c38b8SPeter Wemm 	memset(buf, 0, size);
9908fae3551SRodney W. Grimes 	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
991be1bf707SMike Heffner 		if ((cnt = pread(diskfd, buf, (int)dev_bsize,
992be1bf707SMike Heffner 		    ((off_t)blkno << dev_bshift))) == dev_bsize)
9938fae3551SRodney W. Grimes 			continue;
9948fae3551SRodney W. Grimes 		if (cnt == -1) {
995617dbd3cSIan Dowse 			msg("read error from %s: %s: [sector %jd]: count=%ld\n",
996617dbd3cSIan Dowse 			    disk, strerror(errno), (intmax_t)blkno, dev_bsize);
9978fae3551SRodney W. Grimes 			continue;
9988fae3551SRodney W. Grimes 		}
999617dbd3cSIan Dowse 		msg("short read from %s: [sector %jd]: count=%ld, got=%d\n",
1000617dbd3cSIan Dowse 		    disk, (intmax_t)blkno, dev_bsize, cnt);
10018fae3551SRodney W. Grimes 	}
10028fae3551SRodney W. Grimes }
1003