1 /* $OpenBSD: traverse.c,v 1.36 2014/07/10 09:33:43 krw 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 <sys/disklabel.h> 37 #include <ufs/ffs/fs.h> 38 #include <ufs/ufs/dir.h> 39 #include <ufs/ufs/dinode.h> 40 41 #include <protocols/dumprestore.h> 42 43 #include <ctype.h> 44 #include <errno.h> 45 #include <fts.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include "dump.h" 52 53 extern struct disklabel lab; 54 55 union dinode { 56 struct ufs1_dinode dp1; 57 struct ufs2_dinode dp2; 58 }; 59 #define DIP(dp, field) \ 60 ((sblock->fs_magic == FS_UFS1_MAGIC) ? \ 61 (dp)->dp1.field : (dp)->dp2.field) 62 63 #define HASDUMPEDFILE 0x1 64 #define HASSUBDIRS 0x2 65 66 static int dirindir(ino_t, daddr_t, int, off_t *, int64_t *, int); 67 static void dmpindir(ino_t, daddr_t, int, off_t *); 68 static int searchdir(ino_t, daddr_t, long, off_t, int64_t *, int); 69 void fs_mapinodes(ino_t maxino, off_t *tapesize, int *anydirskipped); 70 71 /* 72 * This is an estimation of the number of TP_BSIZE blocks in the file. 73 * It estimates the number of blocks in files with holes by assuming 74 * that all of the blocks accounted for by di_blocks are data blocks 75 * (when some of the blocks are usually used for indirect pointers); 76 * hence the estimate may be high. 77 */ 78 int64_t 79 blockest(union dinode *dp) 80 { 81 int64_t blkest, sizeest; 82 83 /* 84 * dp->di_size is the size of the file in bytes. 85 * dp->di_blocks stores the number of sectors actually in the file. 86 * If there are more sectors than the size would indicate, this just 87 * means that there are indirect blocks in the file or unused 88 * sectors in the last file block; we can safely ignore these 89 * (blkest = sizeest below). 90 * If the file is bigger than the number of sectors would indicate, 91 * then the file has holes in it. In this case we must use the 92 * block count to estimate the number of data blocks used, but 93 * we use the actual size for estimating the number of indirect 94 * dump blocks (sizeest vs. blkest in the indirect block 95 * calculation). 96 */ 97 blkest = howmany(dbtob((int64_t)DIP(dp, di_blocks)), TP_BSIZE); 98 sizeest = howmany((int64_t)DIP(dp, di_size), TP_BSIZE); 99 if (blkest > sizeest) 100 blkest = sizeest; 101 if (DIP(dp, di_size) > sblock->fs_bsize * NDADDR) { 102 /* calculate the number of indirect blocks on the dump tape */ 103 blkest += 104 howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, 105 TP_NINDIR); 106 } 107 return (blkest + 1); 108 } 109 110 /* true if "nodump" flag has no effect here, i.e. dumping allowed */ 111 #define CHECKNODUMP(dp) \ 112 (nonodump || (DIP((dp), di_flags) & UF_NODUMP) != UF_NODUMP) 113 114 /* 115 * Determine if given inode should be dumped 116 */ 117 void 118 mapfileino(ino_t ino, int64_t *tapesize, int *dirskipped) 119 { 120 int mode; 121 union dinode *dp; 122 123 dp = getino(ino, &mode); 124 if (mode == 0) 125 return; 126 SETINO(ino, usedinomap); 127 if (mode == IFDIR) 128 SETINO(ino, dumpdirmap); 129 if (CHECKNODUMP(dp) && 130 (DIP(dp, di_mtime) >= spcl.c_ddate || 131 DIP(dp, di_ctime) >= spcl.c_ddate)) { 132 SETINO(ino, dumpinomap); 133 if (mode != IFREG && mode != IFDIR && mode != IFLNK) 134 *tapesize += 1; 135 else 136 *tapesize += blockest(dp); 137 return; 138 } 139 if (mode == IFDIR) { 140 if (!CHECKNODUMP(dp)) 141 CLRINO(ino, usedinomap); 142 *dirskipped = 1; 143 } 144 } 145 146 void 147 fs_mapinodes(ino_t maxino, int64_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, int64_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, int64_t *tapesize) 308 { 309 union dinode *dp; 310 int i, isdir, nodump; 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 /* 324 * If a directory has been removed from usedinomap, it 325 * either has the nodump flag set, or has inherited 326 * it. Although a directory can't be in dumpinomap if 327 * it isn't in usedinomap, we have to go through it to 328 * propagate the nodump flag. 329 */ 330 nodump = !nonodump && !TSTINO(ino, usedinomap); 331 if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump)) 332 continue; 333 dp = getino(ino, &i); 334 /* 335 * inode buf may change in searchdir(). 336 */ 337 if (sblock->fs_magic == FS_UFS1_MAGIC) 338 di.dp1 = dp->dp1; 339 else 340 di.dp2 = dp->dp2; 341 filesize = (off_t)DIP(dp, di_size); 342 for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { 343 if (DIP(&di, di_db[i]) != 0) 344 ret |= searchdir(ino, DIP(&di, di_db[i]), 345 sblksize(sblock, DIP(dp, di_size), i), 346 filesize, tapesize, nodump); 347 if (ret & HASDUMPEDFILE) 348 filesize = 0; 349 else 350 filesize -= sblock->fs_bsize; 351 } 352 for (i = 0; filesize > 0 && i < NIADDR; i++) { 353 if (DIP(&di, di_ib[i]) == 0) 354 continue; 355 ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize, 356 tapesize, nodump); 357 } 358 if (ret & HASDUMPEDFILE) { 359 SETINO(ino, dumpinomap); 360 *tapesize += blockest(dp); 361 change = 1; 362 continue; 363 } 364 if (nodump) { 365 if (ret & HASSUBDIRS) 366 change = 1; /* subdirs inherit nodump */ 367 CLRINO(ino, dumpdirmap); 368 } else if ((ret & HASSUBDIRS) == 0) { 369 if (!TSTINO(ino, dumpinomap)) { 370 CLRINO(ino, dumpdirmap); 371 change = 1; 372 } 373 } 374 } 375 return (change); 376 } 377 378 /* 379 * Read indirect blocks, and pass the data blocks to be searched 380 * as directories. Quit as soon as any entry is found that will 381 * require the directory to be dumped. 382 */ 383 static int 384 dirindir(ino_t ino, daddr_t blkno, int ind_level, off_t *filesize, 385 int64_t *tapesize, int nodump) 386 { 387 int ret = 0; 388 int i; 389 char idblk[MAXBSIZE]; 390 391 bread(fsbtodb(sblock, blkno), idblk, (int)sblock->fs_bsize); 392 if (ind_level <= 0) { 393 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 394 if (sblock->fs_magic == FS_UFS1_MAGIC) 395 blkno = ((int32_t *)idblk)[i]; 396 else 397 blkno = ((int64_t *)idblk)[i]; 398 if (blkno != 0) 399 ret |= searchdir(ino, blkno, sblock->fs_bsize, 400 *filesize, tapesize, nodump); 401 if (ret & HASDUMPEDFILE) 402 *filesize = 0; 403 else 404 *filesize -= sblock->fs_bsize; 405 } 406 return (ret); 407 } 408 ind_level--; 409 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 410 if (sblock->fs_magic == FS_UFS1_MAGIC) 411 blkno = ((int32_t *)idblk)[i]; 412 else 413 blkno = ((int64_t *)idblk)[i]; 414 if (blkno != 0) 415 ret |= dirindir(ino, blkno, ind_level, filesize, 416 tapesize, nodump); 417 } 418 return (ret); 419 } 420 421 /* 422 * Scan a disk block containing directory information looking to see if 423 * any of the entries are on the dump list and to see if the directory 424 * contains any subdirectories. 425 */ 426 static int 427 searchdir(ino_t ino, daddr_t blkno, long size, off_t filesize, 428 int64_t *tapesize, int nodump) 429 { 430 struct direct *dp; 431 union dinode *ip; 432 long loc; 433 static caddr_t dblk; 434 int mode, ret = 0; 435 436 if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL) 437 quit("searchdir: cannot allocate indirect memory.\n"); 438 bread(fsbtodb(sblock, blkno), dblk, (int)size); 439 if (filesize < size) 440 size = filesize; 441 for (loc = 0; loc < size; ) { 442 dp = (struct direct *)(dblk + loc); 443 if (dp->d_reclen == 0) { 444 msg("corrupted directory, inumber %llu\n", 445 (unsigned long long)ino); 446 break; 447 } 448 loc += dp->d_reclen; 449 if (dp->d_ino == 0) 450 continue; 451 if (dp->d_name[0] == '.') { 452 if (dp->d_name[1] == '\0') 453 continue; 454 if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') 455 continue; 456 } 457 if (nodump) { 458 ip = getino(dp->d_ino, &mode); 459 if (TSTINO(dp->d_ino, dumpinomap)) { 460 CLRINO(dp->d_ino, dumpinomap); 461 *tapesize -= blockest(ip); 462 } 463 /* 464 * Add back to dumpdirmap and remove from usedinomap 465 * to propagate nodump. 466 */ 467 if (mode == IFDIR) { 468 SETINO(dp->d_ino, dumpdirmap); 469 CLRINO(dp->d_ino, usedinomap); 470 ret |= HASSUBDIRS; 471 } 472 } else { 473 if (TSTINO(dp->d_ino, dumpinomap)) { 474 ret |= HASDUMPEDFILE; 475 if (ret & HASSUBDIRS) 476 break; 477 } 478 if (TSTINO(dp->d_ino, dumpdirmap)) { 479 ret |= HASSUBDIRS; 480 if (ret & HASDUMPEDFILE) 481 break; 482 } 483 } 484 } 485 return (ret); 486 } 487 488 /* 489 * Dump passes 3 and 4. 490 * 491 * Dump the contents of an inode to tape. 492 */ 493 void 494 dumpino(union dinode *dp, ino_t ino) 495 { 496 int ind_level, cnt; 497 off_t size; 498 char buf[TP_BSIZE]; 499 500 if (newtape) { 501 newtape = 0; 502 dumpmap(dumpinomap, TS_BITS, ino); 503 } 504 CLRINO(ino, dumpinomap); 505 if (sblock->fs_magic == FS_UFS1_MAGIC) { 506 spcl.c_mode = dp->dp1.di_mode; 507 spcl.c_size = dp->dp1.di_size; 508 spcl.c_old_atime = (time_t)dp->dp1.di_atime; 509 spcl.c_atime = dp->dp1.di_atime; 510 spcl.c_atimensec = dp->dp1.di_atimensec; 511 spcl.c_old_mtime = (time_t)dp->dp1.di_mtime; 512 spcl.c_mtime = dp->dp1.di_mtime; 513 spcl.c_mtimensec = dp->dp1.di_mtimensec; 514 spcl.c_birthtime = 0; 515 spcl.c_birthtimensec = 0; 516 spcl.c_rdev = dp->dp1.di_rdev; 517 spcl.c_file_flags = dp->dp1.di_flags; 518 spcl.c_uid = dp->dp1.di_uid; 519 spcl.c_gid = dp->dp1.di_gid; 520 } else { 521 spcl.c_mode = dp->dp2.di_mode; 522 spcl.c_size = dp->dp2.di_size; 523 spcl.c_atime = dp->dp2.di_atime; 524 spcl.c_atimensec = dp->dp2.di_atimensec; 525 spcl.c_mtime = dp->dp2.di_mtime; 526 spcl.c_mtimensec = dp->dp2.di_mtimensec; 527 spcl.c_birthtime = dp->dp2.di_birthtime; 528 spcl.c_birthtimensec = dp->dp2.di_birthnsec; 529 spcl.c_rdev = dp->dp2.di_rdev; 530 spcl.c_file_flags = dp->dp2.di_flags; 531 spcl.c_uid = dp->dp2.di_uid; 532 spcl.c_gid = dp->dp2.di_gid; 533 } 534 spcl.c_type = TS_INODE; 535 spcl.c_count = 0; 536 switch (DIP(dp, di_mode) & S_IFMT) { 537 538 case 0: 539 /* 540 * Freed inode. 541 */ 542 return; 543 544 case IFLNK: 545 /* 546 * Check for short symbolic link. 547 */ 548 if (DIP(dp, di_size) > 0 && 549 #ifdef FS_44INODEFMT 550 (DIP(dp, di_size) < sblock->fs_maxsymlinklen || 551 (sblock->fs_maxsymlinklen == 0 && 552 DIP(dp, di_blocks) == 0))) { 553 #else 554 DIP(dp, di_blocks) == 0) { 555 #endif 556 void *shortlink; 557 558 spcl.c_addr[0] = 1; 559 spcl.c_count = 1; 560 writeheader(ino); 561 if (sblock->fs_magic == FS_UFS1_MAGIC) 562 shortlink = dp->dp1.di_shortlink; 563 else 564 shortlink = dp->dp2.di_shortlink; 565 memcpy(buf, shortlink, DIP(dp, di_size)); 566 buf[DIP(dp, di_size)] = '\0'; 567 writerec(buf, 0); 568 return; 569 } 570 /* FALLTHROUGH */ 571 572 case IFDIR: 573 case IFREG: 574 if (DIP(dp, di_size) > 0) 575 break; 576 /* FALLTHROUGH */ 577 578 case IFIFO: 579 case IFSOCK: 580 case IFCHR: 581 case IFBLK: 582 writeheader(ino); 583 return; 584 585 default: 586 msg("Warning: undefined file type 0%o\n", 587 DIP(dp, di_mode) & IFMT); 588 return; 589 } 590 if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) 591 cnt = NDADDR * sblock->fs_frag; 592 else 593 cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); 594 if (sblock->fs_magic == FS_UFS1_MAGIC) 595 ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); 596 else 597 ufs2_blksout(&dp->dp2.di_db[0], cnt, ino); 598 if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0) 599 return; 600 for (ind_level = 0; ind_level < NIADDR; ind_level++) { 601 dmpindir(ino, DIP(dp, di_ib[ind_level]), ind_level, &size); 602 if (size <= 0) 603 return; 604 } 605 } 606 607 /* 608 * Read indirect blocks, and pass the data blocks to be dumped. 609 */ 610 static void 611 dmpindir(ino_t ino, daddr_t blk, int ind_level, off_t *size) 612 { 613 int i, cnt; 614 char idblk[MAXBSIZE]; 615 616 if (blk != 0) 617 bread(fsbtodb(sblock, blk), idblk, (int) sblock->fs_bsize); 618 else 619 memset(idblk, 0, (int)sblock->fs_bsize); 620 if (ind_level <= 0) { 621 if (*size < NINDIR(sblock) * sblock->fs_bsize) 622 cnt = howmany(*size, sblock->fs_fsize); 623 else 624 cnt = NINDIR(sblock) * sblock->fs_frag; 625 *size -= NINDIR(sblock) * sblock->fs_bsize; 626 if (sblock->fs_magic == FS_UFS1_MAGIC) 627 ufs1_blksout((int32_t *)idblk, cnt, ino); 628 else 629 ufs2_blksout((int64_t *)idblk, cnt, ino); 630 return; 631 } 632 ind_level--; 633 for (i = 0; i < NINDIR(sblock); i++) { 634 if (sblock->fs_magic == FS_UFS1_MAGIC) 635 dmpindir(ino, ((int32_t *)idblk)[i], ind_level, 636 size); 637 else 638 dmpindir(ino, ((int64_t *)idblk)[i], ind_level, 639 size); 640 if (*size <= 0) 641 return; 642 } 643 } 644 645 /* 646 * Collect up the data into tape record sized buffers and output them. 647 */ 648 void 649 ufs1_blksout(int32_t *blkp, int frags, ino_t ino) 650 { 651 int32_t *bp; 652 int i, j, count, blks, tbperdb; 653 654 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 655 tbperdb = sblock->fs_bsize >> tp_bshift; 656 for (i = 0; i < blks; i += TP_NINDIR) { 657 if (i + TP_NINDIR > blks) 658 count = blks; 659 else 660 count = i + TP_NINDIR; 661 for (j = i; j < count; j++) 662 if (blkp[j / tbperdb] != 0) 663 spcl.c_addr[j - i] = 1; 664 else 665 spcl.c_addr[j - i] = 0; 666 spcl.c_count = count - i; 667 writeheader(ino); 668 bp = &blkp[i / tbperdb]; 669 for (j = i; j < count; j += tbperdb, bp++) 670 if (*bp != 0) { 671 if (j + tbperdb <= count) 672 dumpblock(*bp, (int)sblock->fs_bsize); 673 else 674 dumpblock(*bp, (count - j) * TP_BSIZE); 675 } 676 spcl.c_type = TS_ADDR; 677 } 678 } 679 680 /* 681 * Collect up the data into tape record sized buffers and output them. 682 */ 683 void 684 ufs2_blksout(daddr_t *blkp, int frags, ino_t ino) 685 { 686 daddr_t *bp; 687 int i, j, count, blks, tbperdb; 688 689 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 690 tbperdb = sblock->fs_bsize >> tp_bshift; 691 for (i = 0; i < blks; i += TP_NINDIR) { 692 if (i + TP_NINDIR > blks) 693 count = blks; 694 else 695 count = i + TP_NINDIR; 696 for (j = i; j < count; j++) 697 if (blkp[j / tbperdb] != 0) 698 spcl.c_addr[j - i] = 1; 699 else 700 spcl.c_addr[j - i] = 0; 701 spcl.c_count = count - i; 702 writeheader(ino); 703 bp = &blkp[i / tbperdb]; 704 for (j = i; j < count; j += tbperdb, bp++) 705 if (*bp != 0) { 706 if (j + tbperdb <= count) 707 dumpblock(*bp, (int)sblock->fs_bsize); 708 else 709 dumpblock(*bp, (count - j) * TP_BSIZE); 710 } 711 spcl.c_type = TS_ADDR; 712 } 713 } 714 715 /* 716 * Dump a map to the tape. 717 */ 718 void 719 dumpmap(map, type, ino) 720 char *map; 721 int type; 722 ino_t ino; 723 { 724 int i; 725 char *cp; 726 727 spcl.c_type = type; 728 spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); 729 writeheader(ino); 730 for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) 731 writerec(cp, 0); 732 } 733 734 /* 735 * Write a header record to the dump tape. 736 */ 737 void 738 writeheader(ino) 739 ino_t ino; 740 { 741 int32_t sum, cnt, *lp; 742 743 spcl.c_inumber = ino; 744 if (sblock->fs_magic == FS_UFS2_MAGIC) { 745 spcl.c_magic = FS_UFS2_MAGIC; 746 } else { 747 spcl.c_magic = NFS_MAGIC; 748 spcl.c_old_date = (int32_t)spcl.c_date; 749 spcl.c_old_ddate = (int32_t)spcl.c_ddate; 750 spcl.c_old_tapea = (int32_t)spcl.c_tapea; 751 spcl.c_old_firstrec = (int32_t)spcl.c_firstrec; 752 } 753 spcl.c_checksum = 0; 754 lp = (int32_t *)&spcl; 755 sum = 0; 756 cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t)); 757 while (--cnt >= 0) { 758 sum += *lp++; 759 sum += *lp++; 760 sum += *lp++; 761 sum += *lp++; 762 } 763 spcl.c_checksum = CHECKSUM - sum; 764 writerec((char *)&spcl, 1); 765 } 766 767 union dinode * 768 getino(ino_t inum, int *modep) 769 { 770 static ino_t minino, maxino; 771 static void *inoblock; 772 struct ufs1_dinode *dp1; 773 struct ufs2_dinode *dp2; 774 775 if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL) 776 quit("cannot allocate inode memory.\n"); 777 curino = inum; 778 if (inum >= minino && inum < maxino) 779 goto gotit; 780 bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock, 781 (int)sblock->fs_bsize); 782 minino = inum - (inum % INOPB(sblock)); 783 maxino = minino + INOPB(sblock); 784 gotit: 785 if (sblock->fs_magic == FS_UFS1_MAGIC) { 786 dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino]; 787 *modep = (dp1->di_mode & IFMT); 788 return ((union dinode *)dp1); 789 } 790 dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino]; 791 *modep = (dp2->di_mode & IFMT); 792 return ((union dinode *)dp2); 793 } 794 795 /* 796 * Read a chunk of data from the disk. 797 * Try to recover from hard errors by reading in sector sized pieces. 798 * Error recovery is attempted at most BREADEMAX times before seeking 799 * consent from the operator to continue. 800 */ 801 int breaderrors = 0; 802 #define BREADEMAX 32 803 804 void 805 bread(daddr_t blkno, char *buf, int size) 806 { 807 static char *mybuf = NULL; 808 char *mybufp, *bufp, *np; 809 static size_t mybufsz = 0; 810 off_t offset; 811 int cnt, i; 812 u_int64_t secno, seccount; 813 u_int32_t secoff, secsize = lab.d_secsize; 814 815 /* 816 * We must read an integral number of sectors large enough to contain 817 * all the requested data. The read must begin at a sector. 818 */ 819 if (DL_BLKOFFSET(&lab, blkno) == 0 && size % secsize == 0) { 820 secno = DL_BLKTOSEC(&lab, blkno); 821 secoff = 0; 822 seccount = size / secsize; 823 bufp = buf; 824 } else { 825 secno = DL_BLKTOSEC(&lab, blkno); 826 secoff = DL_BLKOFFSET(&lab, blkno); 827 seccount = DL_BLKTOSEC(&lab, (size + secoff) / DEV_BSIZE); 828 if (seccount * secsize < (size + secoff)) 829 seccount++; 830 if (mybufsz < seccount * secsize) { 831 np = reallocarray(mybuf, seccount, secsize); 832 if (np == NULL) { 833 msg("No memory to read %llu %u-byte sectors", 834 seccount, secsize); 835 dumpabort(0); 836 } 837 mybufsz = seccount * secsize; 838 mybuf = np; 839 } 840 bufp = mybuf; 841 } 842 843 offset = secno * secsize; 844 845 loop: 846 if ((cnt = pread(diskfd, bufp, seccount * secsize, offset)) == 847 seccount * secsize) 848 goto done; 849 if (blkno + (size / DEV_BSIZE) > 850 fsbtodb(sblock, sblock->fs_ffs1_size)) { 851 /* 852 * Trying to read the final fragment. 853 * 854 * NB - dump only works in TP_BSIZE blocks, hence 855 * rounds `DEV_BSIZE' fragments up to TP_BSIZE pieces. 856 * It should be smarter about not actually trying to 857 * read more than it can get, but for the time being 858 * we punt and scale back the read only when it gets 859 * us into trouble. (mkm 9/25/83) 860 */ 861 size -= secsize; 862 seccount--; 863 goto loop; 864 } 865 if (cnt == -1) 866 msg("read error from %s: %s: [block %lld]: count=%d\n", 867 disk, strerror(errno), (long long)blkno, size); 868 else 869 msg("short read error from %s: [block %lld]: count=%d, " 870 "got=%d\n", disk, (long long)blkno, size, cnt); 871 if (++breaderrors > BREADEMAX) { 872 msg("More than %d block read errors from %s\n", 873 BREADEMAX, disk); 874 broadcast("DUMP IS AILING!\n"); 875 msg("This is an unrecoverable error.\n"); 876 if (!query("Do you want to attempt to continue?")){ 877 dumpabort(0); 878 /*NOTREACHED*/ 879 } else 880 breaderrors = 0; 881 } 882 /* 883 * Zero buffer, then try to read each sector of buffer separately. 884 */ 885 if (bufp == mybuf) 886 memset(bufp, 0, mybufsz); 887 else 888 memset(bufp, 0, size); 889 for (i = 0, mybufp = bufp; i < size; i += secsize, mybufp += secsize) { 890 if ((cnt = pread(diskfd, mybufp, secsize, offset + i)) == 891 secsize) 892 continue; 893 if (cnt == -1) { 894 msg("read error from %s: %s: [block %lld]: " 895 "count=%u\n", disk, strerror(errno), 896 (long long)(offset + i) / DEV_BSIZE, secsize); 897 continue; 898 } 899 msg("short read error from %s: [block %lld]: count=%u, " 900 "got=%d\n", disk, (long long)(offset + i) / DEV_BSIZE, 901 secsize, cnt); 902 } 903 904 done: 905 /* If necessary, copy out data that was read. */ 906 if (bufp == mybuf) 907 memcpy(buf, bufp + secoff, size); 908 } 909