GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/fsck_ffs/pass2.c Lines: 83 274 30.3 %
Date: 2017-11-07 Branches: 45 183 24.6 %

Line Branch Exec Source
1
/*	$OpenBSD: pass2.c,v 1.37 2015/01/20 18:22:21 deraadt Exp $	*/
2
/*	$NetBSD: pass2.c,v 1.17 1996/09/27 22:45:15 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 roundup */
34
#include <sys/time.h>
35
#include <ufs/ufs/dinode.h>
36
#include <ufs/ufs/dir.h>
37
#include <ufs/ffs/fs.h>
38
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <string.h>
42
#include <limits.h>
43
44
#include "fsck.h"
45
#include "fsutil.h"
46
#include "extern.h"
47
48
#define MINDIRSIZE	(sizeof(struct dirtemplate))
49
50
static int pass2check(struct inodesc *);
51
static int blksort(const void *, const void *);
52
53
static int info_max;
54
static int info_pos;
55
56
static int
57
pass2_info1(char *buf, size_t buflen)
58
{
59
	return (snprintf(buf, buflen, "phase 2, directory %d/%d",
60
	    info_pos, info_max) > 0);
61
}
62
63
static int
64
pass2_info2(char *buf, size_t buflen)
65
{
66
	if (snprintf(buf, buflen, "phase 2, parent directory %d/%d",
67
	    info_pos, info_max) > 0)
68
		return (strlen(buf));
69
	return (0);
70
}
71
72
void
73
pass2(void)
74
{
75
	union dinode *dp;
76
	struct inoinfo **inpp, *inp, *pinp;
77
	struct inoinfo **inpend;
78
16
	struct inodesc curino;
79
8
	union dinode dino;
80
8
	char pathbuf[PATH_MAX + 1];
81
	int i;
82
83

8
	switch (GET_ISTATE(ROOTINO)) {
84
85
	case USTATE:
86
		pfatal("ROOT INODE UNALLOCATED");
87
		if (reply("ALLOCATE") == 0) {
88
			ckfini(0);
89
			errexit("%s", "");
90
		}
91
		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
92
			errexit("CANNOT ALLOCATE ROOT INODE\n");
93
		break;
94
95
	case DCLEAR:
96
		pfatal("DUPS/BAD IN ROOT INODE");
97
		if (reply("REALLOCATE")) {
98
			freeino(ROOTINO);
99
			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
100
				errexit("CANNOT ALLOCATE ROOT INODE\n");
101
			break;
102
		}
103
		if (reply("CONTINUE") == 0) {
104
			ckfini(0);
105
			errexit("%s", "");
106
		}
107
		break;
108
109
	case FSTATE:
110
	case FCLEAR:
111
		pfatal("ROOT INODE NOT DIRECTORY");
112
		if (reply("REALLOCATE")) {
113
			freeino(ROOTINO);
114
			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
115
				errexit("CANNOT ALLOCATE ROOT INODE\n");
116
			break;
117
		}
118
		if (reply("FIX") == 0) {
119
			ckfini(0);
120
			errexit("%s", "");
121
		}
122
		dp = ginode(ROOTINO);
123
		DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
124
		DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
125
		inodirty();
126
		break;
127
128
	case DSTATE:
129
		break;
130
131
	default:
132
		errexit("BAD STATE %d FOR ROOT INODE\n", GET_ISTATE(ROOTINO));
133
	}
134
8
	SET_ISTATE(ROOTINO, DFOUND);
135
	/*
136
	 * Sort the directory list into disk block order.
137
	 */
138
8
	qsort(inpsort, (size_t)inplast, sizeof *inpsort, blksort);
139
	/*
140
	 * Check the integrity of each directory.
141
	 */
142
8
	memset(&curino, 0, sizeof(struct inodesc));
143
8
	curino.id_type = DATA;
144
8
	curino.id_func = pass2check;
145
8
	inpend = &inpsort[inplast];
146
8
	info_pos = 0;
147
8
	info_max = inpend - inpsort;
148
8
	info_fn = pass2_info1;
149
32
	for (inpp = inpsort; inpp < inpend; inpp++) {
150
8
		inp = *inpp;
151
8
		info_pos ++;
152
8
		if (inp->i_isize == 0)
153
			continue;
154
8
		if (inp->i_isize < MINDIRSIZE) {
155
			direrror(inp->i_number, "DIRECTORY TOO SHORT");
156
			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
157
			if (reply("FIX") == 1) {
158
				dp = ginode(inp->i_number);
159
				DIP_SET(dp, di_size, inp->i_isize);
160
				inodirty();
161
			}
162
8
		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
163
			getpathname(pathbuf, sizeof pathbuf,
164
			    inp->i_number, inp->i_number);
165
			if (usedsoftdep)
166
			        pfatal("%s %s: LENGTH %zu NOT MULTIPLE of %d",
167
				       "DIRECTORY", pathbuf, inp->i_isize,
168
				       DIRBLKSIZ);
169
			else
170
				pwarn("%s %s: LENGTH %zu NOT MULTIPLE OF %d",
171
				      "DIRECTORY", pathbuf, inp->i_isize,
172
				      DIRBLKSIZ);
173
			if (preen)
174
				printf(" (ADJUSTED)\n");
175
			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
176
			if (preen || reply("ADJUST") == 1) {
177
				dp = ginode(inp->i_number);
178
				DIP_SET(dp, di_size, inp->i_isize);
179
				inodirty();
180
			}
181
		}
182
8
		memset(&dino, 0, sizeof(union dinode));
183
		dp = &dino;
184
8
		DIP_SET(dp, di_mode, IFDIR);
185
8
		DIP_SET(dp, di_size, inp->i_isize);
186
32
		for (i = 0;
187
48
		     i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
188
8
		     i++)
189
16
			DIP_SET(dp, di_db[i], inp->i_blks[i]);
190
8
		if (inp->i_numblks > NDADDR)
191
			for (i = 0; i < NIADDR; i++)
192
				DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
193
8
		curino.id_number = inp->i_number;
194
8
		curino.id_parent = inp->i_parent;
195
8
		(void)ckinode(dp, &curino);
196
8
	}
197
	/*
198
	 * Now that the parents of all directories have been found,
199
	 * make another pass to verify the value of `..'
200
	 */
201
8
	info_pos = 0;
202
8
	info_fn = pass2_info2;
203
32
	for (inpp = inpsort; inpp < inpend; inpp++) {
204
8
		inp = *inpp;
205
8
		info_pos++;
206

16
		if (inp->i_parent == 0 || inp->i_isize == 0)
207
			continue;
208

8
		if (inp->i_dotdot == inp->i_parent ||
209
		    inp->i_dotdot == (ino_t)-1)
210
			continue;
211
		if (inp->i_dotdot == 0) {
212
			inp->i_dotdot = inp->i_parent;
213
			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
214
			if (reply("FIX") == 0)
215
				continue;
216
			(void)makeentry(inp->i_number, inp->i_parent, "..");
217
			ILNCOUNT(inp->i_parent)--;
218
			continue;
219
		}
220
		fileerror(inp->i_parent, inp->i_number,
221
		    "BAD INODE NUMBER FOR '..'");
222
		if (reply("FIX") == 0)
223
			continue;
224
		ILNCOUNT(inp->i_dotdot)++;
225
		ILNCOUNT(inp->i_parent)--;
226
		inp->i_dotdot = inp->i_parent;
227
		(void)changeino(inp->i_number, "..", inp->i_parent);
228
	}
229
8
	info_fn = NULL;
230
	/*
231
	 * Create a list of children for each directory.
232
	 */
233
8
	inpend = &inpsort[inplast];
234
32
	for (inpp = inpsort; inpp < inpend; inpp++) {
235
8
		inp = *inpp;
236

16
		if (inp->i_parent == 0 ||
237
8
		    inp->i_number == ROOTINO)
238
			continue;
239
		pinp = getinoinfo(inp->i_parent);
240
		inp->i_sibling = pinp->i_child;
241
		pinp->i_child = inp;
242
	}
243
	/*
244
	 * Mark all the directories that can be found from the root.
245
	 */
246
8
	propagate(ROOTINO);
247
8
}
248
249
static int
250
pass2check(struct inodesc *idesc)
251
{
252
32
	struct direct *dirp = idesc->id_dirp;
253
	struct inoinfo *inp;
254
	int n, entrysize, ret = 0;
255
	union dinode *dp;
256
	char *errmsg;
257
16
	struct direct proto;
258
16
	char namebuf[PATH_MAX + 1];
259
16
	char pathbuf[PATH_MAX + 1];
260
261
	/*
262
	 * check for "."
263
	 */
264
16
	if (idesc->id_entryno != 0)
265
		goto chk1;
266

16
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
267
8
		if (dirp->d_ino != idesc->id_number) {
268
			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
269
			dirp->d_ino = idesc->id_number;
270
			if (reply("FIX") == 1)
271
				ret |= ALTERED;
272
		}
273
8
		if (dirp->d_type != DT_DIR) {
274
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
275
			dirp->d_type = DT_DIR;
276
			if (reply("FIX") == 1)
277
				ret |= ALTERED;
278
		}
279
		goto chk1;
280
	}
