GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/fsck_ffs/utilities.c Lines: 0 300 0.0 %
Date: 2016-12-06 Branches: 0 204 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: utilities.c,v 1.51 2015/10/15 03:10:05 deraadt Exp $	*/
2
/*	$NetBSD: utilities.c,v 1.18 1996/09/27 22:45:20 christos Exp $	*/
3
4
/*
5
 * Copyright (c) 1980, 1986, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include <sys/param.h>	/* DEV_BSIZE isset setbit clrbit */
34
#include <sys/time.h>
35
#include <sys/uio.h>
36
#include <ufs/ufs/dinode.h>
37
#include <ufs/ufs/dir.h>
38
#include <ufs/ffs/fs.h>
39
#include <signal.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
#include <ctype.h>
44
#include <unistd.h>
45
#include <limits.h>
46
#include <errno.h>
47
#include <fcntl.h>
48
#include <paths.h>
49
50
#include "fsutil.h"
51
#include "fsck.h"
52
#include "extern.h"
53
54
long	diskreads, totalreads;	/* Disk cache statistics */
55
56
static void rwerror(char *, daddr_t);
57
58
int
59
ftypeok(union dinode *dp)
60
{
61
	switch (DIP(dp, di_mode) & IFMT) {
62
	case IFDIR:
63
	case IFREG:
64
	case IFBLK:
65
	case IFCHR:
66
	case IFLNK:
67
	case IFSOCK:
68
	case IFIFO:
69
		return (1);
70
	default:
71
		if (debug)
72
			printf("bad file type 0%o\n", DIP(dp, di_mode));
73
		return (0);
74
	}
75
}
76
77
int
78
reply(char *question)
79
{
80
	int persevere, c;
81
82
	if (preen)
83
		pfatal("INTERNAL ERROR: GOT TO reply()");
84
	persevere = !strcmp(question, "CONTINUE");
85
	printf("\n");
86
	if (!persevere && (nflag || fswritefd < 0)) {
87
		printf("%s? no\n\n", question);
88
		resolved = 0;
89
		return (0);
90
	}
91
	if (yflag || (persevere && nflag)) {
92
		printf("%s? yes\n\n", question);
93
		return (1);
94
	}
95
96
	do {
97
		printf("%s? [Fyn?] ", question);
98
		(void) fflush(stdout);
99
		c = getc(stdin);
100
		if (c == 'F') {
101
			yflag = 1;
102
			return (1);
103
		}
104
		while (c != '\n' && getc(stdin) != '\n') {
105
			if (feof(stdin)) {
106
				resolved = 0;
107
				return (0);
108
			}
109
		}
110
	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
111
	printf("\n");
112
	if (c == 'y' || c == 'Y')
113
		return (1);
114
	resolved = 0;
115
	return (0);
116
}
117
118
/*
119
 * Look up state information for an inode.
120
 */
121
struct inostat *
122
inoinfo(ino_t inum)
123
{
124
	static struct inostat unallocated = { USTATE, 0, 0 };
125
	struct inostatlist *ilp;
126
	int iloff;
127
128
	if (inum > maxino)
129
		errexit("inoinfo: inumber %llu out of range",
130
		    (unsigned long long)inum);
131
	ilp = &inostathead[inum / sblock.fs_ipg];
132
	iloff = inum % sblock.fs_ipg;
133
	if (iloff >= ilp->il_numalloced)
134
		return (&unallocated);
135
	return (&ilp->il_stat[iloff]);
136
}
137
138
/*
139
 * Malloc buffers and set up cache.
140
 */
141
void
142
bufinit(void)
143
{
144
	struct bufarea *bp;
145
	long bufcnt, i;
146
	char *bufp;
147
148
	pbp = pdirbp = NULL;
149
	bufp = malloc((unsigned int)sblock.fs_bsize);
150
	if (bufp == 0)
151
		errexit("cannot allocate buffer pool\n");
152
	cgblk.b_un.b_buf = bufp;
153
	initbarea(&cgblk);
154
	bufhead.b_next = bufhead.b_prev = &bufhead;
155
	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
156
	if (bufcnt < MINBUFS)
157
		bufcnt = MINBUFS;
158
	for (i = 0; i < bufcnt; i++) {
159
		bp = malloc(sizeof(struct bufarea));
160
		bufp = malloc((unsigned int)sblock.fs_bsize);
161
		if (bp == NULL || bufp == NULL) {
162
			free(bp);
163
			free(bufp);
164
			if (i >= MINBUFS)
165
				break;
166
			errexit("cannot allocate buffer pool\n");
167
		}
168
		bp->b_un.b_buf = bufp;
169
		bp->b_prev = &bufhead;
170
		bp->b_next = bufhead.b_next;
171
		bufhead.b_next->b_prev = bp;
172
		bufhead.b_next = bp;
173
		initbarea(bp);
174
	}
175
	bufhead.b_size = i;	/* save number of buffers */
176
}
177
178
/*
179
 * Manage a cache of directory blocks.
180
 */
181
struct bufarea *
182
getdatablk(daddr_t blkno, long size)
183
{
184
	struct bufarea *bp;
185
186
	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
187
		if (bp->b_bno == fsbtodb(&sblock, blkno))
188
			goto foundit;
189
	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
190
		if ((bp->b_flags & B_INUSE) == 0)
191
			break;
192
	if (bp == &bufhead)
193
		errexit("deadlocked buffer pool\n");
194
	getblk(bp, blkno, size);
195
	/* FALLTHROUGH */
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
		diskreads++;
217
		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
218
		bp->b_bno = dblk;
219
		bp->b_size = size;
220
	}
221
}
222
223
void
224
flush(int fd, struct bufarea *bp)
225
{
226
	int i, j;
227
228
	if (!bp->b_dirty)
229
		return;
230
	if (bp->b_errs != 0)
231
		pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
232
		    (bp->b_errs == bp->b_size / DEV_BSIZE) ? "" : "PARTIALLY ",
233
		    (long long)bp->b_bno);
