fsutil.c revision 86514
1/*
2 * Copyright (c) 1980, 1986, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35#if 0
36static const char sccsid[] = "@(#)utilities.c	8.6 (Berkeley) 5/19/95";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: head/sbin/fsck_ffs/fsutil.c 86514 2001-11-17 23:48:21Z iedowse $";
40#endif /* not lint */
41
42#include <sys/param.h>
43#include <sys/types.h>
44#include <sys/sysctl.h>
45#include <sys/stat.h>
46
47#include <ufs/ufs/dinode.h>
48#include <ufs/ufs/dir.h>
49#include <ufs/ffs/fs.h>
50
51#include <err.h>
52#include <errno.h>
53#include <string.h>
54#include <ctype.h>
55#include <fstab.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <unistd.h>
59
60#include "fsck.h"
61
62long	diskreads, totalreads;	/* Disk cache statistics */
63
64int
65ftypeok(dp)
66	struct dinode *dp;
67{
68	switch (dp->di_mode & IFMT) {
69
70	case IFDIR:
71	case IFREG:
72	case IFBLK:
73	case IFCHR:
74	case IFLNK:
75	case IFSOCK:
76	case IFIFO:
77		return (1);
78
79	default:
80		if (debug)
81			printf("bad file type 0%o\n", dp->di_mode);
82		return (0);
83	}
84}
85
86int
87reply(question)
88	char *question;
89{
90	int persevere;
91	char c;
92
93	if (preen)
94		pfatal("INTERNAL ERROR: GOT TO reply()");
95	persevere = !strcmp(question, "CONTINUE");
96	printf("\n");
97	if (!persevere && (nflag || (fswritefd < 0 && bkgrdflag == 0))) {
98		printf("%s? no\n\n", question);
99		resolved = 0;
100		return (0);
101	}
102	if (yflag || (persevere && nflag)) {
103		printf("%s? yes\n\n", question);
104		return (1);
105	}
106	do	{
107		printf("%s? [yn] ", question);
108		(void) fflush(stdout);
109		c = getc(stdin);
110		while (c != '\n' && getc(stdin) != '\n') {
111			if (feof(stdin)) {
112				resolved = 0;
113				return (0);
114			}
115		}
116	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
117	printf("\n");
118	if (c == 'y' || c == 'Y')
119		return (1);
120	resolved = 0;
121	return (0);
122}
123
124/*
125 * Look up state information for an inode.
126 */
127struct inostat *
128inoinfo(inum)
129	ino_t inum;
130{
131	static struct inostat unallocated = { USTATE, 0, 0 };
132	struct inostatlist *ilp;
133	int iloff;
134
135	if (inum > maxino)
136		errx(EEXIT, "inoinfo: inumber %d out of range", inum);
137	ilp = &inostathead[inum / sblock.fs_ipg];
138	iloff = inum % sblock.fs_ipg;
139	if (iloff >= ilp->il_numalloced)
140		return (&unallocated);
141	return (&ilp->il_stat[iloff]);
142}
143
144/*
145 * Malloc buffers and set up cache.
146 */
147void
148bufinit()
149{
150	register struct bufarea *bp;
151	long bufcnt, i;
152	char *bufp;
153
154	pbp = pdirbp = (struct bufarea *)0;
155	bufp = malloc((unsigned int)sblock.fs_bsize);
156	if (bufp == 0)
157		errx(EEXIT, "cannot allocate buffer pool");
158	cgblk.b_un.b_buf = bufp;
159	initbarea(&cgblk);
160	bufhead.b_next = bufhead.b_prev = &bufhead;
161	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
162	if (bufcnt < MINBUFS)
163		bufcnt = MINBUFS;
164	for (i = 0; i < bufcnt; i++) {
165		bp = (struct bufarea *)malloc(sizeof(struct bufarea));
166		bufp = malloc((unsigned int)sblock.fs_bsize);
167		if (bp == NULL || bufp == NULL) {
168			if (i >= MINBUFS)
169				break;
170			errx(EEXIT, "cannot allocate buffer pool");
171		}
172		bp->b_un.b_buf = bufp;
173		bp->b_prev = &bufhead;
174		bp->b_next = bufhead.b_next;
175		bufhead.b_next->b_prev = bp;
176		bufhead.b_next = bp;
177		initbarea(bp);
178	}
179	bufhead.b_size = i;	/* save number of buffers */
180}
181
182/*
183 * Manage a cache of directory blocks.
184 */
185struct bufarea *
186getdatablk(blkno, size)
187	ufs_daddr_t blkno;
188	long size;
189{
190	register struct bufarea *bp;
191
192	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
193		if (bp->b_bno == fsbtodb(&sblock, blkno))
194			goto foundit;
195	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
196		if ((bp->b_flags & B_INUSE) == 0)
197			break;
198	if (bp == &bufhead)
199		errx(EEXIT, "deadlocked buffer pool");
200	getblk(bp, blkno, size);
201	/* fall through */
202foundit:
203	totalreads++;
204	bp->b_prev->b_next = bp->b_next;
205	bp->b_next->b_prev = bp->b_prev;
206	bp->b_prev = &bufhead;
207	bp->b_next = bufhead.b_next;
208	bufhead.b_next->b_prev = bp;
209	bufhead.b_next = bp;
210	bp->b_flags |= B_INUSE;
211	return (bp);
212}
213
214void
215getblk(bp, blk, size)
216	register struct bufarea *bp;
217	ufs_daddr_t blk;
218	long size;
219{
220	ufs_daddr_t dblk;
221
222	dblk = fsbtodb(&sblock, blk);
223	if (bp->b_bno != dblk) {
224		flush(fswritefd, bp);
225		diskreads++;
226		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
227		bp->b_bno = dblk;
228		bp->b_size = size;
229	}
230}
231
232void
233flush(fd, bp)
234	int fd;
235	register struct bufarea *bp;
236{
237	register int i, j;
238
239	if (!bp->b_dirty)
240		return;
241	bp->b_dirty = 0;
242	if (fswritefd < 0) {
243		pfatal("WRITING IN READ_ONLY MODE.\n");
244		return;
245	}
246	if (bp->b_errs != 0)
247		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
248		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
249		    bp->b_bno);
250	bp->b_errs = 0;
251	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
252	if (bp != &sblk)
253		return;
254	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
255		bwrite(fswritefd, (char *)sblock.fs_csp + i,
256		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
257		    sblock.fs_cssize - i < sblock.fs_bsize ?
258		    sblock.fs_cssize - i : sblock.fs_bsize);
259	}
260}
261
262void
263rwerror(mesg, blk)
264	char *mesg;
265	ufs_daddr_t blk;
266{
267
268	if (bkgrdcheck)
269		exit(EEXIT);
270	if (preen == 0)
271		printf("\n");
272	pfatal("CANNOT %s: %ld", mesg, (long)blk);
273	if (reply("CONTINUE") == 0)
274		exit(EEXIT);
275}
276
277void
278ckfini(markclean)
279	int markclean;
280{
281	register struct bufarea *bp, *nbp;
282	int ofsmodified, cnt = 0;
283
284	if (bkgrdflag) {
285		unlink(snapname);
286		if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) {
287			cmd.value = FS_UNCLEAN;
288			cmd.size = markclean ? -1 : 1;
289			if (sysctlbyname("vfs.ffs.setflags", 0, 0,
290			    &cmd, sizeof cmd) == -1)
291				rwerror("SET FILESYSTEM FLAGS", FS_UNCLEAN);
292			if (!preen) {
293				printf("\n***** FILE SYSTEM MARKED %s *****\n",
294				    markclean ? "CLEAN" : "DIRTY");
295				if (!markclean)
296					rerun = 1;
297			}
298		} else if (!preen && !markclean) {
299			printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
300			rerun = 1;
301		}
302	}
303	if (fswritefd < 0) {
304		(void)close(fsreadfd);
305		return;
306	}
307	flush(fswritefd, &sblk);
308	if (havesb && sblk.b_bno != SBOFF / dev_bsize && cursnapshot == 0 &&
309	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
310		sblk.b_bno = SBOFF / dev_bsize;
311		sbdirty();
312		flush(fswritefd, &sblk);
313	}
314	flush(fswritefd, &cgblk);
315	free(cgblk.b_un.b_buf);
316	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
317		cnt++;
318		flush(fswritefd, bp);
319		nbp = bp->b_prev;
320		free(bp->b_un.b_buf);
321		free((char *)bp);
322	}
323	if (bufhead.b_size != cnt)
324		errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt);
325	pbp = pdirbp = (struct bufarea *)0;
326	if (cursnapshot == 0 && sblock.fs_clean != markclean) {
327		if ((sblock.fs_clean = markclean) != 0)
328			sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK);
329		sbdirty();
330		ofsmodified = fsmodified;
331		flush(fswritefd, &sblk);
332		fsmodified = ofsmodified;
333		if (!preen) {
334			printf("\n***** FILE SYSTEM MARKED %s *****\n",
335			    markclean ? "CLEAN" : "DIRTY");
336			if (!markclean)
337				rerun = 1;
338		}
339	} else if (!preen && !markclean) {
340		printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
341		rerun = 1;
342	}
343	if (debug && totalreads > 0)
344		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
345		    totalreads, (int)(diskreads * 100 / totalreads));
346	(void)close(fsreadfd);
347	(void)close(fswritefd);
348}
349
350int
351bread(fd, buf, blk, size)
352	int fd;
353	char *buf;
354	ufs_daddr_t blk;
355	long size;
356{
357	char *cp;
358	int i, errs;
359	off_t offset;
360
361	offset = blk;
362	offset *= dev_bsize;
363	if (lseek(fd, offset, 0) < 0)
364		rwerror("SEEK BLK", blk);
365	else if (read(fd, buf, (int)size) == size)
366		return (0);
367	rwerror("READ BLK", blk);
368	if (lseek(fd, offset, 0) < 0)
369		rwerror("SEEK BLK", blk);
370	errs = 0;
371	memset(buf, 0, (size_t)size);
372	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
373	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
374		if (read(fd, cp, (int)secsize) != secsize) {
375			(void)lseek(fd, offset + i + secsize, 0);
376			if (secsize != dev_bsize && dev_bsize != 1)
377				printf(" %ld (%ld),",
378				    (blk * dev_bsize + i) / secsize,
379				    blk + i / dev_bsize);
380			else
381				printf(" %ld,", blk + i / dev_bsize);
382			errs++;
383		}
384	}
385	printf("\n");
386	if (errs)
387		resolved = 0;
388	return (errs);
389}
390
391void
392bwrite(fd, buf, blk, size)
393	int fd;
394	char *buf;
395	ufs_daddr_t blk;
396	long size;
397{
398	int i;
399	char *cp;
400	off_t offset;
401
402	if (fd < 0)
403		return;
404	offset = blk;
405	offset *= dev_bsize;
406	if (lseek(fd, offset, 0) < 0)
407		rwerror("SEEK BLK", blk);
408	else if (write(fd, buf, (int)size) == size) {
409		fsmodified = 1;
410		return;
411	}
412	resolved = 0;
413	rwerror("WRITE BLK", blk);
414	if (lseek(fd, offset, 0) < 0)
415		rwerror("SEEK BLK", blk);
416	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
417	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
418		if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
419			(void)lseek(fd, offset + i + dev_bsize, 0);
420			printf(" %ld,", blk + i / dev_bsize);
421		}
422	printf("\n");
423	return;
424}
425
426/*
427 * allocate a data block with the specified number of fragments
428 */
429ufs_daddr_t
430allocblk(frags)
431	long frags;
432{
433	int i, j, k, cg, baseblk;
434	struct cg *cgp = &cgrp;
435
436	if (frags <= 0 || frags > sblock.fs_frag)
437		return (0);
438	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
439		for (j = 0; j <= sblock.fs_frag - frags; j++) {
440			if (testbmap(i + j))
441				continue;
442			for (k = 1; k < frags; k++)
443				if (testbmap(i + j + k))
444					break;
445			if (k < frags) {
446				j += k;
447				continue;
448			}
449			cg = dtog(&sblock, i + j);
450			getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
451			if (!cg_chkmagic(cgp))
452				pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
453			baseblk = dtogd(&sblock, i + j);
454			for (k = 0; k < frags; k++) {
455				setbmap(i + j + k);
456				clrbit(cg_blksfree(cgp), baseblk + k);
457			}
458			n_blks += frags;
459			if (frags == sblock.fs_frag)
460				cgp->cg_cs.cs_nbfree--;
461			else
462				cgp->cg_cs.cs_nffree -= frags;
463			cgdirty();
464			return (i + j);
465		}
466	}
467	return (0);
468}
469
470/*
471 * Free a previously allocated block
472 */
473void
474freeblk(blkno, frags)
475	ufs_daddr_t blkno;
476	long frags;
477{
478	struct inodesc idesc;
479
480	idesc.id_blkno = blkno;
481	idesc.id_numfrags = frags;
482	(void)pass4check(&idesc);
483}
484
485/*
486 * Find a pathname
487 */
488void
489getpathname(namebuf, curdir, ino)
490	char *namebuf;
491	ino_t curdir, ino;
492{
493	int len;
494	register char *cp;
495	struct inodesc idesc;
496	static int busy = 0;
497
498	if (curdir == ino && ino == ROOTINO) {
499		(void)strcpy(namebuf, "/");
500		return;
501	}
502	if (busy ||
503	    (inoinfo(curdir)->ino_state != DSTATE &&
504	     inoinfo(curdir)->ino_state != DFOUND)) {
505		(void)strcpy(namebuf, "?");
506		return;
507	}
508	busy = 1;
509	memset(&idesc, 0, sizeof(struct inodesc));
510	idesc.id_type = DATA;
511	idesc.id_fix = IGNORE;
512	cp = &namebuf[MAXPATHLEN - 1];
513	*cp = '\0';
514	if (curdir != ino) {
515		idesc.id_parent = curdir;
516		goto namelookup;
517	}
518	while (ino != ROOTINO) {
519		idesc.id_number = ino;
520		idesc.id_func = findino;
521		idesc.id_name = "..";
522		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
523			break;
524	namelookup:
525		idesc.id_number = idesc.id_parent;
526		idesc.id_parent = ino;
527		idesc.id_func = findname;
528		idesc.id_name = namebuf;
529		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
530			break;
531		len = strlen(namebuf);
532		cp -= len;
533		memmove(cp, namebuf, (size_t)len);
534		*--cp = '/';
535		if (cp < &namebuf[MAXNAMLEN])
536			break;
537		ino = idesc.id_number;
538	}
539	busy = 0;
540	if (ino != ROOTINO)
541		*--cp = '?';
542	memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
543}
544
545void
546catch(sig)
547	int sig;
548{
549	if (!doinglevel2)
550		ckfini(0);
551	exit(12);
552}
553
554/*
555 * When preening, allow a single quit to signal
556 * a special exit after filesystem checks complete
557 * so that reboot sequence may be interrupted.
558 */
559void
560catchquit(sig)
561	int sig;
562{
563	printf("returning to single-user after filesystem check\n");
564	returntosingle = 1;
565	(void)signal(SIGQUIT, SIG_DFL);
566}
567
568/*
569 * determine whether an inode should be fixed.
570 */
571int
572dofix(idesc, msg)
573	register struct inodesc *idesc;
574	char *msg;
575{
576
577	switch (idesc->id_fix) {
578
579	case DONTKNOW:
580		if (idesc->id_type == DATA)
581			direrror(idesc->id_number, msg);
582		else
583			pwarn("%s", msg);
584		if (preen) {
585			printf(" (SALVAGED)\n");
586			idesc->id_fix = FIX;
587			return (ALTERED);
588		}
589		if (reply("SALVAGE") == 0) {
590			idesc->id_fix = NOFIX;
591			return (0);
592		}
593		idesc->id_fix = FIX;
594		return (ALTERED);
595
596	case FIX:
597		return (ALTERED);
598
599	case NOFIX:
600	case IGNORE:
601		return (0);
602
603	default:
604		errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
605	}
606	/* NOTREACHED */
607	return (0);
608}
609
610#if __STDC__
611#include <stdarg.h>
612#else
613#include <varargs.h>
614#endif
615
616/*
617 * An unexpected inconsistency occured.
618 * Die if preening or filesystem is running with soft dependency protocol,
619 * otherwise just print message and continue.
620 */
621void
622#if __STDC__
623pfatal(const char *fmt, ...)
624#else
625pfatal(fmt, va_alist)
626	char *fmt;
627	va_dcl
628#endif
629{
630	va_list ap;
631#if __STDC__
632	va_start(ap, fmt);
633#else
634	va_start(ap);
635#endif
636	if (!preen) {
637		(void)vfprintf(stdout, fmt, ap);
638		va_end(ap);
639		if (usedsoftdep)
640			(void)fprintf(stdout,
641			    "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n");
642		/*
643		 * Force foreground fsck to clean up inconsistency.
644		 */
645		if (bkgrdflag) {
646			cmd.value = FS_NEEDSFSCK;
647			cmd.size = 1;
648			if (sysctlbyname("vfs.ffs.setflags", 0, 0,
649			    &cmd, sizeof cmd) == -1)
650				pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
651			fprintf(stdout, "CANNOT RUN IN BACKGROUND\n");
652			ckfini(0);
653			exit(EEXIT);
654		}
655		return;
656	}
657	if (cdevname == NULL)
658		cdevname = "fsck";
659	(void)fprintf(stdout, "%s: ", cdevname);
660	(void)vfprintf(stdout, fmt, ap);
661	(void)fprintf(stdout,
662	    "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n",
663	    cdevname, usedsoftdep ? " SOFT UPDATE " : " ");
664	/*
665	 * Force foreground fsck to clean up inconsistency.
666	 */
667	if (bkgrdflag) {
668		cmd.value = FS_NEEDSFSCK;
669		cmd.size = 1;
670		if (sysctlbyname("vfs.ffs.setflags", 0, 0,
671		    &cmd, sizeof cmd) == -1)
672			pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
673	}
674	ckfini(0);
675	exit(EEXIT);
676}
677
678/*
679 * Pwarn just prints a message when not preening or running soft dependency
680 * protocol, or a warning (preceded by filename) when preening.
681 */
682void
683#if __STDC__
684pwarn(const char *fmt, ...)
685#else
686pwarn(fmt, va_alist)
687	char *fmt;
688	va_dcl
689#endif
690{
691	va_list ap;
692#if __STDC__
693	va_start(ap, fmt);
694#else
695	va_start(ap);
696#endif
697	if (preen)
698		(void)fprintf(stdout, "%s: ", cdevname);
699	(void)vfprintf(stdout, fmt, ap);
700	va_end(ap);
701}
702
703/*
704 * Stub for routines from kernel.
705 */
706void
707#if __STDC__
708panic(const char *fmt, ...)
709#else
710panic(fmt, va_alist)
711	char *fmt;
712	va_dcl
713#endif
714{
715	va_list ap;
716#if __STDC__
717	va_start(ap, fmt);
718#else
719	va_start(ap);
720#endif
721	pfatal("INTERNAL INCONSISTENCY:");
722	(void)vfprintf(stdout, fmt, ap);
723	va_end(ap);
724	exit(EEXIT);
725}
726