xref: /freebsd/sbin/dump/traverse.c (revision aa90fbed151de512ab6e59f75df009533a15751f)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1988, 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 
35 #include <ufs/ufs/dir.h>
36 #include <ufs/ufs/dinode.h>
37 #include <ufs/ffs/fs.h>
38 
39 #include <protocols/dumprestore.h>
40 
41 #include <assert.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <timeconv.h>
50 #include <unistd.h>
51 
52 #include "dump.h"
53 
54 #define	DIP(dp, field) \
55 	((sblock->fs_magic == FS_UFS1_MAGIC) ? \
56 	(dp)->dp1.field : (dp)->dp2.field)
57 #define DIP_SET(dp, field, val) do {\
58 	if (sblock->fs_magic == FS_UFS1_MAGIC) \
59 		(dp)->dp1.field = (val); \
60 	else \
61 		(dp)->dp2.field = (val); \
62 	} while (0)
63 
64 #define	HASDUMPEDFILE	0x1
65 #define	HASSUBDIRS	0x2
66 
67 static	int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size,
68     long *tapesize, int nodump, ino_t maxino);
69 static	void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int level,
70     off_t *size);
71 static	void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino);
72 static	void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags,
73     ino_t ino, int last);
74 static	int appendextdata(union dinode *dp);
75 static	void writeextdata(union dinode *dp, ino_t ino, int added);
76 static	int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize,
77     long *tapesize, int nodump, ino_t maxino);
78 static	long blockest(union dinode *dp);
79 
80 /*
81  * This is an estimation of the number of TP_BSIZE blocks in the file.
82  * It estimates the number of blocks in files with holes by assuming
83  * that all of the blocks accounted for by di_blocks are data blocks
84  * (when some of the blocks are usually used for indirect pointers);
85  * hence the estimate may be high.
86  */
87 static long
88 blockest(union dinode *dp)
89 {
90 	long blkest, sizeest;
91 
92 	/*
93 	 * dp->di_size is the size of the file in bytes.
94 	 * dp->di_blocks stores the number of sectors actually in the file.
95 	 * If there are more sectors than the size would indicate, this just
96 	 *	means that there are indirect blocks in the file or unused
97 	 *	sectors in the last file block; we can safely ignore these
98 	 *	(blkest = sizeest below).
99 	 * If the file is bigger than the number of sectors would indicate,
100 	 *	then the file has holes in it.	In this case we must use the
101 	 *	block count to estimate the number of data blocks used, but
102 	 *	we use the actual size for estimating the number of indirect
103 	 *	dump blocks (sizeest vs. blkest in the indirect block
104 	 *	calculation).
105 	 */
106 	if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0)
107 		return (1);
108 	blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE);
109 	sizeest = howmany(DIP(dp, di_size), TP_BSIZE);
110 	if (blkest > sizeest)
111 		blkest = sizeest;
112 	if (DIP(dp, di_size) > sblock->fs_bsize * UFS_NDADDR) {
113 		/* calculate the number of indirect blocks on the dump tape */
114 		blkest += howmany(sizeest -
115 		    UFS_NDADDR * sblock->fs_bsize / TP_BSIZE, TP_NINDIR);
116 	}
117 	return (blkest + 1);
118 }
119 
120 /* Auxiliary macro to pick up files changed since previous dump. */
121 #define	CHANGEDSINCE(dp, t) \
122 	(DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t))
123 
124 /* The WANTTODUMP macro decides whether a file should be dumped. */
125 #ifdef UF_NODUMP
126 #define	WANTTODUMP(dp) \
127 	(CHANGEDSINCE(dp, spcl.c_ddate) && \
128 	 (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP))
129 #else
130 #define	WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
131 #endif
132 
133 /*
134  * Dump pass 1.
135  *
136  * Walk the inode list for a file system to find all allocated inodes
137  * that have been modified since the previous dump time. Also, find all
138  * the directories in the file system.
139  */
140 int
141 mapfiles(ino_t maxino, long *tapesize)
142 {
143 	int i, cg, mode, inosused;
144 	int anydirskipped = 0;
145 	union dinode *dp;
146 	struct cg *cgp;
147 	ino_t ino;
148 	u_char *cp;
149 
150 	if ((cgp = malloc(sblock->fs_cgsize)) == NULL)
151 		quit("mapfiles: cannot allocate memory.\n");
152 	for (cg = 0; cg < sblock->fs_ncg; cg++) {
153 		ino = cg * sblock->fs_ipg;
154 		blkread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp,
155 		    sblock->fs_cgsize);
156 		if (sblock->fs_magic == FS_UFS2_MAGIC)
157 			inosused = cgp->cg_initediblk;
158 		else
159 			inosused = sblock->fs_ipg;
160 		/*
161 		 * If we are using soft updates, then we can trust the
162 		 * cylinder group inode allocation maps to tell us which
163 		 * inodes are allocated. We will scan the used inode map
164 		 * to find the inodes that are really in use, and then
165 		 * read only those inodes in from disk.
166 		 */
167 		if (sblock->fs_flags & FS_DOSOFTDEP) {
168 			if (!cg_chkmagic(cgp))
169 				quit("mapfiles: cg %d: bad magic number\n", cg);
170 			cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
171 			for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
172 				if (*cp == 0)
173 					continue;
174 				for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
175 					if (*cp & i)
176 						break;
177 					inosused--;
178 				}
179 				break;
180 			}
181 			if (inosused <= 0)
182 				continue;
183 		}
184 		for (i = 0; i < inosused; i++, ino++) {
185 			if (ino < UFS_ROOTINO ||
186 			    (dp = getino(ino, &mode)) == NULL ||
187 			    (mode & IFMT) == 0)
188 				continue;
189 			if (ino >= maxino) {
190 				msg("Skipping inode %ju >= maxino %ju\n",
191 				    (uintmax_t)ino, (uintmax_t)maxino);
192 				continue;
193 			}
194 			/*
195 			 * Everything must go in usedinomap so that a check
196 			 * for "in dumpdirmap but not in usedinomap" to detect
197 			 * dirs with nodump set has a chance of succeeding
198 			 * (this is used in mapdirs()).
199 			 */
200 			SETINO(ino, usedinomap);
201 			if (mode == IFDIR)
202 				SETINO(ino, dumpdirmap);
203 			if (WANTTODUMP(dp)) {
204 				SETINO(ino, dumpinomap);
205 				if (mode != IFREG &&
206 				    mode != IFDIR &&
207 				    mode != IFLNK)
208 					*tapesize += 1;
209 				else
210 					*tapesize += blockest(dp);
211 				continue;
212 			}
213 			if (mode == IFDIR) {
214 				if (!nonodump &&
215 				    (DIP(dp, di_flags) & UF_NODUMP))
216 					CLRINO(ino, usedinomap);
217 				anydirskipped = 1;
218 			}
219 		}
220 	}
221 	/*
222 	 * Restore gets very upset if the root is not dumped,
223 	 * so ensure that it always is dumped.
224 	 */
225 	SETINO(UFS_ROOTINO, dumpinomap);
226 	return (anydirskipped);
227 }
228 
229 /*
230  * Dump pass 2.
231  *
232  * Scan each directory on the file system to see if it has any modified
233  * files in it. If it does, and has not already been added to the dump
234  * list (because it was itself modified), then add it. If a directory
235  * has not been modified itself, contains no modified files and has no
236  * subdirectories, then it can be deleted from the dump list and from
237  * the list of directories. By deleting it from the list of directories,
238  * its parent may now qualify for the same treatment on this or a later
239  * pass using this algorithm.
240  */
241 int
242 mapdirs(ino_t maxino, long *tapesize)
243 {
244 	union dinode *dp;
245 	int i, isdir, nodump;
246 	char *map;
247 	ino_t ino;
248 	union dinode di;
249 	long filesize;
250 	int ret, change = 0;
251 
252 	isdir = 0;		/* XXX just to get gcc to shut up */
253 	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
254 		if (((ino - 1) % CHAR_BIT) == 0)	/* map is offset by 1 */
255 			isdir = *map++;
256 		else
257 			isdir >>= 1;
258 		/*
259 		 * If a directory has been removed from usedinomap, it
260 		 * either has the nodump flag set, or has inherited
261 		 * it.  Although a directory can't be in dumpinomap if
262 		 * it isn't in usedinomap, we have to go through it to
263 		 * propagate the nodump flag.
264 		 */
265 		nodump = !nonodump && (TSTINO(ino, usedinomap) == 0);
266 		if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump))
267 			continue;
268 		dp = getino(ino, &i);
269 		/*
270 		 * inode buf may change in searchdir().
271 		 */
272 		if (sblock->fs_magic == FS_UFS1_MAGIC)
273 			di.dp1 = dp->dp1;
274 		else
275 			di.dp2 = dp->dp2;
276 		filesize = DIP(&di, di_size);
277 		for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) {
278 			if (DIP(&di, di_db[i]) != 0)
279 				ret |= searchdir(ino, DIP(&di, di_db[i]),
280 				    (long)sblksize(sblock, DIP(&di, di_size),
281 				    i), filesize, tapesize, nodump, maxino);
282 			if (ret & HASDUMPEDFILE)
283 				filesize = 0;
284 			else
285 				filesize -= sblock->fs_bsize;
286 		}
287 		for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) {
288 			if (DIP(&di, di_ib[i]) == 0)
289 				continue;
290 			ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize,
291 			    tapesize, nodump, maxino);
292 		}
293 		if (ret & HASDUMPEDFILE) {
294 			SETINO(ino, dumpinomap);
295 			*tapesize += blockest(&di);
296 			change = 1;
297 			continue;
298 		}
299 		if (nodump) {
300 			if (ret & HASSUBDIRS)
301 				change = 1;	/* subdirs inherit nodump */
302 			CLRINO(ino, dumpdirmap);
303 		} else if ((ret & HASSUBDIRS) == 0)
304 			if (!TSTINO(ino, dumpinomap)) {
305 				CLRINO(ino, dumpdirmap);
306 				change = 1;
307 			}
308 	}
309 	return (change);
310 }
311 
312 /*
313  * Read indirect blocks, and pass the data blocks to be searched
314  * as directories. Quit as soon as any entry is found that will
315  * require the directory to be dumped.
316  */
317 static int
318 dirindir(
319 	ino_t ino,
320 	ufs2_daddr_t blkno,
321 	int ind_level,
322 	long *filesize,
323 	long *tapesize,
324 	int nodump,
325 	ino_t maxino)
326 {
327 	union {
328 		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
329 		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
330 	} idblk;
331 	int ret = 0;
332 	int i;
333 
334 	blkread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize);
335 	if (ind_level <= 0) {
336 		for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
337 			if (sblock->fs_magic == FS_UFS1_MAGIC)
338 				blkno = idblk.ufs1[i];
339 			else
340 				blkno = idblk.ufs2[i];
341 			if (blkno != 0)
342 				ret |= searchdir(ino, blkno, sblock->fs_bsize,
343 					*filesize, tapesize, nodump, maxino);
344 			if (ret & HASDUMPEDFILE)
345 				*filesize = 0;
346 			else
347 				*filesize -= sblock->fs_bsize;
348 		}
349 		return (ret);
350 	}
351 	ind_level--;
352 	for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
353 		if (sblock->fs_magic == FS_UFS1_MAGIC)
354 			blkno = idblk.ufs1[i];
355 		else
356 			blkno = idblk.ufs2[i];
357 		if (blkno != 0)
358 			ret |= dirindir(ino, blkno, ind_level, filesize,
359 			    tapesize, nodump, maxino);
360 	}
361 	return (ret);
362 }
363 
364 /*
365  * Scan a disk block containing directory information looking to see if
366  * any of the entries are on the dump list and to see if the directory
367  * contains any subdirectories.
368  */
369 static int
370 searchdir(
371 	ino_t ino,
372 	ufs2_daddr_t blkno,
373 	long size,
374 	long filesize,
375 	long *tapesize,
376 	int nodump,
377 	ino_t maxino)
378 {
379 	int mode;
380 	struct direct *dp;
381 	union dinode *ip;
382 	long loc, ret = 0;
383 	static caddr_t dblk;
384 
385 	if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL)
386 		quit("searchdir: cannot allocate indirect memory.\n");
387 	blkread(fsbtodb(sblock, blkno), dblk, (int)size);
388 	if (filesize < size)
389 		size = filesize;
390 	for (loc = 0; loc < size; ) {
391 		dp = (struct direct *)(dblk + loc);
392 		if (dp->d_reclen == 0) {
393 			msg("corrupted directory, inumber %ju\n",
394 			    (uintmax_t)ino);
395 			break;
396 		}
397 		loc += dp->d_reclen;
398 		if (dp->d_ino == 0)
399 			continue;
400 		if (dp->d_ino >= maxino) {
401 			msg("corrupted directory entry, d_ino %ju >= %ju\n",
402 			    (uintmax_t)dp->d_ino, (uintmax_t)maxino);
403 			break;
404 		}
405 		if (dp->d_name[0] == '.') {
406 			if (dp->d_name[1] == '\0')
407 				continue;
408 			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
409 				continue;
410 		}
411 		if (nodump) {
412 			ip = getino(dp->d_ino, &mode);
413 			if (TSTINO(dp->d_ino, dumpinomap)) {
414 				CLRINO(dp->d_ino, dumpinomap);
415 				*tapesize -= blockest(ip);
416 			}
417 			/*
418 			 * Add back to dumpdirmap and remove from usedinomap
419 			 * to propagate nodump.
420 			 */
421 			if (mode == IFDIR) {
422 				SETINO(dp->d_ino, dumpdirmap);
423 				CLRINO(dp->d_ino, usedinomap);
424 				ret |= HASSUBDIRS;
425 			}
426 		} else {
427 			if (TSTINO(dp->d_ino, dumpinomap)) {
428 				ret |= HASDUMPEDFILE;
429 				if (ret & HASSUBDIRS)
430 					break;
431 			}
432 			if (TSTINO(dp->d_ino, dumpdirmap)) {
433 				ret |= HASSUBDIRS;
434 				if (ret & HASDUMPEDFILE)
435 					break;
436 			}
437 		}
438 	}
439 	return (ret);
440 }
441 
442 /*
443  * Dump passes 3 and 4.
444  *
445  * Dump the contents of an inode to tape.
446  */
447 void
448 dumpino(union dinode *dp, ino_t ino)
449 {
450 	int ind_level, cnt, last, added;
451 	off_t size;
452 	char buf[TP_BSIZE];
453 
454 	if (newtape) {
455 		newtape = 0;
456 		dumpmap(dumpinomap, TS_BITS, ino);
457 	}
458 	CLRINO(ino, dumpinomap);
459 	/*
460 	 * Zero out the size of a snapshot so that it will be dumped
461 	 * as a zero length file.
462 	 */
463 	if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) {
464 		DIP_SET(dp, di_size, 0);
465 		DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT);
466 	}
467 	if (sblock->fs_magic == FS_UFS1_MAGIC) {
468 		spcl.c_mode = dp->dp1.di_mode;
469 		spcl.c_size = dp->dp1.di_size;
470 		spcl.c_extsize = 0;
471 		spcl.c_atime = _time32_to_time(dp->dp1.di_atime);
472 		spcl.c_atimensec = dp->dp1.di_atimensec;
473 		spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime);
474 		spcl.c_mtimensec = dp->dp1.di_mtimensec;
475 		spcl.c_birthtime = 0;
476 		spcl.c_birthtimensec = 0;
477 		spcl.c_rdev = dp->dp1.di_rdev;
478 		spcl.c_file_flags = dp->dp1.di_flags;
479 		spcl.c_uid = dp->dp1.di_uid;
480 		spcl.c_gid = dp->dp1.di_gid;
481 	} else {
482 		spcl.c_mode = dp->dp2.di_mode;
483 		spcl.c_size = dp->dp2.di_size;
484 		spcl.c_extsize = dp->dp2.di_extsize;
485 		spcl.c_atime = _time64_to_time(dp->dp2.di_atime);
486 		spcl.c_atimensec = dp->dp2.di_atimensec;
487 		spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime);
488 		spcl.c_mtimensec = dp->dp2.di_mtimensec;
489 		spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime);
490 		spcl.c_birthtimensec = dp->dp2.di_birthnsec;
491 		spcl.c_rdev = dp->dp2.di_rdev;
492 		spcl.c_file_flags = dp->dp2.di_flags;
493 		spcl.c_uid = dp->dp2.di_uid;
494 		spcl.c_gid = dp->dp2.di_gid;
495 	}
496 	spcl.c_type = TS_INODE;
497 	spcl.c_count = 0;
498 	switch (DIP(dp, di_mode) & S_IFMT) {
499 
500 	case 0:
501 		/*
502 		 * Freed inode.
503 		 */
504 		return;
505 
506 	case S_IFLNK:
507 		/*
508 		 * Check for short symbolic link.
509 		 */
510 		if (DIP(dp, di_size) > 0 &&
511 		    DIP(dp, di_size) < sblock->fs_maxsymlinklen) {
512 			spcl.c_addr[0] = 1;
513 			spcl.c_count = 1;
514 			added = appendextdata(dp);
515 			writeheader(ino);
516 			memmove(buf, DIP(dp, di_shortlink),
517 			    (u_long)DIP(dp, di_size));
518 			buf[DIP(dp, di_size)] = '\0';
519 			writerec(buf, 0);
520 			writeextdata(dp, ino, added);
521 			return;
522 		}
523 		/* FALLTHROUGH */
524 
525 	case S_IFDIR:
526 	case S_IFREG:
527 		if (DIP(dp, di_size) > 0)
528 			break;
529 		/* FALLTHROUGH */
530 
531 	case S_IFIFO:
532 	case S_IFSOCK:
533 	case S_IFCHR:
534 	case S_IFBLK:
535 		added = appendextdata(dp);
536 		writeheader(ino);
537 		writeextdata(dp, ino, added);
538 		return;
539 
540 	default:
541 		msg("Warning: undefined file type 0%o\n",
542 		    DIP(dp, di_mode) & IFMT);
543 		return;
544 	}
545 	if (DIP(dp, di_size) > UFS_NDADDR * sblock->fs_bsize) {
546 		cnt = UFS_NDADDR * sblock->fs_frag;
547 		last = 0;
548 	} else {
549 		cnt = howmany(DIP(dp, di_size), sblock->fs_fsize);
550 		last = 1;
551 	}
552 	if (sblock->fs_magic == FS_UFS1_MAGIC)
553 		ufs1_blksout(&dp->dp1.di_db[0], cnt, ino);
554 	else
555 		ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last);
556 	if ((size = DIP(dp, di_size) - UFS_NDADDR * sblock->fs_bsize) <= 0)
557 		return;
558 	for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) {
559 		dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size);
560 		if (size <= 0)
561 			return;
562 	}
563 }
564 
565 /*
566  * Read indirect blocks, and pass the data blocks to be dumped.
567  */
568 static void
569 dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int ind_level,
570 	off_t *size)
571 {
572 	union {
573 		ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
574 		ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
575 	} idblk;
576 	int i, cnt, last;
577 
578 	if (blk != 0)
579 		blkread(fsbtodb(sblock, blk), (char *)&idblk,
580 		    (int)sblock->fs_bsize);
581 	else
582 		memset(&idblk, 0, sblock->fs_bsize);
583 	if (ind_level <= 0) {
584 		if (*size > NINDIR(sblock) * sblock->fs_bsize) {
585 			cnt = NINDIR(sblock) * sblock->fs_frag;
586 			last = 0;
587 		} else {
588 			cnt = howmany(*size, sblock->fs_fsize);
589 			last = 1;
590 		}
591 		*size -= NINDIR(sblock) * sblock->fs_bsize;
592 		if (sblock->fs_magic == FS_UFS1_MAGIC)
593 			ufs1_blksout(idblk.ufs1, cnt, ino);
594 		else
595 			ufs2_blksout(dp, idblk.ufs2, cnt, ino, last);
596 		return;
597 	}
598 	ind_level--;
599 	for (i = 0; i < NINDIR(sblock); i++) {
600 		if (sblock->fs_magic == FS_UFS1_MAGIC)
601 			dmpindir(dp, ino, idblk.ufs1[i], ind_level, size);
602 		else
603 			dmpindir(dp, ino, idblk.ufs2[i], ind_level, size);
604 		if (*size <= 0)
605 			return;
606 	}
607 }
608 
609 /*
610  * Collect up the data into tape record sized buffers and output them.
611  */
612 static void
613 ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino)
614 {
615 	ufs1_daddr_t *bp;
616 	int i, j, count, blks, tbperdb;
617 
618 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
619 	tbperdb = sblock->fs_bsize >> tp_bshift;
620 	for (i = 0; i < blks; i += TP_NINDIR) {
621 		if (i + TP_NINDIR > blks)
622 			count = blks;
623 		else
624 			count = i + TP_NINDIR;
625 		assert(count <= TP_NINDIR + i);
626 		for (j = i; j < count; j++)
627 			if (blkp[j / tbperdb] != 0)
628 				spcl.c_addr[j - i] = 1;
629 			else
630 				spcl.c_addr[j - i] = 0;
631 		spcl.c_count = count - i;
632 		writeheader(ino);
633 		bp = &blkp[i / tbperdb];
634 		for (j = i; j < count; j += tbperdb, bp++)
635 			if (*bp != 0) {
636 				if (j + tbperdb <= count)
637 					dumpblock(*bp, (int)sblock->fs_bsize);
638 				else
639 					dumpblock(*bp, (count - j) * TP_BSIZE);
640 			}
641 		spcl.c_type = TS_ADDR;
642 	}
643 }
644 
645 /*
646  * Collect up the data into tape record sized buffers and output them.
647  */
648 static void
649 ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino,
650 	int last)
651 {
652 	ufs2_daddr_t *bp;
653 	int i, j, count, resid, blks, tbperdb, added;
654 	static int writingextdata = 0;
655 
656 	/*
657 	 * Calculate the number of TP_BSIZE blocks to be dumped.
658 	 * For filesystems with a fragment size bigger than TP_BSIZE,
659 	 * only part of the final fragment may need to be dumped.
660 	 */
661 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
662 	if (last) {
663 		if (writingextdata)
664 			resid = howmany(fragoff(sblock, spcl.c_extsize),
665 			    TP_BSIZE);
666 		else
667 			resid = howmany(fragoff(sblock, dp->dp2.di_size),
668 			    TP_BSIZE);
669 		if (resid > 0)
670 			blks -= howmany(sblock->fs_fsize, TP_BSIZE) - resid;
671 	}
672 	tbperdb = sblock->fs_bsize >> tp_bshift;
673 	for (i = 0; i < blks; i += TP_NINDIR) {
674 		if (i + TP_NINDIR > blks)
675 			count = blks;
676 		else
677 			count = i + TP_NINDIR;
678 		assert(count <= TP_NINDIR + i);
679 		for (j = i; j < count; j++)
680 			if (blkp[j / tbperdb] != 0)
681 				spcl.c_addr[j - i] = 1;
682 			else
683 				spcl.c_addr[j - i] = 0;
684 		spcl.c_count = count - i;
685 		if (last && count == blks && !writingextdata)
686 			added = appendextdata(dp);
687 		writeheader(ino);
688 		bp = &blkp[i / tbperdb];
689 		for (j = i; j < count; j += tbperdb, bp++)
690 			if (*bp != 0) {
691 				if (j + tbperdb <= count)
692 					dumpblock(*bp, (int)sblock->fs_bsize);
693 				else
694 					dumpblock(*bp, (count - j) * TP_BSIZE);
695 			}
696 		spcl.c_type = TS_ADDR;
697 		spcl.c_count = 0;
698 		if (last && count == blks && !writingextdata) {
699 			writingextdata = 1;
700 			writeextdata(dp, ino, added);
701 			writingextdata = 0;
702 		}
703 	}
704 }
705 
706 /*
707  * If there is room in the current block for the extended attributes
708  * as well as the file data, update the header to reflect the added
709  * attribute data at the end. Attributes are placed at the end so that
710  * old versions of restore will correctly restore the file and simply
711  * discard the extra data at the end that it does not understand.
712  * The attribute data is dumped following the file data by the
713  * writeextdata() function (below).
714  */
715 static int
716 appendextdata(union dinode *dp)
717 {
718 	int i, blks, tbperdb;
719 
720 	/*
721 	 * If no extended attributes, there is nothing to do.
722 	 */
723 	if (spcl.c_extsize == 0)
724 		return (0);
725 	/*
726 	 * If there is not enough room at the end of this block
727 	 * to add the extended attributes, then rather than putting
728 	 * part of them here, we simply push them entirely into a
729 	 * new block rather than putting some here and some later.
730 	 */
731 	if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize)
732 		blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE);
733 	else
734 		blks = howmany(spcl.c_extsize, TP_BSIZE);
735 	if (spcl.c_count + blks > TP_NINDIR)
736 		return (0);
737 	/*
738 	 * Update the block map in the header to indicate the added
739 	 * extended attribute. They will be appended after the file
740 	 * data by the writeextdata() routine.
741 	 */
742 	tbperdb = sblock->fs_bsize >> tp_bshift;
743 	assert(spcl.c_count + blks <= TP_NINDIR);
744 	for (i = 0; i < blks; i++)
745 		if (&dp->dp2.di_extb[i / tbperdb] != 0)
746 				spcl.c_addr[spcl.c_count + i] = 1;
747 			else
748 				spcl.c_addr[spcl.c_count + i] = 0;
749 	spcl.c_count += blks;
750 	return (blks);
751 }
752 
753 /*
754  * Dump the extended attribute data. If there was room in the file
755  * header, then all we need to do is output the data blocks. If there
756  * was not room in the file header, then an additional TS_ADDR header
757  * is created to hold the attribute data.
758  */
759 static void
760 writeextdata(union dinode *dp, ino_t ino, int added)
761 {
762 	int i, frags, blks, tbperdb, last;
763 	ufs2_daddr_t *bp;
764 	off_t size;
765 
766 	/*
767 	 * If no extended attributes, there is nothing to do.
768 	 */
769 	if (spcl.c_extsize == 0)
770 		return;
771 	/*
772 	 * If there was no room in the file block for the attributes,
773 	 * dump them out in a new block, otherwise just dump the data.
774 	 */
775 	if (added == 0) {
776 		if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) {
777 			frags = UFS_NXADDR * sblock->fs_frag;
778 			last = 0;
779 		} else {
780 			frags = howmany(spcl.c_extsize, sblock->fs_fsize);
781 			last = 1;
782 		}
783 		ufs2_blksout(dp, &dp->dp2.di_extb[0], frags, ino, last);
784 	} else {
785 		if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize)
786 			blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE);
787 		else
788 			blks = howmany(spcl.c_extsize, TP_BSIZE);
789 		tbperdb = sblock->fs_bsize >> tp_bshift;
790 		for (i = 0; i < blks; i += tbperdb) {
791 			bp = &dp->dp2.di_extb[i / tbperdb];
792 			if (*bp != 0) {
793 				if (i + tbperdb <= blks)
794 					dumpblock(*bp, (int)sblock->fs_bsize);
795 				else
796 					dumpblock(*bp, (blks - i) * TP_BSIZE);
797 			}
798 		}
799 
800 	}
801 	/*
802 	 * If an indirect block is added for extended attributes, then
803 	 * di_exti below should be changed to the structure element
804 	 * that references the extended attribute indirect block. This
805 	 * definition is here only to make it compile without complaint.
806 	 */
807 #define di_exti di_spare[0]
808 	/*
809 	 * If the extended attributes fall into an indirect block,
810 	 * dump it as well.
811 	 */
812 	if ((size = spcl.c_extsize - UFS_NXADDR * sblock->fs_bsize) > 0)
813 		dmpindir(dp, ino, dp->dp2.di_exti, 0, &size);
814 }
815 
816 /*
817  * Dump a map to the tape.
818  */
819 void
820 dumpmap(char *map, int type, ino_t ino)
821 {
822 	int i;
823 	char *cp;
824 
825 	spcl.c_type = type;
826 	spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
827 	writeheader(ino);
828 	for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
829 		writerec(cp, 0);
830 }
831 
832 /*
833  * Write a header record to the dump tape.
834  */
835 void
836 writeheader(ino_t ino)
837 {
838 	int32_t sum, cnt, *lp;
839 
840 	if (rsync_friendly >= 2) {
841 		/* don't track changes to access time */
842 		spcl.c_atime = spcl.c_mtime;
843 		spcl.c_atimensec = spcl.c_mtimensec;
844 	}
845 	spcl.c_inumber = ino;
846 	spcl.c_magic = FS_UFS2_MAGIC;
847 	spcl.c_checksum = 0;
848 	lp = (int32_t *)&spcl;
849 	sum = 0;
850 	cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
851 	while (--cnt >= 0) {
852 		sum += *lp++;
853 		sum += *lp++;
854 		sum += *lp++;
855 		sum += *lp++;
856 	}
857 	spcl.c_checksum = CHECKSUM - sum;
858 	writerec((char *)&spcl, 1);
859 }
860 
861 union dinode *
862 getino(ino_t inum, int *modep)
863 {
864 	static ino_t minino, maxino;
865 	static caddr_t inoblock;
866 	struct ufs1_dinode *dp1;
867 	struct ufs2_dinode *dp2;
868 
869 	if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL)
870 		quit("cannot allocate inode memory.\n");
871 	curino = inum;
872 	if (inum >= minino && inum < maxino)
873 		goto gotit;
874 	blkread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock,
875 	    (int)sblock->fs_bsize);
876 	minino = inum - (inum % INOPB(sblock));
877 	maxino = minino + INOPB(sblock);
878 gotit:
879 	if (sblock->fs_magic == FS_UFS1_MAGIC) {
880 		dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino];
881 		*modep = (dp1->di_mode & IFMT);
882 		return ((union dinode *)dp1);
883 	}
884 	dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino];
885 	*modep = (dp2->di_mode & IFMT);
886 	return ((union dinode *)dp2);
887 }
888 
889 /*
890  * Read a chunk of data from the disk.
891  * Try to recover from hard errors by reading in sector sized pieces.
892  * Error recovery is attempted at most BREADEMAX times before seeking
893  * consent from the operator to continue.
894  */
895 int	breaderrors = 0;
896 #define	BREADEMAX 32
897 
898 void
899 blkread(ufs2_daddr_t blkno, char *buf, int size)
900 {
901 	int secsize, bytes, resid, xfer, base, cnt, i;
902 	static char *tmpbuf;
903 	off_t offset;
904 
905 loop:
906 	offset = blkno << dev_bshift;
907 	secsize = sblock->fs_fsize;
908 	base = offset % secsize;
909 	resid = size % secsize;
910 	/*
911 	 * If the transfer request starts or ends on a non-sector
912 	 * boundary, we must read the entire sector and copy out
913 	 * just the part that we need.
914 	 */
915 	if (base == 0 && resid == 0) {
916 		cnt = cread(diskfd, buf, size, offset);
917 		if (cnt == size)
918 			return;
919 	} else {
920 		if (tmpbuf == NULL && (tmpbuf = malloc(secsize)) == NULL)
921 			quit("buffer malloc failed\n");
922 		xfer = 0;
923 		bytes = size;
924 		if (base != 0) {
925 			cnt = cread(diskfd, tmpbuf, secsize, offset - base);
926 			if (cnt != secsize)
927 				goto bad;
928 			xfer = MIN(secsize - base, size);
929 			offset += xfer;
930 			bytes -= xfer;
931 			resid = bytes % secsize;
932 			memcpy(buf, &tmpbuf[base], xfer);
933 		}
934 		if (bytes >= secsize) {
935 			cnt = cread(diskfd, &buf[xfer], bytes - resid, offset);
936 			if (cnt != bytes - resid)
937 				goto bad;
938 			xfer += cnt;
939 			offset += cnt;
940 		}
941 		if (resid == 0)
942 			return;
943 		cnt = cread(diskfd, tmpbuf, secsize, offset);
944 		if (cnt == secsize) {
945 			memcpy(&buf[xfer], tmpbuf, resid);
946 			return;
947 		}
948 	}
949 bad:
950 	if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
951 		/*
952 		 * Trying to read the final fragment.
953 		 *
954 		 * NB - dump only works in TP_BSIZE blocks, hence
955 		 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
956 		 * It should be smarter about not actually trying to
957 		 * read more than it can get, but for the time being
958 		 * we punt and scale back the read only when it gets
959 		 * us into trouble. (mkm 9/25/83)
960 		 */
961 		size -= dev_bsize;
962 		goto loop;
963 	}
964 	if (cnt == -1)
965 		msg("read error from %s: %s: [block %jd]: count=%d\n",
966 			disk, strerror(errno), (intmax_t)blkno, size);
967 	else
968 		msg("short read error from %s: [block %jd]: count=%d, got=%d\n",
969 			disk, (intmax_t)blkno, size, cnt);
970 	if (++breaderrors > BREADEMAX) {
971 		msg("More than %d block read errors from %s\n",
972 			BREADEMAX, disk);
973 		broadcast("DUMP IS AILING!\n");
974 		msg("This is an unrecoverable error.\n");
975 		if (!query("Do you want to attempt to continue?")){
976 			dumpabort(0);
977 			/*NOTREACHED*/
978 		} else
979 			breaderrors = 0;
980 	}
981 	/*
982 	 * Zero buffer, then try to read each sector of buffer separately,
983 	 * and bypass the cache.
984 	 */
985 	memset(buf, 0, size);
986 	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
987 		if ((cnt = pread(diskfd, buf, (int)dev_bsize,
988 		    ((off_t)blkno << dev_bshift))) == dev_bsize)
989 			continue;
990 		if (cnt == -1) {
991 			msg("read error from %s: %s: [sector %jd]: count=%ld\n",
992 			    disk, strerror(errno), (intmax_t)blkno, dev_bsize);
993 			continue;
994 		}
995 		msg("short read from %s: [sector %jd]: count=%ld, got=%d\n",
996 		    disk, (intmax_t)blkno, dev_bsize, cnt);
997 	}
998 }
999