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