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