281
	direrror(idesc->id_number, "MISSING '.'");
282
	proto.d_ino = idesc->id_number;
283
	proto.d_type = DT_DIR;
284
	proto.d_namlen = 1;
285
	(void)strlcpy(proto.d_name, ".", sizeof proto.d_name);
286
	entrysize = DIRSIZ(0, &proto);
287
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
288
		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
289
			dirp->d_name);
290
	} else if (dirp->d_reclen < entrysize) {
291
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
292
	} else if (dirp->d_reclen < 2 * entrysize) {
293
		proto.d_reclen = dirp->d_reclen;
294
		memcpy(dirp, &proto, (size_t)entrysize);
295
		if (reply("FIX") == 1)
296
			ret |= ALTERED;
297
	} else {
298
		n = dirp->d_reclen - entrysize;
299
		proto.d_reclen = entrysize;
300
		memcpy(dirp, &proto, (size_t)entrysize);
301
		idesc->id_entryno++;
302
		ILNCOUNT(dirp->d_ino)--;
303
		dirp = (struct direct *)((char *)(dirp) + entrysize);
304
		memset(dirp, 0, (size_t)n);
305
		dirp->d_reclen = n;
306
		if (reply("FIX") == 1)
307
			ret |= ALTERED;
308
	}
309
chk1:
310
16
	if (idesc->id_entryno > 1)
