1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
8
9 /*
10 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms are permitted
14 * provided that: (1) source distributions retain this entire copyright
15 * notice and comment, and (2) distributions including binaries display
16 * the following acknowledgement: ``This product includes software
17 * developed by the University of California, Berkeley and its contributors''
18 * in the documentation or other materials provided with the distribution
19 * and in all advertising materials mentioning features or use of this
20 * software. Neither the name of the University nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/sysmacros.h>
33 #include <sys/mntent.h>
34 #include <sys/fs/ufs_fs.h>
35 #include <sys/vnode.h>
36 #include <sys/fs/ufs_inode.h>
37 #define _KERNEL
38 #include <sys/fs/ufs_fsdir.h>
39 #undef _KERNEL
40 #include <string.h>
41 #include "fsck.h"
42
43 #define MINDIRSIZE (sizeof (struct dirtemplate))
44
45 static int blksort(const void *, const void *);
46 static int pass2check(struct inodesc *);
47
48 void
pass2(void)49 pass2(void)
50 {
51 struct dinode *dp, *dp2, *dpattr;
52 struct inoinfo **inpp, *inp;
53 struct inoinfo **inpend;
54 struct inodesc curino;
55 struct inodesc ldesc;
56 struct dinode dino;
57 char pathbuf[MAXPATHLEN + 1];
58 int found;
59 int dirtype;
60 caddr_t errmsg;
61 struct shadowclientinfo *sci;
62
63 switch (statemap[UFSROOTINO] & ~INDELAYD) {
64 case USTATE:
65 pfatal("ROOT INODE UNALLOCATED");
66 if (reply("ALLOCATE") == 0) {
67 errexit("Program terminated.");
68 }
69 if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) != UFSROOTINO)
70 errexit("CANNOT ALLOCATE ROOT INODE\n");
71 break;
72
73 case DCLEAR:
74 pfatal("DUPS/BAD IN ROOT INODE");
75 if (reply("REALLOCATE") == 1) {
76 freeino(UFSROOTINO, TI_NOPARENT);
77 if (allocdir(UFSROOTINO, UFSROOTINO,
78 0755, 0) != UFSROOTINO)
79 errexit("CANNOT ALLOCATE ROOT INODE\n");
80 break;
81 }
82 if (reply("CONTINUE") == 0) {
83 errexit("Program terminated.");
84 }
85 break;
86
87 case FSTATE:
88 case FCLEAR:
89 case FZLINK:
90 case SSTATE:
91 case SCLEAR:
92 pfatal("ROOT INODE NOT DIRECTORY");
93 if (reply("REALLOCATE") == 1) {
94 freeino(UFSROOTINO, TI_NOPARENT);
95 if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) !=
96 UFSROOTINO)
97 errexit("CANNOT ALLOCATE ROOT INODE\n");
98 break;
99 }
100 if (reply("FIX") == 0) {
101 ckfini();
102 errexit("Program terminated.");
103 }
104 dp = ginode(UFSROOTINO);
105 dp->di_mode &= ~IFMT;
106 dp->di_mode |= IFDIR;
107 inodirty();
108 break;
109
110 case DSTATE:
111 case DZLINK:
112 break;
113
114 default:
115 errexit("BAD STATE 0x%x FOR ROOT INODE\n",
116 statemap[UFSROOTINO]);
117 }
118 statemap[UFSROOTINO] = DFOUND;
119
120 /*
121 * Technically, we do know who the parent is. However,
122 * if this is set, then we'll get confused during the
123 * second-dir-entry-is-dotdot test for the root inode.
124 */
125 inp = getinoinfo(UFSROOTINO);
126 if (inp != NULL && inp->i_dotdot != 0)
127 inp->i_dotdot = 0;
128
129 /*
130 * Sort the directory list into disk block order. There's no
131 * requirement to do this, but it may help improve our i/o times
132 * somewhat.
133 */
134 qsort((void *)inpsort, (size_t)inplast, sizeof (*inpsort), blksort);
135 /*
136 * Check the integrity of each directory. In general, we treat
137 * attribute directories just like normal ones. Only the handling
138 * of .. is really different.
139 */
140 (void) memset(&dino, 0, sizeof (struct dinode));
141 dino.di_mode = IFDIR;
142 inpend = &inpsort[inplast];
143 for (inpp = inpsort; inpp < inpend; inpp++) {
144 inp = *inpp;
145
146 if (inp->i_isize == 0)
147 continue;
148
149 /* != DSTATE also covers case of == USTATE */
150 if (((statemap[inp->i_number] & STMASK) != DSTATE) ||
151 ((statemap[inp->i_number] & INCLEAR) == INCLEAR))
152 continue;
153
154 if (inp->i_isize < (offset_t)MINDIRSIZE) {
155 direrror(inp->i_number, "DIRECTORY TOO SHORT");
156 inp->i_isize = (offset_t)roundup(MINDIRSIZE, DIRBLKSIZ);
157 if (reply("FIX") == 1) {
158 dp = ginode(inp->i_number);
159 dp->di_size = (u_offset_t)inp->i_isize;
160 inodirty();
161 } else {
162 iscorrupt = 1;
163 }
164 }
165 if ((inp->i_isize & (offset_t)(DIRBLKSIZ - 1)) != 0) {
166 getpathname(pathbuf, inp->i_number, inp->i_number);
167 pwarn("DIRECTORY %s: LENGTH %lld NOT MULTIPLE OF %d",
168 pathbuf, (longlong_t)inp->i_isize, DIRBLKSIZ);
169 inp->i_isize = roundup(inp->i_isize,
170 (offset_t)DIRBLKSIZ);
171 if (preen || reply("ADJUST") == 1) {
172 dp = ginode(inp->i_number);
173 dp->di_size =
174 (u_offset_t)roundup(inp->i_isize,
175 (offset_t)DIRBLKSIZ);
176 inodirty();
177 if (preen)
178 (void) printf(" (ADJUSTED)\n");
179 } else {
180 iscorrupt = 1;
181 }
182 }
183 dp = ginode(inp->i_number);
184 if ((dp->di_mode & IFMT) == IFATTRDIR &&
185 (dp->di_cflags & IXATTR) == 0) {
186 pwarn("ATTRIBUTE DIRECTORY I=%d MISSING IXATTR FLAG",
187 inp->i_number);
188 if (preen || reply("CORRECT") == 1) {
189 dp->di_cflags |= IXATTR;
190 inodirty();
191 if (preen)
192 (void) printf(" (CORRECTED)\n");
193 }
194 }
195 dp = &dino;
196 dp->di_size = (u_offset_t)inp->i_isize;
197 (void) memmove((void *)&dp->di_db[0], (void *)&inp->i_blks[0],
198 inp->i_blkssize);
199 init_inodesc(&curino);
200 curino.id_type = DATA;
201 curino.id_func = pass2check;
202 curino.id_number = inp->i_number;
203 curino.id_parent = inp->i_parent;
204 curino.id_fix = DONTKNOW;
205 (void) ckinode(dp, &curino, CKI_TRAVERSE);
206
207 /*
208 * Make sure we mark attrdirs as DFOUND, since they won't
209 * be located during normal scan of standard directories.
210 */
211 if (curino.id_parent == 0) {
212 dpattr = ginode(inp->i_number);
213 if ((dpattr->di_mode & IFMT) == IFATTRDIR) {
214 for (sci = attrclientinfo; sci != NULL;
215 sci = sci->next) {
216 if (sci->shadow == inp->i_number) {
217 curino.id_parent =
218 sci->clients->client[0];
219 statemap[inp->i_number] =
220 DFOUND;
221 inp->i_parent =
222 curino.id_parent;
223 }
224 }
225 }
226 }
227 }
228 /*
229 * Now that the parents of all directories have been found,
230 * make another pass to verify the value of ..
231 */
232 for (inpp = inpsort; inpp < inpend; inpp++) {
233 inp = *inpp;
234 if (inp->i_parent == 0 || inp->i_isize == 0)
235 continue;
236 /*
237 * There are only directories in inpsort[], so only
238 * directory-related states need to be checked. There
239 * should never be any flags associated with USTATE.
240 */
241 if ((statemap[inp->i_number] & (STMASK | INCLEAR)) == DCLEAR ||
242 statemap[inp->i_number] == USTATE) {
243 continue;
244 }
245 if (statemap[inp->i_parent] == DFOUND &&
246 S_IS_DUNFOUND(statemap[inp->i_number])) {
247 statemap[inp->i_number] = DFOUND |
248 (statemap[inp->i_number] & INCLEAR);
249 }
250 if (inp->i_dotdot == inp->i_parent ||
251 inp->i_dotdot == (fsck_ino_t)-1) {
252 continue;
253 }
254 if (inp->i_dotdot == 0) {
255 inp->i_dotdot = inp->i_parent;
256 fileerror(inp->i_parent, inp->i_number,
257 "MISSING '..'");
258 if (reply("FIX") == 0) {
259 iscorrupt = 1;
260 continue;
261 }
262 dp = ginode(inp->i_number);
263 found = 0;
264 dirtype = (dp->di_mode & IFMT);
265
266 /*
267 * See if this is an attrdir that we located in pass1.
268 * i.e. it was on an i_oeftflag of some other inode.
269 * if it isn't found then we have an orphaned attrdir
270 * that needs to be tossed into lost+found.
271 */
272 if (dirtype == IFATTRDIR) {
273 for (sci = attrclientinfo;
274 sci != NULL;
275 sci = sci->next) {
276 if (sci->shadow == inp->i_number) {
277 inp->i_parent =
278 sci->clients->client[0];
279 found = 1;
280 }
281 }
282 }
283
284 /*
285 * We've already proven there's no "..", so this
286 * can't create a duplicate.
287 */
288 if (makeentry(inp->i_number, inp->i_parent, "..")) {
289
290 /*
291 * is it an orphaned attrdir?
292 */
293 if (dirtype == IFATTRDIR && found == 0) {
294 /*
295 * Throw it into lost+found
296 */
297 if (linkup(inp->i_number, lfdir,
298 NULL) == 0) {
299 pwarn(
300 "Unable to move attrdir I=%d to lost+found\n",
301 inp->i_number);
302 iscorrupt = 1;
303 }
304 maybe_convert_attrdir_to_dir(
305 inp->i_number);
306 }
307 if (dirtype == IFDIR) {
308 LINK_RANGE(errmsg,
309 lncntp[inp->i_parent], -1);
310 if (errmsg != NULL) {
311 LINK_CLEAR(errmsg,
312 inp->i_parent, IFDIR,
313 &ldesc);
314 if (statemap[inp->i_parent] !=
315 USTATE) {
316 /*
317 * iscorrupt is
318 * already set
319 */
320 continue;
321 }
322 }
323 TRACK_LNCNTP(inp->i_parent,
324 lncntp[inp->i_parent]--);
325 }
326
327 continue;
328 }
329 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
330 iscorrupt = 1;
331 inp->i_dotdot = (fsck_ino_t)-1;
332 continue;
333 }
334
335 dp2 = ginode(inp->i_parent);
336
337 if ((dp2->di_mode & IFMT) == IFATTRDIR) {
338 continue;
339 }
340 fileerror(inp->i_parent, inp->i_number,
341 "BAD INODE NUMBER FOR '..'");
342 if (reply("FIX") == 0) {
343 iscorrupt = 1;
344 continue;
345 }
346
347 LINK_RANGE(errmsg, lncntp[inp->i_dotdot], 1);
348 if (errmsg != NULL) {
349 LINK_CLEAR(errmsg, inp->i_dotdot, IFDIR, &ldesc);
350 if (statemap[inp->i_dotdot] != USTATE) {
351 /* iscorrupt is already set */
352 continue;
353 }
354 }
355 TRACK_LNCNTP(inp->i_dotdot, lncntp[inp->i_dotdot]++);
356
357 LINK_RANGE(errmsg, lncntp[inp->i_parent], -1);
358 if (errmsg != NULL) {
359 LINK_CLEAR(errmsg, inp->i_parent, IFDIR, &ldesc);
360 if (statemap[inp->i_parent] != USTATE) {
361 /* iscorrupt is already set */
362 continue;
363 }
364 }
365 TRACK_LNCNTP(inp->i_parent, lncntp[inp->i_parent]--);
366
367 inp->i_dotdot = inp->i_parent;
368 (void) changeino(inp->i_number, "..", inp->i_parent);
369 }
370 /*
371 * Mark all the directories that can be found from the root.
372 */
373 propagate();
374 }
375
376 /*
377 * Sanity-check a single directory entry. Which entry is being
378 * examined is tracked via idesc->id_entryno. There are two
379 * special ones, 0 (.) and 1 (..). Those have to exist in order
380 * in the first two locations in the directory, and have the usual
381 * properties. All other entries have to not be for either of
382 * the special two, and the inode they reference has to be
383 * reasonable.
384 *
385 * This is only called from dirscan(), which looks for the
386 * ALTERED flag after each invocation. If it finds it, the
387 * relevant buffer gets pushed out, so we don't have to worry
388 * about it here.
389 */
390 #define PASS2B_PROMPT "REMOVE DIRECTORY ENTRY FROM I=%d"
391
392 static int
pass2check(struct inodesc * idesc)393 pass2check(struct inodesc *idesc)
394 {
395 struct direct *dirp = idesc->id_dirp;
396 struct inodesc ldesc;
397 struct inoinfo *inp;
398 short reclen, entrysize;
399 int ret = 0;
400 int act, update_lncntp;
401 struct dinode *dp, *pdirp, *attrdirp;
402 caddr_t errmsg;
403 struct direct proto;
404 char namebuf[MAXPATHLEN + 1];
405 char pathbuf[MAXPATHLEN + 1];
406 int isattr;
407 int pdirtype;
408 int breakout = 0;
409 int dontreconnect;
410
411 if (idesc->id_entryno != 0)
412 goto chk1;
413 /*
414 * check for "."
415 */
416 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
417 if (dirp->d_ino != idesc->id_number) {
418 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
419 dirp->d_ino = idesc->id_number;
420 if (reply("FIX") == 1) {
421 ret |= ALTERED;
422 } else {
423 iscorrupt = 1;
424 }
425 }
426 goto chk1;
427 }
428 /*
429 * Build up a new one, and make sure there's room to put
430 * it where it belongs.
431 */
432 direrror(idesc->id_number, "MISSING '.'");
433 proto.d_ino = idesc->id_number;
434 proto.d_namlen = 1;
435 (void) strcpy(proto.d_name, ".");
436 entrysize = DIRSIZ(&proto);
437 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
438 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
439 dirp->d_name);
440 iscorrupt = 1;
441 } else if ((int)dirp->d_reclen < entrysize) {
442 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
443 iscorrupt = 1;
444 } else if ((int)dirp->d_reclen < 2 * entrysize) {
445 /*
446 * No room for another entry after us ("." is the
447 * smallest entry you can have), so just put all
448 * of the old entry's space into the new entry.
449 *
450 * Because we don't touch id_entryno, we end up going
451 * through the chk2 tests as well.
452 */
453 proto.d_reclen = dirp->d_reclen;
454 (void) memmove((void *)dirp, (void *)&proto,
455 (size_t)entrysize);
456 if (reply("FIX") == 1) {
457 ret |= ALTERED;
458 } else {
459 iscorrupt = 1;
460 }
461 } else {
462 /*
463 * There's enough room for an entire additional entry
464 * after this, so create the "." entry and follow it
465 * with an empty entry that covers the rest of the
466 * space.
467 *
468 * The increment of id_entryno means we'll skip the
469 * "." case of chk1, doing the ".." tests instead.
470 * Since we know that there's not a ".." where it
471 * should be (because we just created an empty entry
472 * there), that's the best way of getting it recreated
473 * as well.
474 */
475 reclen = dirp->d_reclen - entrysize;
476 proto.d_reclen = entrysize;
477 (void) memmove((void *)dirp, (void *)&proto,
478 (size_t)entrysize);
479 idesc->id_entryno++;
480 /*
481 * Make sure the link count is in range before updating
482 * it. This makes the assumption that the link count
483 * for this inode included one for ".", even though
484 * there wasn't a "." entry. Even if that's not true,
485 * it's a reasonable working hypothesis, and the link
486 * count verification done in pass4 will fix it for
487 * us anyway.
488 */
489 LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
490 if (errmsg != NULL) {
491 LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
492 if (statemap[dirp->d_ino] == USTATE) {
493 /*
494 * The inode got zapped, so reset the
495 * directory entry. Extend it to also
496 * cover the space we were going to make
497 * into a new entry.
498 */
499 dirp->d_ino = 0;
500 dirp->d_reclen += reclen;
501 ret |= ALTERED;
502 return (ret);
503 }
504 }
505
506 /*
507 * Create the new empty entry.
508 */
509 /* LINTED pointer cast alignment (entrysize is valid) */
510 dirp = (struct direct *)((char *)(dirp) + entrysize);
511 (void) memset((void *)dirp, 0, (size_t)reclen);
512 dirp->d_reclen = reclen;
513
514 /*
515 * Did the user want us to create a new "."? This
516 * query assumes that the direrror(MISSING) was the
517 * last thing printed, so if the LINK_RANGE() check
518 * fails, it can't pass through here.
519 */
520 if (reply("FIX") == 1) {
521 TRACK_LNCNTP(idesc->id_number,
522 lncntp[idesc->id_number]--);
523 ret |= ALTERED;
524 } else {
525 iscorrupt = 1;
526 }
527 }
528
529 /*
530 * XXX The next few lines are needed whether we're processing "."
531 * or "..". However, there are some extra steps still needed
532 * for the former, hence the big block of code for
533 * id_entryno == 0. Alternatively, there could be a label just
534 * before this comment, and everything through the end of that
535 * block moved there. In some ways, that might make the
536 * control flow more logical (factoring out to separate functions
537 * would be even better).
538 */
539
540 chk1:
541 if (idesc->id_entryno > 1)
542 goto chk2;
543 inp = getinoinfo(idesc->id_number);
544 if (inp == NULL) {
545 /*
546 * This is a can't-happen, since inodes get cached before
547 * we get called on them.
548 */
549 errexit("pass2check got NULL from getinoinfo at chk1 I=%d\n",
550 idesc->id_number);
551 }
552 proto.d_ino = inp->i_parent;
553 proto.d_namlen = 2;
554 (void) strcpy(proto.d_name, "..");
555 entrysize = DIRSIZ(&proto);
556 if (idesc->id_entryno == 0) {
557 /*
558 * We may not actually need to split things up, but if
559 * there's room to do so, we should, as that implies
560 * that the "." entry is larger than it is supposed
561 * to be, and therefore there's something wrong, albeit
562 * possibly harmlessly so.
563 */
564 reclen = DIRSIZ(dirp);
565 if ((int)dirp->d_reclen < reclen + entrysize) {
566 /*
567 * Not enough room for inserting a ".." after
568 * the "." entry.
569 */
570 goto chk2;
571 }
572 /*
573 * There's enough room for an entire additional entry
574 * after "."'s, so split it up. There's no reason "."
575 * should be bigger than the minimum, so shrink it to
576 * fit, too. Since by the time we're done with this
577 * part, dirp will be pointing at where ".." should be,
578 * update id_entryno to show that that's the entry
579 * we're on.
580 */
581 proto.d_reclen = dirp->d_reclen - reclen;
582 dirp->d_reclen = reclen;
583 idesc->id_entryno++;
584 if (dirp->d_ino > 0 && dirp->d_ino <= maxino) {
585 /*
586 * Account for the link to ourselves.
587 */
588 LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
589 if (errmsg != NULL) {
590 LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
591 if (statemap[dirp->d_ino] == USTATE) {
592 /*
593 * We were going to split the entry
594 * up, but the link count overflowed.
595 * Since we got rid of the inode,
596 * we need to also zap the directory
597 * entry, and restoring the original
598 * state of things is the least-bad
599 * result.
600 */
601 dirp->d_ino = 0;
602 dirp->d_reclen += proto.d_reclen;
603 ret |= ALTERED;
604 return (ret);
605 }
606 }
607 TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--);
608 /*
609 * Make sure the new entry doesn't get interpreted
610 * as having actual content.
611 */
612 /* LINTED pointer cast alignment (reclen is valid) */
613 dirp = (struct direct *)((char *)(dirp) + reclen);
614 (void) memset((void *)dirp, 0, (size_t)proto.d_reclen);
615 dirp->d_reclen = proto.d_reclen;
616 } else {
617 /*
618 * Everything was fine, up until we realized that
619 * the indicated inode was impossible. By clearing
620 * d_ino here, we'll trigger the recreation of it
621 * down below, using i_parent. Unlike the other
622 * half of this if(), we're everything so it shows
623 * that we're still on the "." entry.
624 */
625 fileerror(idesc->id_number, dirp->d_ino,
626 "I OUT OF RANGE");
627 dirp->d_ino = 0;
628 if (reply("FIX") == 1) {
629 ret |= ALTERED;
630 } else {
631 iscorrupt = 1;
632 }
633 }
634 }
635 /*
636 * Record this ".." inode, but only if we haven't seen one before.
637 * If this isn't the first, it'll get cleared below, and so we
638 * want to remember the entry that'll still be around later.
639 */
640 if (dirp->d_ino != 0 && inp->i_dotdot == 0 &&
641 strcmp(dirp->d_name, "..") == 0) {
642 inp->i_dotdot = dirp->d_ino;
643 goto chk2;
644 }
645 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
646 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
647 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
648 dirp->d_name);
649 iscorrupt = 1;
650 inp->i_dotdot = (fsck_ino_t)-1;
651 } else if ((int)dirp->d_reclen < entrysize) {
652 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
653 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
654 /* XXX Same consideration as immediately above. */
655 iscorrupt = 1;
656 inp->i_dotdot = (fsck_ino_t)-1;
657 } else if (inp->i_parent != 0) {
658 /*
659 * We know the parent, so fix now.
660 */
661 proto.d_ino = inp->i_dotdot = inp->i_parent;
662 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
663 /*
664 * Lint won't be quiet about d_reclen being set but not
665 * used. It apparently doesn't understand the implications
666 * of calling memmove(), and won't believe us that it's ok.
667 */
668 proto.d_reclen = dirp->d_reclen;
669 (void) memmove((void *)dirp, (void *)&proto,
670 (size_t)entrysize);
671 if (reply("FIX") == 1) {
672 ret |= ALTERED;
673 } else {
674 iscorrupt = 1;
675 }
676 } else if (inp->i_number == UFSROOTINO) {
677 /*
678 * Always know parent of root inode, so fix now.
679 */
680 proto.d_ino = inp->i_dotdot = inp->i_parent = UFSROOTINO;
681 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
682 /*
683 * Lint won't be quiet about d_reclen being set but not
684 * used. It apparently doesn't understand the implications
685 * of calling memmove(), and won't believe us that it's ok.
686 */
687 proto.d_reclen = dirp->d_reclen;
688 (void) memmove((void *)dirp, (void *)&proto, (size_t)entrysize);
689 if (reply("FIX") == 1) {
690 ret |= ALTERED;
691 } else {
692 iscorrupt = 1;
693 }
694 }
695 idesc->id_entryno++;
696 if (dirp->d_ino != 0) {
697 LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1);
698 if (errmsg != NULL) {
699 LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc);
700 if (statemap[dirp->d_ino] == USTATE) {
701 dirp->d_ino = 0;
702 ret |= ALTERED;
703 }
704 }
705 TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--);
706 }
707 return (ret|KEEPON);
708 chk2:
709 if (dirp->d_ino == 0)
710 return (ret|KEEPON);
711 if (dirp->d_namlen <= 2 &&
712 dirp->d_name[0] == '.' &&
713 idesc->id_entryno >= 2) {
714 if (dirp->d_namlen == 1) {
715 direrror(idesc->id_number, "EXTRA '.' ENTRY");
716 dirp->d_ino = 0;
717 if (reply("FIX") == 1) {
718 ret |= ALTERED;
719 } else {
720 iscorrupt = 1;
721 }
722 return (KEEPON | ret);
723 }
724 if (dirp->d_name[1] == '.') {
725 direrror(idesc->id_number, "EXTRA '..' ENTRY");
726 dirp->d_ino = 0;
727 if (reply("FIX") == 1) {
728 ret |= ALTERED;
729 } else {
730 iscorrupt = 1;
731 }
732 return (KEEPON | ret);
733 }
734 }
735 /*
736 * Because of this increment, all tests for skipping . and ..
737 * below are ``> 2'', not ``> 1'' as would logically be expected.
738 */
739 idesc->id_entryno++;
740 act = -1;
741 /*
742 * The obvious check would be for d_ino < UFSROOTINO. However,
743 * 1 is a valid inode number. Although it isn't currently used,
744 * as it was once the bad block list, there's nothing to prevent
745 * it from acquiring a new purpose in the future. So, don't
746 * arbitrarily disallow it. We don't test for <= zero, because
747 * d_ino is unsigned.
748 */
749 update_lncntp = 0;
750 if (dirp->d_ino > maxino || dirp->d_ino == 0) {
751 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
752 act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
753 } else {
754 again:
755 update_lncntp = 0;
756 switch (statemap[dirp->d_ino] & ~(INDELAYD)) {
757 case USTATE:
758 if (idesc->id_entryno <= 2)
759 break;
760 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
761 act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
762 break;
763
764 case DCLEAR:
765 case FCLEAR:
766 case SCLEAR:
767 if (idesc->id_entryno <= 2)
768 break;
769 dp = ginode(dirp->d_ino);
770 if (statemap[dirp->d_ino] == DCLEAR) {
771 errmsg = ((dp->di_mode & IFMT) == IFATTRDIR) ?
772 "REFERENCE TO ZERO LENGTH ATTRIBUTE DIRECTORY" :
773 "REFERENCE TO ZERO LENGTH DIRECTORY";
774 inp = getinoinfo(dirp->d_ino);
775 if (inp == NULL) {
776 /*
777 * The inode doesn't exist, as all
778 * should be cached by now. This
779 * gets caught by the range check
780 * above, and so it is a can't-happen
781 * at this point.
782 */
783 errexit("pass2check found a zero-len "
784 "reference to bad I=%d\n",
785 dirp->d_ino);
786 }
787 if (inp->i_parent != 0) {
788 (void) printf(
789 "Multiple links to I=%d, link counts wrong, rerun fsck\n",
790 inp->i_number);
791 iscorrupt = 1;
792 }
793 } else if (statemap[dirp->d_ino] == SCLEAR) {
794 /*
795 * In theory, this is a can't-happen,
796 * because shadows don't appear in directory
797 * entries. However, an inode might've
798 * been reused without a stale directory
799 * entry having been cleared, so check
800 * for it just in case. We'll check for
801 * the no-dir-entry shadows in pass3b().
802 */
803 errmsg = "ZERO LENGTH SHADOW";
804 } else {
805 errmsg = "DUP/BAD";
806 }
807 fileerror(idesc->id_number, dirp->d_ino, errmsg);
808 if ((act = reply(PASS2B_PROMPT, idesc->id_number)) == 1)
809 break;
810 /*
811 * Not doing anything about it, so just try
812 * again as whatever the base type was.
813 *
814 * fileerror() invalidated dp. Lint thinks this
815 * is unnecessary, but we know better.
816 */
817 dp = ginode(dirp->d_ino);
818 statemap[dirp->d_ino] &= STMASK;
819 TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino] = 0);
820 goto again;
821
822 case DSTATE:
823 case DZLINK:
824 if (statemap[idesc->id_number] == DFOUND) {
825 statemap[dirp->d_ino] = DFOUND;
826 }
827 /* FALLTHROUGH */
828
829 case DFOUND:
830 /*
831 * This is encouraging the best-practice of not
832 * hard-linking directories. It's legal (see POSIX),
833 * but not a good idea. So, don't consider it an
834 * instance of corruption, but offer to nuke it.
835 */
836 inp = getinoinfo(dirp->d_ino);
837 if (inp == NULL) {
838 /*
839 * Same can't-happen argument as in the
840 * zero-len case above.
841 */
842 errexit("pass2check found bad reference to "
843 "hard-linked directory I=%d\n",
844 dirp->d_ino);
845 }
846 dp = ginode(idesc->id_number);
847 if (inp->i_parent != 0 && idesc->id_entryno > 2 &&
848 ((dp->di_mode & IFMT) != IFATTRDIR)) {
849 /*
850 * XXX For nested dirs, this can report
851 * the same name for both paths.
852 */
853 getpathname(pathbuf, idesc->id_number,
854 dirp->d_ino);
855 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
856 pwarn(
857 "%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s\n",
858 pathbuf, namebuf);
859 if (preen) {
860 (void) printf(" (IGNORED)\n");
861 } else {
862 act = reply(PASS2B_PROMPT,
863 idesc->id_number);
864 if (act == 1) {
865 update_lncntp = 1;
866 broke_dir_link = 1;
867 break;
868 }
869 }
870 }
871
872 if ((idesc->id_entryno > 2) &&
873 (inp->i_extattr != idesc->id_number)) {
874 inp->i_parent = idesc->id_number;
875 }
876 /* FALLTHROUGH */
877
878 case FSTATE:
879 case FZLINK:
880 /*
881 * There's nothing to do for normal file-like
882 * things. Extended attributes come through
883 * here as well, though, and for them, .. may point
884 * to a file. In this situation we don't want
885 * to decrement link count as it was already
886 * decremented when the entry was seen in the
887 * directory it actually lives in.
888 */
889 pdirp = ginode(idesc->id_number);
890 pdirtype = (pdirp->di_mode & IFMT);
891 dp = ginode(dirp->d_ino);
892 isattr = (dp->di_cflags & IXATTR);
893 act = -1;
894 if (pdirtype == IFATTRDIR &&
895 (strcmp(dirp->d_name, "..") == 0)) {
896 dontreconnect = 0;
897 if (dp->di_oeftflag != 0) {
898 attrdirp = ginode(dp->di_oeftflag);
899
900 /*
901 * is it really an attrdir?
902 * if so, then don't do anything.
903 */
904
905 if ((attrdirp->di_mode & IFMT) ==
906 IFATTRDIR)
907 dontreconnect = 1;
908 dp = ginode(dirp->d_ino);
909 }
910 /*
911 * Rare corner case - the attrdir's ..
912 * points to the attrdir itself.
913 */
914 if (dirp->d_ino == idesc->id_number) {
915 dontreconnect = 1;
916 TRACK_LNCNTP(idesc->id_number,
917 lncntp[idesc->id_number]--);
918 }
919 /*
920 * Lets see if we have an orphaned attrdir
921 * that thinks it belongs to this file.
922 * Only re-connect it if the current
923 * attrdir is 0 or not an attrdir.
924 */
925 if ((dp->di_oeftflag != idesc->id_number) &&
926 (dontreconnect == 0)) {
927 fileerror(idesc->id_number,
928 dirp->d_ino,
929 "Attribute directory I=%d not "
930 "attached to file I=%d\n",
931 idesc->id_number, dirp->d_ino);
932 if ((act = reply("FIX")) == 1) {
933 dp = ginode(dirp->d_ino);
934 if (debug)
935 (void) printf(
936 "debug: changing i=%d's oeft from %d ",
937 dirp->d_ino,
938 dp->di_oeftflag);
939 dp->di_oeftflag =
940 idesc->id_number;
941 if (debug)
942 (void) printf("to %d\n",
943 dp->di_oeftflag);
944 inodirty();
945 registershadowclient(
946 idesc->id_number,
947 dirp->d_ino,
948 &attrclientinfo);
949 }
950 dp = ginode(dirp->d_ino);
951 }
952
953 /*
954 * This can only be true if we've modified
955 * an inode/xattr connection, and we
956 * don't keep track of those in the link
957 * counts. So, skipping the checks just
958 * after this is not a problem.
959 */
960 if (act > 0)
961 return (KEEPON | ALTERED);
962
963 /*
964 * Don't screw up link counts for directories.
965 * If we aren't careful we can perform
966 * an extra decrement, since the .. of
967 * an attrdir could be either a file or a
968 * directory. If it's a file then its link
969 * should be correct after it is seen when the
970 * directory it lives in scanned.
971 */
972 if ((pdirtype == IFATTRDIR) &&
973 ((dp->di_mode & IFMT) == IFDIR))
974 breakout = 1;
975 if ((dp->di_mode & IFMT) != IFDIR)
976 breakout = 1;
977
978 } else if ((pdirtype != IFATTRDIR) ||
979 (strcmp(dirp->d_name, ".") != 0)) {
980 if ((pdirtype == IFDIR) && isattr) {
981 fileerror(idesc->id_number,
982 dirp->d_ino,
983 "File should NOT be marked as "
984 "extended attribute\n");
985 if ((act = reply("FIX")) == 1) {
986 dp = ginode(dirp->d_ino);
987 if (debug)
988 (void) printf(
989 "changing i=%d's cflags from 0x%x to ",
990 dirp->d_ino,
991 dp->di_cflags);
992
993 dp->di_cflags &= ~IXATTR;
994 if (debug)
995 (void) printf("0x%x\n",
996 dp->di_cflags);
997 inodirty();
998 if ((dp->di_mode & IFMT) ==
999 IFATTRDIR) {
1000 dp->di_mode &=
1001 ~IFATTRDIR;
1002 dp->di_mode |= IFDIR;
1003 inodirty();
1004 pdirp = ginode(
1005 idesc->id_number);
1006 if (pdirp->di_oeftflag
1007 != 0) {
1008 pdirp->di_oeftflag = 0;
1009 inodirty();
1010 }
1011 }
1012 }
1013 } else {
1014 if (pdirtype == IFATTRDIR &&
1015 (isattr == 0)) {
1016 fileerror(idesc->id_number,
1017 dirp->d_ino,
1018 "File should BE marked as "
1019 "extended attribute\n");
1020 if ((act = reply("FIX")) == 1) {
1021 dp = ginode(
1022 dirp->d_ino);
1023 dp->di_cflags |= IXATTR;
1024 /*
1025 * Make sure it's a file
1026 * while we're at it.
1027 */
1028 dp->di_mode &= ~IFMT;
1029 dp->di_mode |= IFREG;
1030 inodirty();
1031 }
1032 }
1033 }
1034
1035 }
1036 if (breakout == 0 || dontreconnect == 0) {
1037 TRACK_LNCNTP(dirp->d_ino,
1038 lncntp[dirp->d_ino]--);
1039 if (act > 0)
1040 return (KEEPON | ALTERED);
1041 }
1042 break;
1043
1044 case SSTATE:
1045 errmsg = "ACL IN DIRECTORY";
1046 fileerror(idesc->id_number, dirp->d_ino, errmsg);
1047 act = (reply(PASS2B_PROMPT, idesc->id_number) == 1);
1048 break;
1049
1050 default:
1051 errexit("BAD STATE 0x%x FOR INODE I=%d",
1052 statemap[dirp->d_ino], dirp->d_ino);
1053 }
1054 }
1055
1056 if (act == 0) {
1057 iscorrupt = 1;
1058 }
1059
1060 if (act <= 0)
1061 return (ret|KEEPON);
1062
1063 if (update_lncntp) {
1064 LINK_RANGE(errmsg, lncntp[idesc->id_number], 1);
1065 if (errmsg != NULL) {
1066 LINK_CLEAR(errmsg, idesc->id_number, IFDIR, &ldesc);
1067 if (statemap[idesc->id_number] == USTATE) {
1068 idesc->id_number = 0;
1069 ret |= ALTERED;
1070 }
1071 }
1072 TRACK_LNCNTP(idesc->id_number, lncntp[idesc->id_number]++);
1073 }
1074
1075 dirp->d_ino = 0;
1076
1077 return (ret|KEEPON|ALTERED);
1078 }
1079
1080 #undef PASS2B_PROMPT
1081
1082 /*
1083 * Routine to sort disk blocks.
1084 */
1085 static int
blksort(const void * arg1,const void * arg2)1086 blksort(const void *arg1, const void *arg2)
1087 {
1088 const struct inoinfo **inpp1 = (const struct inoinfo **)arg1;
1089 const struct inoinfo **inpp2 = (const struct inoinfo **)arg2;
1090
1091 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
1092 }
1093