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
61union {
62	struct	fs	sblk;
63	char xxx[SBSIZE];	/* because fs is variable length */
64} real_fs;
65#define	sblock real_fs.sblk
66
67struct	dinode	*itab;
68unsigned itab_size;
69
70
71struct 	dinode	*gip;
72
73/* inode list */
74struct ilist {
75	ino_t	ino;
76	ushort_t	mode;
77	uid_t	uid;
78	gid_t	gid;
79} *ilist;
80int ilist_size = 0;	/* size of ilist[] */
81int 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
85struct	htab
86{
87	ino_t	h_ino;
88	ino_t	h_pino;
89	int	h_name_index;		/* index into string table */
90} *htab;
91unsigned htab_size;		/* how much malloc'd for htab */
92
93/*
94 * string table: used to hold filenames.
95 */
96char *strngtab;
97int strngloc;
98int 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
103long hsize;
104
105struct dirstuff {
106	int loc;
107	struct dinode *ip;
108	char dbuf[MAXBSIZE];
109};
110
111
112int	aflg = 0;
113int	sflg = 0;
114int	iflg = 0; /* number of inodes being searched for */
115int	mflg = 0;
116int	fi;
117ino_t	ino;
118int	nhent;
119
120int	nerror;
121
122long	atol();
123daddr_t	bmap(daddr_t);
124void	bread(diskaddr_t bno, char *buf, int cnt);
125void	check(char *file);
126int	dotname(struct direct *dp);
127offset_t llseek();
128struct htab *lookup(ino_t i, int ef);
129void	pass1(struct dinode *ip);
130void	pass2(struct dinode *ip);
131void	pass3(struct dinode *ip);
132void	pname(ino_t i, int lev);
133char 	*strcpy();
134void	usage();
135struct direct *dreaddir();
136void extend_ilist();
137int extend_strngtab(unsigned int size);
138uchar_t *extend_tbl(uchar_t *tbl, unsigned int *current_size,
139	unsigned int new_size);
140
141extern int	optind;
142extern char	*optarg;
143
144char *subopts [] = {
145#define	M_FLAG		0
146	"m",
147	NULL
148	};
149
150int
151main(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
235void
236check(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
379void
380pass1(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
410void
411pass2(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
451void
452pass3(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 */
492struct direct *
493dreaddir(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
524int
525dotname(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
536void
537pname(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
557struct htab *
558lookup(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
580void
581bread(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
609daddr_t
610bmap(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
627void
628usage()
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 */
645void
646extend_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 */
663int
664extend_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 */
677uchar_t *
678extend_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