xref: /titanic_50/usr/src/cmd/fs.d/ufs/fsck/utilities.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * provided that: (1) source distributions retain this entire copyright
15  * notice and comment, and (2) distributions including binaries display
16  * the following acknowledgement:  ``This product includes software
17  * developed by the University of California, Berkeley and its contributors''
18  * in the documentation or other materials provided with the distribution
19  * and in all advertising materials mentioning features or use of this
20  * software. Neither the name of the University nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <stdio.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/mntent.h>
34 #include <sys/filio.h>
35 
36 #define	bcopy(f, t, n)    memcpy(t, f, n)
37 #define	bzero(s, n)	memset(s, 0, n)
38 #define	bcmp(s, d, n)	memcmp(s, d, n)
39 
40 #define	index(s, r)	strchr(s, r)
41 #define	rindex(s, r)	strrchr(s, r)
42 
43 #include <sys/fs/ufs_fs.h>
44 #include <sys/vnode.h>
45 #include <sys/fs/ufs_inode.h>
46 #include <sys/fs/ufs_acl.h>
47 #define	_KERNEL
48 #include <sys/fs/ufs_fsdir.h>
49 #undef _KERNEL
50 #include <sys/mnttab.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/signal.h>
54 #include <string.h>
55 #include <ctype.h>
56 #include "fsck.h"
57 #include <sys/vfstab.h>
58 #include <sys/lockfs.h>
59 #include <errno.h>
60 
61 int64_t	diskreads, totalreads;	/* Disk cache statistics */
62 offset_t	llseek();
63 char	*malloc();
64 char	*mount_point = NULL;
65 
66 extern int	mflag;
67 extern uint_t largefile_count;
68 
69 static struct bufarea *alloc_bufarea();
70 
71 ftypeok(dp)
72 	struct dinode *dp;
73 {
74 	switch (dp->di_mode & IFMT) {
75 
76 	case IFDIR:
77 	case IFREG:
78 	case IFBLK:
79 	case IFCHR:
80 	case IFLNK:
81 	case IFSOCK:
82 	case IFIFO:
83 	case IFSHAD:
84 	case IFATTRDIR:
85 		return (1);
86 
87 	default:
88 		if (debug)
89 			printf("bad file type 0%o\n", dp->di_mode);
90 		return (0);
91 	}
92 }
93 
94 acltypeok(dp)
95 	struct dinode *dp;
96 {
97 	if (CHECK_ACL_ALLOWED(dp->di_mode & IFMT))
98 		return (1);
99 
100 	if (debug)
101 		printf("bad file type for acl 0%o\n", dp->di_mode);
102 	return (0);
103 }
104 
105 reply(question)
106 	char *question;
107 {
108 	char line[80];
109 
110 	if (preen)
111 		pfatal("INTERNAL ERROR: GOT TO reply()");
112 
113 	if (mflag) {
114 		printf("\n");
115 		printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
116 			devname);
117 		exit(39);
118 	}
119 
120 	printf("\n%s? ", question);
121 	if (nflag || fswritefd < 0) {
122 		printf(" no\n\n");
123 		iscorrupt = 1;		/* known to be corrupt */
124 		return (0);
125 	}
126 	if (yflag) {
127 		printf(" yes\n\n");
128 		return (1);
129 	}
130 	if (getline(stdin, line, sizeof (line)) == EOF)
131 		errexit("\n");
132 	printf("\n");
133 	if (line[0] == 'y' || line[0] == 'Y')
134 		return (1);
135 	else {
136 		iscorrupt = 1;		/* known to be corrupt */
137 		return (0);
138 	}
139 }
140 
141 getline(fp, loc, maxlen)
142 	FILE *fp;
143 	char *loc;
144 {
145 	int n;
146 	char *p, *lastloc;
147 
148 	p = loc;
149 	lastloc = &p[maxlen-1];
150 	while ((n = getc(fp)) != '\n') {
151 		if (n == EOF)
152 			return (EOF);
153 		if (!isspace(n) && p < lastloc)
154 			*p++ = n;
155 	}
156 	*p = 0;
157 	return (p - loc);
158 }
159 /*
160  * Malloc buffers and set up cache.
161  */
162 bufinit()
163 {
164 	struct bufarea *bp;
165 	int bufcnt, i;
166 	char *bufp;
167 
168 	bufp = malloc((unsigned int)sblock.fs_bsize);
169 	if (bufp == 0)
170 		errexit("cannot allocate buffer pool\n");
171 	cgblk.b_un.b_buf = bufp;
172 	initbarea(&cgblk);
173 	bufhead.b_next = bufhead.b_prev = &bufhead;
174 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
175 	if (bufcnt < MINBUFS)
176 		bufcnt = MINBUFS;
177 	for (i = 0; i < bufcnt; i++) {
178 		bp = (struct bufarea *)malloc(sizeof (struct bufarea));
179 		bufp = malloc((unsigned int)sblock.fs_bsize);
180 		if (bp == NULL || bufp == NULL) {
181 			if (bp)
182 				free((char *)bp);
183 			if (bufp)
184 				free(bufp);
185 			if (i >= MINBUFS)
186 				break;
187 			errexit("cannot allocate buffer pool\n");
188 		}
189 		bp->b_un.b_buf = bufp;
190 		bp->b_prev = &bufhead;
191 		bp->b_next = bufhead.b_next;
192 		bufhead.b_next->b_prev = bp;
193 		bufhead.b_next = bp;
194 		initbarea(bp);
195 	}
196 	bufhead.b_size = i;	/* save number of buffers */
197 	pbp = pdirbp = NULL;
198 }
199 
200 /*
201  * Manage a cache of directory blocks.
202  */
203 struct bufarea *
204 getdatablk(blkno, size)
205 	daddr32_t blkno;
206 	int size;
207 {
208 	struct bufarea *bp;
209 
210 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
211 		if (bp->b_bno == fsbtodb(&sblock, blkno))
212 			goto foundit;
213 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
214 		if ((bp->b_flags & B_INUSE) == 0)
215 			break;
216 	if (bp == &bufhead) {
217 		bp = alloc_bufarea();
218 		if (bp == NULL)
219 			errexit("deadlocked buffer pool\n");
220 	}
221 	getblk(bp, blkno, size);
222 	/* fall through */
223 foundit:
224 	totalreads++;
225 	bp->b_cnt++;
226 	/*
227 	 * Move the buffer to head of link-list if it isn't
228 	 * already there.
229 	 */
230 	if (bufhead.b_next != bp) {
231 		bp->b_prev->b_next = bp->b_next;
232 		bp->b_next->b_prev = bp->b_prev;
233 		bp->b_prev = &bufhead;
234 		bp->b_next = bufhead.b_next;
235 		bufhead.b_next->b_prev = bp;
236 		bufhead.b_next = bp;
237 	}
238 	bp->b_flags |= B_INUSE;
239 	return (bp);
240 }
241 
242 int
243 brelse(struct bufarea *bp)
244 {
245 	bp->b_cnt--;
246 	if (bp->b_cnt == 0) {
247 		bp->b_flags &= ~B_INUSE;
248 	}
249 }
250 
251 struct bufarea *
252 getblk(bp, blk, size)
253 	struct bufarea *bp;
254 	daddr32_t blk;
255 	int size;
256 {
257 	diskaddr_t dblk;
258 
259 	dblk = fsbtodb(&sblock, blk);
260 	if (bp->b_bno == dblk)
261 		return (bp);
262 	flush(fswritefd, bp);
263 	diskreads++;
264 	bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, (long)size);
265 	bp->b_bno = dblk;
266 	bp->b_size = size;
267 	return (bp);
268 }
269 
270 flush(fd, bp)
271 	int fd;
272 	struct bufarea *bp;
273 {
274 	int i, j;
275 	caddr_t sip;
276 	long size;
277 
278 	if (!bp->b_dirty)
279 		return;
280 	if (bp->b_errs != 0)
281 		pfatal("WRITING ZERO'ED BLOCK %lld TO DISK\n", bp->b_bno);
282 	bp->b_dirty = 0;
283 	bp->b_errs = 0;
284 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
285 	if (bp != &sblk)
286 		return;
287 	sip = (caddr_t)sblock.fs_u.fs_csp;
288 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
289 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
290 		    sblock.fs_cssize - i : sblock.fs_bsize;
291 		bwrite(fswritefd, sip,
292 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
293 		    size);
294 		sip += size;
295 	}
296 }
297 
298 rwerror(mesg, blk)
299 	char *mesg;
300 	diskaddr_t blk;
301 {
302 
303 	if (preen == 0)
304 		printf("\n");
305 	pfatal("CANNOT %s: BLK %lld", mesg, blk);
306 	if (reply("CONTINUE") == 0)
307 		errexit("Program terminated\n");
308 }
309 
310 ckfini()
311 {
312 	struct bufarea *bp, *nbp;
313 	int cnt = 0;
314 
315 	/*
316 	 * Mark the filesystem bad if a re-check is required.
317 	 */
318 	if (dirholes && havesb) {
319 		sblock.fs_clean = FSBAD;
320 		sblock.fs_state = -(FSOKAY - (long)sblock.fs_time);
321 		sbdirty();
322 	}
323 	flush(fswritefd, &sblk);
324 	if (havesb && sblk.b_bno != SBOFF / dev_bsize) {
325 		sblk.b_bno = SBOFF / dev_bsize;
326 		sbdirty();
327 		flush(fswritefd, &sblk);
328 	}
329 	flush(fswritefd, &cgblk);
330 	if (cgblk.b_un.b_buf) {
331 		free(cgblk.b_un.b_buf);
332 		cgblk.b_un.b_buf = NULL;
333 	}
334 	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
335 		cnt++;
336 		flush(fswritefd, bp);
337 		nbp = bp->b_prev;
338 		free(bp->b_un.b_buf);
339 		free((char *)bp);
340 	}
341 	pbp = pdirbp = NULL;
342 	if (bufhead.b_size != cnt)
343 		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
344 	if (debug)
345 		printf("cache missed %d of %d (%d%%)\n",
346 		    diskreads, totalreads,
347 		    totalreads ? diskreads * 100 / totalreads : 0);
348 	(void) close(fsreadfd);
349 	(void) close(fswritefd);
350 }
351 
352 bread(fd, buf, blk, size)
353 	int fd;
354 	char *buf;
355 	diskaddr_t blk;
356 	long size;
357 {
358 	char *cp;
359 	int	i;
360 	int errs;
361 	offset_t offset = ldbtob(blk);
362 	offset_t addr;
363 
364 	if (debug && (blk < SBLOCK)) {
365 		char msg[256];
366 		sprintf(msg, "WARNING: fsck bread() passed blkno < %d (%ld)\n",
367 		    SBLOCK, blk);
368 		printf(msg);
369 	}
370 	if (llseek(fd, offset, 0) < 0) {
371 		rwerror("SEEK", blk);
372 	} else if (read(fd, buf, (int)size) == size)
373 		return (0);
374 	rwerror("READ", blk);
375 	if (llseek(fd, offset, 0) < 0) {
376 		rwerror("SEEK", blk);
377 	}
378 	errs = 0;
379 	bzero(buf, (size_t)size);
380 	pwarn("THE FOLLOWING SECTORS COULD NOT BE READ:");
381 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
382 		addr = ldbtob(blk + i);
383 		if (llseek(fd, addr, SEEK_CUR) < 0 ||
384 		    read(fd, cp, (int)secsize) < 0) {
385 			printf(" %d", blk + i);
386 			errs++;
387 		}
388 	}
389 	printf("\n");
390 	return (errs);
391 }
392 
393 bwrite(fd, buf, blk, size)
394 	int fd;
395 	char *buf;
396 	diskaddr_t blk;
397 	long size;
398 {
399 	int	i;
400 	int n;
401 	char *cp;
402 	offset_t offset = ldbtob(blk);
403 	offset_t addr;
404 
405 	if (fd < 0)
406 		return;
407 	if (blk < SBLOCK) {
408 		char msg[256];
409 		sprintf(msg,
410 		    "WARNING: Attempt to write illegal blkno %lld on %s\n",
411 		    blk, devname);
412 		if (debug)
413 			printf(msg);
414 		return;
415 	}
416 	if (llseek(fd, offset, 0) < 0) {
417 		rwerror("SEEK", blk);
418 	} else if (write(fd, buf, (int)size) == size) {
419 		fsmodified = 1;
420 		return;
421 	}
422 	rwerror("WRITE", blk);
423 	if (llseek(fd, offset, 0) < 0) {
424 		rwerror("SEEK", blk);
425 	}
426 	pwarn("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
427 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
428 		n = 0;
429 		addr = ldbtob(blk + i);
430 		if (llseek(fd, addr, SEEK_CUR) < 0 ||
431 		    (n = write(fd, cp, DEV_BSIZE)) < 0) {
432 			printf(" %d", blk + i);
433 		} else if (n > 0) {
434 			fsmodified = 1;
435 		}
436 
437 	}
438 	printf("\n");
439 }
440 
441 /*
442  * allocate a data block with the specified number of fragments
443  */
444 daddr32_t
445 allocblk(frags)
446 	int frags;
447 {
448 	int i, j, k;
449 
450 	if (frags <= 0 || frags > sblock.fs_frag)
451 		return (0);
452 	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
453 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
454 			if (testbmap(i + j))
455 				continue;
456 			for (k = 1; k < frags; k++)
457 				if (testbmap(i + j + k))
458 					break;
459 			if (k < frags) {
460 				j += k;
461 				continue;
462 			}
463 			for (k = 0; k < frags; k++)
464 				setbmap(i + j + k);
465 			n_blks += frags;
466 			return (i + j);
467 		}
468 	}
469 	return (0);
470 }
471 
472 /*
473  * Free a previously allocated block
474  */
475 freeblk(blkno, frags)
476 	daddr32_t blkno;
477 	int frags;
478 {
479 	struct inodesc idesc;
480 
481 	idesc.id_blkno = blkno;
482 	idesc.id_numfrags = frags;
483 	pass4check(&idesc);
484 }
485 
486 /*
487  * Find a pathname
488  */
489 getpathname(namebuf, curdir, ino)
490 	char *namebuf;
491 	ino_t curdir, ino;
492 {
493 	int len;
494 	char *cp;
495 	struct inodesc idesc;
496 	struct inoinfo *inp;
497 	extern int findname();
498 
499 	if (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND) {
500 		strcpy(namebuf, "?");
501 		return;
502 	}
503 	bzero((char *)&idesc, sizeof (struct inodesc));
504 	idesc.id_type = DATA;
505 	cp = &namebuf[MAXPATHLEN - 1];
506 	*cp = '\0';
507 	if (curdir != ino) {
508 		idesc.id_parent = curdir;
509 		goto namelookup;
510 	}
511 	while (ino != UFSROOTINO) {
512 		idesc.id_number = ino;
513 		idesc.id_func = findino;
514 		idesc.id_name = "..";
515 		idesc.id_fix = NOFIX;
516 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) {
517 			inp = getinoinfo(ino);
518 			if (inp->i_parent == 0)
519 				break;
520 			idesc.id_parent = inp->i_parent;
521 		}
522 	namelookup:
523 		idesc.id_number = idesc.id_parent;
524 		idesc.id_parent = ino;
525 		idesc.id_func = findname;
526 		idesc.id_name = namebuf;
527 		idesc.id_fix = NOFIX;
528 		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
529 			break;
530 		len = strlen(namebuf);
531 		cp -= len;
532 		if (cp < &namebuf[MAXNAMLEN])
533 			break;
534 		bcopy(namebuf, cp, len);
535 		*--cp = '/';
536 		ino = idesc.id_number;
537 	}
538 	if (ino != UFSROOTINO) {
539 		strcpy(namebuf, "?");
540 		return;
541 	}
542 	bcopy(cp, namebuf, &namebuf[MAXPATHLEN] - cp);
543 }
544 
545 void
546 catch()
547 {
548 	ckfini();
549 	exit(37);
550 }
551 
552 /*
553  * When preening, allow a single quit to signal
554  * a special exit after filesystem checks complete
555  * so that reboot sequence may be interrupted.
556  */
557 void
558 catchquit()
559 {
560 	extern returntosingle;
561 
562 	printf("returning to single-user after filesystem check\n");
563 	returntosingle = 1;
564 	(void) signal(SIGQUIT, SIG_DFL);
565 }
566 
567 /*
568  * Ignore a single quit signal; wait and flush just in case.
569  * Used by child processes in preen.
570  */
571 void
572 voidquit()
573 {
574 
575 	sleep(1);
576 	(void) signal(SIGQUIT, SIG_IGN);
577 	(void) signal(SIGQUIT, SIG_DFL);
578 }
579 
580 /*
581  * determine whether an inode should be fixed.
582  */
583 dofix(idesc, msg)
584 	struct inodesc *idesc;
585 	char *msg;
586 {
587 
588 	switch (idesc->id_fix) {
589 
590 	case DONTKNOW:
591 		if (idesc->id_type == DATA)
592 			direrror(idesc->id_number, msg);
593 		else
594 			pwarn(msg);
595 		if (preen) {
596 			printf(" (SALVAGED)\n");
597 			idesc->id_fix = FIX;
598 			return (ALTERED);
599 		}
600 		if (reply("SALVAGE") == 0) {
601 			idesc->id_fix = NOFIX;
602 			return (0);
603 		}
604 		idesc->id_fix = FIX;
605 		return (ALTERED);
606 
607 	case FIX:
608 		return (ALTERED);
609 
610 	case NOFIX:
611 		return (0);
612 
613 	default:
614 		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
615 	}
616 	/* NOTREACHED */
617 }
618 
619 /* VARARGS1 */
620 errexit(s1, s2, s3, s4)
621 	char *s1;
622 {
623 	extern void write_altsb(int);
624 
625 	if (errorlocked) {
626 		if (havesb) {
627 			sblock.fs_clean = FSBAD;
628 			sblock.fs_state = -(FSOKAY - (long)sblock.fs_time);
629 			sbdirty();
630 			write_altsb(fswritefd);
631 			flush(fswritefd, &sblk);
632 		}
633 	}
634 	printf(s1, s2, s3, s4);
635 	exit(39);
636 }
637 
638 /*
639  * An unexpected inconsistency occured.
640  * Die if preening, otherwise just print message and continue.
641  */
642 /* VARARGS1 */
643 pfatal(s, a1, a2, a3)
644 	char *s;
645 {
646 	if (preen) {
647 		printf("%s: ", devname);
648 		printf(s, a1, a2, a3);
649 		printf("\n");
650 		printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
651 			devname);
652 		if (havesb) {
653 			sblock.fs_clean = FSBAD;
654 			sblock.fs_state = -(FSOKAY - (long)sblock.fs_time);
655 			sbdirty();
656 			flush(fswritefd, &sblk);
657 		}
658 		exit(36);
659 	}
660 	printf(s, a1, a2, a3);
661 }
662 
663 /*
664  * Pwarn just prints a message when not preening,
665  * or a warning (preceded by filename) when preening.
666  */
667 /* VARARGS1 */
668 pwarn(s, a1, a2, a3, a4, a5, a6)
669 	char *s;
670 {
671 
672 	if (preen)
673 		printf("%s: ", devname);
674 	printf(s, a1, a2, a3, a4, a5, a6);
675 }
676 
677 #ifndef lint
678 /*
679  * Stub for routines from kernel.
680  */
681 panic(s)
682 	char *s;
683 {
684 
685 	pfatal("INTERNAL INCONSISTENCY:");
686 	errexit(s);
687 }
688 #define	CE_PANIC 3
689 void
690 cmn_err(level, s)
691 	int level;
692 	char *s;
693 {
694 
695 	if (level == CE_PANIC) {
696 		pfatal("INTERNAL INCONSISTENCY:");
697 		errexit(s);
698 	}
699 	else
700 		printf(s);
701 }
702 #endif
703 
704 /*
705  * Check to see if unraw version of name is already mounted.
706  * Since we do not believe /etc/mnttab, we stat the mount point
707  * to see if it is really looks mounted.
708  */
709 mounted(name)
710 	char *name;
711 {
712 	int found = 0;
713 	struct mnttab mnt;
714 	FILE *mnttab;
715 	struct stat64 device_stat, mount_stat;
716 	char *blkname, *unrawname();
717 
718 	mnttab = fopen(MNTTAB, "r");
719 	if (mnttab == NULL) {
720 		return (0);
721 	}
722 	blkname = unrawname(name);
723 	while ((getmntent(mnttab, &mnt)) == NULL) {
724 		if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0) {
725 			continue;
726 		}
727 		if (strcmp(blkname, mnt.mnt_special) == 0) {
728 			stat64(mnt.mnt_mountp, &mount_stat);
729 			stat64(mnt.mnt_special, &device_stat);
730 			if (device_stat.st_rdev == mount_stat.st_dev) {
731 				if (hasmntopt(&mnt, MNTOPT_RO) != 0)
732 					found = 2;	/* mounted as RO */
733 				else
734 					found = 1; 	/* mounted as R/W */
735 			}
736 			if (mount_point == NULL) {
737 				mount_point = strdup(mnt.mnt_mountp);
738 				if (mount_point == NULL) {
739 					printf("fsck: memory allocation"
740 					    " failure\n");
741 					exit(39);
742 				}
743 			}
744 			break;
745 		}
746 	}
747 	fclose(mnttab);
748 	return (found);
749 }
750 
751 /*
752  * Check to see if name corresponds to an entry in vfstab, and that the entry
753  * does not have option ro.
754  */
755 writable(name)
756 	char *name;
757 {
758 	int rw = 1;
759 	struct vfstab vfsbuf;
760 	FILE *vfstab;
761 	char *blkname, *unrawname();
762 
763 	vfstab = fopen(VFSTAB, "r");
764 	if (vfstab == NULL) {
765 		printf("can't open %s\n", VFSTAB);
766 		return (1);
767 	}
768 	blkname = unrawname(name);
769 	if ((getvfsspec(vfstab, &vfsbuf, blkname) == 0) &&
770 	    (vfsbuf.vfs_fstype != NULL) &&
771 	    (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UFS) == 0) &&
772 	    (hasvfsopt(&vfsbuf, MNTOPT_RO))) {
773 		rw = 0;
774 	}
775 	fclose(vfstab);
776 	return (rw);
777 }
778 
779 /*
780  * debugclean
781  */
782 debugclean()
783 {
784 	char	s[256];
785 
786 	if (debug == 0)
787 		return;
788 
789 	if ((iscorrupt == 0) && (isdirty == 0))
790 		return;
791 
792 	if ((sblock.fs_clean != FSSTABLE) && (sblock.fs_clean != FSCLEAN) &&
793 	    (sblock.fs_clean != FSLOG || !islog || !islogok))
794 		return;
795 
796 	if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked)
797 		return;
798 
799 	sprintf(s,
800 	    "WARNING: inconsistencies detected on `%s' filesystem %s",
801 	    sblock.fs_clean == FSSTABLE ? "stable" :
802 	    sblock.fs_clean == FSLOG ? "logging" :
803 	    sblock.fs_clean == FSFIX ? "being fixed" : "clean", devname);
804 	printf("%s\n", s);
805 }
806 
807 /*
808  * updateclean
809  *	Carefully and transparently update the clean flag.
810  */
811 updateclean()
812 {
813 	struct bufarea	cleanbuf;
814 	int	size;
815 	daddr32_t	bno;
816 	int	fsclean;
817 	int	fsreclaim;
818 	int	fsflags;
819 	int	r;
820 	daddr32_t	fslogbno;
821 	offset_t sblkoff;
822 	time_t t;
823 
824 	/*
825 	 * debug stuff
826 	 */
827 	debugclean();
828 
829 	/*
830 	 * set fsclean to its appropriate value
831 	 */
832 	fslogbno = sblock.fs_logbno;
833 	fsclean = sblock.fs_clean;
834 	fsreclaim = sblock.fs_reclaim;
835 	fsflags = sblock.fs_flags;
836 	if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked)
837 		fsclean = FSACTIVE;
838 
839 	/* if ufs log is not okay, clear it */
840 	if (fslogbno && !(islog && islogok)) {
841 		fsclean = FSACTIVE;
842 		fslogbno = 0;
843 	}
844 
845 	/*
846 	 * if necessary, update fs_clean and fs_state
847 	 */
848 	switch (fsclean) {
849 
850 	case FSACTIVE:
851 		if (!iscorrupt) {
852 			fsclean = FSSTABLE;
853 			fsreclaim = 0;
854 		}
855 		break;
856 
857 	case FSCLEAN:
858 	case FSSTABLE:
859 		if (iscorrupt)
860 			fsclean = FSACTIVE;
861 		else
862 			fsreclaim = 0;
863 		break;
864 
865 	case FSLOG:
866 		if (iscorrupt)
867 			fsclean = FSACTIVE;
868 		else if (!islog) {
869 			fsreclaim = 0;
870 			fsclean = FSSTABLE;
871 		} else if (fflag)
872 			fsreclaim = 0;
873 		break;
874 
875 	case FSFIX:
876 		fsreclaim = needs_reclaim;
877 		fsclean = FSBAD;
878 		if (errorlocked && !iscorrupt) {
879 			fsclean = islog? FSLOG: FSCLEAN;
880 		}
881 		break;
882 
883 	default:
884 		if (iscorrupt)
885 			fsclean = FSACTIVE;
886 		else {
887 			fsclean = FSSTABLE;
888 			fsreclaim = 0;
889 		}
890 	}
891 
892 	if (largefile_count > 0)
893 		fsflags |= FSLARGEFILES;
894 	else
895 		fsflags &= ~FSLARGEFILES;
896 
897 	/*
898 	 * fs is unchanged, do nothing
899 	 */
900 	if (debug)
901 		printf("** largefile count=%d, fs.fs_flags=%x\n",
902 		    largefile_count, sblock.fs_flags);
903 
904 	if ((!isdirty) && (fsflags == sblock.fs_flags) &&
905 	    (fslogbno == sblock.fs_logbno) &&
906 	    (sblock.fs_clean == fsclean) && (sblock.fs_reclaim == fsreclaim) &&
907 	    (FSOKAY == (sblock.fs_state + sblock.fs_time))) {
908 		if (islog && !islogok)
909 			(void) ioctl(fswritefd, _FIOLOGRESET, NULL);
910 
911 		if (errorlocked) {
912 			if (!do_errorlock(LOCKFS_ULOCK))
913 				pwarn(
914 		    "updateclean(unchanged): unlock(LOCKFS_ULOCK) failed\n");
915 		}
916 		return;
917 	}
918 
919 	/*
920 	 * if user allows, update superblock state
921 	 */
922 	if (!isdirty && !preen &&
923 	    (reply("FILE SYSTEM STATE IN SUPERBLOCK IS WRONG; FIX") == 0))
924 		return;
925 
926 	(void) time(&t);
927 	sblock.fs_time = (time32_t)t;
928 	if (debug)
929 		printclean();
930 	sblock.fs_logbno = fslogbno;
931 	sblock.fs_clean = fsclean;
932 	sblock.fs_state = FSOKAY - (long)sblock.fs_time;
933 	sblock.fs_reclaim = fsreclaim;
934 	sblock.fs_flags = fsflags;
935 
936 	/*
937 	 * if superblock can't be written, return
938 	 */
939 	if (fswritefd < 0)
940 		return;
941 
942 	/*
943 	 * read private copy of superblock, update clean flag, and write it
944 	 */
945 	bno  = sblk.b_bno;
946 	size = sblk.b_size;
947 
948 	sblkoff = ldbtob(bno);
949 
950 	if ((cleanbuf.b_un.b_buf = malloc(size)) == NULL)
951 		errexit("out of memory");
952 
953 	if (llseek(fsreadfd, sblkoff, 0) == -1)
954 		return;
955 	if (read(fsreadfd, cleanbuf.b_un.b_buf, (int)size) != size)
956 		return;
957 
958 	cleanbuf.b_un.b_fs->fs_logbno  = sblock.fs_logbno;
959 	cleanbuf.b_un.b_fs->fs_clean   = sblock.fs_clean;
960 	cleanbuf.b_un.b_fs->fs_state   = sblock.fs_state;
961 	cleanbuf.b_un.b_fs->fs_time    = sblock.fs_time;
962 	cleanbuf.b_un.b_fs->fs_reclaim = sblock.fs_reclaim;
963 	cleanbuf.b_un.b_fs->fs_flags   = sblock.fs_flags;
964 
965 	if (llseek(fswritefd, sblkoff, 0) == -1)
966 		return;
967 	if (write(fswritefd, cleanbuf.b_un.b_buf, (int)size) != size)
968 		return;
969 
970 	/*
971 	 * 1208040
972 	 * If we had to use -b to grab an alternate superblock, then we
973 	 * likely had to do so because of unacceptable differences between
974 	 * the main and alternate superblocks.  SO, we had better update
975 	 * the alternate superblock as well, or we'll just fail again
976 	 * the next time we attempt to run fsck!
977 	 */
978 	if (bflag) {
979 		extern struct bufarea asblk;
980 
981 		if (llseek(fswritefd, ldbtob(asblk.b_bno), 0) == -1)
982 			return;
983 		if (write(fswritefd, cleanbuf.b_un.b_buf, (int)size) != size)
984 			return;
985 	}
986 
987 	if (islog && !islogok)
988 		(void) ioctl(fswritefd, _FIOLOGRESET, NULL);
989 
990 	if (errorlocked) {
991 		if (!do_errorlock(LOCKFS_ULOCK))
992 			pwarn(
993 		    "updateclean(changed): unlock(LOCKFS_ULOCK) failed\n");
994 	}
995 }
996 
997 /*
998  * print out clean info
999  */
1000 printclean()
1001 {
1002 	char	*s;
1003 
1004 	if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked)
1005 		s = "unknown";
1006 	else
1007 		switch (sblock.fs_clean) {
1008 
1009 		case FSACTIVE:
1010 			s = "active";
1011 			break;
1012 
1013 		case FSCLEAN:
1014 			s = "clean";
1015 			break;
1016 
1017 		case FSSTABLE:
1018 			s = "stable";
1019 			break;
1020 
1021 		case FSLOG:
1022 			s = "logging";
1023 			break;
1024 
1025 		case FSBAD:
1026 			s = "is bad";
1027 			break;
1028 
1029 		case FSFIX:
1030 			s = "being fixed";
1031 			break;
1032 
1033 		default:
1034 			s = "unknown";
1035 		}
1036 
1037 	if (preen)
1038 		pwarn("is %s.\n", s);
1039 	else
1040 		printf("** %s is %s.\n", devname, s);
1041 }
1042 
1043 /* see if all numbers */
1044 numbers(yp)
1045 	char	*yp;
1046 {
1047 	if (yp == NULL)
1048 		return (0);
1049 	while ('0' <= *yp && *yp <= '9')
1050 		yp++;
1051 	if (*yp)
1052 		return (0);
1053 	return (1);
1054 }
1055 
1056 is_errorlocked(char *fs)
1057 {
1058 	struct stat64	 statb;
1059 	char 		*mountp;
1060 	static char	*getmountp(char *);
1061 	char		*unrawname(char *);
1062 
1063 	mountp = NULL;
1064 
1065 	if (!fs)
1066 		return (0);
1067 
1068 	if (stat64(fs, &statb) < 0)
1069 		return (0);
1070 
1071 	if (S_ISDIR(statb.st_mode)) {
1072 		mountp = fs;
1073 
1074 	} else if (S_ISBLK(statb.st_mode) || S_ISCHR(statb.st_mode)) {
1075 		mountp = getmountp(S_ISCHR(statb.st_mode)? unrawname(fs): fs);
1076 		if (!mountp) {
1077 			return (0);
1078 		}
1079 	} else {
1080 		return (0);
1081 	}
1082 
1083 	if (elock_combuf == NULL) {
1084 		elock_combuf =
1085 			(char *)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char));
1086 	} else {
1087 		elock_combuf =
1088 			(char *)realloc(elock_combuf, LOCKFS_MAXCOMMENTLEN);
1089 	}
1090 
1091 	if (elock_combuf == NULL)
1092 		return (0);
1093 
1094 	bzero((caddr_t)elock_combuf, LOCKFS_MAXCOMMENTLEN);
1095 
1096 	elock_mountp = strdup(mountp);
1097 
1098 	if (mountfd < 0) {
1099 		if ((mountfd = open64(mountp, O_RDONLY)) == -1)
1100 			return (0);
1101 	}
1102 
1103 	if (!lfp) {
1104 		lfp = (struct lockfs *)malloc(sizeof (struct lockfs));
1105 		if (!lfp)
1106 			return (0);
1107 		bzero((caddr_t)lfp, sizeof (struct lockfs));
1108 	}
1109 
1110 	lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN;
1111 	lfp->lf_comment = elock_combuf;
1112 
1113 	if (ioctl(mountfd, _FIOLFSS, lfp) == -1)
1114 		return (0);
1115 
1116 	return (LOCKFS_IS_ELOCK(lfp));
1117 }
1118 
1119 static char *
1120 getmountp(char *dev) {
1121 	FILE		*vfstab;
1122 	struct vfstab	 vfsbuf;
1123 	char		*mountp;
1124 	int		 rc;
1125 
1126 	mountp = NULL;
1127 	if ((vfstab = fopen(VFSTAB, "r")) == NULL) {
1128 		return (NULL);
1129 	}
1130 	if ((rc = getvfsspec(vfstab, &vfsbuf, dev)) == 0) {
1131 		if (!(mountp = malloc(MAXPATHLEN)) ||
1132 		    vfsbuf.vfs_mountp == NULL)
1133 			return (NULL);
1134 		strcpy(mountp, vfsbuf.vfs_mountp);
1135 	} else if (rc == -1) {
1136 		return (NULL);
1137 	}
1138 	fclose(vfstab);
1139 	return (mountp);
1140 }
1141 
1142 do_errorlock(int lock_type)
1143 {
1144 	char		*buf;
1145 	time_t		 now;
1146 	struct tm	*local;
1147 	int		 rc = 0;
1148 
1149 	if (!elock_combuf)
1150 		errexit("do_errorlock(%s, %d): unallocated elock_combuf\n",
1151 			elock_mountp? elock_mountp: "<null>", lock_type);
1152 
1153 	if (!(buf = (char *)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char))))
1154 		errexit("Couldn't alloc memory for temp. lock status buffer\n");
1155 
1156 	if (!lfp) {
1157 		errexit("do_errorlock(%s, %d): lockfs status unallocated\n",
1158 					elock_mountp, lock_type);
1159 	}
1160 
1161 	bcopy(elock_combuf, buf, LOCKFS_MAXCOMMENTLEN-1);
1162 
1163 	switch (lock_type) {
1164 	case LOCKFS_ELOCK:
1165 		if (time(&now) != (time_t)-1) {
1166 			if ((local = localtime(&now)) != NULL)
1167 				sprintf(buf,
1168 		    "%s [pid:%d fsck start:%02d/%02d/%02d %02d:%02d:%02d",
1169 				    elock_combuf, pid,
1170 				    local->tm_mon+1, local->tm_mday,
1171 				    (local->tm_year % 100), local->tm_hour,
1172 				    local->tm_min, local->tm_sec);
1173 			else
1174 				sprintf(buf, "%s [fsck pid %d",
1175 							    elock_combuf, pid);
1176 
1177 		} else {
1178 			sprintf(buf, "%s [fsck pid %d", elock_combuf, pid);
1179 		}
1180 		break;
1181 
1182 	case LOCKFS_ULOCK:
1183 		if (time(&now) != (time_t)-1) {
1184 			if ((local = localtime(&now)) != NULL) {
1185 				sprintf(buf,
1186 				    "%s, done:%02d/%02d/%02d %02d:%02d:%02d]",
1187 				    elock_combuf,
1188 				    local->tm_mon+1, local->tm_mday,
1189 				    (local->tm_year % 100), local->tm_hour,
1190 				    local->tm_min, local->tm_sec);
1191 			} else {
1192 				sprintf(buf, "%s]", elock_combuf);
1193 			}
1194 		} else {
1195 			sprintf(buf, "%s]", elock_combuf);
1196 		}
1197 		if ((rc = ioctl(mountfd, _FIOLFSS, lfp)) == -1) {
1198 			goto out;
1199 		}
1200 		break;
1201 
1202 	default:
1203 		break;
1204 	}
1205 
1206 	bcopy(buf, elock_combuf, LOCKFS_MAXCOMMENTLEN-1);
1207 
1208 	lfp->lf_lock	= lock_type;
1209 	lfp->lf_comlen	= LOCKFS_MAXCOMMENTLEN;
1210 	lfp->lf_comment	= elock_combuf;
1211 	lfp->lf_flags	= 0;
1212 	errno		= 0;
1213 
1214 	if ((rc = ioctl(mountfd, _FIOLFS, lfp)) == -1) {
1215 		if (errno == EINVAL) {
1216 			pwarn("Another fsck active?\n");
1217 			iscorrupt = 0;	/* don't go away mad, just go away */
1218 		} else {
1219 			pwarn(
1220 			"do_errorlock(lock_type:%d, %s) failed: errno:%d\n",
1221 						lock_type, elock_combuf, errno);
1222 		}
1223 	}
1224 out:
1225 	if (buf)
1226 		free(buf);
1227 
1228 	return (rc != -1);
1229 }
1230 
1231 /*
1232  * Shadow inode support.  To `register' a shadow with a client is to note
1233  * that an inode (the `client') refers to the shadow.  See fsck.h for more
1234  * on how the shadowclientinfo and shadowclients structures are used.
1235  */
1236 
1237 static struct shadowclients *
1238 newshadowclient(struct shadowclients *prev)
1239 {
1240 	struct shadowclients *rc;
1241 
1242 	rc = (struct shadowclients *)malloc(sizeof (*rc));
1243 	if (rc == NULL)
1244 		errexit("newshadowclient: cannot malloc (1)");
1245 
1246 	rc->next = prev;
1247 	rc->nclients = 0;
1248 
1249 	rc->client = (ino_t *)
1250 	    malloc(sizeof (ino_t) * maxshadowclients);
1251 	if (rc->client == NULL)
1252 		errexit("newshadowclient: cannot malloc (2)");
1253 
1254 	return (rc);
1255 }
1256 
1257 void
1258 registershadowclient(ino_t shadow, ino_t client, struct shadowclientinfo **info)
1259 {
1260 	struct shadowclientinfo *sci;
1261 	struct shadowclients *scc;
1262 
1263 	for (sci = *info; sci; sci = sci->next)
1264 		if (sci->shadow == shadow)
1265 			break;
1266 	if (sci == NULL) {
1267 		sci = (struct shadowclientinfo *)malloc(sizeof (*sci));
1268 		if (sci == NULL)
1269 			errexit("registershadowclient: cannot malloc");
1270 		sci->next = *info;
1271 		*info = sci;
1272 		sci->shadow = shadow;
1273 		sci->totalClients = 0;
1274 		sci->clients = newshadowclient(NULL);
1275 	}
1276 
1277 	sci->totalClients++;
1278 	scc = sci->clients;
1279 	if (scc->nclients >= maxshadowclients) {
1280 		scc = newshadowclient(sci->clients);
1281 		sci->clients = scc;
1282 	}
1283 
1284 	scc->client[scc->nclients++] = client;
1285 }
1286 
1287 /*
1288  * Allocate more buffer as need arises but allocate one at a time.
1289  * This is done to make sure that fsck does not exit with error if it
1290  * needs more buffer to complete it's task.
1291  */
1292 static struct bufarea *
1293 alloc_bufarea()
1294 {
1295 	struct bufarea *bp;
1296 	char *bufp;
1297 
1298 	bp = (struct bufarea *)malloc(sizeof (struct bufarea));
1299 	bufp = malloc((unsigned int)sblock.fs_bsize);
1300 	if (bp == NULL || bufp == NULL) {
1301 		if (bp)
1302 			free((char *)bp);
1303 		if (bufp)
1304 			free(bufp);
1305 		return (NULL);
1306 	}
1307 	bp->b_un.b_buf = bufp;
1308 	bp->b_prev = &bufhead;
1309 	bp->b_next = bufhead.b_next;
1310 	bufhead.b_next->b_prev = bp;
1311 	bufhead.b_next = bp;
1312 	initbarea(bp);
1313 	bufhead.b_size++;
1314 	return (bp);
1315 }
1316