311
		goto chk2;
312
16
	inp = getinoinfo(idesc->id_number);
313
16
	proto.d_ino = inp->i_parent;
314
16
	proto.d_type = DT_DIR;
315
16
	proto.d_namlen = 2;
316
16
	(void)strlcpy(proto.d_name, "..", sizeof proto.d_name);
317
16
	entrysize = DIRSIZ(0, &proto);
318
16
	if (idesc->id_entryno == 0) {
319
8
		n = DIRSIZ(0, dirp);
320
8
		if (dirp->d_reclen < n + entrysize)
321
			goto chk2;
322
		proto.d_reclen = dirp->d_reclen - n;
323
		dirp->d_reclen = n;
324
		idesc->id_entryno++;
325
		ILNCOUNT(dirp->d_ino)--;
326
		dirp = (struct direct *)((char *)(dirp) + n);
327
		memset(dirp, 0, (size_t)proto.d_reclen);
328
		dirp->d_reclen = proto.d_reclen;
329
	}
330

16
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
331
8
		inp->i_dotdot = dirp->d_ino;
332
8
		if (dirp->d_type != DT_DIR) {
333
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
334
			dirp->d_type = DT_DIR;
335
			if (reply("FIX") == 1)
336
				ret |= ALTERED;
337
		}
338
		goto chk2;
339
	}
340
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
341
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
342
		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
343
			dirp->d_name);
344
		inp->i_dotdot = -1;
345
	} else if (dirp->d_reclen < entrysize) {
346
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
347
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
348
		inp->i_dotdot = -1;
349
	} else if (inp->i_parent != 0) {
350
		/*
351
		 * We know the parent, so fix now.
352
		 */
353
		inp->i_dotdot = inp->i_parent;
354
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
355
		proto.d_reclen = dirp->d_reclen;
356
		memcpy(dirp, &proto, (size_t)entrysize);
357
		if (reply("FIX") == 1)
358
			ret |= ALTERED;
359
	}
360
	idesc->id_entryno++;
361
	if (dirp->d_ino != 0)
362
		ILNCOUNT(dirp->d_ino)--;
363
	return (ret|KEEPON);
364
chk2:
365
16
	if (dirp->d_ino == 0)
366
		return (ret|KEEPON);
367

