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