xref: /openbsd-src/sbin/fsck_ext2fs/utilities.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: utilities.c,v 1.8 2001/01/31 22:31:51 deraadt Exp $	*/
2 /*	$NetBSD: utilities.c,v 1.1 1997/06/11 11:22:02 bouyer 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. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)utilities.c	8.1 (Berkeley) 6/5/93";
41 #else
42 #if 0
43 static char rcsid[] = "$NetBSD: utilities.c,v 1.1 1997/06/11 11:22:02 bouyer Exp $";
44 #else
45 static char rcsid[] = "$OpenBSD: utilities.c,v 1.8 2001/01/31 22:31:51 deraadt Exp $";
46 #endif
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <ufs/ext2fs/ext2fs_dinode.h>
53 #include <ufs/ext2fs/ext2fs_dir.h>
54 #include <ufs/ext2fs/ext2fs.h>
55 #include <ufs/ufs/dinode.h> /* for IFMT & friends */
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <ctype.h>
60 #include <unistd.h>
61 
62 #include "fsutil.h"
63 #include "fsck.h"
64 #include "extern.h"
65 
66 long	diskreads, totalreads;	/* Disk cache statistics */
67 
68 static void rwerror __P((char *, daddr_t));
69 
70 int
71 ftypeok(dp)
72 	struct ext2fs_dinode *dp;
73 {
74 	switch (fs2h16(dp->e2di_mode) & IFMT) {
75 
76 	case IFDIR:
77 	case IFREG:
78 	case IFBLK:
79 	case IFCHR:
80 	case IFLNK:
81 	case IFSOCK:
82 	case IFIFO:
83 		return (1);
84 
85 	default:
86 		if (debug)
87 			printf("bad file type 0%o\n", dp->e2di_mode);
88 		return (0);
89 	}
90 }
91 
92 int
93 reply(question)
94 	char *question;
95 {
96 	int persevere;
97 	char c;
98 
99 	if (preen)
100 		pfatal("INTERNAL ERROR: GOT TO reply()");
101 	persevere = !strcmp(question, "CONTINUE");
102 	printf("\n");
103 	if (!persevere && (nflag || fswritefd < 0)) {
104 		printf("%s? no\n\n", question);
105 		return (0);
106 	}
107 	if (yflag || (persevere && nflag)) {
108 		printf("%s? yes\n\n", question);
109 		return (1);
110 	}
111 	do	{
112 		printf("%s? [yn] ", question);
113 		(void) fflush(stdout);
114 		c = getc(stdin);
115 		while (c != '\n' && getc(stdin) != '\n')
116 			if (feof(stdin))
117 				return (0);
118 	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
119 	printf("\n");
120 	if (c == 'y' || c == 'Y')
121 		return (1);
122 	return (0);
123 }
124 
125 /*
126  * Malloc buffers and set up cache.
127  */
128 void
129 bufinit()
130 {
131 	register struct bufarea *bp;
132 	long bufcnt, i;
133 	char *bufp;
134 
135 	diskreads = totalreads = 0;
136 	pbp = pdirbp = (struct bufarea *)0;
137 	bufhead.b_next = bufhead.b_prev = &bufhead;
138 	bufcnt = MAXBUFSPACE / sblock.e2fs_bsize;
139 	if (bufcnt < MINBUFS)
140 		bufcnt = MINBUFS;
141 	for (i = 0; i < bufcnt; i++) {
142 		bp = (struct bufarea *)malloc(sizeof(struct bufarea));
143 		bufp = malloc((unsigned int)sblock.e2fs_bsize);
144 		if (bp == NULL || bufp == NULL) {
145 			if (i >= MINBUFS)
146 				break;
147 			errexit("cannot allocate buffer pool\n");
148 		}
149 		bp->b_un.b_buf = bufp;
150 		bp->b_prev = &bufhead;
151 		bp->b_next = bufhead.b_next;
152 		bufhead.b_next->b_prev = bp;
153 		bufhead.b_next = bp;
154 		initbarea(bp);
155 	}
156 	bufhead.b_size = i;	/* save number of buffers */
157 }
158 
159 /*
160  * Manage a cache of directory blocks.
161  */
162 struct bufarea *
163 getdatablk(blkno, size)
164 	daddr_t blkno;
165 	long size;
166 {
167 	register struct bufarea *bp;
168 
169 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
170 		if (bp->b_bno == fsbtodb(&sblock, blkno))
171 			goto foundit;
172 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
173 		if ((bp->b_flags & B_INUSE) == 0)
174 			break;
175 	if (bp == &bufhead)
176 		errexit("deadlocked buffer pool\n");
177 	getblk(bp, blkno, size);
178 	diskreads++;
179 	/* fall through */
180 foundit:
181 	totalreads++;
182 	bp->b_prev->b_next = bp->b_next;
183 	bp->b_next->b_prev = bp->b_prev;
184 	bp->b_prev = &bufhead;
185 	bp->b_next = bufhead.b_next;
186 	bufhead.b_next->b_prev = bp;
187 	bufhead.b_next = bp;
188 	bp->b_flags |= B_INUSE;
189 	return (bp);
190 }
191 
192 void
193 getblk(bp, blk, size)
194 	register struct bufarea *bp;
195 	daddr_t blk;
196 	long size;
197 {
198 	daddr_t dblk;
199 
200 	dblk = fsbtodb(&sblock, blk);
201 	if (bp->b_bno != dblk) {
202 		flush(fswritefd, bp);
203 		diskreads++;
204 		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
205 		bp->b_bno = dblk;
206 		bp->b_size = size;
207 	}
208 }
209 
210 void
211 flush(fd, bp)
212 	int fd;
213 	register struct bufarea *bp;
214 {
215 	register int i;
216 
217 	if (!bp->b_dirty)
218 		return;
219 	if (bp->b_errs != 0)
220 		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
221 		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
222 		    bp->b_bno);
223 	bp->b_dirty = 0;
224 	bp->b_errs = 0;
225 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
226 	if (bp != &sblk)
227 		return;
228 	for (i = 0; i < sblock.e2fs_ngdb; i++) {
229 		bwrite(fswritefd, (char *)
230 			&sblock.e2fs_gd[i* sblock.e2fs_bsize / sizeof(struct ext2_gd)],
231 		    fsbtodb(&sblock, ((sblock.e2fs_bsize>1024)?0:1)+i+1),
232 		    sblock.e2fs_bsize);
233 	}
234 }
235 
236 static void
237 rwerror(mesg, blk)
238 	char *mesg;
239 	daddr_t blk;
240 {
241 
242 	if (preen == 0)
243 		printf("\n");
244 	pfatal("CANNOT %s: BLK %d", mesg, blk);
245 	if (reply("CONTINUE") == 0)
246 		errexit("Program terminated\n");
247 }
248 
249 void
250 ckfini(markclean)
251 	int markclean;
252 {
253 	register struct bufarea *bp, *nbp;
254 	int cnt = 0;
255 
256 	if (fswritefd < 0) {
257 		(void)close(fsreadfd);
258 		return;
259 	}
260 	flush(fswritefd, &sblk);
261 	if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
262 	    !preen && reply("UPDATE STANDARD SUPERBLOCKS")) {
263 		sblk.b_bno = SBOFF / dev_bsize;
264 		sbdirty();
265 		flush(fswritefd, &sblk);
266 		copyback_sb(&asblk);
267 		asblk.b_dirty = 1;
268 		flush(fswritefd, &asblk);
269 	}
270 	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
271 		cnt++;
272 		flush(fswritefd, bp);
273 		nbp = bp->b_prev;
274 		free(bp->b_un.b_buf);
275 		free((char *)bp);
276 	}
277 	if (bufhead.b_size != cnt)
278 		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
279 	pbp = pdirbp = (struct bufarea *)0;
280 	if (markclean && (sblock.e2fs.e2fs_state & E2FS_ISCLEAN) == 0) {
281 		/*
282 		 * Mark the file system as clean, and sync the superblock.
283 		 */
284 		if (preen)
285 			pwarn("MARKING FILE SYSTEM CLEAN\n");
286 		else if (!reply("MARK FILE SYSTEM CLEAN"))
287 			markclean = 0;
288 		if (markclean) {
289 			sblock.e2fs.e2fs_state = E2FS_ISCLEAN;
290 			sbdirty();
291 			flush(fswritefd, &sblk);
292 		}
293 	}
294 	if (debug)
295 		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
296 		    totalreads, (int)(diskreads * 100 / totalreads));
297 	(void)close(fsreadfd);
298 	(void)close(fswritefd);
299 }
300 
301 int
302 bread(fd, buf, blk, size)
303 	int fd;
304 	char *buf;
305 	daddr_t blk;
306 	long size;
307 {
308 	char *cp;
309 	int i, errs;
310 	off_t offset;
311 
312 	offset = blk;
313 	offset *= dev_bsize;
314 	if (lseek(fd, offset, 0) < 0)
315 		rwerror("SEEK", blk);
316 	else if (read(fd, buf, (int)size) == size)
317 		return (0);
318 	rwerror("READ", blk);
319 	if (lseek(fd, offset, 0) < 0)
320 		rwerror("SEEK", blk);
321 	errs = 0;
322 	memset(buf, 0, (size_t)size);
323 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
324 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
325 		if (read(fd, cp, (int)secsize) != secsize) {
326 			(void)lseek(fd, offset + i + secsize, 0);
327 			if (secsize != dev_bsize && dev_bsize != 1)
328 				printf(" %ld (%ld),",
329 				    (blk * dev_bsize + i) / secsize,
330 				    blk + i / dev_bsize);
331 			else
332 				printf(" %ld,", blk + i / dev_bsize);
333 			errs++;
334 		}
335 	}
336 	printf("\n");
337 	return (errs);
338 }
339 
340 void
341 bwrite(fd, buf, blk, size)
342 	int fd;
343 	char *buf;
344 	daddr_t blk;
345 	long size;
346 {
347 	int i;
348 	char *cp;
349 	off_t offset;
350 
351 	if (fd < 0)
352 		return;
353 	offset = blk;
354 	offset *= dev_bsize;
355 	if (lseek(fd, offset, 0) < 0)
356 		rwerror("SEEK", blk);
357 	else if (write(fd, buf, (int)size) == size) {
358 		fsmodified = 1;
359 		return;
360 	}
361 	rwerror("WRITE", blk);
362 	if (lseek(fd, offset, 0) < 0)
363 		rwerror("SEEK", blk);
364 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
365 	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
366 		if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
367 			(void)lseek(fd, offset + i + dev_bsize, 0);
368 			printf(" %ld,", blk + i / dev_bsize);
369 		}
370 	printf("\n");
371 	return;
372 }
373 
374 /*
375  * allocate a data block
376  */
377 int
378 allocblk()
379 {
380 	register int i;
381 
382 	for (i = 0; i < maxfsblock - 1; i++) {
383 		if (testbmap(i))
384 				continue;
385 		setbmap(i);
386 		n_blks ++;
387 		return (i);
388 	}
389 	return (0);
390 }
391 
392 /*
393  * Free a previously allocated block
394  */
395 void
396 freeblk(blkno)
397 	daddr_t blkno;
398 {
399 	struct inodesc idesc;
400 
401 	idesc.id_blkno = blkno;
402 	idesc.id_numfrags = 1;
403 	(void)pass4check(&idesc);
404 }
405 
406 /*
407  * Find a pathname
408  */
409 void
410 getpathname(namebuf, curdir, ino)
411 	char *namebuf;
412 	ino_t curdir, ino;
413 {
414 	int len;
415 	register char *cp;
416 	struct inodesc idesc;
417 	static int busy = 0;
418 
419 	if (curdir == ino && ino == EXT2_ROOTINO) {
420 		(void)strcpy(namebuf, "/");
421 		return;
422 	}
423 	if (busy ||
424 	    (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
425 		(void)strcpy(namebuf, "?");
426 		return;
427 	}
428 	busy = 1;
429 	memset(&idesc, 0, sizeof(struct inodesc));
430 	idesc.id_type = DATA;
431 	idesc.id_fix = IGNORE;
432 	cp = &namebuf[MAXPATHLEN - 1];
433 	*cp = '\0';
434 	if (curdir != ino) {
435 		idesc.id_parent = curdir;
436 		goto namelookup;
437 	}
438 	while (ino != EXT2_ROOTINO) {
439 		idesc.id_number = ino;
440 		idesc.id_func = findino;
441 		idesc.id_name = "..";
442 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
443 			break;
444 	namelookup:
445 		idesc.id_number = idesc.id_parent;
446 		idesc.id_parent = ino;
447 		idesc.id_func = findname;
448 		idesc.id_name = namebuf;
449 		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
450 			break;
451 		len = strlen(namebuf);
452 		cp -= len;
453 		memcpy(cp, namebuf, (size_t)len);
454 		*--cp = '/';
455 		if (cp < &namebuf[EXT2FS_MAXNAMLEN])
456 			break;
457 		ino = idesc.id_number;
458 	}
459 	busy = 0;
460 	if (ino != EXT2_ROOTINO)
461 		*--cp = '?';
462 	memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
463 }
464 
465 void
466 catch(n)
467 	int n;
468 {
469 	/* XXX signal race */
470 	ckfini(0);
471 	exit(12);
472 }
473 
474 /*
475  * When preening, allow a single quit to signal
476  * a special exit after filesystem checks complete
477  * so that reboot sequence may be interrupted.
478  */
479 void
480 catchquit(n)
481 	int n;
482 {
483 	extern int returntosingle;
484 
485 	/* XXX signal race */
486 	printf("returning to single-user after filesystem check\n");
487 	returntosingle = 1;
488 	(void)signal(SIGQUIT, SIG_DFL);
489 }
490 
491 /*
492  * Ignore a single quit signal; wait and flush just in case.
493  * Used by child processes in preen.
494  */
495 void
496 voidquit(n)
497 	int n;
498 {
499 
500 	/* XXX signal race */
501 	sleep(1);
502 	(void)signal(SIGQUIT, SIG_IGN);
503 	(void)signal(SIGQUIT, SIG_DFL);
504 }
505 
506 /*
507  * determine whether an inode should be fixed.
508  */
509 int
510 dofix(idesc, msg)
511 	register struct inodesc *idesc;
512 	char *msg;
513 {
514 
515 	switch (idesc->id_fix) {
516 
517 	case DONTKNOW:
518 		if (idesc->id_type == DATA)
519 			direrror(idesc->id_number, msg);
520 		else
521 			pwarn("%s", msg);
522 		if (preen) {
523 			printf(" (SALVAGED)\n");
524 			idesc->id_fix = FIX;
525 			return (ALTERED);
526 		}
527 		if (reply("SALVAGE") == 0) {
528 			idesc->id_fix = NOFIX;
529 			return (0);
530 		}
531 		idesc->id_fix = FIX;
532 		return (ALTERED);
533 
534 	case FIX:
535 		return (ALTERED);
536 
537 	case NOFIX:
538 	case IGNORE:
539 		return (0);
540 
541 	default:
542 		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
543 	}
544 	/* NOTREACHED */
545 }
546