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")) {
88 freedirino(UFS_ROOTINO, UFS_ROOTINO);
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;
217 if (inp->i_dotdot == inp->i_parent ||
218 inp->i_dotdot == (ino_t)-1)
220 if (inp->i_dotdot == 0) {
221 inp->i_dotdot = inp->i_parent;
223 fileerror(inp->i_parent, inp->i_number,
224 "DEFERRED MISSING '..' FIX");
225 (void)makeentry(inp->i_number, inp->i_parent, "..");
226 inoinfo(inp->i_parent)->ino_linkcnt--;
231 * inp->i_number is directory with bad ".." in it.
232 * inp->i_dotdot is current value of "..".
233 * inp->i_parent is directory to which ".." should point.
235 getpathname(pathbuf, inp->i_parent, inp->i_number);
236 printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
237 (uintmax_t)inp->i_number, pathbuf);
238 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
239 printf("CURRENTLY POINTS TO I=%ju (%s), ",
240 (uintmax_t)inp->i_dotdot, pathbuf);
241 getpathname(pathbuf, inp->i_parent, inp->i_parent);
242 printf("SHOULD POINT TO I=%ju (%s)",
243 (uintmax_t)inp->i_parent, pathbuf);
244 if (cursnapshot != 0) {
247 * setcwd(inp->i_number);
248 * setdotdot(inp->i_dotdot, inp->i_parent);
250 cmd.value = inp->i_number;
251 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
252 &cmd, sizeof cmd) == -1) {
253 /* kernel lacks support for these functions */
254 printf(" (IGNORED)\n");
257 cmd.value = inp->i_dotdot; /* verify same value */
258 cmd.size = inp->i_parent; /* new parent */
259 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
260 &cmd, sizeof cmd) == -1) {
261 printf(" (FIX FAILED: %s)\n", strerror(errno));
264 printf(" (FIXED)\n");
265 inoinfo(inp->i_parent)->ino_linkcnt--;
266 inp->i_dotdot = inp->i_parent;
270 printf(" (FIXED)\n");
271 else if (reply("FIX") == 0)
273 inoinfo(inp->i_dotdot)->ino_linkcnt++;
274 inoinfo(inp->i_parent)->ino_linkcnt--;
275 inp->i_dotdot = inp->i_parent;
276 (void)changeino(inp->i_number, "..", inp->i_parent,
277 getinoinfo(inp->i_parent)->i_depth + 1);
280 * Mark all the directories that can be found from the root.
286 pass2check(struct inodesc *idesc)
288 struct direct *dirp = idesc->id_dirp;
289 char dirname[MAXPATHLEN + 1];
291 int n, entrysize, ret = 0;
295 struct direct proto, *newdirp;
300 if (idesc->id_entryno != 0)
302 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
303 if (dirp->d_ino != idesc->id_number) {
304 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
305 if (reply("FIX") == 1) {
306 dirp->d_ino = idesc->id_number;
310 if (dirp->d_type != DT_DIR) {
311 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
312 if (reply("FIX") == 1) {
313 dirp->d_type = DT_DIR;
319 proto.d_ino = idesc->id_number;
320 proto.d_type = DT_DIR;
322 (void)strcpy(proto.d_name, ".");
323 entrysize = DIRSIZ(0, &proto);
324 direrror(idesc->id_number, "MISSING '.'");
325 errmsg = "ADD '.' ENTRY";
326 if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
327 /* Not enough space to add '.', replace first entry with '.' */
328 if (dirp->d_ino != 0) {
329 pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n",
331 errmsg = "REPLACE WITH '.'";
333 if (reply(errmsg) == 0)
335 proto.d_reclen = dirp->d_reclen;
336 memmove(dirp, &proto, (size_t)entrysize);
339 /* Move over first entry and add '.' entry */
340 if (reply(errmsg) == 0)
342 newdirp = (struct direct *)((char *)(dirp) + entrysize);
343 dirp->d_reclen -= entrysize;
344 memmove(newdirp, dirp, dirp->d_reclen);
345 proto.d_reclen = entrysize;
346 memmove(dirp, &proto, (size_t)entrysize);
348 inoinfo(idesc->id_number)->ino_linkcnt--;
353 if (idesc->id_entryno > 1)
355 inp = getinoinfo(idesc->id_number);
356 proto.d_ino = inp->i_parent;
357 proto.d_type = DT_DIR;
359 (void)strcpy(proto.d_name, "..");
360 entrysize = DIRSIZ(0, &proto);
361 if (idesc->id_entryno == 0) {
363 if (dirp->d_reclen < n + entrysize)
365 proto.d_reclen = dirp->d_reclen - n;
368 inoinfo(dirp->d_ino)->ino_linkcnt--;
369 dirp = (struct direct *)((char *)(dirp) + n);
370 memset(dirp, 0, (size_t)proto.d_reclen);
371 dirp->d_reclen = proto.d_reclen;
373 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
374 if (dirp->d_ino >= maxino) {
375 direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
377 * If we know parent set it now, otherwise let it
378 * point to the root inode and it will get cleaned
379 * up later if that is not correct.
381 if (inp->i_parent != 0)
382 dirp->d_ino = inp->i_parent;
384 dirp->d_ino = UFS_ROOTINO;
385 if (reply("FIX") == 1)
388 inp->i_dotdot = dirp->d_ino;
389 if (dirp->d_type != DT_DIR) {
390 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
391 dirp->d_type = DT_DIR;
392 if (reply("FIX") == 1)
397 fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number,
398 idesc->id_number, "MISSING '..'");
399 errmsg = "ADD '..' ENTRY";
400 if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
401 /* No space to add '..', replace second entry with '..' */
402 if (dirp->d_ino != 0) {
403 pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
405 errmsg = "REPLACE WITH '..'";
407 if (reply(errmsg) == 0) {
408 inp->i_dotdot = (ino_t)-1;
411 if (proto.d_ino == 0) {
412 /* Defer processing until parent known */
415 printf("(FIX DEFERRED)\n");
417 inp->i_dotdot = proto.d_ino;
418 proto.d_reclen = dirp->d_reclen;
419 memmove(dirp, &proto, (size_t)entrysize);
422 /* Move over second entry and add '..' entry */
423 if (reply(errmsg) == 0) {
424 inp->i_dotdot = (ino_t)-1;
427 if (proto.d_ino == 0) {
428 /* Defer processing until parent known */
431 printf("(FIX DEFERRED)\n");
433 inp->i_dotdot = proto.d_ino;
434 if (dirp->d_ino == 0) {
435 proto.d_reclen = dirp->d_reclen;
436 memmove(dirp, &proto, (size_t)entrysize);
438 newdirp = (struct direct *)((char *)(dirp) + entrysize);
439 dirp->d_reclen -= entrysize;
440 memmove(newdirp, dirp, dirp->d_reclen);
441 proto.d_reclen = entrysize;
442 memmove(dirp, &proto, (size_t)entrysize);
443 if (dirp->d_ino != 0) {
445 inoinfo(dirp->d_ino)->ino_linkcnt--;
452 if (dirp->d_ino == 0)
454 if (dirp->d_namlen <= 2 &&
455 dirp->d_name[0] == '.' &&
456 idesc->id_entryno >= 2) {
457 if (dirp->d_namlen == 1) {
458 direrror(idesc->id_number, "EXTRA '.' ENTRY");
460 if (reply("FIX") == 1)
462 return (KEEPON | ret);
464 if (dirp->d_name[1] == '.') {
465 direrror(idesc->id_number, "EXTRA '..' ENTRY");
467 if (reply("FIX") == 1)
469 return (KEEPON | ret);
474 if (dirp->d_ino >= maxino) {
475 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
477 } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
478 (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) {
479 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
480 dirp->d_ino = UFS_WINO;
481 dirp->d_type = DT_WHT;
482 if (reply("FIX") == 1)
486 switch (inoinfo(dirp->d_ino)->ino_state) {
488 if (idesc->id_entryno <= 2)
490 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
496 if (idesc->id_entryno <= 2)
498 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
500 else if (!preen && !usedsoftdep)
501 errmsg = "ZERO LENGTH DIRECTORY";
502 else if (cursnapshot == 0) {
506 getpathname(dirname, idesc->id_number,
508 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
509 dirname, (uintmax_t)dirp->d_ino);
512 * setcwd(idesc->id_parent);
513 * rmdir(dirp->d_name);
515 cmd.value = idesc->id_number;
516 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
517 &cmd, sizeof cmd) == -1) {
518 /* kernel lacks support */
519 printf(" (IGNORED)\n");
523 if (rmdir(dirp->d_name) == -1) {
524 printf(" (REMOVAL FAILED: %s)\n",
529 /* ".." reference to parent is removed */
530 inoinfo(idesc->id_number)->ino_linkcnt--;
531 printf(" (REMOVED)\n");
534 fileerror(idesc->id_number, dirp->d_ino, errmsg);
535 if ((n = reply("REMOVE")) == 1)
537 ginode(dirp->d_ino, &ip);
539 inoinfo(dirp->d_ino)->ino_state =
540 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
541 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
547 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
548 inoinfo(dirp->d_ino)->ino_state = DFOUND;
552 inp = getinoinfo(dirp->d_ino);
553 if (idesc->id_entryno > 2) {
554 if (inp->i_parent == 0) {
555 inp->i_parent = idesc->id_number;
557 } else if ((n = fix_extraneous(inp, idesc))) {
565 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
566 fileerror(idesc->id_number, dirp->d_ino,
568 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
569 if (reply("FIX") == 1)
572 inoinfo(dirp->d_ino)->ino_linkcnt--;
576 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
577 inoinfo(dirp->d_ino)->ino_state,
578 (uintmax_t)dirp->d_ino);
584 return (ret|KEEPON|ALTERED);
588 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
592 struct inodesc dotdesc;
593 char oldname[MAXPATHLEN + 1];
594 char newname[MAXPATHLEN + 1];
597 * If we have not yet found "..", look it up now so we know
598 * which inode the directory itself believes is its parent.
600 if (inp->i_dotdot == 0) {
601 memset(&dotdesc, 0, sizeof(struct inodesc));
602 dotdesc.id_type = DATA;
603 dotdesc.id_number = idesc->id_dirp->d_ino;
604 dotdesc.id_func = findino;
605 dotdesc.id_name = strdup("..");
606 ginode(dotdesc.id_number, &ip);
607 if ((ckinode(ip.i_dp, &dotdesc) & FOUND))
608 inp->i_dotdot = dotdesc.id_parent;
610 free(dotdesc.id_name);
613 * We have the previously found old name (inp->i_parent) and the
614 * just found new name (idesc->id_number). We have five cases:
615 * 1) ".." is missing - can remove either name, choose to delete
616 * new one and let fsck create ".." pointing to old name.
617 * 2) Both new and old are in same directory, choose to delete
618 * the new name and let fsck fix ".." if it is wrong.
619 * 3) ".." does not point to the new name, so delete it and let
620 * fsck fix ".." to point to the old one if it is wrong.
621 * 4) ".." points to the old name only, so delete the new one.
622 * 5) ".." points to the new name only, so delete the old one.
624 * For cases 1-4 we eliminate the new name;
625 * for case 5 we eliminate the old name.
627 if (inp->i_dotdot == 0 || /* Case 1 */
628 idesc->id_number == inp->i_parent || /* Case 2 */
629 inp->i_dotdot != idesc->id_number || /* Case 3 */
630 inp->i_dotdot == inp->i_parent) { /* Case 4 */
631 getpathname(newname, idesc->id_number, idesc->id_number);
632 if (strcmp(newname, "/") != 0)
633 strcat (newname, "/");
634 strcat(newname, idesc->id_dirp->d_name);
635 getpathname(oldname, inp->i_number, inp->i_number);
636 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
638 if (cursnapshot != 0) {
641 * setcwd(idesc->id_number);
642 * unlink(idesc->id_dirp->d_name);
644 cmd.value = idesc->id_number;
645 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
646 &cmd, sizeof cmd) == -1) {
647 printf(" (IGNORED)\n");
650 cmd.value = (intptr_t)idesc->id_dirp->d_name;
651 cmd.size = inp->i_number; /* verify same name */
652 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
653 &cmd, sizeof cmd) == -1) {
654 printf(" (UNLINK FAILED: %s)\n",
658 printf(" (REMOVED)\n");
662 printf(" (REMOVED)\n");
665 return (reply("REMOVE"));
668 * None of the first four cases above, so must be case (5).
669 * Eliminate the old name and make the new the name the parent.
671 getpathname(oldname, inp->i_parent, inp->i_number);
672 getpathname(newname, inp->i_number, inp->i_number);
673 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
675 if (cursnapshot != 0) {
678 * setcwd(inp->i_parent);
679 * unlink(last component of oldname pathname);
681 cmd.value = inp->i_parent;
682 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
683 &cmd, sizeof cmd) == -1) {
684 printf(" (IGNORED)\n");
687 if ((cp = strchr(oldname, '/')) == NULL) {
688 printf(" (IGNORED)\n");
691 cmd.value = (intptr_t)(cp + 1);
692 cmd.size = inp->i_number; /* verify same name */
693 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
694 &cmd, sizeof cmd) == -1) {
695 printf(" (UNLINK FAILED: %s)\n",
699 printf(" (REMOVED)\n");
700 inp->i_parent = idesc->id_number; /* reparent to correct dir */
703 if (!preen && !reply("REMOVE"))
705 memset(&dotdesc, 0, sizeof(struct inodesc));
706 dotdesc.id_type = DATA;
707 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
708 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
709 dotdesc.id_func = deleteentry;
710 ginode(dotdesc.id_number, &ip);
711 if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen)
712 printf(" (REMOVED)\n");
714 inp->i_parent = idesc->id_number; /* reparent to correct directory */
715 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
720 deleteentry(struct inodesc *idesc)
722 struct direct *dirp = idesc->id_dirp;
724 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
727 return (ALTERED|STOP|FOUND);
731 * Routine to sort disk blocks.
734 blksort(const void *arg1, const void *arg2)
737 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
738 (*(struct inoinfo * const *)arg2)->i_blks[0]);