utilities.c revision 23675
1251886Speter/*
2251886Speter * Copyright (c) 1980, 1986, 1993
3251886Speter *	The Regents of the University of California.  All rights reserved.
4251886Speter *
5251886Speter * Redistribution and use in source and binary forms, with or without
6251886Speter * modification, are permitted provided that the following conditions
7251886Speter * are met:
8251886Speter * 1. Redistributions of source code must retain the above copyright
9251886Speter *    notice, this list of conditions and the following disclaimer.
10251886Speter * 2. Redistributions in binary form must reproduce the above copyright
11251886Speter *    notice, this list of conditions and the following disclaimer in the
12251886Speter *    documentation and/or other materials provided with the distribution.
13251886Speter * 3. All advertising materials mentioning features or use of this software
14251886Speter *    must display the following acknowledgement:
15251886Speter *	This product includes software developed by the University of
16251886Speter *	California, Berkeley and its contributors.
17251886Speter * 4. Neither the name of the University nor the names of its contributors
18251886Speter *    may be used to endorse or promote products derived from this software
19251886Speter *    without specific prior written permission.
20251886Speter *
21251886Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22251886Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23251886Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24251886Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25251886Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26251886Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27251886Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28251886Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29251886Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30251886Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31251886Speter * SUCH DAMAGE.
32251886Speter */
33251886Speter
34251886Speter#ifndef lint
35251886Speterstatic const char sccsid[] = "@(#)utilities.c	8.6 (Berkeley) 5/19/95";
36251886Speter#endif /* not lint */
37251886Speter
38251886Speter#include <sys/param.h>
39251886Speter#include <sys/time.h>
40251886Speter
41251886Speter#include <ufs/ufs/dinode.h>
42251886Speter#include <ufs/ufs/dir.h>
43251886Speter#include <ufs/ffs/fs.h>
44251886Speter#include <stdio.h>
45#include <stdlib.h>
46#include <unistd.h>
47#include <stdarg.h>
48#include <string.h>
49#include <ctype.h>
50#include <err.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		return (0);
94	}
95	if (yflag || (persevere && nflag)) {
96		printf("%s? yes\n\n", question);
97		return (1);
98	}
99	do	{
100		printf("%s? [yn] ", question);
101		(void) fflush(stdout);
102		c = getc(stdin);
103		while (c != '\n' && getc(stdin) != '\n')
104			if (feof(stdin))
105				return (0);
106	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
107	printf("\n");
108	if (c == 'y' || c == 'Y')
109		return (1);
110	return (0);
111}
112
113/*
114 * Malloc buffers and set up cache.
115 */
116void
117bufinit()
118{
119	register struct bufarea *bp;
120	long bufcnt, i;
121	char *bufp;
122
123	pbp = pdirbp = (struct bufarea *)0;
124	bufp = malloc((unsigned int)sblock.fs_bsize);
125	if (bufp == 0)
126		errx(EEXIT, "cannot allocate buffer pool");
127	cgblk.b_un.b_buf = bufp;
128	initbarea(&cgblk);
129	bufhead.b_next = bufhead.b_prev = &bufhead;
130	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
131	if (bufcnt < MINBUFS)
132		bufcnt = MINBUFS;
133	for (i = 0; i < bufcnt; i++) {
134		bp = (struct bufarea *)malloc(sizeof(struct bufarea));
135		bufp = malloc((unsigned int)sblock.fs_bsize);
136		if (bp == NULL || bufp == NULL) {
137			if (i >= MINBUFS)
138				break;
139			errx(EEXIT, "cannot allocate buffer pool");
140		}
141		bp->b_un.b_buf = bufp;
142		bp->b_prev = &bufhead;
143		bp->b_next = bufhead.b_next;
144		bufhead.b_next->b_prev = bp;
145		bufhead.b_next = bp;
146		initbarea(bp);
147	}
148	bufhead.b_size = i;	/* save number of buffers */
149}
150
151/*
152 * Manage a cache of directory blocks.
153 */
154struct bufarea *
155getdatablk(blkno, size)
156	ufs_daddr_t blkno;
157	long size;
158{
159	register struct bufarea *bp;
160
161	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
162		if (bp->b_bno == fsbtodb(&sblock, blkno))
163			goto foundit;
164	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
165		if ((bp->b_flags & B_INUSE) == 0)
166			break;
167	if (bp == &bufhead)
168		errx(EEXIT, "deadlocked buffer pool");
169	getblk(bp, blkno, size);
170	/* fall through */
171foundit:
172	totalreads++;
173	bp->b_prev->b_next = bp->b_next;
174	bp->b_next->b_prev = bp->b_prev;
175	bp->b_prev = &bufhead;
176	bp->b_next = bufhead.b_next;
177	bufhead.b_next->b_prev = bp;
178	bufhead.b_next = bp;
179	bp->b_flags |= B_INUSE;
180	return (bp);
181}
182
183void
184getblk(bp, blk, size)
185	register struct bufarea *bp;
186	ufs_daddr_t blk;
187	long size;
188{
189	ufs_daddr_t dblk;
190
191	dblk = fsbtodb(&sblock, blk);
192	if (bp->b_bno != dblk) {
193		flush(fswritefd, bp);
194		diskreads++;
195		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
196		bp->b_bno = dblk;
197		bp->b_size = size;
198	}
199}
200
201void
202flush(fd, bp)
203	int fd;
204	register struct bufarea *bp;
205{
206	register int i, j;
207
208	if (!bp->b_dirty)
209		return;
210	if (bp->b_errs != 0)
211		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
212		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
213		    bp->b_bno);
214	bp->b_dirty = 0;
215	bp->b_errs = 0;
216	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
217	if (bp != &sblk)
218		return;
219	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
220		bwrite(fswritefd, (char *)sblock.fs_csp[j],
221		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
222		    sblock.fs_cssize - i < sblock.fs_bsize ?
223		    sblock.fs_cssize - i : sblock.fs_bsize);
224	}
225}
226
227static void
228rwerror(mesg, blk)
229	char *mesg;
230	ufs_daddr_t blk;
231{
232
233	if (preen == 0)
234		printf("\n");
235	pfatal("CANNOT %s: BLK %ld", mesg, blk);
236	if (reply("CONTINUE") == 0)
237		exit(EEXIT);
238}
239
240void
241ckfini(markclean)
242	int markclean;
243{
244	register struct bufarea *bp, *nbp;
245	int ofsmodified, cnt = 0;
246
247	if (fswritefd < 0) {
248		(void)close(fsreadfd);
249		return;
250	}
251	flush(fswritefd, &sblk);
252	if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
253	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
254		sblk.b_bno = SBOFF / dev_bsize;
255		sbdirty();
256		flush(fswritefd, &sblk);
257	}
258	flush(fswritefd, &cgblk);
259	free(cgblk.b_un.b_buf);
260	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
261		cnt++;
262		flush(fswritefd, bp);
263		nbp = bp->b_prev;
264		free(bp->b_un.b_buf);
265		free((char *)bp);
266	}
267	if (bufhead.b_size != cnt)
268		errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt);
269	pbp = pdirbp = (struct bufarea *)0;
270	if (markclean && sblock.fs_clean == 0) {
271		sblock.fs_clean = 1;
272		sbdirty();
273		ofsmodified = fsmodified;
274		flush(fswritefd, &sblk);
275		fsmodified = ofsmodified;
276		if (!preen)
277			printf("\n***** FILE SYSTEM MARKED CLEAN *****\n");
278	}
279	if (debug)
280		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
281		    totalreads, (int)(diskreads * 100 / totalreads));
282	(void)close(fsreadfd);
283	(void)close(fswritefd);
284}
285
286int
287bread(fd, buf, blk, size)
288	int fd;
289	char *buf;
290	ufs_daddr_t blk;
291	long size;
292{
293	char *cp;
294	int i, errs;
295	off_t offset;
296
297	offset = blk;
298	offset *= dev_bsize;
299	if (lseek(fd, offset, 0) < 0)
300		rwerror("SEEK", blk);
301	else if (read(fd, buf, (int)size) == size)
302		return (0);
303	rwerror("READ", blk);
304	if (lseek(fd, offset, 0) < 0)
305		rwerror("SEEK", blk);
306	errs = 0;
307	memset(buf, 0, (size_t)size);
308	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
309	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
310		if (read(fd, cp, (int)secsize) != secsize) {
311			(void)lseek(fd, offset + i + secsize, 0);
312			if (secsize != dev_bsize && dev_bsize != 1)
313				printf(" %ld (%ld),",
314				    (blk * dev_bsize + i) / secsize,
315				    blk + i / dev_bsize);
316			else
317				printf(" %ld,", blk + i / dev_bsize);
318			errs++;
319		}
320	}
321	printf("\n");
322	return (errs);
323}
324
325void
326bwrite(fd, buf, blk, size)
327	int fd;
328	char *buf;
329	ufs_daddr_t blk;
330	long size;
331{
332	int i;
333	char *cp;
334	off_t offset;
335
336	if (fd < 0)
337		return;
338	offset = blk;
339	offset *= dev_bsize;
340	if (lseek(fd, offset, 0) < 0)
341		rwerror("SEEK", blk);
342	else if (write(fd, buf, (int)size) == size) {
343		fsmodified = 1;
344		return;
345	}
346	rwerror("WRITE", blk);
347	if (lseek(fd, offset, 0) < 0)
348		rwerror("SEEK", blk);
349	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
350	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
351		if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
352			(void)lseek(fd, offset + i + dev_bsize, 0);
353			printf(" %ld,", blk + i / dev_bsize);
354		}
355	printf("\n");
356	return;
357}
358
359/*
360 * allocate a data block with the specified number of fragments
361 */
362ufs_daddr_t
363allocblk(frags)
364	long frags;
365{
366	register int i, j, k;
367
368	if (frags <= 0 || frags > sblock.fs_frag)
369		return (0);
370	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
371		for (j = 0; j <= sblock.fs_frag - frags; j++) {
372			if (testbmap(i + j))
373				continue;
374			for (k = 1; k < frags; k++)
375				if (testbmap(i + j + k))
376					break;
377			if (k < frags) {
378				j += k;
379				continue;
380			}
381			for (k = 0; k < frags; k++)
382				setbmap(i + j + k);
383			n_blks += frags;
384			return (i + j);
385		}
386	}
387	return (0);
388}
389
390/*
391 * Free a previously allocated block
392 */
393void
394freeblk(blkno, frags)
395	ufs_daddr_t blkno;
396	long frags;
397{
398	struct inodesc idesc;
399
400	idesc.id_blkno = blkno;
401	idesc.id_numfrags = frags;
402	(void)pass4check(&idesc);
403}
404
405/*
406 * Find a pathname
407 */
408void
409getpathname(namebuf, curdir, ino)
410	char *namebuf;
411	ino_t curdir, ino;
412{
413	int len;
414	register char *cp;
415	struct inodesc idesc;
416	static int busy = 0;
417
418	if (curdir == ino && ino == ROOTINO) {
419		(void)strcpy(namebuf, "/");
420		return;
421	}
422	if (busy ||
423	    (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
424		(void)strcpy(namebuf, "?");
425		return;
426	}
427	busy = 1;
428	memset(&idesc, 0, sizeof(struct inodesc));
429	idesc.id_type = DATA;
430	idesc.id_fix = IGNORE;
431	cp = &namebuf[MAXPATHLEN - 1];
432	*cp = '\0';
433	if (curdir != ino) {
434		idesc.id_parent = curdir;
435		goto namelookup;
436	}
437	while (ino != ROOTINO) {
438		idesc.id_number = ino;
439		idesc.id_func = findino;
440		idesc.id_name = "..";
441		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
442			break;
443	namelookup:
444		idesc.id_number = idesc.id_parent;
445		idesc.id_parent = ino;
446		idesc.id_func = findname;
447		idesc.id_name = namebuf;
448		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
449			break;
450		len = strlen(namebuf);
451		cp -= len;
452		memmove(cp, namebuf, (size_t)len);
453		*--cp = '/';
454		if (cp < &namebuf[MAXNAMLEN])
455			break;
456		ino = idesc.id_number;
457	}
458	busy = 0;
459	if (ino != ROOTINO)
460		*--cp = '?';
461	memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
462}
463
464void
465catch(sig)
466	int sig;
467{
468	if (!doinglevel2)
469		ckfini(0);
470	exit(12);
471}
472
473/*
474 * When preening, allow a single quit to signal
475 * a special exit after filesystem checks complete
476 * so that reboot sequence may be interrupted.
477 */
478void
479catchquit(sig)
480	int sig;
481{
482	printf("returning to single-user after filesystem check\n");
483	returntosingle = 1;
484	(void)signal(SIGQUIT, SIG_DFL);
485}
486
487/*
488 * Ignore a single quit signal; wait and flush just in case.
489 * Used by child processes in preen.
490 */
491void
492voidquit(sig)
493	int sig;
494{
495
496	sleep(1);
497	(void)signal(SIGQUIT, SIG_IGN);
498	(void)signal(SIGQUIT, SIG_DFL);
499}
500
501/*
502 * determine whether an inode should be fixed.
503 */
504int
505dofix(idesc, msg)
506	register struct inodesc *idesc;
507	char *msg;
508{
509
510	switch (idesc->id_fix) {
511
512	case DONTKNOW:
513		if (idesc->id_type == DATA)
514			direrror(idesc->id_number, msg);
515		else
516			pwarn(msg);
517		if (preen) {
518			printf(" (SALVAGED)\n");
519			idesc->id_fix = FIX;
520			return (ALTERED);
521		}
522		if (reply("SALVAGE") == 0) {
523			idesc->id_fix = NOFIX;
524			return (0);
525		}
526		idesc->id_fix = FIX;
527		return (ALTERED);
528
529	case FIX:
530		return (ALTERED);
531
532	case NOFIX:
533	case IGNORE:
534		return (0);
535
536	default:
537		errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
538	}
539	/* NOTREACHED */
540	return (0);
541}
542
543#if __STDC__
544#include <stdarg.h>
545#else
546#include <varargs.h>
547#endif
548
549/*
550 * An unexpected inconsistency occured.
551 * Die if preening, otherwise just print message and continue.
552 */
553void
554#if __STDC__
555pfatal(const char *fmt, ...)
556#else
557pfatal(fmt, va_alist)
558	char *fmt;
559	va_dcl
560#endif
561{
562	va_list ap;
563#if __STDC__
564	va_start(ap, fmt);
565#else
566	va_start(ap);
567#endif
568	if (!preen) {
569		(void)vfprintf(stderr, fmt, ap);
570		va_end(ap);
571		return;
572	}
573	(void)fprintf(stderr, "%s: ", cdevname);
574	(void)vfprintf(stderr, fmt, ap);
575	(void)fprintf(stderr,
576	    "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
577	    cdevname);
578	exit(EEXIT);
579}
580
581/*
582 * Pwarn just prints a message when not preening,
583 * or a warning (preceded by filename) when preening.
584 */
585void
586#if __STDC__
587pwarn(const char *fmt, ...)
588#else
589pwarn(fmt, va_alist)
590	char *fmt;
591	va_dcl
592#endif
593{
594	va_list ap;
595#if __STDC__
596	va_start(ap, fmt);
597#else
598	va_start(ap);
599#endif
600	if (preen)
601		(void)fprintf(stderr, "%s: ", cdevname);
602	(void)vfprintf(stderr, fmt, ap);
603	va_end(ap);
604}
605
606/*
607 * Stub for routines from kernel.
608 */
609void
610#if __STDC__
611panic(const char *fmt, ...)
612#else
613panic(fmt, va_alist)
614	char *fmt;
615	va_dcl
616#endif
617{
618	va_list ap;
619#if __STDC__
620	va_start(ap, fmt);
621#else
622	va_start(ap);
623#endif
624	pfatal("INTERNAL INCONSISTENCY:");
625	(void)vfprintf(stderr, fmt, ap);
626	va_end(ap);
627	exit(EEXIT);
628}
629