xref: /minix/sbin/fsck_ext2fs/utilities.c (revision 84d9c625)
1 /*	$NetBSD: utilities.c,v 1.23 2013/06/23 02:06:04 dholland 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.23 2013/06/23 02:06:04 dholland 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
ftypeok(struct ext2fs_dinode * dp)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
reply(const char * question)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
bufinit(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 *
getdatablk(daddr_t blkno,long size)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 == EXT2_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
getblk(struct bufarea * bp,daddr_t blk,long size)209 getblk(struct bufarea *bp, daddr_t blk, long size)
210 {
211 	daddr_t dblk;
212 
213 	dblk = EXT2_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
flush(int fd,struct bufarea * bp)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 		    EXT2_FSBTODB(&sblock, ((sblock.e2fs_bsize>1024)?0:1)+i+1),
242 		    sblock.e2fs_bsize);
243 	}
244 }
245 
246 static void
rwerror(const char * mesg,daddr_t blk)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
ckfini(int markclean)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
bread(int fd,char * buf,daddr_t blk,long size)309 bread(int fd, char *buf, daddr_t blk, long size)
310 {
311 	char *cp;
312 	int i, errs;
313 	off_t offset;
314 
315 	offset = blk;
316 	offset *= dev_bsize;
317 	if (lseek(fd, offset, 0) < 0)
318 		rwerror("SEEK", blk);
319 	else if (read(fd, buf, (int)size) == size)
320 		return (0);
321 	rwerror("READ", blk);
322 	if (lseek(fd, offset, 0) < 0)
323 		rwerror("SEEK", blk);
324 	errs = 0;
325 	memset(buf, 0, (size_t)size);
326 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
327 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
328 		if (read(fd, cp, (int)secsize) != secsize) {
329 			(void)lseek(fd, offset + i + secsize, 0);
330 			if (secsize != dev_bsize && dev_bsize != 1)
331 				printf(" %lld (%lld),",
332 				    (long long)((blk*dev_bsize + i) / secsize),
333 				    (long long)(blk + i / dev_bsize));
334 			else
335 				printf(" %lld,", (long long)(blk +
336 							i / dev_bsize));
337 			errs++;
338 		}
339 	}
340 	printf("\n");
341 	return (errs);
342 }
343 
344 void
bwrite(int fd,char * buf,daddr_t blk,long size)345 bwrite(int fd, char *buf, daddr_t blk, 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(" %lld,", (long long)(blk + i / dev_bsize));
369 		}
370 	printf("\n");
371 	return;
372 }
373 
374 /*
375  * allocate a data block
376  */
377 int
allocblk(void)378 allocblk(void)
379 {
380 	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
freeblk(daddr_t blkno)396 freeblk(daddr_t blkno)
397 {
398 	struct inodesc idesc;
399 
400 	idesc.id_blkno = blkno;
401 	idesc.id_numfrags = 1;
402 	(void)pass4check(&idesc);
403 }
404 
405 /*
406  * Find a pathname
407  */
408 void
getpathname(char * namebuf,size_t namebuflen,ino_t curdir,ino_t ino)409 getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino)
410 {
411 	int len;
412 	char *cp;
413 	struct inodesc idesc;
414 	static int busy = 0;
415 
416 	if (curdir == ino && ino == EXT2_ROOTINO) {
417 		(void)strlcpy(namebuf, "/", namebuflen);
418 		return;
419 	}
420 	if (busy ||
421 	    (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
422 		(void)strlcpy(namebuf, "?", namebuflen);
423 		return;
424 	}
425 	busy = 1;
426 	memset(&idesc, 0, sizeof(struct inodesc));
427 	idesc.id_type = DATA;
428 	idesc.id_fix = IGNORE;
429 	cp = &namebuf[MAXPATHLEN - 1];
430 	*cp = '\0';
431 	if (curdir != ino) {
432 		idesc.id_parent = curdir;
433 		goto namelookup;
434 	}
435 	while (ino != EXT2_ROOTINO) {
436 		idesc.id_number = ino;
437 		idesc.id_func = findino;
438 		idesc.id_name = "..";
439 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
440 			break;
441 	namelookup:
442 		idesc.id_number = idesc.id_parent;
443 		idesc.id_parent = ino;
444 		idesc.id_func = findname;
445 		idesc.id_name = namebuf;
446 		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
447 			break;
448 		len = strlen(namebuf);
449 		cp -= len;
450 		memcpy(cp, namebuf, (size_t)len);
451 		*--cp = '/';
452 		if (cp < &namebuf[EXT2FS_MAXNAMLEN])
453 			break;
454 		ino = idesc.id_number;
455 	}
456 	busy = 0;
457 	if (ino != EXT2_ROOTINO)
458 		*--cp = '?';
459 	memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
460 }
461 
462 /*
463  * determine whether an inode should be fixed.
464  */
465 int
dofix(struct inodesc * idesc,const char * msg)466 dofix(struct inodesc *idesc, const char *msg)
467 {
468 
469 	switch (idesc->id_fix) {
470 
471 	case DONTKNOW:
472 		if (idesc->id_type == DATA)
473 			direrror(idesc->id_number, msg);
474 		else
475 			pwarn("%s", msg);
476 		if (preen) {
477 			printf(" (SALVAGED)\n");
478 			idesc->id_fix = FIX;
479 			return (ALTERED);
480 		}
481 		if (reply("SALVAGE") == 0) {
482 			idesc->id_fix = NOFIX;
483 			return (0);
484 		}
485 		idesc->id_fix = FIX;
486 		return (ALTERED);
487 
488 	case FIX:
489 		return (ALTERED);
490 
491 	case NOFIX:
492 	case IGNORE:
493 		return (0);
494 
495 	default:
496 		errexit("UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
497 	}
498 	/* NOTREACHED */
499 }
500