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