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 *);
65 struct inoinfo **inpp, *inp;
66 struct inoinfo **inpend;
67 struct inodesc curino;
70 char pathbuf[MAXPATHLEN + 1];
72 switch (inoinfo(UFS_ROOTINO)->ino_state) {
75 pfatal("ROOT INODE UNALLOCATED");
76 if (reply("ALLOCATE") == 0) {
80 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
81 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
85 pfatal("DUPS/BAD IN ROOT INODE");
86 if (reply("REALLOCATE")) {
88 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
90 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
93 if (reply("CONTINUE") == 0) {
102 pfatal("ROOT INODE NOT DIRECTORY");
103 if (reply("REALLOCATE")) {
104 freeino(UFS_ROOTINO);
105 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
107 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
110 if (reply("FIX") == 0) {
114 dp = ginode(UFS_ROOTINO);
115 DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
116 DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
125 errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
126 inoinfo(UFS_ROOTINO)->ino_state);
128 inoinfo(UFS_ROOTINO)->ino_state = DFOUND;
129 inoinfo(UFS_WINO)->ino_state = FSTATE;
130 inoinfo(UFS_WINO)->ino_type = DT_WHT;
132 * Sort the directory list into disk block order.
134 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
136 * Check the integrity of each directory.
138 memset(&curino, 0, sizeof(struct inodesc));
139 curino.id_type = DATA;
140 curino.id_func = pass2check;
141 inpend = &inpsort[inplast];
142 for (inpp = inpsort; inpp < inpend; inpp++) {
144 printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
145 inpp - inpsort, (int)inplast,
146 (int)((inpp - inpsort) * 100 / inplast));
150 setproctitle("%s p2 %d%%", cdevname,
151 (int)((inpp - inpsort) * 100 / inplast));
155 if (inp->i_isize == 0)
157 if (inp->i_isize < MINDIRSIZE) {
158 direrror(inp->i_number, "DIRECTORY TOO SHORT");
159 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
160 if (reply("FIX") == 1) {
161 dp = ginode(inp->i_number);
162 DIP_SET(dp, di_size, inp->i_isize);
165 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
166 getpathname(pathbuf, inp->i_number, inp->i_number);
168 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
169 "DIRECTORY", pathbuf,
170 (intmax_t)inp->i_isize, DIRBLKSIZ);
172 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
173 "DIRECTORY", pathbuf,
174 (intmax_t)inp->i_isize, DIRBLKSIZ);
176 printf(" (ADJUSTED)\n");
177 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
178 if (preen || reply("ADJUST") == 1) {
179 dp = ginode(inp->i_number);
181 roundup(inp->i_isize, DIRBLKSIZ));
186 memset(dp, 0, sizeof(struct ufs2_dinode));
187 DIP_SET(dp, di_mode, IFDIR);
188 DIP_SET(dp, di_size, inp->i_isize);
189 for (i = 0; i < MIN(inp->i_numblks, UFS_NDADDR); i++)
190 DIP_SET(dp, di_db[i], inp->i_blks[i]);
191 if (inp->i_numblks > UFS_NDADDR)
192 for (i = 0; i < UFS_NIADDR; i++)
193 DIP_SET(dp, di_ib[i],
194 inp->i_blks[UFS_NDADDR + i]);
195 curino.id_number = inp->i_number;
196 curino.id_parent = inp->i_parent;
197 (void)ckinode(dp, &curino);
200 * Now that the parents of all directories have been found,
201 * make another pass to verify the value of `..'
203 for (inpp = inpsort; inpp < inpend; inpp++) {
205 if (inp->i_parent == 0 || inp->i_isize == 0)
207 if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
208 INO_IS_DUNFOUND(inp->i_number))
209 inoinfo(inp->i_number)->ino_state = DFOUND;
210 if (inp->i_dotdot == inp->i_parent ||
211 inp->i_dotdot == (ino_t)-1)
213 if (inp->i_dotdot == 0) {
214 inp->i_dotdot = inp->i_parent;
215 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
216 if (reply("FIX") == 0)
218 (void)makeentry(inp->i_number, inp->i_parent, "..");
219 inoinfo(inp->i_parent)->ino_linkcnt--;
224 * inp->i_number is directory with bad ".." in it.
225 * inp->i_dotdot is current value of "..".
226 * inp->i_parent is directory to which ".." should point.
228 getpathname(pathbuf, inp->i_parent, inp->i_number);
229 printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
230 (uintmax_t)inp->i_number, pathbuf);
231 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
232 printf("CURRENTLY POINTS TO I=%ju (%s), ",
233 (uintmax_t)inp->i_dotdot, pathbuf);
234 getpathname(pathbuf, inp->i_parent, inp->i_parent);
235 printf("SHOULD POINT TO I=%ju (%s)",
236 (uintmax_t)inp->i_parent, pathbuf);
237 if (cursnapshot != 0) {
240 * setcwd(inp->i_number);
241 * setdotdot(inp->i_dotdot, inp->i_parent);
243 cmd.value = inp->i_number;
244 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
245 &cmd, sizeof cmd) == -1) {
246 /* kernel lacks support for these functions */
247 printf(" (IGNORED)\n");
250 cmd.value = inp->i_dotdot; /* verify same value */
251 cmd.size = inp->i_parent; /* new parent */
252 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
253 &cmd, sizeof cmd) == -1) {
254 printf(" (FIX FAILED: %s)\n", strerror(errno));
257 printf(" (FIXED)\n");
258 inoinfo(inp->i_parent)->ino_linkcnt--;
259 inp->i_dotdot = inp->i_parent;
263 printf(" (FIXED)\n");
264 else if (reply("FIX") == 0)
266 inoinfo(inp->i_dotdot)->ino_linkcnt++;
267 inoinfo(inp->i_parent)->ino_linkcnt--;
268 inp->i_dotdot = inp->i_parent;
269 (void)changeino(inp->i_number, "..", inp->i_parent);
272 * Mark all the directories that can be found from the root.
278 pass2check(struct inodesc *idesc)
280 struct direct *dirp = idesc->id_dirp;
281 char dirname[MAXPATHLEN + 1];
283 int n, entrysize, ret = 0;
291 if (dirp->d_ino > maxino)
293 if (idesc->id_entryno != 0)
295 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
296 if (dirp->d_ino != idesc->id_number) {
297 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
298 dirp->d_ino = idesc->id_number;
299 if (reply("FIX") == 1)
302 if (dirp->d_type != DT_DIR) {
303 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
304 dirp->d_type = DT_DIR;
305 if (reply("FIX") == 1)
310 direrror(idesc->id_number, "MISSING '.'");
311 proto.d_ino = idesc->id_number;
312 proto.d_type = DT_DIR;
314 (void)strcpy(proto.d_name, ".");
315 entrysize = DIRSIZ(0, &proto);
316 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
317 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
319 } else if (dirp->d_reclen < entrysize) {
320 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
321 } else if (dirp->d_reclen < 2 * entrysize) {
322 proto.d_reclen = dirp->d_reclen;
323 memmove(dirp, &proto, (size_t)entrysize);
324 if (reply("FIX") == 1)
327 n = dirp->d_reclen - entrysize;
328 proto.d_reclen = entrysize;
329 memmove(dirp, &proto, (size_t)entrysize);
331 inoinfo(dirp->d_ino)->ino_linkcnt--;
332 dirp = (struct direct *)((char *)(dirp) + entrysize);
333 memset(dirp, 0, (size_t)n);
335 if (reply("FIX") == 1)
339 if (idesc->id_entryno > 1)
341 inp = getinoinfo(idesc->id_number);
342 proto.d_ino = inp->i_parent;
343 proto.d_type = DT_DIR;
345 (void)strcpy(proto.d_name, "..");
346 entrysize = DIRSIZ(0, &proto);
347 if (idesc->id_entryno == 0) {
349 if (dirp->d_reclen < n + entrysize)
351 proto.d_reclen = dirp->d_reclen - n;
354 inoinfo(dirp->d_ino)->ino_linkcnt--;
355 dirp = (struct direct *)((char *)(dirp) + n);
356 memset(dirp, 0, (size_t)proto.d_reclen);
357 dirp->d_reclen = proto.d_reclen;
359 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
360 inp->i_dotdot = dirp->d_ino;
361 if (dirp->d_type != DT_DIR) {
362 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
363 dirp->d_type = DT_DIR;
364 if (reply("FIX") == 1)
369 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
370 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
371 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
373 inp->i_dotdot = (ino_t)-1;
374 } else if (dirp->d_reclen < entrysize) {
375 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
376 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
377 inp->i_dotdot = (ino_t)-1;
378 } else if (inp->i_parent != 0) {
380 * We know the parent, so fix now.
382 inp->i_dotdot = inp->i_parent;
383 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
384 proto.d_reclen = dirp->d_reclen;
385 memmove(dirp, &proto, (size_t)entrysize);
386 if (reply("FIX") == 1)
390 if (dirp->d_ino != 0)
391 inoinfo(dirp->d_ino)->ino_linkcnt--;
394 if (dirp->d_ino == 0)
396 if (dirp->d_namlen <= 2 &&
397 dirp->d_name[0] == '.' &&
398 idesc->id_entryno >= 2) {
399 if (dirp->d_namlen == 1) {
400 direrror(idesc->id_number, "EXTRA '.' ENTRY");
402 if (reply("FIX") == 1)
404 return (KEEPON | ret);
406 if (dirp->d_name[1] == '.') {
407 direrror(idesc->id_number, "EXTRA '..' ENTRY");
409 if (reply("FIX") == 1)
411 return (KEEPON | ret);
416 if (dirp->d_ino > maxino) {
417 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
419 } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
420 (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) {
421 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
422 dirp->d_ino = UFS_WINO;
423 dirp->d_type = DT_WHT;
424 if (reply("FIX") == 1)
428 switch (inoinfo(dirp->d_ino)->ino_state) {
430 if (idesc->id_entryno <= 2)
432 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
438 if (idesc->id_entryno <= 2)
440 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
442 else if (!preen && !usedsoftdep)
443 errmsg = "ZERO LENGTH DIRECTORY";
444 else if (cursnapshot == 0) {
448 getpathname(dirname, idesc->id_number,
450 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
451 dirname, (uintmax_t)dirp->d_ino);
454 * setcwd(idesc->id_parent);
455 * rmdir(dirp->d_name);
457 cmd.value = idesc->id_number;
458 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
459 &cmd, sizeof cmd) == -1) {
460 /* kernel lacks support */
461 printf(" (IGNORED)\n");
465 if (rmdir(dirp->d_name) == -1) {
466 printf(" (REMOVAL FAILED: %s)\n",
471 /* ".." reference to parent is removed */
472 inoinfo(idesc->id_number)->ino_linkcnt--;
473 printf(" (REMOVED)\n");
476 fileerror(idesc->id_number, dirp->d_ino, errmsg);
477 if ((n = reply("REMOVE")) == 1)
479 dp = ginode(dirp->d_ino);
480 inoinfo(dirp->d_ino)->ino_state =
481 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
482 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
487 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
488 inoinfo(dirp->d_ino)->ino_state = DFOUND;
492 inp = getinoinfo(dirp->d_ino);
493 if (idesc->id_entryno > 2) {
494 if (inp->i_parent == 0)
495 inp->i_parent = idesc->id_number;
496 else if ((n = fix_extraneous(inp, idesc)) == 1)
503 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
504 fileerror(idesc->id_number, dirp->d_ino,
506 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
507 if (reply("FIX") == 1)
510 inoinfo(dirp->d_ino)->ino_linkcnt--;
514 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
515 inoinfo(dirp->d_ino)->ino_state,
516 (uintmax_t)dirp->d_ino);
522 return (ret|KEEPON|ALTERED);
526 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
529 struct inodesc dotdesc;
530 char oldname[MAXPATHLEN + 1];
531 char newname[MAXPATHLEN + 1];
534 * If we have not yet found "..", look it up now so we know
535 * which inode the directory itself believes is its parent.
537 if (inp->i_dotdot == 0) {
538 memset(&dotdesc, 0, sizeof(struct inodesc));
539 dotdesc.id_type = DATA;
540 dotdesc.id_number = idesc->id_dirp->d_ino;
541 dotdesc.id_func = findino;
542 dotdesc.id_name = strdup("..");
543 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
544 inp->i_dotdot = dotdesc.id_parent;
547 * We have the previously found old name (inp->i_parent) and the
548 * just found new name (idesc->id_number). We have five cases:
549 * 1) ".." is missing - can remove either name, choose to delete
550 * new one and let fsck create ".." pointing to old name.
551 * 2) Both new and old are in same directory, choose to delete
552 * the new name and let fsck fix ".." if it is wrong.
553 * 3) ".." does not point to the new name, so delete it and let
554 * fsck fix ".." to point to the old one if it is wrong.
555 * 4) ".." points to the old name only, so delete the new one.
556 * 5) ".." points to the new name only, so delete the old one.
558 * For cases 1-4 we eliminate the new name;
559 * for case 5 we eliminate the old name.
561 if (inp->i_dotdot == 0 || /* Case 1 */
562 idesc->id_number == inp->i_parent || /* Case 2 */
563 inp->i_dotdot != idesc->id_number || /* Case 3 */
564 inp->i_dotdot == inp->i_parent) { /* Case 4 */
565 getpathname(newname, idesc->id_number, idesc->id_number);
566 if (strcmp(newname, "/") != 0)
567 strcat (newname, "/");
568 strcat(newname, idesc->id_dirp->d_name);
569 getpathname(oldname, inp->i_number, inp->i_number);
570 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
572 if (cursnapshot != 0) {
575 * setcwd(idesc->id_number);
576 * unlink(idesc->id_dirp->d_name);
578 cmd.value = idesc->id_number;
579 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
580 &cmd, sizeof cmd) == -1) {
581 printf(" (IGNORED)\n");
584 cmd.value = (intptr_t)idesc->id_dirp->d_name;
585 cmd.size = inp->i_number; /* verify same name */
586 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
587 &cmd, sizeof cmd) == -1) {
588 printf(" (UNLINK FAILED: %s)\n",
592 printf(" (REMOVED)\n");
596 printf(" (REMOVED)\n");
599 return (reply("REMOVE"));
602 * None of the first four cases above, so must be case (5).
603 * Eliminate the old name and make the new the name the parent.
605 getpathname(oldname, inp->i_parent, inp->i_number);
606 getpathname(newname, inp->i_number, inp->i_number);
607 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
609 if (cursnapshot != 0) {
612 * setcwd(inp->i_parent);
613 * unlink(last component of oldname pathname);
615 cmd.value = inp->i_parent;
616 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
617 &cmd, sizeof cmd) == -1) {
618 printf(" (IGNORED)\n");
621 if ((cp = strchr(oldname, '/')) == NULL) {
622 printf(" (IGNORED)\n");
625 cmd.value = (intptr_t)(cp + 1);
626 cmd.size = inp->i_number; /* verify same name */
627 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
628 &cmd, sizeof cmd) == -1) {
629 printf(" (UNLINK FAILED: %s)\n",
633 printf(" (REMOVED)\n");
634 inp->i_parent = idesc->id_number; /* reparent to correct dir */
637 if (!preen && !reply("REMOVE"))
639 memset(&dotdesc, 0, sizeof(struct inodesc));
640 dotdesc.id_type = DATA;
641 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
642 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
643 dotdesc.id_func = deleteentry;
644 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
645 printf(" (REMOVED)\n");
646 inp->i_parent = idesc->id_number; /* reparent to correct directory */
647 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
652 deleteentry(struct inodesc *idesc)
654 struct direct *dirp = idesc->id_dirp;
656 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
659 return (ALTERED|STOP|FOUND);
663 * Routine to sort disk blocks.
666 blksort(const void *arg1, const void *arg2)
669 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
670 (*(struct inoinfo * const *)arg2)->i_blks[0]);