234
	bp->b_dirty = 0;
235
	bp->b_errs = 0;
236
	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
237
	if (bp != &sblk)
238
		return;
239
	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
240
		bwrite(fswritefd, (char *)sblock.fs_csp + i,
241
		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
242
		    sblock.fs_cssize - i < sblock.fs_bsize ?
243
		    sblock.fs_cssize - i : sblock.fs_bsize);
244
	}
245
}
246
247
static void
248
rwerror(char *mesg, daddr_t blk)
249
{
250
251
	if (preen == 0)
252
		printf("\n");
253
	pfatal("CANNOT %s: BLK %lld", mesg, (long long)blk);
254
	if (reply("CONTINUE") == 0)
255
		errexit("Program terminated\n");
256
}
257
258
void
259
ckfini(int markclean)
260
{
261
	struct bufarea *bp, *nbp;
262
	int cnt = 0;
263
	sigset_t oset, nset;
264
	int64_t sblockloc;
265
266
	sigemptyset(&nset);
267
	sigaddset(&nset, SIGINT);
268
	sigprocmask(SIG_BLOCK, &nset, &oset);
269
270
	if (fswritefd < 0) {
271
		(void)close(fsreadfd);
272
		fsreadfd = -1;
273
		sigprocmask(SIG_SETMASK, &oset, NULL);
274
		return;
275
	}
276
	if (sblock.fs_magic == FS_UFS1_MAGIC) {
277
		sblockloc = SBLOCK_UFS1;
278
		sblock.fs_ffs1_time = sblock.fs_time;
279
		sblock.fs_ffs1_size = sblock.fs_size;
280
		sblock.fs_ffs1_dsize = sblock.fs_dsize;
281
		sblock.fs_ffs1_csaddr = sblock.fs_csaddr;
282
		sblock.fs_ffs1_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
283
		sblock.fs_ffs1_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
284
		sblock.fs_ffs1_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
285
		sblock.fs_ffs1_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
286
		/* Force update on next mount */
287
		sblock.fs_ffs1_flags &= ~FS_FLAGS_UPDATED;
288
	} else
289
		sblockloc = SBLOCK_UFS2;
290
	flush(fswritefd, &sblk);
291
	if (havesb && sblk.b_bno != sblockloc / DEV_BSIZE && !preen &&
292
	    reply("UPDATE STANDARD SUPERBLOCK")) {
293
		sblk.b_bno = sblockloc / DEV_BSIZE;
294
		sbdirty();
295
		flush(fswritefd, &sblk);
296
	}
297
	flush(fswritefd, &cgblk);
298
	free(cgblk.b_un.b_buf);
299
	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
300
		cnt++;
301
		flush(fswritefd, bp);
302
		nbp = bp->b_prev;
303
		free(bp->b_un.b_buf);
304
		free(bp);
305
	}
306
	if (bufhead.b_size != cnt)
307
		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
308
	pbp = pdirbp = NULL;
309
	if (markclean && (sblock.fs_clean & FS_ISCLEAN) == 0) {
310
		/*
311
		 * Mark the file system as clean, and sync the superblock.
312
		 */
313
		if (preen)
314
			pwarn("MARKING FILE SYSTEM CLEAN\n");
315
		else if (!reply("MARK FILE SYSTEM CLEAN"))
316
			markclean = 0;
317
		if (markclean) {
318
			sblock.fs_clean = FS_ISCLEAN;
319
			sbdirty();
320
			flush(fswritefd, &sblk);
321
		}
322
	}
