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