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