]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/fsck_ffs/pass2.c
Document SA-19:09, SA-19:11.
[FreeBSD/FreeBSD.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(dp);
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(dp);
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(dp);
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; i < MIN(inp->i_numblks, NDADDR); i++)
186                         DIP_SET(dp, di_db[i], inp->i_blks[i]);
187                 if (inp->i_numblks > NDADDR)
188                         for (i = 0; i < NIADDR; i++)
189                                 DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
190                 curino.id_number = inp->i_number;
191                 curino.id_parent = inp->i_parent;
192                 (void)ckinode(dp, &curino);
193         }
194         /*
195          * Now that the parents of all directories have been found,
196          * make another pass to verify the value of `..'
197          */
198         for (inpp = inpsort; inpp < inpend; inpp++) {
199                 inp = *inpp;
200                 if (inp->i_parent == 0 || inp->i_isize == 0)
201                         continue;
202                 if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
203                     INO_IS_DUNFOUND(inp->i_number))
204                         inoinfo(inp->i_number)->ino_state = DFOUND;
205                 if (inp->i_dotdot == inp->i_parent ||
206                     inp->i_dotdot == (ino_t)-1)
207                         continue;
208                 if (inp->i_dotdot == 0) {
209                         inp->i_dotdot = inp->i_parent;
210                         fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
211                         if (reply("FIX") == 0)
212                                 continue;
213                         (void)makeentry(inp->i_number, inp->i_parent, "..");
214                         inoinfo(inp->i_parent)->ino_linkcnt--;
215                         continue;
216                 }
217                 /*
218                  * Here we have:
219                  *    inp->i_number is directory with bad ".." in it.
220                  *    inp->i_dotdot is current value of "..".
221                  *    inp->i_parent is directory to which ".." should point.
222                  */
223                 getpathname(pathbuf, inp->i_parent, inp->i_number);
224                 printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
225                     (uintmax_t)inp->i_number, pathbuf);
226                 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
227                 printf("CURRENTLY POINTS TO I=%ju (%s), ",
228                     (uintmax_t)inp->i_dotdot, pathbuf);
229                 getpathname(pathbuf, inp->i_parent, inp->i_parent);
230                 printf("SHOULD POINT TO I=%ju (%s)",
231                     (uintmax_t)inp->i_parent, pathbuf);
232                 if (cursnapshot != 0) {
233                         /*
234                          * We need to:
235                          *    setcwd(inp->i_number);
236                          *    setdotdot(inp->i_dotdot, inp->i_parent);
237                          */
238                         cmd.value = inp->i_number;
239                         if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
240                             &cmd, sizeof cmd) == -1) {
241                                 /* kernel lacks support for these functions */
242                                 printf(" (IGNORED)\n");
243                                 continue;
244                         }
245                         cmd.value = inp->i_dotdot; /* verify same value */
246                         cmd.size = inp->i_parent;  /* new parent */
247                         if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
248                             &cmd, sizeof cmd) == -1) {
249                                 printf(" (FIX FAILED: %s)\n", strerror(errno));
250                                 continue;
251                         }
252                         printf(" (FIXED)\n");
253                         inoinfo(inp->i_parent)->ino_linkcnt--;
254                         inp->i_dotdot = inp->i_parent;
255                         continue;
256                 }
257                 if (preen)
258                         printf(" (FIXED)\n");
259                 else if (reply("FIX") == 0)
260                         continue;
261                 inoinfo(inp->i_dotdot)->ino_linkcnt++;
262                 inoinfo(inp->i_parent)->ino_linkcnt--;
263                 inp->i_dotdot = inp->i_parent;
264                 (void)changeino(inp->i_number, "..", inp->i_parent);
265         }
266         /*
267          * Mark all the directories that can be found from the root.
268          */
269         propagate();
270 }
271
272 static int
273 pass2check(struct inodesc *idesc)
274 {
275         struct direct *dirp = idesc->id_dirp;
276         char dirname[MAXPATHLEN + 1];
277         struct inoinfo *inp;
278         int n, entrysize, ret = 0;
279         union dinode *dp;
280         const char *errmsg;
281         struct direct proto;
282
283         /*
284          * check for "."
285          */
286         if (dirp->d_ino > maxino)
287                 goto chk2;
288         if (idesc->id_entryno != 0)
289                 goto chk1;
290         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
291                 if (dirp->d_ino != idesc->id_number) {
292                         direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
293                         dirp->d_ino = idesc->id_number;
294                         if (reply("FIX") == 1)
295                                 ret |= ALTERED;
296                 }
297                 if (dirp->d_type != DT_DIR) {
298                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
299                         dirp->d_type = DT_DIR;
300                         if (reply("FIX") == 1)
301                                 ret |= ALTERED;
302                 }
303                 goto chk1;
304         }
305         direrror(idesc->id_number, "MISSING '.'");
306         proto.d_ino = idesc->id_number;
307         proto.d_type = DT_DIR;
308         proto.d_namlen = 1;
309         (void)strcpy(proto.d_name, ".");
310         entrysize = DIRSIZ(0, &proto);
311         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
312                 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
313                         dirp->d_name);
314         } else if (dirp->d_reclen < entrysize) {
315                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
316         } else if (dirp->d_reclen < 2 * entrysize) {
317                 proto.d_reclen = dirp->d_reclen;
318                 memmove(dirp, &proto, (size_t)entrysize);
319                 if (reply("FIX") == 1)
320                         ret |= ALTERED;
321         } else {
322                 n = dirp->d_reclen - entrysize;
323                 proto.d_reclen = entrysize;
324                 memmove(dirp, &proto, (size_t)entrysize);
325                 idesc->id_entryno++;
326                 inoinfo(dirp->d_ino)->ino_linkcnt--;
327                 dirp = (struct direct *)((char *)(dirp) + entrysize);
328                 memset(dirp, 0, (size_t)n);
329                 dirp->d_reclen = n;
330                 if (reply("FIX") == 1)
331                         ret |= ALTERED;
332         }
333 chk1:
334         if (idesc->id_entryno > 1)
335                 goto chk2;
336         inp = getinoinfo(idesc->id_number);
337         proto.d_ino = inp->i_parent;
338         proto.d_type = DT_DIR;
339         proto.d_namlen = 2;
340         (void)strcpy(proto.d_name, "..");
341         entrysize = DIRSIZ(0, &proto);
342         if (idesc->id_entryno == 0) {
343                 n = DIRSIZ(0, dirp);
344                 if (dirp->d_reclen < n + entrysize)
345                         goto chk2;
346                 proto.d_reclen = dirp->d_reclen - n;
347                 dirp->d_reclen = n;
348                 idesc->id_entryno++;
349                 inoinfo(dirp->d_ino)->ino_linkcnt--;
350                 dirp = (struct direct *)((char *)(dirp) + n);
351                 memset(dirp, 0, (size_t)proto.d_reclen);
352                 dirp->d_reclen = proto.d_reclen;
353         }
354         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
355                 inp->i_dotdot = dirp->d_ino;
356                 if (dirp->d_type != DT_DIR) {
357                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
358                         dirp->d_type = DT_DIR;
359                         if (reply("FIX") == 1)
360                                 ret |= ALTERED;
361                 }
362                 goto chk2;
363         }
364         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
365                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
366                 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
367                         dirp->d_name);
368                 inp->i_dotdot = (ino_t)-1;
369         } else if (dirp->d_reclen < entrysize) {
370                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
371                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
372                 inp->i_dotdot = (ino_t)-1;
373         } else if (inp->i_parent != 0) {
374                 /*
375                  * We know the parent, so fix now.
376                  */
377                 inp->i_dotdot = inp->i_parent;
378                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
379                 proto.d_reclen = dirp->d_reclen;
380                 memmove(dirp, &proto, (size_t)entrysize);
381                 if (reply("FIX") == 1)
382                         ret |= ALTERED;
383         }
384         idesc->id_entryno++;
385         if (dirp->d_ino != 0)
386                 inoinfo(dirp->d_ino)->ino_linkcnt--;
387         return (ret|KEEPON);
388 chk2:
389         if (dirp->d_ino == 0)
390                 return (ret|KEEPON);
391         if (dirp->d_namlen <= 2 &&
392             dirp->d_name[0] == '.' &&
393             idesc->id_entryno >= 2) {
394                 if (dirp->d_namlen == 1) {
395                         direrror(idesc->id_number, "EXTRA '.' ENTRY");
396                         dirp->d_ino = 0;
397                         if (reply("FIX") == 1)
398                                 ret |= ALTERED;
399                         return (KEEPON | ret);
400                 }
401                 if (dirp->d_name[1] == '.') {
402                         direrror(idesc->id_number, "EXTRA '..' ENTRY");
403                         dirp->d_ino = 0;
404                         if (reply("FIX") == 1)
405                                 ret |= ALTERED;
406                         return (KEEPON | ret);
407                 }
408         }
409         idesc->id_entryno++;
410         n = 0;
411         if (dirp->d_ino > maxino) {
412                 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
413                 n = reply("REMOVE");
414         } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
415                     (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
416                 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
417                 dirp->d_ino = WINO;
418                 dirp->d_type = DT_WHT;
419                 if (reply("FIX") == 1)
420                         ret |= ALTERED;
421         } else {
422 again:
423                 switch (inoinfo(dirp->d_ino)->ino_state) {
424                 case USTATE:
425                         if (idesc->id_entryno <= 2)
426                                 break;
427                         fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
428                         n = reply("REMOVE");
429                         break;
430
431                 case DCLEAR:
432                 case FCLEAR:
433                         if (idesc->id_entryno <= 2)
434                                 break;
435                         if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
436                                 errmsg = "DUP/BAD";
437                         else if (!preen && !usedsoftdep)
438                                 errmsg = "ZERO LENGTH DIRECTORY";
439                         else if (cursnapshot == 0) {
440                                 n = 1;
441                                 break;
442                         } else {
443                                 getpathname(dirname, idesc->id_number,
444                                     dirp->d_ino);
445                                 pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
446                                     dirname, (uintmax_t)dirp->d_ino);
447                                 /*
448                                  * We need to:
449                                  *    setcwd(idesc->id_parent);
450                                  *    rmdir(dirp->d_name);
451                                  */
452                                 cmd.value = idesc->id_number;
453                                 if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
454                                     &cmd, sizeof cmd) == -1) {
455                                         /* kernel lacks support */
456                                         printf(" (IGNORED)\n");
457                                         n = 1;
458                                         break;
459                                 }
460                                 if (rmdir(dirp->d_name) == -1) {
461                                         printf(" (REMOVAL FAILED: %s)\n",
462                                             strerror(errno));
463                                         n = 1;
464                                         break;
465                                 }
466                                 /* ".." reference to parent is removed */
467                                 inoinfo(idesc->id_number)->ino_linkcnt--;
468                                 printf(" (REMOVED)\n");
469                                 break;
470                         }
471                         fileerror(idesc->id_number, dirp->d_ino, errmsg);
472                         if ((n = reply("REMOVE")) == 1)
473                                 break;
474                         dp = ginode(dirp->d_ino);
475                         inoinfo(dirp->d_ino)->ino_state =
476                            (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
477                         inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
478                         goto again;
479
480                 case DSTATE:
481                 case DZLINK:
482                         if (inoinfo(idesc->id_number)->ino_state == DFOUND)
483                                 inoinfo(dirp->d_ino)->ino_state = DFOUND;
484                         /* FALLTHROUGH */
485
486                 case DFOUND:
487                         inp = getinoinfo(dirp->d_ino);
488                         if (idesc->id_entryno > 2) {
489                                 if (inp->i_parent == 0)
490                                         inp->i_parent = idesc->id_number;
491                                 else if ((n = fix_extraneous(inp, idesc)) == 1)
492                                         break;
493                         }
494                         /* FALLTHROUGH */
495
496                 case FSTATE:
497                 case FZLINK:
498                         if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
499                                 fileerror(idesc->id_number, dirp->d_ino,
500                                     "BAD TYPE VALUE");
501                                 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
502                                 if (reply("FIX") == 1)
503                                         ret |= ALTERED;
504                         }
505                         inoinfo(dirp->d_ino)->ino_linkcnt--;
506                         break;
507
508                 default:
509                         errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
510                             inoinfo(dirp->d_ino)->ino_state,
511                             (uintmax_t)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 = strchr(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 }