1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * ncheck -- obtain file names from reading filesystem 44 */ 45 46 #define MAXNINDIR (MAXBSIZE / sizeof (daddr_t)) 47 48 #include <sys/param.h> 49 #include <sys/types.h> 50 #include <sys/vnode.h> 51 #include <sys/fs/ufs_inode.h> 52 #include <sys/fs/ufs_fs.h> 53 #include <sys/fs/ufs_fsdir.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <unistd.h> 60 #include "roll_log.h" 61 62 union { 63 struct fs sblk; 64 char xxx[SBSIZE]; /* because fs is variable length */ 65 } real_fs; 66 #define sblock real_fs.sblk 67 68 struct dinode *itab; 69 unsigned itab_size; 70 71 72 struct dinode *gip; 73 74 /* inode list */ 75 struct ilist { 76 ino_t ino; 77 ushort_t mode; 78 uid_t uid; 79 gid_t gid; 80 } *ilist; 81 int ilist_size = 0; /* size of ilist[] */ 82 int ilist_index = 0; /* current index for storing into ilist; */ 83 #define ILIST_SZ_INCR 1000 /* initial size, amount to incr sz of ilist */ 84 #define MAX_ILIST_INDEX() (ilist_size - 1) 85 86 struct htab 87 { 88 ino_t h_ino; 89 ino_t h_pino; 90 int h_name_index; /* index into string table */ 91 } *htab; 92 unsigned htab_size; /* how much malloc'd for htab */ 93 94 /* 95 * string table: used to hold filenames. 96 */ 97 char *strngtab; 98 int strngloc; 99 int strngtab_size; 100 #define STRNGTAB_INCR (1024*16) /* amount to grow strngtab */ 101 #define MAX_STRNGTAB_INDEX() (strngtab_size - 1) 102 #define AVG_PATH_LEN 30 /* average (?) length of name */ 103 104 long hsize; 105 106 struct dirstuff { 107 int loc; 108 struct dinode *ip; 109 char dbuf[MAXBSIZE]; 110 }; 111 112 113 int aflg = 0; 114 int sflg = 0; 115 int iflg = 0; /* number of inodes being searched for */ 116 int mflg = 0; 117 int fi; 118 ino_t ino; 119 int nhent; 120 121 int nerror; 122 123 long atol(); 124 daddr_t bmap(); 125 void bread(diskaddr_t bno, char *buf, int cnt); 126 void check(char *file); 127 int dotname(struct direct *dp); 128 offset_t llseek(); 129 struct htab *lookup(ino_t i, int ef); 130 void pass1(struct dinode *ip); 131 void pass2(struct dinode *ip); 132 void pass3(struct dinode *ip); 133 void pname(ino_t i, int lev); 134 char *strcpy(); 135 void usage(); 136 struct direct *dreaddir(); 137 void extend_ilist(); 138 int extend_strngtab(unsigned int size); 139 uchar_t *extend_tbl(uchar_t *tbl, unsigned int *current_size, 140 unsigned int new_size); 141 142 extern int optind; 143 extern char *optarg; 144 145 char *subopts [] = { 146 #define M_FLAG 0 147 "m", 148 NULL 149 }; 150 151 main(argc, argv) 152 int argc; 153 char *argv[]; 154 { 155 long n; 156 int opt; 157 char *suboptions, *value; 158 int suboption; 159 char *p; 160 int first = 0; 161 162 extend_ilist(); 163 while ((opt = getopt(argc, argv, "ao:i:s")) != EOF) { 164 switch (opt) { 165 166 case 'a': 167 aflg++; 168 break; 169 170 case 'o': 171 /* 172 * ufs specific options. 173 */ 174 suboptions = optarg; 175 while (*suboptions != '\0') { 176 suboption = getsubopt(&suboptions, 177 subopts, &value); 178 switch (suboption) { 179 180 case M_FLAG: 181 mflg++; 182 break; 183 184 default: 185 usage(); 186 } 187 } 188 break; 189 190 case 'i': 191 while ((p = (char *)strtok((first++ == 0 ? optarg : 0), 192 ", ")) != NULL) { 193 if ((n = atoi(p)) == 0) 194 break; 195 ilist[iflg].ino = n; 196 iflg++; 197 ilist_index = iflg; 198 if (iflg > MAX_ILIST_INDEX()) 199 extend_ilist(); 200 } 201 break; 202 203 case 's': 204 sflg++; 205 break; 206 #if 0 207 case 'V': 208 { 209 int opt_count; 210 char *opt_text; 211 212 (void) fprintf(stdout, "ncheck -F ufs "); 213 for (opt_count = 1; opt_count < argc; 214 opt_count++) { 215 opt_text = argv[opt_count]; 216 if (opt_text) 217 (void) fprintf(stdout, " %s ", 218 opt_text); 219 } 220 (void) fprintf(stdout, "\n"); 221 } 222 break; 223 #endif 224 case '?': 225 usage(); 226 } 227 } 228 argc -= optind; 229 argv = &argv[optind]; 230 while (argc--) { 231 check(*argv); 232 argv++; 233 } 234 return (nerror); 235 } 236 237 void 238 check(file) 239 char *file; 240 { 241 register int i, j, c; 242 243 fi = open64(file, 0); 244 if (fi < 0) { 245 (void) fprintf(stderr, "ncheck: cannot open %s\n", file); 246 nerror++; 247 return; 248 } 249 nhent = 0; 250 (void) printf("%s:\n", file); 251 sync(); 252 bread((diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE); 253 if ((sblock.fs_magic != FS_MAGIC) && 254 (sblock.fs_magic != MTB_UFS_MAGIC)) { 255 (void) printf("%s: not a ufs file system\n", file); 256 nerror++; 257 return; 258 } 259 260 if ((sblock.fs_magic == FS_MAGIC) && 261 ((sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2) && 262 (sblock.fs_version != UFS_VERSION_MIN))) { 263 (void) printf("%s: unrecognized ufs version number %d\n", 264 file, sblock.fs_version); 265 nerror++; 266 return; 267 } 268 269 if ((sblock.fs_magic == MTB_UFS_MAGIC) && 270 ((sblock.fs_version > MTB_UFS_VERSION_1) || 271 (sblock.fs_version < MTB_UFS_VERSION_MIN))) { 272 (void) printf("%s: unrecognized ufs version number %d\n", 273 file, sblock.fs_version); 274 nerror++; 275 return; 276 } 277 278 /* If fs is logged, roll the log. */ 279 if (sblock.fs_logbno) { 280 switch (rl_roll_log(file)) { 281 case RL_SUCCESS: 282 /* 283 * Reread the superblock. Rolling the log may have 284 * changed it. 285 */ 286 bread((diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE); 287 break; 288 case RL_SYSERR: 289 (void) printf("Warning: cannot roll log for %s. %s\n", 290 file, strerror(errno)); 291 break; 292 default: 293 (void) printf("Warning: cannot roll log for %s.\n", 294 file); 295 break; 296 } 297 } 298 299 itab = (struct dinode *)extend_tbl((uchar_t *)itab, &itab_size, 300 (unsigned)(sblock.fs_ipg * sizeof (struct dinode))); 301 if (itab == 0) { 302 (void) fprintf(stderr, 303 "ncheck: not enough memory for itab table\n"); 304 nerror++; 305 return; 306 } 307 308 hsize = sblock.fs_ipg * sblock.fs_ncg - sblock.fs_cstotal.cs_nifree + 1; 309 310 htab = (struct htab *)extend_tbl((uchar_t *)htab, &htab_size, 311 (unsigned)(hsize * sizeof (struct htab))); 312 if (htab == 0) { 313 (void) fprintf(stderr, 314 "ncheck: not enough memory for htab table\n"); 315 nerror++; 316 return; 317 } 318 319 if (!extend_strngtab(AVG_PATH_LEN * hsize)) { 320 (void) printf("not enough memory to allocate tables\n"); 321 nerror++; 322 return; 323 } 324 strngloc = 0; 325 326 ino = 0; 327 for (c = 0; c < sblock.fs_ncg; c++) { 328 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 329 (int)(sblock.fs_ipg * sizeof (struct dinode))); 330 for (j = 0; j < sblock.fs_ipg; j++) { 331 if (itab[j].di_smode != 0) { 332 itab[j].di_mode = itab[j].di_smode; 333 if (itab[j].di_suid != UID_LONG) 334 itab[j].di_uid = itab[j].di_suid; 335 if (itab[j].di_sgid != GID_LONG) 336 itab[j].di_gid = itab[j].di_sgid; 337 pass1(&itab[j]); 338 } 339 ino++; 340 } 341 } 342 ilist[ilist_index++].ino = 0; 343 if (ilist_index > MAX_ILIST_INDEX()) 344 extend_ilist(); 345 ino = 0; 346 for (c = 0; c < sblock.fs_ncg; c++) { 347 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 348 (int)(sblock.fs_ipg * sizeof (struct dinode))); 349 for (j = 0; j < sblock.fs_ipg; j++) { 350 351 if (itab[j].di_smode != 0) { 352 itab[j].di_mode = itab[j].di_smode; 353 pass2(&itab[j]); 354 } 355 ino++; 356 } 357 } 358 ino = 0; 359 for (c = 0; c < sblock.fs_ncg; c++) { 360 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 361 (int)(sblock.fs_ipg * sizeof (struct dinode))); 362 for (j = 0; j < sblock.fs_ipg; j++) { 363 if (itab[j].di_smode != 0) { 364 itab[j].di_mode = itab[j].di_smode; 365 pass3(&itab[j]); 366 } 367 ino++; 368 } 369 } 370 (void) close(fi); 371 372 /* 373 * Clear those elements after inodes specified by "-i" out of 374 * ilist. 375 */ 376 for (i = iflg; i < ilist_index; i++) { 377 ilist[i].ino = 0; 378 } 379 ilist_index = iflg; 380 } 381 382 void 383 pass1(ip) 384 register struct dinode *ip; 385 { 386 int i; 387 388 if (mflg) { 389 for (i = 0; i < iflg; i++) 390 if (ino == ilist[i].ino) { 391 ilist[i].mode = ip->di_mode; 392 ilist[i].uid = ip->di_uid; 393 ilist[i].gid = ip->di_gid; 394 } 395 } 396 if ((ip->di_mode & IFMT) != IFDIR) { 397 if (sflg == 0) 398 return; 399 if ((ip->di_mode & IFMT) == IFBLK || 400 (ip->di_mode & IFMT) == IFCHR || 401 ip->di_mode&(ISUID|ISGID)) { 402 ilist[ilist_index].ino = ino; 403 ilist[ilist_index].mode = ip->di_mode; 404 ilist[ilist_index].uid = ip->di_uid; 405 ilist[ilist_index].gid = ip->di_gid; 406 if (++ilist_index > MAX_ILIST_INDEX()) 407 extend_ilist(); 408 return; 409 } 410 } 411 (void) lookup(ino, 1); 412 } 413 414 void 415 pass2(ip) 416 register struct dinode *ip; 417 { 418 register struct direct *dp; 419 struct dirstuff dirp; 420 struct htab *hp; 421 422 423 if ((ip->di_mode&IFMT) != IFDIR) 424 return; 425 dirp.loc = 0; 426 dirp.ip = ip; 427 gip = ip; 428 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 429 int nmlen; 430 431 if (dp->d_ino == 0) 432 continue; 433 434 hp = lookup(dp->d_ino, 0); 435 if (hp == 0) 436 continue; 437 438 if (dotname(dp)) 439 continue; 440 hp->h_pino = ino; 441 nmlen = strlen(dp->d_name); 442 443 if (strngloc + nmlen + 1 > MAX_STRNGTAB_INDEX()) { 444 if (!extend_strngtab(STRNGTAB_INCR)) { 445 perror("ncheck: can't grow string table\n"); 446 exit(32); 447 } 448 } 449 450 hp->h_name_index = strngloc; 451 (void) strcpy(&strngtab[strngloc], dp->d_name); 452 strngloc += nmlen + 1; 453 } 454 } 455 456 void 457 pass3(ip) 458 register struct dinode *ip; 459 { 460 register struct direct *dp; 461 struct dirstuff dirp; 462 int k; 463 464 if ((ip->di_mode&IFMT) != IFDIR) 465 return; 466 dirp.loc = 0; 467 dirp.ip = ip; 468 gip = ip; 469 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 470 if (aflg == 0 && dotname(dp)) 471 continue; 472 473 if (sflg == 0 && iflg == 0) 474 goto pr; 475 for (k = 0; k < ilist_index && ilist[k].ino != 0; k++) { 476 if (ilist[k].ino == dp->d_ino) { 477 break; 478 } 479 } 480 if (ilist[k].ino == 0) 481 continue; 482 if (mflg) 483 (void) printf("mode %-6o uid %-5ld gid %-5ld ino ", 484 ilist[k].mode, ilist[k].uid, ilist[k].gid); 485 pr: 486 (void) printf("%-5u\t", dp->d_ino); 487 pname(ino, 0); 488 (void) printf("/%s", dp->d_name); 489 if (lookup(dp->d_ino, 0)) 490 (void) printf("/."); 491 (void) printf("\n"); 492 } 493 } 494 495 /* 496 * get next entry in a directory. 497 */ 498 struct direct * 499 dreaddir(dirp) 500 register struct dirstuff *dirp; 501 { 502 register struct direct *dp; 503 daddr_t lbn, d; 504 505 for (;;) { 506 507 if (dirp->loc >= (int)dirp->ip->di_size) 508 return (NULL); 509 if (blkoff(&sblock, dirp->loc) == 0) { 510 511 lbn = lblkno(&sblock, dirp->loc); 512 513 d = bmap(lbn); 514 if (d == 0) 515 return (NULL); 516 517 bread(fsbtodb(&sblock, d), dirp->dbuf, 518 (int)dblksize(&sblock, dirp->ip, (int)lbn)); 519 520 } 521 dp = (struct direct *) 522 (dirp->dbuf + blkoff(&sblock, dirp->loc)); 523 dirp->loc += dp->d_reclen; 524 if (dp->d_ino == 0) { 525 continue; 526 } 527 return (dp); 528 } 529 } 530 531 dotname(dp) 532 register struct direct *dp; 533 { 534 535 if (dp->d_name[0] == '.') { 536 if (dp->d_name[1] == 0 || 537 (dp->d_name[1] == '.' && dp->d_name[2] == 0)) 538 return (1); 539 } 540 return (0); 541 } 542 543 void 544 pname(i, lev) 545 ino_t i; 546 int lev; 547 { 548 register struct htab *hp; 549 550 if (i == UFSROOTINO) 551 return; 552 553 if ((hp = lookup(i, 0)) == 0) { 554 (void) printf("???"); 555 return; 556 } 557 if (lev > 10) { 558 (void) printf("..."); 559 return; 560 } 561 pname(hp->h_pino, ++lev); 562 (void) printf("/%s", &(strngtab[hp->h_name_index])); 563 564 } 565 566 struct htab * 567 lookup(i, ef) 568 ino_t i; 569 int ef; 570 { 571 register struct htab *hp; 572 573 for (hp = &htab[(int)i%hsize]; hp->h_ino; ) { 574 if (hp->h_ino == i) 575 return (hp); 576 if (++hp >= &htab[hsize]) 577 hp = htab; 578 } 579 580 if (ef == 0) 581 return (0); 582 if (++nhent >= hsize) { 583 (void) fprintf(stderr, "ncheck: hsize of %ld is too small\n", 584 hsize); 585 exit(32); 586 } 587 hp->h_ino = i; 588 return (hp); 589 } 590 591 void 592 bread(bno, buf, cnt) 593 diskaddr_t bno; 594 char *buf; 595 int cnt; 596 { 597 register i; 598 int got; 599 600 if (llseek(fi, (offset_t)bno * DEV_BSIZE, 0) == -1) { 601 (void) fprintf(stderr, "ncheck: lseek error %lld\n", 602 (offset_t)bno * DEV_BSIZE); 603 604 for (i = 0; i < cnt; i++) { 605 buf[i] = 0; 606 } 607 608 return; 609 } 610 611 got = read((int)fi, buf, cnt); 612 613 if (got != cnt) { 614 (void) fprintf(stderr, 615 "ncheck: read error at block %lld (wanted %d got %d)\n", 616 bno, cnt, got); 617 618 for (i = 0; i < cnt; i++) 619 buf[i] = 0; 620 } 621 } 622 623 daddr_t 624 bmap(i) 625 daddr_t i; 626 { 627 daddr_t ibuf[MAXNINDIR]; 628 629 if (i < NDADDR) 630 return (gip->di_db[i]); 631 i -= NDADDR; 632 if (i > NINDIR(&sblock)) { 633 (void) fprintf(stderr, "ncheck: %lu - huge directory\n", ino); 634 return ((daddr_t)0); 635 } 636 637 bread(fsbtodb(&sblock, gip->di_ib[0]), (char *)ibuf, sizeof (ibuf)); 638 639 return (ibuf[i]); 640 } 641 642 void 643 usage() 644 { 645 (void) fprintf(stderr, 646 /*CSTYLED*/ 647 "ufs usage: ncheck [-F ufs] [generic options] [-a -i #list -s] [-o m] special\n"); 648 exit(32); 649 } 650 651 652 /* 653 * Extend or create the inode list; 654 * this is used to contains the list of inodes we've been 655 * asked to check using the "-i" flag and to hold the 656 * inode numbers of files which we detect as being 657 * blk|char|setuid|setgid ("-s" flag support). 658 * Preserves contents. 659 */ 660 void 661 extend_ilist() 662 { 663 ilist_size += ILIST_SZ_INCR; 664 ilist = (struct ilist *)realloc(ilist, 665 (ilist_size * sizeof (struct ilist))); 666 667 if (ilist == NULL) { 668 perror("ncheck: not enough memory to grow ilist\n"); 669 exit(32); 670 } 671 } 672 673 /* 674 * Extend or create the string table. 675 * Preserves contents. 676 * Return non-zero for success. 677 */ 678 int 679 extend_strngtab(size) 680 unsigned int size; 681 { 682 strngtab_size += size; 683 strngtab = (char *)realloc(strngtab, strngtab_size); 684 685 return ((int)strngtab); 686 } 687 688 /* 689 * Extend or create a table, throwing away previous 690 * contents. 691 * Return null on failure. 692 */ 693 uchar_t * 694 extend_tbl(tbl, current_size, new_size) 695 uchar_t *tbl; 696 unsigned int *current_size; /* current size */ 697 unsigned int new_size; /* bytes required */ 698 { 699 /* 700 * if we've already allocated tbl, 701 * but it is too small, free it. 702 * we don't realloc because we are throwing 703 * away its contents. 704 */ 705 706 if (tbl && (*current_size < new_size)) { 707 free(tbl); 708 tbl = NULL; 709 } 710 711 if (tbl == NULL) { 712 tbl = (uchar_t *)malloc(new_size); 713 if (tbl == 0) 714 return ((uchar_t *)0); 715 716 *current_size = new_size; 717 } 718 (void) memset(tbl, 0, new_size); 719 720 return (tbl); 721 } 722