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