323
	if (debug)
324
		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
325
		    totalreads, (int)(diskreads * 100 / totalreads));
326
	(void)close(fsreadfd);
327
	fsreadfd = -1;
328
	(void)close(fswritefd);
329
	fswritefd = -1;
330
	sigprocmask(SIG_SETMASK, &oset, NULL);
331
}
332
333
int
334
bread(int fd, char *buf, daddr_t blk, long size)
335
{
336
	char *cp;
337
	int i, errs;
338
	off_t offset;
339
340
	offset = blk;
341
	offset *= DEV_BSIZE;
342
	if (pread(fd, buf, size, offset) == size)
343
		return (0);
344
	rwerror("READ", blk);
345
	errs = 0;
346
	memset(buf, 0, (size_t)size);
347
	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
348
	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
349
		if (pread(fd, cp, secsize, offset + i) != secsize) {
350
			if (secsize != DEV_BSIZE)
351
				printf(" %lld (%lld),",
352
				    (long long)(offset + i) / secsize,
353
				    (long long)blk + i / DEV_BSIZE);
354
			else
355
				printf(" %lld,", (long long)blk +
356
				    i / DEV_BSIZE);
357
			errs++;
358
		}
359
	}
360
	printf("\n");
361
	return (errs);
362
}
363
364
void
365
bwrite(int fd, char *buf, daddr_t blk, long size)
366
{
367
	int i;
368
	char *cp;
369
	off_t offset;
370
371
	if (fd < 0)
372
		return;
373
	offset = blk;
374
	offset *= DEV_BSIZE;
375
	if (pwrite(fd, buf, size, offset) == size) {
376
		fsmodified = 1;
377
		return;
378
	}
379
	rwerror("WRITE", blk);
380
	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
381
	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize)
382
		if (pwrite(fd, cp, secsize, offset + i) != secsize) {
383
			if (secsize != DEV_BSIZE)
384
				printf(" %lld (%lld),",
385
				    (long long)(offset + i) / secsize,
386
				    (long long)blk + i / DEV_BSIZE);
387
			else
388
				printf(" %lld,", (long long)blk +
389
				    i / DEV_BSIZE);
390
		}
391
	printf("\n");
392
	return;
393
}
394
395
/*
396
 * allocate a data block with the specified number of fragments
397
 */
398
daddr_t
399
allocblk(int frags)
400
{
401
	daddr_t i, baseblk;
402
	int j, k, cg;
403
	struct cg *cgp = &cgrp;
404
405
	if (frags <= 0 || frags > sblock.fs_frag)
406
		return (0);
407
	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
408
		for (j = 0; j <= sblock.fs_frag - frags; j++) {
409
			if (testbmap(i + j))
410
				continue;
411
			for (k = 1; k < frags; k++)
412
				if (testbmap(i + j + k))
413
					break;
414
			if (k < frags) {
415
				j += k;
416
				continue;
417
			}
418
			cg = dtog(&sblock, i + j);
419
			getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
420
			if (!cg_chkmagic(cgp))
421
				pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
422
			baseblk = dtogd(&sblock, i + j);
423
424
			for (k = 0; k < frags; k++) {
425
				setbmap(i + j + k);
426
				clrbit(cg_blksfree(cgp), baseblk + k);
427
			}
428
			n_blks += frags;
429
			if (frags == sblock.fs_frag)
430
				cgp->cg_cs.cs_nbfree--;
431
			else
432
				cgp->cg_cs.cs_nffree -= frags;
433
			return (i + j);
434
		}
435
	}
436
	return (0);
437
}
438
439
/*
440
 * Free a previously allocated block
441
 */
442
void
443
freeblk(daddr_t blkno, int frags)
444
{
445
	struct inodesc idesc;
446
447
	idesc.id_blkno = blkno;
448
	idesc.id_numfrags = frags;
449
	(void)pass4check(&idesc);
450
}
451
452
/*
453
 * Find a pathname
454
 */
