pass2.c revision 1.35 1 /* $NetBSD: pass2.c,v 1.35 2020/04/03 19:36:33 joerg 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 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/time.h>
35 #include <sys/mount.h>
36 #include <sys/buf.h>
37
38 #include <ufs/lfs/lfs.h>
39 #include <ufs/lfs/lfs_accessors.h>
40 #include <ufs/lfs/lfs_inode.h>
41
42 #include <err.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include "bufcache.h"
48 #include "lfs_user.h"
49
50 #include "fsck.h"
51 #include "fsutil.h"
52 #include "extern.h"
53
54 #define MINDIRSIZE(fs) \
55 ((fs)->lfs_is64 ? sizeof(struct lfs_dirtemplate64) : \
56 sizeof(struct lfs_dirtemplate32))
57
58 static int pass2check(struct inodesc *);
59 static int blksort(const void *, const void *);
60
61 void
62 pass2(void)
63 {
64 union lfs_dinode *dp;
65 struct uvnode *vp;
66 struct inoinfo **inpp, *inp;
67 struct inoinfo **inpend;
68 struct inodesc curino;
69 union lfs_dinode dino;
70 char pathbuf[MAXPATHLEN + 1];
71 uint16_t mode;
72 unsigned ii;
73
74 switch (statemap[ULFS_ROOTINO]) {
75
76 case USTATE:
77 pfatal("ROOT INODE UNALLOCATED");
78 if (reply("ALLOCATE") == 0)
79 err(EEXIT, "%s", "");
80 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO)
81 err(EEXIT, "CANNOT ALLOCATE ROOT INODE");
82 break;
83
84 case DCLEAR:
85 pfatal("DUPS/BAD IN ROOT INODE");
86 if (reply("REALLOCATE")) {
87 freeino(ULFS_ROOTINO);
88 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO)
89 err(EEXIT, "CANNOT ALLOCATE ROOT INODE");
90 break;
91 }
92 if (reply("CONTINUE") == 0)
93 err(EEXIT, "%s", "");
94 break;
95
96 case FSTATE:
97 case FCLEAR:
98 pfatal("ROOT INODE NOT DIRECTORY");
99 if (reply("REALLOCATE")) {
100 freeino(ULFS_ROOTINO);
101 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO)
102 err(EEXIT, "CANNOT ALLOCATE ROOT INODE");
103 break;
104 }
105 if (reply("FIX") == 0)
106 errx(EEXIT, "%s", "");
107 vp = vget(fs, ULFS_ROOTINO);
108 dp = VTOD(vp);
109 mode = lfs_dino_getmode(fs, dp);
110 mode &= ~LFS_IFMT;
111 mode |= LFS_IFDIR;
112 lfs_dino_setmode(fs, dp, mode);
113 inodirty(VTOI(vp));
114 break;
115
116 case DSTATE:
117 break;
118
119 default:
120 errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ULFS_ROOTINO]);
121 }
122 statemap[ULFS_WINO] = FSTATE;
123 typemap[ULFS_WINO] = LFS_DT_WHT;
124 /*
125 * Sort the directory list into disk block order.
126 */
127 qsort((char *) inpsort, (size_t) inplast, sizeof *inpsort, blksort);
128 /*
129 * Check the integrity of each directory.
130 */
131 memset(&curino, 0, sizeof(struct inodesc));
132 curino.id_type = DATA;
133 curino.id_func = pass2check;
134 inpend = &inpsort[inplast];
135 for (inpp = inpsort; inpp < inpend; inpp++) {
136 inp = *inpp;
137 if (inp->i_isize == 0)
138 continue;
139 if (inp->i_isize < MINDIRSIZE(fs)) {
140 direrror(inp->i_number, "DIRECTORY TOO SHORT");
141 inp->i_isize = roundup(MINDIRSIZE(fs), LFS_DIRBLKSIZ);
142 if (reply("FIX") == 1) {
143 vp = vget(fs, inp->i_number);
144 dp = VTOD(vp);
145 lfs_dino_setsize(fs, dp, inp->i_isize);
146 inodirty(VTOI(vp));
147 }
148 } else if ((inp->i_isize & (LFS_DIRBLKSIZ - 1)) != 0) {
149 getpathname(pathbuf, sizeof(pathbuf), inp->i_number,
150 inp->i_number);
151 pwarn("DIRECTORY %s: LENGTH %lu NOT MULTIPLE OF %d",
152 pathbuf, (unsigned long) inp->i_isize, LFS_DIRBLKSIZ);
153 if (preen)
154 printf(" (ADJUSTED)\n");
155 inp->i_isize = roundup(inp->i_isize, LFS_DIRBLKSIZ);
156 if (preen || reply("ADJUST") == 1) {
157 vp = vget(fs, inp->i_number);
158 dp = VTOD(vp);
159 lfs_dino_setsize(fs, dp, inp->i_isize);
160 inodirty(VTOI(vp));
161 }
162 }
163 memset(&dino, 0, sizeof(dino));
164 lfs_dino_setmode(fs, &dino, LFS_IFDIR);
165 lfs_dino_setsize(fs, &dino, inp->i_isize);
166 for (ii = 0; ii < inp->i_numblks / sizeof(inp->i_blks[0]) &&
167 ii < ULFS_NDADDR; ii++) {
168 lfs_dino_setdb(fs, &dino, ii, inp->i_blks[ii]);
169 }
170 for (; ii < inp->i_numblks / sizeof(inp->i_blks[0]); ii++) {
171 lfs_dino_setib(fs, &dino, ii - ULFS_NDADDR,
172 inp->i_blks[ii]);
173 }
174 curino.id_number = inp->i_number;
175 curino.id_parent = inp->i_parent;
176 (void) ckinode(&dino, &curino);
177 }
178 /*
179 * Now that the parents of all directories have been found,
180 * make another pass to verify the value of `..'
181 */
182 for (inpp = inpsort; inpp < inpend; inpp++) {
183 inp = *inpp;
184 if (inp->i_parent == 0 || inp->i_isize == 0)
185 continue;
186 if (inp->i_dotdot == inp->i_parent ||
187 inp->i_dotdot == (ino_t) - 1)
188 continue;
189 if (inp->i_dotdot == 0) {
190 inp->i_dotdot = inp->i_parent;
191 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
192 if (reply("FIX") == 0)
193 continue;
194 (void) makeentry(inp->i_number, inp->i_parent, "..");
195 lncntp[inp->i_parent]--;
196 continue;
197 }
198 fileerror(inp->i_parent, inp->i_number,
199 "BAD INODE NUMBER FOR '..'");
200 if (reply("FIX") == 0)
201 continue;
202 lncntp[inp->i_dotdot]++;
203 lncntp[inp->i_parent]--;
204 inp->i_dotdot = inp->i_parent;
205 (void) changeino(inp->i_number, "..", inp->i_parent);
206 }
207 /*
208 * Mark all the directories that can be found from the root.
209 */
210 propagate();
211 }
212
213 static int
214 pass2check(struct inodesc * idesc)
215 {
216 LFS_DIRHEADER *dirp = idesc->id_dirp;
217 struct inoinfo *inp;
218 int n, entrysize, ret = 0;
219 union lfs_dinode *dp;
220 const char *errmsg;
221 LFS_DIRHEADER proto;
222 char namebuf[MAXPATHLEN + 1];
223 char pathbuf[MAXPATHLEN + 1];
224
225 /*
226 * check for "."
227 */
228 if (idesc->id_entryno != 0)
229 goto chk1;
230 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), ".") == 0) {
231 if (lfs_dir_getino(fs, dirp) != idesc->id_number) {
232 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
233 if (reply("FIX") == 1) {
234 lfs_dir_setino(fs, dirp, idesc->id_number);
235 ret |= ALTERED;
236 }
237 }
238 if (lfs_dir_gettype(fs, dirp) != LFS_DT_DIR) {
239 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
240 if (reply("FIX") == 1) {
241 lfs_dir_settype(fs, dirp, LFS_DT_DIR);
242 ret |= ALTERED;
243 }
244 }
245 goto chk1;
246 }
247 direrror(idesc->id_number, "MISSING '.'");
248 lfs_dir_setino(fs, &proto, idesc->id_number);
249 lfs_dir_settype(fs, &proto, LFS_DT_DIR);
250 lfs_dir_setnamlen(fs, &proto, 1);
251 entrysize = LFS_DIRECTSIZ(fs, 1);
252 lfs_dir_setreclen(fs, &proto, entrysize);
253 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), "..") != 0) {
254 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
255 lfs_dir_nameptr(fs, dirp));
256 } else if (lfs_dir_getreclen(fs, dirp) < entrysize) {
257 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
258 } else if (lfs_dir_getreclen(fs, dirp) < 2 * entrysize) {
259 /* convert this entry to a . entry */
260 lfs_dir_setreclen(fs, &proto, lfs_dir_getreclen(fs, dirp));
261 memcpy(dirp, &proto, sizeof(proto));
262 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), ".", 1,
263 lfs_dir_getreclen(fs, dirp));
264 if (reply("FIX") == 1)
265 ret |= ALTERED;
266 } else {
267 /* split this entry and use the beginning for the . entry */
268 n = lfs_dir_getreclen(fs, dirp) - entrysize;
269 memcpy(dirp, &proto, sizeof(proto));
270 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), ".", 1,
271 lfs_dir_getreclen(fs, dirp));
272 idesc->id_entryno++;
273 lncntp[lfs_dir_getino(fs, dirp)]--;
274 dirp = LFS_NEXTDIR(fs, dirp);
275 memset(dirp, 0, (size_t) n);
276 lfs_dir_setreclen(fs, dirp, n);
277 if (reply("FIX") == 1)
278 ret |= ALTERED;
279 }
280 chk1:
281 if (idesc->id_entryno > 1)
282 goto chk2;
283 inp = getinoinfo(idesc->id_number);
284 lfs_dir_setino(fs, &proto, inp->i_parent);
285 lfs_dir_settype(fs, &proto, LFS_DT_DIR);
286 lfs_dir_setnamlen(fs, &proto, 2);
287 entrysize = LFS_DIRECTSIZ(fs, 2);
288 lfs_dir_setreclen(fs, &proto, entrysize);
289 if (idesc->id_entryno == 0) {
290 n = LFS_DIRSIZ(fs, dirp);
291 if (lfs_dir_getreclen(fs, dirp) < n + entrysize)
292 goto chk2;
293 lfs_dir_setreclen(fs, &proto, lfs_dir_getreclen(fs, dirp) - n);
294 lfs_dir_setreclen(fs, dirp, n);
295 idesc->id_entryno++;
296 lncntp[lfs_dir_getino(fs, dirp)]--;
297 dirp = (LFS_DIRHEADER *) ((char *) (dirp) + n);
298 memset(dirp, 0, lfs_dir_getreclen(fs, &proto));
299 lfs_dir_setreclen(fs, dirp, lfs_dir_getreclen(fs, &proto));
300 }
301 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), "..") == 0) {
302 inp->i_dotdot = lfs_dir_getino(fs, dirp);
303 if (lfs_dir_gettype(fs, dirp) != LFS_DT_DIR) {
304 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
305 lfs_dir_settype(fs, dirp, LFS_DT_DIR);
306 if (reply("FIX") == 1)
307 ret |= ALTERED;
308 }
309 goto chk2;
310 }
311 if (lfs_dir_getino(fs, dirp) != 0 && strcmp(lfs_dir_nameptr(fs, dirp), ".") != 0) {
312 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
313 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
314 lfs_dir_nameptr(fs, dirp));
315 inp->i_dotdot = (ino_t) - 1;
316 } else if (lfs_dir_getreclen(fs, dirp) < entrysize) {
317 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
318 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
319 inp->i_dotdot = (ino_t) - 1;
320 } else if (inp->i_parent != 0) {
321 /*
322 * We know the parent, so fix now.
323 */
324 inp->i_dotdot = inp->i_parent;
325 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
326 lfs_dir_setreclen(fs, &proto, lfs_dir_getreclen(fs, dirp));
327 memcpy(dirp, &proto, (size_t) entrysize);
328 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "..", 2,
329 lfs_dir_getreclen(fs, dirp));
330 if (reply("FIX") == 1)
331 ret |= ALTERED;
332 }
333 idesc->id_entryno++;
334 if (lfs_dir_getino(fs, dirp) != 0)
335 lncntp[lfs_dir_getino(fs, dirp)]--;
336 return (ret | KEEPON);
337 chk2:
338 if (lfs_dir_getino(fs, dirp) == 0)
339 return (ret | KEEPON);
340 if (lfs_dir_getnamlen(fs, dirp) <= 2 &&
341 lfs_dir_nameptr(fs, dirp)[0] == '.' &&
342 idesc->id_entryno >= 2) {
343 if (lfs_dir_getnamlen(fs, dirp) == 1) {
344 direrror(idesc->id_number, "EXTRA '.' ENTRY");
345 if (reply("FIX") == 1) {
346 lfs_dir_setino(fs, dirp, 0);
347 ret |= ALTERED;
348 }
349 return (KEEPON | ret);
350 }
351 if (lfs_dir_nameptr(fs, dirp)[1] == '.') {
352 direrror(idesc->id_number, "EXTRA '..' ENTRY");
353 if (reply("FIX") == 1) {
354 lfs_dir_setino(fs, dirp, 0);
355 ret |= ALTERED;
356 }
357 return (KEEPON | ret);
358 }
359 }
360 idesc->id_entryno++;
361 n = 0;
362 if (lfs_dir_getino(fs, dirp) >= maxino) {
363 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), "I OUT OF RANGE");
364 n = reply("REMOVE");
365 } else if (lfs_dir_getino(fs, dirp) == LFS_IFILE_INUM &&
366 idesc->id_number == ULFS_ROOTINO) {
367 if (lfs_dir_gettype(fs, dirp) != LFS_DT_REG) {
368 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp),
369 "BAD TYPE FOR IFILE");
370 if (reply("FIX") == 1) {
371 lfs_dir_settype(fs, dirp, LFS_DT_REG);
372 ret |= ALTERED;
373 }
374 }
375 } else if (((lfs_dir_getino(fs, dirp) == ULFS_WINO && lfs_dir_gettype(fs, dirp) != LFS_DT_WHT) ||
376 (lfs_dir_getino(fs, dirp) != ULFS_WINO && lfs_dir_gettype(fs, dirp) == LFS_DT_WHT))) {
377 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), "BAD WHITEOUT ENTRY");
378 if (reply("FIX") == 1) {
379 lfs_dir_setino(fs, dirp, ULFS_WINO);
380 lfs_dir_settype(fs, dirp, LFS_DT_WHT);
381 ret |= ALTERED;
382 }
383 } else {
384 again:
385 switch (statemap[lfs_dir_getino(fs, dirp)]) {
386 case USTATE:
387 if (idesc->id_entryno <= 2)
388 break;
389 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp),
390 "UNALLOCATED");
391 n = reply("REMOVE");
392 break;
393
394 case DCLEAR:
395 case FCLEAR:
396 if (idesc->id_entryno <= 2)
397 break;
398 if (statemap[lfs_dir_getino(fs, dirp)] == FCLEAR)
399 errmsg = "DUP/BAD";
400 else if (!preen)
401 errmsg = "ZERO LENGTH DIRECTORY";
402 else {
403 n = 1;
404 break;
405 }
406 fileerror(idesc->id_number, lfs_dir_getino(fs, dirp), errmsg);
407 if ((n = reply("REMOVE")) == 1)
408 break;
409 dp = ginode(lfs_dir_getino(fs, dirp));
410 statemap[lfs_dir_getino(fs, dirp)] =
411 (lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR ? DSTATE : FSTATE;
412 lncntp[lfs_dir_getino(fs, dirp)] = lfs_dino_getnlink(fs, dp);
413 goto again;
414
415 case DSTATE:
416 case DFOUND:
417 inp = getinoinfo(lfs_dir_getino(fs, dirp));
418 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
419 getpathname(pathbuf, sizeof(pathbuf),
420 idesc->id_number, idesc->id_number);
421 getpathname(namebuf, sizeof(namebuf),
422 lfs_dir_getino(fs, dirp),
423 lfs_dir_getino(fs, dirp));
424 pwarn("%s %s %s\n", pathbuf,
425 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
426 namebuf);
427 if (preen)
428 printf(" (IGNORED)\n");
429 else if ((n = reply("REMOVE")) == 1)
430 break;
431 }
432 if (idesc->id_entryno > 2)
433 inp->i_parent = idesc->id_number;
434 /* fall through */
435
436 case FSTATE:
437 if (lfs_dir_gettype(fs, dirp) != typemap[lfs_dir_getino(fs, dirp)]) {
438 fileerror(idesc->id_number,
439 lfs_dir_getino(fs, dirp),
440 "BAD TYPE VALUE");
441 if (debug)
442 pwarn("dir has %d, typemap has %d\n",
443 lfs_dir_gettype(fs, dirp), typemap[lfs_dir_getino(fs, dirp)]);
444 lfs_dir_settype(fs, dirp, typemap[lfs_dir_getino(fs, dirp)]);
445 if (reply("FIX") == 1)
446 ret |= ALTERED;
447 }
448 lncntp[lfs_dir_getino(fs, dirp)]--;
449 break;
450
451 default:
452 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
453 statemap[lfs_dir_getino(fs, dirp)],
454 (uintmax_t)lfs_dir_getino(fs, dirp));
455 }
456 }
457 if (n == 0)
458 return (ret | KEEPON);
459 lfs_dir_setino(fs, dirp, 0);
460 return (ret | KEEPON | ALTERED);
461 }
462 /*
463 * Routine to sort disk blocks.
464 */
465 static int
466 blksort(const void *inpp1, const void *inpp2)
467 {
468 return ((*(const struct inoinfo *const *) inpp1)->i_blks[0] -
469 (*(const struct inoinfo *const *) inpp2)->i_blks[0]);
470 }
471