1/*	$NetBSD: traverse.c,v 1.47 2006/06/24 05:28:54 perseant 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.47 2006/06/24 05:28:54 perseant 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/ufs/dinode.h>
46#include <ufs/ffs/fs.h>
47#include <ufs/ffs/ffs_extern.h>
48
49#include <protocols/dumprestore.h>
50
51#include <ctype.h>
52#include <errno.h>
53#include <fts.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59#include "dump.h"
60
61#define	HASDUMPEDFILE	0x1
62#define	HASSUBDIRS	0x2
63
64static	int dirindir(ino_t, daddr_t, int, off_t *, u_int64_t *, int);
65static	void dmpindir(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 * NDADDR) {
102		/* calculate the number of indirect blocks on the dump tape */
103		blkest +=
104			howmany(sizeest - 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			case FTS_DP:		/* already seen dir */
206				continue;
207			}
208			mapfileino(entry->fts_statp->st_ino, tape_size,
209			    &anydirskipped);
210		}
211		(void)fts_close(dirh);
212
213		/*
214		 * Add any parent directories
215		 */
216		for (d = 0 ; dirv[d] != NULL ; d++) {
217			char path[MAXPATHLEN];
218
219			if (dirv[d][0] != '/')
220				(void)snprintf(path, sizeof(path), "%s/%s",
221				    curdir, dirv[d]);
222			else
223				(void)snprintf(path, sizeof(path), "%s",
224				    dirv[d]);
225			while (strcmp(path, diskname) != 0) {
226				char *p;
227				struct stat sb;
228
229				if (*path == '\0')
230					break;
231				if ((p = strrchr(path, '/')) == NULL)
232					break;
233				if (p == path)
234					break;
235				*p = '\0';
236				if (stat(path, &sb) == -1) {
237					msg("Can't stat %s: %s\n", path,
238					    strerror(errno));
239					break;
240				}
241				mapfileino(sb.st_ino, tape_size, &anydirskipped);
242			}
243		}
244
245		/*
246		 * Ensure that the root inode actually appears in the
247		 * file list for a subdir
248		 */
249		mapfileino(ROOTINO, tape_size, &anydirskipped);
250	} else {
251		fs_mapinodes(maxino, tape_size, &anydirskipped);
252	}
253	/*
254	 * Restore gets very upset if the root is not dumped,
255	 * so ensure that it always is dumped.
256	 */
257	SETINO(ROOTINO, dumpinomap);
258	return (anydirskipped);
259}
260
261/*
262 * Dump pass 2.
263 *
264 * Scan each directory on the file system to see if it has any modified
265 * files in it. If it does, and has not already been added to the dump
266 * list (because it was itself modified), then add it. If a directory
267 * has not been modified itself, contains no modified files and has no
268 * subdirectories, then it can be deleted from the dump list and from
269 * the list of directories. By deleting it from the list of directories,
270 * its parent may now qualify for the same treatment on this or a later
271 * pass using this algorithm.
272 */
273int
274mapdirs(ino_t maxino, u_int64_t *tape_size)
275{
276	union dinode *dp, di;
277	int i, isdir, nodump;
278	char *map;
279	ino_t ino;
280	off_t filesize;
281	int ret, change = 0;
282	daddr_t blk;
283
284	isdir = 0;		/* XXX just to get gcc to shut up */
285	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
286		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
287			isdir = *map++;
288		else
289			isdir >>= 1;
290		/*
291		 * If dir has been removed from the used map, it's either
292		 * because it had the nodump flag, or it inherited it from
293		 * its parent. A directory can't be in dumpinomap if
294		 * not in usedinomap, but we have to go throuh it anyway
295		 * to propagate the nodump attribute.
296		 */
297		nodump = (TSTINO(ino, usedinomap) == 0);
298		if ((isdir & 1) == 0 ||
299		    (TSTINO(ino, dumpinomap) && nodump == 0))
300			continue;
301
302		dp = getino(ino);
303		/*
304		 * inode buf may be changed in searchdir().
305		 */
306		if (is_ufs2)
307			di.dp2 = dp->dp2;
308		else
309			di.dp1 = dp->dp1;
310		filesize = DIP(&di, size);
311		for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
312			if (is_ufs2)
313				blk = iswap64(di.dp2.di_db[i]);
314			else
315				blk = iswap32(di.dp1.di_db[i]);
316			if (blk != 0)
317				ret |= searchdir(ino, blk,
318					(long)ufs_dblksize(ufsib, &di, i),
319					filesize, tape_size, nodump);
320			if (ret & HASDUMPEDFILE)
321				filesize = 0;
322			else
323				filesize -= ufsib->ufs_bsize;
324		}
325		for (i = 0; filesize > 0 && i < NIADDR; i++) {
326			if (is_ufs2)
327				blk = iswap64(di.dp2.di_ib[i]);
328			else
329				blk = iswap32(di.dp1.di_ib[i]);
330			if (blk == 0)
331				continue;
332			ret |= dirindir(ino, blk, i, &filesize,
333			    tape_size, nodump);
334		}
335		if (ret & HASDUMPEDFILE) {
336			SETINO(ino, dumpinomap);
337			*tape_size += blockest(&di);
338			change = 1;
339			continue;
340		}
341		if (nodump) {
342			if (ret & HASSUBDIRS)
343				change = 1; /* subdirs have inherited nodump */
344			CLRINO(ino, dumpdirmap);
345		} else if ((ret & HASSUBDIRS) == 0) {
346			if (!TSTINO(ino, dumpinomap)) {
347				CLRINO(ino, dumpdirmap);
348				change = 1;
349			}
350		}
351	}
352	return (change);
353}
354
355/*
356 * Read indirect blocks, and pass the data blocks to be searched
357 * as directories. Quit as soon as any entry is found that will
358 * require the directory to be dumped.
359 */
360static int
361dirindir(ino_t ino, daddr_t blkno, int ind_level, off_t *filesize,
362	u_int64_t *tape_size, int nodump)
363{
364	int ret = 0;
365	int i;
366	union {
367		int64_t i64[MAXBSIZE / sizeof (int64_t)];
368		int32_t i32[MAXBSIZE / sizeof (int32_t)];
369	} idblk;
370
371	bread(fsatoda(ufsib, blkno), (char *)&idblk, (int)ufsib->ufs_bsize);
372	if (ind_level <= 0) {
373		for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) {
374			if (is_ufs2)
375				blkno = iswap64(idblk.i64[i]);
376			else
377				blkno = iswap32(idblk.i32[i]);
378			if (blkno != 0)
379				ret |= searchdir(ino, blkno,
380				    ufsib->ufs_bsize, *filesize,
381				    tape_size, nodump);
382			if (ret & HASDUMPEDFILE)
383				*filesize = 0;
384			else
385				*filesize -= ufsib->ufs_bsize;
386		}
387		return (ret);
388	}
389	ind_level--;
390	for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) {
391		if (is_ufs2)
392			blkno = iswap64(idblk.i64[i]);
393		else
394			blkno = iswap32(idblk.i32[i]);
395		if (blkno != 0)
396			ret |= dirindir(ino, blkno, ind_level, filesize,
397			    tape_size, nodump);
398	}
399	return (ret);
400}
401
402/*
403 * Scan a disk block containing directory information looking to see if
404 * any of the entries are on the dump list and to see if the directory
405 * contains any subdirectories.
406 */
407static int
408searchdir(ino_t dino, daddr_t blkno, long size, off_t filesize,
409	u_int64_t *tape_size, int nodump)
410{
411	struct direct *dp;
412	union dinode *ip;
413	long loc, ret = 0;
414	char *dblk;
415	ino_t ino;
416
417	dblk = malloc(size);
418	if (dblk == NULL)
419		quit("searchdir: cannot allocate directory memory.\n");
420	bread(fsatoda(ufsib, blkno), dblk, (int)size);
421	if (filesize < size)
422		size = filesize;
423	for (loc = 0; loc < size; ) {
424		dp = (struct direct *)(dblk + loc);
425		if (dp->d_reclen == 0) {
426			msg("corrupted directory, inumber %llu\n",
427			    (unsigned long long)dino);
428			break;
429		}
430		loc += iswap16(dp->d_reclen);
431		if (dp->d_ino == 0)
432			continue;
433		if (dp->d_name[0] == '.') {
434			if (dp->d_name[1] == '\0')
435				continue;
436			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
437				continue;
438		}
439		ino = iswap32(dp->d_ino);
440		if (nodump) {
441			ip = getino(ino);
442			if (TSTINO(ino, dumpinomap)) {
443				CLRINO(ino, dumpinomap);
444				*tape_size -= blockest(ip);
445			}
446			/*
447			 * Add back to dumpdirmap and remove from usedinomap
448			 * to propagate nodump.
449			 */
450			if ((DIP(ip, mode) & IFMT) == IFDIR) {
451				SETINO(ino, dumpdirmap);
452				CLRINO(ino, usedinomap);
453				ret |= HASSUBDIRS;
454			}
455		} else {
456			if (TSTINO(ino, dumpinomap)) {
457				ret |= HASDUMPEDFILE;
458				if (ret & HASSUBDIRS)
459					break;
460			}
461			if (TSTINO(ino, dumpdirmap)) {
462				ret |= HASSUBDIRS;
463				if (ret & HASDUMPEDFILE)
464					break;
465			}
466		}
467	}
468	free(dblk);
469	return (ret);
470}
471
472/*
473 * Dump passes 3 and 4.
474 *
475 * Dump the contents of an inode to tape.
476 */
477void
478dumpino(union dinode *dp, ino_t ino)
479{
480	int ind_level, cnt;
481	off_t size;
482	char buf[TP_BSIZE];
483	daddr_t blk;
484	void *shortlink;
485
486	if (newtape) {
487		newtape = 0;
488		dumpmap(dumpinomap, TS_BITS, ino);
489	}
490	CLRINO(ino, dumpinomap);
491	/*
492	 * Zero out the size of a snapshot so that it will be dumped
493	 * as a zero length file.
494	 */
495	if (DIP(dp, flags) & SF_SNAPSHOT) {
496		DIP_SET(dp, size, 0);
497		DIP_SET(dp, flags, DIP(dp, flags) & ~SF_SNAPSHOT);
498	}
499	if (!is_ufs2) {
500		if (needswap)
501			ffs_dinode1_swap(&dp->dp1, &spcl.c_dinode);
502		else
503			spcl.c_dinode = dp->dp1;
504	} else {
505		if (needswap)
506			ffs_dinode2_swap(&dp->dp2, &dp->dp2);
507		spcl.c_mode = dp->dp2.di_mode;
508		spcl.c_size = dp->dp2.di_size;
509		spcl.c_atime = dp->dp2.di_atime;
510		spcl.c_atimensec = dp->dp2.di_atimensec;
511		spcl.c_mtime = dp->dp2.di_mtime;
512		spcl.c_mtimensec = dp->dp2.di_mtimensec;
513		spcl.c_birthtime = dp->dp2.di_birthtime;
514		spcl.c_birthtimensec = dp->dp2.di_birthnsec;
515		spcl.c_rdev = dp->dp2.di_rdev;
516		spcl.c_file_flags = dp->dp2.di_flags;
517		spcl.c_uid = dp->dp2.di_uid;
518		spcl.c_gid = dp->dp2.di_gid;
519	}
520	spcl.c_type = iswap32(TS_INODE);
521	spcl.c_count = 0;
522	switch (DIP(dp, mode) & IFMT) {
523
524	case 0:
525		/*
526		 * Freed inode.
527		 */
528		return;
529
530	case IFLNK:
531		/*
532		 * Check for short symbolic link.
533		 */
534		if (DIP(dp, size) > 0 &&
535#ifdef FS_44INODEFMT
536		    (DIP(dp, size) < ufsib->ufs_maxsymlinklen ||
537		     (ufsib->ufs_maxsymlinklen == 0 && DIP(dp, blocks) == 0))
538#else
539		    DIP(dp, blocks) == 0
540#endif
541			) {
542			spcl.c_addr[0] = 1;
543			spcl.c_count = iswap32(1);
544			writeheader(ino);
545			if (is_ufs2)
546				shortlink = dp->dp2.di_db;
547			else
548				shortlink = dp->dp1.di_db;
549			memmove(buf, shortlink, DIP(dp, size));
550			buf[DIP(dp, size)] = '\0';
551			writerec(buf, 0);
552			return;
553		}
554		/* fall through */
555
556	case IFDIR:
557	case IFREG:
558		if (DIP(dp, size) > 0)
559			break;
560		/* fall through */
561
562	case IFIFO:
563	case IFSOCK:
564	case IFCHR:
565	case IFBLK:
566		writeheader(ino);
567		return;
568
569	default:
570		msg("Warning: undefined file type 0%o\n", DIP(dp, mode) & IFMT);
571		return;
572	}
573	if (DIP(dp, size) > NDADDR * ufsib->ufs_bsize)
574		cnt = NDADDR * ufsib->ufs_frag;
575	else
576		cnt = howmany(DIP(dp, size), ufsib->ufs_fsize);
577	if (is_ufs2)
578		blksout64(&dp->dp2.di_db[0], cnt, ino);
579	else
580		blksout32(&dp->dp1.di_db[0], cnt, ino);
581
582	if ((size = DIP(dp, size) - NDADDR * ufsib->ufs_bsize) <= 0)
583		return;
584	for (ind_level = 0; ind_level < NIADDR; ind_level++) {
585		if (is_ufs2)
586			blk = iswap64(dp->dp2.di_ib[ind_level]);
587		else
588			blk = iswap32(dp->dp1.di_ib[ind_level]);
589		dmpindir(ino, blk, ind_level, &size);
590		if (size <= 0)
591			return;
592	}
593}
594
595/*
596 * Read indirect blocks, and pass the data blocks to be dumped.
597 */
598static void
599dmpindir(ino_t ino, daddr_t blk, int ind_level, off_t *size)
600{
601	int i, cnt;
602	union {
603		int32_t i32[MAXBSIZE / sizeof (int32_t)];
604		int64_t i64[MAXBSIZE / sizeof (int64_t)];
605	} idblk;
606	daddr_t iblk;
607
608
609	if (blk != 0)
610		bread(fsatoda(ufsib, blk), (char *)&idblk,
611			(int) ufsib->ufs_bsize);
612	else
613		memset(&idblk, 0, (int)ufsib->ufs_bsize);
614	if (ind_level <= 0) {
615		if (*size < ufsib->ufs_nindir * ufsib->ufs_bsize)
616			cnt = howmany(*size, ufsib->ufs_fsize);
617		else
618			cnt = ufsib->ufs_nindir * ufsib->ufs_frag;
619		*size -= ufsib->ufs_nindir * ufsib->ufs_bsize;
620		if (is_ufs2)
621			blksout64(&idblk.i64[0], cnt, ino);
622		else
623			blksout32(&idblk.i32[0], cnt, ino);
624		return;
625	}
626	ind_level--;
627	for (i = 0; i < ufsib->ufs_nindir; i++) {
628		if (is_ufs2)
629			iblk = iswap64(idblk.i64[i]);
630		else
631			iblk = iswap32(idblk.i32[i]);
632		dmpindir(ino, iblk, ind_level, size);
633		if (*size <= 0)
634			return;
635	}
636}
637
638/*
639 * Collect up the data into tape record sized buffers and output them.
640 */
641void
642blksout32(int32_t *blkp, int frags, ino_t ino)
643{
644	int32_t *bp;
645	int i, j, count, blks, tbperdb;
646
647	blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE);
648	tbperdb = ufsib->ufs_bsize >> tp_bshift;
649	for (i = 0; i < blks; i += TP_NINDIR) {
650		if (i + TP_NINDIR > blks)
651			count = blks;
652		else
653			count = i + TP_NINDIR;
654		for (j = i; j < count; j++)
655			if (blkp[j / tbperdb] != 0)
656				spcl.c_addr[j - i] = 1;
657			else
658				spcl.c_addr[j - i] = 0;
659		spcl.c_count = iswap32(count - i);
660		writeheader(ino);
661		bp = &blkp[i / tbperdb];
662		for (j = i; j < count; j += tbperdb, bp++)
663			if (*bp != 0) {
664				if (j + tbperdb <= count)
665					dumpblock(iswap32(*bp), (int)ufsib->ufs_bsize);
666				else
667					dumpblock(iswap32(*bp), (count - j) * TP_BSIZE);
668			}
669		spcl.c_type = iswap32(TS_ADDR);
670	}
671}
672
673void
674blksout64(int64_t *blkp, int frags, ino_t ino)
675{
676	int64_t *bp;
677	int i, j, count, blks, tbperdb;
678
679	blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE);
680	tbperdb = ufsib->ufs_bsize >> tp_bshift;
681	for (i = 0; i < blks; i += TP_NINDIR) {
682		if (i + TP_NINDIR > blks)
683			count = blks;
684		else
685			count = i + TP_NINDIR;
686		for (j = i; j < count; j++)
687			if (blkp[j / tbperdb] != 0)
688				spcl.c_addr[j - i] = 1;
689			else
690				spcl.c_addr[j - i] = 0;
691		spcl.c_count = iswap32(count - i);
692		writeheader(ino);
693		bp = &blkp[i / tbperdb];
694		for (j = i; j < count; j += tbperdb, bp++)
695			if (*bp != 0) {
696				if (j + tbperdb <= count)
697					dumpblock(iswap64(*bp), (int)ufsib->ufs_bsize);
698				else
699					dumpblock(iswap64(*bp), (count - j) * TP_BSIZE);
700			}
701		spcl.c_type = iswap32(TS_ADDR);
702	}
703}
704
705/*
706 * Dump a map to the tape.
707 */
708void
709dumpmap(char *map, int type, ino_t ino)
710{
711	int i;
712	char *cp;
713
714	spcl.c_type = iswap32(type);
715	spcl.c_count = iswap32(howmany(mapsize * sizeof(char), TP_BSIZE));
716	writeheader(ino);
717	for (i = 0, cp = map; i < iswap32(spcl.c_count); i++, cp += TP_BSIZE)
718		writerec(cp, 0);
719}
720
721/*
722 * Write a header record to the dump tape.
723 */
724void
725writeheader(ino_t ino)
726{
727	int32_t sum, cnt, *lp;
728
729	spcl.c_inumber = iswap32(ino);
730	if (is_ufs2)
731		spcl.c_magic = iswap32(FS_UFS2_MAGIC);
732	else {
733		spcl.c_magic = iswap32(NFS_MAGIC);
734		spcl.c_old_date = iswap32(iswap64(spcl.c_date));
735		spcl.c_old_ddate = iswap32(iswap64(spcl.c_ddate));
736		spcl.c_old_tapea = iswap32(iswap64(spcl.c_tapea));
737		spcl.c_old_firstrec = iswap32(iswap64(spcl.c_firstrec));
738	}
739	spcl.c_checksum = 0;
740	lp = (int32_t *)&spcl;
741	sum = 0;
742	cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
743	while (--cnt >= 0) {
744		sum += iswap32(*lp++);
745		sum += iswap32(*lp++);
746		sum += iswap32(*lp++);
747		sum += iswap32(*lp++);
748	}
749	spcl.c_checksum = iswap32(CHECKSUM - sum);
750	writerec((char *)&spcl, 1);
751}
752