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