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