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