1 /* $OpenBSD: traverse.c,v 1.24 2009/10/27 23:59:32 deraadt Exp $ */ 2 /* $NetBSD: traverse.c,v 1.17 1997/06/05 11:13:27 lukem Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1988, 1991, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/time.h> 35 #include <sys/stat.h> 36 #include <ufs/ffs/fs.h> 37 #include <ufs/ufs/dir.h> 38 #include <ufs/ufs/dinode.h> 39 40 #include <protocols/dumprestore.h> 41 42 #include <ctype.h> 43 #include <errno.h> 44 #include <fts.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include "dump.h" 51 52 union dinode { 53 struct ufs1_dinode dp1; 54 struct ufs2_dinode dp2; 55 }; 56 #define DIP(dp, field) \ 57 ((sblock->fs_magic == FS_UFS1_MAGIC) ? \ 58 (dp)->dp1.field : (dp)->dp2.field) 59 60 #define HASDUMPEDFILE 0x1 61 #define HASSUBDIRS 0x2 62 63 static int dirindir(ino_t ino, daddr64_t blkno, int level, off_t *size); 64 static void dmpindir(ino_t ino, daddr64_t blk, int level, off_t *size); 65 static int searchdir(ino_t ino, daddr64_t blkno, long size, off_t filesize); 66 67 /* 68 * This is an estimation of the number of TP_BSIZE blocks in the file. 69 * It estimates the number of blocks in files with holes by assuming 70 * that all of the blocks accounted for by di_blocks are data blocks 71 * (when some of the blocks are usually used for indirect pointers); 72 * hence the estimate may be high. 73 */ 74 off_t 75 blockest(union dinode *dp) 76 { 77 off_t blkest, sizeest; 78 79 /* 80 * dp->di_size is the size of the file in bytes. 81 * dp->di_blocks stores the number of sectors actually in the file. 82 * If there are more sectors than the size would indicate, this just 83 * means that there are indirect blocks in the file or unused 84 * sectors in the last file block; we can safely ignore these 85 * (blkest = sizeest below). 86 * If the file is bigger than the number of sectors would indicate, 87 * then the file has holes in it. In this case we must use the 88 * block count to estimate the number of data blocks used, but 89 * we use the actual size for estimating the number of indirect 90 * dump blocks (sizeest vs. blkest in the indirect block 91 * calculation). 92 */ 93 blkest = howmany(dbtob((off_t)DIP(dp, di_blocks)), TP_BSIZE); 94 sizeest = howmany((off_t)DIP(dp, di_size), TP_BSIZE); 95 if (blkest > sizeest) 96 blkest = sizeest; 97 if (DIP(dp, di_size) > sblock->fs_bsize * NDADDR) { 98 /* calculate the number of indirect blocks on the dump tape */ 99 blkest += 100 howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, 101 TP_NINDIR); 102 } 103 return (blkest + 1); 104 } 105 106 /* Auxiliary macro to pick up files changed since previous dump. */ 107 #define CHANGEDSINCE(dp, t) \ 108 (DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t)) 109 110 /* The WANTTODUMP macro decides whether a file should be dumped. */ 111 #ifdef UF_NODUMP 112 #define WANTTODUMP(dp) \ 113 (CHANGEDSINCE(dp, spcl.c_ddate) && \ 114 (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP)) 115 #else 116 #define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate) 117 #endif 118 119 /* 120 * Determine if given inode should be dumped 121 */ 122 void 123 mapfileino(ino_t ino, off_t *tapesize, int *dirskipped) 124 { 125 int mode; 126 union dinode *dp; 127 128 dp = getino(ino, &mode); 129 if (mode == 0) 130 return; 131 SETINO(ino, usedinomap); 132 if (mode == IFDIR) 133 SETINO(ino, dumpdirmap); 134 if (WANTTODUMP(dp)) { 135 SETINO(ino, dumpinomap); 136 if (mode != IFREG && mode != IFDIR && mode != IFLNK) 137 *tapesize += 1; 138 else 139 *tapesize += blockest(dp); 140 return; 141 } 142 if (mode == IFDIR) 143 *dirskipped = 1; 144 } 145 146 void 147 fs_mapinodes(ino_t maxino, off_t *tapesize, int *anydirskipped) 148 { 149 int i, cg, inosused; 150 struct cg *cgp; 151 ino_t ino; 152 char *cp; 153 154 if ((cgp = malloc(sblock->fs_cgsize)) == NULL) 155 quit("fs_mapinodes: cannot allocate memory.\n"); 156 157 for (cg = 0; cg < sblock->fs_ncg; cg++) { 158 ino = cg * sblock->fs_ipg; 159 bread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp, 160 sblock->fs_cgsize); 161 if (sblock->fs_magic == FS_UFS2_MAGIC) 162 inosused = cgp->cg_initediblk; 163 else 164 inosused = sblock->fs_ipg; 165 /* 166 * If we are using soft updates, then we can trust the 167 * cylinder group inode allocation maps to tell us which 168 * inodes are allocated. We will scan the used inode map 169 * to find the inodes that are really in use, and then 170 * read only those inodes in from disk. 171 */ 172 if (sblock->fs_flags & FS_DOSOFTDEP) { 173 if (!cg_chkmagic(cgp)) 174 quit("mapfiles: cg %d: bad magic number\n", cg); 175 cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT]; 176 for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { 177 if (*cp == 0) 178 continue; 179 for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { 180 if (*cp & i) 181 break; 182 inosused--; 183 } 184 break; 185 } 186 if (inosused <= 0) 187 continue; 188 } 189 for (i = 0; i < inosused; i++, ino++) { 190 if (ino < ROOTINO) 191 continue; 192 mapfileino(ino, tapesize, anydirskipped); 193 } 194 } 195 196 free(cgp); 197 } 198 199 /* 200 * Dump pass 1. 201 * 202 * Walk the inode list for a filesystem to find all allocated inodes 203 * that have been modified since the previous dump time. Also, find all 204 * the directories in the filesystem. 205 */ 206 int 207 mapfiles(ino_t maxino, off_t *tapesize, char *disk, char * const *dirv) 208 { 209 int anydirskipped = 0; 210 211 if (dirv != NULL) { 212 char curdir[MAXPATHLEN]; 213 FTS *dirh; 214 FTSENT *entry; 215 int d; 216 217 if (getcwd(curdir, sizeof(curdir)) == NULL) { 218 msg("Can't determine cwd: %s\n", strerror(errno)); 219 dumpabort(0); 220 } 221 if ((dirh = fts_open(dirv, FTS_PHYSICAL|FTS_SEEDOT|FTS_XDEV, 222 NULL)) == NULL) { 223 msg("fts_open failed: %s\n", strerror(errno)); 224 dumpabort(0); 225 } 226 while ((entry = fts_read(dirh)) != NULL) { 227 switch (entry->fts_info) { 228 case FTS_DNR: /* an error */ 229 case FTS_ERR: 230 case FTS_NS: 231 msg("Can't fts_read %s: %s\n", entry->fts_path, 232 strerror(errno)); 233 /* FALLTHROUGH */ 234 case FTS_DP: /* already seen dir */ 235 continue; 236 } 237 mapfileino(entry->fts_statp->st_ino, tapesize, 238 &anydirskipped); 239 } 240 if (errno) { 241 msg("fts_read failed: %s\n", strerror(errno)); 242 dumpabort(0); 243 } 244 (void)fts_close(dirh); 245 246 /* 247 * Add any parent directories 248 */ 249 for (d = 0 ; dirv[d] != NULL ; d++) { 250 char path[MAXPATHLEN]; 251 252 if (dirv[d][0] != '/') 253 (void)snprintf(path, sizeof(path), "%s/%s", 254 curdir, dirv[d]); 255 else 256 (void)snprintf(path, sizeof(path), "%s", 257 dirv[d]); 258 while (strcmp(path, disk) != 0) { 259 char *p; 260 struct stat sb; 261 262 if (*path == '\0') 263 break; 264 if ((p = strrchr(path, '/')) == NULL) 265 break; 266 if (p == path) 267 break; 268 *p = '\0'; 269 if (stat(path, &sb) == -1) { 270 msg("Can't stat %s: %s\n", path, 271 strerror(errno)); 272 break; 273 } 274 mapfileino(sb.st_ino, tapesize, &anydirskipped); 275 } 276 } 277 278 /* 279 * Ensure that the root inode actually appears in the 280 * file list for a subdir 281 */ 282 mapfileino(ROOTINO, tapesize, &anydirskipped); 283 } else { 284 fs_mapinodes(maxino, tapesize, &anydirskipped); 285 } 286 /* 287 * Restore gets very upset if the root is not dumped, 288 * so ensure that it always is dumped. 289 */ 290 SETINO(ROOTINO, dumpinomap); 291 return (anydirskipped); 292 } 293 294 /* 295 * Dump pass 2. 296 * 297 * Scan each directory on the filesystem to see if it has any modified 298 * files in it. If it does, and has not already been added to the dump 299 * list (because it was itself modified), then add it. If a directory 300 * has not been modified itself, contains no modified files and has no 301 * subdirectories, then it can be deleted from the dump list and from 302 * the list of directories. By deleting it from the list of directories, 303 * its parent may now qualify for the same treatment on this or a later 304 * pass using this algorithm. 305 */ 306 int 307 mapdirs(ino_t maxino, off_t *tapesize) 308 { 309 union dinode *dp; 310 int i, isdir; 311 char *map; 312 ino_t ino; 313 union dinode di; 314 off_t filesize; 315 int ret, change = 0; 316 317 isdir = 0; /* XXX just to get gcc to shut up */ 318 for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { 319 if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 320 isdir = *map++; 321 else 322 isdir >>= 1; 323 if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap)) 324 continue; 325 dp = getino(ino, &i); 326 /* 327 * inode buf may change in searchdir(). 328 */ 329 if (sblock->fs_magic == FS_UFS1_MAGIC) 330 di.dp1 = dp->dp1; 331 else 332 di.dp2 = dp->dp2; 333 filesize = (off_t)DIP(dp, di_size); 334 for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { 335 if (DIP(&di, di_db[i]) != 0) 336 ret |= searchdir(ino, DIP(&di, di_db[i]), 337 sblksize(sblock, DIP(dp, di_size), i), 338 filesize); 339 if (ret & HASDUMPEDFILE) 340 filesize = 0; 341 else 342 filesize -= sblock->fs_bsize; 343 } 344 for (i = 0; filesize > 0 && i < NIADDR; i++) { 345 if (DIP(&di, di_ib[i]) == 0) 346 continue; 347 ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize); 348 } 349 if (ret & HASDUMPEDFILE) { 350 SETINO(ino, dumpinomap); 351 *tapesize += blockest(dp); 352 change = 1; 353 continue; 354 } 355 if ((ret & HASSUBDIRS) == 0) { 356 if (!TSTINO(ino, dumpinomap)) { 357 CLRINO(ino, dumpdirmap); 358 change = 1; 359 } 360 } 361 } 362 return (change); 363 } 364 365 /* 366 * Read indirect blocks, and pass the data blocks to be searched 367 * as directories. Quit as soon as any entry is found that will 368 * require the directory to be dumped. 369 */ 370 static int 371 dirindir(ino_t ino, daddr64_t blkno, int ind_level, off_t *filesize) 372 { 373 int ret = 0; 374 int i; 375 char idblk[MAXBSIZE]; 376 377 bread(fsbtodb(sblock, blkno), idblk, (int)sblock->fs_bsize); 378 if (ind_level <= 0) { 379 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 380 if (sblock->fs_magic == FS_UFS1_MAGIC) 381 blkno = ((int32_t *)idblk)[i]; 382 else 383 blkno = ((int64_t *)idblk)[i]; 384 if (blkno != 0) 385 ret |= searchdir(ino, blkno, sblock->fs_bsize, 386 *filesize); 387 if (ret & HASDUMPEDFILE) 388 *filesize = 0; 389 else 390 *filesize -= sblock->fs_bsize; 391 } 392 return (ret); 393 } 394 ind_level--; 395 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 396 if (sblock->fs_magic == FS_UFS1_MAGIC) 397 blkno = ((int32_t *)idblk)[i]; 398 else 399 blkno = ((int64_t *)idblk)[i]; 400 if (blkno != 0) 401 ret |= dirindir(ino, blkno, ind_level, filesize); 402 } 403 return (ret); 404 } 405 406 /* 407 * Scan a disk block containing directory information looking to see if 408 * any of the entries are on the dump list and to see if the directory 409 * contains any subdirectories. 410 */ 411 static int 412 searchdir(ino_t ino, daddr64_t blkno, long size, off_t filesize) 413 { 414 struct direct *dp; 415 long loc; 416 static caddr_t dblk; 417 int ret = 0; 418 419 if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL) 420 quit("searchdir: cannot allocate indirect memory.\n"); 421 bread(fsbtodb(sblock, 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 %d\n", ino); 428 break; 429 } 430 loc += 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 if (TSTINO(dp->d_ino, dumpinomap)) { 440 ret |= HASDUMPEDFILE; 441 if (ret & HASSUBDIRS) 442 break; 443 } 444 if (TSTINO(dp->d_ino, dumpdirmap)) { 445 ret |= HASSUBDIRS; 446 if (ret & HASDUMPEDFILE) 447 break; 448 } 449 } 450 return (ret); 451 } 452 453 /* 454 * Dump passes 3 and 4. 455 * 456 * Dump the contents of an inode to tape. 457 */ 458 void 459 dumpino(union dinode *dp, ino_t ino) 460 { 461 int ind_level, cnt; 462 off_t size; 463 char buf[TP_BSIZE]; 464 465 if (newtape) { 466 newtape = 0; 467 dumpmap(dumpinomap, TS_BITS, ino); 468 } 469 CLRINO(ino, dumpinomap); 470 if (sblock->fs_magic == FS_UFS1_MAGIC) { 471 spcl.c_mode = dp->dp1.di_mode; 472 spcl.c_size = dp->dp1.di_size; 473 spcl.c_old_atime = (time_t)dp->dp1.di_atime; 474 spcl.c_atime = dp->dp1.di_atime; 475 spcl.c_atimensec = dp->dp1.di_atimensec; 476 spcl.c_old_mtime = (time_t)dp->dp1.di_mtime; 477 spcl.c_mtime = dp->dp1.di_mtime; 478 spcl.c_mtimensec = dp->dp1.di_mtimensec; 479 spcl.c_birthtime = 0; 480 spcl.c_birthtimensec = 0; 481 spcl.c_rdev = dp->dp1.di_rdev; 482 spcl.c_file_flags = dp->dp1.di_flags; 483 spcl.c_uid = dp->dp1.di_uid; 484 spcl.c_gid = dp->dp1.di_gid; 485 } else { 486 spcl.c_mode = dp->dp2.di_mode; 487 spcl.c_size = dp->dp2.di_size; 488 spcl.c_atime = dp->dp2.di_atime; 489 spcl.c_atimensec = dp->dp2.di_atimensec; 490 spcl.c_mtime = dp->dp2.di_mtime; 491 spcl.c_mtimensec = dp->dp2.di_mtimensec; 492 spcl.c_birthtime = dp->dp2.di_birthtime; 493 spcl.c_birthtimensec = dp->dp2.di_birthnsec; 494 spcl.c_rdev = dp->dp2.di_rdev; 495 spcl.c_file_flags = dp->dp2.di_flags; 496 spcl.c_uid = dp->dp2.di_uid; 497 spcl.c_gid = dp->dp2.di_gid; 498 } 499 spcl.c_type = TS_INODE; 500 spcl.c_count = 0; 501 switch (DIP(dp, di_mode) & S_IFMT) { 502 503 case 0: 504 /* 505 * Freed inode. 506 */ 507 return; 508 509 case IFLNK: 510 /* 511 * Check for short symbolic link. 512 */ 513 if (DIP(dp, di_size) > 0 && 514 #ifdef FS_44INODEFMT 515 (DIP(dp, di_size) < sblock->fs_maxsymlinklen || 516 (sblock->fs_maxsymlinklen == 0 && DIP(dp, di_blocks) == 0))) { 517 #else 518 DIP(dp, di_blocks) == 0) { 519 #endif 520 void *shortlink; 521 522 spcl.c_addr[0] = 1; 523 spcl.c_count = 1; 524 writeheader(ino); 525 if (sblock->fs_magic == FS_UFS1_MAGIC) 526 shortlink = dp->dp1.di_shortlink; 527 else 528 shortlink = dp->dp2.di_shortlink; 529 memcpy(buf, shortlink, DIP(dp, di_size)); 530 buf[DIP(dp, di_size)] = '\0'; 531 writerec(buf, 0); 532 return; 533 } 534 /* FALLTHROUGH */ 535 536 case IFDIR: 537 case IFREG: 538 if (DIP(dp, di_size) > 0) 539 break; 540 /* FALLTHROUGH */ 541 542 case IFIFO: 543 case IFSOCK: 544 case IFCHR: 545 case IFBLK: 546 writeheader(ino); 547 return; 548 549 default: 550 msg("Warning: undefined file type 0%o\n", 551 DIP(dp, di_mode) & IFMT); 552 return; 553 } 554 if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) 555 cnt = NDADDR * sblock->fs_frag; 556 else 557 cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); 558 if (sblock->fs_magic == FS_UFS1_MAGIC) 559 ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); 560 else 561 ufs2_blksout(&dp->dp2.di_db[0], cnt, ino); 562 if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0) 563 return; 564 for (ind_level = 0; ind_level < NIADDR; ind_level++) { 565 dmpindir(ino, DIP(dp, di_ib[ind_level]), ind_level, &size); 566 if (size <= 0) 567 return; 568 } 569 } 570 571 /* 572 * Read indirect blocks, and pass the data blocks to be dumped. 573 */ 574 static void 575 dmpindir(ino_t ino, daddr64_t blk, int ind_level, off_t *size) 576 { 577 int i, cnt; 578 char idblk[MAXBSIZE]; 579 580 if (blk != 0) 581 bread(fsbtodb(sblock, blk), idblk, (int) sblock->fs_bsize); 582 else 583 memset(idblk, 0, (int)sblock->fs_bsize); 584 if (ind_level <= 0) { 585 if (*size < NINDIR(sblock) * sblock->fs_bsize) 586 cnt = howmany(*size, sblock->fs_fsize); 587 else 588 cnt = NINDIR(sblock) * sblock->fs_frag; 589 *size -= NINDIR(sblock) * sblock->fs_bsize; 590 if (sblock->fs_magic == FS_UFS1_MAGIC) 591 ufs1_blksout((int32_t *)idblk, cnt, ino); 592 else 593 ufs2_blksout((int64_t *)idblk, cnt, ino); 594 return; 595 } 596 ind_level--; 597 for (i = 0; i < NINDIR(sblock); i++) { 598 if (sblock->fs_magic == FS_UFS1_MAGIC) 599 dmpindir(ino, ((int32_t *)idblk)[i], ind_level, 600 size); 601 else 602 dmpindir(ino, ((int64_t *)idblk)[i], ind_level, 603 size); 604 if (*size <= 0) 605 return; 606 } 607 } 608 609 /* 610 * Collect up the data into tape record sized buffers and output them. 611 */ 612 void 613 ufs1_blksout(int32_t *blkp, int frags, ino_t ino) 614 { 615 int32_t *bp; 616 int i, j, count, blks, tbperdb; 617 618 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 619 tbperdb = sblock->fs_bsize >> tp_bshift; 620 for (i = 0; i < blks; i += TP_NINDIR) { 621 if (i + TP_NINDIR > blks) 622 count = blks; 623 else 624 count = i + TP_NINDIR; 625 for (j = i; j < count; j++) 626 if (blkp[j / tbperdb] != 0) 627 spcl.c_addr[j - i] = 1; 628 else 629 spcl.c_addr[j - i] = 0; 630 spcl.c_count = count - i; 631 writeheader(ino); 632 bp = &blkp[i / tbperdb]; 633 for (j = i; j < count; j += tbperdb, bp++) 634 if (*bp != 0) { 635 if (j + tbperdb <= count) 636 dumpblock(*bp, (int)sblock->fs_bsize); 637 else 638 dumpblock(*bp, (count - j) * TP_BSIZE); 639 } 640 spcl.c_type = TS_ADDR; 641 } 642 } 643 644 /* 645 * Collect up the data into tape record sized buffers and output them. 646 */ 647 void 648 ufs2_blksout(daddr64_t *blkp, int frags, ino_t ino) 649 { 650 daddr64_t *bp; 651 int i, j, count, blks, tbperdb; 652 653 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 654 tbperdb = sblock->fs_bsize >> tp_bshift; 655 for (i = 0; i < blks; i += TP_NINDIR) { 656 if (i + TP_NINDIR > blks) 657 count = blks; 658 else 659 count = i + TP_NINDIR; 660 for (j = i; j < count; j++) 661 if (blkp[j / tbperdb] != 0) 662 spcl.c_addr[j - i] = 1; 663 else 664 spcl.c_addr[j - i] = 0; 665 spcl.c_count = count - i; 666 writeheader(ino); 667 bp = &blkp[i / tbperdb]; 668 for (j = i; j < count; j += tbperdb, bp++) 669 if (*bp != 0) { 670 if (j + tbperdb <= count) 671 dumpblock(*bp, (int)sblock->fs_bsize); 672 else 673 dumpblock(*bp, (count - j) * TP_BSIZE); 674 } 675 spcl.c_type = TS_ADDR; 676 } 677 } 678 679 /* 680 * Dump a map to the tape. 681 */ 682 void 683 dumpmap(map, type, ino) 684 char *map; 685 int type; 686 ino_t ino; 687 { 688 int i; 689 char *cp; 690 691 spcl.c_type = type; 692 spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); 693 writeheader(ino); 694 for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) 695 writerec(cp, 0); 696 } 697 698 /* 699 * Write a header record to the dump tape. 700 */ 701 void 702 writeheader(ino) 703 ino_t ino; 704 { 705 int32_t sum, cnt, *lp; 706 707 spcl.c_inumber = ino; 708 if (sblock->fs_magic == FS_UFS2_MAGIC) { 709 spcl.c_magic = FS_UFS2_MAGIC; 710 } else { 711 spcl.c_magic = NFS_MAGIC; 712 spcl.c_old_date = (int32_t)spcl.c_date; 713 spcl.c_old_ddate = (int32_t)spcl.c_ddate; 714 spcl.c_old_tapea = (int32_t)spcl.c_tapea; 715 spcl.c_old_firstrec = (int32_t)spcl.c_firstrec; 716 } 717 spcl.c_checksum = 0; 718 lp = (int32_t *)&spcl; 719 sum = 0; 720 cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t)); 721 while (--cnt >= 0) { 722 sum += *lp++; 723 sum += *lp++; 724 sum += *lp++; 725 sum += *lp++; 726 } 727 spcl.c_checksum = CHECKSUM - sum; 728 writerec((char *)&spcl, 1); 729 } 730 731 union dinode * 732 getino(ino_t inum, int *modep) 733 { 734 static ino_t minino, maxino; 735 static void *inoblock; 736 struct ufs1_dinode *dp1; 737 struct ufs2_dinode *dp2; 738 739 if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL) 740 quit("cannot allocate inode memory.\n"); 741 curino = inum; 742 if (inum >= minino && inum < maxino) 743 goto gotit; 744 bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock, 745 (int)sblock->fs_bsize); 746 minino = inum - (inum % INOPB(sblock)); 747 maxino = minino + INOPB(sblock); 748 gotit: 749 if (sblock->fs_magic == FS_UFS1_MAGIC) { 750 dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino]; 751 *modep = (dp1->di_mode & IFMT); 752 return ((union dinode *)dp1); 753 } 754 dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino]; 755 *modep = (dp2->di_mode & IFMT); 756 return ((union dinode *)dp2); 757 } 758 759 /* 760 * Read a chunk of data from the disk. 761 * Try to recover from hard errors by reading in sector sized pieces. 762 * Error recovery is attempted at most BREADEMAX times before seeking 763 * consent from the operator to continue. 764 */ 765 int breaderrors = 0; 766 #define BREADEMAX 32 767 768 void 769 bread(daddr64_t blkno, char *buf, int size) 770 { 771 int cnt, i; 772 773 loop: 774 if (lseek(diskfd, ((off_t)blkno << dev_bshift), SEEK_SET) < 0) 775 msg("bread: lseek fails\n"); 776 if ((cnt = read(diskfd, buf, size)) == size) 777 return; 778 if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_ffs1_size)) { 779 /* 780 * Trying to read the final fragment. 781 * 782 * NB - dump only works in TP_BSIZE blocks, hence 783 * rounds `dev_bsize' fragments up to TP_BSIZE pieces. 784 * It should be smarter about not actually trying to 785 * read more than it can get, but for the time being 786 * we punt and scale back the read only when it gets 787 * us into trouble. (mkm 9/25/83) 788 */ 789 size -= dev_bsize; 790 goto loop; 791 } 792 if (cnt == -1) 793 msg("read error from %s: %s: [block %lld]: count=%d\n", 794 disk, strerror(errno), blkno, size); 795 else 796 msg("short read error from %s: [block %lld]: count=%d, got=%d\n", 797 disk, blkno, size, cnt); 798 if (++breaderrors > BREADEMAX) { 799 msg("More than %d block read errors from %s\n", 800 BREADEMAX, disk); 801 broadcast("DUMP IS AILING!\n"); 802 msg("This is an unrecoverable error.\n"); 803 if (!query("Do you want to attempt to continue?")){ 804 dumpabort(0); 805 /*NOTREACHED*/ 806 } else 807 breaderrors = 0; 808 } 809 /* 810 * Zero buffer, then try to read each sector of buffer separately. 811 */ 812 memset(buf, 0, size); 813 for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { 814 if (lseek(diskfd, ((off_t)blkno << dev_bshift), SEEK_SET) < 0) 815 msg("bread: lseek2 fails!\n"); 816 if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize) 817 continue; 818 if (cnt == -1) { 819 msg("read error from %s: %s: [sector %lld]: count=%ld\n", 820 disk, strerror(errno), blkno, dev_bsize); 821 continue; 822 } 823 msg("short read error from %s: [sector %lld]: count=%ld, got=%d\n", 824 disk, blkno, dev_bsize, cnt); 825 } 826 } 827