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) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * ff -- obtain file names from reading filesystem 39 */ 40 41 #define NB 500 42 #define MAXNINDIR (MAXBSIZE / sizeof (daddr32_t)) 43 44 #include <sys/param.h> 45 #include <sys/types.h> 46 #include <sys/mntent.h> 47 #include <sys/vnode.h> 48 #include <sys/fs/ufs_inode.h> 49 #include <sys/stat.h> 50 #include <sys/fs/ufs_fs.h> 51 #include <sys/fs/ufs_fsdir.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <strings.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <unistd.h> 58 #include <pwd.h> 59 #include "roll_log.h" 60 61 #define MIN_PHYS_READ BBSIZE 62 #define DAY (24*60*60) 63 64 65 union { 66 struct fs sblk; 67 char xxx[SBSIZE]; /* because fs is variable length */ 68 } real_fs; 69 #define sblock real_fs.sblk 70 71 struct dinode *itab; /* = (struct dinode *)itab; */ 72 73 struct dinode *gip; 74 75 struct ilist { 76 ino_t ino; 77 ushort_t mode; 78 uid_t uid; 79 gid_t gid; 80 } ilist[NB]; 81 82 struct htab 83 { 84 ino_t h_ino; 85 ino_t h_pino; 86 int h_name_index; /* index into string table */ 87 } *htab; 88 char *strngtab; 89 long hsize; 90 int strngloc; 91 int strngtab_size; 92 #define STRNGTAB_INCR (1024*16) /* amount to grow strngtab */ 93 #define MAX_STRNGTAB_INDEX() (strngtab_size - 1) 94 #define AVG_PATH_LEN 30 /* average (?) length of name */ 95 96 struct dirstuff { 97 int loc; 98 struct dinode *ip; 99 char dbuf[MAXBSIZE]; 100 }; 101 int Aflg = 0; /* accessed in n days */ 102 int Mflg = 0; /* modified in n days */ 103 int Nflg = 0; /* modified more recently than 'file' */ 104 int Cflg = 0; /* changed within n days */ 105 int aflg = 0; /* print the names `.' and `..' */ 106 int sflg = 0; /* print only special files and files with set-user-ID mode */ 107 int Sflg = 0; /* print file size */ 108 int iflg = 0; /* number of inodes being searched for */ 109 int Iflg = 0; /* do not print i-number */ 110 int Lflg = 0; /* supplementary list of multiply linked files */ 111 int mflg = 0; 112 int pflg = 0; /* a prefix exists */ 113 int uflg = 0; /* print the owner's login name */ 114 int fi; 115 ino_t ino; 116 int nhent; 117 int nxfile; 118 int imax; /* highest inode number */ 119 int inode_reads; 120 int passwd_lookups; 121 int Adelay; /* Access delay */ 122 int Asign; /* Access sign */ 123 int Mdelay; /* Modify delay */ 124 int Msign; /* Modify sign */ 125 int Cdelay; /* change delay */ 126 int Csign; /* change sign */ 127 time_t Nage; /* Last modification time of the file */ 128 char *Lname; /* filename for supplementary list */ 129 FILE *Lfile; /* file for supplementary list */ 130 131 /* 132 * Function prototypes 133 */ 134 void check(char *file); 135 void pass1(struct dinode *ip); 136 void pass2(struct dinode *ip); 137 void pass3(struct dinode *ip); 138 struct direct *dreaddir(struct dirstuff *dirp); 139 int dotname(struct direct *dp); 140 void pname(FILE *stream, ino_t i, int lev); 141 struct htab *lookup(ino_t i, int ef); 142 void bread(diskaddr_t bno, char *buf, int cnt); 143 diskaddr_t bmap(diskaddr_t i); 144 struct dinode *ginode(ino_t inumber); 145 char *user_name(int uid); 146 int cmp(int a, int b, int s); 147 time_t mod_time(char *file); 148 void out_multilinks(); 149 void usage(); 150 int extend_strngtab(unsigned int size); 151 152 long atol(); 153 offset_t llseek(); 154 char *strcpy(); 155 156 char *prefix; 157 time_t Today; 158 int nerror; 159 160 161 extern int optind; 162 extern char *optarg; 163 164 char *subopts [] = { 165 #define A_FLAG 0 166 "a", 167 #define M_FLAG 1 168 "m", 169 #define S_FLAG 2 170 "s", 171 NULL 172 }; 173 174 int 175 main(argc, argv) 176 int argc; 177 char *argv[]; 178 { 179 long n; 180 int opt; 181 char *suboptions, *value; 182 char *p; 183 int first = 0; 184 185 Today = time((time_t *)0); 186 while ((opt = getopt(argc, argv, "Ia:c:i:lm:n:o:p:su")) != EOF) { 187 switch (opt) { 188 189 case 'a': 190 Aflg++; 191 Adelay = atoi(optarg); 192 Asign = optarg[0]; 193 break; 194 195 case 'I': 196 Iflg++; 197 break; 198 199 case 'c': 200 Cflg++; 201 Cdelay = atoi(optarg); 202 Csign = optarg[0]; 203 break; 204 205 case 'l': 206 Lflg++; 207 Lname = tmpnam((char *)0); 208 if ((Lfile = fopen(Lname, "w+")) == NULL) { 209 perror("open"); 210 (void) fprintf(stderr, 211 "ff: unable to open temp file, -l ignored\n"); 212 Lflg = 0; 213 } 214 break; 215 216 case 'm': 217 Mflg++; 218 Mdelay = atoi(optarg); 219 Msign = optarg[0]; 220 break; 221 222 case 'n': 223 Nflg++; 224 Nage = mod_time(optarg); 225 break; 226 227 case 'o': 228 /* 229 * ufs specific options. 230 */ 231 suboptions = optarg; 232 233 if (*suboptions == '\0') 234 usage(); 235 while (*suboptions != '\0') { 236 switch ((getsubopt(&suboptions, 237 subopts, &value))) { 238 239 case A_FLAG: 240 aflg++; 241 break; 242 243 case M_FLAG: 244 mflg++; 245 break; 246 247 case S_FLAG: 248 sflg++; 249 break; 250 251 default: 252 usage(); 253 } 254 } 255 break; 256 257 case 'i': 258 while ((p = (char *)strtok(((first++ == 0) ? 259 optarg: ((char *)0)), ", ")) != NULL) { 260 if ((n = atoi(p)) == 0) 261 break; 262 ilist[iflg].ino = n; 263 nxfile = iflg; 264 iflg++; 265 } 266 break; 267 268 case 'p': 269 prefix = optarg; 270 pflg++; 271 break; 272 273 case 's': 274 Sflg++; 275 break; 276 277 case 'u': 278 uflg++; 279 break; 280 281 case '?': 282 usage(); 283 } 284 } 285 argc -= optind; 286 argv = &argv[optind]; 287 while (argc--) { 288 check(*argv); 289 argv++; 290 } 291 if (Lflg) { 292 out_multilinks(); 293 } 294 if (nerror) 295 return (32); 296 return (0); 297 } 298 299 void 300 check(char *file) 301 { 302 register int i, j, c; 303 304 fi = open64(file, 0); 305 if (fi < 0) { 306 (void) fprintf(stderr, "ff: cannot open %s\n", file); 307 nerror++; 308 return; 309 } 310 nhent = 0; 311 (void) printf("%s:\n", file); 312 sync(); 313 bread(SBLOCK, (char *)&sblock, SBSIZE); 314 if ((sblock.fs_magic != FS_MAGIC) && 315 (sblock.fs_magic != MTB_UFS_MAGIC)) { 316 (void) fprintf(stderr, "%s: not a ufs file system\n", file); 317 nerror++; 318 return; 319 } 320 321 if (sblock.fs_magic == FS_MAGIC && 322 (sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 && 323 sblock.fs_version != UFS_VERSION_MIN)) { 324 (void) fprintf(stderr, "%s: unrecognized version of UFS: %d\n", 325 file, sblock.fs_version); 326 nerror++; 327 return; 328 } 329 330 if (sblock.fs_magic == MTB_UFS_MAGIC && 331 (sblock.fs_version > MTB_UFS_VERSION_1 || 332 sblock.fs_version < MTB_UFS_VERSION_MIN)) { 333 (void) fprintf(stderr, "%s: unrecognized version of UFS: %d\n", 334 file, sblock.fs_version); 335 nerror++; 336 return; 337 } 338 339 /* If fs is logged, roll the log. */ 340 if (sblock.fs_logbno) { 341 switch (rl_roll_log(file)) { 342 case RL_SUCCESS: 343 /* 344 * Reread the superblock. Rolling the log may have 345 * changed it. 346 */ 347 bread(SBLOCK, (char *)&sblock, SBSIZE); 348 break; 349 case RL_SYSERR: 350 (void) printf("Warning: Cannot roll log for %s. %s\n", 351 file, strerror(errno)); 352 break; 353 default: 354 (void) printf("Warning: Cannot roll log for %s.\n ", 355 file); 356 break; 357 } 358 } 359 360 361 itab = (struct dinode *)calloc(sblock.fs_ipg, sizeof (struct dinode)); 362 imax = sblock.fs_ncg * sblock.fs_ipg; 363 364 hsize = sblock.fs_ipg * sblock.fs_ncg - sblock.fs_cstotal.cs_nifree + 1; 365 htab = (struct htab *)calloc(hsize, sizeof (struct htab)); 366 367 if (!extend_strngtab(AVG_PATH_LEN * hsize)) { 368 (void) printf("not enough memory to allocate tables\n"); 369 nerror++; 370 return; 371 } 372 strngloc = 0; 373 374 if ((itab == NULL) || (htab == NULL)) { 375 (void) printf("not enough memory to allocate tables\n"); 376 nerror++; 377 return; 378 } 379 ino = 0; 380 for (c = 0; c < sblock.fs_ncg; c++) { 381 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 382 (int)(sblock.fs_ipg * sizeof (struct dinode))); 383 for (j = 0; j < sblock.fs_ipg; j++) { 384 if (itab[j].di_smode != 0) { 385 itab[j].di_mode = itab[j].di_smode; 386 if (itab[j].di_suid != (o_uid_t)UID_LONG) 387 itab[j].di_uid = (unsigned int)itab[j].di_suid; 388 if (itab[j].di_sgid != GID_LONG) 389 itab[j].di_gid = (unsigned int)itab[j].di_sgid; 390 pass1(&itab[j]); 391 } 392 ino++; 393 } 394 } 395 ilist[nxfile+1].ino = 0; 396 ino = 0; 397 for (c = 0; c < sblock.fs_ncg; c++) { 398 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 399 (int)(sblock.fs_ipg * sizeof (struct dinode))); 400 for (j = 0; j < sblock.fs_ipg; j++) { 401 if (itab[j].di_smode != 0) { 402 itab[j].di_mode = itab[j].di_smode; 403 pass2(&itab[j]); 404 } 405 ino++; 406 } 407 } 408 ino = 0; 409 for (c = 0; c < sblock.fs_ncg; c++) { 410 bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab, 411 (int)(sblock.fs_ipg * sizeof (struct dinode))); 412 for (j = 0; j < sblock.fs_ipg; j++) { 413 if (itab[j].di_smode != 0) { 414 itab[j].di_mode = itab[j].di_smode; 415 pass3(&itab[j]); 416 } 417 ino++; 418 } 419 } 420 (void) close(fi); 421 for (i = iflg; i < NB; i++) 422 ilist[i].ino = 0; 423 nxfile = iflg; 424 free(itab); 425 free(htab); 426 free(strngtab); 427 } 428 429 void 430 pass1(struct dinode *ip) 431 { 432 int i; 433 434 if (mflg) 435 for (i = 0; i < iflg; i++) 436 if (ino == ilist[i].ino) { 437 ilist[i].mode = ip->di_mode; 438 ilist[i].uid = ip->di_uid; 439 ilist[i].gid = ip->di_gid; 440 } 441 if ((ip->di_mode & IFMT) != IFDIR) { 442 if (sflg == 0 || nxfile >= NB) 443 return; 444 if ((ip->di_mode&IFMT) == IFBLK || 445 (ip->di_mode&IFMT) == IFCHR || ip->di_mode&(ISUID|ISGID)) { 446 ilist[nxfile].ino = ino; 447 ilist[nxfile].mode = ip->di_mode; 448 ilist[nxfile].uid = ip->di_uid; 449 ilist[nxfile++].gid = ip->di_gid; 450 return; 451 } 452 } 453 (void) lookup(ino, 1); 454 } 455 456 void 457 pass2(struct dinode *ip) 458 { 459 register struct direct *dp; 460 struct dirstuff dirp; 461 struct htab *hp; 462 463 if ((ip->di_mode&IFMT) != IFDIR) 464 return; 465 dirp.loc = 0; 466 dirp.ip = ip; 467 gip = ip; 468 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 469 int nmlen; 470 471 if (dp->d_ino == 0) 472 continue; 473 hp = lookup(dp->d_ino, 0); 474 if (hp == 0) 475 continue; 476 if (dotname(dp)) 477 continue; 478 hp->h_pino = ino; 479 nmlen = strlen(dp->d_name); 480 481 if (strngloc + nmlen + 1 > MAX_STRNGTAB_INDEX()) { 482 if (!extend_strngtab(STRNGTAB_INCR)) { 483 perror("ncheck: can't grow string table\n"); 484 exit(32); 485 } 486 } 487 488 hp->h_name_index = strngloc; 489 (void) strcpy(&strngtab[strngloc], dp->d_name); 490 strngloc += nmlen + 1; 491 } 492 } 493 494 void 495 pass3(struct dinode *ip) 496 { 497 register struct direct *dp; 498 struct dirstuff dirp; 499 struct dinode *dip; 500 int k; 501 502 if ((ip->di_mode&IFMT) != IFDIR) 503 return; 504 dirp.loc = 0; 505 dirp.ip = ip; 506 gip = ip; 507 for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) { 508 if (aflg == 0 && dotname(dp)) 509 continue; 510 if (sflg == 0 && iflg == 0) 511 goto pr; 512 for (k = 0; ilist[k].ino != 0; k++) 513 if (ilist[k].ino == dp->d_ino) 514 break; 515 if (ilist[k].ino == 0) 516 continue; 517 if (mflg) 518 (void) printf("mode %-6o uid %-5ld gid %-5ld ino ", 519 ilist[k].mode, ilist[k].uid, ilist[k].gid); 520 pr: 521 if (Sflg || uflg || Aflg || Mflg || Cflg || Nflg || Lflg) 522 dip = ginode(dp->d_ino); 523 if ((!Aflg || 524 cmp((Today - dip->di_un.di_icom.ic_atime)/DAY, Adelay, 525 Asign)) && 526 (!Mflg || cmp((Today - dip->di_un.di_icom.ic_mtime)/DAY, 527 Mdelay, Msign)) && 528 (!Cflg || cmp((Today - dip->di_un.di_icom.ic_mtime)/DAY, 529 Cdelay, Csign)) && 530 (!Nflg || cmp(dip->di_un.di_icom.ic_mtime, Nage, '+'))) { 531 if (Iflg == 0) 532 (void) printf("%-5u\t", dp->d_ino); 533 pname(stdout, ino, 0); 534 (void) printf("/%s", dp->d_name); 535 if (lookup(dp->d_ino, 0)) 536 (void) printf("/."); 537 if (Sflg) 538 (void) printf("\t%6lld", 539 dip->di_un.di_icom.ic_lsize); 540 if (uflg) 541 (void) printf("\t%s", 542 user_name(dip->di_un.di_icom.ic_uid)); 543 (void) printf("\n"); 544 if (Lflg && (dip->di_un.di_icom.ic_nlink > 1)) { 545 (void) fprintf(Lfile, "%-5u\t", 546 dp->d_ino); 547 (void) fprintf(Lfile, "%-5u\t", 548 dip->di_un.di_icom.ic_nlink); 549 pname(Lfile, ino, 0); 550 (void) fprintf(Lfile, "/%s\n", dp->d_name); 551 } 552 } 553 } 554 } 555 556 557 558 /* 559 * get next entry in a directory. 560 */ 561 struct direct * 562 dreaddir(struct dirstuff *dirp) 563 { 564 register struct direct *dp; 565 diskaddr_t lbn, d; 566 567 for (;;) { 568 if (dirp->loc >= (int)dirp->ip->di_size) 569 return (NULL); 570 if (blkoff(&sblock, dirp->loc) == 0) { 571 lbn = lblkno(&sblock, dirp->loc); 572 d = bmap(lbn); 573 if (d == 0) 574 return (NULL); 575 bread(fsbtodb(&sblock, d), dirp->dbuf, 576 (int)dblksize(&sblock, dirp->ip, (int)lbn)); 577 } 578 dp = (struct direct *) 579 (dirp->dbuf + blkoff(&sblock, dirp->loc)); 580 dirp->loc += dp->d_reclen; 581 if (dp->d_ino == 0) 582 continue; 583 return (dp); 584 } 585 } 586 587 int 588 dotname(struct direct *dp) 589 { 590 591 if (dp->d_name[0] == '.') 592 if (dp->d_name[1] == 0 || 593 (dp->d_name[1] == '.' && dp->d_name[2] == 0)) 594 return (1); 595 return (0); 596 } 597 598 void 599 pname(FILE *stream, ino_t i, int lev) 600 { 601 register struct htab *hp; 602 603 if (i == UFSROOTINO) 604 return; 605 if ((hp = lookup(i, 0)) == 0) { 606 (void) fprintf(stream, "???"); 607 return; 608 } 609 if (lev > 10) { 610 (void) fprintf(stream, "..."); 611 return; 612 } 613 pname(stream, hp->h_pino, ++lev); 614 if (pflg) 615 (void) fprintf(stream, "%s/%s", prefix, 616 &(strngtab[hp->h_name_index])); 617 else 618 (void) fprintf(stream, "/%s", 619 &(strngtab[hp->h_name_index])); 620 } 621 622 struct htab * 623 lookup(ino_t i, int ef) 624 { 625 register struct htab *hp; 626 627 for (hp = &htab[(int)i%hsize]; hp->h_ino; ) { 628 if (hp->h_ino == i) 629 return (hp); 630 if (++hp >= &htab[hsize]) 631 hp = htab; 632 } 633 if (ef == 0) 634 return (0); 635 if (++nhent >= hsize) { 636 (void) fprintf(stderr, 637 "ff: hsize of %ld is too small\n", hsize); 638 exit(32); 639 } 640 hp->h_ino = i; 641 return (hp); 642 } 643 644 void 645 bread(diskaddr_t bno, char *buf, int cnt) 646 { 647 register i; 648 int got; 649 offset_t offset; 650 651 offset = (offset_t)bno * DEV_BSIZE; 652 if (llseek(fi, offset, 0) == (offset_t)-1) { 653 (void) fprintf(stderr, 654 "ff: llseek error %lx %lx\n", 655 ((long *)&offset)[0], ((long *)&offset)[1]); 656 for (i = 0; i < cnt; i++) 657 buf[i] = 0; 658 return; 659 } 660 661 got = read((int)fi, buf, cnt); 662 if (got != cnt) { 663 perror("read"); 664 (void) fprintf(stderr, 665 "ff: (wanted %d got %d blk %lld)\n", cnt, got, bno); 666 for (i = 0; i < cnt; i++) 667 buf[i] = 0; 668 } 669 } 670 671 diskaddr_t 672 bmap(diskaddr_t i) 673 { 674 daddr32_t ibuf[MAXNINDIR]; 675 676 if (i < NDADDR) 677 return ((diskaddr_t)gip->di_db[i]); 678 i -= NDADDR; 679 if (i > NINDIR(&sblock)) { 680 (void) fprintf(stderr, "ff : %lu - huge directory\n", ino); 681 return ((diskaddr_t)0); 682 } 683 bread(fsbtodb(&sblock, gip->di_ib[0]), (char *)ibuf, sizeof (ibuf)); 684 return ((diskaddr_t)ibuf[i]); 685 } 686 687 struct dinode * 688 ginode(ino_t inumber) 689 { 690 diskaddr_t iblk; 691 diskaddr_t dblk; 692 int ioff; 693 static diskaddr_t curr_dblk; 694 static char buf[MIN_PHYS_READ]; 695 struct dinode *ibuf; 696 697 if (inumber < UFSROOTINO || (int)inumber > imax) { 698 (void) fprintf(stderr, 699 "bad inode number %ld to ginode\n", inumber); 700 exit(32); 701 } 702 iblk = itod(&sblock, (int)inumber); 703 dblk = fsbtodb(&sblock, iblk); 704 ioff = itoo(&sblock, (int)inumber); 705 if (dblk != curr_dblk) { 706 bread(dblk, &buf[0], sizeof (buf)); 707 curr_dblk = dblk; 708 inode_reads++; 709 } 710 ibuf = (struct dinode *)&buf[0]; 711 ibuf += ioff; 712 return (ibuf); 713 } 714 715 #define HASHNAMESIZE 16 716 717 struct name_ent { 718 struct name_ent *name_nxt; 719 int name_uid; 720 char *name_string; 721 }; 722 struct name_ent *hashtable[HASHNAMESIZE]; 723 724 char * 725 user_name(int uid) 726 { 727 int h_index; 728 struct name_ent *hp; 729 struct passwd *pwent; 730 731 h_index = uid % HASHNAMESIZE; 732 for (hp = hashtable[h_index]; hp != NULL; hp = hp->name_nxt) { 733 if (hp->name_uid == uid) { 734 return (hp->name_string); 735 } 736 } 737 hp = (struct name_ent *)calloc(1, sizeof (struct name_ent)); 738 hp->name_nxt = hashtable[h_index]; 739 hp->name_uid = uid; 740 hashtable[h_index] = hp; 741 if ((pwent = getpwuid(uid)) == NULL) { 742 hp->name_string = "unknown"; 743 } else { 744 hp->name_string = (char *)strdup(pwent->pw_name); 745 } 746 passwd_lookups++; 747 748 return (hp->name_string); 749 } 750 751 int 752 cmp(int a, int b, int s) 753 { 754 if (s == '+') 755 return (a > b); 756 if (s == '-') 757 return (a < -(b)); 758 return (a == b); 759 } 760 761 /* 762 * We can't do this one by reading the disk directly, since there 763 * is no guarantee that the file is even on a local disk. 764 */ 765 time_t 766 mod_time(char *file) 767 { 768 struct stat64 stat_buf; 769 770 if (stat64(file, &stat_buf) < 0) { 771 (void) fprintf(stderr, "ff: can't stat '%s' - ignored\n", file); 772 return (0); 773 } 774 return (stat_buf.st_mtime); 775 } 776 777 void 778 out_multilinks() 779 { 780 int length; 781 782 if ((length = fseek(Lfile, 0L, 2)) < 0) { 783 perror("fseek"); 784 exit(32); 785 } else 786 if ((length = ftell(Lfile)) > 0) { 787 (void) fprintf(stdout, 788 "\nmultilink files\nIno\tLinks\tPathname\n\n"); 789 rewind(Lfile); 790 while (length-- > 0) 791 (void) putc(getc(Lfile), stdout); 792 } else 793 (void) fprintf(stdout, "No multilink files\n"); 794 (void) fclose(Lfile); 795 } 796 797 void 798 usage() 799 { 800 (void) fprintf(stderr, 801 "ufs usage: ff [-F ufs] [generic options] [-o a,m,s] special\n"); 802 exit(32); 803 } 804 805 /* 806 * Extend or create the string table. 807 * Preserves contents. 808 * Return non-zero for success. 809 */ 810 int 811 extend_strngtab(unsigned int size) 812 { 813 strngtab_size += size; 814 strngtab = (char *)realloc(strngtab, strngtab_size); 815 816 return ((int)strngtab); 817 } 818