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