32
	if (dirp->d_namlen <= 2 &&
368
16
	    dirp->d_name[0] == '.' &&
369
16
	    idesc->id_entryno >= 2) {
370
		if (dirp->d_namlen == 1) {
371
			direrror(idesc->id_number, "EXTRA '.' ENTRY");
372
			dirp->d_ino = 0;
373
			if (reply("FIX") == 1)
374
				ret |= ALTERED;
375
			return (KEEPON | ret);
376
		}
377
		if (dirp->d_name[1] == '.') {
378
			direrror(idesc->id_number, "EXTRA '..' ENTRY");
379
			dirp->d_ino = 0;
380
			if (reply("FIX") == 1)
381
				ret |= ALTERED;
382
			return (KEEPON | ret);
383
		}
384
	}
385
16
	idesc->id_entryno++;
386
	n = 0;
387
16
	if (dirp->d_ino > maxino) {
388
		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
389
		n = reply("REMOVE");
390
	} else {
391
again:
392

32
		switch (GET_ISTATE(dirp->d_ino)) {
393
		case USTATE:
394
			if (idesc->id_entryno <= 2)
395
				break;
396
			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
397
			n = reply("REMOVE");
398
			break;
399
400
		case DCLEAR:
401
		case FCLEAR:
402
			if (idesc->id_entryno <= 2)
403
				break;
404
			if (GET_ISTATE(dirp->d_ino) == FCLEAR)
405
				errmsg = "DUP/BAD";
406
			else if (!preen && !usedsoftdep)
407
				errmsg = "ZERO LENGTH DIRECTORY";
408
			else {
409
				n = 1;
410
				break;
411
			}
412
			fileerror(idesc->id_number, dirp->d_ino, errmsg);
413
			if ((n = reply("REMOVE")) == 1)
414
				break;
415
			dp = ginode(dirp->d_ino);
416
			SET_ISTATE(dirp->d_ino, (DIP(dp, di_mode) & IFMT) ==
417
			    IFDIR ? DSTATE : FSTATE);
418
			ILNCOUNT(dirp->d_ino) = DIP(dp, di_nlink);
419
			goto again;
420
421
		case DSTATE:
422
		case DFOUND:
423
16
			inp = getinoinfo(dirp->d_ino);
424

32
			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
425
				getpathname(pathbuf, sizeof pathbuf,
426
				    idesc->id_number, idesc->id_number);
427
				getpathname(namebuf, sizeof namebuf,
428
				    dirp->d_ino, dirp->d_ino);
429
				pwarn("%s %s %s\n", pathbuf,
430
				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
431
				    namebuf);
432
				if (preen) {
433
					printf (" (REMOVED)\n");
434
					n = 1;
435
					break;
436
				}
437
				if ((n = reply("REMOVE")) == 1)
438
					break;
439
			}
440
16
			if (idesc->id_entryno > 2)
441
				inp->i_parent = idesc->id_number;
442
			/* FALLTHROUGH */
443
444
		case FSTATE:
445
16
			if (dirp->d_type != GET_ITYPE(dirp->d_ino)) {
446
				fileerror(idesc->id_number, dirp->d_ino,
447
				    "BAD TYPE VALUE");
448
				dirp->d_type = GET_ITYPE(dirp->d_ino);
449
				if (reply("FIX") == 1)
450
					ret |= ALTERED;
451
			}
452
16
			ILNCOUNT(dirp->d_ino)--;
453
16
			break;
454
455
		default:
456
			errexit("BAD STATE %d FOR INODE I=%llu\n",
457
			    GET_ISTATE(dirp->d_ino),
458
			    (unsigned long long)dirp->d_ino);
459
		}
460
	}
461
16
	if (n == 0)
462
16
		return (ret|KEEPON);
463
	dirp->d_ino = 0;
464
	return (ret|KEEPON|ALTERED);
465
16
}
466
467
/*
468
 * Routine to sort disk blocks.
469
 */
470
static int
471
blksort(const void *inpp1, const void *inpp2)
472
{
473
	daddr_t d;
474
475
	d = (* (struct inoinfo **) inpp1)->i_blks[0] -
476
	    (* (struct inoinfo **) inpp2)->i_blks[0];
477
	if (d < 0)
478
		return (-1);
479
	else if (d > 0)
480
		return (1);
481
	else
482
		return (0);
483
}