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