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;
220 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
221 if (reply("FIX") == 0)
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;
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 dirp->d_ino = idesc->id_number;
305 if (reply("FIX") == 1)
308 if (dirp->d_type != DT_DIR) {
309 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
310 dirp->d_type = DT_DIR;
311 if (reply("FIX") == 1)
316 direrror(idesc->id_number, "MISSING '.'");
317 proto.d_ino = idesc->id_number;
318 proto.d_type = DT_DIR;
320 (void)strcpy(proto.d_name, ".");
321 entrysize = DIRSIZ(0, &proto);
322 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
323 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
325 } else if (dirp->d_reclen < entrysize) {
326 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
327 } else if (dirp->d_reclen < 2 * entrysize) {
328 proto.d_reclen = dirp->d_reclen;
329 memmove(dirp, &proto, (size_t)entrysize);
330 if (reply("FIX") == 1)
333 n = dirp->d_reclen - entrysize;
334 proto.d_reclen = entrysize;
335 memmove(dirp, &proto, (size_t)entrysize);
337 inoinfo(dirp->d_ino)->ino_linkcnt--;
338 dirp = (struct direct *)((char *)(dirp) + entrysize);
339 memset(dirp, 0, (size_t)n);
341 if (reply("FIX") == 1)
345 if (idesc->id_entryno > 1)
347 inp = getinoinfo(idesc->id_number);
348 proto.d_ino = inp->i_parent;
349 proto.d_type = DT_DIR;
351 (void)strcpy(proto.d_name, "..");
352 entrysize = DIRSIZ(0, &proto);
353 if (idesc->id_entryno == 0) {
355 if (dirp->d_reclen < n + entrysize)
357 proto.d_reclen = dirp->d_reclen - n;
360 inoinfo(dirp->d_ino)->ino_linkcnt--;
361 dirp = (struct direct *)((char *)(dirp) + n);
362 memset(dirp, 0, (size_t)proto.d_reclen);
363 dirp->d_reclen = proto.d_reclen;
365 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
366 inp->i_dotdot = dirp->d_ino;
367 if (dirp->d_type != DT_DIR) {
368 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
369 dirp->d_type = DT_DIR;
370 if (reply("FIX") == 1)
375 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
376 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
377 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
379 inp->i_dotdot = (ino_t)-1;
380 } else if (dirp->d_reclen < entrysize) {
381 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
382 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
383 inp->i_dotdot = (ino_t)-1;
384 } else if (inp->i_parent != 0) {
386 * We know the parent, so fix now.
388 inp->i_dotdot = inp->i_parent;
389 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
390 proto.d_reclen = dirp->d_reclen;
391 memmove(dirp, &proto, (size_t)entrysize);
392 if (reply("FIX") == 1)
396 if (dirp->d_ino != 0)
397 inoinfo(dirp->d_ino)->ino_linkcnt--;
400 if (dirp->d_ino == 0)
402 if (dirp->d_namlen <= 2 &&
403 dirp->d_name[0] == '.' &&
404 idesc->id_entryno >= 2) {
405 if (dirp->d_namlen == 1) {
406 direrror(idesc->id_number, "EXTRA '.' ENTRY");
408 if (reply("FIX") == 1)
410 return (KEEPON | ret);
412 if (dirp->d_name[1] == '.') {
413 direrror(idesc->id_number, "EXTRA '..' ENTRY");
415 if (reply("FIX") == 1)
417 return (KEEPON | ret);
422 if (dirp->d_ino > maxino) {
423 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
425 } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
426 (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) {
427 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
428 dirp->d_ino = UFS_WINO;
429 dirp->d_type = DT_WHT;
430 if (reply("FIX") == 1)
434 switch (inoinfo(dirp->d_ino)->ino_state) {
436 if (idesc->id_entryno <= 2)
438 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
444 if (idesc->id_entryno <= 2)
446 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
448 else if (!preen && !usedsoftdep)
449 errmsg = "ZERO LENGTH DIRECTORY";
450 else if (cursnapshot == 0) {
454 getpathname(dirname, idesc->id_number,
456 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
457 dirname, (uintmax_t)dirp->d_ino);
460 * setcwd(idesc->id_parent);
461 * rmdir(dirp->d_name);
463 cmd.value = idesc->id_number;
464 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
465 &cmd, sizeof cmd) == -1) {
466 /* kernel lacks support */
467 printf(" (IGNORED)\n");
471 if (rmdir(dirp->d_name) == -1) {
472 printf(" (REMOVAL FAILED: %s)\n",
477 /* ".." reference to parent is removed */
478 inoinfo(idesc->id_number)->ino_linkcnt--;
479 printf(" (REMOVED)\n");
482 fileerror(idesc->id_number, dirp->d_ino, errmsg);
483 if ((n = reply("REMOVE")) == 1)
485 ginode(dirp->d_ino, &ip);
487 inoinfo(dirp->d_ino)->ino_state =
488 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
489 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
495 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
496 inoinfo(dirp->d_ino)->ino_state = DFOUND;
500 inp = getinoinfo(dirp->d_ino);
501 if (idesc->id_entryno > 2) {
502 if (inp->i_parent == 0)
503 inp->i_parent = idesc->id_number;
504 else if ((n = fix_extraneous(inp, idesc)) == 1)
511 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
512 fileerror(idesc->id_number, dirp->d_ino,
514 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
515 if (reply("FIX") == 1)
518 inoinfo(dirp->d_ino)->ino_linkcnt--;
522 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
523 inoinfo(dirp->d_ino)->ino_state,
524 (uintmax_t)dirp->d_ino);
530 return (ret|KEEPON|ALTERED);
534 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
538 struct inodesc dotdesc;
539 char oldname[MAXPATHLEN + 1];
540 char newname[MAXPATHLEN + 1];
543 * If we have not yet found "..", look it up now so we know
544 * which inode the directory itself believes is its parent.
546 if (inp->i_dotdot == 0) {
547 memset(&dotdesc, 0, sizeof(struct inodesc));
548 dotdesc.id_type = DATA;
549 dotdesc.id_number = idesc->id_dirp->d_ino;
550 dotdesc.id_func = findino;
551 dotdesc.id_name = strdup("..");
552 ginode(dotdesc.id_number, &ip);
553 if ((ckinode(ip.i_dp, &dotdesc) & FOUND))
554 inp->i_dotdot = dotdesc.id_parent;
558 * We have the previously found old name (inp->i_parent) and the
559 * just found new name (idesc->id_number). We have five cases:
560 * 1) ".." is missing - can remove either name, choose to delete
561 * new one and let fsck create ".." pointing to old name.
562 * 2) Both new and old are in same directory, choose to delete
563 * the new name and let fsck fix ".." if it is wrong.
564 * 3) ".." does not point to the new name, so delete it and let
565 * fsck fix ".." to point to the old one if it is wrong.
566 * 4) ".." points to the old name only, so delete the new one.
567 * 5) ".." points to the new name only, so delete the old one.
569 * For cases 1-4 we eliminate the new name;
570 * for case 5 we eliminate the old name.
572 if (inp->i_dotdot == 0 || /* Case 1 */
573 idesc->id_number == inp->i_parent || /* Case 2 */
574 inp->i_dotdot != idesc->id_number || /* Case 3 */
575 inp->i_dotdot == inp->i_parent) { /* Case 4 */
576 getpathname(newname, idesc->id_number, idesc->id_number);
577 if (strcmp(newname, "/") != 0)
578 strcat (newname, "/");
579 strcat(newname, idesc->id_dirp->d_name);
580 getpathname(oldname, inp->i_number, inp->i_number);
581 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
583 if (cursnapshot != 0) {
586 * setcwd(idesc->id_number);
587 * unlink(idesc->id_dirp->d_name);
589 cmd.value = idesc->id_number;
590 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
591 &cmd, sizeof cmd) == -1) {
592 printf(" (IGNORED)\n");
595 cmd.value = (intptr_t)idesc->id_dirp->d_name;
596 cmd.size = inp->i_number; /* verify same name */
597 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
598 &cmd, sizeof cmd) == -1) {
599 printf(" (UNLINK FAILED: %s)\n",
603 printf(" (REMOVED)\n");
607 printf(" (REMOVED)\n");
610 return (reply("REMOVE"));
613 * None of the first four cases above, so must be case (5).
614 * Eliminate the old name and make the new the name the parent.
616 getpathname(oldname, inp->i_parent, inp->i_number);
617 getpathname(newname, inp->i_number, inp->i_number);
618 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
620 if (cursnapshot != 0) {
623 * setcwd(inp->i_parent);
624 * unlink(last component of oldname pathname);
626 cmd.value = inp->i_parent;
627 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
628 &cmd, sizeof cmd) == -1) {
629 printf(" (IGNORED)\n");
632 if ((cp = strchr(oldname, '/')) == NULL) {
633 printf(" (IGNORED)\n");
636 cmd.value = (intptr_t)(cp + 1);
637 cmd.size = inp->i_number; /* verify same name */
638 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
639 &cmd, sizeof cmd) == -1) {
640 printf(" (UNLINK FAILED: %s)\n",
644 printf(" (REMOVED)\n");
645 inp->i_parent = idesc->id_number; /* reparent to correct dir */
648 if (!preen && !reply("REMOVE"))
650 memset(&dotdesc, 0, sizeof(struct inodesc));
651 dotdesc.id_type = DATA;
652 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
653 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
654 dotdesc.id_func = deleteentry;
655 ginode(dotdesc.id_number, &ip);
656 if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen)
657 printf(" (REMOVED)\n");
659 inp->i_parent = idesc->id_number; /* reparent to correct directory */
660 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
665 deleteentry(struct inodesc *idesc)
667 struct direct *dirp = idesc->id_dirp;
669 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
672 return (ALTERED|STOP|FOUND);
676 * Routine to sort disk blocks.
679 blksort(const void *arg1, const void *arg2)
682 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
683 (*(struct inoinfo * const *)arg2)->i_blks[0]);