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