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 * 3. 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(UFS_ROOTINO)->ino_state) {
73 pfatal("ROOT INODE UNALLOCATED");
74 if (reply("ALLOCATE") == 0) {
78 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
79 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
83 pfatal("DUPS/BAD IN ROOT INODE");
84 if (reply("REALLOCATE")) {
86 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
88 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
91 if (reply("CONTINUE") == 0) {
100 pfatal("ROOT INODE NOT DIRECTORY");
101 if (reply("REALLOCATE")) {
102 freeino(UFS_ROOTINO);
103 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
105 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
108 if (reply("FIX") == 0) {
112 dp = ginode(UFS_ROOTINO);
113 DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
114 DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
123 errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
124 inoinfo(UFS_ROOTINO)->ino_state);
126 inoinfo(UFS_ROOTINO)->ino_state = DFOUND;
127 inoinfo(UFS_WINO)->ino_state = FSTATE;
128 inoinfo(UFS_WINO)->ino_type = DT_WHT;
130 * Sort the directory list into disk block order.
132 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
134 * Check the integrity of each directory.
136 memset(&curino, 0, sizeof(struct inodesc));
137 curino.id_type = DATA;
138 curino.id_func = pass2check;
139 inpend = &inpsort[inplast];
140 for (inpp = inpsort; inpp < inpend; inpp++) {
142 printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
143 inpp - inpsort, (int)inplast,
144 (int)((inpp - inpsort) * 100 / inplast));
148 setproctitle("%s p2 %d%%", cdevname,
149 (int)((inpp - inpsort) * 100 / inplast));
153 if (inp->i_isize == 0)
155 if (inp->i_isize < MINDIRSIZE) {
156 direrror(inp->i_number, "DIRECTORY TOO SHORT");
157 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
158 if (reply("FIX") == 1) {
159 dp = ginode(inp->i_number);
160 DIP_SET(dp, di_size, inp->i_isize);
163 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
164 getpathname(pathbuf, inp->i_number, inp->i_number);
166 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
167 "DIRECTORY", pathbuf,
168 (intmax_t)inp->i_isize, DIRBLKSIZ);
170 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
171 "DIRECTORY", pathbuf,
172 (intmax_t)inp->i_isize, DIRBLKSIZ);
174 printf(" (ADJUSTED)\n");
175 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
176 if (preen || reply("ADJUST") == 1) {
177 dp = ginode(inp->i_number);
179 roundup(inp->i_isize, DIRBLKSIZ));
184 memset(dp, 0, sizeof(struct ufs2_dinode));
185 DIP_SET(dp, di_mode, IFDIR);
186 DIP_SET(dp, di_size, inp->i_isize);
187 for (i = 0; i < MIN(inp->i_numblks, UFS_NDADDR); i++)
188 DIP_SET(dp, di_db[i], inp->i_blks[i]);
189 if (inp->i_numblks > UFS_NDADDR)
190 for (i = 0; i < UFS_NIADDR; i++)
191 DIP_SET(dp, di_ib[i],
192 inp->i_blks[UFS_NDADDR + i]);
193 curino.id_number = inp->i_number;
194 curino.id_parent = inp->i_parent;
195 (void)ckinode(dp, &curino);
198 * Now that the parents of all directories have been found,
199 * make another pass to verify the value of `..'
201 for (inpp = inpsort; inpp < inpend; inpp++) {
203 if (inp->i_parent == 0 || inp->i_isize == 0)
205 if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
206 INO_IS_DUNFOUND(inp->i_number))
207 inoinfo(inp->i_number)->ino_state = DFOUND;
208 if (inp->i_dotdot == inp->i_parent ||
209 inp->i_dotdot == (ino_t)-1)
211 if (inp->i_dotdot == 0) {
212 inp->i_dotdot = inp->i_parent;
213 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
214 if (reply("FIX") == 0)
216 (void)makeentry(inp->i_number, inp->i_parent, "..");
217 inoinfo(inp->i_parent)->ino_linkcnt--;
222 * inp->i_number is directory with bad ".." in it.
223 * inp->i_dotdot is current value of "..".
224 * inp->i_parent is directory to which ".." should point.
226 getpathname(pathbuf, inp->i_parent, inp->i_number);
227 printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
228 (uintmax_t)inp->i_number, pathbuf);
229 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
230 printf("CURRENTLY POINTS TO I=%ju (%s), ",
231 (uintmax_t)inp->i_dotdot, pathbuf);
232 getpathname(pathbuf, inp->i_parent, inp->i_parent);
233 printf("SHOULD POINT TO I=%ju (%s)",
234 (uintmax_t)inp->i_parent, pathbuf);
235 if (cursnapshot != 0) {
238 * setcwd(inp->i_number);
239 * setdotdot(inp->i_dotdot, inp->i_parent);
241 cmd.value = inp->i_number;
242 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
243 &cmd, sizeof cmd) == -1) {
244 /* kernel lacks support for these functions */
245 printf(" (IGNORED)\n");
248 cmd.value = inp->i_dotdot; /* verify same value */
249 cmd.size = inp->i_parent; /* new parent */
250 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
251 &cmd, sizeof cmd) == -1) {
252 printf(" (FIX FAILED: %s)\n", strerror(errno));
255 printf(" (FIXED)\n");
256 inoinfo(inp->i_parent)->ino_linkcnt--;
257 inp->i_dotdot = inp->i_parent;
261 printf(" (FIXED)\n");
262 else if (reply("FIX") == 0)
264 inoinfo(inp->i_dotdot)->ino_linkcnt++;
265 inoinfo(inp->i_parent)->ino_linkcnt--;
266 inp->i_dotdot = inp->i_parent;
267 (void)changeino(inp->i_number, "..", inp->i_parent);
270 * Mark all the directories that can be found from the root.
276 pass2check(struct inodesc *idesc)
278 struct direct *dirp = idesc->id_dirp;
279 char dirname[MAXPATHLEN + 1];
281 int n, entrysize, ret = 0;
289 if (dirp->d_ino > maxino)
291 if (idesc->id_entryno != 0)
293 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
294 if (dirp->d_ino != idesc->id_number) {
295 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
296 dirp->d_ino = idesc->id_number;
297 if (reply("FIX") == 1)
300 if (dirp->d_type != DT_DIR) {
301 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
302 dirp->d_type = DT_DIR;
303 if (reply("FIX") == 1)
308 direrror(idesc->id_number, "MISSING '.'");
309 proto.d_ino = idesc->id_number;
310 proto.d_type = DT_DIR;
312 (void)strcpy(proto.d_name, ".");
313 entrysize = DIRSIZ(0, &proto);
314 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
315 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
317 } else if (dirp->d_reclen < entrysize) {
318 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
319 } else if (dirp->d_reclen < 2 * entrysize) {
320 proto.d_reclen = dirp->d_reclen;
321 memmove(dirp, &proto, (size_t)entrysize);
322 if (reply("FIX") == 1)
325 n = dirp->d_reclen - entrysize;
326 proto.d_reclen = entrysize;
327 memmove(dirp, &proto, (size_t)entrysize);
329 inoinfo(dirp->d_ino)->ino_linkcnt--;
330 dirp = (struct direct *)((char *)(dirp) + entrysize);
331 memset(dirp, 0, (size_t)n);
333 if (reply("FIX") == 1)
337 if (idesc->id_entryno > 1)
339 inp = getinoinfo(idesc->id_number);
340 proto.d_ino = inp->i_parent;
341 proto.d_type = DT_DIR;
343 (void)strcpy(proto.d_name, "..");
344 entrysize = DIRSIZ(0, &proto);
345 if (idesc->id_entryno == 0) {
347 if (dirp->d_reclen < n + entrysize)
349 proto.d_reclen = dirp->d_reclen - n;
352 inoinfo(dirp->d_ino)->ino_linkcnt--;
353 dirp = (struct direct *)((char *)(dirp) + n);
354 memset(dirp, 0, (size_t)proto.d_reclen);
355 dirp->d_reclen = proto.d_reclen;
357 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
358 inp->i_dotdot = dirp->d_ino;
359 if (dirp->d_type != DT_DIR) {
360 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
361 dirp->d_type = DT_DIR;
362 if (reply("FIX") == 1)
367 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
368 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
369 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
371 inp->i_dotdot = (ino_t)-1;
372 } else if (dirp->d_reclen < entrysize) {
373 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
374 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
375 inp->i_dotdot = (ino_t)-1;
376 } else if (inp->i_parent != 0) {
378 * We know the parent, so fix now.
380 inp->i_dotdot = inp->i_parent;
381 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
382 proto.d_reclen = dirp->d_reclen;
383 memmove(dirp, &proto, (size_t)entrysize);
384 if (reply("FIX") == 1)
388 if (dirp->d_ino != 0)
389 inoinfo(dirp->d_ino)->ino_linkcnt--;
392 if (dirp->d_ino == 0)
394 if (dirp->d_namlen <= 2 &&
395 dirp->d_name[0] == '.' &&
396 idesc->id_entryno >= 2) {
397 if (dirp->d_namlen == 1) {
398 direrror(idesc->id_number, "EXTRA '.' ENTRY");
400 if (reply("FIX") == 1)
402 return (KEEPON | ret);
404 if (dirp->d_name[1] == '.') {
405 direrror(idesc->id_number, "EXTRA '..' ENTRY");
407 if (reply("FIX") == 1)
409 return (KEEPON | ret);
414 if (dirp->d_ino > maxino) {
415 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
417 } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
418 (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) {
419 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
420 dirp->d_ino = UFS_WINO;
421 dirp->d_type = DT_WHT;
422 if (reply("FIX") == 1)
426 switch (inoinfo(dirp->d_ino)->ino_state) {
428 if (idesc->id_entryno <= 2)
430 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
436 if (idesc->id_entryno <= 2)
438 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
440 else if (!preen && !usedsoftdep)
441 errmsg = "ZERO LENGTH DIRECTORY";
442 else if (cursnapshot == 0) {
446 getpathname(dirname, idesc->id_number,
448 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
449 dirname, (uintmax_t)dirp->d_ino);
452 * setcwd(idesc->id_parent);
453 * rmdir(dirp->d_name);
455 cmd.value = idesc->id_number;
456 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
457 &cmd, sizeof cmd) == -1) {
458 /* kernel lacks support */
459 printf(" (IGNORED)\n");
463 if (rmdir(dirp->d_name) == -1) {
464 printf(" (REMOVAL FAILED: %s)\n",
469 /* ".." reference to parent is removed */
470 inoinfo(idesc->id_number)->ino_linkcnt--;
471 printf(" (REMOVED)\n");
474 fileerror(idesc->id_number, dirp->d_ino, errmsg);
475 if ((n = reply("REMOVE")) == 1)
477 dp = ginode(dirp->d_ino);
478 inoinfo(dirp->d_ino)->ino_state =
479 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
480 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
485 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
486 inoinfo(dirp->d_ino)->ino_state = DFOUND;
490 inp = getinoinfo(dirp->d_ino);
491 if (idesc->id_entryno > 2) {
492 if (inp->i_parent == 0)
493 inp->i_parent = idesc->id_number;
494 else if ((n = fix_extraneous(inp, idesc)) == 1)
501 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
502 fileerror(idesc->id_number, dirp->d_ino,
504 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
505 if (reply("FIX") == 1)
508 inoinfo(dirp->d_ino)->ino_linkcnt--;
512 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
513 inoinfo(dirp->d_ino)->ino_state,
514 (uintmax_t)dirp->d_ino);
520 return (ret|KEEPON|ALTERED);
524 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
527 struct inodesc dotdesc;
528 char oldname[MAXPATHLEN + 1];
529 char newname[MAXPATHLEN + 1];
532 * If we have not yet found "..", look it up now so we know
533 * which inode the directory itself believes is its parent.
535 if (inp->i_dotdot == 0) {
536 memset(&dotdesc, 0, sizeof(struct inodesc));
537 dotdesc.id_type = DATA;
538 dotdesc.id_number = idesc->id_dirp->d_ino;
539 dotdesc.id_func = findino;
540 dotdesc.id_name = strdup("..");
541 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
542 inp->i_dotdot = dotdesc.id_parent;
545 * We have the previously found old name (inp->i_parent) and the
546 * just found new name (idesc->id_number). We have five cases:
547 * 1) ".." is missing - can remove either name, choose to delete
548 * new one and let fsck create ".." pointing to old name.
549 * 2) Both new and old are in same directory, choose to delete
550 * the new name and let fsck fix ".." if it is wrong.
551 * 3) ".." does not point to the new name, so delete it and let
552 * fsck fix ".." to point to the old one if it is wrong.
553 * 4) ".." points to the old name only, so delete the new one.
554 * 5) ".." points to the new name only, so delete the old one.
556 * For cases 1-4 we eliminate the new name;
557 * for case 5 we eliminate the old name.
559 if (inp->i_dotdot == 0 || /* Case 1 */
560 idesc->id_number == inp->i_parent || /* Case 2 */
561 inp->i_dotdot != idesc->id_number || /* Case 3 */
562 inp->i_dotdot == inp->i_parent) { /* Case 4 */
563 getpathname(newname, idesc->id_number, idesc->id_number);
564 if (strcmp(newname, "/") != 0)
565 strcat (newname, "/");
566 strcat(newname, idesc->id_dirp->d_name);
567 getpathname(oldname, inp->i_number, inp->i_number);
568 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
570 if (cursnapshot != 0) {
573 * setcwd(idesc->id_number);
574 * unlink(idesc->id_dirp->d_name);
576 cmd.value = idesc->id_number;
577 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
578 &cmd, sizeof cmd) == -1) {
579 printf(" (IGNORED)\n");
582 cmd.value = (intptr_t)idesc->id_dirp->d_name;
583 cmd.size = inp->i_number; /* verify same name */
584 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
585 &cmd, sizeof cmd) == -1) {
586 printf(" (UNLINK FAILED: %s)\n",
590 printf(" (REMOVED)\n");
594 printf(" (REMOVED)\n");
597 return (reply("REMOVE"));
600 * None of the first four cases above, so must be case (5).
601 * Eliminate the old name and make the new the name the parent.
603 getpathname(oldname, inp->i_parent, inp->i_number);
604 getpathname(newname, inp->i_number, inp->i_number);
605 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
607 if (cursnapshot != 0) {
610 * setcwd(inp->i_parent);
611 * unlink(last component of oldname pathname);
613 cmd.value = inp->i_parent;
614 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
615 &cmd, sizeof cmd) == -1) {
616 printf(" (IGNORED)\n");
619 if ((cp = strchr(oldname, '/')) == NULL) {
620 printf(" (IGNORED)\n");
623 cmd.value = (intptr_t)(cp + 1);
624 cmd.size = inp->i_number; /* verify same name */
625 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
626 &cmd, sizeof cmd) == -1) {
627 printf(" (UNLINK FAILED: %s)\n",
631 printf(" (REMOVED)\n");
632 inp->i_parent = idesc->id_number; /* reparent to correct dir */
635 if (!preen && !reply("REMOVE"))
637 memset(&dotdesc, 0, sizeof(struct inodesc));
638 dotdesc.id_type = DATA;
639 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
640 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
641 dotdesc.id_func = deleteentry;
642 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
643 printf(" (REMOVED)\n");
644 inp->i_parent = idesc->id_number; /* reparent to correct directory */
645 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
650 deleteentry(struct inodesc *idesc)
652 struct direct *dirp = idesc->id_dirp;
654 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
657 return (ALTERED|STOP|FOUND);
661 * Routine to sort disk blocks.
664 blksort(const void *arg1, const void *arg2)
667 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
668 (*(struct inoinfo * const *)arg2)->i_blks[0]);