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