1 /* $OpenBSD: traverse.c,v 1.39 2018/04/26 17:40:48 guenther 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> /* MAXBSIZE DEV_BSIZE dbtob */ 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 #include <limits.h> 51 52 #include "dump.h" 53 54 extern struct disklabel lab; 55 56 union dinode { 57 struct ufs1_dinode dp1; 58 struct ufs2_dinode dp2; 59 }; 60 #define DIP(dp, field) \ 61 ((sblock->fs_magic == FS_UFS1_MAGIC) ? \ 62 (dp)->dp1.field : (dp)->dp2.field) 63 64 #define HASDUMPEDFILE 0x1 65 #define HASSUBDIRS 0x2 66 67 static int dirindir(ino_t, daddr_t, int, off_t *, int64_t *, int); 68 static void dmpindir(ino_t, daddr_t, int, off_t *); 69 static int searchdir(ino_t, daddr_t, long, off_t, int64_t *, int); 70 void fs_mapinodes(ino_t maxino, off_t *tapesize, int *anydirskipped); 71 72 /* 73 * This is an estimation of the number of TP_BSIZE blocks in the file. 74 * It estimates the number of blocks in files with holes by assuming 75 * that all of the blocks accounted for by di_blocks are data blocks 76 * (when some of the blocks are usually used for indirect pointers); 77 * hence the estimate may be high. 78 */ 79 int64_t 80 blockest(union dinode *dp) 81 { 82 int64_t blkest, sizeest; 83 84 /* 85 * dp->di_size is the size of the file in bytes. 86 * dp->di_blocks stores the number of sectors actually in the file. 87 * If there are more sectors than the size would indicate, this just 88 * means that there are indirect blocks in the file or unused 89 * sectors in the last file block; we can safely ignore these 90 * (blkest = sizeest below). 91 * If the file is bigger than the number of sectors would indicate, 92 * then the file has holes in it. In this case we must use the 93 * block count to estimate the number of data blocks used, but 94 * we use the actual size for estimating the number of indirect 95 * dump blocks (sizeest vs. blkest in the indirect block 96 * calculation). 97 */ 98 blkest = howmany(dbtob((int64_t)DIP(dp, di_blocks)), TP_BSIZE); 99 sizeest = howmany((int64_t)DIP(dp, di_size), TP_BSIZE); 100 if (blkest > sizeest) 101 blkest = sizeest; 102 if (DIP(dp, di_size) > sblock->fs_bsize * NDADDR) { 103 /* calculate the number of indirect blocks on the dump tape */ 104 blkest += 105 howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, 106 TP_NINDIR); 107 } 108 return (blkest + 1); 109 } 110 111 /* true if "nodump" flag has no effect here, i.e. dumping allowed */ 112 #define CHECKNODUMP(dp) \ 113 (nonodump || (DIP((dp), di_flags) & UF_NODUMP) != UF_NODUMP) 114 115 /* 116 * Determine if given inode should be dumped 117 */ 118 void 119 mapfileino(ino_t ino, int64_t *tapesize, int *dirskipped) 120 { 121 int mode; 122 union dinode *dp; 123 124 dp = getino(ino, &mode); 125 if (mode == 0) 126 return; 127 SETINO(ino, usedinomap); 128 if (mode == IFDIR) 129 SETINO(ino, dumpdirmap); 130 if (CHECKNODUMP(dp) && 131 (DIP(dp, di_mtime) >= spcl.c_ddate || 132 DIP(dp, di_ctime) >= spcl.c_ddate)) { 133 SETINO(ino, dumpinomap); 134 if (mode != IFREG && mode != IFDIR && mode != IFLNK) 135 *tapesize += 1; 136 else 137 *tapesize += blockest(dp); 138 return; 139 } 140 if (mode == IFDIR) { 141 if (!CHECKNODUMP(dp)) 142 CLRINO(ino, usedinomap); 143 *dirskipped = 1; 144 } 145 } 146 147 void 148 fs_mapinodes(ino_t maxino, int64_t *tapesize, int *anydirskipped) 149 { 150 int i, cg, inosused; 151 struct cg *cgp; 152 ino_t ino; 153 char *cp; 154 155 if ((cgp = malloc(sblock->fs_cgsize)) == NULL) 156 quit("fs_mapinodes: cannot allocate memory.\n"); 157 158 for (cg = 0; cg < sblock->fs_ncg; cg++) { 159 ino = cg * (ino_t)sblock->fs_ipg; 160 bread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp, 161 sblock->fs_cgsize); 162 if (sblock->fs_magic == FS_UFS2_MAGIC) 163 inosused = cgp->cg_initediblk; 164 else 165 inosused = sblock->fs_ipg; 166 /* 167 * If we are using soft updates, then we can trust the 168 * cylinder group inode allocation maps to tell us which 169 * inodes are allocated. We will scan the used inode map 170 * to find the inodes that are really in use, and then 171 * read only those inodes in from disk. 172 */ 173 if (sblock->fs_flags & FS_DOSOFTDEP) { 174 if (!cg_chkmagic(cgp)) 175 quit("mapfiles: cg %d: bad magic number\n", cg); 176 cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT]; 177 for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { 178 if (*cp == 0) 179 continue; 180 for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { 181 if (*cp & i) 182 break; 183 inosused--; 184 } 185 break; 186 } 187 if (inosused <= 0) 188 continue; 189 } 190 for (i = 0; i < inosused; i++, ino++) { 191 if (ino < ROOTINO) 192 continue; 193 mapfileino(ino, tapesize, anydirskipped); 194 } 195 } 196 197 free(cgp); 198 } 199 200 /* 201 * Dump pass 1. 202 * 203 * Walk the inode list for a filesystem to find all allocated inodes 204 * that have been modified since the previous dump time. Also, find all 205 * the directories in the filesystem. 206 */ 207 int 208 mapfiles(ino_t maxino, int64_t *tapesize, char *disk, char * const *dirv) 209 { 210 int anydirskipped = 0; 211 212 if (dirv != NULL) { 213 char curdir[PATH_MAX]; 214 FTS *dirh; 215 FTSENT *entry; 216 int d; 217 218 if (getcwd(curdir, sizeof(curdir)) == NULL) { 219 msg("Can't determine cwd: %s\n", strerror(errno)); 220 dumpabort(0); 221 } 222 if ((dirh = fts_open(dirv, FTS_PHYSICAL|FTS_SEEDOT|FTS_XDEV, 223 NULL)) == NULL) { 224 msg("fts_open failed: %s\n", strerror(errno)); 225 dumpabort(0); 226 } 227 while ((entry = fts_read(dirh)) != NULL) { 228 switch (entry->fts_info) { 229 case FTS_DNR: /* an error */ 230 case FTS_ERR: 231 case FTS_NS: 232 msg("Can't fts_read %s: %s\n", entry->fts_path, 233 strerror(errno)); 234 /* FALLTHROUGH */ 235 case FTS_DP: /* already seen dir */ 236 continue; 237 } 238 mapfileino(entry->fts_statp->st_ino, tapesize, 239 &anydirskipped); 240 } 241 if (errno) { 242 msg("fts_read failed: %s\n", strerror(errno)); 243 dumpabort(0); 244 } 245 (void)fts_close(dirh); 246 247 /* 248 * Add any parent directories 249 */ 250 for (d = 0 ; dirv[d] != NULL ; d++) { 251 char path[PATH_MAX]; 252 253 if (dirv[d][0] != '/') 254 (void)snprintf(path, sizeof(path), "%s/%s", 255 curdir, dirv[d]); 256 else 257 (void)snprintf(path, sizeof(path), "%s", 258 dirv[d]); 259 while (strcmp(path, disk) != 0) { 260 char *p; 261 struct stat sb; 262 263 if (*path == '\0') 264 break; 265 if ((p = strrchr(path, '/')) == NULL) 266 break; 267 if (p == path) 268 break; 269 *p = '\0'; 270 if (stat(path, &sb) == -1) { 271 msg("Can't stat %s: %s\n", path, 272 strerror(errno)); 273 break; 274 } 275 mapfileino(sb.st_ino, tapesize, &anydirskipped); 276 } 277 } 278 279 /* 280 * Ensure that the root inode actually appears in the 281 * file list for a subdir 282 */ 283 mapfileino(ROOTINO, tapesize, &anydirskipped); 284 } else { 285 fs_mapinodes(maxino, tapesize, &anydirskipped); 286 } 287 /* 288 * Restore gets very upset if the root is not dumped, 289 * so ensure that it always is dumped. 290 */ 291 SETINO(ROOTINO, dumpinomap); 292 return (anydirskipped); 293 } 294 295 /* 296 * Dump pass 2. 297 * 298 * Scan each directory on the filesystem to see if it has any modified 299 * files in it. If it does, and has not already been added to the dump 300 * list (because it was itself modified), then add it. If a directory 301 * has not been modified itself, contains no modified files and has no 302 * subdirectories, then it can be deleted from the dump list and from 303 * the list of directories. By deleting it from the list of directories, 304 * its parent may now qualify for the same treatment on this or a later 305 * pass using this algorithm. 306 */ 307 int 308 mapdirs(ino_t maxino, int64_t *tapesize) 309 { 310 union dinode *dp; 311 int i, isdir, nodump; 312 char *map; 313 ino_t ino; 314 union dinode di; 315 off_t filesize; 316 int ret, change = 0; 317 318 isdir = 0; /* XXX just to get gcc to shut up */ 319 for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { 320 if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 321 isdir = *map++; 322 else 323 isdir >>= 1; 324 /* 325 * If a directory has been removed from usedinomap, it 326 * either has the nodump flag set, or has inherited 327 * it. Although a directory can't be in dumpinomap if 328 * it isn't in usedinomap, we have to go through it to 329 * propagate the nodump flag. 330 */ 331 nodump = !nonodump && !TSTINO(ino, usedinomap); 332 if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump)) 333 continue; 334 dp = getino(ino, &i); 335 /* 336 * inode buf may change in searchdir(). 337 */ 338 if (sblock->fs_magic == FS_UFS1_MAGIC) 339 di.dp1 = dp->dp1; 340 else 341 di.dp2 = dp->dp2; 342 filesize = (off_t)DIP(dp, di_size); 343 for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { 344 if (DIP(&di, di_db[i]) != 0) 345 ret |= searchdir(ino, DIP(&di, di_db[i]), 346 sblksize(sblock, DIP(dp, di_size), i), 347 filesize, tapesize, nodump); 348 if (ret & HASDUMPEDFILE) 349 filesize = 0; 350 else 351 filesize -= sblock->fs_bsize; 352 } 353 for (i = 0; filesize > 0 && i < NIADDR; i++) { 354 if (DIP(&di, di_ib[i]) == 0) 355 continue; 356 ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize, 357 tapesize, nodump); 358 } 359 if (ret & HASDUMPEDFILE) { 360 SETINO(ino, dumpinomap); 361 *tapesize += blockest(dp); 362 change = 1; 363 continue; 364 } 365 if (nodump) { 366 if (ret & HASSUBDIRS) 367 change = 1; /* subdirs inherit nodump */ 368 CLRINO(ino, dumpdirmap); 369 } else if ((ret & HASSUBDIRS) == 0) { 370 if (!TSTINO(ino, dumpinomap)) { 371 CLRINO(ino, dumpdirmap); 372 change = 1; 373 } 374 } 375 } 376 return (change); 377 } 378 379 /* 380 * Read indirect blocks, and pass the data blocks to be searched 381 * as directories. Quit as soon as any entry is found that will 382 * require the directory to be dumped. 383 */ 384 static int 385 dirindir(ino_t ino, daddr_t blkno, int ind_level, off_t *filesize, 386 int64_t *tapesize, int nodump) 387 { 388 int ret = 0; 389 int i; 390 char idblk[MAXBSIZE]; 391 392 bread(fsbtodb(sblock, blkno), idblk, (int)sblock->fs_bsize); 393 if (ind_level <= 0) { 394 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 395 if (sblock->fs_magic == FS_UFS1_MAGIC) 396 blkno = ((int32_t *)idblk)[i]; 397 else 398 blkno = ((int64_t *)idblk)[i]; 399 if (blkno != 0) 400 ret |= searchdir(ino, blkno, sblock->fs_bsize, 401 *filesize, tapesize, nodump); 402 if (ret & HASDUMPEDFILE) 403 *filesize = 0; 404 else 405 *filesize -= sblock->fs_bsize; 406 } 407 return (ret); 408 } 409 ind_level--; 410 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 411 if (sblock->fs_magic == FS_UFS1_MAGIC) 412 blkno = ((int32_t *)idblk)[i]; 413 else 414 blkno = ((int64_t *)idblk)[i]; 415 if (blkno != 0) 416 ret |= dirindir(ino, blkno, ind_level, filesize, 417 tapesize, nodump); 418 } 419 return (ret); 420 } 421 422 /* 423 * Scan a disk block containing directory information looking to see if 424 * any of the entries are on the dump list and to see if the directory 425 * contains any subdirectories. 426 */ 427 static int 428 searchdir(ino_t ino, daddr_t blkno, long size, off_t filesize, 429 int64_t *tapesize, int nodump) 430 { 431 struct direct *dp; 432 union dinode *ip; 433 long loc; 434 static caddr_t dblk; 435 int mode, ret = 0; 436 437 if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL) 438 quit("searchdir: cannot allocate indirect memory.\n"); 439 bread(fsbtodb(sblock, blkno), dblk, (int)size); 440 if (filesize < size) 441 size = filesize; 442 for (loc = 0; loc < size; ) { 443 dp = (struct direct *)(dblk + loc); 444 if (dp->d_reclen == 0) { 445 msg("corrupted directory, inumber %llu\n", 446 (unsigned long long)ino); 447 break; 448 } 449 loc += dp->d_reclen; 450 if (dp->d_ino == 0) 451 continue; 452 if (dp->d_name[0] == '.') { 453 if (dp->d_name[1] == '\0') 454 continue; 455 if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') 456 continue; 457 } 458 if (nodump) { 459 ip = getino(dp->d_ino, &mode); 460 if (TSTINO(dp->d_ino, dumpinomap)) { 461 CLRINO(dp->d_ino, dumpinomap); 462 *tapesize -= blockest(ip); 463 } 464 /* 465 * Add back to dumpdirmap and remove from usedinomap 466 * to propagate nodump. 467 */ 468 if (mode == IFDIR) { 469 SETINO(dp->d_ino, dumpdirmap); 470 CLRINO(dp->d_ino, usedinomap); 471 ret |= HASSUBDIRS; 472 } 473 } else { 474 if (TSTINO(dp->d_ino, dumpinomap)) { 475 ret |= HASDUMPEDFILE; 476 if (ret & HASSUBDIRS) 477 break; 478 } 479 if (TSTINO(dp->d_ino, dumpdirmap)) { 480 ret |= HASSUBDIRS; 481 if (ret & HASDUMPEDFILE) 482 break; 483 } 484 } 485 } 486 return (ret); 487 } 488 489 /* 490 * Dump passes 3 and 4. 491 * 492 * Dump the contents of an inode to tape. 493 */ 494 void 495 dumpino(union dinode *dp, ino_t ino) 496 { 497 int ind_level, cnt; 498 off_t size; 499 char buf[TP_BSIZE]; 500 501 if (newtape) { 502 newtape = 0; 503 dumpmap(dumpinomap, TS_BITS, ino); 504 } 505 CLRINO(ino, dumpinomap); 506 if (sblock->fs_magic == FS_UFS1_MAGIC) { 507 spcl.c_mode = dp->dp1.di_mode; 508 spcl.c_size = dp->dp1.di_size; 509 spcl.c_old_atime = (time_t)dp->dp1.di_atime; 510 spcl.c_atime = dp->dp1.di_atime; 511 spcl.c_atimensec = dp->dp1.di_atimensec; 512 spcl.c_old_mtime = (time_t)dp->dp1.di_mtime; 513 spcl.c_mtime = dp->dp1.di_mtime; 514 spcl.c_mtimensec = dp->dp1.di_mtimensec; 515 spcl.c_birthtime = 0; 516 spcl.c_birthtimensec = 0; 517 spcl.c_rdev = dp->dp1.di_rdev; 518 spcl.c_file_flags = dp->dp1.di_flags; 519 spcl.c_uid = dp->dp1.di_uid; 520 spcl.c_gid = dp->dp1.di_gid; 521 } else { 522 spcl.c_mode = dp->dp2.di_mode; 523 spcl.c_size = dp->dp2.di_size; 524 spcl.c_atime = dp->dp2.di_atime; 525 spcl.c_atimensec = dp->dp2.di_atimensec; 526 spcl.c_mtime = dp->dp2.di_mtime; 527 spcl.c_mtimensec = dp->dp2.di_mtimensec; 528 spcl.c_birthtime = dp->dp2.di_birthtime; 529 spcl.c_birthtimensec = dp->dp2.di_birthnsec; 530 spcl.c_rdev = dp->dp2.di_rdev; 531 spcl.c_file_flags = dp->dp2.di_flags; 532 spcl.c_uid = dp->dp2.di_uid; 533 spcl.c_gid = dp->dp2.di_gid; 534 } 535 spcl.c_type = TS_INODE; 536 spcl.c_count = 0; 537 switch (DIP(dp, di_mode) & S_IFMT) { 538 539 case 0: 540 /* 541 * Freed inode. 542 */ 543 return; 544 545 case IFLNK: 546 /* 547 * Check for short symbolic link. 548 */ 549 if (DIP(dp, di_size) > 0 && 550 #ifdef FS_44INODEFMT 551 (DIP(dp, di_size) < sblock->fs_maxsymlinklen || 552 (sblock->fs_maxsymlinklen == 0 && 553 DIP(dp, di_blocks) == 0))) { 554 #else 555 DIP(dp, di_blocks) == 0) { 556 #endif 557 void *shortlink; 558 559 spcl.c_addr[0] = 1; 560 spcl.c_count = 1; 561 writeheader(ino); 562 if (sblock->fs_magic == FS_UFS1_MAGIC) 563 shortlink = dp->dp1.di_shortlink; 564 else 565 shortlink = dp->dp2.di_shortlink; 566 memcpy(buf, shortlink, DIP(dp, di_size)); 567 buf[DIP(dp, di_size)] = '\0'; 568 writerec(buf, 0); 569 return; 570 } 571 /* FALLTHROUGH */ 572 573 case IFDIR: 574 case IFREG: 575 if (DIP(dp, di_size) > 0) 576 break; 577 /* FALLTHROUGH */ 578 579 case IFIFO: 580 case IFSOCK: 581 case IFCHR: 582 case IFBLK: 583 writeheader(ino); 584 return; 585 586 default: 587 msg("Warning: undefined file type 0%o\n", 588 DIP(dp, di_mode) & IFMT); 589 return; 590 } 591 if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) 592 cnt = NDADDR * sblock->fs_frag; 593 else 594 cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); 595 if (sblock->fs_magic == FS_UFS1_MAGIC) 596 ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); 597 else 598 ufs2_blksout(&dp->dp2.di_db[0], cnt, ino); 599 if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0) 600 return; 601 for (ind_level = 0; ind_level < NIADDR; ind_level++) { 602 dmpindir(ino, DIP(dp, di_ib[ind_level]), ind_level, &size); 603 if (size <= 0) 604 return; 605 } 606 } 607 608 /* 609 * Read indirect blocks, and pass the data blocks to be dumped. 610 */ 611 static void 612 dmpindir(ino_t ino, daddr_t blk, int ind_level, off_t *size) 613 { 614 int i, cnt; 615 char idblk[MAXBSIZE]; 616 617 if (blk != 0) 618 bread(fsbtodb(sblock, blk), idblk, (int) sblock->fs_bsize); 619 else 620 memset(idblk, 0, (int)sblock->fs_bsize); 621 if (ind_level <= 0) { 622 if (*size < NINDIR(sblock) * sblock->fs_bsize) 623 cnt = howmany(*size, sblock->fs_fsize); 624 else 625 cnt = NINDIR(sblock) * sblock->fs_frag; 626 *size -= NINDIR(sblock) * sblock->fs_bsize; 627 if (sblock->fs_magic == FS_UFS1_MAGIC) 628 ufs1_blksout((int32_t *)idblk, cnt, ino); 629 else 630 ufs2_blksout((int64_t *)idblk, cnt, ino); 631 return; 632 } 633 ind_level--; 634 for (i = 0; i < NINDIR(sblock); i++) { 635 if (sblock->fs_magic == FS_UFS1_MAGIC) 636 dmpindir(ino, ((int32_t *)idblk)[i], ind_level, 637 size); 638 else 639 dmpindir(ino, ((int64_t *)idblk)[i], ind_level, 640 size); 641 if (*size <= 0) 642 return; 643 } 644 } 645 646 /* 647 * Collect up the data into tape record sized buffers and output them. 648 */ 649 void 650 ufs1_blksout(int32_t *blkp, int frags, ino_t ino) 651 { 652 int32_t *bp; 653 int i, j, count, blks, tbperdb; 654 655 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 656 tbperdb = sblock->fs_bsize >> tp_bshift; 657 for (i = 0; i < blks; i += TP_NINDIR) { 658 if (i + TP_NINDIR > blks) 659 count = blks; 660 else 661 count = i + TP_NINDIR; 662 for (j = i; j < count; j++) 663 if (blkp[j / tbperdb] != 0) 664 spcl.c_addr[j - i] = 1; 665 else 666 spcl.c_addr[j - i] = 0; 667 spcl.c_count = count - i; 668 writeheader(ino); 669 bp = &blkp[i / tbperdb]; 670 for (j = i; j < count; j += tbperdb, bp++) 671 if (*bp != 0) { 672 if (j + tbperdb <= count) 673 dumpblock(*bp, (int)sblock->fs_bsize); 674 else 675 dumpblock(*bp, (count - j) * TP_BSIZE); 676 } 677 spcl.c_type = TS_ADDR; 678 } 679 } 680 681 /* 682 * Collect up the data into tape record sized buffers and output them. 683 */ 684 void 685 ufs2_blksout(daddr_t *blkp, int frags, ino_t ino) 686 { 687 daddr_t *bp; 688 int i, j, count, blks, tbperdb; 689 690 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 691 tbperdb = sblock->fs_bsize >> tp_bshift; 692 for (i = 0; i < blks; i += TP_NINDIR) { 693 if (i + TP_NINDIR > blks) 694 count = blks; 695 else 696 count = i + TP_NINDIR; 697 for (j = i; j < count; j++) 698 if (blkp[j / tbperdb] != 0) 699 spcl.c_addr[j - i] = 1; 700 else 701 spcl.c_addr[j - i] = 0; 702 spcl.c_count = count - i; 703 writeheader(ino); 704 bp = &blkp[i / tbperdb]; 705 for (j = i; j < count; j += tbperdb, bp++) 706 if (*bp != 0) { 707 if (j + tbperdb <= count) 708 dumpblock(*bp, (int)sblock->fs_bsize); 709 else 710 dumpblock(*bp, (count - j) * TP_BSIZE); 711 } 712 spcl.c_type = TS_ADDR; 713 } 714 } 715 716 /* 717 * Dump a map to the tape. 718 */ 719 void 720 dumpmap(map, type, ino) 721 char *map; 722 int type; 723 ino_t ino; 724 { 725 int i; 726 char *cp; 727 728 spcl.c_type = type; 729 spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); 730 writeheader(ino); 731 for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) 732 writerec(cp, 0); 733 } 734 735 /* 736 * Write a header record to the dump tape. 737 */ 738 void 739 writeheader(ino) 740 ino_t ino; 741 { 742 int32_t sum, cnt, *lp; 743 744 spcl.c_inumber = ino; 745 if (sblock->fs_magic == FS_UFS2_MAGIC) { 746 spcl.c_magic = FS_UFS2_MAGIC; 747 } else { 748 spcl.c_magic = NFS_MAGIC; 749 spcl.c_old_date = (int32_t)spcl.c_date; 750 spcl.c_old_ddate = (int32_t)spcl.c_ddate; 751 spcl.c_old_tapea = (int32_t)spcl.c_tapea; 752 spcl.c_old_firstrec = (int32_t)spcl.c_firstrec; 753 } 754 spcl.c_checksum = 0; 755 lp = (int32_t *)&spcl; 756 sum = 0; 757 cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t)); 758 while (--cnt >= 0) { 759 sum += *lp++; 760 sum += *lp++; 761 sum += *lp++; 762 sum += *lp++; 763 } 764 spcl.c_checksum = CHECKSUM - sum; 765 writerec((char *)&spcl, 1); 766 } 767 768 union dinode * 769 getino(ino_t inum, int *modep) 770 { 771 static ino_t minino, maxino; 772 static void *inoblock; 773 struct ufs1_dinode *dp1; 774 struct ufs2_dinode *dp2; 775 776 if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL) 777 quit("cannot allocate inode memory.\n"); 778 curino = inum; 779 if (inum >= minino && inum < maxino) 780 goto gotit; 781 bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock, 782 (int)sblock->fs_bsize); 783 minino = inum - (inum % INOPB(sblock)); 784 maxino = minino + INOPB(sblock); 785 gotit: 786 if (sblock->fs_magic == FS_UFS1_MAGIC) { 787 dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino]; 788 *modep = (dp1->di_mode & IFMT); 789 return ((union dinode *)dp1); 790 } 791 dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino]; 792 *modep = (dp2->di_mode & IFMT); 793 return ((union dinode *)dp2); 794 } 795 796 /* 797 * Read a chunk of data from the disk. 798 * Try to recover from hard errors by reading in sector sized pieces. 799 * Error recovery is attempted at most BREADEMAX times before seeking 800 * consent from the operator to continue. 801 */ 802 int breaderrors = 0; 803 #define BREADEMAX 32 804 805 void 806 bread(daddr_t blkno, char *buf, int size) 807 { 808 static char *mybuf = NULL; 809 char *mybufp, *bufp, *np; 810 static size_t mybufsz = 0; 811 off_t offset; 812 int cnt, i; 813 u_int64_t secno, seccount; 814 u_int32_t secoff, secsize = lab.d_secsize; 815 816 /* 817 * We must read an integral number of sectors large enough to contain 818 * all the requested data. The read must begin at a sector. 819 */ 820 if (DL_BLKOFFSET(&lab, blkno) == 0 && size % secsize == 0) { 821 secno = DL_BLKTOSEC(&lab, blkno); 822 secoff = 0; 823 seccount = size / secsize; 824 bufp = buf; 825 } else { 826 secno = DL_BLKTOSEC(&lab, blkno); 827 secoff = DL_BLKOFFSET(&lab, blkno); 828 seccount = DL_BLKTOSEC(&lab, (size + secoff) / DEV_BSIZE); 829 if (seccount * secsize < (size + secoff)) 830 seccount++; 831 if (mybufsz < seccount * secsize) { 832 np = reallocarray(mybuf, seccount, secsize); 833 if (np == NULL) { 834 msg("No memory to read %llu %u-byte sectors", 835 seccount, secsize); 836 dumpabort(0); 837 } 838 mybufsz = seccount * secsize; 839 mybuf = np; 840 } 841 bufp = mybuf; 842 } 843 844 offset = secno * secsize; 845 846 loop: 847 if ((cnt = pread(diskfd, bufp, seccount * secsize, offset)) == 848 seccount * secsize) 849 goto done; 850 if (blkno + (size / DEV_BSIZE) > 851 fsbtodb(sblock, sblock->fs_ffs1_size)) { 852 /* 853 * Trying to read the final fragment. 854 * 855 * NB - dump only works in TP_BSIZE blocks, hence 856 * rounds `DEV_BSIZE' fragments up to TP_BSIZE pieces. 857 * It should be smarter about not actually trying to 858 * read more than it can get, but for the time being 859 * we punt and scale back the read only when it gets 860 * us into trouble. (mkm 9/25/83) 861 */ 862 size -= secsize; 863 seccount--; 864 goto loop; 865 } 866 if (cnt == -1) 867 msg("read error from %s: %s: [block %lld]: count=%d\n", 868 disk, strerror(errno), (long long)blkno, size); 869 else 870 msg("short read error from %s: [block %lld]: count=%d, " 871 "got=%d\n", disk, (long long)blkno, size, cnt); 872 if (++breaderrors > BREADEMAX) { 873 msg("More than %d block read errors from %s\n", 874 BREADEMAX, disk); 875 broadcast("DUMP IS AILING!\n"); 876 msg("This is an unrecoverable error.\n"); 877 if (!query("Do you want to attempt to continue?")){ 878 dumpabort(0); 879 /*NOTREACHED*/ 880 } else 881 breaderrors = 0; 882 } 883 /* 884 * Zero buffer, then try to read each sector of buffer separately. 885 */ 886 if (bufp == mybuf) 887 memset(bufp, 0, mybufsz); 888 else 889 memset(bufp, 0, size); 890 for (i = 0, mybufp = bufp; i < size; i += secsize, mybufp += secsize) { 891 if ((cnt = pread(diskfd, mybufp, secsize, offset + i)) == 892 secsize) 893 continue; 894 if (cnt == -1) { 895 msg("read error from %s: %s: [block %lld]: " 896 "count=%u\n", disk, strerror(errno), 897 (long long)(offset + i) / DEV_BSIZE, secsize); 898 continue; 899 } 900 msg("short read error from %s: [block %lld]: count=%u, " 901 "got=%d\n", disk, (long long)(offset + i) / DEV_BSIZE, 902 secsize, cnt); 903 } 904 905 done: 906 /* If necessary, copy out data that was read. */ 907 if (bufp == mybuf) 908 memcpy(buf, bufp + secoff, size); 909 } 910