1/*	$NetBSD: traverse.c,v 1.56 2023/08/07 23:26:40 mrg Exp $	*/
2
3/*-
4 * Copyright (c) 1980, 1988, 1991, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. 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/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)traverse.c	8.7 (Berkeley) 6/15/95";
36#else
37__RCSID("$NetBSD: traverse.c,v 1.56 2023/08/07 23:26:40 mrg Exp $");
38#endif
39#endif /* not lint */
40
41#include <sys/param.h>
42#include <sys/time.h>
43#include <sys/stat.h>
44#include <ufs/ufs/dir.h>
45#include <ufs/ffs/fs.h>
46#include <ufs/ffs/ffs_extern.h>
47
48#include <assert.h>
49#include <ctype.h>
50#include <errno.h>
51#include <fts.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <unistd.h>
56
57#include "dump.h"
58
59#define	HASDUMPEDFILE	0x1
60#define	HASSUBDIRS	0x2
61
62static	int appendextdata(union dinode *dp);
63static	void writeextdata(union dinode *dp, ino_t ino, int added);
64static	int dirindir(ino_t, daddr_t, int, off_t *, u_int64_t *, int);
65static	void dmpindir(union dinode *dp, ino_t, daddr_t, int, off_t *);
66static	int searchdir(ino_t, daddr_t, long, off_t, u_int64_t *, int);
67
68/*
69 * This is an estimation of the number of TP_BSIZE blocks in the file.
70 * It estimates the number of blocks in files with holes by assuming
71 * that all of the blocks accounted for by di_blocks are data blocks
72 * (when some of the blocks are usually used for indirect pointers);
73 * hence the estimate may be high.
74 */
75int64_t
76blockest(union dinode *dp)
77{
78	int64_t blkest, sizeest;
79
80	/*
81	 * dp->di_size is the size of the file in bytes.
82	 * dp->di_blocks stores the number of sectors actually in the file.
83	 * If there are more sectors than the size would indicate, this just
84	 *	means that there are indirect blocks in the file or unused
85	 *	sectors in the last file block; we can safely ignore these
86	 *	(blkest = sizeest below).
87	 * If the file is bigger than the number of sectors would indicate,
88	 *	then the file has holes in it.	In this case we must use the
89	 *	block count to estimate the number of data blocks used, but
90	 *	we use the actual size for estimating the number of indirect
91	 *	dump blocks (sizeest vs. blkest in the indirect block
92	 *	calculation).
93	 */
94	if (DIP(dp, flags) & SF_SNAPSHOT)
95		return (1);
96	blkest = howmany(ufs_fragroundup(ufsib,
97	    dbtob((u_int64_t)DIP(dp, blocks))), TP_BSIZE);
98	sizeest = howmany(ufs_fragroundup(ufsib, DIP(dp, size)), TP_BSIZE);
99	if (blkest > sizeest)
100		blkest = sizeest;
101	if (DIP(dp, size) > ufsib->ufs_bsize * UFS_NDADDR) {
102		/* calculate the number of indirect blocks on the dump tape */
103		blkest +=
104			howmany(sizeest - UFS_NDADDR * ufsib->ufs_bsize / TP_BSIZE,
105			TP_NINDIR);
106	}
107	return (blkest + 1);
108}
109
110/* Auxiliary macro to pick up files changed since previous dump. */
111#define	CHANGEDSINCE(dp, t) \
112	(DIP((dp), mtime) >= (t) || DIP((dp), ctime) >= (t))
113
114/* The WANTTODUMP macro decides whether a file should be dumped. */
115#ifdef UF_NODUMP
116#define	WANTTODUMP(dp) \
117	(CHANGEDSINCE(dp, iswap64(spcl.c_ddate)) && \
118	 (nonodump || (DIP((dp), flags) & UF_NODUMP) != UF_NODUMP))
119#else
120#define	WANTTODUMP(dp) CHANGEDSINCE(dp, iswap64(spcl.c_ddate))
121#endif
122
123/*
124 * Determine if given inode should be dumped
125 */
126void
127mapfileino(ino_t ino, u_int64_t *tape_size, int *dirskipped)
128{
129	int mode;
130	union dinode *dp;
131
132	/*
133	 * Skip inode if we've already marked it for dumping
134	 */
135	if (TSTINO(ino, usedinomap))
136		return;
137	dp = getino(ino);
138	if (dp == NULL || (mode = (DIP(dp, mode) & IFMT)) == 0)
139		return;
140	/*
141	 * Skip WAPBL log file inodes.
142	 */
143	if (DIP(dp, flags) & SF_LOG)
144		return;
145	/*
146	 * Put all dirs in dumpdirmap, inodes that are to be dumped in the
147	 * used map. All inode but dirs who have the nodump attribute go
148	 * to the usedinomap.
149	 */
150	SETINO(ino, usedinomap);
151	if (mode == IFDIR)
152		SETINO(ino, dumpdirmap);
153	if (WANTTODUMP(dp)) {
154		SETINO(ino, dumpinomap);
155		if (mode != IFREG && mode != IFDIR && mode != IFLNK)
156			*tape_size += 1;
157		else
158			*tape_size += blockest(dp);
159		return;
160	}
161	if (mode == IFDIR) {
162#ifdef UF_NODUMP
163		if (!nonodump && (DIP(dp, flags) & UF_NODUMP))
164			CLRINO(ino, usedinomap);
165#endif
166		*dirskipped = 1;
167	}
168}
169
170/*
171 * Dump pass 1.
172 *
173 * Walk the inode list for a file system to find all allocated inodes
174 * that have been modified since the previous dump time. Also, find all
175 * the directories in the file system.
176 * disk may be NULL if dirv is NULL.
177 */
178int
179mapfiles(ino_t maxino, u_int64_t *tape_size, char *diskname, char * const *dirv)
180{
181	int anydirskipped = 0;
182
183	if (dirv != NULL) {
184		char	 curdir[MAXPATHLEN];
185		FTS	*dirh;
186		FTSENT	*entry;
187		int	 d;
188
189		if (getcwd(curdir, sizeof(curdir)) == NULL) {
190			msg("Can't determine cwd: %s\n", strerror(errno));
191			dumpabort(0);
192		}
193		if ((dirh = fts_open(dirv, FTS_PHYSICAL|FTS_SEEDOT|FTS_XDEV,
194		    		    NULL)) == NULL) {
195			msg("fts_open failed: %s\n", strerror(errno));
196			dumpabort(0);
197		}
198		while ((entry = fts_read(dirh)) != NULL) {
199			switch (entry->fts_info) {
200			case FTS_DNR:		/* an error */
201			case FTS_ERR:
202			case FTS_NS:
203				msg("Can't fts_read %s: %s\n", entry->fts_path,
204				    strerror(errno));
205				/* FALLTHROUGH */
206			case FTS_DP:		/* already seen dir */
207				continue;
208			}
209			mapfileino(entry->fts_statp->st_ino, tape_size,
210			    &anydirskipped);
211		}
212		(void)fts_close(dirh);
213
214		/*
215		 * Add any parent directories
216		 */
217		for (d = 0 ; dirv[d] != NULL ; d++) {
218			char path[MAXPATHLEN];
219
220			if (dirv[d][0] != '/')
221				(void)snprintf(path, sizeof(path), "%s/%s",
222				    curdir, dirv[d]);
223			else
224				(void)snprintf(path, sizeof(path), "%s",
225				    dirv[d]);
226			while (strcmp(path, diskname) != 0) {
227				char *p;
228				struct stat sb;
229
230				if (*path == '\0')
231					break;
232				if ((p = strrchr(path, '/')) == NULL)
233					break;
234				if (p == path)
235					break;
236				*p = '\0';
237				if (stat(path, &sb) == -1) {
238					msg("Can't stat %s: %s\n", path,
239					    strerror(errno));
240					break;
241				}
242				mapfileino(sb.st_ino, tape_size, &anydirskipped);
243			}
244		}
245
246		/*
247		 * Ensure that the root inode actually appears in the
248		 * file list for a subdir
249		 */
250		mapfileino(UFS_ROOTINO, tape_size, &anydirskipped);
251	} else {
252		fs_mapinodes(maxino, tape_size, &anydirskipped);
253	}
254	/*
255	 * Restore gets very upset if the root is not dumped,
256	 * so ensure that it always is dumped.
257	 */
258	SETINO(UFS_ROOTINO, dumpinomap);
259	return (anydirskipped);
260}
261
262/*
263 * Dump pass 2.
264 *
265 * Scan each directory on the file system to see if it has any modified
266 * files in it. If it does, and has not already been added to the dump
267 * list (because it was itself modified), then add it. If a directory
268 * has not been modified itself, contains no modified files and has no
269 * subdirectories, then it can be deleted from the dump list and from
270 * the list of directories. By deleting it from the list of directories,
271 * its parent may now qualify for the same treatment on this or a later
272 * pass using this algorithm.
273 */
274int
275mapdirs(ino_t maxino, u_int64_t *tape_size)
276{
277	union dinode *dp, di;
278	int i, isdir, nodump;
279	char *map;
280	ino_t ino;
281	off_t filesize;
282	int ret, change = 0;
283	daddr_t blk;
284
285	isdir = 0;		/* XXX just to get gcc to shut up */
286	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
287		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
288			isdir = *map++;
289		else
290			isdir >>= 1;
291		/*
292		 * If dir has been removed from the used map, it's either
293		 * because it had the nodump flag, or it inherited it from
294		 * its parent. A directory can't be in dumpinomap if
295		 * not in usedinomap, but we have to go throuh it anyway
296		 * to propagate the nodump attribute.
297		 */
298		nodump = (TSTINO(ino, usedinomap) == 0);
299		if ((isdir & 1) == 0 ||
300		    (TSTINO(ino, dumpinomap) && nodump == 0))
301			continue;
302
303		dp = getino(ino);
304		/*
305		 * inode buf may be changed in searchdir().
306		 */
307		if (is_ufs2)
308			di.dp2 = dp->dp2;
309		else
310			di.dp1 = dp->dp1;
311		filesize = DIP(&di, size);
312		for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) {
313			if (is_ufs2)
314				blk = iswap64(di.dp2.di_db[i]);
315			else
316				blk = iswap32(di.dp1.di_db[i]);
317			if (blk != 0)
318				ret |= searchdir(ino, blk,
319					(long)ufs_dblksize(ufsib, &di, i),
320					filesize, tape_size, nodump);
321			if (ret & HASDUMPEDFILE)
322				filesize = 0;
323			else
324				filesize -= ufsib->ufs_bsize;
325		}
326		for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) {
327			if (is_ufs2)
328				blk = iswap64(di.dp2.di_ib[i]);
329			else
330				blk = iswap32(di.dp1.di_ib[i]);
331			if (blk == 0)
332				continue;
333			ret |= dirindir(ino, blk, i, &filesize,
334			    tape_size, nodump);
335		}
336		if (ret & HASDUMPEDFILE) {
337			SETINO(ino, dumpinomap);
338			*tape_size += blockest(&di);
339			change = 1;
340			continue;
341		}
342		if (nodump) {
343			if (ret & HASSUBDIRS)
344				change = 1; /* subdirs have inherited nodump */
345			CLRINO(ino, dumpdirmap);
346		} else if ((ret & HASSUBDIRS) == 0) {
347			if (!TSTINO(ino, dumpinomap)) {
348				CLRINO(ino, dumpdirmap);
349				change = 1;
350			}
351		}
352	}
353	return (change);
354}
355
356/*
357 * Read indirect blocks, and pass the data blocks to be searched
358 * as directories. Quit as soon as any entry is found that will
359 * require the directory to be dumped.
360 */
361static int
362dirindir(ino_t ino, daddr_t blkno, int ind_level, off_t *filesize,
363	u_int64_t *tape_size, int nodump)
364{
365	int ret = 0;
366	int i;
367	union {
368		int64_t i64[MAXBSIZE / sizeof (int64_t)];
369		int32_t i32[MAXBSIZE / sizeof (int32_t)];
370	} idblk;
371
372	bread(fsatoda(ufsib, blkno), (char *)&idblk, (int)ufsib->ufs_bsize);
373	if (ind_level <= 0) {
374		for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) {
375			if (is_ufs2)
376				blkno = iswap64(idblk.i64[i]);
377			else
378				blkno = iswap32(idblk.i32[i]);
379			if (blkno != 0)
380				ret |= searchdir(ino, blkno,
381				    ufsib->ufs_bsize, *filesize,
382				    tape_size, nodump);
383			if (ret & HASDUMPEDFILE)
384				*filesize = 0;
385			else
386				*filesize -= ufsib->ufs_bsize;
387		}
388		return (ret);
389	}
390	ind_level--;
391	for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) {
392		if (is_ufs2)
393			blkno = iswap64(idblk.i64[i]);
394		else
395			blkno = iswap32(idblk.i32[i]);
396		if (blkno != 0)
397			ret |= dirindir(ino, blkno, ind_level, filesize,
398			    tape_size, nodump);
399	}
400	return (ret);
401}
402
403/*
404 * Scan a disk block containing directory information looking to see if
405 * any of the entries are on the dump list and to see if the directory
406 * contains any subdirectories.
407 */
408static int
409searchdir(ino_t dino, daddr_t blkno, long size, off_t filesize,
410	u_int64_t *tape_size, int nodump)
411{
412	struct direct *dp;
413	union dinode *ip;
414	long loc, ret = 0;
415	char *dblk;
416	ino_t ino;
417
418	dblk = malloc(size);
419	if (dblk == NULL)
420		quit("%s: cannot allocate directory memory", __func__);
421	bread(fsatoda(ufsib, blkno), dblk, (int)size);
422	if (filesize < size)
423		size = filesize;
424	for (loc = 0; loc < size; ) {
425		dp = (struct direct *)(dblk + loc);
426		if (dp->d_reclen == 0) {
427			msg("corrupted directory, inumber %llu\n",
428			    (unsigned long long)dino);
429			break;
430		}
431		loc += iswap16(dp->d_reclen);
432		if (dp->d_ino == 0)
433			continue;
434		if (dp->d_name[0] == '.') {
435			if (dp->d_name[1] == '\0')
436				continue;
437			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
438				continue;
439		}
440		ino = iswap32(dp->d_ino);
441		if (nodump) {
442			ip = getino(ino);
443			if (TSTINO(ino, dumpinomap)) {
444				CLRINO(ino, dumpinomap);
445				*tape_size -= blockest(ip);
446			}
447			/*
448			 * Add back to dumpdirmap and remove from usedinomap
449			 * to propagate nodump.
450			 */
451			if ((DIP(ip, mode) & IFMT) == IFDIR) {
452				SETINO(ino, dumpdirmap);
453				CLRINO(ino, usedinomap);
454				ret |= HASSUBDIRS;
455			}
456		} else {
457			if (TSTINO(ino, dumpinomap)) {
458				ret |= HASDUMPEDFILE;
459				if (ret & HASSUBDIRS)
460					break;
461			}
462			if (TSTINO(ino, dumpdirmap)) {
463				ret |= HASSUBDIRS;
464				if (ret & HASDUMPEDFILE)
465					break;
466			}
467		}
468	}
469	free(dblk);
470	return (ret);
471}
472
473/*
474 * Dump passes 3 and 4.
475 *
476 * Dump the contents of an inode to tape.
477 */
478void
479dumpino(union dinode *dp, ino_t ino)
480{
481	int ind_level, cnt, last, added;
482	off_t size;
483	char buf[TP_BSIZE];
484	daddr_t blk;
485	void *shortlink;
486
487	if (newtape) {
488		newtape = 0;
489		dumpmap(dumpinomap, TS_BITS, ino);
490	}
491	CLRINO(ino, dumpinomap);
492	/*
493	 * Zero out the size of a snapshot so that it will be dumped
494	 * as a zero length file.
495	 */
496	if (DIP(dp, flags) & SF_SNAPSHOT) {
497		DIP_SET(dp, size, 0);
498		DIP_SET(dp, flags, DIP(dp, flags) & ~SF_SNAPSHOT);
499	}
500	if (!is_ufs2) {
501		if (needswap)
502			ffs_dinode1_swap(&dp->dp1, &spcl.c_dinode);
503		else
504			spcl.c_dinode = dp->dp1;
505		spcl.c_extsize = 0;
506	} else {
507		if (needswap)
508			ffs_dinode2_swap(&dp->dp2, &dp->dp2);
509		spcl.c_mode = dp->dp2.di_mode;
510		spcl.c_size = dp->dp2.di_size;
511		spcl.c_extsize = dp->dp2.di_extsize;
512		spcl.c_atime = dp->dp2.di_atime;
513		spcl.c_atimensec = dp->dp2.di_atimensec;
514		spcl.c_mtime = dp->dp2.di_mtime;
515		spcl.c_mtimensec = dp->dp2.di_mtimensec;
516		spcl.c_birthtime = dp->dp2.di_birthtime;
517		spcl.c_birthtimensec = dp->dp2.di_birthnsec;
518		spcl.c_rdev = dp->dp2.di_rdev;
519		spcl.c_file_flags = dp->dp2.di_flags;
520		spcl.c_uid = dp->dp2.di_uid;
521		spcl.c_gid = dp->dp2.di_gid;
522	}
523	spcl.c_type = iswap32(TS_INODE);
524	spcl.c_count = 0;
525	switch (DIP(dp, mode) & IFMT) {
526
527	case 0:
528		/*
529		 * Freed inode.
530		 */
531		return;
532
533	case IFLNK:
534		/*
535		 * Check for short symbolic link.
536		 */
537		if (DIP(dp, size) > 0 &&
538#ifdef FS_44INODEFMT
539		    (DIP(dp, size) < ufsib->ufs_maxsymlinklen ||
540		     (ufsib->ufs_maxsymlinklen == 0 && DIP(dp, blocks) == 0))
541#else
542		    DIP(dp, blocks) == 0
543#endif
544			) {
545			spcl.c_addr[0] = 1;
546			spcl.c_count = iswap32(1);
547			added = appendextdata(dp);
548			writeheader(ino);
549			if (is_ufs2)
550				shortlink = dp->dp2.di_db;
551			else
552				shortlink = dp->dp1.di_db;
553			memmove(buf, shortlink, DIP(dp, size));
554			buf[DIP(dp, size)] = '\0';
555			writerec(buf, 0);
556			writeextdata(dp, ino, added);
557			return;
558		}
559		/* fall through */
560
561	case IFDIR:
562	case IFREG:
563		if (DIP(dp, size) > 0)
564			break;
565		/* fall through */
566
567	case IFIFO:
568	case IFSOCK:
569	case IFCHR:
570	case IFBLK:
571		added = appendextdata(dp);
572		writeheader(ino);
573		writeextdata(dp, ino, added);
574		return;
575
576	default:
577		msg("Warning: undefined file type 0%o\n", DIP(dp, mode) & IFMT);
578		return;
579	}
580	if (DIP(dp, size) > UFS_NDADDR * ufsib->ufs_bsize) {
581		cnt = UFS_NDADDR * ufsib->ufs_frag;
582		last = 0;
583	} else {
584		cnt = howmany(DIP(dp, size), ufsib->ufs_fsize);
585		last = 1;
586	}
587	if (is_ufs2)
588		blksout64(dp, &dp->dp2.di_db[0], cnt, ino, last);
589	else
590		blksout32(&dp->dp1.di_db[0], cnt, ino);
591
592	if ((size = DIP(dp, size) - UFS_NDADDR * ufsib->ufs_bsize) <= 0)
593		return;
594	for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) {
595		if (is_ufs2)
596			blk = iswap64(dp->dp2.di_ib[ind_level]);
597		else
598			blk = iswap32(dp->dp1.di_ib[ind_level]);
599		dmpindir(dp, ino, blk, ind_level, &size);
600		if (size <= 0)
601			return;
602	}
603}
604
605/*
606 * Read indirect blocks, and pass the data blocks to be dumped.
607 */
608static void
609dmpindir(union dinode *dp, ino_t ino, daddr_t blk, int ind_level, off_t *size)
610{
611	int i, cnt, last;
612	union {
613		int32_t i32[MAXBSIZE / sizeof (int32_t)];
614		int64_t i64[MAXBSIZE / sizeof (int64_t)];
615	} idblk;
616	daddr_t iblk;
617
618
619	if (blk != 0)
620		bread(fsatoda(ufsib, blk), (char *)&idblk,
621			(int) ufsib->ufs_bsize);
622	else
623		memset(&idblk, 0, (int)ufsib->ufs_bsize);
624	if (ind_level <= 0) {
625		if (*size < ufsib->ufs_nindir * ufsib->ufs_bsize) {
626			cnt = howmany(*size, ufsib->ufs_fsize);
627			last = 0;
628		} else {
629			cnt = ufsib->ufs_nindir * ufsib->ufs_frag;
630			last = 1;
631		}
632		*size -= ufsib->ufs_nindir * ufsib->ufs_bsize;
633		if (is_ufs2)
634			blksout64(dp, &idblk.i64[0], cnt, ino, last);
635		else
636			blksout32(&idblk.i32[0], cnt, ino);
637		return;
638	}
639	ind_level--;
640	for (i = 0; i < ufsib->ufs_nindir; i++) {
641		if (is_ufs2)
642			iblk = iswap64(idblk.i64[i]);
643		else
644			iblk = iswap32(idblk.i32[i]);
645		dmpindir(dp, ino, iblk, ind_level, size);
646		if (*size <= 0)
647			return;
648	}
649}
650
651/*
652 * Collect up the data into tape record sized buffers and output them.
653 */
654void
655blksout32(int32_t *blkp, int frags, ino_t ino)
656{
657	int32_t *bp;
658	int i, j, count, blks, tbperdb;
659
660	blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE);
661	tbperdb = ufsib->ufs_bsize >> tp_bshift;
662	for (i = 0; i < blks; i += TP_NINDIR) {
663		if (i + TP_NINDIR > blks)
664			count = blks;
665		else
666			count = i + TP_NINDIR;
667		for (j = i; j < count; j++)
668			if (blkp[j / tbperdb] != 0)
669				spcl.c_addr[j - i] = 1;
670			else
671				spcl.c_addr[j - i] = 0;
672		spcl.c_count = iswap32(count - i);
673		writeheader(ino);
674		bp = &blkp[i / tbperdb];
675		for (j = i; j < count; j += tbperdb, bp++)
676			if (*bp != 0) {
677				if (j + tbperdb <= count)
678					dumpblock(iswap32(*bp), (int)ufsib->ufs_bsize);
679				else
680					dumpblock(iswap32(*bp), (count - j) * TP_BSIZE);
681			}
682		spcl.c_type = iswap32(TS_ADDR);
683	}
684}
685
686void
687blksout64(union dinode *dp, int64_t *blkp, int frags, ino_t ino, int last)
688{
689	int64_t *bp;
690	int i, j, count, blks, tbperdb, added = 0;
691	static int writingextdata = 0;
692
693	blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE);
694#if 0
695	if (last) {
696		int resid;
697		int extsize = iswap32(spcl.c_extsize);
698		if (writingextdata)
699			resid = howmany(ufsib->ufs_qfmask & extsize,
700			    TP_BSIZE);
701		else
702			resid = howmany(ufsib->ufs_qfmask & dp->dp2.di_size,
703			    TP_BSIZE);
704		if (resid > 0)
705			blks -= howmany(ufsib->ufs_fsize, TP_BSIZE) - resid;
706	}
707#endif
708	tbperdb = ufsib->ufs_bsize >> tp_bshift;
709	for (i = 0; i < blks; i += TP_NINDIR) {
710		if (i + TP_NINDIR > blks)
711			count = blks;
712		else
713			count = i + TP_NINDIR;
714		assert(count <= TP_NINDIR + i);
715		for (j = i; j < count; j++)
716			if (blkp[j / tbperdb] != 0)
717				spcl.c_addr[j - i] = 1;
718			else
719				spcl.c_addr[j - i] = 0;
720		spcl.c_count = iswap32(count - i);
721		if (last && count == blks && !writingextdata)
722			added = appendextdata(dp);
723		writeheader(ino);
724		bp = &blkp[i / tbperdb];
725		for (j = i; j < count; j += tbperdb, bp++)
726			if (*bp != 0) {
727				if (j + tbperdb <= count)
728					dumpblock(iswap64(*bp), (int)ufsib->ufs_bsize);
729				else
730					dumpblock(iswap64(*bp), (count - j) * TP_BSIZE);
731			}
732		spcl.c_type = iswap32(TS_ADDR);
733		spcl.c_count = 0;
734		if (last && count == blks && !writingextdata) {
735			writingextdata = 1;
736			writeextdata(dp, ino, added);
737			writingextdata = 0;
738		}
739	}
740}
741
742/*
743 * If there is room in the current block for the extended attributes
744 * as well as the file data, update the header to reflect the added
745 * attribute data at the end. Attributes are placed at the end so that
746 * old versions of restore will correctly restore the file and simply
747 * discard the extra data at the end that it does not understand.
748 * The attribute data is dumped following the file data by the
749 * writeextdata() function (below).
750 */
751static int
752appendextdata(union dinode *dp)
753{
754	int i, blks, tbperdb, count, extsize;
755
756	count = iswap32(spcl.c_count);
757	extsize = iswap32(spcl.c_extsize);
758	/*
759	 * If no extended attributes, there is nothing to do.
760	 */
761	if (extsize == 0)
762		return (0);
763	/*
764	 * If there is not enough room at the end of this block
765	 * to add the extended attributes, then rather than putting
766	 * part of them here, we simply push them entirely into a
767	 * new block rather than putting some here and some later.
768	 */
769	if (extsize > UFS_NXADDR * ufsib->ufs_bsize)
770		blks = howmany(UFS_NXADDR * ufsib->ufs_bsize, TP_BSIZE);
771	else
772		blks = howmany(extsize, TP_BSIZE);
773	if (count + blks > TP_NINDIR)
774		return (0);
775	/*
776	 * Update the block map in the header to indicate the added
777	 * extended attribute. They will be appended after the file
778	 * data by the writeextdata() routine.
779	 */
780	tbperdb = ufsib->ufs_bsize >> tp_bshift;
781	assert(count + blks < TP_NINDIR);
782	for (i = 0; i < blks; i++)
783		if (dp->dp2.di_extb[i / tbperdb] != 0)
784				spcl.c_addr[count + i] = 1;
785			else
786				spcl.c_addr[count + i] = 0;
787	spcl.c_count = iswap32(count + blks);
788	return (blks);
789}
790
791/*
792 * Dump the extended attribute data. If there was room in the file
793 * header, then all we need to do is output the data blocks. If there
794 * was not room in the file header, then an additional TS_ADDR header
795 * is created to hold the attribute data.
796 */
797static void
798writeextdata(union dinode *dp, ino_t ino, int added)
799{
800	int i, frags, blks, tbperdb, last, extsize;
801	int64_t *bp;
802	off_t size;
803
804	extsize = iswap32(spcl.c_extsize);
805
806	/*
807	 * If no extended attributes, there is nothing to do.
808	 */
809	if (extsize == 0)
810		return;
811	/*
812	 * If there was no room in the file block for the attributes,
813	 * dump them out in a new block, otherwise just dump the data.
814	 */
815	if (added == 0) {
816		if (extsize > UFS_NXADDR * ufsib->ufs_bsize) {
817			frags = UFS_NXADDR * ufsib->ufs_frag;
818			last = 0;
819		} else {
820			frags = howmany(extsize, ufsib->ufs_fsize);
821			last = 1;
822		}
823		blksout64(dp, &dp->dp2.di_extb[0], frags, ino, last);
824	} else {
825		if (extsize > UFS_NXADDR * ufsib->ufs_bsize)
826			blks = howmany(UFS_NXADDR * ufsib->ufs_bsize, TP_BSIZE);
827		else
828			blks = howmany(extsize, TP_BSIZE);
829		tbperdb = ufsib->ufs_bsize >> tp_bshift;
830		for (i = 0; i < blks; i += tbperdb) {
831			bp = &dp->dp2.di_extb[i / tbperdb];
832			if (*bp != 0) {
833				if (i + tbperdb <= blks)
834					dumpblock(iswap64(*bp), (int)ufsib->ufs_bsize);
835				else
836					dumpblock(iswap64(*bp), (blks - i) * TP_BSIZE);
837			}
838		}
839
840	}
841	/*
842	 * If an indirect block is added for extended attributes, then
843	 * di_exti below should be changed to the structure element
844	 * that references the extended attribute indirect block. This
845	 * definition is here only to make it compile without complaint.
846	 */
847#define di_exti di_spare[0]
848	/*
849	 * If the extended attributes fall into an indirect block,
850	 * dump it as well.
851	 */
852	if ((size = extsize - UFS_NXADDR * ufsib->ufs_bsize) > 0)
853		dmpindir(dp, ino, dp->dp2.di_exti, 0, &size);
854}
855
856/*
857 * Dump a map to the tape.
858 */
859void
860dumpmap(char *map, int type, ino_t ino)
861{
862	int i;
863	char *cp;
864
865	spcl.c_type = iswap32(type);
866	spcl.c_count = iswap32(howmany(mapsize * sizeof(char), TP_BSIZE));
867	writeheader(ino);
868	for (i = 0, cp = map; i < iswap32(spcl.c_count); i++, cp += TP_BSIZE)
869		writerec(cp, 0);
870}
871
872/*
873 * Write a header record to the dump tape.
874 */
875void
876writeheader(ino_t ino)
877{
878	int32_t sum, cnt, *lp;
879
880	spcl.c_inumber = iswap32(ino);
881	if (is_ufs2)
882		spcl.c_magic = iswap32(FS_UFS2_MAGIC);
883	else {
884		spcl.c_magic = iswap32(NFS_MAGIC);
885		spcl.c_old_date = iswap32(iswap64(spcl.c_date));
886		spcl.c_old_ddate = iswap32(iswap64(spcl.c_ddate));
887		spcl.c_old_tapea = iswap32(iswap64(spcl.c_tapea));
888		spcl.c_old_firstrec = iswap32(iswap64(spcl.c_firstrec));
889	}
890	spcl.c_checksum = 0;
891	lp = (int32_t *)&spcl;
892	sum = 0;
893	cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
894	while (--cnt >= 0) {
895		sum += iswap32(*lp++);
896		sum += iswap32(*lp++);
897		sum += iswap32(*lp++);
898		sum += iswap32(*lp++);
899	}
900	spcl.c_checksum = iswap32(CHECKSUM - sum);
901	writerec((char *)&spcl, 1);
902}
903