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