2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
34 static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
40 #include <sys/param.h>
41 #include <sys/sysctl.h>
43 #include <ufs/ufs/dinode.h>
44 #include <ufs/ufs/dir.h>
45 #include <ufs/ffs/fs.h>
54 #define MINDIRSIZE (sizeof (struct dirtemplate))
56 static int fix_extraneous(struct inoinfo *, struct inodesc *);
57 static int deleteentry(struct inodesc *);
58 static int blksort(const void *, const void *);
59 static int pass2check(struct inodesc *);
66 struct inoinfo **inpp, *inp;
67 struct inoinfo **inpend;
68 struct inodesc curino;
71 char pathbuf[MAXPATHLEN + 1];
73 switch (inoinfo(UFS_ROOTINO)->ino_state) {
76 pfatal("ROOT INODE UNALLOCATED");
77 if (reply("ALLOCATE") == 0) {
81 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
82 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
86 pfatal("DUPS/BAD IN ROOT INODE");
87 if (reply("REALLOCATE")) {
89 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
91 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
94 if (reply("CONTINUE") == 0) {
103 pfatal("ROOT INODE NOT DIRECTORY");
104 if (reply("REALLOCATE")) {
105 freeino(UFS_ROOTINO);
106 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
108 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
111 if (reply("FIX") == 0) {
115 ginode(UFS_ROOTINO, &ip);
117 DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
118 DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
128 errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
129 inoinfo(UFS_ROOTINO)->ino_state);
131 inoinfo(UFS_ROOTINO)->ino_state = DFOUND;
132 inoinfo(UFS_WINO)->ino_state = FSTATE;
133 inoinfo(UFS_WINO)->ino_type = DT_WHT;
135 * Sort the directory list into disk block order.
137 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
139 * Check the integrity of each directory.
141 memset(&curino, 0, sizeof(struct inodesc));
142 curino.id_type = DATA;
143 curino.id_func = pass2check;
144 inpend = &inpsort[inplast];
145 for (inpp = inpsort; inpp < inpend; inpp++) {
147 printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
148 inpp - inpsort, (int)inplast,
149 (int)((inpp - inpsort) * 100 / inplast));
153 setproctitle("%s p2 %d%%", cdevname,
154 (int)((inpp - inpsort) * 100 / inplast));
158 if (inp->i_isize == 0)
160 if (inp->i_isize < MINDIRSIZE) {
161 direrror(inp->i_number, "DIRECTORY TOO SHORT");
162 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
163 if (reply("FIX") == 1) {
164 ginode(inp->i_number, &ip);
165 DIP_SET(ip.i_dp, di_size, inp->i_isize);
169 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
170 getpathname(pathbuf, inp->i_number, inp->i_number);
172 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
173 "DIRECTORY", pathbuf,
174 (intmax_t)inp->i_isize, DIRBLKSIZ);
176 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
177 "DIRECTORY", pathbuf,
178 (intmax_t)inp->i_isize, DIRBLKSIZ);
180 printf(" (ADJUSTED)\n");
181 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
182 if (preen || reply("ADJUST") == 1) {
183 ginode(inp->i_number, &ip);
184 DIP_SET(ip.i_dp, di_size,
185 roundup(inp->i_isize, DIRBLKSIZ));
191 memset(dp, 0, sizeof(struct ufs2_dinode));
192 DIP_SET(dp, di_mode, IFDIR);
193 DIP_SET(dp, di_size, inp->i_isize);
194 for (i = 0; i < MIN(inp->i_numblks, UFS_NDADDR); i++)
195 DIP_SET(dp, di_db[i], inp->i_blks[i]);
196 if (inp->i_numblks > UFS_NDADDR)
197 for (i = 0; i < UFS_NIADDR; i++)
198 DIP_SET(dp, di_ib[i],
199 inp->i_blks[UFS_NDADDR + i]);
200 curino.id_number = inp->i_number;
201 curino.id_parent = inp->i_parent;
202 (void)ckinode(dp, &curino);
205 * Now that the parents of all directories have been found,
206 * make another pass to verify the value of `..'
208 for (inpp = inpsort; inpp < inpend; inpp++) {
210 if (inp->i_parent == 0 || inp->i_isize == 0)
212 if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
213 INO_IS_DUNFOUND(inp->i_number))
214 inoinfo(inp->i_number)->ino_state = DFOUND;
215 if (inp->i_dotdot == inp->i_parent ||
216 inp->i_dotdot == (ino_t)-1)
218 if (inp->i_dotdot == 0) {
219 inp->i_dotdot = inp->i_parent;
221 fileerror(inp->i_parent, inp->i_number,
222 "DEFERRED MISSING '..' FIX");
223 (void)makeentry(inp->i_number, inp->i_parent, "..");
224 inoinfo(inp->i_parent)->ino_linkcnt--;
229 * inp->i_number is directory with bad ".." in it.
230 * inp->i_dotdot is current value of "..".
231 * inp->i_parent is directory to which ".." should point.
233 getpathname(pathbuf, inp->i_parent, inp->i_number);
234 printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
235 (uintmax_t)inp->i_number, pathbuf);
236 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
237 printf("CURRENTLY POINTS TO I=%ju (%s), ",
238 (uintmax_t)inp->i_dotdot, pathbuf);
239 getpathname(pathbuf, inp->i_parent, inp->i_parent);
240 printf("SHOULD POINT TO I=%ju (%s)",
241 (uintmax_t)inp->i_parent, pathbuf);
242 if (cursnapshot != 0) {
245 * setcwd(inp->i_number);
246 * setdotdot(inp->i_dotdot, inp->i_parent);
248 cmd.value = inp->i_number;
249 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
250 &cmd, sizeof cmd) == -1) {
251 /* kernel lacks support for these functions */
252 printf(" (IGNORED)\n");
255 cmd.value = inp->i_dotdot; /* verify same value */
256 cmd.size = inp->i_parent; /* new parent */
257 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
258 &cmd, sizeof cmd) == -1) {
259 printf(" (FIX FAILED: %s)\n", strerror(errno));
262 printf(" (FIXED)\n");
263 inoinfo(inp->i_parent)->ino_linkcnt--;
264 inp->i_dotdot = inp->i_parent;
268 printf(" (FIXED)\n");
269 else if (reply("FIX") == 0)
271 inoinfo(inp->i_dotdot)->ino_linkcnt++;
272 inoinfo(inp->i_parent)->ino_linkcnt--;
273 inp->i_dotdot = inp->i_parent;
274 (void)changeino(inp->i_number, "..", inp->i_parent);
277 * Mark all the directories that can be found from the root.
283 pass2check(struct inodesc *idesc)
285 struct direct *dirp = idesc->id_dirp;
286 char dirname[MAXPATHLEN + 1];
288 int n, entrysize, ret = 0;
292 struct direct proto, *newdirp;
297 if (dirp->d_ino > maxino)
299 if (idesc->id_entryno != 0)
301 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
302 if (dirp->d_ino != idesc->id_number) {
303 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
304 if (reply("FIX") == 1) {
305 dirp->d_ino = idesc->id_number;
309 if (dirp->d_type != DT_DIR) {
310 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
311 if (reply("FIX") == 1) {
312 dirp->d_type = DT_DIR;
318 proto.d_ino = idesc->id_number;
319 proto.d_type = DT_DIR;
321 (void)strcpy(proto.d_name, ".");
322 entrysize = DIRSIZ(0, &proto);
323 direrror(idesc->id_number, "MISSING '.'");
324 errmsg = "ADD '.' ENTRY";
325 if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
326 /* Not enough space to add '.', replace first entry with '.' */
327 if (dirp->d_ino != 0) {
328 pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n",
330 errmsg = "REPLACE WITH '.'";
332 if (reply(errmsg) == 0)
334 proto.d_reclen = dirp->d_reclen;
335 memmove(dirp, &proto, (size_t)entrysize);
338 /* Move over first entry and add '.' entry */
339 if (reply(errmsg) == 0)
341 newdirp = (struct direct *)((char *)(dirp) + entrysize);
342 dirp->d_reclen -= entrysize;
343 memmove(newdirp, dirp, dirp->d_reclen);
344 proto.d_reclen = entrysize;
345 memmove(dirp, &proto, (size_t)entrysize);
347 inoinfo(idesc->id_number)->ino_linkcnt--;
352 if (idesc->id_entryno > 1)
354 inp = getinoinfo(idesc->id_number);
355 proto.d_ino = inp->i_parent;
356 proto.d_type = DT_DIR;
358 (void)strcpy(proto.d_name, "..");
359 entrysize = DIRSIZ(0, &proto);
360 if (idesc->id_entryno == 0) {
362 if (dirp->d_reclen < n + entrysize)
364 proto.d_reclen = dirp->d_reclen - n;
367 inoinfo(dirp->d_ino)->ino_linkcnt--;
368 dirp = (struct direct *)((char *)(dirp) + n);
369 memset(dirp, 0, (size_t)proto.d_reclen);
370 dirp->d_reclen = proto.d_reclen;
372 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
373 inp->i_dotdot = dirp->d_ino;
374 if (dirp->d_type != DT_DIR) {
375 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
376 dirp->d_type = DT_DIR;
377 if (reply("FIX") == 1)
382 fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number,
383 idesc->id_number, "MISSING '..'");
384 errmsg = "ADD '..' ENTRY";
385 if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
386 /* No space to add '..', replace second entry with '..' */
387 if (dirp->d_ino != 0) {
388 pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
390 errmsg = "REPLACE WITH '..'";
392 if (reply(errmsg) == 0) {
393 inp->i_dotdot = (ino_t)-1;
396 if (proto.d_ino == 0) {
397 /* Defer processing until parent known */
400 printf("(FIX DEFERRED)\n");
402 inp->i_dotdot = proto.d_ino;
403 proto.d_reclen = dirp->d_reclen;
404 memmove(dirp, &proto, (size_t)entrysize);
407 /* Move over second entry and add '..' entry */
408 if (reply(errmsg) == 0) {
409 inp->i_dotdot = (ino_t)-1;
412 if (proto.d_ino == 0) {
413 /* Defer processing until parent known */
416 printf("(FIX DEFERRED)\n");
418 inp->i_dotdot = proto.d_ino;
419 if (dirp->d_ino == 0) {
420 proto.d_reclen = dirp->d_reclen;
421 memmove(dirp, &proto, (size_t)entrysize);
423 newdirp = (struct direct *)((char *)(dirp) + entrysize);
424 dirp->d_reclen -= entrysize;
425 memmove(newdirp, dirp, dirp->d_reclen);
426 proto.d_reclen = entrysize;
427 memmove(dirp, &proto, (size_t)entrysize);
428 if (dirp->d_ino != 0) {
430 inoinfo(dirp->d_ino)->ino_linkcnt--;
437 if (dirp->d_ino == 0)
439 if (dirp->d_namlen <= 2 &&
440 dirp->d_name[0] == '.' &&
441 idesc->id_entryno >= 2) {
442 if (dirp->d_namlen == 1) {
443 direrror(idesc->id_number, "EXTRA '.' ENTRY");
445 if (reply("FIX") == 1)
447 return (KEEPON | ret);
449 if (dirp->d_name[1] == '.') {
450 direrror(idesc->id_number, "EXTRA '..' ENTRY");
452 if (reply("FIX") == 1)
454 return (KEEPON | ret);
459 if (dirp->d_ino > maxino) {
460 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
462 } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
463 (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) {
464 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
465 dirp->d_ino = UFS_WINO;
466 dirp->d_type = DT_WHT;
467 if (reply("FIX") == 1)
471 switch (inoinfo(dirp->d_ino)->ino_state) {
473 if (idesc->id_entryno <= 2)
475 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
481 if (idesc->id_entryno <= 2)
483 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
485 else if (!preen && !usedsoftdep)
486 errmsg = "ZERO LENGTH DIRECTORY";
487 else if (cursnapshot == 0) {
491 getpathname(dirname, idesc->id_number,
493 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
494 dirname, (uintmax_t)dirp->d_ino);
497 * setcwd(idesc->id_parent);
498 * rmdir(dirp->d_name);
500 cmd.value = idesc->id_number;
501 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
502 &cmd, sizeof cmd) == -1) {
503 /* kernel lacks support */
504 printf(" (IGNORED)\n");
508 if (rmdir(dirp->d_name) == -1) {
509 printf(" (REMOVAL FAILED: %s)\n",
514 /* ".." reference to parent is removed */
515 inoinfo(idesc->id_number)->ino_linkcnt--;
516 printf(" (REMOVED)\n");
519 fileerror(idesc->id_number, dirp->d_ino, errmsg);
520 if ((n = reply("REMOVE")) == 1)
522 ginode(dirp->d_ino, &ip);
524 inoinfo(dirp->d_ino)->ino_state =
525 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
526 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
532 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
533 inoinfo(dirp->d_ino)->ino_state = DFOUND;
537 inp = getinoinfo(dirp->d_ino);
538 if (idesc->id_entryno > 2) {
539 if (inp->i_parent == 0)
540 inp->i_parent = idesc->id_number;
541 else if ((n = fix_extraneous(inp, idesc)) == 1)
548 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
549 fileerror(idesc->id_number, dirp->d_ino,
551 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
552 if (reply("FIX") == 1)
555 inoinfo(dirp->d_ino)->ino_linkcnt--;
559 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
560 inoinfo(dirp->d_ino)->ino_state,
561 (uintmax_t)dirp->d_ino);
567 return (ret|KEEPON|ALTERED);
571 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
575 struct inodesc dotdesc;
576 char oldname[MAXPATHLEN + 1];
577 char newname[MAXPATHLEN + 1];
580 * If we have not yet found "..", look it up now so we know
581 * which inode the directory itself believes is its parent.
583 if (inp->i_dotdot == 0) {
584 memset(&dotdesc, 0, sizeof(struct inodesc));
585 dotdesc.id_type = DATA;
586 dotdesc.id_number = idesc->id_dirp->d_ino;
587 dotdesc.id_func = findino;
588 dotdesc.id_name = strdup("..");
589 ginode(dotdesc.id_number, &ip);
590 if ((ckinode(ip.i_dp, &dotdesc) & FOUND))
591 inp->i_dotdot = dotdesc.id_parent;
595 * We have the previously found old name (inp->i_parent) and the
596 * just found new name (idesc->id_number). We have five cases:
597 * 1) ".." is missing - can remove either name, choose to delete
598 * new one and let fsck create ".." pointing to old name.
599 * 2) Both new and old are in same directory, choose to delete
600 * the new name and let fsck fix ".." if it is wrong.
601 * 3) ".." does not point to the new name, so delete it and let
602 * fsck fix ".." to point to the old one if it is wrong.
603 * 4) ".." points to the old name only, so delete the new one.
604 * 5) ".." points to the new name only, so delete the old one.
606 * For cases 1-4 we eliminate the new name;
607 * for case 5 we eliminate the old name.
609 if (inp->i_dotdot == 0 || /* Case 1 */
610 idesc->id_number == inp->i_parent || /* Case 2 */
611 inp->i_dotdot != idesc->id_number || /* Case 3 */
612 inp->i_dotdot == inp->i_parent) { /* Case 4 */
613 getpathname(newname, idesc->id_number, idesc->id_number);
614 if (strcmp(newname, "/") != 0)
615 strcat (newname, "/");
616 strcat(newname, idesc->id_dirp->d_name);
617 getpathname(oldname, inp->i_number, inp->i_number);
618 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
620 if (cursnapshot != 0) {
623 * setcwd(idesc->id_number);
624 * unlink(idesc->id_dirp->d_name);
626 cmd.value = idesc->id_number;
627 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
628 &cmd, sizeof cmd) == -1) {
629 printf(" (IGNORED)\n");
632 cmd.value = (intptr_t)idesc->id_dirp->d_name;
633 cmd.size = inp->i_number; /* verify same name */
634 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
635 &cmd, sizeof cmd) == -1) {
636 printf(" (UNLINK FAILED: %s)\n",
640 printf(" (REMOVED)\n");
644 printf(" (REMOVED)\n");
647 return (reply("REMOVE"));
650 * None of the first four cases above, so must be case (5).
651 * Eliminate the old name and make the new the name the parent.
653 getpathname(oldname, inp->i_parent, inp->i_number);
654 getpathname(newname, inp->i_number, inp->i_number);
655 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
657 if (cursnapshot != 0) {
660 * setcwd(inp->i_parent);
661 * unlink(last component of oldname pathname);
663 cmd.value = inp->i_parent;
664 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
665 &cmd, sizeof cmd) == -1) {
666 printf(" (IGNORED)\n");
669 if ((cp = strchr(oldname, '/')) == NULL) {
670 printf(" (IGNORED)\n");
673 cmd.value = (intptr_t)(cp + 1);
674 cmd.size = inp->i_number; /* verify same name */
675 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
676 &cmd, sizeof cmd) == -1) {
677 printf(" (UNLINK FAILED: %s)\n",
681 printf(" (REMOVED)\n");
682 inp->i_parent = idesc->id_number; /* reparent to correct dir */
685 if (!preen && !reply("REMOVE"))
687 memset(&dotdesc, 0, sizeof(struct inodesc));
688 dotdesc.id_type = DATA;
689 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
690 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
691 dotdesc.id_func = deleteentry;
692 ginode(dotdesc.id_number, &ip);
693 if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen)
694 printf(" (REMOVED)\n");
696 inp->i_parent = idesc->id_number; /* reparent to correct directory */
697 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
702 deleteentry(struct inodesc *idesc)
704 struct direct *dirp = idesc->id_dirp;
706 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
709 return (ALTERED|STOP|FOUND);
713 * Routine to sort disk blocks.
716 blksort(const void *arg1, const void *arg2)
719 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
720 (*(struct inoinfo * const *)arg2)->i_blks[0]);