GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/fsck_ext2fs/utilities.c Lines: 0 239 0.0 %
Date: 2017-11-07 Branches: 0 153 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: utilities.c,v 1.27 2016/03/16 15:41:10 krw 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
52
long	diskreads, totalreads;	/* Disk cache statistics */
53
54
static void rwerror(char *, daddr32_t);
55
56
int
57
ftypeok(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
77
int
78
reply(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
 */
116
void
117
bufinit(void)
118
{
119
	struct bufarea *bp;
120
	long bufcnt, i;
121
	char *bufp;
122
123
	diskreads = totalreads = 0;
124
	pbp = pdirbp = NULL;
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
 */
152
struct bufarea *
153
getdatablk(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 */
168
foundit:
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
180
void
181
getblk(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
195
void
196
flush(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
219
static void
220
rwerror(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
230
void
231
ckfini(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 = NULL;
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
281
int
282
bread(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
312
void
313
bwrite(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
 */
346
int
347
allocblk(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
 */
364
void
365
freeblk(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
 */
377
void
378
getpathname(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*/
433
void
434
catch(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*/
446
void
447
catchquit(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*/
463
void
464
voidquit(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
 */
477
int
478
dofix(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
}