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