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 2003 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 == MTB_UFS_MAGIC) && 261 ((sblock.fs_version > MTB_UFS_VERSION_1) || 262 (sblock.fs_version < MTB_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 fs is logged, roll the log. */ 270 if (sblock.fs_logbno) { 271 switch (rl_roll_log(file)) { 272 case RL_SUCCESS: 273 /* 274 * Reread the superblock. Rolling the log may have 275 * changed it. 276 */ 277 bread((diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE); 278 break; 279 case RL_SYSERR: 280 (void) printf("Warning: cannot roll log for %s. %s\n", 281 file, strerror(errno)); 282 break; 283 default: 284 (void) printf("Warning: cannot roll log for %s.\n", 285 file); 286 break; 287 } 288 } 289 290 itab = (struct dinode *)extend_tbl((uchar_t *)itab, &itab_size, 291 (unsigned)(sblock.fs_ipg * sizeof (struct dinode))); 292 if (itab == 0) { 293 (void) fprintf(stderr, 294 "ncheck: not enough memory for itab table\n"); 295 nerror++; 296 return; 297 } 298 299 hsize = sblock.fs_ipg * sblock.fs_ncg - sblock.fs_cstotal.cs_nifree + 1; 300 301 htab = (struct htab *)extend_tbl((uchar_t *)htab, &htab_size, 302 (unsigned)(hsize * sizeof (struct htab))); 303 if (htab == 0) { 304 (void) fprintf(stderr, 305 "ncheck: not enough memory for htab table\n"); 306 nerror++; 307 return; 308 } 309 310 if (!extend_strngtab(AVG_PATH_LEN * hsize)) { 311 (void) printf("not enough memory to allocate tables\n"); 312 nerror++; 313 return; 314 } 315 strngloc = 0; 316 317 ino = 0; 318 for (c = 0; c < sblock.fs_ncg; c++) { 319 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 320 (int)(sblock.fs_ipg * sizeof (struct dinode))); 321 for (j = 0; j < sblock.fs_ipg; j++) { 322 if (itab[j].di_smode != 0) { 323 itab[j].di_mode = itab[j].di_smode; 324 if (itab[j].di_suid != UID_LONG) 325 itab[j].di_uid = itab[j].di_suid; 326 if (itab[j].di_sgid != GID_LONG) 327 itab[j].di_gid = itab[j].di_sgid; 328 pass1(&itab[j]); 329 } 330 ino++; 331 } 332 } 333 ilist[ilist_index++].ino = 0; 334 if (ilist_index > MAX_ILIST_INDEX()) 335 extend_ilist(); 336 ino = 0; 337 for (c = 0; c < sblock.fs_ncg; c++) { 338 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 339 (int)(sblock.fs_ipg * sizeof (struct dinode))); 340 for (j = 0; j < sblock.fs_ipg; j++) { 341 342 if (itab[j].di_smode != 0) { 343 itab[j].di_mode = itab[j].di_smode; 344 pass2(&itab[j]); 345 } 346 ino++; 347 } 348 } 349 ino = 0; 350 for (c = 0; c < sblock.fs_ncg; c++) { 351 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 352 (int)(sblock.fs_ipg * sizeof (struct dinode))); 353 for (j = 0; j < sblock.fs_ipg; j++) { 354 if (itab[j].di_smode != 0) { 355 itab[j].di_mode = itab[j].di_smode; 356 pass3(&itab[j]); 357 } 358 ino++; 359 } 360 } 361 (void) close(fi); 362 363 /* 364 * Clear those elements after inodes specified by "-i" out of 365 * ilist. 366 */ 367 for (i = iflg; i < ilist_index; i++) { 368 ilist[i].ino = 0; 369 } 370 ilist_index = iflg; 371 } 372 373 void 374 pass1(ip) 375 register struct dinode *ip; 376 { 377 int i; 378 379 if (mflg) { 380 for (i = 0; i < iflg; i++) 381 if (ino == ilist[i].ino) { 382 ilist[i].mode = ip->di_mode; 383 ilist[i].uid = ip->di_uid; 384 ilist[i].gid = ip->di_gid; 385 } 386 } 387 if ((ip->di_mode & IFMT) != IFDIR) { 388 if (sflg == 0) 389 return; 390 if ((ip->di_mode & IFMT) == IFBLK || 391 (ip->di_mode & IFMT) == IFCHR || 392 ip->di_mode&(ISUID|ISGID)) { 393 ilist[ilist_index].ino = ino; 394 ilist[ilist_index].mode = ip->di_mode; 395 ilist[ilist_index].uid = ip->di_uid; 396 ilist[ilist_index].gid = ip->di_gid; 397 if (++ilist_index > MAX_ILIST_INDEX()) 398 extend_ilist(); 399 return; 400 } 401 } 402 (void) lookup(ino, 1); 403 } 404 405 void 406 pass2(ip) 407 register struct dinode *ip; 408 { 409 register struct direct *dp; 410 struct dirstuff dirp; 411 struct htab *hp; 412 413 414 if ((ip->di_mode&IFMT) != IFDIR) 415 return; 416 dirp.loc = 0; 417 dirp.ip = ip; 418 gip = ip; 419 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 420 int nmlen; 421 422 if (dp->d_ino == 0) 423 continue; 424 425 hp = lookup(dp->d_ino, 0); 426 if (hp == 0) 427 continue; 428 429 if (dotname(dp)) 430 continue; 431 hp->h_pino = ino; 432 nmlen = strlen(dp->d_name); 433 434 if (strngloc + nmlen + 1 > MAX_STRNGTAB_INDEX()) { 435 if (!extend_strngtab(STRNGTAB_INCR)) { 436 perror("ncheck: can't grow string table\n"); 437 exit(32); 438 } 439 } 440 441 hp->h_name_index = strngloc; 442 (void) strcpy(&strngtab[strngloc], dp->d_name); 443 strngloc += nmlen + 1; 444 } 445 } 446 447 void 448 pass3(ip) 449 register struct dinode *ip; 450 { 451 register struct direct *dp; 452 struct dirstuff dirp; 453 int k; 454 455 if ((ip->di_mode&IFMT) != IFDIR) 456 return; 457 dirp.loc = 0; 458 dirp.ip = ip; 459 gip = ip; 460 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 461 if (aflg == 0 && dotname(dp)) 462 continue; 463 464 if (sflg == 0 && iflg == 0) 465 goto pr; 466 for (k = 0; k < ilist_index && ilist[k].ino != 0; k++) { 467 if (ilist[k].ino == dp->d_ino) { 468 break; 469 } 470 } 471 if (ilist[k].ino == 0) 472 continue; 473 if (mflg) 474 (void) printf("mode %-6o uid %-5ld gid %-5ld ino ", 475 ilist[k].mode, ilist[k].uid, ilist[k].gid); 476 pr: 477 (void) printf("%-5u\t", dp->d_ino); 478 pname(ino, 0); 479 (void) printf("/%s", dp->d_name); 480 if (lookup(dp->d_ino, 0)) 481 (void) printf("/."); 482 (void) printf("\n"); 483 } 484 } 485 486 /* 487 * get next entry in a directory. 488 */ 489 struct direct * 490 dreaddir(dirp) 491 register struct dirstuff *dirp; 492 { 493 register struct direct *dp; 494 daddr_t lbn, d; 495 496 for (;;) { 497 498 if (dirp->loc >= (int)dirp->ip->di_size) 499 return (NULL); 500 if (blkoff(&sblock, dirp->loc) == 0) { 501 502 lbn = lblkno(&sblock, dirp->loc); 503 504 d = bmap(lbn); 505 if (d == 0) 506 return (NULL); 507 508 bread(fsbtodb(&sblock, d), dirp->dbuf, 509 (int)dblksize(&sblock, dirp->ip, (int)lbn)); 510 511 } 512 dp = (struct direct *) 513 (dirp->dbuf + blkoff(&sblock, dirp->loc)); 514 dirp->loc += dp->d_reclen; 515 if (dp->d_ino == 0) { 516 continue; 517 } 518 return (dp); 519 } 520 } 521 522 dotname(dp) 523 register struct direct *dp; 524 { 525 526 if (dp->d_name[0] == '.') { 527 if (dp->d_name[1] == 0 || 528 (dp->d_name[1] == '.' && dp->d_name[2] == 0)) 529 return (1); 530 } 531 return (0); 532 } 533 534 void 535 pname(i, lev) 536 ino_t i; 537 int lev; 538 { 539 register struct htab *hp; 540 541 if (i == UFSROOTINO) 542 return; 543 544 if ((hp = lookup(i, 0)) == 0) { 545 (void) printf("???"); 546 return; 547 } 548 if (lev > 10) { 549 (void) printf("..."); 550 return; 551 } 552 pname(hp->h_pino, ++lev); 553 (void) printf("/%s", &(strngtab[hp->h_name_index])); 554 555 } 556 557 struct htab * 558 lookup(i, ef) 559 ino_t i; 560 int ef; 561 { 562 register struct htab *hp; 563 564 for (hp = &htab[(int)i%hsize]; hp->h_ino; ) { 565 if (hp->h_ino == i) 566 return (hp); 567 if (++hp >= &htab[hsize]) 568 hp = htab; 569 } 570 571 if (ef == 0) 572 return (0); 573 if (++nhent >= hsize) { 574 (void) fprintf(stderr, "ncheck: hsize of %ld is too small\n", 575 hsize); 576 exit(32); 577 } 578 hp->h_ino = i; 579 return (hp); 580 } 581 582 void 583 bread(bno, buf, cnt) 584 diskaddr_t bno; 585 char *buf; 586 int cnt; 587 { 588 register i; 589 int got; 590 591 if (llseek(fi, (offset_t)bno * DEV_BSIZE, 0) == -1) { 592 (void) fprintf(stderr, "ncheck: lseek error %lld\n", 593 (offset_t)bno * DEV_BSIZE); 594 595 for (i = 0; i < cnt; i++) { 596 buf[i] = 0; 597 } 598 599 return; 600 } 601 602 got = read((int)fi, buf, cnt); 603 604 if (got != cnt) { 605 (void) fprintf(stderr, 606 "ncheck: read error at block %lld (wanted %d got %d)\n", 607 bno, cnt, got); 608 609 for (i = 0; i < cnt; i++) 610 buf[i] = 0; 611 } 612 } 613 614 daddr_t 615 bmap(i) 616 daddr_t i; 617 { 618 daddr_t ibuf[MAXNINDIR]; 619 620 if (i < NDADDR) 621 return (gip->di_db[i]); 622 i -= NDADDR; 623 if (i > NINDIR(&sblock)) { 624 (void) fprintf(stderr, "ncheck: %lu - huge directory\n", ino); 625 return ((daddr_t)0); 626 } 627 628 bread(fsbtodb(&sblock, gip->di_ib[0]), (char *)ibuf, sizeof (ibuf)); 629 630 return (ibuf[i]); 631 } 632 633 void 634 usage() 635 { 636 (void) fprintf(stderr, 637 /*CSTYLED*/ 638 "ufs usage: ncheck [-F ufs] [generic options] [-a -i #list -s] [-o m] special\n"); 639 exit(32); 640 } 641 642 643 /* 644 * Extend or create the inode list; 645 * this is used to contains the list of inodes we've been 646 * asked to check using the "-i" flag and to hold the 647 * inode numbers of files which we detect as being 648 * blk|char|setuid|setgid ("-s" flag support). 649 * Preserves contents. 650 */ 651 void 652 extend_ilist() 653 { 654 ilist_size += ILIST_SZ_INCR; 655 ilist = (struct ilist *)realloc(ilist, 656 (ilist_size * sizeof (struct ilist))); 657 658 if (ilist == NULL) { 659 perror("ncheck: not enough memory to grow ilist\n"); 660 exit(32); 661 } 662 } 663 664 /* 665 * Extend or create the string table. 666 * Preserves contents. 667 * Return non-zero for success. 668 */ 669 int 670 extend_strngtab(size) 671 unsigned int size; 672 { 673 strngtab_size += size; 674 strngtab = (char *)realloc(strngtab, strngtab_size); 675 676 return ((int)strngtab); 677 } 678 679 /* 680 * Extend or create a table, throwing away previous 681 * contents. 682 * Return null on failure. 683 */ 684 uchar_t * 685 extend_tbl(tbl, current_size, new_size) 686 uchar_t *tbl; 687 unsigned int *current_size; /* current size */ 688 unsigned int new_size; /* bytes required */ 689 { 690 /* 691 * if we've already allocated tbl, 692 * but it is too small, free it. 693 * we don't realloc because we are throwing 694 * away its contents. 695 */ 696 697 if (tbl && (*current_size < new_size)) { 698 free(tbl); 699 tbl = NULL; 700 } 701 702 if (tbl == NULL) { 703 tbl = (uchar_t *)malloc(new_size); 704 if (tbl == 0) 705 return ((uchar_t *)0); 706 707 *current_size = new_size; 708 } 709 (void) memset(tbl, 0, new_size); 710 711 return (tbl); 712 } 713