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