1 /* $NetBSD: dir.c,v 1.62 2023/07/04 20:40:53 riastradh Exp $ */
2
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 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95";
36 #else
37 __RCSID("$NetBSD: dir.c,v 1.62 2023/07/04 20:40:53 riastradh Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include <sys/param.h>
42 #include <sys/time.h>
43
44 #include <ufs/ufs/dinode.h>
45 #include <ufs/ufs/dir.h>
46 #include <ufs/ffs/fs.h>
47 #include <ufs/ffs/ffs_extern.h>
48
49 #include <err.h>
50 #include <stdio.h>
51 #include <string.h>
52
53 #include "fsck.h"
54 #include "fsutil.h"
55 #include "extern.h"
56
57 const char *lfname = "lost+found";
58 int lfmode = 01700;
59 ino_t lfdir;
60 struct dirtemplate emptydir = {
61 .dot_ino = 0,
62 .dot_reclen = UFS_DIRBLKSIZ,
63 };
64 struct dirtemplate dirhead = {
65 .dot_ino = 0,
66 .dot_reclen = 12,
67 .dot_type = DT_DIR,
68 .dot_namlen = 1,
69 .dot_name = ".",
70 .dotdot_ino = 0,
71 .dotdot_reclen = UFS_DIRBLKSIZ - 12,
72 .dotdot_type = DT_DIR,
73 .dotdot_namlen = 2,
74 .dotdot_name = "..",
75 };
76 struct odirtemplate odirhead = {
77 .dot_ino = 0,
78 .dot_reclen = 12,
79 .dot_namlen = 1,
80 .dot_name = ".",
81 .dotdot_ino = 0,
82 .dotdot_reclen = UFS_DIRBLKSIZ - 12,
83 .dotdot_namlen = 2,
84 .dotdot_name = "..",
85 };
86
87 static int chgino(struct inodesc *);
88 static int dircheck(struct inodesc *, struct direct *, struct bufarea *);
89 static int expanddir(union dinode *, char *);
90 static void freedir(ino_t, ino_t);
91 static struct direct *fsck_readdir(struct inodesc *);
92 static struct bufarea *getdirblk(daddr_t, long);
93 static int lftempname(char *, ino_t);
94 static int mkentry(struct inodesc *);
95 void reparent(ino_t, ino_t);
96
97 /*
98 * Propagate connected state through the tree.
99 */
100 void
propagate(ino_t inumber)101 propagate(ino_t inumber)
102 {
103 struct inoinfo *inp;
104
105 inp = getinoinfo(inumber);
106
107 for (;;) {
108 inoinfo(inp->i_number)->ino_state = DMARK;
109 if (inp->i_child &&
110 inoinfo(inp->i_child->i_number)->ino_state != DMARK)
111 inp = inp->i_child;
112 else if (inp->i_number == inumber)
113 break;
114 else if (inp->i_sibling)
115 inp = inp->i_sibling;
116 else
117 inp = getinoinfo(inp->i_parent);
118 }
119
120 for (;;) {
121 inoinfo(inp->i_number)->ino_state = DFOUND;
122 if (inp->i_child &&
123 inoinfo(inp->i_child->i_number)->ino_state != DFOUND)
124 inp = inp->i_child;
125 else if (inp->i_number == inumber)
126 break;
127 else if (inp->i_sibling)
128 inp = inp->i_sibling;
129 else
130 inp = getinoinfo(inp->i_parent);
131 }
132 }
133
134 void
reparent(ino_t inumber,ino_t parent)135 reparent(ino_t inumber, ino_t parent)
136 {
137 struct inoinfo *inp, *pinp;
138
139 inp = getinoinfo(inumber);
140 inp->i_parent = inp->i_dotdot = parent;
141 pinp = getinoinfo(parent);
142 inp->i_sibling = pinp->i_child;
143 pinp->i_child = inp;
144 propagate(inumber);
145 }
146
147 #if (BYTE_ORDER == LITTLE_ENDIAN)
148 # define NEEDSWAP (!needswap)
149 #else
150 # define NEEDSWAP (needswap)
151 #endif
152
153 static void
dirswap(void * dbuf)154 dirswap(void *dbuf)
155 {
156 struct direct *tdp = (struct direct *)dbuf;
157 u_char tmp;
158
159 tmp = tdp->d_namlen;
160 tdp->d_namlen = tdp->d_type;
161 tdp->d_type = tmp;
162 }
163
164 /*
165 * Scan each entry in a directory block.
166 */
167 int
dirscan(struct inodesc * idesc)168 dirscan(struct inodesc *idesc)
169 {
170 struct direct *dp;
171 struct bufarea *bp;
172 int dsize, n;
173 long blksiz;
174 #if !defined(NO_APPLE_UFS) && UFS_DIRBLKSIZ < APPLEUFS_DIRBLKSIZ
175 char dbuf[APPLEUFS_DIRBLKSIZ];
176 #else
177 char dbuf[UFS_DIRBLKSIZ];
178 #endif
179
180 if (idesc->id_type != DATA)
181 errexit("wrong type to dirscan %d", idesc->id_type);
182 if (idesc->id_entryno == 0 &&
183 (idesc->id_filesize & (dirblksiz - 1)) != 0)
184 idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz);
185 blksiz = idesc->id_numfrags * sblock->fs_fsize;
186 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
187 idesc->id_filesize -= blksiz;
188 return (SKIP);
189 }
190
191 /*
192 * If we are swapping byte order in directory entries, just swap
193 * this block and return.
194 */
195 if (do_dirswap) {
196 int off;
197 bp = getdirblk(idesc->id_blkno, blksiz);
198 for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) {
199 dp = (struct direct *)(bp->b_un.b_buf + off);
200 dp->d_ino = bswap32(dp->d_ino);
201 dp->d_reclen = bswap16(dp->d_reclen);
202 if (!newinofmt) {
203 u_int8_t tmp = dp->d_namlen;
204 dp->d_namlen = dp->d_type;
205 dp->d_type = tmp;
206 }
207 if (dp->d_reclen == 0)
208 break;
209 }
210 dirty(bp);
211 idesc->id_filesize -= blksiz;
212 return (idesc->id_filesize > 0 ? KEEPON : STOP);
213 }
214
215 idesc->id_loc = 0;
216 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
217 dsize = iswap16(dp->d_reclen);
218 if (dsize > (int)sizeof dbuf)
219 dsize = sizeof dbuf;
220 memmove(dbuf, dp, (size_t)dsize);
221 if (!newinofmt && NEEDSWAP)
222 dirswap(dbuf);
223 idesc->id_dirp = (struct direct *)dbuf;
224 if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
225 if (!newinofmt && !doinglevel2 && NEEDSWAP)
226 dirswap(dbuf);
227 bp = getdirblk(idesc->id_blkno, blksiz);
228 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
229 (size_t)dsize);
230 dirty(bp);
231 sbdirty();
232 }
233 if (n & STOP)
234 return (n);
235 }
236 return (idesc->id_filesize > 0 ? KEEPON : STOP);
237 }
238
239 /*
240 * get next entry in a directory.
241 */
242 static struct direct *
fsck_readdir(struct inodesc * idesc)243 fsck_readdir(struct inodesc *idesc)
244 {
245 struct direct *dp, *ndp;
246 struct bufarea *bp;
247 long size, blksiz, fix, dploc;
248
249 blksiz = idesc->id_numfrags * sblock->fs_fsize;
250 bp = getdirblk(idesc->id_blkno, blksiz);
251 if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
252 idesc->id_loc < blksiz) {
253 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
254 if (dircheck(idesc, dp, bp))
255 goto dpok;
256 if (idesc->id_fix == IGNORE)
257 return (0);
258 fix = dofix(idesc, "DIRECTORY CORRUPTED");
259 bp = getdirblk(idesc->id_blkno, blksiz);
260 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
261 dp->d_reclen = iswap16(dirblksiz);
262 dp->d_ino = 0;
263 dp->d_type = 0;
264 dp->d_namlen = 0;
265 dp->d_name[0] = '\0';
266 if (fix)
267 dirty(bp);
268 else
269 markclean = 0;
270 idesc->id_loc += dirblksiz;
271 idesc->id_filesize -= dirblksiz;
272 return (dp);
273 }
274 dpok:
275 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
276 return NULL;
277 dploc = idesc->id_loc;
278 dp = (struct direct *)(bp->b_un.b_buf + dploc);
279 idesc->id_loc += iswap16(dp->d_reclen);
280 idesc->id_filesize -= iswap16(dp->d_reclen);
281 if ((idesc->id_loc % dirblksiz) == 0)
282 return (dp);
283 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
284 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
285 dircheck(idesc, ndp, bp) == 0) {
286 size = dirblksiz - (idesc->id_loc % dirblksiz);
287 idesc->id_loc += size;
288 idesc->id_filesize -= size;
289 if (idesc->id_fix == IGNORE)
290 return (0);
291 fix = dofix(idesc, "DIRECTORY CORRUPTED");
292 bp = getdirblk(idesc->id_blkno, blksiz);
293 dp = (struct direct *)(bp->b_un.b_buf + dploc);
294 dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
295 if (fix)
296 dirty(bp);
297 else
298 markclean = 0;
299 }
300 return (dp);
301 }
302
303 /*
304 * Verify that a directory entry is valid.
305 * This is a superset of the checks made in the kernel.
306 * Returns:
307 * 1: good
308 * 0: bad
309 */
310 static int
dircheck(struct inodesc * idesc,struct direct * dp,struct bufarea * bp)311 dircheck(struct inodesc *idesc, struct direct *dp, struct bufarea *bp)
312 {
313 uint8_t namlen, type;
314 uint16_t reclen;
315 uint32_t ino;
316 char *cp;
317 int size, spaceleft, modified, unused, i;
318
319 modified = 0;
320 spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
321
322 /* fill in the correct info for our fields */
323 ino = iswap32(dp->d_ino);
324 reclen = iswap16(dp->d_reclen);
325 if (!newinofmt && NEEDSWAP) {
326 type = dp->d_namlen;
327 namlen = dp->d_type;
328 } else {
329 namlen = dp->d_namlen;
330 type = dp->d_type;
331 }
332
333 if (ino >= maxino ||
334 reclen == 0 || reclen > spaceleft || (reclen & 0x3) != 0)
335 goto bad;
336
337 size = UFS_DIRSIZ(!newinofmt, dp, needswap);
338 if (ino == 0) {
339 /*
340 * Special case of an unused directory entry. Normally
341 * the kernel would coalesce unused space with the previous
342 * entry by extending its d_reclen, but there are situations
343 * (e.g. fsck) where that doesn't occur.
344 * If we're clearing out directory cruft (-z flag), then make
345 * sure this entry gets fully cleared as well.
346 */
347 if (!zflag || fswritefd < 0)
348 return 1;
349
350 if (dp->d_type != 0) {
351 dp->d_type = 0;
352 modified = 1;
353 }
354 if (dp->d_namlen != 0) {
355 dp->d_namlen = 0;
356 modified = 1;
357 }
358 if (dp->d_name[0] != '\0') {
359 dp->d_name[0] = '\0';
360 modified = 1;
361 }
362 goto good;
363 }
364
365 if (reclen < size || idesc->id_filesize < size ||
366 /* namlen > MAXNAMLEN || */
367 type > 15)
368 goto bad;
369
370 for (cp = dp->d_name, i = 0; i < namlen; i++)
371 if (*cp == '\0' || (*cp++ == '/'))
372 goto bad;
373
374 if (*cp != '\0')
375 goto bad;
376
377 if (!zflag || fswritefd < 0)
378 return 1;
379 good:
380 /*
381 * Clear unused directory entry space, including the d_name
382 * padding.
383 */
384 /* First figure the number of pad bytes. */
385 unused = UFS_NAMEPAD(namlen);
386
387 /* Add in the free space to the end of the record. */
388 unused += iswap16(dp->d_reclen) - size;
389
390 /*
391 * Now clear out the unused space, keeping track if we actually
392 * changed anything.
393 */
394 for (cp = &dp->d_name[namlen]; unused > 0; unused--, cp++) {
395 if (*cp == '\0')
396 continue;
397 *cp = '\0';
398 modified = 1;
399 }
400
401 /* mark dirty so we update the zeroed space */
402 if (modified)
403 dirty(bp);
404 return 1;
405 bad:
406 if (debug)
407 printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
408 ino, reclen, namlen, type, dp->d_name);
409 return 0;
410 }
411
412 void
direrror(ino_t ino,const char * errmesg)413 direrror(ino_t ino, const char *errmesg)
414 {
415
416 fileerror(ino, ino, errmesg);
417 }
418
419 void
fileerror(ino_t cwd,ino_t ino,const char * errmesg)420 fileerror(ino_t cwd, ino_t ino, const char *errmesg)
421 {
422 union dinode *dp;
423 char pathbuf[MAXPATHLEN + 1];
424 uint16_t mode;
425
426 pwarn("%s ", errmesg);
427 pinode(ino);
428 printf("\n");
429 getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
430 if (ino < UFS_ROOTINO || ino > maxino) {
431 pfatal("NAME=%s\n", pathbuf);
432 return;
433 }
434 dp = ginode(ino);
435 if (ftypeok(dp)) {
436 mode = DIP(dp, mode);
437 pfatal("%s=%s\n",
438 (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
439 }
440 else
441 pfatal("NAME=%s\n", pathbuf);
442 }
443
444 void
adjust(struct inodesc * idesc,int lcnt)445 adjust(struct inodesc *idesc, int lcnt)
446 {
447 union dinode *dp;
448 int16_t nlink;
449 int saveresolved;
450
451 dp = ginode(idesc->id_number);
452 nlink = iswap16(DIP(dp, nlink));
453 if (nlink == lcnt) {
454 /*
455 * If we have not hit any unresolved problems, are running
456 * in preen mode, and are on a file system using soft updates,
457 * then just toss any partially allocated files.
458 */
459 if (resolved && preen && usedsoftdep) {
460 clri(idesc, "UNREF", 1);
461 return;
462 } else {
463 /*
464 * The file system can be marked clean even if
465 * a file is not linked up, but is cleared.
466 * Hence, resolved should not be cleared when
467 * linkup is answered no, but clri is answered yes.
468 */
469 saveresolved = resolved;
470 if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
471 resolved = saveresolved;
472 clri(idesc, "UNREF", 0);
473 return;
474 }
475 /*
476 * Account for the new reference created by linkup().
477 */
478 dp = ginode(idesc->id_number);
479 lcnt--;
480 }
481 }
482 if (lcnt != 0) {
483 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
484 ((iswap16(DIP(dp, mode)) & IFMT) == IFDIR ?
485 "DIR" : "FILE"));
486 pinode(idesc->id_number);
487 printf(" COUNT %d SHOULD BE %d",
488 nlink, nlink - lcnt);
489 if (preen || usedsoftdep) {
490 if (lcnt < 0) {
491 printf("\n");
492 pfatal("LINK COUNT INCREASING");
493 }
494 if (preen)
495 printf(" (ADJUSTED)\n");
496 }
497 if (preen || reply("ADJUST") == 1) {
498 DIP_SET(dp, nlink, iswap16(nlink - lcnt));
499 inodirty();
500 } else
501 markclean = 0;
502 }
503 }
504
505 static int
mkentry(struct inodesc * idesc)506 mkentry(struct inodesc *idesc)
507 {
508 struct direct *dirp = idesc->id_dirp;
509 struct direct newent;
510 int newlen, oldlen;
511
512 newent.d_namlen = strlen(idesc->id_name);
513 newlen = UFS_DIRSIZ(0, &newent, 0);
514 if (dirp->d_ino != 0)
515 oldlen = UFS_DIRSIZ(0, dirp, 0);
516 else
517 oldlen = 0;
518 if (iswap16(dirp->d_reclen) - oldlen < newlen)
519 return (KEEPON);
520 newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen);
521 dirp->d_reclen = iswap16(oldlen);
522 dirp = (struct direct *)(((char *)dirp) + oldlen);
523 /* ino to be entered is in id_parent */
524 dirp->d_ino = iswap32(idesc->id_parent);
525 dirp->d_reclen = newent.d_reclen;
526 if (newinofmt)
527 dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
528 else
529 dirp->d_type = 0;
530 dirp->d_namlen = newent.d_namlen;
531 memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
532 /*
533 * If the entry was split, dirscan() will only reverse the byte
534 * order of the original entry, and not the new one, before
535 * writing it back out. So, we reverse the byte order here if
536 * necessary.
537 */
538 if (oldlen != 0 && !newinofmt && !doinglevel2 && NEEDSWAP)
539 dirswap(dirp);
540 return (ALTERED|STOP);
541 }
542
543 static int
chgino(struct inodesc * idesc)544 chgino(struct inodesc *idesc)
545 {
546 struct direct *dirp = idesc->id_dirp;
547
548 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
549 return (KEEPON);
550 dirp->d_ino = iswap32(idesc->id_parent);
551 if (newinofmt)
552 dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
553 else
554 dirp->d_type = 0;
555 return (ALTERED|STOP);
556 }
557
558 int
linkup(ino_t orphan,ino_t parentdir,char * name)559 linkup(ino_t orphan, ino_t parentdir, char *name)
560 {
561 union dinode *dp;
562 int lostdir;
563 ino_t oldlfdir;
564 struct inodesc idesc;
565 char tempname[BUFSIZ];
566 int16_t nlink;
567 uint16_t mode;
568
569 memset(&idesc, 0, sizeof(struct inodesc));
570 dp = ginode(orphan);
571 mode = iswap16(DIP(dp, mode));
572 nlink = iswap16(DIP(dp, nlink));
573 lostdir = (mode & IFMT) == IFDIR;
574 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
575 pinode(orphan);
576 if (preen && DIP(dp, size) == 0)
577 return (0);
578 if (preen)
579 printf(" (RECONNECTED)\n");
580 else
581 if (reply("RECONNECT") == 0) {
582 markclean = 0;
583 return (0);
584 }
585 if (parentdir != 0)
586 inoinfo(parentdir)->ino_linkcnt++;
587 if (lfdir == 0) {
588 dp = ginode(UFS_ROOTINO);
589 idesc.id_name = lfname;
590 idesc.id_type = DATA;
591 idesc.id_func = findino;
592 idesc.id_number = UFS_ROOTINO;
593 idesc.id_uid = iswap32(DIP(dp, uid));
594 idesc.id_gid = iswap32(DIP(dp, gid));
595 if ((ckinode(dp, &idesc) & FOUND) != 0) {
596 lfdir = idesc.id_parent;
597 } else {
598 pwarn("NO lost+found DIRECTORY");
599 if (preen || reply("CREATE")) {
600 lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode);
601 if (lfdir != 0) {
602 if (makeentry(UFS_ROOTINO, lfdir, lfname) != 0) {
603 numdirs++;
604 if (preen)
605 printf(" (CREATED)\n");
606 } else {
607 freedir(lfdir, UFS_ROOTINO);
608 lfdir = 0;
609 if (preen)
610 printf("\n");
611 }
612 }
613 if (lfdir != 0) {
614 reparent(lfdir, UFS_ROOTINO);
615 }
616 }
617 }
618 if (lfdir == 0) {
619 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
620 markclean = 0;
621 return (0);
622 }
623 }
624 dp = ginode(lfdir);
625 mode = DIP(dp, mode);
626 mode = iswap16(mode);
627 if ((mode & IFMT) != IFDIR) {
628 pfatal("lost+found IS NOT A DIRECTORY");
629 if (reply("REALLOCATE") == 0) {
630 markclean = 0;
631 return (0);
632 }
633 oldlfdir = lfdir;
634 lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode);
635 if (lfdir == 0) {
636 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
637 markclean = 0;
638 return (0);
639 }
640 if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
641 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
642 markclean = 0;
643 return (0);
644 }
645 inodirty();
646 reparent(lfdir, UFS_ROOTINO);
647 idesc.id_type = ADDR;
648 idesc.id_func = pass4check;
649 idesc.id_number = oldlfdir;
650 adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
651 inoinfo(oldlfdir)->ino_linkcnt = 0;
652 dp = ginode(lfdir);
653 }
654 if (inoinfo(lfdir)->ino_state != DFOUND) {
655 pfatal("SORRY. NO lost+found DIRECTORY\n\n");
656 markclean = 0;
657 return (0);
658 }
659 (void)lftempname(tempname, orphan);
660 if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
661 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
662 printf("\n\n");
663 markclean = 0;
664 return (0);
665 }
666 inoinfo(orphan)->ino_linkcnt--;
667 if (lostdir) {
668 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
669 parentdir != (ino_t)-1)
670 (void)makeentry(orphan, lfdir, "..");
671 dp = ginode(lfdir);
672 nlink = DIP(dp, nlink);
673 DIP_SET(dp, nlink, iswap16(iswap16(nlink) + 1));
674 inodirty();
675 inoinfo(lfdir)->ino_linkcnt++;
676 reparent(orphan, lfdir);
677 pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
678 if (parentdir != (ino_t)-1)
679 printf("PARENT WAS I=%llu\n",
680 (unsigned long long)parentdir);
681 if (preen == 0)
682 printf("\n");
683 }
684 return (1);
685 }
686
687 /*
688 * fix an entry in a directory.
689 */
690 int
changeino(ino_t dir,const char * name,ino_t newnum)691 changeino(ino_t dir, const char *name, ino_t newnum)
692 {
693 struct inodesc idesc;
694 union dinode *dp;
695
696 dp = ginode(dir);
697 memset(&idesc, 0, sizeof(struct inodesc));
698 idesc.id_type = DATA;
699 idesc.id_func = chgino;
700 idesc.id_number = dir;
701 idesc.id_fix = DONTKNOW;
702 idesc.id_name = name;
703 idesc.id_parent = newnum; /* new value for name */
704 idesc.id_uid = iswap32(DIP(dp, uid));
705 idesc.id_gid = iswap32(DIP(dp, gid));
706 return (ckinode(dp, &idesc));
707 }
708
709 /*
710 * make an entry in a directory
711 */
712 int
makeentry(ino_t parent,ino_t ino,const char * name)713 makeentry(ino_t parent, ino_t ino, const char *name)
714 {
715 union dinode *dp;
716 struct inodesc idesc;
717 char pathbuf[MAXPATHLEN + 1];
718
719 if (parent < UFS_ROOTINO || parent >= maxino ||
720 ino < UFS_ROOTINO || ino >= maxino)
721 return (0);
722 dp = ginode(parent);
723 memset(&idesc, 0, sizeof(struct inodesc));
724 idesc.id_type = DATA;
725 idesc.id_func = mkentry;
726 idesc.id_number = parent;
727 idesc.id_parent = ino; /* this is the inode to enter */
728 idesc.id_fix = DONTKNOW;
729 idesc.id_name = name;
730 idesc.id_uid = iswap32(DIP(dp, uid));
731 idesc.id_gid = iswap32(DIP(dp, gid));
732 if (iswap64(DIP(dp, size)) % dirblksiz) {
733 DIP_SET(dp, size,
734 iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz)));
735 inodirty();
736 }
737 if ((ckinode(dp, &idesc) & ALTERED) != 0)
738 return (1);
739 getpathname(pathbuf, sizeof(pathbuf), parent, parent);
740 dp = ginode(parent);
741 if (expanddir(dp, pathbuf) == 0)
742 return (0);
743 update_uquot(idesc.id_number, idesc.id_uid, idesc.id_gid,
744 btodb(sblock->fs_bsize), 0);
745 return (ckinode(dp, &idesc) & ALTERED);
746 }
747
748 /*
749 * Attempt to expand the size of a directory
750 */
751 static int
expanddir(union dinode * dp,char * name)752 expanddir(union dinode *dp, char *name)
753 {
754 daddr_t lastbn, newblk, dirblk;
755 struct bufarea *bp;
756 char *cp;
757 #if !defined(NO_APPLE_UFS) && UFS_DIRBLKSIZ < APPLEUFS_DIRBLKSIZ
758 char firstblk[APPLEUFS_DIRBLKSIZ];
759 #else
760 char firstblk[UFS_DIRBLKSIZ];
761 #endif
762 struct ufs1_dinode *dp1 = NULL;
763 struct ufs2_dinode *dp2 = NULL;
764
765 if (is_ufs2)
766 dp2 = &dp->dp2;
767 else
768 dp1 = &dp->dp1;
769
770 lastbn = ffs_lblkno(sblock, iswap64(DIP(dp, size)));
771 if (lastbn >= UFS_NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
772 DIP(dp, size) == 0)
773 return (0);
774 if ((newblk = allocblk(sblock->fs_frag)) == 0)
775 return (0);
776 if (is_ufs2) {
777 dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
778 dp2->di_db[lastbn] = iswap64(newblk);
779 dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
780 dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
781 btodb(sblock->fs_bsize));
782 dirblk = iswap64(dp2->di_db[lastbn + 1]);
783 } else {
784 dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
785 dp1->di_db[lastbn] = iswap32((int32_t)newblk);
786 dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
787 dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
788 btodb(sblock->fs_bsize));
789 dirblk = iswap32(dp1->di_db[lastbn + 1]);
790 }
791 bp = getdirblk(dirblk, ffs_sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
792 if (bp->b_errs)
793 goto bad;
794 memmove(firstblk, bp->b_un.b_buf, dirblksiz);
795 bp = getdirblk(newblk, sblock->fs_bsize);
796 if (bp->b_errs)
797 goto bad;
798 memmove(bp->b_un.b_buf, firstblk, dirblksiz);
799 emptydir.dot_reclen = iswap16(dirblksiz);
800 for (cp = &bp->b_un.b_buf[dirblksiz];
801 cp < &bp->b_un.b_buf[sblock->fs_bsize];
802 cp += dirblksiz)
803 memmove(cp, &emptydir, sizeof emptydir);
804 dirty(bp);
805 bp = getdirblk(dirblk, ffs_sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
806 if (bp->b_errs)
807 goto bad;
808 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
809 pwarn("NO SPACE LEFT IN %s", name);
810 if (preen)
811 printf(" (EXPANDED)\n");
812 else if (reply("EXPAND") == 0)
813 goto bad;
814 dirty(bp);
815 inodirty();
816 return (1);
817 bad:
818 if (is_ufs2) {
819 dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
820 dp2->di_db[lastbn + 1] = 0;
821 dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
822 dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
823 btodb(sblock->fs_bsize));
824 } else {
825 dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
826 dp1->di_db[lastbn + 1] = 0;
827 dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
828 dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
829 btodb(sblock->fs_bsize));
830 }
831 freeblk(newblk, sblock->fs_frag);
832 markclean = 0;
833 return (0);
834 }
835
836 /*
837 * allocate a new directory
838 */
839 ino_t
allocdir(ino_t parent,ino_t request,int mode)840 allocdir(ino_t parent, ino_t request, int mode)
841 {
842 ino_t ino;
843 char *cp;
844 union dinode *dp;
845 struct bufarea *bp;
846 struct inoinfo *inp;
847 struct dirtemplate *dirp;
848 daddr_t dirblk;
849
850 ino = allocino(request, IFDIR|mode);
851 if (ino < UFS_ROOTINO)
852 return 0;
853 update_uquot(ino, 0, 0, btodb(sblock->fs_fsize), 1);
854 dirhead.dot_reclen = iswap16(12);
855 dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
856 odirhead.dot_reclen = iswap16(12);
857 odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
858 odirhead.dot_namlen = iswap16(1);
859 odirhead.dotdot_namlen = iswap16(2);
860 if (newinofmt)
861 dirp = &dirhead;
862 else
863 dirp = (struct dirtemplate *)&odirhead;
864 dirp->dot_ino = iswap32(ino);
865 dirp->dotdot_ino = iswap32(parent);
866 dp = ginode(ino);
867 dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
868 : iswap32(dp->dp1.di_db[0]);
869 bp = getdirblk(dirblk, sblock->fs_fsize);
870 if (bp->b_errs) {
871 freeino(ino);
872 return (0);
873 }
874 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
875 emptydir.dot_reclen = iswap16(dirblksiz);
876 for (cp = &bp->b_un.b_buf[dirblksiz];
877 cp < &bp->b_un.b_buf[sblock->fs_fsize];
878 cp += dirblksiz)
879 memmove(cp, &emptydir, sizeof emptydir);
880 dirty(bp);
881 DIP_SET(dp, nlink, iswap16(2));
882 inodirty();
883 if (ino == UFS_ROOTINO) {
884 inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
885 cacheino(dp, ino);
886 return(ino);
887 }
888 if (inoinfo(parent)->ino_state != DSTATE &&
889 inoinfo(parent)->ino_state != DFOUND) {
890 freeino(ino);
891 return (0);
892 }
893 cacheino(dp, ino);
894 inp = getinoinfo(ino);
895 inp->i_parent = parent;
896 inp->i_dotdot = parent;
897 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
898 if (inoinfo(ino)->ino_state == DSTATE) {
899 inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
900 inoinfo(parent)->ino_linkcnt++;
901 }
902 dp = ginode(parent);
903 DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1));
904 inodirty();
905 return (ino);
906 }
907
908 /*
909 * free a directory inode
910 */
911 static void
freedir(ino_t ino,ino_t parent)912 freedir(ino_t ino, ino_t parent)
913 {
914 union dinode *dp;
915
916 if (ino != parent) {
917 dp = ginode(parent);
918 DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) - 1));
919 inodirty();
920 }
921 freeino(ino);
922 }
923
924 /*
925 * generate a temporary name for the lost+found directory.
926 */
927 static int
lftempname(char * bufp,ino_t ino)928 lftempname(char *bufp, ino_t ino)
929 {
930 ino_t in;
931 char *cp;
932 int namlen;
933
934 cp = bufp + 2;
935 for (in = maxino; in > 0; in /= 10)
936 cp++;
937 *--cp = 0;
938 namlen = cp - bufp;
939 in = ino;
940 while (cp > bufp) {
941 *--cp = (in % 10) + '0';
942 in /= 10;
943 }
944 *cp = '#';
945 return (namlen);
946 }
947
948 /*
949 * Get a directory block.
950 * Insure that it is held until another is requested.
951 */
952 static struct bufarea *
getdirblk(daddr_t blkno,long size)953 getdirblk(daddr_t blkno, long size)
954 {
955
956 if (pdirbp != 0)
957 pdirbp->b_flags &= ~B_INUSE;
958 pdirbp = getdatablk(blkno, size);
959 return (pdirbp);
960 }
961