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