xref: /original-bsd/sbin/dump/traverse.c (revision 95a66346)
1 /*-
2  * Copyright (c) 1980, 1988, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)traverse.c	5.11 (Berkeley) 03/07/91";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <ufs/dir.h>
14 #include <ufs/dinode.h>
15 #include <ufs/fs.h>
16 #include <protocols/dumprestore.h>
17 #ifdef __STDC__
18 #include <unistd.h>
19 #include <string.h>
20 #endif
21 #include "dump.h"
22 
23 void	dmpindir();
24 #define	HASDUMPEDFILE	0x1
25 #define	HASSUBDIRS	0x2
26 
27 /*
28  * This is an estimation of the number of TP_BSIZE blocks in the file.
29  * It estimates the number of blocks in files with holes by assuming
30  * that all of the blocks accounted for by di_blocks are data blocks
31  * (when some of the blocks are usually used for indirect pointers);
32  * hence the estimate may be high.
33  */
34 long
35 blockest(dp)
36 	register struct dinode *dp;
37 {
38 	long blkest, sizeest;
39 
40 	/*
41 	 * dp->di_size is the size of the file in bytes.
42 	 * dp->di_blocks stores the number of sectors actually in the file.
43 	 * If there are more sectors than the size would indicate, this just
44 	 *	means that there are indirect blocks in the file or unused
45 	 *	sectors in the last file block; we can safely ignore these
46 	 *	(blkest = sizeest below).
47 	 * If the file is bigger than the number of sectors would indicate,
48 	 *	then the file has holes in it.	In this case we must use the
49 	 *	block count to estimate the number of data blocks used, but
50 	 *	we use the actual size for estimating the number of indirect
51 	 *	dump blocks (sizeest vs. blkest in the indirect block
52 	 *	calculation).
53 	 */
54 	blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE);
55 	sizeest = howmany(dp->di_size, TP_BSIZE);
56 	if (blkest > sizeest)
57 		blkest = sizeest;
58 	if (dp->di_size > sblock->fs_bsize * NDADDR) {
59 		/* calculate the number of indirect blocks on the dump tape */
60 		blkest +=
61 			howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
62 			TP_NINDIR);
63 	}
64 	return (blkest + 1);
65 }
66 
67 /*
68  * Dump pass 1.
69  *
70  * Walk the inode list for a filesystem to find all allocated inodes
71  * that have been modified since the previous dump time. Also, find all
72  * the directories in the filesystem.
73  */
74 mapfiles(maxino, tapesize)
75 	ino_t maxino;
76 	long *tapesize;
77 {
78 	register int mode;
79 	register ino_t ino;
80 	register struct dinode *dp;
81 	int anydirskipped = 0;
82 
83 	for (ino = 0; ino < maxino; ino++) {
84 		dp = getino(ino);
85 		if ((mode = (dp->di_mode & IFMT)) == 0)
86 			continue;
87 		SETINO(ino, usedinomap);
88 		if (mode == IFDIR)
89 			SETINO(ino, dumpdirmap);
90 		if (dp->di_mtime >= spcl.c_ddate ||
91 		    dp->di_ctime >= spcl.c_ddate) {
92 			SETINO(ino, dumpinomap);
93 			if (mode != IFREG && mode != IFDIR && mode != IFLNK) {
94 				*tapesize += 1;
95 				continue;
96 			}
97 			*tapesize += blockest(dp);
98 			continue;
99 		}
100 		if (mode == IFDIR)
101 			anydirskipped = 1;
102 	}
103 	/*
104 	 * Restore gets very upset if the root is not dumped,
105 	 * so ensure that it always is dumped.
106 	 */
107 	SETINO(ROOTINO, usedinomap);
108 	return (anydirskipped);
109 }
110 
111 /*
112  * Dump pass 2.
113  *
114  * Scan each directory on the filesystem to see if it has any modified
115  * files in it. If it does, and has not already been added to the dump
116  * list (because it was itself modified), then add it. If a directory
117  * has not been modified itself, contains no modified files and has no
118  * subdirectories, then it can be deleted from the dump list and from
119  * the list of directories. By deleting it from the list of directories,
120  * its parent may now qualify for the same treatment on this or a later
121  * pass using this algorithm.
122  */
123 mapdirs(maxino, tapesize)
124 	ino_t maxino;
125 	long *tapesize;
126 {
127 	register struct	dinode *dp;
128 	register int i, dirty;
129 	register char *map;
130 	register ino_t ino;
131 	long filesize, blkcnt = 0;
132 	int ret, change = 0;
133 
134 	for (map = dumpdirmap, ino = 0; ino < maxino; ) {
135 		if ((ino % NBBY) == 0)
136 			dirty = *map++;
137 		else
138 			dirty >>= 1;
139 		ino++;
140 		if ((dirty & 1) == 0 || TSTINO(ino, dumpinomap))
141 			continue;
142 		dp = getino(ino);
143 		filesize = dp->di_size;
144 		for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
145 			if (dp->di_db[i] != 0)
146 				ret |= searchdir(ino, dp->di_db[i],
147 					dblksize(sblock, dp, i), filesize);
148 			if (ret & HASDUMPEDFILE)
149 				filesize = 0;
150 			else
151 				filesize -= sblock->fs_bsize;
152 		}
153 		for (i = 0; filesize > 0 && i < NIADDR; i++) {
154 			if (dp->di_ib[i] == 0)
155 				continue;
156 			ret |= dirindir(ino, dp->di_ib[i], i, &filesize);
157 		}
158 		if (ret & HASDUMPEDFILE) {
159 			if (!TSTINO(ino, dumpinomap)) {
160 				SETINO(ino, dumpinomap);
161 				*tapesize += blockest(dp);
162 			}
163 			change = 1;
164 			continue;
165 		}
166 		if ((ret & HASSUBDIRS) == 0) {
167 			if (!TSTINO(ino, dumpinomap)) {
168 				CLRINO(ino, dumpdirmap);
169 				change = 1;
170 			}
171 		}
172 	}
173 	return (change);
174 }
175 
176 /*
177  * Read indirect blocks, and pass the data blocks to be searched
178  * as directories. Quit as soon as any entry is found that will
179  * require the directory to be dumped.
180  */
181 dirindir(ino, blkno, level, filesize)
182 	ino_t ino;
183 	daddr_t blkno;
184 	int level, *filesize;
185 {
186 	int ret = 0;
187 	register int i;
188 	daddr_t	idblk[MAXNINDIR];
189 
190 	bread(fsbtodb(sblock, blkno), (char *)idblk, sblock->fs_bsize);
191 	if (level <= 0) {
192 		for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
193 			blkno = idblk[i];
194 			if (blkno != 0)
195 				ret |= searchdir(ino, blkno, sblock->fs_bsize,
196 					filesize);
197 			if (ret & HASDUMPEDFILE)
198 				*filesize = 0;
199 			else
200 				*filesize -= sblock->fs_bsize;
201 		}
202 		return (ret);
203 	}
204 	level--;
205 	for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
206 		blkno = idblk[i];
207 		if (blkno != 0)
208 			ret |= dirindir(ino, blkno, level, filesize);
209 	}
210 	return (ret);
211 }
212 
213 /*
214  * Scan a disk block containing directory information looking to see if
215  * any of the entries are on the dump list and to see if the directory
216  * contains any subdirectories.
217  */
218 searchdir(ino, blkno, size, filesize)
219 	ino_t ino;
220 	daddr_t blkno;
221 	register int size;
222 	int filesize;
223 {
224 	register struct direct *dp;
225 	register long loc;
226 	char dblk[MAXBSIZE];
227 
228 	bread(fsbtodb(sblock, blkno), dblk, size);
229 	if (filesize < size)
230 		size = filesize;
231 	for (loc = 0; loc < size; ) {
232 		dp = (struct direct *)(dblk + loc);
233 		if (dp->d_reclen == 0) {
234 			msg("corrupted directory, inumber %d\n", ino);
235 			break;
236 		}
237 		loc += dp->d_reclen;
238 		if (dp->d_ino == 0)
239 			continue;
240 		if (dp->d_name[0] == '.') {
241 			if (dp->d_name[1] == '\0')
242 				continue;
243 			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
244 				continue;
245 		}
246 		if (TSTINO(dp->d_ino, dumpinomap))
247 			return (HASDUMPEDFILE);
248 		if (TSTINO(dp->d_ino, dumpdirmap))
249 			return (HASSUBDIRS);
250 	}
251 	return (0);
252 }
253 
254 /*
255  * Dump passes 3 and 4.
256  *
257  * Dump the contents of an inode to tape.
258  */
259 void
260 dumpino(dp, ino)
261 	register struct dinode *dp;
262 	ino_t ino;
263 {
264 	int mode, level, cnt;
265 	long size;
266 
267 	if (newtape) {
268 		newtape = 0;
269 		dumpmap(dumpinomap, TS_BITS, ino);
270 	}
271 	CLRINO(ino, dumpinomap);
272 	spcl.c_dinode = *dp;
273 	spcl.c_type = TS_INODE;
274 	spcl.c_count = 0;
275 	/*
276 	 * Check for freed inode.
277 	 */
278 	if ((mode = (dp->di_mode & IFMT)) == 0)
279 		return;
280 	if ((mode != IFDIR && mode != IFREG && mode != IFLNK) ||
281 	    dp->di_size == 0) {
282 		writeheader(ino);
283 		return;
284 	}
285 	if (dp->di_size > NDADDR * sblock->fs_bsize)
286 		cnt = NDADDR * sblock->fs_frag;
287 	else
288 		cnt = howmany(dp->di_size, sblock->fs_fsize);
289 	blksout(&dp->di_db[0], cnt, ino);
290 	if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0)
291 		return;
292 	for (level = 0; level < NIADDR; level++) {
293 		dmpindir(ino, dp->di_ib[level], level, &size);
294 		if (size <= 0)
295 			return;
296 	}
297 }
298 
299 /*
300  * Read indirect blocks, and pass the data blocks to be dumped.
301  */
302 void
303 dmpindir(ino, blk, level, size)
304 	ino_t ino;
305 	daddr_t blk;
306 	int level;
307 	long *size;
308 {
309 	int i, cnt;
310 	daddr_t idblk[MAXNINDIR];
311 
312 	if (blk != 0)
313 		bread(fsbtodb(sblock, blk), (char *)idblk, sblock->fs_bsize);
314 	else
315 		bzero((char *)idblk, sblock->fs_bsize);
316 	if (level <= 0) {
317 		if (*size < NINDIR(sblock) * sblock->fs_bsize)
318 			cnt = howmany(*size, sblock->fs_fsize);
319 		else
320 			cnt = NINDIR(sblock) * sblock->fs_frag;
321 		*size -= NINDIR(sblock) * sblock->fs_bsize;
322 		blksout(&idblk[0], cnt, ino);
323 		return;
324 	}
325 	level--;
326 	for (i = 0; i < NINDIR(sblock); i++) {
327 		dmpindir(ino, idblk[i], level, size);
328 		if (*size <= 0)
329 			return;
330 	}
331 }
332 
333 /*
334  * Collect up the data into tape record sized buffers and output them.
335  */
336 void
337 blksout(blkp, frags, ino)
338 	daddr_t *blkp;
339 	int frags;
340 	ino_t ino;
341 {
342 	register daddr_t *bp;
343 	int i, j, count, blks, tbperdb;
344 
345 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
346 	tbperdb = sblock->fs_bsize >> tp_bshift;
347 	for (i = 0; i < blks; i += TP_NINDIR) {
348 		if (i + TP_NINDIR > blks)
349 			count = blks;
350 		else
351 			count = i + TP_NINDIR;
352 		for (j = i; j < count; j++)
353 			if (blkp[j / tbperdb] != 0)
354 				spcl.c_addr[j - i] = 1;
355 			else
356 				spcl.c_addr[j - i] = 0;
357 		spcl.c_count = count - i;
358 		writeheader(ino);
359 		bp = &blkp[i / tbperdb];
360 		for (j = i; j < count; j += tbperdb, bp++)
361 			if (*bp != 0)
362 				if (j + tbperdb <= count)
363 					dumpblock(*bp, sblock->fs_bsize);
364 				else
365 					dumpblock(*bp, (count - j) * TP_BSIZE);
366 		spcl.c_type = TS_ADDR;
367 	}
368 }
369 
370 /*
371  * Dump a map to the tape.
372  */
373 void
374 dumpmap(map, type, ino)
375 	char *map;
376 	int type;
377 	ino_t ino;
378 {
379 	register int i;
380 	char *cp;
381 
382 	spcl.c_type = type;
383 	spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
384 	writeheader(ino);
385 	for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
386 		writerec(cp);
387 }
388 
389 /*
390  * Write a header record to the dump tape.
391  */
392 void
393 writeheader(ino)
394 	ino_t ino;
395 {
396 	register long sum, cnt, *lp;
397 
398 	spcl.c_inumber = ino;
399 	spcl.c_magic = NFS_MAGIC;
400 	spcl.c_checksum = 0;
401 	lp = (long *)&spcl;
402 	sum = 0;
403 	cnt = sizeof(union u_spcl) / (4 * sizeof(long));
404 	while (--cnt >= 0) {
405 		sum += *lp++;
406 		sum += *lp++;
407 		sum += *lp++;
408 		sum += *lp++;
409 	}
410 	spcl.c_checksum = CHECKSUM - sum;
411 	writerec((char *)&spcl);
412 }
413 
414 struct dinode *
415 getino(inum)
416 	ino_t inum;
417 {
418 	static daddr_t minino, maxino;
419 	static struct dinode inoblock[MAXINOPB];
420 
421 	curino = inum;
422 	if (inum >= minino && inum < maxino)
423 		return (&inoblock[inum - minino]);
424 	bread(fsbtodb(sblock, itod(sblock, inum)), inoblock, sblock->fs_bsize);
425 	minino = inum - (inum % INOPB(sblock));
426 	maxino = minino + INOPB(sblock);
427 	return (&inoblock[inum - minino]);
428 }
429 
430 /*
431  * Read a chunk of data from the disk.
432  * Try to recover from hard errors by reading in sector sized pieces.
433  * Error recovery is attempted at most BREADEMAX times before seeking
434  * consent from the operator to continue.
435  */
436 int	breaderrors = 0;
437 #define	BREADEMAX 32
438 
439 void
440 bread(blkno, buf, size)
441 	daddr_t blkno;
442 	char *buf;
443 	int size;
444 {
445 	int cnt, i;
446 	extern int errno;
447 
448 loop:
449 	if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0)
450 		msg("bread: lseek fails\n");
451 	if ((cnt = read(diskfd, buf, size)) == size)
452 		return;
453 	if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
454 		/*
455 		 * Trying to read the final fragment.
456 		 *
457 		 * NB - dump only works in TP_BSIZE blocks, hence
458 		 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
459 		 * It should be smarter about not actually trying to
460 		 * read more than it can get, but for the time being
461 		 * we punt and scale back the read only when it gets
462 		 * us into trouble. (mkm 9/25/83)
463 		 */
464 		size -= dev_bsize;
465 		goto loop;
466 	}
467 	msg("read error from %s [block %d]: count=%d, got=%d, errno=%d (%s)\n",
468 		disk, blkno, size, cnt, errno, strerror(errno));
469 	if (++breaderrors > BREADEMAX) {
470 		msg("More than %d block read errors from %d\n",
471 			BREADEMAX, disk);
472 		broadcast("DUMP IS AILING!\n");
473 		msg("This is an unrecoverable error.\n");
474 		if (!query("Do you want to attempt to continue?")){
475 			dumpabort();
476 			/*NOTREACHED*/
477 		} else
478 			breaderrors = 0;
479 	}
480 	/*
481 	 * Zero buffer, then try to read each sector of buffer separately.
482 	 */
483 	bzero(buf, size);
484 	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
485 		if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0)
486 			msg("bread: lseek2 fails!\n");
487 		if ((cnt = read(diskfd, buf, dev_bsize)) != dev_bsize)
488 			msg("    read error from %s [sector %d, errno %d]\n",
489 			    disk, blkno, errno);
490 	}
491 }
492