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