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