Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / sbin / fsck_ffs / dir.c
blobd3b3ff373006df7e386101165291d56191c1fbd7
1 /* $NetBSD: dir.c,v 1.51 2008/07/08 08:14:37 simonb Exp $ */
3 /*
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
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.51 2008/07/08 08:14:37 simonb Exp $");
38 #endif
39 #endif /* not lint */
41 #include <sys/param.h>
42 #include <sys/time.h>
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>
49 #include <err.h>
50 #include <stdio.h>
51 #include <string.h>
53 #include "fsck.h"
54 #include "fsutil.h"
55 #include "extern.h"
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 = DIRBLKSIZ,
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 = DIRBLKSIZ - 12,
72 .dotdot_type = DT_DIR,
73 .dotdot_namlen = 2,
74 .dotdot_name = "..",
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 = DIRBLKSIZ - 12,
83 .dotdot_namlen = 2,
84 .dotdot_name = "..",
87 static int chgino(struct inodesc *);
88 static int dircheck(struct inodesc *, struct direct *);
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);
98 * Propagate connected state through the tree.
100 void
101 propagate(ino_t inumber)
103 struct inoinfo *inp;
105 inp = getinoinfo(inumber);
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);
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);
134 void
135 reparent(ino_t inumber, ino_t parent)
137 struct inoinfo *inp, *pinp;
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);
148 * Scan each entry in a directory block.
151 dirscan(struct inodesc *idesc)
153 struct direct *dp;
154 struct bufarea *bp;
155 int dsize, n;
156 long blksiz;
157 #if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
158 char dbuf[DIRBLKSIZ];
159 #else
160 char dbuf[APPLEUFS_DIRBLKSIZ];
161 #endif
163 if (idesc->id_type != DATA)
164 errexit("wrong type to dirscan %d", idesc->id_type);
165 if (idesc->id_entryno == 0 &&
166 (idesc->id_filesize & (dirblksiz - 1)) != 0)
167 idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz);
168 blksiz = idesc->id_numfrags * sblock->fs_fsize;
169 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
170 idesc->id_filesize -= blksiz;
171 return (SKIP);
175 * If we are are swapping byte order in directory entries, just swap
176 * this block and return.
178 if (do_dirswap) {
179 int off;
180 bp = getdirblk(idesc->id_blkno, blksiz);
181 for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) {
182 dp = (struct direct *)(bp->b_un.b_buf + off);
183 dp->d_ino = bswap32(dp->d_ino);
184 dp->d_reclen = bswap16(dp->d_reclen);
185 if (!newinofmt) {
186 u_int8_t tmp = dp->d_namlen;
187 dp->d_namlen = dp->d_type;
188 dp->d_type = tmp;
190 if (dp->d_reclen == 0)
191 break;
193 dirty(bp);
194 idesc->id_filesize -= blksiz;
195 return (idesc->id_filesize > 0 ? KEEPON : STOP);
198 idesc->id_loc = 0;
199 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
200 dsize = iswap16(dp->d_reclen);
201 if (dsize > (int)sizeof dbuf)
202 dsize = sizeof dbuf;
203 memmove(dbuf, dp, (size_t)dsize);
204 # if (BYTE_ORDER == LITTLE_ENDIAN)
205 if (!newinofmt && !needswap) {
206 # else
207 if (!newinofmt && needswap) {
208 # endif
209 struct direct *tdp = (struct direct *)dbuf;
210 u_char tmp;
212 tmp = tdp->d_namlen;
213 tdp->d_namlen = tdp->d_type;
214 tdp->d_type = tmp;
216 idesc->id_dirp = (struct direct *)dbuf;
217 if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
218 # if (BYTE_ORDER == LITTLE_ENDIAN)
219 if (!newinofmt && !doinglevel2 && !needswap) {
220 # else
221 if (!newinofmt && !doinglevel2 && needswap) {
222 # endif
223 struct direct *tdp;
224 u_char tmp;
226 tdp = (struct direct *)dbuf;
227 tmp = tdp->d_namlen;
228 tdp->d_namlen = tdp->d_type;
229 tdp->d_type = tmp;
231 bp = getdirblk(idesc->id_blkno, blksiz);
232 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
233 (size_t)dsize);
234 dirty(bp);
235 sbdirty();
237 if (n & STOP)
238 return (n);
240 return (idesc->id_filesize > 0 ? KEEPON : STOP);
244 * get next entry in a directory.
246 static struct direct *
247 fsck_readdir(struct inodesc *idesc)
249 struct direct *dp, *ndp;
250 struct bufarea *bp;
251 long size, blksiz, fix, dploc;
253 blksiz = idesc->id_numfrags * sblock->fs_fsize;
254 bp = getdirblk(idesc->id_blkno, blksiz);
255 if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
256 idesc->id_loc < blksiz) {
257 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
258 if (dircheck(idesc, dp))
259 goto dpok;
260 if (idesc->id_fix == IGNORE)
261 return (0);
262 fix = dofix(idesc, "DIRECTORY CORRUPTED");
263 bp = getdirblk(idesc->id_blkno, blksiz);
264 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
265 dp->d_reclen = iswap16(dirblksiz);
266 dp->d_ino = 0;
267 dp->d_type = 0;
268 dp->d_namlen = 0;
269 dp->d_name[0] = '\0';
270 if (fix)
271 dirty(bp);
272 else
273 markclean = 0;
274 idesc->id_loc += dirblksiz;
275 idesc->id_filesize -= dirblksiz;
276 return (dp);
278 dpok:
279 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
280 return NULL;
281 dploc = idesc->id_loc;
282 dp = (struct direct *)(bp->b_un.b_buf + dploc);
283 idesc->id_loc += iswap16(dp->d_reclen);
284 idesc->id_filesize -= iswap16(dp->d_reclen);
285 if ((idesc->id_loc % dirblksiz) == 0)
286 return (dp);
287 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
288 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
289 dircheck(idesc, ndp) == 0) {
290 size = dirblksiz - (idesc->id_loc % dirblksiz);
291 idesc->id_loc += size;
292 idesc->id_filesize -= size;
293 if (idesc->id_fix == IGNORE)
294 return (0);
295 fix = dofix(idesc, "DIRECTORY CORRUPTED");
296 bp = getdirblk(idesc->id_blkno, blksiz);
297 dp = (struct direct *)(bp->b_un.b_buf + dploc);
298 dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
299 if (fix)
300 dirty(bp);
301 else
302 markclean = 0;
304 return (dp);
308 * Verify that a directory entry is valid.
309 * This is a superset of the checks made in the kernel.
311 static int
312 dircheck(struct inodesc *idesc, struct direct *dp)
314 int size;
315 char *cp;
316 u_char namlen, type;
317 int spaceleft;
319 spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
320 if (iswap32(dp->d_ino) >= maxino ||
321 dp->d_reclen == 0 ||
322 iswap16(dp->d_reclen) > spaceleft ||
323 (iswap16(dp->d_reclen) & 0x3) != 0)
324 return (0);
325 if (dp->d_ino == 0)
326 return (1);
327 size = DIRSIZ(!newinofmt, dp, needswap);
328 # if (BYTE_ORDER == LITTLE_ENDIAN)
329 if (!newinofmt && !needswap) {
330 # else
331 if (!newinofmt && needswap) {
332 # endif
333 type = dp->d_namlen;
334 namlen = dp->d_type;
335 } else {
336 namlen = dp->d_namlen;
337 type = dp->d_type;
339 if (iswap16(dp->d_reclen) < size ||
340 idesc->id_filesize < size ||
341 /* namlen > MAXNAMLEN || */
342 type > 15)
343 return (0);
344 for (cp = dp->d_name, size = 0; size < namlen; size++)
345 if (*cp == '\0' || (*cp++ == '/'))
346 return (0);
347 if (*cp != '\0')
348 return (0);
349 return (1);
352 void
353 direrror(ino_t ino, const char *errmesg)
356 fileerror(ino, ino, errmesg);
359 void
360 fileerror(ino_t cwd, ino_t ino, const char *errmesg)
362 union dinode *dp;
363 char pathbuf[MAXPATHLEN + 1];
364 uint16_t mode;
366 pwarn("%s ", errmesg);
367 pinode(ino);
368 printf("\n");
369 getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
370 if (ino < ROOTINO || ino > maxino) {
371 pfatal("NAME=%s\n", pathbuf);
372 return;
374 dp = ginode(ino);
375 if (ftypeok(dp)) {
376 mode = DIP(dp, mode);
377 pfatal("%s=%s\n",
378 (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
380 else
381 pfatal("NAME=%s\n", pathbuf);
384 void
385 adjust(struct inodesc *idesc, int lcnt)
387 union dinode *dp;
388 int16_t nlink;
389 int saveresolved;
391 dp = ginode(idesc->id_number);
392 nlink = iswap16(DIP(dp, nlink));
393 if (nlink == lcnt) {
395 * If we have not hit any unresolved problems, are running
396 * in preen mode, and are on a file system using soft updates,
397 * then just toss any partially allocated files.
399 if (resolved && preen && usedsoftdep) {
400 clri(idesc, "UNREF", 1);
401 return;
402 } else {
404 * The file system can be marked clean even if
405 * a file is not linked up, but is cleared.
406 * Hence, resolved should not be cleared when
407 * linkup is answered no, but clri is answered yes.
409 saveresolved = resolved;
410 if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
411 resolved = saveresolved;
412 clri(idesc, "UNREF", 0);
413 return;
416 * Account for the new reference created by linkup().
418 dp = ginode(idesc->id_number);
419 lcnt--;
422 if (lcnt != 0) {
423 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
424 ((iswap16(DIP(dp, mode)) & IFMT) == IFDIR ?
425 "DIR" : "FILE"));
426 pinode(idesc->id_number);
427 printf(" COUNT %d SHOULD BE %d",
428 nlink, nlink - lcnt);
429 if (preen || usedsoftdep) {
430 if (lcnt < 0) {
431 printf("\n");
432 pfatal("LINK COUNT INCREASING");
434 if (preen)
435 printf(" (ADJUSTED)\n");
437 if (preen || reply("ADJUST") == 1) {
438 DIP_SET(dp, nlink, iswap16(nlink - lcnt));
439 inodirty();
440 } else
441 markclean = 0;
445 static int
446 mkentry(struct inodesc *idesc)
448 struct direct *dirp = idesc->id_dirp;
449 struct direct newent;
450 int newlen, oldlen;
452 newent.d_namlen = strlen(idesc->id_name);
453 newlen = DIRSIZ(0, &newent, 0);
454 if (dirp->d_ino != 0)
455 oldlen = DIRSIZ(0, dirp, 0);
456 else
457 oldlen = 0;
458 if (iswap16(dirp->d_reclen) - oldlen < newlen)
459 return (KEEPON);
460 newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen);
461 dirp->d_reclen = iswap16(oldlen);
462 dirp = (struct direct *)(((char *)dirp) + oldlen);
463 /* ino to be entered is in id_parent */
464 dirp->d_ino = iswap32(idesc->id_parent);
465 dirp->d_reclen = newent.d_reclen;
466 if (newinofmt)
467 dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
468 else
469 dirp->d_type = 0;
470 dirp->d_namlen = newent.d_namlen;
471 memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
472 # if (BYTE_ORDER == LITTLE_ENDIAN)
474 * If the entry was split, dirscan() will only reverse the byte
475 * order of the original entry, and not the new one, before
476 * writing it back out. So, we reverse the byte order here if
477 * necessary.
479 if (oldlen != 0 && !newinofmt && !doinglevel2 && !needswap) {
480 # else
481 if (oldlen != 0 && !newinofmt && !doinglevel2 && needswap) {
482 # endif
483 u_char tmp;
485 tmp = dirp->d_namlen;
486 dirp->d_namlen = dirp->d_type;
487 dirp->d_type = tmp;
489 return (ALTERED|STOP);
492 static int
493 chgino(struct inodesc *idesc)
495 struct direct *dirp = idesc->id_dirp;
497 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
498 return (KEEPON);
499 dirp->d_ino = iswap32(idesc->id_parent);
500 if (newinofmt)
501 dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
502 else
503 dirp->d_type = 0;
504 return (ALTERED|STOP);
508 linkup(ino_t orphan, ino_t parentdir, char *name)
510 union dinode *dp;
511 int lostdir;
512 ino_t oldlfdir;
513 struct inodesc idesc;
514 char tempname[BUFSIZ];
515 int16_t nlink;
516 uint16_t mode;
518 memset(&idesc, 0, sizeof(struct inodesc));
519 dp = ginode(orphan);
520 mode = iswap16(DIP(dp, mode));
521 nlink = iswap16(DIP(dp, nlink));
522 lostdir = (mode & IFMT) == IFDIR;
523 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
524 pinode(orphan);
525 if (preen && DIP(dp, size) == 0)
526 return (0);
527 if (preen)
528 printf(" (RECONNECTED)\n");
529 else
530 if (reply("RECONNECT") == 0) {
531 markclean = 0;
532 return (0);
534 if (parentdir != 0)
535 inoinfo(parentdir)->ino_linkcnt++;
536 if (lfdir == 0) {
537 dp = ginode(ROOTINO);
538 idesc.id_name = lfname;
539 idesc.id_type = DATA;
540 idesc.id_func = findino;
541 idesc.id_number = ROOTINO;
542 if ((ckinode(dp, &idesc) & FOUND) != 0) {
543 lfdir = idesc.id_parent;
544 } else {
545 pwarn("NO lost+found DIRECTORY");
546 if (preen || reply("CREATE")) {
547 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
548 if (lfdir != 0) {
549 if (makeentry(ROOTINO, lfdir, lfname) != 0) {
550 numdirs++;
551 if (preen)
552 printf(" (CREATED)\n");
553 } else {
554 freedir(lfdir, ROOTINO);
555 lfdir = 0;
556 if (preen)
557 printf("\n");
560 if (lfdir != 0) {
561 reparent(lfdir, ROOTINO);
565 if (lfdir == 0) {
566 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
567 markclean = 0;
568 return (0);
571 dp = ginode(lfdir);
572 mode = DIP(dp, mode);
573 mode = iswap16(mode);
574 if ((mode & IFMT) != IFDIR) {
575 pfatal("lost+found IS NOT A DIRECTORY");
576 if (reply("REALLOCATE") == 0) {
577 markclean = 0;
578 return (0);
580 oldlfdir = lfdir;
581 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
582 if (lfdir == 0) {
583 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
584 markclean = 0;
585 return (0);
587 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
588 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
589 markclean = 0;
590 return (0);
592 inodirty();
593 reparent(lfdir, ROOTINO);
594 idesc.id_type = ADDR;
595 idesc.id_func = pass4check;
596 idesc.id_number = oldlfdir;
597 adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
598 inoinfo(oldlfdir)->ino_linkcnt = 0;
599 dp = ginode(lfdir);
601 if (inoinfo(lfdir)->ino_state != DFOUND) {
602 pfatal("SORRY. NO lost+found DIRECTORY\n\n");
603 markclean = 0;
604 return (0);
606 (void)lftempname(tempname, orphan);
607 if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
608 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
609 printf("\n\n");
610 markclean = 0;
611 return (0);
613 inoinfo(orphan)->ino_linkcnt--;
614 if (lostdir) {
615 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
616 parentdir != (ino_t)-1)
617 (void)makeentry(orphan, lfdir, "..");
618 dp = ginode(lfdir);
619 nlink = DIP(dp, nlink);
620 DIP_SET(dp, nlink, iswap16(iswap16(nlink) + 1));
621 inodirty();
622 inoinfo(lfdir)->ino_linkcnt++;
623 reparent(orphan, lfdir);
624 pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
625 if (parentdir != (ino_t)-1)
626 printf("PARENT WAS I=%llu\n",
627 (unsigned long long)parentdir);
628 if (preen == 0)
629 printf("\n");
631 return (1);
635 * fix an entry in a directory.
638 changeino(ino_t dir, const char *name, ino_t newnum)
640 struct inodesc idesc;
642 memset(&idesc, 0, sizeof(struct inodesc));
643 idesc.id_type = DATA;
644 idesc.id_func = chgino;
645 idesc.id_number = dir;
646 idesc.id_fix = DONTKNOW;
647 idesc.id_name = name;
648 idesc.id_parent = newnum; /* new value for name */
649 return (ckinode(ginode(dir), &idesc));
653 * make an entry in a directory
656 makeentry(ino_t parent, ino_t ino, const char *name)
658 union dinode *dp;
659 struct inodesc idesc;
660 char pathbuf[MAXPATHLEN + 1];
662 if (parent < ROOTINO || parent >= maxino ||
663 ino < ROOTINO || ino >= maxino)
664 return (0);
665 memset(&idesc, 0, sizeof(struct inodesc));
666 idesc.id_type = DATA;
667 idesc.id_func = mkentry;
668 idesc.id_number = parent;
669 idesc.id_parent = ino; /* this is the inode to enter */
670 idesc.id_fix = DONTKNOW;
671 idesc.id_name = name;
672 dp = ginode(parent);
673 if (iswap64(DIP(dp, size)) % dirblksiz) {
674 DIP_SET(dp, size,
675 iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz)));
676 inodirty();
678 if ((ckinode(dp, &idesc) & ALTERED) != 0)
679 return (1);
680 getpathname(pathbuf, sizeof(pathbuf), parent, parent);
681 dp = ginode(parent);
682 if (expanddir(dp, pathbuf) == 0)
683 return (0);
684 return (ckinode(dp, &idesc) & ALTERED);
688 * Attempt to expand the size of a directory
690 static int
691 expanddir(union dinode *dp, char *name)
693 daddr_t lastbn, newblk, dirblk;
694 struct bufarea *bp;
695 char *cp;
696 #if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
697 char firstblk[DIRBLKSIZ];
698 #else
699 char firstblk[APPLEUFS_DIRBLKSIZ];
700 #endif
701 struct ufs1_dinode *dp1 = NULL;
702 struct ufs2_dinode *dp2 = NULL;
704 if (is_ufs2)
705 dp2 = &dp->dp2;
706 else
707 dp1 = &dp->dp1;
709 lastbn = lblkno(sblock, iswap64(DIP(dp, size)));
710 if (lastbn >= NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
711 DIP(dp, size) == 0)
712 return (0);
713 if ((newblk = allocblk(sblock->fs_frag)) == 0)
714 return (0);
715 if (is_ufs2) {
716 dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
717 dp2->di_db[lastbn] = iswap64(newblk);
718 dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
719 dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
720 btodb(sblock->fs_bsize));
721 dirblk = iswap64(dp2->di_db[lastbn + 1]);
722 } else {
723 dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
724 dp1->di_db[lastbn] = iswap32((int32_t)newblk);
725 dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
726 dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
727 btodb(sblock->fs_bsize));
728 dirblk = iswap32(dp1->di_db[lastbn + 1]);
730 bp = getdirblk(dirblk, sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
731 if (bp->b_errs)
732 goto bad;
733 memmove(firstblk, bp->b_un.b_buf, dirblksiz);
734 bp = getdirblk(newblk, sblock->fs_bsize);
735 if (bp->b_errs)
736 goto bad;
737 memmove(bp->b_un.b_buf, firstblk, dirblksiz);
738 emptydir.dot_reclen = iswap16(dirblksiz);
739 for (cp = &bp->b_un.b_buf[dirblksiz];
740 cp < &bp->b_un.b_buf[sblock->fs_bsize];
741 cp += dirblksiz)
742 memmove(cp, &emptydir, sizeof emptydir);
743 dirty(bp);
744 bp = getdirblk(dirblk, sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
745 if (bp->b_errs)
746 goto bad;
747 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
748 pwarn("NO SPACE LEFT IN %s", name);
749 if (preen)
750 printf(" (EXPANDED)\n");
751 else if (reply("EXPAND") == 0)
752 goto bad;
753 dirty(bp);
754 inodirty();
755 return (1);
756 bad:
757 if (is_ufs2) {
758 dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
759 dp2->di_db[lastbn + 1] = 0;
760 dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
761 dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
762 btodb(sblock->fs_bsize));
763 } else {
764 dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
765 dp1->di_db[lastbn + 1] = 0;
766 dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
767 dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
768 btodb(sblock->fs_bsize));
770 freeblk(newblk, sblock->fs_frag);
771 markclean = 0;
772 return (0);
776 * allocate a new directory
778 ino_t
779 allocdir(ino_t parent, ino_t request, int mode)
781 ino_t ino;
782 char *cp;
783 union dinode *dp;
784 struct bufarea *bp;
785 struct inoinfo *inp;
786 struct dirtemplate *dirp;
787 daddr_t dirblk;
789 ino = allocino(request, IFDIR|mode);
790 if (ino < ROOTINO)
791 return 0;
792 dirhead.dot_reclen = iswap16(12);
793 dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
794 odirhead.dot_reclen = iswap16(12);
795 odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
796 odirhead.dot_namlen = iswap16(1);
797 odirhead.dotdot_namlen = iswap16(2);
798 if (newinofmt)
799 dirp = &dirhead;
800 else
801 dirp = (struct dirtemplate *)&odirhead;
802 dirp->dot_ino = iswap32(ino);
803 dirp->dotdot_ino = iswap32(parent);
804 dp = ginode(ino);
805 dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
806 : iswap32(dp->dp1.di_db[0]);
807 bp = getdirblk(dirblk, sblock->fs_fsize);
808 if (bp->b_errs) {
809 freeino(ino);
810 return (0);
812 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
813 emptydir.dot_reclen = iswap16(dirblksiz);
814 for (cp = &bp->b_un.b_buf[dirblksiz];
815 cp < &bp->b_un.b_buf[sblock->fs_fsize];
816 cp += dirblksiz)
817 memmove(cp, &emptydir, sizeof emptydir);
818 dirty(bp);
819 DIP_SET(dp, nlink, iswap16(2));
820 inodirty();
821 if (ino == ROOTINO) {
822 inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
823 cacheino(dp, ino);
824 return(ino);
826 if (inoinfo(parent)->ino_state != DSTATE &&
827 inoinfo(parent)->ino_state != DFOUND) {
828 freeino(ino);
829 return (0);
831 cacheino(dp, ino);
832 inp = getinoinfo(ino);
833 inp->i_parent = parent;
834 inp->i_dotdot = parent;
835 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
836 if (inoinfo(ino)->ino_state == DSTATE) {
837 inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
838 inoinfo(parent)->ino_linkcnt++;
840 dp = ginode(parent);
841 DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1));
842 inodirty();
843 return (ino);
847 * free a directory inode
849 static void
850 freedir(ino_t ino, ino_t parent)
852 union dinode *dp;
854 if (ino != parent) {
855 dp = ginode(parent);
856 DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) - 1));
857 inodirty();
859 freeino(ino);
863 * generate a temporary name for the lost+found directory.
865 static int
866 lftempname(char *bufp, ino_t ino)
868 ino_t in;
869 char *cp;
870 int namlen;
872 cp = bufp + 2;
873 for (in = maxino; in > 0; in /= 10)
874 cp++;
875 *--cp = 0;
876 namlen = cp - bufp;
877 in = ino;
878 while (cp > bufp) {
879 *--cp = (in % 10) + '0';
880 in /= 10;
882 *cp = '#';
883 return (namlen);
887 * Get a directory block.
888 * Insure that it is held until another is requested.
890 static struct bufarea *
891 getdirblk(daddr_t blkno, long size)
894 if (pdirbp != 0)
895 pdirbp->b_flags &= ~B_INUSE;
896 pdirbp = getdatablk(blkno, size);
897 return (pdirbp);