utilities.c revision 1.19
1/*	$OpenBSD: utilities.c,v 1.19 2011/03/12 17:50:47 deraadt Exp $	*/
2/*	$NetBSD: utilities.c,v 1.6 2001/02/04 21:19:34 christos Exp $	*/
3
4/*
5 * Copyright (c) 1997 Manuel Bouyer.
6 * Copyright (c) 1980, 1986, 1993
7 *	The Regents of the University of California.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. 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#include <sys/param.h>
35#include <sys/time.h>
36#include <ufs/ext2fs/ext2fs_dinode.h>
37#include <ufs/ext2fs/ext2fs_dir.h>
38#include <ufs/ext2fs/ext2fs.h>
39#include <ufs/ufs/dinode.h> /* for IFMT & friends */
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <ctype.h>
44#include <unistd.h>
45#include <errno.h>
46
47#include "fsutil.h"
48#include "fsck.h"
49#include "extern.h"
50
51long	diskreads, totalreads;	/* Disk cache statistics */
52
53static void rwerror(char *, daddr32_t);
54
55int
56ftypeok(struct ext2fs_dinode *dp)
57{
58	switch (fs2h16(dp->e2di_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", fs2h16(dp->e2di_mode));
72		return (0);
73	}
74}
75
76int
77reply(char *question)
78{
79	int persevere;
80	int c;
81
82	if (preen)
83		pfatal("INTERNAL ERROR: GOT TO reply()");
84	persevere = !strcmp(question, "CONTINUE");
85	printf("\n");
86	if (!persevere && (nflag || fswritefd < 0)) {
87		printf("%s? no\n\n", question);
88		return (0);
89	}
90	if (yflag || (persevere && nflag)) {
91		printf("%s? yes\n\n", question);
92		return (1);
93	}
94	do {
95		printf("%s? [Fyn?] ", question);
96		(void) fflush(stdout);
97		c = getc(stdin);
98		if (c == 'F') {
99			yflag = 1;
100			return (1);
101		}
102		while (c != '\n' && getc(stdin) != '\n')
103			if (feof(stdin))
104				return (0);
105	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
106	printf("\n");
107	if (c == 'y' || c == 'Y')
108		return (1);
109	return (0);
110}
111
112/*
113 * Malloc buffers and set up cache.
114 */
115void
116bufinit(void)
117{
118	struct bufarea *bp;
119	long bufcnt, i;
120	char *bufp;
121
122	diskreads = totalreads = 0;
123	pbp = pdirbp = (struct bufarea *)0;
124	bufhead.b_next = bufhead.b_prev = &bufhead;
125	bufcnt = MAXBUFSPACE / sblock.e2fs_bsize;
126	if (bufcnt < MINBUFS)
127		bufcnt = MINBUFS;
128	for (i = 0; i < bufcnt; i++) {
129		bp = (struct bufarea *)malloc(sizeof(struct bufarea));
130		bufp = malloc((unsigned int)sblock.e2fs_bsize);
131		if (bp == NULL || bufp == NULL) {
132			free(bp);
133			free(bufp);
134			if (i >= MINBUFS)
135				break;
136			errexit("cannot allocate buffer pool\n");
137		}
138		bp->b_un.b_buf = bufp;
139		bp->b_prev = &bufhead;
140		bp->b_next = bufhead.b_next;
141		bufhead.b_next->b_prev = bp;
142		bufhead.b_next = bp;
143		initbarea(bp);
144	}
145	bufhead.b_size = i;	/* save number of buffers */
146}
147
148/*
149 * Manage a cache of directory blocks.
150 */
151struct bufarea *
152getdatablk(daddr32_t blkno, long size)
153{
154	struct bufarea *bp;
155
156	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
157		if (bp->b_bno == fsbtodb(&sblock, blkno))
158			goto foundit;
159	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
160		if ((bp->b_flags & B_INUSE) == 0)
161			break;
162	if (bp == &bufhead)
163		errexit("deadlocked buffer pool\n");
164	getblk(bp, blkno, size);
165	diskreads++;
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(struct bufarea *bp, daddr32_t blk, long size)
181{
182	daddr32_t dblk;
183
184	dblk = fsbtodb(&sblock, blk);
185	if (bp->b_bno != dblk) {
186		flush(fswritefd, bp);
187		diskreads++;
188		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
189		bp->b_bno = dblk;
190		bp->b_size = size;
191	}
192}
193
194void
195flush(int fd, struct bufarea *bp)
196{
197	int i;
198
199	if (!bp->b_dirty)
200		return;
201	if (bp->b_errs != 0)
202		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
203		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
204		    bp->b_bno);
205	bp->b_dirty = 0;
206	bp->b_errs = 0;
207	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
208	if (bp != &sblk)
209		return;
210	for (i = 0; i < sblock.e2fs_ngdb; i++) {
211		bwrite(fswritefd, (char *)
212			&sblock.e2fs_gd[i* sblock.e2fs_bsize / sizeof(struct ext2_gd)],
213		    fsbtodb(&sblock, ((sblock.e2fs_bsize>1024)?0:1)+i+1),
214		    sblock.e2fs_bsize);
215	}
216}
217
218static void
219rwerror(char *mesg, daddr32_t blk)
220{
221
222	if (preen == 0)
223		printf("\n");
224	pfatal("CANNOT %s: BLK %d", mesg, blk);
225	if (reply("CONTINUE") == 0)
226		errexit("Program terminated\n");
227}
228
229void
230ckfini(int markclean)
231{
232	struct bufarea *bp, *nbp;
233	int cnt = 0;
234
235	if (fswritefd < 0) {
236		(void)close(fsreadfd);
237		return;
238	}
239	flush(fswritefd, &sblk);
240	if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
241	    !preen && reply("UPDATE STANDARD SUPERBLOCKS")) {
242		sblk.b_bno = SBOFF / dev_bsize;
243		sbdirty();
244		flush(fswritefd, &sblk);
245		copyback_sb(&asblk);
246		asblk.b_dirty = 1;
247		flush(fswritefd, &asblk);
248	}
249	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
250		cnt++;
251		flush(fswritefd, bp);
252		nbp = bp->b_prev;
253		free(bp->b_un.b_buf);
254		free((char *)bp);
255	}
256	if (bufhead.b_size != cnt)
257		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
258	pbp = pdirbp = (struct bufarea *)0;
259	if (markclean && (sblock.e2fs.e2fs_state & E2FS_ISCLEAN) == 0) {
260		/*
261		 * Mark the file system as clean, and sync the superblock.
262		 */
263		if (preen)
264			pwarn("MARKING FILE SYSTEM CLEAN\n");
265		else if (!reply("MARK FILE SYSTEM CLEAN"))
266			markclean = 0;
267		if (markclean) {
268			sblock.e2fs.e2fs_state = E2FS_ISCLEAN;
269			sbdirty();
270			flush(fswritefd, &sblk);
271		}
272	}
273	if (debug)
274		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
275		    totalreads, (int)(diskreads * 100 / totalreads));
276	(void)close(fsreadfd);
277	(void)close(fswritefd);
278}
279
280int
281bread(int fd, char *buf, daddr32_t blk, long size)
282{
283	char *cp;
284	int i, errs;
285	off_t offset;
286
287	offset = blk;
288	offset *= dev_bsize;
289	if (lseek(fd, offset, SEEK_SET) < 0)
290		rwerror("SEEK", blk);
291	else if (read(fd, buf, (int)size) == size)
292		return (0);
293	rwerror("READ", blk);
294	if (lseek(fd, offset, SEEK_SET) < 0)
295		rwerror("SEEK", blk);
296	errs = 0;
297	memset(buf, 0, (size_t)size);
298	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
299	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
300		if (read(fd, cp, (int)secsize) != secsize) {
301			(void)lseek(fd, offset + i + secsize, SEEK_SET);
302			if (secsize != dev_bsize && dev_bsize != 1)
303				printf(" %ld (%ld),",
304				    (blk * dev_bsize + i) / secsize,
305				    blk + i / dev_bsize);
306			else
307				printf(" %ld,", blk + i / dev_bsize);
308			errs++;
309		}
310	}
311	printf("\n");
312	return (errs);
313}
314
315void
316bwrite(int fd, char *buf, daddr32_t blk, 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, SEEK_SET) < 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, SEEK_SET) < 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, SEEK_SET);
339			printf(" %ld,", blk + i / dev_bsize);
340		}
341	printf("\n");
342	return;
343}
344
345/*
346 * allocate a data block
347 */
348int
349allocblk(void)
350{
351	int i;
352
353	for (i = 0; i < maxfsblock - 1; i++) {
354		if (testbmap(i))
355				continue;
356		setbmap(i);
357		n_blks ++;
358		return (i);
359	}
360	return (0);
361}
362
363/*
364 * Free a previously allocated block
365 */
366void
367freeblk(daddr32_t blkno)
368{
369	struct inodesc idesc;
370
371	idesc.id_blkno = blkno;
372	idesc.id_numfrags = 1;
373	(void)pass4check(&idesc);
374}
375
376/*
377 * Find a pathname
378 */
379void
380getpathname(char *namebuf, size_t buflen, ino_t curdir, ino_t ino)
381{
382	size_t len;
383	char *cp;
384	struct inodesc idesc;
385	static int busy = 0;
386
387	if (curdir == ino && ino == EXT2_ROOTINO) {
388		(void)strlcpy(namebuf, "/", buflen);
389		return;
390	}
391	if (busy ||
392	    (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
393
394		(void)strlcpy(namebuf, "?", buflen);
395		return;
396	}
397	busy = 1;
398	memset(&idesc, 0, sizeof(struct inodesc));
399	idesc.id_type = DATA;
400	idesc.id_fix = IGNORE;
401	cp = &namebuf[buflen - 1];
402	*cp = '\0';
403	if (curdir != ino) {
404		idesc.id_parent = curdir;
405		goto namelookup;
406	}
407	while (ino != EXT2_ROOTINO) {
408		idesc.id_number = ino;
409		idesc.id_func = findino;
410		idesc.id_name = "..";
411		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
412			break;
413	namelookup:
414		idesc.id_number = idesc.id_parent;
415		idesc.id_parent = ino;
416		idesc.id_func = findname;
417		idesc.id_name = namebuf;
418		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
419			break;
420		len = strlen(namebuf);
421		cp -= len;
422		memcpy(cp, namebuf, len);
423		*(--cp) = '/';
424		if (cp < &namebuf[EXT2FS_MAXNAMLEN])
425			break;
426		ino = idesc.id_number;
427	}
428	busy = 0;
429	if (ino != EXT2_ROOTINO)
430		*(--cp) = '?';
431	memcpy(namebuf, cp, (size_t)(&namebuf[buflen] - cp));
432}
433
434/*ARGSUSED*/
435void
436catch(int signo)
437{
438	ckfini(0);			/* XXX signal race */
439	_exit(12);
440}
441
442/*
443 * When preening, allow a single quit to signal
444 * a special exit after filesystem checks complete
445 * so that reboot sequence may be interrupted.
446 */
447/*ARGSUSED*/
448void
449catchquit(int signo)
450{
451	extern volatile sig_atomic_t returntosingle;
452	char buf[1024];
453
454	snprintf(buf, sizeof buf,
455	    "returning to single-user after filesystem check\n");
456	write(STDOUT_FILENO, buf, strlen(buf));
457	returntosingle = 1;
458	(void)signal(SIGQUIT, SIG_DFL);
459}
460
461/*
462 * Ignore a single quit signal; wait and flush just in case.
463 * Used by child processes in preen.
464 */
465/*ARGSUSED*/
466void
467voidquit(int signo)
468{
469	int save_errno = errno;
470
471	sleep(1);
472	(void)signal(SIGQUIT, SIG_IGN);
473	(void)signal(SIGQUIT, SIG_DFL);
474	errno = save_errno;
475}
476
477/*
478 * determine whether an inode should be fixed.
479 */
480int
481dofix(struct inodesc *idesc, char *msg)
482{
483
484	switch (idesc->id_fix) {
485
486	case DONTKNOW:
487		if (idesc->id_type == DATA)
488			direrror(idesc->id_number, msg);
489		else
490			pwarn("%s", msg);
491		if (preen) {
492			printf(" (SALVAGED)\n");
493			idesc->id_fix = FIX;
494			return (ALTERED);
495		}
496		if (reply("SALVAGE") == 0) {
497			idesc->id_fix = NOFIX;
498			return (0);
499		}
500		idesc->id_fix = FIX;
501		return (ALTERED);
502
503	case FIX:
504		return (ALTERED);
505
506	case NOFIX:
507	case IGNORE:
508		return (0);
509
510	default:
511		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
512	}
513	/* NOTREACHED */
514}
515