455
void
456
getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino)
457
{
458
	int len;
459
	char *cp;
460
	struct inodesc idesc;
461
	static int busy = 0;
462
463
	if (curdir == ino && ino == ROOTINO) {
464
		(void)strlcpy(namebuf, "/", namebuflen);
465
		return;
466
	}
467
	if (busy ||
468
	    (GET_ISTATE(curdir) != DSTATE && GET_ISTATE(curdir) != DFOUND)) {
469
		(void)strlcpy(namebuf, "?", namebuflen);
470
		return;
471
	}
472
	busy = 1;
473
	memset(&idesc, 0, sizeof(struct inodesc));
474
	idesc.id_type = DATA;
475
	idesc.id_fix = IGNORE;
476
	cp = &namebuf[PATH_MAX - 1];
477
	*cp = '\0';
478
	if (curdir != ino) {
479
		idesc.id_parent = curdir;
480
		goto namelookup;
481
	}
482
	while (ino != ROOTINO) {
483
		idesc.id_number = ino;
484
		idesc.id_func = findino;
485
		idesc.id_name = "..";
486
		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
487
			break;
488
	namelookup:
489
		idesc.id_number = idesc.id_parent;
490
		idesc.id_parent = ino;
491
		idesc.id_func = findname;
492
		idesc.id_name = namebuf;
493
		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
494
			break;
495
		len = strlen(namebuf);
496
		cp -= len;
497
		memcpy(cp, namebuf, (size_t)len);
498
		*--cp = '/';
499
		if (cp < &namebuf[MAXNAMLEN])
500
			break;
501
		ino = idesc.id_number;
502
	}
503
	busy = 0;
504
	if (ino != ROOTINO)
505
		*--cp = '?';
506
	memcpy(namebuf, cp, (size_t)(&namebuf[PATH_MAX] - cp));
507
}
508
509
/*ARGSUSED*/
510
void
511
catch(int signo)
512
{
513
	ckfini(0);			/* XXX signal race */
514
	_exit(12);
515
}
516
517
/*
518
 * When preening, allow a single quit to signal
519
 * a special exit after filesystem checks complete
520
 * so that reboot sequence may be interrupted.
521
 */
522
/*ARGSUSED*/
523
void
524
catchquit(int signo)
525
{
526
	extern volatile sig_atomic_t returntosingle;
527
	static const char message[] =
528
	    "returning to single-user after filesystem check\n";
529
530
	write(STDOUT_FILENO, message, sizeof(message)-1);
531
	returntosingle = 1;
532
	(void)signal(SIGQUIT, SIG_DFL);
533
}
534
535
/*
536
 * Ignore a single quit signal; wait and flush just in case.
537
 * Used by child processes in preen.
538
 */
539
/*ARGSUSED*/
540
void
541
voidquit(int signo)
542
{
543
	int save_errno = errno;
544
545
	sleep(1);
546
	(void)signal(SIGQUIT, SIG_IGN);
547
	(void)signal(SIGQUIT, SIG_DFL);
548
	errno = save_errno;
549
}
550
551
/*
552
 * determine whether an inode should be fixed.
553
 */
554
int
555
dofix(struct inodesc *idesc, char *msg)
556
{
557
	switch (idesc->id_fix) {
558
559
	case DONTKNOW:
560
		if (idesc->id_type == DATA)
561
			direrror(idesc->id_number, msg);
562
		else
563
			pwarn("%s", msg);
564
		if (preen) {
565
			printf(" (SALVAGED)\n");
566
			idesc->id_fix = FIX;
567
			return (ALTERED);
568
		}
569
		if (reply("SALVAGE") == 0) {
570
			idesc->id_fix = NOFIX;
571
			return (0);
572
		}
573
		idesc->id_fix = FIX;
574
		return (ALTERED);
575
576
	case FIX:
577
		return (ALTERED);
578
579
	case NOFIX:
580
	case IGNORE:
581
		return (0);
582
583
	default:
584
		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
585
	}
586
	/* NOTREACHED */
587
}
588
589
int (* info_fn)(char *, size_t) = NULL;
590
char *info_filesys = "?";
591
592
/*ARGSUSED*/
593
void
594
catchinfo(int signo)
595
{
596
	static int info_fd;
597
	int save_errno = errno;
598
	struct iovec iov[4];
599
	char buf[1024];
600
601
	if (signo == 0) {
602
		info_fd = open(_PATH_TTY, O_WRONLY);
603
		signal(SIGINFO, catchinfo);
604
	} else if (info_fd > 0 && info_fn != NULL && info_fn(buf, sizeof buf)) {
605
		iov[0].iov_base = info_filesys;
606
		iov[0].iov_len = strlen(info_filesys);
607
		iov[1].iov_base = ": ";
608
		iov[1].iov_len = sizeof ": " - 1;
609
		iov[2].iov_base = buf;
610
		iov[2].iov_len = strlen(buf);
611
		iov[3].iov_base = "\n";
612
		iov[3].iov_len = sizeof "\n" - 1;
613
614
		writev(info_fd, iov, 4);
615
	}
616
	errno = save_errno;
617
}