Deleted Added
full compact
pass2.c (208330) pass2.c (221110)
1/*
2 * Copyright (c) 1980, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
16 *
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
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
33#endif /* not lint */
34#endif
35#include <sys/cdefs.h>
1/*
2 * Copyright (c) 1980, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
16 *
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
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
33#endif /* not lint */
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/sbin/fsck_ffs/pass2.c 208330 2010-05-20 06:05:40Z mckusick $");
36__FBSDID("$FreeBSD: head/sbin/fsck_ffs/pass2.c 221110 2011-04-27 02:55:03Z des $");
37
38#include <sys/param.h>
39#include <sys/sysctl.h>
40
41#include <ufs/ufs/dinode.h>
42#include <ufs/ufs/dir.h>
43#include <ufs/ffs/fs.h>
44
45#include <err.h>
46#include <errno.h>
47#include <stdint.h>
48#include <string.h>
49
50#include "fsck.h"
51
52#define MINDIRSIZE (sizeof (struct dirtemplate))
53
54static int fix_extraneous(struct inoinfo *, struct inodesc *);
55static int deleteentry(struct inodesc *);
56static int blksort(const void *, const void *);
57static int pass2check(struct inodesc *);
58
59void
60pass2(void)
61{
62 union dinode *dp;
63 struct inoinfo **inpp, *inp;
64 struct inoinfo **inpend;
65 struct inodesc curino;
66 union dinode dino;
67 int i;
68 char pathbuf[MAXPATHLEN + 1];
69
70 switch (inoinfo(ROOTINO)->ino_state) {
71
72 case USTATE:
73 pfatal("ROOT INODE UNALLOCATED");
74 if (reply("ALLOCATE") == 0) {
75 ckfini(0);
76 exit(EEXIT);
77 }
78 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
79 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
80 break;
81
82 case DCLEAR:
83 pfatal("DUPS/BAD IN ROOT INODE");
84 if (reply("REALLOCATE")) {
85 freeino(ROOTINO);
86 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
87 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
88 break;
89 }
90 if (reply("CONTINUE") == 0) {
91 ckfini(0);
92 exit(EEXIT);
93 }
94 break;
95
96 case FSTATE:
97 case FCLEAR:
98 case FZLINK:
99 pfatal("ROOT INODE NOT DIRECTORY");
100 if (reply("REALLOCATE")) {
101 freeino(ROOTINO);
102 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
103 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
104 break;
105 }
106 if (reply("FIX") == 0) {
107 ckfini(0);
108 exit(EEXIT);
109 }
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);
113 inodirty();
114 break;
115
116 case DSTATE:
117 case DZLINK:
118 break;
119
120 default:
121 errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
122 inoinfo(ROOTINO)->ino_state);
123 }
124 inoinfo(ROOTINO)->ino_state = DFOUND;
125 inoinfo(WINO)->ino_state = FSTATE;
126 inoinfo(WINO)->ino_type = DT_WHT;
127 /*
128 * Sort the directory list into disk block order.
129 */
130 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
131 /*
132 * Check the integrity of each directory.
133 */
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++) {
139 if (got_siginfo) {
140 printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
141 inpp - inpsort, (int)inplast,
142 (int)((inpp - inpsort) * 100 / inplast));
143 got_siginfo = 0;
144 }
145 if (got_sigalarm) {
146 setproctitle("%s p2 %d%%", cdevname,
147 (int)((inpp - inpsort) * 100 / inplast));
148 got_sigalarm = 0;
149 }
150 inp = *inpp;
151 if (inp->i_isize == 0)
152 continue;
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);
159 inodirty();
160 }
161 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
162 getpathname(pathbuf, inp->i_number, inp->i_number);
163 if (usedsoftdep)
164 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
165 "DIRECTORY", pathbuf,
166 (intmax_t)inp->i_isize, DIRBLKSIZ);
167 else
168 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
169 "DIRECTORY", pathbuf,
170 (intmax_t)inp->i_isize, DIRBLKSIZ);
171 if (preen)
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);
176 DIP_SET(dp, di_size,
177 roundup(inp->i_isize, DIRBLKSIZ));
178 inodirty();
179 }
180 }
181 dp = &dino;
182 memset(dp, 0, sizeof(struct ufs2_dinode));
183 DIP_SET(dp, di_mode, IFDIR);
184 DIP_SET(dp, di_size, inp->i_isize);
185 for (i = 0;
186 i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
187 i++)
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);
195 }
196 /*
197 * Now that the parents of all directories have been found,
198 * make another pass to verify the value of `..'
199 */
200 for (inpp = inpsort; inpp < inpend; inpp++) {
201 inp = *inpp;
202 if (inp->i_parent == 0 || inp->i_isize == 0)
203 continue;
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)
209 continue;
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)
214 continue;
215 (void)makeentry(inp->i_number, inp->i_parent, "..");
216 inoinfo(inp->i_parent)->ino_linkcnt--;
217 continue;
218 }
219 /*
220 * Here we have:
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.
224 */
225 getpathname(pathbuf, inp->i_parent, inp->i_number);
226 printf("BAD INODE NUMBER FOR '..' in DIR I=%d (%s)\n",
227 inp->i_number, pathbuf);
228 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
229 printf("CURRENTLY POINTS TO I=%d (%s), ", inp->i_dotdot,
230 pathbuf);
231 getpathname(pathbuf, inp->i_parent, inp->i_parent);
232 printf("SHOULD POINT TO I=%d (%s)", inp->i_parent, pathbuf);
233 if (cursnapshot != 0) {
234 /*
235 * We need to:
236 * setcwd(inp->i_number);
237 * setdotdot(inp->i_dotdot, inp->i_parent);
238 */
239 cmd.value = inp->i_number;
240 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
241 &cmd, sizeof cmd) == -1) {
242 /* kernel lacks support for these functions */
243 printf(" (IGNORED)\n");
244 continue;
245 }
246 cmd.value = inp->i_dotdot; /* verify same value */
247 cmd.size = inp->i_parent; /* new parent */
248 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
249 &cmd, sizeof cmd) == -1) {
250 printf(" (FIX FAILED: %s)\n", strerror(errno));
251 continue;
252 }
253 printf(" (FIXED)\n");
254 inoinfo(inp->i_parent)->ino_linkcnt--;
255 inp->i_dotdot = inp->i_parent;
256 continue;
257 }
258 if (preen)
259 printf(" (FIXED)\n");
260 else if (reply("FIX") == 0)
261 continue;
262 inoinfo(inp->i_dotdot)->ino_linkcnt++;
263 inoinfo(inp->i_parent)->ino_linkcnt--;
264 inp->i_dotdot = inp->i_parent;
265 (void)changeino(inp->i_number, "..", inp->i_parent);
266 }
267 /*
268 * Mark all the directories that can be found from the root.
269 */
270 propagate();
271}
272
273static int
274pass2check(struct inodesc *idesc)
275{
276 struct direct *dirp = idesc->id_dirp;
277 char dirname[MAXPATHLEN + 1];
278 struct inoinfo *inp;
279 int n, entrysize, ret = 0;
280 union dinode *dp;
281 const char *errmsg;
282 struct direct proto;
283
284 /*
285 * check for "."
286 */
287 if (dirp->d_ino > maxino)
288 goto chk2;
289 if (idesc->id_entryno != 0)
290 goto chk1;
291 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
292 if (dirp->d_ino != idesc->id_number) {
293 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
294 dirp->d_ino = idesc->id_number;
295 if (reply("FIX") == 1)
296 ret |= ALTERED;
297 }
298 if (dirp->d_type != DT_DIR) {
299 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
300 dirp->d_type = DT_DIR;
301 if (reply("FIX") == 1)
302 ret |= ALTERED;
303 }
304 goto chk1;
305 }
306 direrror(idesc->id_number, "MISSING '.'");
307 proto.d_ino = idesc->id_number;
308 proto.d_type = DT_DIR;
309 proto.d_namlen = 1;
310 (void)strcpy(proto.d_name, ".");
311 entrysize = DIRSIZ(0, &proto);
312 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
313 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
314 dirp->d_name);
315 } else if (dirp->d_reclen < entrysize) {
316 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
317 } else if (dirp->d_reclen < 2 * entrysize) {
318 proto.d_reclen = dirp->d_reclen;
319 memmove(dirp, &proto, (size_t)entrysize);
320 if (reply("FIX") == 1)
321 ret |= ALTERED;
322 } else {
323 n = dirp->d_reclen - entrysize;
324 proto.d_reclen = entrysize;
325 memmove(dirp, &proto, (size_t)entrysize);
326 idesc->id_entryno++;
327 inoinfo(dirp->d_ino)->ino_linkcnt--;
328 dirp = (struct direct *)((char *)(dirp) + entrysize);
329 memset(dirp, 0, (size_t)n);
330 dirp->d_reclen = n;
331 if (reply("FIX") == 1)
332 ret |= ALTERED;
333 }
334chk1:
335 if (idesc->id_entryno > 1)
336 goto chk2;
337 inp = getinoinfo(idesc->id_number);
338 proto.d_ino = inp->i_parent;
339 proto.d_type = DT_DIR;
340 proto.d_namlen = 2;
341 (void)strcpy(proto.d_name, "..");
342 entrysize = DIRSIZ(0, &proto);
343 if (idesc->id_entryno == 0) {
344 n = DIRSIZ(0, dirp);
345 if (dirp->d_reclen < n + entrysize)
346 goto chk2;
347 proto.d_reclen = dirp->d_reclen - n;
348 dirp->d_reclen = n;
349 idesc->id_entryno++;
350 inoinfo(dirp->d_ino)->ino_linkcnt--;
351 dirp = (struct direct *)((char *)(dirp) + n);
352 memset(dirp, 0, (size_t)proto.d_reclen);
353 dirp->d_reclen = proto.d_reclen;
354 }
355 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
356 inp->i_dotdot = dirp->d_ino;
357 if (dirp->d_type != DT_DIR) {
358 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
359 dirp->d_type = DT_DIR;
360 if (reply("FIX") == 1)
361 ret |= ALTERED;
362 }
363 goto chk2;
364 }
365 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
366 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
367 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
368 dirp->d_name);
369 inp->i_dotdot = (ino_t)-1;
370 } else if (dirp->d_reclen < entrysize) {
371 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
372 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
373 inp->i_dotdot = (ino_t)-1;
374 } else if (inp->i_parent != 0) {
375 /*
376 * We know the parent, so fix now.
377 */
378 inp->i_dotdot = inp->i_parent;
379 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
380 proto.d_reclen = dirp->d_reclen;
381 memmove(dirp, &proto, (size_t)entrysize);
382 if (reply("FIX") == 1)
383 ret |= ALTERED;
384 }
385 idesc->id_entryno++;
386 if (dirp->d_ino != 0)
387 inoinfo(dirp->d_ino)->ino_linkcnt--;
388 return (ret|KEEPON);
389chk2:
390 if (dirp->d_ino == 0)
391 return (ret|KEEPON);
392 if (dirp->d_namlen <= 2 &&
393 dirp->d_name[0] == '.' &&
394 idesc->id_entryno >= 2) {
395 if (dirp->d_namlen == 1) {
396 direrror(idesc->id_number, "EXTRA '.' ENTRY");
397 dirp->d_ino = 0;
398 if (reply("FIX") == 1)
399 ret |= ALTERED;
400 return (KEEPON | ret);
401 }
402 if (dirp->d_name[1] == '.') {
403 direrror(idesc->id_number, "EXTRA '..' ENTRY");
404 dirp->d_ino = 0;
405 if (reply("FIX") == 1)
406 ret |= ALTERED;
407 return (KEEPON | ret);
408 }
409 }
410 idesc->id_entryno++;
411 n = 0;
412 if (dirp->d_ino > maxino) {
413 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
414 n = reply("REMOVE");
415 } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
416 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
417 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
418 dirp->d_ino = WINO;
419 dirp->d_type = DT_WHT;
420 if (reply("FIX") == 1)
421 ret |= ALTERED;
422 } else {
423again:
424 switch (inoinfo(dirp->d_ino)->ino_state) {
425 case USTATE:
426 if (idesc->id_entryno <= 2)
427 break;
428 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
429 n = reply("REMOVE");
430 break;
431
432 case DCLEAR:
433 case FCLEAR:
434 if (idesc->id_entryno <= 2)
435 break;
436 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
437 errmsg = "DUP/BAD";
438 else if (!preen && !usedsoftdep)
439 errmsg = "ZERO LENGTH DIRECTORY";
440 else if (cursnapshot == 0) {
441 n = 1;
442 break;
443 } else {
444 getpathname(dirname, idesc->id_number,
445 dirp->d_ino);
446 pwarn("ZERO LENGTH DIRECTORY %s I=%d",
447 dirname, dirp->d_ino);
448 /*
449 * We need to:
450 * setcwd(idesc->id_parent);
451 * rmdir(dirp->d_name);
452 */
453 cmd.value = idesc->id_number;
454 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
455 &cmd, sizeof cmd) == -1) {
456 /* kernel lacks support */
457 printf(" (IGNORED)\n");
458 n = 1;
459 break;
460 }
461 if (rmdir(dirp->d_name) == -1) {
462 printf(" (REMOVAL FAILED: %s)\n",
463 strerror(errno));
464 n = 1;
465 break;
466 }
467 /* ".." reference to parent is removed */
468 inoinfo(idesc->id_number)->ino_linkcnt--;
469 printf(" (REMOVED)\n");
470 break;
471 }
472 fileerror(idesc->id_number, dirp->d_ino, errmsg);
473 if ((n = reply("REMOVE")) == 1)
474 break;
475 dp = ginode(dirp->d_ino);
476 inoinfo(dirp->d_ino)->ino_state =
477 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
478 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
479 goto again;
480
481 case DSTATE:
482 case DZLINK:
483 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
484 inoinfo(dirp->d_ino)->ino_state = DFOUND;
485 /* FALLTHROUGH */
486
487 case DFOUND:
488 inp = getinoinfo(dirp->d_ino);
489 if (idesc->id_entryno > 2) {
490 if (inp->i_parent == 0)
491 inp->i_parent = idesc->id_number;
492 else if ((n = fix_extraneous(inp, idesc)) == 1)
493 break;
494 }
495 /* FALLTHROUGH */
496
497 case FSTATE:
498 case FZLINK:
499 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
500 fileerror(idesc->id_number, dirp->d_ino,
501 "BAD TYPE VALUE");
502 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
503 if (reply("FIX") == 1)
504 ret |= ALTERED;
505 }
506 inoinfo(dirp->d_ino)->ino_linkcnt--;
507 break;
508
509 default:
510 errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
511 inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
512 }
513 }
514 if (n == 0)
515 return (ret|KEEPON);
516 dirp->d_ino = 0;
517 return (ret|KEEPON|ALTERED);
518}
519
520static int
521fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
522{
523 char *cp;
524 struct inodesc dotdesc;
525 char oldname[MAXPATHLEN + 1];
526 char newname[MAXPATHLEN + 1];
37
38#include <sys/param.h>
39#include <sys/sysctl.h>
40
41#include <ufs/ufs/dinode.h>
42#include <ufs/ufs/dir.h>
43#include <ufs/ffs/fs.h>
44
45#include <err.h>
46#include <errno.h>
47#include <stdint.h>
48#include <string.h>
49
50#include "fsck.h"
51
52#define MINDIRSIZE (sizeof (struct dirtemplate))
53
54static int fix_extraneous(struct inoinfo *, struct inodesc *);
55static int deleteentry(struct inodesc *);
56static int blksort(const void *, const void *);
57static int pass2check(struct inodesc *);
58
59void
60pass2(void)
61{
62 union dinode *dp;
63 struct inoinfo **inpp, *inp;
64 struct inoinfo **inpend;
65 struct inodesc curino;
66 union dinode dino;
67 int i;
68 char pathbuf[MAXPATHLEN + 1];
69
70 switch (inoinfo(ROOTINO)->ino_state) {
71
72 case USTATE:
73 pfatal("ROOT INODE UNALLOCATED");
74 if (reply("ALLOCATE") == 0) {
75 ckfini(0);
76 exit(EEXIT);
77 }
78 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
79 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
80 break;
81
82 case DCLEAR:
83 pfatal("DUPS/BAD IN ROOT INODE");
84 if (reply("REALLOCATE")) {
85 freeino(ROOTINO);
86 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
87 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
88 break;
89 }
90 if (reply("CONTINUE") == 0) {
91 ckfini(0);
92 exit(EEXIT);
93 }
94 break;
95
96 case FSTATE:
97 case FCLEAR:
98 case FZLINK:
99 pfatal("ROOT INODE NOT DIRECTORY");
100 if (reply("REALLOCATE")) {
101 freeino(ROOTINO);
102 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
103 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
104 break;
105 }
106 if (reply("FIX") == 0) {
107 ckfini(0);
108 exit(EEXIT);
109 }
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);
113 inodirty();
114 break;
115
116 case DSTATE:
117 case DZLINK:
118 break;
119
120 default:
121 errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
122 inoinfo(ROOTINO)->ino_state);
123 }
124 inoinfo(ROOTINO)->ino_state = DFOUND;
125 inoinfo(WINO)->ino_state = FSTATE;
126 inoinfo(WINO)->ino_type = DT_WHT;
127 /*
128 * Sort the directory list into disk block order.
129 */
130 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
131 /*
132 * Check the integrity of each directory.
133 */
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++) {
139 if (got_siginfo) {
140 printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
141 inpp - inpsort, (int)inplast,
142 (int)((inpp - inpsort) * 100 / inplast));
143 got_siginfo = 0;
144 }
145 if (got_sigalarm) {
146 setproctitle("%s p2 %d%%", cdevname,
147 (int)((inpp - inpsort) * 100 / inplast));
148 got_sigalarm = 0;
149 }
150 inp = *inpp;
151 if (inp->i_isize == 0)
152 continue;
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);
159 inodirty();
160 }
161 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
162 getpathname(pathbuf, inp->i_number, inp->i_number);
163 if (usedsoftdep)
164 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
165 "DIRECTORY", pathbuf,
166 (intmax_t)inp->i_isize, DIRBLKSIZ);
167 else
168 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
169 "DIRECTORY", pathbuf,
170 (intmax_t)inp->i_isize, DIRBLKSIZ);
171 if (preen)
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);
176 DIP_SET(dp, di_size,
177 roundup(inp->i_isize, DIRBLKSIZ));
178 inodirty();
179 }
180 }
181 dp = &dino;
182 memset(dp, 0, sizeof(struct ufs2_dinode));
183 DIP_SET(dp, di_mode, IFDIR);
184 DIP_SET(dp, di_size, inp->i_isize);
185 for (i = 0;
186 i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
187 i++)
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);
195 }
196 /*
197 * Now that the parents of all directories have been found,
198 * make another pass to verify the value of `..'
199 */
200 for (inpp = inpsort; inpp < inpend; inpp++) {
201 inp = *inpp;
202 if (inp->i_parent == 0 || inp->i_isize == 0)
203 continue;
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)
209 continue;
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)
214 continue;
215 (void)makeentry(inp->i_number, inp->i_parent, "..");
216 inoinfo(inp->i_parent)->ino_linkcnt--;
217 continue;
218 }
219 /*
220 * Here we have:
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.
224 */
225 getpathname(pathbuf, inp->i_parent, inp->i_number);
226 printf("BAD INODE NUMBER FOR '..' in DIR I=%d (%s)\n",
227 inp->i_number, pathbuf);
228 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
229 printf("CURRENTLY POINTS TO I=%d (%s), ", inp->i_dotdot,
230 pathbuf);
231 getpathname(pathbuf, inp->i_parent, inp->i_parent);
232 printf("SHOULD POINT TO I=%d (%s)", inp->i_parent, pathbuf);
233 if (cursnapshot != 0) {
234 /*
235 * We need to:
236 * setcwd(inp->i_number);
237 * setdotdot(inp->i_dotdot, inp->i_parent);
238 */
239 cmd.value = inp->i_number;
240 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
241 &cmd, sizeof cmd) == -1) {
242 /* kernel lacks support for these functions */
243 printf(" (IGNORED)\n");
244 continue;
245 }
246 cmd.value = inp->i_dotdot; /* verify same value */
247 cmd.size = inp->i_parent; /* new parent */
248 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
249 &cmd, sizeof cmd) == -1) {
250 printf(" (FIX FAILED: %s)\n", strerror(errno));
251 continue;
252 }
253 printf(" (FIXED)\n");
254 inoinfo(inp->i_parent)->ino_linkcnt--;
255 inp->i_dotdot = inp->i_parent;
256 continue;
257 }
258 if (preen)
259 printf(" (FIXED)\n");
260 else if (reply("FIX") == 0)
261 continue;
262 inoinfo(inp->i_dotdot)->ino_linkcnt++;
263 inoinfo(inp->i_parent)->ino_linkcnt--;
264 inp->i_dotdot = inp->i_parent;
265 (void)changeino(inp->i_number, "..", inp->i_parent);
266 }
267 /*
268 * Mark all the directories that can be found from the root.
269 */
270 propagate();
271}
272
273static int
274pass2check(struct inodesc *idesc)
275{
276 struct direct *dirp = idesc->id_dirp;
277 char dirname[MAXPATHLEN + 1];
278 struct inoinfo *inp;
279 int n, entrysize, ret = 0;
280 union dinode *dp;
281 const char *errmsg;
282 struct direct proto;
283
284 /*
285 * check for "."
286 */
287 if (dirp->d_ino > maxino)
288 goto chk2;
289 if (idesc->id_entryno != 0)
290 goto chk1;
291 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
292 if (dirp->d_ino != idesc->id_number) {
293 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
294 dirp->d_ino = idesc->id_number;
295 if (reply("FIX") == 1)
296 ret |= ALTERED;
297 }
298 if (dirp->d_type != DT_DIR) {
299 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
300 dirp->d_type = DT_DIR;
301 if (reply("FIX") == 1)
302 ret |= ALTERED;
303 }
304 goto chk1;
305 }
306 direrror(idesc->id_number, "MISSING '.'");
307 proto.d_ino = idesc->id_number;
308 proto.d_type = DT_DIR;
309 proto.d_namlen = 1;
310 (void)strcpy(proto.d_name, ".");
311 entrysize = DIRSIZ(0, &proto);
312 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
313 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
314 dirp->d_name);
315 } else if (dirp->d_reclen < entrysize) {
316 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
317 } else if (dirp->d_reclen < 2 * entrysize) {
318 proto.d_reclen = dirp->d_reclen;
319 memmove(dirp, &proto, (size_t)entrysize);
320 if (reply("FIX") == 1)
321 ret |= ALTERED;
322 } else {
323 n = dirp->d_reclen - entrysize;
324 proto.d_reclen = entrysize;
325 memmove(dirp, &proto, (size_t)entrysize);
326 idesc->id_entryno++;
327 inoinfo(dirp->d_ino)->ino_linkcnt--;
328 dirp = (struct direct *)((char *)(dirp) + entrysize);
329 memset(dirp, 0, (size_t)n);
330 dirp->d_reclen = n;
331 if (reply("FIX") == 1)
332 ret |= ALTERED;
333 }
334chk1:
335 if (idesc->id_entryno > 1)
336 goto chk2;
337 inp = getinoinfo(idesc->id_number);
338 proto.d_ino = inp->i_parent;
339 proto.d_type = DT_DIR;
340 proto.d_namlen = 2;
341 (void)strcpy(proto.d_name, "..");
342 entrysize = DIRSIZ(0, &proto);
343 if (idesc->id_entryno == 0) {
344 n = DIRSIZ(0, dirp);
345 if (dirp->d_reclen < n + entrysize)
346 goto chk2;
347 proto.d_reclen = dirp->d_reclen - n;
348 dirp->d_reclen = n;
349 idesc->id_entryno++;
350 inoinfo(dirp->d_ino)->ino_linkcnt--;
351 dirp = (struct direct *)((char *)(dirp) + n);
352 memset(dirp, 0, (size_t)proto.d_reclen);
353 dirp->d_reclen = proto.d_reclen;
354 }
355 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
356 inp->i_dotdot = dirp->d_ino;
357 if (dirp->d_type != DT_DIR) {
358 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
359 dirp->d_type = DT_DIR;
360 if (reply("FIX") == 1)
361 ret |= ALTERED;
362 }
363 goto chk2;
364 }
365 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
366 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
367 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
368 dirp->d_name);
369 inp->i_dotdot = (ino_t)-1;
370 } else if (dirp->d_reclen < entrysize) {
371 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
372 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
373 inp->i_dotdot = (ino_t)-1;
374 } else if (inp->i_parent != 0) {
375 /*
376 * We know the parent, so fix now.
377 */
378 inp->i_dotdot = inp->i_parent;
379 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
380 proto.d_reclen = dirp->d_reclen;
381 memmove(dirp, &proto, (size_t)entrysize);
382 if (reply("FIX") == 1)
383 ret |= ALTERED;
384 }
385 idesc->id_entryno++;
386 if (dirp->d_ino != 0)
387 inoinfo(dirp->d_ino)->ino_linkcnt--;
388 return (ret|KEEPON);
389chk2:
390 if (dirp->d_ino == 0)
391 return (ret|KEEPON);
392 if (dirp->d_namlen <= 2 &&
393 dirp->d_name[0] == '.' &&
394 idesc->id_entryno >= 2) {
395 if (dirp->d_namlen == 1) {
396 direrror(idesc->id_number, "EXTRA '.' ENTRY");
397 dirp->d_ino = 0;
398 if (reply("FIX") == 1)
399 ret |= ALTERED;
400 return (KEEPON | ret);
401 }
402 if (dirp->d_name[1] == '.') {
403 direrror(idesc->id_number, "EXTRA '..' ENTRY");
404 dirp->d_ino = 0;
405 if (reply("FIX") == 1)
406 ret |= ALTERED;
407 return (KEEPON | ret);
408 }
409 }
410 idesc->id_entryno++;
411 n = 0;
412 if (dirp->d_ino > maxino) {
413 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
414 n = reply("REMOVE");
415 } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
416 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
417 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
418 dirp->d_ino = WINO;
419 dirp->d_type = DT_WHT;
420 if (reply("FIX") == 1)
421 ret |= ALTERED;
422 } else {
423again:
424 switch (inoinfo(dirp->d_ino)->ino_state) {
425 case USTATE:
426 if (idesc->id_entryno <= 2)
427 break;
428 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
429 n = reply("REMOVE");
430 break;
431
432 case DCLEAR:
433 case FCLEAR:
434 if (idesc->id_entryno <= 2)
435 break;
436 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
437 errmsg = "DUP/BAD";
438 else if (!preen && !usedsoftdep)
439 errmsg = "ZERO LENGTH DIRECTORY";
440 else if (cursnapshot == 0) {
441 n = 1;
442 break;
443 } else {
444 getpathname(dirname, idesc->id_number,
445 dirp->d_ino);
446 pwarn("ZERO LENGTH DIRECTORY %s I=%d",
447 dirname, dirp->d_ino);
448 /*
449 * We need to:
450 * setcwd(idesc->id_parent);
451 * rmdir(dirp->d_name);
452 */
453 cmd.value = idesc->id_number;
454 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
455 &cmd, sizeof cmd) == -1) {
456 /* kernel lacks support */
457 printf(" (IGNORED)\n");
458 n = 1;
459 break;
460 }
461 if (rmdir(dirp->d_name) == -1) {
462 printf(" (REMOVAL FAILED: %s)\n",
463 strerror(errno));
464 n = 1;
465 break;
466 }
467 /* ".." reference to parent is removed */
468 inoinfo(idesc->id_number)->ino_linkcnt--;
469 printf(" (REMOVED)\n");
470 break;
471 }
472 fileerror(idesc->id_number, dirp->d_ino, errmsg);
473 if ((n = reply("REMOVE")) == 1)
474 break;
475 dp = ginode(dirp->d_ino);
476 inoinfo(dirp->d_ino)->ino_state =
477 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
478 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
479 goto again;
480
481 case DSTATE:
482 case DZLINK:
483 if (inoinfo(idesc->id_number)->ino_state == DFOUND)
484 inoinfo(dirp->d_ino)->ino_state = DFOUND;
485 /* FALLTHROUGH */
486
487 case DFOUND:
488 inp = getinoinfo(dirp->d_ino);
489 if (idesc->id_entryno > 2) {
490 if (inp->i_parent == 0)
491 inp->i_parent = idesc->id_number;
492 else if ((n = fix_extraneous(inp, idesc)) == 1)
493 break;
494 }
495 /* FALLTHROUGH */
496
497 case FSTATE:
498 case FZLINK:
499 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
500 fileerror(idesc->id_number, dirp->d_ino,
501 "BAD TYPE VALUE");
502 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
503 if (reply("FIX") == 1)
504 ret |= ALTERED;
505 }
506 inoinfo(dirp->d_ino)->ino_linkcnt--;
507 break;
508
509 default:
510 errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
511 inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
512 }
513 }
514 if (n == 0)
515 return (ret|KEEPON);
516 dirp->d_ino = 0;
517 return (ret|KEEPON|ALTERED);
518}
519
520static int
521fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
522{
523 char *cp;
524 struct inodesc dotdesc;
525 char oldname[MAXPATHLEN + 1];
526 char newname[MAXPATHLEN + 1];
527
527
528 /*
529 * If we have not yet found "..", look it up now so we know
530 * which inode the directory itself believes is its parent.
531 */
532 if (inp->i_dotdot == 0) {
533 memset(&dotdesc, 0, sizeof(struct inodesc));
534 dotdesc.id_type = DATA;
535 dotdesc.id_number = idesc->id_dirp->d_ino;
536 dotdesc.id_func = findino;
537 dotdesc.id_name = strdup("..");
538 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
539 inp->i_dotdot = dotdesc.id_parent;
540 }
541 /*
542 * We have the previously found old name (inp->i_parent) and the
543 * just found new name (idesc->id_number). We have five cases:
544 * 1) ".." is missing - can remove either name, choose to delete
545 * new one and let fsck create ".." pointing to old name.
546 * 2) Both new and old are in same directory, choose to delete
547 * the new name and let fsck fix ".." if it is wrong.
548 * 3) ".." does not point to the new name, so delete it and let
549 * fsck fix ".." to point to the old one if it is wrong.
550 * 4) ".." points to the old name only, so delete the new one.
551 * 5) ".." points to the new name only, so delete the old one.
552 *
553 * For cases 1-4 we eliminate the new name;
554 * for case 5 we eliminate the old name.
555 */
556 if (inp->i_dotdot == 0 || /* Case 1 */
557 idesc->id_number == inp->i_parent || /* Case 2 */
558 inp->i_dotdot != idesc->id_number || /* Case 3 */
559 inp->i_dotdot == inp->i_parent) { /* Case 4 */
560 getpathname(newname, idesc->id_number, idesc->id_number);
561 if (strcmp(newname, "/") != 0)
562 strcat (newname, "/");
563 strcat(newname, idesc->id_dirp->d_name);
564 getpathname(oldname, inp->i_number, inp->i_number);
565 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
566 newname, oldname);
567 if (cursnapshot != 0) {
568 /*
569 * We need to
570 * setcwd(idesc->id_number);
571 * unlink(idesc->id_dirp->d_name);
572 */
573 cmd.value = idesc->id_number;
574 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
575 &cmd, sizeof cmd) == -1) {
576 printf(" (IGNORED)\n");
577 return (0);
578 }
579 cmd.value = (intptr_t)idesc->id_dirp->d_name;
580 cmd.size = inp->i_number; /* verify same name */
581 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
582 &cmd, sizeof cmd) == -1) {
583 printf(" (UNLINK FAILED: %s)\n",
584 strerror(errno));
585 return (0);
586 }
587 printf(" (REMOVED)\n");
588 return (0);
589 }
590 if (preen) {
591 printf(" (REMOVED)\n");
592 return (1);
593 }
594 return (reply("REMOVE"));
595 }
596 /*
597 * None of the first four cases above, so must be case (5).
598 * Eliminate the old name and make the new the name the parent.
599 */
600 getpathname(oldname, inp->i_parent, inp->i_number);
601 getpathname(newname, inp->i_number, inp->i_number);
602 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
603 newname);
604 if (cursnapshot != 0) {
605 /*
606 * We need to
607 * setcwd(inp->i_parent);
608 * unlink(last component of oldname pathname);
609 */
610 cmd.value = inp->i_parent;
611 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
612 &cmd, sizeof cmd) == -1) {
613 printf(" (IGNORED)\n");
614 return (0);
615 }
616 if ((cp = rindex(oldname, '/')) == NULL) {
617 printf(" (IGNORED)\n");
618 return (0);
619 }
620 cmd.value = (intptr_t)(cp + 1);
621 cmd.size = inp->i_number; /* verify same name */
622 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
623 &cmd, sizeof cmd) == -1) {
624 printf(" (UNLINK FAILED: %s)\n",
625 strerror(errno));
626 return (0);
627 }
628 printf(" (REMOVED)\n");
629 inp->i_parent = idesc->id_number; /* reparent to correct dir */
630 return (0);
631 }
632 if (!preen && !reply("REMOVE"))
633 return (0);
634 memset(&dotdesc, 0, sizeof(struct inodesc));
635 dotdesc.id_type = DATA;
636 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
637 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
638 dotdesc.id_func = deleteentry;
639 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
640 printf(" (REMOVED)\n");
641 inp->i_parent = idesc->id_number; /* reparent to correct directory */
642 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
643 return (0);
644}
645
646static int
647deleteentry(struct inodesc *idesc)
648{
649 struct direct *dirp = idesc->id_dirp;
650
651 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
652 return (KEEPON);
653 dirp->d_ino = 0;
654 return (ALTERED|STOP|FOUND);
655}
656
657/*
658 * Routine to sort disk blocks.
659 */
660static int
661blksort(const void *arg1, const void *arg2)
662{
663
664 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
665 (*(struct inoinfo * const *)arg2)->i_blks[0]);
666}
528 /*
529 * If we have not yet found "..", look it up now so we know
530 * which inode the directory itself believes is its parent.
531 */
532 if (inp->i_dotdot == 0) {
533 memset(&dotdesc, 0, sizeof(struct inodesc));
534 dotdesc.id_type = DATA;
535 dotdesc.id_number = idesc->id_dirp->d_ino;
536 dotdesc.id_func = findino;
537 dotdesc.id_name = strdup("..");
538 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
539 inp->i_dotdot = dotdesc.id_parent;
540 }
541 /*
542 * We have the previously found old name (inp->i_parent) and the
543 * just found new name (idesc->id_number). We have five cases:
544 * 1) ".." is missing - can remove either name, choose to delete
545 * new one and let fsck create ".." pointing to old name.
546 * 2) Both new and old are in same directory, choose to delete
547 * the new name and let fsck fix ".." if it is wrong.
548 * 3) ".." does not point to the new name, so delete it and let
549 * fsck fix ".." to point to the old one if it is wrong.
550 * 4) ".." points to the old name only, so delete the new one.
551 * 5) ".." points to the new name only, so delete the old one.
552 *
553 * For cases 1-4 we eliminate the new name;
554 * for case 5 we eliminate the old name.
555 */
556 if (inp->i_dotdot == 0 || /* Case 1 */
557 idesc->id_number == inp->i_parent || /* Case 2 */
558 inp->i_dotdot != idesc->id_number || /* Case 3 */
559 inp->i_dotdot == inp->i_parent) { /* Case 4 */
560 getpathname(newname, idesc->id_number, idesc->id_number);
561 if (strcmp(newname, "/") != 0)
562 strcat (newname, "/");
563 strcat(newname, idesc->id_dirp->d_name);
564 getpathname(oldname, inp->i_number, inp->i_number);
565 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
566 newname, oldname);
567 if (cursnapshot != 0) {
568 /*
569 * We need to
570 * setcwd(idesc->id_number);
571 * unlink(idesc->id_dirp->d_name);
572 */
573 cmd.value = idesc->id_number;
574 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
575 &cmd, sizeof cmd) == -1) {
576 printf(" (IGNORED)\n");
577 return (0);
578 }
579 cmd.value = (intptr_t)idesc->id_dirp->d_name;
580 cmd.size = inp->i_number; /* verify same name */
581 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
582 &cmd, sizeof cmd) == -1) {
583 printf(" (UNLINK FAILED: %s)\n",
584 strerror(errno));
585 return (0);
586 }
587 printf(" (REMOVED)\n");
588 return (0);
589 }
590 if (preen) {
591 printf(" (REMOVED)\n");
592 return (1);
593 }
594 return (reply("REMOVE"));
595 }
596 /*
597 * None of the first four cases above, so must be case (5).
598 * Eliminate the old name and make the new the name the parent.
599 */
600 getpathname(oldname, inp->i_parent, inp->i_number);
601 getpathname(newname, inp->i_number, inp->i_number);
602 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
603 newname);
604 if (cursnapshot != 0) {
605 /*
606 * We need to
607 * setcwd(inp->i_parent);
608 * unlink(last component of oldname pathname);
609 */
610 cmd.value = inp->i_parent;
611 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
612 &cmd, sizeof cmd) == -1) {
613 printf(" (IGNORED)\n");
614 return (0);
615 }
616 if ((cp = rindex(oldname, '/')) == NULL) {
617 printf(" (IGNORED)\n");
618 return (0);
619 }
620 cmd.value = (intptr_t)(cp + 1);
621 cmd.size = inp->i_number; /* verify same name */
622 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
623 &cmd, sizeof cmd) == -1) {
624 printf(" (UNLINK FAILED: %s)\n",
625 strerror(errno));
626 return (0);
627 }
628 printf(" (REMOVED)\n");
629 inp->i_parent = idesc->id_number; /* reparent to correct dir */
630 return (0);
631 }
632 if (!preen && !reply("REMOVE"))
633 return (0);
634 memset(&dotdesc, 0, sizeof(struct inodesc));
635 dotdesc.id_type = DATA;
636 dotdesc.id_number = inp->i_parent; /* directory in which name appears */
637 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
638 dotdesc.id_func = deleteentry;
639 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
640 printf(" (REMOVED)\n");
641 inp->i_parent = idesc->id_number; /* reparent to correct directory */
642 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
643 return (0);
644}
645
646static int
647deleteentry(struct inodesc *idesc)
648{
649 struct direct *dirp = idesc->id_dirp;
650
651 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
652 return (KEEPON);
653 dirp->d_ino = 0;
654 return (ALTERED|STOP|FOUND);
655}
656
657/*
658 * Routine to sort disk blocks.
659 */
660static int
661blksort(const void *arg1, const void *arg2)
662{
663
664 return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
665 (*(struct inoinfo * const *)arg2)->i_blks[0]);
666}