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