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