1 /*- 2 * Copyright (c) 1992 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)library.c 5.7 (Berkeley) 11/01/92"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/time.h> 14 #include <sys/stat.h> 15 #include <sys/mount.h> 16 17 #include <ufs/ufs/dinode.h> 18 #include <ufs/lfs/lfs.h> 19 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <unistd.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include "clean.h" 26 27 void add_blocks __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, 28 daddr_t, daddr_t)); 29 void add_inodes __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, 30 daddr_t)); 31 int bi_compare __P((const void *, const void *)); 32 int bi_toss __P((const void *, const void *, const void *)); 33 void get_ifile __P((FS_INFO *)); 34 int get_superblock __P((FS_INFO *, struct lfs *)); 35 int pseg_valid __P((FS_INFO *, SEGSUM *)); 36 37 /* 38 * This function will get information on all mounted file systems 39 * of a given type. 40 */ 41 int 42 fs_getmntinfo(buf, type) 43 struct statfs **buf; 44 int type; 45 { 46 struct statfs *tstatfsp; 47 struct statfs *sbp; 48 int count, i, tcount; 49 50 tcount = getmntinfo(&tstatfsp, MNT_NOWAIT); 51 52 if (tcount < 0) { 53 err(0, "getmntinfo failed"); 54 return (-1); 55 } 56 57 for (count = 0, i = 0; i < tcount ; ++i) 58 if (tstatfsp[i].f_type == type) 59 ++count; 60 61 if (count) { 62 if (!(*buf = (struct statfs *) 63 malloc(count * sizeof(struct statfs)))) 64 err(1, "fs_getmntinfo: out of space"); 65 for (i = 0, sbp = *buf; i < tcount ; ++i) { 66 if (tstatfsp[i].f_type == type) { 67 *sbp = tstatfsp[i]; 68 ++sbp; 69 } 70 } 71 } 72 return (count); 73 } 74 75 /* 76 * Get all the information available on an LFS file system. 77 * Returns an array of FS_INFO structures, NULL on error. 78 */ 79 FS_INFO * 80 get_fs_info (lstatfsp, count) 81 struct statfs *lstatfsp; /* IN: array of statfs structs */ 82 int count; /* IN: number of file systems */ 83 { 84 FS_INFO *fp, *fsp; 85 int i; 86 87 fsp = (FS_INFO *)malloc(count * sizeof(FS_INFO)); 88 89 for (fp = fsp, i = 0; i < count; ++i, ++fp) { 90 fp->fi_statfsp = lstatfsp++; 91 if (get_superblock (fp, &fp->fi_lfs)) 92 err(1, "get_fs_info: get_superblock failed"); 93 fp->fi_daddr_shift = 94 fp->fi_lfs.lfs_bshift - fp->fi_lfs.lfs_fsbtodb; 95 get_ifile (fp); 96 } 97 return (fsp); 98 } 99 100 /* 101 * If we are reading the ifile then we need to refresh it. Even if 102 * we are mmapping it, it might have grown. Finally, we need to 103 * refresh the file system information (statfs) info. 104 */ 105 void 106 reread_fs_info(fsp, count) 107 FS_INFO *fsp; /* IN: array of fs_infos to free */ 108 int count; /* IN: number of file systems */ 109 { 110 int i; 111 112 for (i = 0; i < count; ++i, ++fsp) { 113 if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp)) 114 err(0, "reread_fs_info: statfs failed"); 115 #ifdef MMAP_WORKS 116 if (munmap(fsp->fi_cip, fsp->fi_ifile_length) < 0) 117 err(0, "reread_fs_info: munmap failed"); 118 #else 119 free (fsp->fi_cip); 120 #endif /* MMAP_WORKS */ 121 get_ifile (fsp); 122 } 123 } 124 125 /* 126 * Gets the superblock from disk (possibly in face of errors) 127 */ 128 int 129 get_superblock (fsp, sbp) 130 FS_INFO *fsp; /* local file system info structure */ 131 struct lfs *sbp; 132 { 133 char mntfromname[MNAMELEN+1]; 134 int fid; 135 136 strcpy(mntfromname, "/dev/r"); 137 strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); 138 139 if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { 140 err(0, "get_superblock: bad open"); 141 return (-1); 142 } 143 144 get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs)); 145 close (fid); 146 147 return (0); 148 } 149 150 /* 151 * This function will map the ifile into memory. It causes a 152 * fatal error on failure. 153 */ 154 void 155 get_ifile (fsp) 156 FS_INFO *fsp; 157 { 158 struct stat file_stat; 159 caddr_t ifp; 160 char *ifile_name; 161 int count, fid; 162 163 ifp = NULL; 164 ifile_name = malloc(strlen(fsp->fi_statfsp->f_mntonname) + 165 strlen(IFILE_NAME)+2); 166 strcat(strcat(strcpy(ifile_name, fsp->fi_statfsp->f_mntonname), "/"), 167 IFILE_NAME); 168 169 if ((fid = open(ifile_name, O_RDWR, (mode_t)0)) < 0) 170 err(1, "get_ifile: bad open"); 171 172 if (fstat (fid, &file_stat)) 173 err(1, "get_ifile: fstat failed"); 174 175 fsp->fi_ifile_length = file_stat.st_size; 176 177 /* get the ifile */ 178 #ifndef MMAP_WORKS 179 if (!(ifp = malloc ((size_t)fsp->fi_ifile_length))) 180 err (1, "get_ifile: malloc failed"); 181 redo_read: 182 count = read (fid, ifp, (size_t) fsp->fi_ifile_length); 183 184 if (count < 0) 185 err(1, "get_ifile: bad ifile read"); 186 else if (count < (int)fsp->fi_ifile_length) { 187 err(0, "get_ifile"); 188 if (lseek(fid, 0, SEEK_SET) < 0) 189 err(1, "get_ifile: bad ifile lseek"); 190 goto redo_read; 191 } 192 #else /* MMAP_WORKS */ 193 ifp = mmap ((caddr_t)0, (size_t) fsp->fi_ifile_length, PROT_READ|PROT_WRITE, 194 MAP_FILE|MAP_SHARED, fid, (off_t)0); 195 if (ifp < 0) 196 err(1, "get_ifile: mmap failed"); 197 #endif /* MMAP_WORKS */ 198 199 close (fid); 200 201 fsp->fi_cip = (CLEANERINFO *)ifp; 202 fsp->fi_segusep = (SEGUSE *)(ifp + CLEANSIZE(fsp)); 203 fsp->fi_ifilep = (IFILE *)((caddr_t)fsp->fi_segusep + SEGTABSIZE(fsp)); 204 205 /* 206 * The number of ifile entries is equal to the number of blocks 207 * blocks in the ifile minus the ones allocated to cleaner info 208 * and segment usage table multiplied by the number of ifile 209 * entries per page. 210 */ 211 fsp->fi_ifile_count = (fsp->fi_ifile_length >> fsp->fi_lfs.lfs_bshift - 212 fsp->fi_lfs.lfs_cleansz - fsp->fi_lfs.lfs_segtabsz) * 213 fsp->fi_lfs.lfs_ifpb; 214 215 free (ifile_name); 216 } 217 218 /* 219 * This function will scan a segment and return a list of 220 * <inode, blocknum> pairs which indicate which blocks were 221 * contained as live data within the segment when the segment 222 * summary was read (it may have "died" since then). Any given 223 * pair will be listed at most once. 224 */ 225 int 226 lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) 227 FS_INFO *fsp; /* pointer to local file system information */ 228 int seg; /* the segment number */ 229 caddr_t seg_buf; /* the buffer containing the segment's data */ 230 BLOCK_INFO **blocks; /* OUT: array of block_info for live blocks */ 231 int *bcount; /* OUT: number of active blocks in segment */ 232 { 233 BLOCK_INFO *bip; 234 SEGSUM *sp; 235 SEGUSE *sup; 236 FINFO *fip; 237 struct lfs *lfsp; 238 caddr_t s, segend; 239 daddr_t pseg_addr, seg_addr; 240 int i, nelem, nblocks, sumsize; 241 time_t timestamp; 242 243 lfsp = &fsp->fi_lfs; 244 nelem = 2 * lfsp->lfs_ssize; 245 if (!(bip = malloc(nelem * sizeof(BLOCK_INFO)))) 246 goto err0; 247 248 sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, seg); 249 s = seg_buf + (sup->su_flags & SEGUSE_SUPERBLOCK ? LFS_SBPAD : 0); 250 seg_addr = sntoda(lfsp, seg); 251 pseg_addr = seg_addr + (sup->su_flags & SEGUSE_SUPERBLOCK ? btodb(LFS_SBPAD) : 0); 252 #ifdef VERBOSE 253 printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s, seg_addr); 254 #endif /* VERBOSE */ 255 256 *bcount = 0; 257 for (segend = seg_buf + seg_size(lfsp), timestamp = 0; s < segend; ) { 258 sp = (SEGSUM *)s; 259 260 #ifdef VERBOSE 261 printf("\tpartial at: 0x%x\n", pseg_addr); 262 print_SEGSUM(lfsp, sp); 263 fflush(stdout); 264 #endif /* VERBOSE */ 265 266 nblocks = pseg_valid(fsp, sp); 267 if (nblocks <= 0) 268 break; 269 270 /* Check if we have hit old data */ 271 if (timestamp > ((SEGSUM*)s)->ss_create) 272 break; 273 timestamp = ((SEGSUM*)s)->ss_create; 274 275 #ifdef DIAGNOSTIC 276 /* Verfiy size of summary block */ 277 sumsize = sizeof(SEGSUM) + 278 (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); 279 for (fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) { 280 sumsize += sizeof(FINFO) + 281 (fip->fi_nblocks - 1) * sizeof(daddr_t); 282 fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]); 283 } 284 if (sumsize > LFS_SUMMARY_SIZE) { 285 fprintf(stderr, 286 "Segment %d summary block too big: %d\n", 287 seg, sumsize); 288 exit(1); 289 } 290 #endif 291 292 if (*bcount + nblocks + sp->ss_ninos > nelem) { 293 nelem = *bcount + nblocks + sp->ss_ninos; 294 bip = realloc (bip, nelem * sizeof(BLOCK_INFO)); 295 if (!bip) 296 goto err0; 297 } 298 add_blocks(fsp, bip, bcount, sp, seg_buf, seg_addr, pseg_addr); 299 add_inodes(fsp, bip, bcount, sp, seg_buf, seg_addr); 300 pseg_addr += fsbtodb(lfsp, nblocks) + 301 bytetoda(fsp, LFS_SUMMARY_SIZE); 302 s += (nblocks << lfsp->lfs_bshift) + LFS_SUMMARY_SIZE; 303 } 304 qsort(bip, *bcount, sizeof(BLOCK_INFO), bi_compare); 305 toss(bip, bcount, sizeof(BLOCK_INFO), bi_toss, NULL); 306 #ifdef VERBOSE 307 { 308 BLOCK_INFO *_bip; 309 int i; 310 311 printf("BLOCK INFOS\n"); 312 for (_bip = bip, i=0; i < *bcount; ++_bip, ++i) 313 PRINT_BINFO(_bip); 314 } 315 #endif 316 *blocks = bip; 317 return (0); 318 319 err0: *bcount = 0; 320 return (-1); 321 322 } 323 324 /* 325 * This will parse a partial segment and fill in BLOCK_INFO structures 326 * for each block described in the segment summary. It will not include 327 * blocks or inodes from files with new version numbers. 328 */ 329 void 330 add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr) 331 FS_INFO *fsp; /* pointer to super block */ 332 BLOCK_INFO *bip; /* Block info array */ 333 int *countp; /* IN/OUT: number of blocks in array */ 334 SEGSUM *sp; /* segment summmary pointer */ 335 caddr_t seg_buf; /* buffer containing segment */ 336 daddr_t segaddr; /* address of this segment */ 337 daddr_t psegaddr; /* address of this partial segment */ 338 { 339 IFILE *ifp; 340 FINFO *fip; 341 caddr_t bp; 342 daddr_t *dp, *iaddrp; 343 int db_per_block, i, j; 344 u_long page_size; 345 346 #ifdef VERBOSE 347 printf("FILE INFOS\n"); 348 #endif 349 db_per_block = fsbtodb(&fsp->fi_lfs, 1); 350 page_size = fsp->fi_lfs.lfs_bsize; 351 bp = seg_buf + datobyte(fsp, psegaddr - segaddr) + LFS_SUMMARY_SIZE; 352 bip += *countp; 353 psegaddr += bytetoda(fsp, LFS_SUMMARY_SIZE); 354 iaddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); 355 --iaddrp; 356 for (fip = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; 357 ++i, fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks])) { 358 359 ifp = IFILE_ENTRY(&fsp->fi_lfs, fsp->fi_ifilep, fip->fi_ino); 360 PRINT_FINFO(fip, ifp); 361 if (ifp->if_version > fip->fi_version) 362 continue; 363 dp = &(fip->fi_blocks[0]); 364 for (j = 0; j < fip->fi_nblocks; j++, dp++) { 365 while (psegaddr == *iaddrp) { 366 psegaddr += db_per_block; 367 bp += page_size; 368 --iaddrp; 369 } 370 bip->bi_inode = fip->fi_ino; 371 bip->bi_lbn = *dp; 372 bip->bi_daddr = psegaddr; 373 bip->bi_segcreate = (time_t)(sp->ss_create); 374 bip->bi_bp = bp; 375 bip->bi_version = ifp->if_version; 376 psegaddr += db_per_block; 377 bp += page_size; 378 ++bip; 379 ++(*countp); 380 } 381 } 382 } 383 384 /* 385 * For a particular segment summary, reads the inode blocks and adds 386 * INODE_INFO structures to the array. Returns the number of inodes 387 * actually added. 388 */ 389 void 390 add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) 391 FS_INFO *fsp; /* pointer to super block */ 392 BLOCK_INFO *bip; /* block info array */ 393 int *countp; /* pointer to current number of inodes */ 394 SEGSUM *sp; /* segsum pointer */ 395 caddr_t seg_buf; /* the buffer containing the segment's data */ 396 daddr_t seg_addr; /* disk address of seg_buf */ 397 { 398 struct dinode *di; 399 struct lfs *lfsp; 400 IFILE *ifp; 401 BLOCK_INFO *bp; 402 daddr_t *daddrp; 403 ino_t inum; 404 int i; 405 406 if (sp->ss_ninos <= 0) 407 return; 408 409 bp = bip + *countp; 410 lfsp = &fsp->fi_lfs; 411 #ifdef VERBOSE 412 (void) printf("INODES:\n"); 413 #endif 414 daddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); 415 for (i = 0; i < sp->ss_ninos; ++i) { 416 if (i % INOPB(lfsp) == 0) { 417 --daddrp; 418 di = (struct dinode *)(seg_buf + 419 ((*daddrp - seg_addr) << fsp->fi_daddr_shift)); 420 } else 421 ++di; 422 423 inum = di->di_inum; 424 bp->bi_lbn = LFS_UNUSED_LBN; 425 bp->bi_inode = inum; 426 bp->bi_daddr = *daddrp; 427 bp->bi_bp = di; 428 bp->bi_segcreate = sp->ss_create; 429 430 if (inum == LFS_IFILE_INUM) { 431 bp->bi_version = 1; /* Ifile version should be 1 */ 432 bp++; 433 ++(*countp); 434 PRINT_INODE(1, bp); 435 } else { 436 ifp = IFILE_ENTRY(lfsp, fsp->fi_ifilep, inum); 437 PRINT_INODE(ifp->if_daddr == *daddrp, bp); 438 bp->bi_version = ifp->if_version; 439 if (ifp->if_daddr == *daddrp) { 440 bp++; 441 ++(*countp); 442 } 443 } 444 } 445 } 446 447 /* 448 * Checks the summary checksum and the data checksum to determine if the 449 * segment is valid or not. Returns the size of the partial segment if it 450 * is valid, * and 0 otherwise. Use dump_summary to figure out size of the 451 * the partial as well as whether or not the checksum is valid. 452 */ 453 int 454 pseg_valid (fsp, ssp) 455 FS_INFO *fsp; /* pointer to file system info */ 456 SEGSUM *ssp; /* pointer to segment summary block */ 457 { 458 caddr_t p; 459 int i, nblocks; 460 u_long *datap; 461 SEGUSE *sup; 462 463 if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 || 464 nblocks > fsp->fi_lfs.lfs_ssize - 1) 465 return(0); 466 467 /* check data/inode block(s) checksum too */ 468 datap = (u_long *)malloc(nblocks * sizeof(u_long)); 469 p = (caddr_t)ssp + LFS_SUMMARY_SIZE; 470 for (i = 0; i < nblocks; ++i) { 471 datap[i] = *((u_long *)p); 472 p += fsp->fi_lfs.lfs_bsize; 473 } 474 if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum) 475 return (0); 476 477 return (nblocks); 478 } 479 480 481 /* #define MMAP_SEGMENT */ 482 /* 483 * read a segment into a memory buffer 484 */ 485 int 486 mmap_segment (fsp, segment, segbuf) 487 FS_INFO *fsp; /* file system information */ 488 int segment; /* segment number */ 489 caddr_t *segbuf; /* pointer to buffer area */ 490 { 491 struct lfs *lfsp; 492 int fid; /* fildes for file system device */ 493 daddr_t seg_daddr; /* base disk address of segment */ 494 off_t seg_byte; 495 size_t ssize; 496 char mntfromname[MNAMELEN+2]; 497 498 lfsp = &fsp->fi_lfs; 499 500 /* get the disk address of the beginning of the segment */ 501 seg_daddr = sntoda(lfsp, segment); 502 seg_byte = datobyte(fsp, seg_daddr); 503 ssize = seg_size(lfsp); 504 505 strcpy(mntfromname, "/dev/r"); 506 strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); 507 508 if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { 509 err(0, "mmap_segment: bad open"); 510 return (-1); 511 } 512 513 #ifdef MMAP_SEGMENT 514 *segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ, 515 MAP_FILE, fid, seg_byte); 516 if (*(long *)segbuf < 0) { 517 err(0, "mmap_segment: mmap failed"); 518 return (NULL); 519 } 520 #else /* MMAP_SEGMENT */ 521 #ifdef VERBOSE 522 printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n", 523 seg_daddr, ssize, seg_byte); 524 #endif 525 /* malloc the space for the buffer */ 526 *segbuf = malloc(ssize); 527 if (!*segbuf) { 528 err(0, "mmap_segment: malloc failed"); 529 return(NULL); 530 } 531 532 /* read the segment data into the buffer */ 533 if (lseek (fid, seg_byte, SEEK_SET) != seg_byte) { 534 err (0, "mmap_segment: bad lseek"); 535 free(*segbuf); 536 return (-1); 537 } 538 539 if (read (fid, *segbuf, ssize) != ssize) { 540 err (0, "mmap_segment: bad read"); 541 free(*segbuf); 542 return (-1); 543 } 544 #endif /* MMAP_SEGMENT */ 545 close (fid); 546 547 return (0); 548 } 549 550 void 551 munmap_segment (fsp, seg_buf) 552 FS_INFO *fsp; /* file system information */ 553 caddr_t seg_buf; /* pointer to buffer area */ 554 { 555 #ifdef MMAP_SEGMENT 556 munmap (seg_buf, seg_size(&fsp->fi_lfs)); 557 #else /* MMAP_SEGMENT */ 558 free (seg_buf); 559 #endif /* MMAP_SEGMENT */ 560 } 561 562 563 /* 564 * USEFUL DEBUGGING TOOLS: 565 */ 566 void 567 print_SEGSUM (lfsp, p) 568 struct lfs *lfsp; 569 SEGSUM *p; 570 { 571 if (p) 572 (void) dump_summary(lfsp, p, DUMP_ALL, NULL); 573 else printf("0x0"); 574 fflush(stdout); 575 } 576 577 int 578 bi_compare(a, b) 579 const void *a; 580 const void *b; 581 { 582 const BLOCK_INFO *ba, *bb; 583 int diff; 584 585 ba = a; 586 bb = b; 587 588 if (diff = (int)(ba->bi_inode - bb->bi_inode)) 589 return (diff); 590 if (diff = (int)(ba->bi_lbn - bb->bi_lbn)) { 591 if (ba->bi_lbn == LFS_UNUSED_LBN) 592 return(-1); 593 else if (bb->bi_lbn == LFS_UNUSED_LBN) 594 return(1); 595 else if (ba->bi_lbn < 0 && bb->bi_lbn >= 0) 596 return(1); 597 else if (bb->bi_lbn < 0 && ba->bi_lbn >= 0) 598 return(-1); 599 else 600 return (diff); 601 } 602 if (diff = (int)(ba->bi_segcreate - bb->bi_segcreate)) 603 return (diff); 604 diff = (int)(ba->bi_daddr - bb->bi_daddr); 605 return (diff); 606 } 607 608 int 609 bi_toss(dummy, a, b) 610 const void *dummy; 611 const void *a; 612 const void *b; 613 { 614 const BLOCK_INFO *ba, *bb; 615 616 ba = a; 617 bb = b; 618 619 return(ba->bi_inode == bb->bi_inode && ba->bi_lbn == bb->bi_lbn); 620 } 621 622 void 623 toss(p, nump, size, dotoss, client) 624 void *p; 625 int *nump; 626 size_t size; 627 int (*dotoss) __P((const void *, const void *, const void *)); 628 void *client; 629 { 630 int i; 631 void *p1; 632 633 if (*nump == 0) 634 return; 635 636 for (i = *nump; --i > 0;) { 637 p1 = p + size; 638 if (dotoss(client, p, p1)) { 639 bcopy(p1, p, i * size); 640 --(*nump); 641 } else 642 p += size; 643 } 644 } 645