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