xref: /titanic_44/usr/src/cmd/fs.d/ufs/ncheck/ncheck.c (revision 09f67678c27dda8a89f87f1f408a87dd49ceb0e1)
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 2003 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 == MTB_UFS_MAGIC) &&
261 	    ((sblock.fs_version > MTB_UFS_VERSION_1) ||
262 	    (sblock.fs_version < MTB_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 fs is logged, roll the log. */
270 	if (sblock.fs_logbno) {
271 		switch (rl_roll_log(file)) {
272 		case RL_SUCCESS:
273 			/*
274 			 * Reread the superblock.  Rolling the log may have
275 			 * changed it.
276 			 */
277 			bread((diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE);
278 			break;
279 		case RL_SYSERR:
280 			(void) printf("Warning: cannot roll log for %s.  %s\n",
281 				file, strerror(errno));
282 			break;
283 		default:
284 			(void) printf("Warning: cannot roll log for %s.\n",
285 				file);
286 			break;
287 		}
288 	}
289 
290 	itab = (struct dinode *)extend_tbl((uchar_t *)itab, &itab_size,
291 		(unsigned)(sblock.fs_ipg * sizeof (struct dinode)));
292 	if (itab == 0) {
293 		(void) fprintf(stderr,
294 			"ncheck: not enough memory for itab table\n");
295 		nerror++;
296 		return;
297 	}
298 
299 	hsize = sblock.fs_ipg * sblock.fs_ncg - sblock.fs_cstotal.cs_nifree + 1;
300 
301 	htab = (struct htab *)extend_tbl((uchar_t *)htab, &htab_size,
302 		(unsigned)(hsize * sizeof (struct htab)));
303 	if (htab == 0) {
304 		(void) fprintf(stderr,
305 			"ncheck: not enough memory for htab table\n");
306 		nerror++;
307 		return;
308 	}
309 
310 	if (!extend_strngtab(AVG_PATH_LEN * hsize)) {
311 		(void) printf("not enough memory to allocate tables\n");
312 		nerror++;
313 		return;
314 	}
315 	strngloc = 0;
316 
317 	ino = 0;
318 	for (c = 0; c < sblock.fs_ncg; c++) {
319 		bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab,
320 		    (int)(sblock.fs_ipg * sizeof (struct dinode)));
321 		for (j = 0; j < sblock.fs_ipg; j++) {
322 			if (itab[j].di_smode != 0) {
323 				itab[j].di_mode = itab[j].di_smode;
324 				if (itab[j].di_suid != UID_LONG)
325 					itab[j].di_uid = itab[j].di_suid;
326 				if (itab[j].di_sgid != GID_LONG)
327 					itab[j].di_gid = itab[j].di_sgid;
328 				pass1(&itab[j]);
329 			}
330 			ino++;
331 		}
332 	}
333 	ilist[ilist_index++].ino = 0;
334 	if (ilist_index > MAX_ILIST_INDEX())
335 		extend_ilist();
336 	ino = 0;
337 	for (c = 0; c < sblock.fs_ncg; c++) {
338 		bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab,
339 		    (int)(sblock.fs_ipg * sizeof (struct dinode)));
340 		for (j = 0; j < sblock.fs_ipg; j++) {
341 
342 			if (itab[j].di_smode != 0) {
343 				itab[j].di_mode = itab[j].di_smode;
344 				pass2(&itab[j]);
345 			}
346 			ino++;
347 		}
348 	}
349 	ino = 0;
350 	for (c = 0; c < sblock.fs_ncg; c++) {
351 		bread(fsbtodb(&sblock, cgimin(&sblock, c)), (char *)itab,
352 		    (int)(sblock.fs_ipg * sizeof (struct dinode)));
353 		for (j = 0; j < sblock.fs_ipg; j++) {
354 			if (itab[j].di_smode != 0) {
355 				itab[j].di_mode = itab[j].di_smode;
356 				pass3(&itab[j]);
357 			}
358 			ino++;
359 		}
360 	}
361 	(void) close(fi);
362 
363 	/*
364 	 * Clear those elements after inodes specified by "-i" out of
365 	 * ilist.
366 	 */
367 	for (i = iflg; i < ilist_index; i++) {
368 		ilist[i].ino = 0;
369 	}
370 	ilist_index = iflg;
371 }
372 
373 void
374 pass1(ip)
375 	register struct dinode *ip;
376 {
377 	int i;
378 
379 	if (mflg) {
380 		for (i = 0; i < iflg; i++)
381 			if (ino == ilist[i].ino) {
382 				ilist[i].mode = ip->di_mode;
383 				ilist[i].uid = ip->di_uid;
384 				ilist[i].gid = ip->di_gid;
385 			}
386 	}
387 	if ((ip->di_mode & IFMT) != IFDIR) {
388 		if (sflg == 0)
389 			return;
390 		if ((ip->di_mode & IFMT) == IFBLK ||
391 				(ip->di_mode & IFMT) == IFCHR ||
392 				ip->di_mode&(ISUID|ISGID)) {
393 			ilist[ilist_index].ino = ino;
394 			ilist[ilist_index].mode = ip->di_mode;
395 			ilist[ilist_index].uid = ip->di_uid;
396 			ilist[ilist_index].gid = ip->di_gid;
397 			if (++ilist_index > MAX_ILIST_INDEX())
398 				extend_ilist();
399 			return;
400 		}
401 	}
402 	(void) lookup(ino, 1);
403 }
404 
405 void
406 pass2(ip)
407 	register struct dinode *ip;
408 {
409 	register struct direct *dp;
410 	struct dirstuff dirp;
411 	struct htab *hp;
412 
413 
414 	if ((ip->di_mode&IFMT) != IFDIR)
415 		return;
416 	dirp.loc = 0;
417 	dirp.ip = ip;
418 	gip = ip;
419 	for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) {
420 		int nmlen;
421 
422 		if (dp->d_ino == 0)
423 			continue;
424 
425 		hp = lookup(dp->d_ino, 0);
426 		if (hp == 0)
427 			continue;
428 
429 		if (dotname(dp))
430 			continue;
431 		hp->h_pino = ino;
432 		nmlen = strlen(dp->d_name);
433 
434 		if (strngloc + nmlen + 1 > MAX_STRNGTAB_INDEX()) {
435 			if (!extend_strngtab(STRNGTAB_INCR)) {
436 				perror("ncheck: can't grow string table\n");
437 				exit(32);
438 			}
439 		}
440 
441 		hp->h_name_index = strngloc;
442 		(void) strcpy(&strngtab[strngloc], dp->d_name);
443 		strngloc += nmlen + 1;
444 	}
445 }
446 
447 void
448 pass3(ip)
449 	register struct dinode *ip;
450 {
451 	register struct direct *dp;
452 	struct dirstuff dirp;
453 	int k;
454 
455 	if ((ip->di_mode&IFMT) != IFDIR)
456 		return;
457 	dirp.loc = 0;
458 	dirp.ip = ip;
459 	gip = ip;
460 	for (dp = dreaddir(&dirp); dp != NULL; dp = dreaddir(&dirp)) {
461 		if (aflg == 0 && dotname(dp))
462 			continue;
463 
464 		if (sflg == 0 && iflg == 0)
465 			goto pr;
466 		for (k = 0; k < ilist_index && ilist[k].ino != 0; k++) {
467 			if (ilist[k].ino == dp->d_ino) {
468 				break;
469 			}
470 		}
471 		if (ilist[k].ino == 0)
472 			continue;
473 		if (mflg)
474 			(void) printf("mode %-6o uid %-5ld gid %-5ld ino ",
475 			    ilist[k].mode, ilist[k].uid, ilist[k].gid);
476 	pr:
477 		(void) printf("%-5u\t", dp->d_ino);
478 		pname(ino, 0);
479 		(void) printf("/%s", dp->d_name);
480 		if (lookup(dp->d_ino, 0))
481 			(void) printf("/.");
482 		(void) printf("\n");
483 	}
484 }
485 
486 /*
487  * get next entry in a directory.
488  */
489 struct direct *
490 dreaddir(dirp)
491 	register struct dirstuff *dirp;
492 {
493 	register 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 dotname(dp)
523 	register 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(i, lev)
536 	ino_t i;
537 	int lev;
538 {
539 	register 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 
557 struct htab *
558 lookup(i, ef)
559 	ino_t i;
560 	int ef;
561 {
562 	register struct htab *hp;
563 
564 	for (hp = &htab[(int)i%hsize]; hp->h_ino; ) {
565 		if (hp->h_ino == i)
566 			return (hp);
567 		if (++hp >= &htab[hsize])
568 			hp = htab;
569 	}
570 
571 	if (ef == 0)
572 		return (0);
573 	if (++nhent >= hsize) {
574 		(void) fprintf(stderr, "ncheck: hsize of %ld is too small\n",
575 									hsize);
576 		exit(32);
577 	}
578 	hp->h_ino = i;
579 	return (hp);
580 }
581 
582 void
583 bread(bno, buf, cnt)
584 	diskaddr_t bno;
585 	char *buf;
586 	int cnt;
587 {
588 	register i;
589 	int got;
590 
591 	if (llseek(fi, (offset_t)bno * DEV_BSIZE, 0) == -1) {
592 		(void) fprintf(stderr, "ncheck: lseek error %lld\n",
593 		    (offset_t)bno * DEV_BSIZE);
594 
595 		for (i = 0; i < cnt; i++) {
596 			buf[i] = 0;
597 		}
598 
599 		return;
600 	}
601 
602 	got = read((int)fi, buf, cnt);
603 
604 	if (got != cnt) {
605 		(void) fprintf(stderr,
606 		    "ncheck: read error at block %lld (wanted %d got %d)\n",
607 		    bno, cnt, got);
608 
609 		for (i = 0; i < cnt; i++)
610 			buf[i] = 0;
611 	}
612 }
613 
614 daddr_t
615 bmap(i)
616 	daddr_t i;
617 {
618 	daddr_t ibuf[MAXNINDIR];
619 
620 	if (i < NDADDR)
621 		return (gip->di_db[i]);
622 	i -= NDADDR;
623 	if (i > NINDIR(&sblock)) {
624 		(void) fprintf(stderr, "ncheck: %lu - huge directory\n", ino);
625 		return ((daddr_t)0);
626 	}
627 
628 	bread(fsbtodb(&sblock, gip->di_ib[0]), (char *)ibuf, sizeof (ibuf));
629 
630 	return (ibuf[i]);
631 }
632 
633 void
634 usage()
635 {
636 	(void) fprintf(stderr,
637 		/*CSTYLED*/
638 		"ufs usage: ncheck [-F ufs] [generic options] [-a -i #list -s] [-o m] special\n");
639 	exit(32);
640 }
641 
642 
643 /*
644  * Extend or create the inode list;
645  * this is used to contains the list of inodes we've been
646  * asked to check using the "-i" flag and to hold the
647  * inode numbers of files which we detect as being
648  * blk|char|setuid|setgid ("-s" flag support).
649  * Preserves contents.
650  */
651 void
652 extend_ilist()
653 {
654 	ilist_size += ILIST_SZ_INCR;
655 	ilist = (struct ilist *)realloc(ilist,
656 		(ilist_size * sizeof (struct ilist)));
657 
658 	if (ilist == NULL) {
659 		perror("ncheck: not enough memory to grow ilist\n");
660 		exit(32);
661 	}
662 }
663 
664 /*
665  * Extend or create the string table.
666  * Preserves contents.
667  * Return non-zero for success.
668  */
669 int
670 extend_strngtab(size)
671 	unsigned int size;
672 {
673 	strngtab_size += size;
674 	strngtab = (char *)realloc(strngtab, strngtab_size);
675 
676 	return ((int)strngtab);
677 }
678 
679 /*
680  * Extend or create a table, throwing away previous
681  * contents.
682  * Return null on failure.
683  */
684 uchar_t *
685 extend_tbl(tbl, current_size, new_size)
686 	uchar_t *tbl;
687 	unsigned int *current_size;	/* current size */
688 	unsigned int new_size;		/* bytes required */
689 {
690 	/*
691 	 * if we've already allocated tbl,
692 	 * but it is too small, free it.
693 	 * we don't realloc because we are throwing
694 	 * away its contents.
695 	 */
696 
697 	if (tbl && (*current_size < new_size)) {
698 		free(tbl);
699 		tbl = NULL;
700 	}
701 
702 	if (tbl == NULL) {
703 		tbl = (uchar_t *)malloc(new_size);
704 		if (tbl == 0)
705 			return ((uchar_t *)0);
706 
707 		*current_size = new_size;
708 	}
709 	(void) memset(tbl, 0, new_size);
710 
711 	return (tbl);
712 }
713