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