]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sbin/fsck_ffs/pass2.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sbin / fsck_ffs / pass2.c
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
32 static 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$");
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
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
60 pass2(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=%ju (%s)\n",
227                     (uintmax_t)inp->i_number, pathbuf);
228                 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
229                 printf("CURRENTLY POINTS TO I=%ju (%s), ",
230                     (uintmax_t)inp->i_dotdot, pathbuf);
231                 getpathname(pathbuf, inp->i_parent, inp->i_parent);
232                 printf("SHOULD POINT TO I=%ju (%s)",
233                     (uintmax_t)inp->i_parent, pathbuf);
234                 if (cursnapshot != 0) {
235                         /*
236                          * We need to:
237                          *    setcwd(inp->i_number);
238                          *    setdotdot(inp->i_dotdot, inp->i_parent);
239                          */
240                         cmd.value = inp->i_number;
241                         if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
242                             &cmd, sizeof cmd) == -1) {
243                                 /* kernel lacks support for these functions */
244                                 printf(" (IGNORED)\n");
245                                 continue;
246                         }
247                         cmd.value = inp->i_dotdot; /* verify same value */
248                         cmd.size = inp->i_parent;  /* new parent */
249                         if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
250                             &cmd, sizeof cmd) == -1) {
251                                 printf(" (FIX FAILED: %s)\n", strerror(errno));
252                                 continue;
253                         }
254                         printf(" (FIXED)\n");
255                         inoinfo(inp->i_parent)->ino_linkcnt--;
256                         inp->i_dotdot = inp->i_parent;
257                         continue;
258                 }
259                 if (preen)
260                         printf(" (FIXED)\n");
261                 else if (reply("FIX") == 0)
262                         continue;
263                 inoinfo(inp->i_dotdot)->ino_linkcnt++;
264                 inoinfo(inp->i_parent)->ino_linkcnt--;
265                 inp->i_dotdot = inp->i_parent;
266                 (void)changeino(inp->i_number, "..", inp->i_parent);
267         }
268         /*
269          * Mark all the directories that can be found from the root.
270          */
271         propagate();
272 }
273
274 static int
275 pass2check(struct inodesc *idesc)
276 {
277         struct direct *dirp = idesc->id_dirp;
278         char dirname[MAXPATHLEN + 1];
279         struct inoinfo *inp;
280         int n, entrysize, ret = 0;
281         union dinode *dp;
282         const char *errmsg;
283         struct direct proto;
284
285         /*
286          * check for "."
287          */
288         if (dirp->d_ino > maxino)
289                 goto chk2;
290         if (idesc->id_entryno != 0)
291                 goto chk1;
292         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
293                 if (dirp->d_ino != idesc->id_number) {
294                         direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
295                         dirp->d_ino = idesc->id_number;
296                         if (reply("FIX") == 1)
297                                 ret |= ALTERED;
298                 }
299                 if (dirp->d_type != DT_DIR) {
300                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
301                         dirp->d_type = DT_DIR;
302                         if (reply("FIX") == 1)
303                                 ret |= ALTERED;
304                 }
305                 goto chk1;
306         }
307         direrror(idesc->id_number, "MISSING '.'");
308         proto.d_ino = idesc->id_number;
309         proto.d_type = DT_DIR;
310         proto.d_namlen = 1;
311         (void)strcpy(proto.d_name, ".");
312         entrysize = DIRSIZ(0, &proto);
313         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
314                 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
315                         dirp->d_name);
316         } else if (dirp->d_reclen < entrysize) {
317                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
318         } else if (dirp->d_reclen < 2 * entrysize) {
319                 proto.d_reclen = dirp->d_reclen;
320                 memmove(dirp, &proto, (size_t)entrysize);
321                 if (reply("FIX") == 1)
322                         ret |= ALTERED;
323         } else {
324                 n = dirp->d_reclen - entrysize;
325                 proto.d_reclen = entrysize;
326                 memmove(dirp, &proto, (size_t)entrysize);
327                 idesc->id_entryno++;
328                 inoinfo(dirp->d_ino)->ino_linkcnt--;
329                 dirp = (struct direct *)((char *)(dirp) + entrysize);
330                 memset(dirp, 0, (size_t)n);
331                 dirp->d_reclen = n;
332                 if (reply("FIX") == 1)
333                         ret |= ALTERED;
334         }
335 chk1:
336         if (idesc->id_entryno > 1)
337                 goto chk2;
338         inp = getinoinfo(idesc->id_number);
339         proto.d_ino = inp->i_parent;
340         proto.d_type = DT_DIR;
341         proto.d_namlen = 2;
342         (void)strcpy(proto.d_name, "..");
343         entrysize = DIRSIZ(0, &proto);
344         if (idesc->id_entryno == 0) {
345                 n = DIRSIZ(0, dirp);
346                 if (dirp->d_reclen < n + entrysize)
347                         goto chk2;
348                 proto.d_reclen = dirp->d_reclen - n;
349                 dirp->d_reclen = n;
350                 idesc->id_entryno++;
351                 inoinfo(dirp->d_ino)->ino_linkcnt--;
352                 dirp = (struct direct *)((char *)(dirp) + n);
353                 memset(dirp, 0, (size_t)proto.d_reclen);
354                 dirp->d_reclen = proto.d_reclen;
355         }
356         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
357                 inp->i_dotdot = dirp->d_ino;
358                 if (dirp->d_type != DT_DIR) {
359                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
360                         dirp->d_type = DT_DIR;
361                         if (reply("FIX") == 1)
362                                 ret |= ALTERED;
363                 }
364                 goto chk2;
365         }
366         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
367                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
368                 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
369                         dirp->d_name);
370                 inp->i_dotdot = (ino_t)-1;
371         } else if (dirp->d_reclen < entrysize) {
372                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
373                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
374                 inp->i_dotdot = (ino_t)-1;
375         } else if (inp->i_parent != 0) {
376                 /*
377                  * We know the parent, so fix now.
378                  */
379                 inp->i_dotdot = inp->i_parent;
380                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
381                 proto.d_reclen = dirp->d_reclen;
382                 memmove(dirp, &proto, (size_t)entrysize);
383                 if (reply("FIX") == 1)
384                         ret |= ALTERED;
385         }
386         idesc->id_entryno++;
387         if (dirp->d_ino != 0)
388                 inoinfo(dirp->d_ino)->ino_linkcnt--;
389         return (ret|KEEPON);
390 chk2:
391         if (dirp->d_ino == 0)
392                 return (ret|KEEPON);
393         if (dirp->d_namlen <= 2 &&
394             dirp->d_name[0] == '.' &&
395             idesc->id_entryno >= 2) {
396                 if (dirp->d_namlen == 1) {
397                         direrror(idesc->id_number, "EXTRA '.' ENTRY");
398                         dirp->d_ino = 0;
399                         if (reply("FIX") == 1)
400                                 ret |= ALTERED;
401                         return (KEEPON | ret);
402                 }
403                 if (dirp->d_name[1] == '.') {
404                         direrror(idesc->id_number, "EXTRA '..' ENTRY");
405                         dirp->d_ino = 0;
406                         if (reply("FIX") == 1)
407                                 ret |= ALTERED;
408                         return (KEEPON | ret);
409                 }
410         }
411         idesc->id_entryno++;
412         n = 0;
413         if (dirp->d_ino > maxino) {
414                 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
415                 n = reply("REMOVE");
416         } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
417                     (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
418                 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
419                 dirp->d_ino = WINO;
420                 dirp->d_type = DT_WHT;
421                 if (reply("FIX") == 1)
422                         ret |= ALTERED;
423         } else {
424 again:
425                 switch (inoinfo(dirp->d_ino)->ino_state) {
426                 case USTATE:
427                         if (idesc->id_entryno <= 2)
428                                 break;
429                         fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
430                         n = reply("REMOVE");
431                         break;
432
433                 case DCLEAR:
434                 case FCLEAR:
435                         if (idesc->id_entryno <= 2)
436                                 break;
437                         if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
438                                 errmsg = "DUP/BAD";
439                         else if (!preen && !usedsoftdep)
440                                 errmsg = "ZERO LENGTH DIRECTORY";
441                         else if (cursnapshot == 0) {
442                                 n = 1;
443                                 break;
444                         } else {
445                                 getpathname(dirname, idesc->id_number,
446                                     dirp->d_ino);
447                                 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
448                                     dirname, (uintmax_t)dirp->d_ino);
449                                 /*
450                                  * We need to:
451                                  *    setcwd(idesc->id_parent);
452                                  *    rmdir(dirp->d_name);
453                                  */
454                                 cmd.value = idesc->id_number;
455                                 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
456                                     &cmd, sizeof cmd) == -1) {
457                                         /* kernel lacks support */
458                                         printf(" (IGNORED)\n");
459                                         n = 1;
460                                         break;
461                                 }
462                                 if (rmdir(dirp->d_name) == -1) {
463                                         printf(" (REMOVAL FAILED: %s)\n",
464                                             strerror(errno));
465                                         n = 1;
466                                         break;
467                                 }
468                                 /* ".." reference to parent is removed */
469                                 inoinfo(idesc->id_number)->ino_linkcnt--;
470                                 printf(" (REMOVED)\n");
471                                 break;
472                         }
473                         fileerror(idesc->id_number, dirp->d_ino, errmsg);
474                         if ((n = reply("REMOVE")) == 1)
475                                 break;
476                         dp = ginode(dirp->d_ino);
477                         inoinfo(dirp->d_ino)->ino_state =
478                            (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
479                         inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
480                         goto again;
481
482                 case DSTATE:
483                 case DZLINK:
484                         if (inoinfo(idesc->id_number)->ino_state == DFOUND)
485                                 inoinfo(dirp->d_ino)->ino_state = DFOUND;
486                         /* FALLTHROUGH */
487
488                 case DFOUND:
489                         inp = getinoinfo(dirp->d_ino);
490                         if (idesc->id_entryno > 2) {
491                                 if (inp->i_parent == 0)
492                                         inp->i_parent = idesc->id_number;
493                                 else if ((n = fix_extraneous(inp, idesc)) == 1)
494                                         break;
495                         }
496                         /* FALLTHROUGH */
497
498                 case FSTATE:
499                 case FZLINK:
500                         if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
501                                 fileerror(idesc->id_number, dirp->d_ino,
502                                     "BAD TYPE VALUE");
503                                 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
504                                 if (reply("FIX") == 1)
505                                         ret |= ALTERED;
506                         }
507                         inoinfo(dirp->d_ino)->ino_linkcnt--;
508                         break;
509
510                 default:
511                         errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
512                             inoinfo(dirp->d_ino)->ino_state,
513                             (uintmax_t)dirp->d_ino);
514                 }
515         }
516         if (n == 0)
517                 return (ret|KEEPON);
518         dirp->d_ino = 0;
519         return (ret|KEEPON|ALTERED);
520 }
521
522 static int
523 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
524 {
525         char *cp;
526         struct inodesc dotdesc;
527         char oldname[MAXPATHLEN + 1];
528         char newname[MAXPATHLEN + 1];
529
530         /*
531          * If we have not yet found "..", look it up now so we know
532          * which inode the directory itself believes is its parent.
533          */
534         if (inp->i_dotdot == 0) {
535                 memset(&dotdesc, 0, sizeof(struct inodesc));
536                 dotdesc.id_type = DATA;
537                 dotdesc.id_number = idesc->id_dirp->d_ino;
538                 dotdesc.id_func = findino;
539                 dotdesc.id_name = strdup("..");
540                 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
541                         inp->i_dotdot = dotdesc.id_parent;
542         }
543         /*
544          * We have the previously found old name (inp->i_parent) and the
545          * just found new name (idesc->id_number). We have five cases:
546          * 1)  ".." is missing - can remove either name, choose to delete
547          *     new one and let fsck create ".." pointing to old name.
548          * 2) Both new and old are in same directory, choose to delete
549          *    the new name and let fsck fix ".." if it is wrong.
550          * 3) ".." does not point to the new name, so delete it and let
551          *    fsck fix ".." to point to the old one if it is wrong.
552          * 4) ".." points to the old name only, so delete the new one.
553          * 5) ".." points to the new name only, so delete the old one.
554          *
555          * For cases 1-4 we eliminate the new name;
556          * for case 5 we eliminate the old name.
557          */
558         if (inp->i_dotdot == 0 ||                   /* Case 1 */
559             idesc->id_number == inp->i_parent ||    /* Case 2 */
560             inp->i_dotdot != idesc->id_number ||    /* Case 3 */
561             inp->i_dotdot == inp->i_parent) {       /* Case 4 */
562                 getpathname(newname, idesc->id_number, idesc->id_number);
563                 if (strcmp(newname, "/") != 0)
564                         strcat (newname, "/");
565                 strcat(newname, idesc->id_dirp->d_name);
566                 getpathname(oldname, inp->i_number, inp->i_number);
567                 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
568                     newname, oldname);
569                 if (cursnapshot != 0) {
570                         /*
571                          * We need to
572                          *    setcwd(idesc->id_number);
573                          *    unlink(idesc->id_dirp->d_name);
574                          */
575                         cmd.value = idesc->id_number;
576                         if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
577                             &cmd, sizeof cmd) == -1) {
578                                 printf(" (IGNORED)\n");
579                                 return (0);
580                         }
581                         cmd.value = (intptr_t)idesc->id_dirp->d_name;
582                         cmd.size = inp->i_number; /* verify same name */
583                         if (sysctlbyname("vfs.ffs.unlink", 0, 0,
584                             &cmd, sizeof cmd) == -1) {
585                                 printf(" (UNLINK FAILED: %s)\n",
586                                     strerror(errno));
587                                 return (0);
588                         }
589                         printf(" (REMOVED)\n");
590                         return (0);
591                 }
592                 if (preen) {
593                         printf(" (REMOVED)\n");
594                         return (1);
595                 }
596                 return (reply("REMOVE"));
597         }
598         /*
599          * None of the first four cases above, so must be case (5).
600          * Eliminate the old name and make the new the name the parent.
601          */
602         getpathname(oldname, inp->i_parent, inp->i_number);
603         getpathname(newname, inp->i_number, inp->i_number);
604         pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
605             newname);
606         if (cursnapshot != 0) {
607                 /*
608                  * We need to
609                  *    setcwd(inp->i_parent);
610                  *    unlink(last component of oldname pathname);
611                  */
612                 cmd.value = inp->i_parent;
613                 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
614                     &cmd, sizeof cmd) == -1) {
615                         printf(" (IGNORED)\n");
616                         return (0);
617                 }
618                 if ((cp = strchr(oldname, '/')) == NULL) {
619                         printf(" (IGNORED)\n");
620                         return (0);
621                 }
622                 cmd.value = (intptr_t)(cp + 1);
623                 cmd.size = inp->i_number; /* verify same name */
624                 if (sysctlbyname("vfs.ffs.unlink", 0, 0,
625                     &cmd, sizeof cmd) == -1) {
626                         printf(" (UNLINK FAILED: %s)\n",
627                             strerror(errno));
628                         return (0);
629                 }
630                 printf(" (REMOVED)\n");
631                 inp->i_parent = idesc->id_number;  /* reparent to correct dir */
632                 return (0);
633         }
634         if (!preen && !reply("REMOVE"))
635                 return (0);
636         memset(&dotdesc, 0, sizeof(struct inodesc));
637         dotdesc.id_type = DATA;
638         dotdesc.id_number = inp->i_parent; /* directory in which name appears */
639         dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
640         dotdesc.id_func = deleteentry;
641         if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
642                 printf(" (REMOVED)\n");
643         inp->i_parent = idesc->id_number;  /* reparent to correct directory */
644         inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
645         return (0);
646 }
647
648 static int
649 deleteentry(struct inodesc *idesc)
650 {
651         struct direct *dirp = idesc->id_dirp;
652
653         if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
654                 return (KEEPON);
655         dirp->d_ino = 0;
656         return (ALTERED|STOP|FOUND);
657 }
658
659 /*
660  * Routine to sort disk blocks.
661  */
662 static int
663 blksort(const void *arg1, const void *arg2)
664 {
665
666         return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
667                 (*(struct inoinfo * const *)arg2)->i_blks[0]);
668 }