2 * Copyright (c) 1980, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/sysctl.h>
41 #include <ufs/ufs/dinode.h>
42 #include <ufs/ufs/dir.h>
43 #include <ufs/ffs/fs.h>
52 #define MINDIRSIZE (sizeof (struct dirtemplate))
54 static int fix_extraneous(struct inoinfo *, struct inodesc *);
55 static int deleteentry(struct inodesc *);
56 static int blksort(const void *, const void *);
57 static int pass2check(struct inodesc *);
63 struct inoinfo **inpp, *inp;
64 struct inoinfo **inpend;
65 struct inodesc curino;
68 char pathbuf[MAXPATHLEN + 1];
70 switch (inoinfo(ROOTINO)->ino_state) {
73 pfatal("ROOT INODE UNALLOCATED");
74 if (reply("ALLOCATE") == 0) {
78 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
79 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
83 pfatal("DUPS/BAD IN ROOT INODE");
84 if (reply("REALLOCATE")) {
86 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
87 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
90 if (reply("CONTINUE") == 0) {
99 pfatal("ROOT INODE NOT DIRECTORY");
100 if (reply("REALLOCATE")) {
102 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
103 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
106 if (reply("FIX") == 0) {
110 dp = ginode(ROOTINO);
111 DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
112 DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
121 errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
122 inoinfo(ROOTINO)->ino_state);
124 inoinfo(ROOTINO)->ino_state = DFOUND;
125 inoinfo(WINO)->ino_state = FSTATE;
126 inoinfo(WINO)->ino_type = DT_WHT;
128 * Sort the directory list into disk block order.
130 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
132 * Check the integrity of each directory.
134 memset(&curino, 0, sizeof(struct inodesc));
135 curino.id_type = DATA;
136 curino.id_func = pass2check;
137 inpend = &inpsort[inplast];
138 for (inpp = inpsort; inpp < inpend; inpp++) {
140 printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
141 inpp - inpsort, (int)inplast,
142 (int)((inpp - inpsort) * 100 / inplast));
146 setproctitle("%s p2 %d%%", cdevname,
147 (int)((inpp - inpsort) * 100 / inplast));
151 if (inp->i_isize == 0)
153 if (inp->i_isize < MINDIRSIZE) {
154 direrror(inp->i_number, "DIRECTORY TOO SHORT");
155 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
156 if (reply("FIX") == 1) {
157 dp = ginode(inp->i_number);
158 DIP_SET(dp, di_size, inp->i_isize);
161 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
162 getpathname(pathbuf, inp->i_number, inp->i_number);
164 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
165 "DIRECTORY", pathbuf,
166 (intmax_t)inp->i_isize, DIRBLKSIZ);
168 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
169 "DIRECTORY", pathbuf,
170 (intmax_t)inp->i_isize, DIRBLKSIZ);
172 printf(" (ADJUSTED)\n");
173 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
174 if (preen || reply("ADJUST") == 1) {
175 dp = ginode(inp->i_number);
177 roundup(inp->i_isize, DIRBLKSIZ));
182 memset(dp, 0, sizeof(struct ufs2_dinode));
183 DIP_SET(dp, di_mode, IFDIR);
184 DIP_SET(dp, di_size, inp->i_isize);
186 i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
188 DIP_SET(dp, di_db[i], inp->i_blks[i]);
189 if (inp->i_numblks > NDADDR)
190 for (i = 0; i < NIADDR; i++)
191 DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
192 curino.id_number = inp->i_number;
193 curino.id_parent = inp->i_parent;
194 (void)ckinode(dp, &curino);
197 * Now that the parents of all directories have been found,
198 * make another pass to verify the value of `..'
200 for (inpp = inpsort; inpp < inpend; inpp++) {
202 if (inp->i_parent == 0 || inp->i_isize == 0)
204 if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
205 INO_IS_DUNFOUND(inp->i_number))
206 inoinfo(inp->i_number)->ino_state = DFOUND;
207 if (inp->i_dotdot == inp->i_parent ||
208 inp->i_dotdot == (ino_t)-1)
210 if (inp->i_dotdot == 0) {
211 inp->i_dotdot = inp->i_parent;
212 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
213 if (reply("FIX") == 0)
215 (void)makeentry(inp->i_number, inp->i_parent, "..");
216 inoinfo(inp->i_parent)->ino_linkcnt--;
221 * inp->i_number is directory with bad ".." in it.
222 * inp->i_dotdot is current value of "..".
223 * inp->i_parent is directory to which ".." should point.
225 getpathname(pathbuf, inp->i_parent, inp->i_number);
226 printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
227 (uintmax_t)inp->i_number, pathbuf);
228 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
229 printf("CURRENTLY POINTS TO I=%ju (%s), ",
230 (uintmax_t)inp->i_dotdot, pathbuf);
231 getpathname(pathbuf, inp->i_parent, inp->i_parent);
232 printf("SHOULD POINT TO I=%ju (%s)",
233 (uintmax_t)inp->i_parent, pathbuf);
234 if (cursnapshot != 0) {
237 * setcwd(inp->i_number);
238 * setdotdot(inp->i_dotdot, inp->i_parent);
240 cmd.value = inp->i_number;
241 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
242 &cmd, sizeof cmd) == -1) {
243 /* kernel lacks support for these functions */
244 printf(" (IGNORED)\n");
247 cmd.value = inp->i_dotdot; /* verify same value */
248 cmd.size = inp->i_parent; /* new parent */
249 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
250 &cmd, sizeof cmd) == -1) {
251 printf(" (FIX FAILED: %s)\n", strerror(errno));
254 printf(" (FIXED)\n");
255 inoinfo(inp->i_parent)->ino_linkcnt--;
256 inp->i_dotdot = inp->i_parent;
260 printf(" (FIXED)\n");
261 else if (reply("FIX") == 0)
263 inoinfo(inp->i_dotdot)->ino_linkcnt++;
264 inoinfo(inp->i_parent)->ino_linkcnt--;
265 inp->i_dotdot = inp->i_parent;
266 (void)changeino(inp->i_number, "..", inp->i_parent);
269 * Mark all the directories that can be found from the root.
275 pass2check(struct inodesc *idesc)
277 struct direct *dirp = idesc->id_dirp;
278 char dirname[MAXPATHLEN + 1];
280 int n, entrysize, ret = 0;
288 if (dirp->d_ino > maxino)
290 if (idesc->id_entryno != 0)
292 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
293 if (dirp->d_ino != idesc->id_number) {
294 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
295 dirp->d_ino = idesc->id_number;
296 if (reply("FIX") == 1)
299 if (dirp->d_type != DT_DIR) {
300 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
301 dirp->d_type = DT_DIR;
302 if (reply("FIX") == 1)
307 direrror(idesc->id_number, "MISSING '.'");
308 proto.d_ino = idesc->id_number;
309 proto.d_type = DT_DIR;
311 (void)strcpy(proto.d_name, ".");
312 entrysize = DIRSIZ(0, &proto);
313 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
314 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
316 } else if (dirp->d_reclen < entrysize) {
317 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
318 } else if (dirp->d_reclen < 2 * entrysize) {
319 proto.d_reclen = dirp->d_reclen;
320 memmove(dirp, &proto, (size_t)entrysize);
321 if (reply("FIX") == 1)
324 n = dirp->d_reclen - entrysize;
325 proto.d_reclen = entrysize;
326 memmove(dirp, &proto, (size_t)entrysize);
328 inoinfo(dirp->d_ino)->ino_linkcnt--;
329 dirp = (struct direct *)((char *)(dirp) + entrysize);
330 memset(dirp, 0, (size_t)n);
332 if (reply("FIX") == 1)
336 if (idesc->id_entryno > 1)
338 inp = getinoinfo(idesc->id_number);
339 proto.d_ino = inp->i_parent;
340 proto.d_type = DT_DIR;
342 (void)strcpy(proto.d_name, "..");
343 entrysize = DIRSIZ(0, &proto);
344 if (idesc->id_entryno == 0) {
346 if (dirp->d_reclen < n + entrysize)
348 proto.d_reclen = dirp->d_reclen - n;
351 inoinfo(dirp->d_ino)->ino_linkcnt--;
352 dirp = (struct direct *)((char *)(dirp) + n);
353 memset(dirp, 0, (size_t)proto.d_reclen);
354 dirp->d_reclen = proto.d_reclen;
356 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
357 inp->i_dotdot = dirp->d_ino;
358 if (dirp->d_type != DT_DIR) {
359 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
360 dirp->d_type = DT_DIR;
361 if (reply("FIX") == 1)
366 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
367 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
368 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
370 inp->i_dotdot = (ino_t)-1;
371 } else if (dirp->d_reclen < entrysize) {
372 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
373 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
374 inp->i_dotdot = (ino_t)-1;
375 } else if (inp->i_parent != 0) {
377 * We know the parent, so fix now.
379 inp->i_dotdot = inp->i_parent;
380 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
381 proto.d_reclen = dirp->d_reclen;
382 memmove(dirp, &proto, (size_t)entrysize);
383 if (reply("FIX") == 1)
387 if (dirp->d_ino != 0)
388 inoinfo(dirp->d_ino)->ino_linkcnt--;
391 if (dirp->d_ino == 0)
393 if (dirp->d_namlen <= 2 &&
394 dirp->d_name[0] == '.' &&
395 idesc->id_entryno >= 2) {
396 if (dirp->d_namlen == 1) {
397 direrror(idesc->id_number, "EXTRA '.' ENTRY");
399 if (reply("FIX") == 1)
401 return (KEEPON | ret);
403 if (dirp->d_name[1] == '.') {
404 direrror(idesc->id_number, "EXTRA '..' ENTRY");
406 if (reply("FIX") == 1)
408 return (KEEPON | ret);
413 if (dirp->d_ino > maxino) {
414 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
416 } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
417 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
418 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
420 dirp->d_type = DT_WHT;
421 if (reply("FIX") == 1)
425 switch (inoinfo(dirp->d_ino)->ino_state) {
427 if (idesc->id_entryno <= 2)
429 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
435 if (idesc->id_entryno <= 2)
437 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
439 else if (!preen && !usedsoftdep)
440 errmsg = "ZERO LENGTH DIRECTORY";
441 else if (cursnapshot == 0) {
445 getpathname(dirname, idesc->id_number,
447 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
448 dirname, (uintmax_t)dirp->d_ino);
451 * setcwd(idesc->id_parent);
452 * rmdir(dirp->d_name);
454 cmd.value = idesc->id_number;
455 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
456 &cmd, sizeof cmd) == -1) {
457 /* kernel lacks support */
458 printf(" (IGNORED)\n");
462 if (rmdir(dirp->d_name) == -1) {
463 printf(" (REMOVAL FAILED: %s)\n",
468 /* ".." reference to parent is removed */
469 inoinfo(idesc->id_number)->ino_linkcnt--;
470 printf(" (REMOVED)\n");
473 fileerror(idesc->id_number, dirp->d_ino, errmsg);
474 if ((n = reply("REMOVE")) == 1)
476 dp = ginode(dirp->d_ino);
477 inoinfo(dirp->d_ino)->ino_state =
478 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
479 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
484 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
485 inoinfo(dirp->d_ino)->ino_state = DFOUND;
489 inp = getinoinfo(dirp->d_ino);
490 if (idesc->id_entryno > 2) {
491 if (inp->i_parent == 0)
492 inp->i_parent = idesc->id_number;
493 else if ((n = fix_extraneous(inp, idesc)) == 1)
500 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
501 fileerror(idesc->id_number, dirp->d_ino,
503 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
504 if (reply("FIX") == 1)
507 inoinfo(dirp->d_ino)->ino_linkcnt--;
511 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
512 inoinfo(dirp->d_ino)->ino_state,
513 (uintmax_t)dirp->d_ino);
519 return (ret|KEEPON|ALTERED);
523 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
526 struct inodesc dotdesc;
527 char oldname[MAXPATHLEN + 1];
528 char newname[MAXPATHLEN + 1];
531 * If we have not yet found "..", look it up now so we know
532 * which inode the directory itself believes is its parent.
534 if (inp->i_dotdot == 0) {
535 memset(&dotdesc, 0, sizeof(struct inodesc));
536 dotdesc.id_type = DATA;
537 dotdesc.id_number = idesc->id_dirp->d_ino;
538 dotdesc.id_func = findino;
539 dotdesc.id_name = strdup("..");
540 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
541 inp->i_dotdot = dotdesc.id_parent;
544 * We have the previously found old name (inp->i_parent) and the
545 * just found new name (idesc->id_number). We have five cases:
546 * 1) ".." is missing - can remove either name, choose to delete
547 * new one and let fsck create ".." pointing to old name.
548 * 2) Both new and old are in same directory, choose to delete
549 * the new name and let fsck fix ".." if it is wrong.
550 * 3) ".." does not point to the new name, so delete it and let
551 * fsck fix ".." to point to the old one if it is wrong.
552 * 4) ".." points to the old name only, so delete the new one.
553 * 5) ".." points to the new name only, so delete the old one.
555 * For cases 1-4 we eliminate the new name;
556 * for case 5 we eliminate the old name.
558 if (inp->i_dotdot == 0 || /* Case 1 */
559 idesc->id_number == inp->i_parent || /* Case 2 */
560 inp->i_dotdot != idesc->id_number || /* Case 3 */
561 inp->i_dotdot == inp->i_parent) { /* Case 4 */
562 getpathname(newname, idesc->id_number, idesc->id_number);
563 if (strcmp(newname, "/") != 0)
564 strcat (newname, "/");
565 strcat(newname, idesc->id_dirp->d_name);
566 getpathname(oldname, inp->i_number, inp->i_number);
567 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
569 if (cursnapshot != 0) {
572 * setcwd(idesc->id_number);
573 * unlink(idesc->id_dirp->d_name);
575 cmd.value = idesc->id_number;
576 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
577 &cmd, sizeof cmd) == -1) {
578 printf(" (IGNORED)\n");
581 cmd.value = (intptr_t)idesc->id_dirp->d_name;
582 cmd.size = inp->i_number; /* verify same name */
583 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
584 &cmd, sizeof cmd) == -1) {
585 printf(" (UNLINK FAILED: %s)\n",
589 printf(" (REMOVED)\n");
593 printf(" (REMOVED)\n");
596 return (reply("REMOVE"));
599 * None of the first four cases above, so must be case (5).
600 * Eliminate the old name and make the new the name the parent.
602 getpathname(oldname, inp->i_parent, inp->i_number);
603 getpathname(newname, inp->i_number, inp->i_number);
604 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
606 if (cursnapshot != 0) {
609 * setcwd(inp->i_parent);
610 * unlink(last component of oldname pathname);
612 cmd.value = inp->i_parent;
613 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
614 &cmd, sizeof cmd) == -1) {
615 printf(" (IGNORED)\n");
618 if ((cp = strchr(oldname, '/')) == NULL) {
619 printf(" (IGNORED)\n");
622 cmd.value = (intptr_t)(cp + 1);
623 cmd.size = inp->i_number; /* verify same name */
624 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
625 &cmd, sizeof cmd) == -1) {
626 printf(" (UNLINK FAILED: %s)\n",
630 printf(" (REMOVED)\n");
631 inp->i_parent = idesc->id_number; /* reparent to correct dir */
634 if (!preen && !reply("REMOVE"))
636 memset(&dotdesc, 0, sizeof(struct inodesc));
637 dotdesc.id_type = DATA;
638 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
639 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
640 dotdesc.id_func = deleteentry;
641 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
642 printf(" (REMOVED)\n");
643 inp->i_parent = idesc->id_number; /* reparent to correct directory */
644 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
649 deleteentry(struct inodesc *idesc)
651 struct direct *dirp = idesc->id_dirp;
653 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
656 return (ALTERED|STOP|FOUND);
660 * Routine to sort disk blocks.
663 blksort(const void *arg1, const void *arg2)
666 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
667 (*(struct inoinfo * const *)arg2)->i_blks[0]);