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