xref: /netbsd-src/sbin/fsck_lfs/dir.c (revision 9fbd88883c38d0c0fbfcbe66d76fe6b0fab3f9de)
1 /* $NetBSD: dir.c,v 1.5 2001/07/13 20:30:18 perseant 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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/param.h>
37 #include <sys/time.h>
38 #include <ufs/ufs/dinode.h>
39 #include <ufs/ufs/dir.h>
40 #include <sys/mount.h>		/* XXX */
41 #include <ufs/lfs/lfs.h>
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 #include "fsck.h"
48 #include "fsutil.h"
49 #include "extern.h"
50 
51 char           *lfname = "lost+found";
52 int             lfmode = 01700;
53 struct dirtemplate emptydir = {0, DIRBLKSIZ};
54 struct dirtemplate dirhead = {
55 	0, 12, DT_DIR, 1, ".",
56 	0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
57 };
58 struct odirtemplate odirhead = {
59 	0, 12, 1, ".",
60 	0, DIRBLKSIZ - 12, 2, ".."
61 };
62 
63 static int      expanddir(struct dinode *, char *);
64 static void     freedir(ino_t, ino_t);
65 static struct direct *fsck_readdir(struct inodesc *);
66 static struct bufarea *getdirblk(daddr_t, long);
67 static int      lftempname(char *, ino_t);
68 static int      mkentry(struct inodesc *);
69 static int      chgino(struct inodesc *);
70 
71 /*
72  * Propagate connected state through the tree.
73  */
74 void
75 propagate()
76 {
77 	register struct inoinfo **inpp, *inp, *pinp;
78 	struct inoinfo **inpend;
79 
80 	/*
81 	 * Create a list of children for each directory.
82 	 */
83 	inpend = &inpsort[inplast];
84 	for (inpp = inpsort; inpp < inpend; inpp++) {
85 		inp = *inpp;
86 		if (inp->i_parent == 0 ||
87 		    inp->i_number == ROOTINO)
88 			continue;
89 		pinp = getinoinfo(inp->i_parent);
90 		inp->i_parentp = pinp;
91 		inp->i_sibling = pinp->i_child;
92 		pinp->i_child = inp;
93 	}
94 	inp = getinoinfo(ROOTINO);
95 	while (inp) {
96 		statemap[inp->i_number] = DFOUND;
97 		if (inp->i_child &&
98 		    statemap[inp->i_child->i_number] == DSTATE)
99 			inp = inp->i_child;
100 		else if (inp->i_sibling)
101 			inp = inp->i_sibling;
102 		else
103 			inp = inp->i_parentp;
104 	}
105 }
106 
107 /*
108  * Scan each entry in a directory block.
109  */
110 int
111 dirscan(struct inodesc * idesc)
112 {
113 	register struct direct *dp;
114 	register struct bufarea *bp;
115 	int             dsize, n;
116 	long            blksiz;
117 	char            dbuf[DIRBLKSIZ];
118 
119 	if (idesc->id_type != DATA)
120 		errexit("wrong type to dirscan %d\n", idesc->id_type);
121 	if (idesc->id_entryno == 0 &&
122 	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
123 		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
124 	blksiz = idesc->id_numfrags * sblock.lfs_fsize;
125 	if (chkrange(idesc->id_blkno, fragstofsb(&sblock, idesc->id_numfrags))) {
126 		idesc->id_filesize -= blksiz;
127 		return (SKIP);
128 	}
129 	idesc->id_loc = 0;
130 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
131 		dsize = dp->d_reclen;
132 		memcpy(dbuf, dp, (size_t)dsize);
133 #		if (BYTE_ORDER == LITTLE_ENDIAN)
134 			if (!newinofmt) {
135 				struct direct  *tdp = (struct direct *)dbuf;
136 				u_char          tmp;
137 
138 				tmp = tdp->d_namlen;
139 				tdp->d_namlen = tdp->d_type;
140 				tdp->d_type = tmp;
141 			}
142 #		endif
143 		idesc->id_dirp = (struct direct *)dbuf;
144 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
145 #			if (BYTE_ORDER == LITTLE_ENDIAN)
146 				if (!newinofmt && !doinglevel2) {
147 					struct direct  *tdp;
148 					u_char          tmp;
149 
150 					tdp = (struct direct *)dbuf;
151 					tmp = tdp->d_namlen;
152 					tdp->d_namlen = tdp->d_type;
153 					tdp->d_type = tmp;
154 				}
155 #			endif
156 			bp = getdirblk(idesc->id_blkno, blksiz);
157 			memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
158 			       (size_t)dsize);
159 			dirty(bp);
160 			sbdirty();
161 		}
162 		if (n & STOP)
163 			return (n);
164 	}
165 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
166 }
167 
168 /*
169  * get next entry in a directory.
170  */
171 static struct direct *
172 fsck_readdir(struct inodesc * idesc)
173 {
174 	register struct direct *dp, *ndp;
175 	register struct bufarea *bp;
176 	long            size, blksiz, fix, dploc;
177 
178 	blksiz = idesc->id_numfrags * sblock.lfs_fsize;
179 	bp = getdirblk(idesc->id_blkno, blksiz);
180 	if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
181 	    idesc->id_loc < blksiz) {
182 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
183 		if (dircheck(idesc, dp))
184 			goto dpok;
185 		if (idesc->id_fix == IGNORE)
186 			return (0);
187 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
188 		bp = getdirblk(idesc->id_blkno, blksiz);
189 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
190 		dp->d_reclen = DIRBLKSIZ;
191 		dp->d_ino = 0;
192 		dp->d_type = 0;
193 		dp->d_namlen = 0;
194 		dp->d_name[0] = '\0';
195 		if (fix)
196 			dirty(bp);
197 		idesc->id_loc += DIRBLKSIZ;
198 		idesc->id_filesize -= DIRBLKSIZ;
199 		return (dp);
200 	}
201 dpok:
202 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
203 		return NULL;
204 	dploc = idesc->id_loc;
205 	dp = (struct direct *)(bp->b_un.b_buf + dploc);
206 	idesc->id_loc += dp->d_reclen;
207 	idesc->id_filesize -= dp->d_reclen;
208 	if ((idesc->id_loc % DIRBLKSIZ) == 0)
209 		return (dp);
210 	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
211 	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
212 	    dircheck(idesc, ndp) == 0) {
213 		size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
214 		idesc->id_loc += size;
215 		idesc->id_filesize -= size;
216 		if (idesc->id_fix == IGNORE)
217 			return (0);
218 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
219 		bp = getdirblk(idesc->id_blkno, blksiz);
220 		dp = (struct direct *)(bp->b_un.b_buf + dploc);
221 		dp->d_reclen += size;
222 		if (fix)
223 			dirty(bp);
224 	}
225 	return (dp);
226 }
227 
228 /*
229  * Verify that a directory entry is valid.
230  * This is a superset of the checks made in the kernel.
231  */
232 int
233 dircheck(struct inodesc * idesc, struct direct * dp)
234 {
235 	register int    size;
236 	register char  *cp;
237 	u_char          namlen, type;
238 	int             spaceleft;
239 
240 	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
241 	if (dp->d_ino >= maxino ||
242 	    dp->d_reclen == 0 ||
243 	    dp->d_reclen > spaceleft ||
244 	    (dp->d_reclen & 0x3) != 0) {
245 		pwarn("ino too large, reclen=0, reclen>space, or reclen&3!=0\n");
246 		pwarn("dp->d_ino = 0x%x\tdp->d_reclen = 0x%x\n",
247 		      dp->d_ino, dp->d_reclen);
248 		pwarn("maxino = 0x%x\tspaceleft = 0x%x\n", maxino, spaceleft);
249 		return (0);
250 	}
251 	if (dp->d_ino == 0)
252 		return (1);
253 	size = DIRSIZ(!newinofmt, dp, 0);
254 #	if (BYTE_ORDER == LITTLE_ENDIAN)
255 		if (!newinofmt) {
256 			type = dp->d_namlen;
257 			namlen = dp->d_type;
258 		} else {
259 			namlen = dp->d_namlen;
260 			type = dp->d_type;
261 		}
262 #	else
263 		namlen = dp->d_namlen;
264 		type = dp->d_type;
265 #	endif
266 	if (dp->d_reclen < size ||
267 	    idesc->id_filesize < size ||
268 	    namlen > MAXNAMLEN ||
269 	    type > 15) {
270 		printf("reclen<size, filesize<size, namlen too large, or type>15\n");
271 		return (0);
272 	}
273 	for (cp = dp->d_name, size = 0; size < namlen; size++)
274 		if (*cp == '\0' || (*cp++ == '/')) {
275 			printf("name contains NUL or /\n");
276 			return (0);
277 		}
278 	if (*cp != '\0') {
279 		printf("name size misstated\n");
280 		return (0);
281 	}
282 	return (1);
283 }
284 
285 void
286 direrror(ino_t ino, char *errmesg)
287 {
288 
289 	fileerror(ino, ino, errmesg);
290 }
291 
292 void
293 fileerror(ino_t cwd, ino_t ino, char *errmesg)
294 {
295 	register struct dinode *dp;
296 	char            pathbuf[MAXPATHLEN + 1];
297 
298 	pwarn("%s ", errmesg);
299 	pinode(ino);
300 	printf("\n");
301 	getpathname(pathbuf, cwd, ino);
302 	if (ino < ROOTINO || ino >= maxino) {
303 		pfatal("NAME=%s\n", pathbuf);
304 		return;
305 	}
306 	dp = ginode(ino);
307 	if (dp == NULL)
308 		pfatal("INO is NULL\n");
309 	else {
310 		if (ftypeok(dp))
311 			pfatal("%s=%s\n",
312 			       (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
313 		else
314 			pfatal("NAME=%s\n", pathbuf);
315 	}
316 }
317 
318 void
319 adjust(struct inodesc * idesc, short lcnt)
320 {
321 	register struct dinode *dp;
322 
323 	dp = ginode(idesc->id_number);
324 	if (dp->di_nlink == lcnt) {
325 		if (linkup(idesc->id_number, (ino_t)0) == 0)
326 			clri(idesc, "UNREF", 0);
327 	} else {
328 		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
329 		      ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"));
330 		pinode(idesc->id_number);
331 		printf(" COUNT %d SHOULD BE %d",
332 		       dp->di_nlink, dp->di_nlink - lcnt);
333 		if (preen) {
334 			if (lcnt < 0) {
335 				printf("\n");
336 				pfatal("LINK COUNT INCREASING");
337 			}
338 			printf(" (ADJUSTED)\n");
339 		}
340 		if (preen || reply("ADJUST") == 1) {
341 			dp->di_nlink -= lcnt;
342 			inodirty();
343 		}
344 	}
345 }
346 
347 static int
348 mkentry(struct inodesc * idesc)
349 {
350 	register struct direct *dirp = idesc->id_dirp;
351 	struct direct   newent;
352 	int             newlen, oldlen;
353 
354 	newent.d_namlen = strlen(idesc->id_name);
355 	newlen = DIRSIZ(0, &newent, 0);
356 	if (dirp->d_ino != 0)
357 		oldlen = DIRSIZ(0, dirp, 0);
358 	else
359 		oldlen = 0;
360 	if (dirp->d_reclen - oldlen < newlen)
361 		return (KEEPON);
362 	newent.d_reclen = dirp->d_reclen - oldlen;
363 	dirp->d_reclen = oldlen;
364 	dirp = (struct direct *)(((char *)dirp) + oldlen);
365 	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
366 	dirp->d_reclen = newent.d_reclen;
367 	if (newinofmt)
368 		dirp->d_type = typemap[idesc->id_parent];
369 	else
370 		dirp->d_type = 0;
371 	dirp->d_namlen = newent.d_namlen;
372 	memcpy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1);
373 #	if (BYTE_ORDER == LITTLE_ENDIAN)
374 		/*
375 	 	* If the entry was split, dirscan()will only reverse the byte
376 	 	* order of the original entry, and not the new one, before
377 	 	* writing it back out.  So, we reverse the byte order here if
378 	 	* necessary.
379 	 	*/
380 		if (oldlen != 0 && !newinofmt && !doinglevel2) {
381 			u_char          tmp;
382 
383 			tmp = dirp->d_namlen;
384 			dirp->d_namlen = dirp->d_type;
385 			dirp->d_type = tmp;
386 		}
387 #	endif
388 	return (ALTERED | STOP);
389 }
390 
391 static int
392 chgino(struct inodesc * idesc)
393 {
394 	register struct direct *dirp = idesc->id_dirp;
395 
396 	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
397 		return (KEEPON);
398 	dirp->d_ino = idesc->id_parent;
399 	if (newinofmt)
400 		dirp->d_type = typemap[idesc->id_parent];
401 	else
402 		dirp->d_type = 0;
403 	return (ALTERED | STOP);
404 }
405 
406 int
407 linkup(ino_t orphan, ino_t parentdir)
408 {
409 	register struct dinode *dp;
410 	int             lostdir;
411 	ino_t           oldlfdir;
412 	struct inodesc  idesc;
413 	char            tempname[BUFSIZ];
414 
415 	memset(&idesc, 0, sizeof(struct inodesc));
416 	dp = ginode(orphan);
417 	lostdir = (dp->di_mode & IFMT) == IFDIR;
418 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
419 	pinode(orphan);
420 	if (preen && dp->di_size == 0)
421 		return (0);
422 	if (preen)
423 		printf(" (RECONNECTED)\n");
424 	else if (reply("RECONNECT") == 0)
425 		return (0);
426 	if (lfdir == 0) {
427 		dp = ginode(ROOTINO);
428 		idesc.id_name = lfname;
429 		idesc.id_type = DATA;
430 		idesc.id_func = findino;
431 		idesc.id_number = ROOTINO;
432 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
433 			lfdir = idesc.id_parent;
434 		} else {
435 			pwarn("NO lost+found DIRECTORY");
436 			if (preen || reply("CREATE")) {
437 				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
438 				if (lfdir != 0) {
439 					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
440 						if (preen)
441 							printf(" (CREATED)\n");
442 					} else {
443 						freedir(lfdir, ROOTINO);
444 						lfdir = 0;
445 						if (preen)
446 							printf("\n");
447 					}
448 				}
449 			}
450 		}
451 		if (lfdir == 0) {
452 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
453 			printf("\n\n");
454 			return (0);
455 		}
456 	}
457 	dp = ginode(lfdir);
458 	if ((dp->di_mode & IFMT) != IFDIR) {
459 		pfatal("lost+found IS NOT A DIRECTORY");
460 		if (reply("REALLOCATE") == 0)
461 			return (0);
462 		oldlfdir = lfdir;
463 		if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
464 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
465 			return (0);
466 		}
467 		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
468 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
469 			return (0);
470 		}
471 		inodirty();
472 		idesc.id_type = ADDR;
473 		idesc.id_func = pass4check;
474 		idesc.id_number = oldlfdir;
475 		adjust(&idesc, lncntp[oldlfdir] + 1);
476 		lncntp[oldlfdir] = 0;
477 		dp = ginode(lfdir);
478 	}
479 	if (statemap[lfdir] != DFOUND) {
480 		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
481 		return (0);
482 	}
483 	(void)lftempname(tempname, orphan);
484 	if (makeentry(lfdir, orphan, tempname) == 0) {
485 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
486 		printf("\n\n");
487 		return (0);
488 	}
489 	lncntp[orphan]--;
490 	if (lostdir) {
491 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
492 		    parentdir != (ino_t)-1)
493 			(void)makeentry(orphan, lfdir, "..");
494 		dp = ginode(lfdir);
495 		dp->di_nlink++;
496 		inodirty();
497 		lncntp[lfdir]++;
498 		pwarn("DIR I=%u CONNECTED. ", orphan);
499 		if (parentdir != (ino_t)-1)
500 			printf("PARENT WAS I=%u\n", parentdir);
501 		if (preen == 0)
502 			printf("\n");
503 	}
504 	return (1);
505 }
506 
507 /*
508  * fix an entry in a directory.
509  */
510 int
511 changeino(ino_t dir, char *name, ino_t newnum)
512 {
513 	struct inodesc  idesc;
514 
515 	memset(&idesc, 0, sizeof(struct inodesc));
516 	idesc.id_type = DATA;
517 	idesc.id_func = chgino;
518 	idesc.id_number = dir;
519 	idesc.id_fix = DONTKNOW;
520 	idesc.id_name = name;
521 	idesc.id_parent = newnum;	/* new value for name */
522 	return (ckinode(ginode(dir), &idesc));
523 }
524 
525 /*
526  * make an entry in a directory
527  */
528 int
529 makeentry(ino_t parent, ino_t ino, char *name)
530 {
531 	struct dinode  *dp;
532 	struct inodesc  idesc;
533 	char            pathbuf[MAXPATHLEN + 1];
534 
535 	if (parent < ROOTINO || parent >= maxino ||
536 	    ino < ROOTINO || ino >= maxino)
537 		return (0);
538 	memset(&idesc, 0, sizeof(struct inodesc));
539 	idesc.id_type = DATA;
540 	idesc.id_func = mkentry;
541 	idesc.id_number = parent;
542 	idesc.id_parent = ino;	/* this is the inode to enter */
543 	idesc.id_fix = DONTKNOW;
544 	idesc.id_name = name;
545 	dp = ginode(parent);
546 	if (dp->di_size % DIRBLKSIZ) {
547 		dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
548 		inodirty();
549 	}
550 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
551 		return (1);
552 	getpathname(pathbuf, parent, parent);
553 	dp = ginode(parent);
554 	if (expanddir(dp, pathbuf) == 0)
555 		return (0);
556 	return (ckinode(dp, &idesc) & ALTERED);
557 }
558 
559 /*
560  * Attempt to expand the size of a directory
561  */
562 static int
563 expanddir(struct dinode * dp, char *name)
564 {
565 	daddr_t         lastbn, newblk;
566 	register struct bufarea *bp;
567 	char           *cp, firstblk[DIRBLKSIZ];
568 
569 	lastbn = lblkno(&sblock, dp->di_size);
570 	if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0)
571 		return (0);
572 	if ((newblk = allocblk(sblock.lfs_frag)) == 0)
573 		return (0);
574 	dp->di_db[lastbn + 1] = dp->di_db[lastbn];
575 	dp->di_db[lastbn] = newblk;
576 	dp->di_size += sblock.lfs_bsize;
577 	dp->di_blocks += btofsb(&sblock, sblock.lfs_bsize);
578 	bp = getdirblk(dp->di_db[lastbn + 1],
579 		(long)dblksize(&sblock, dp, lastbn + 1));
580 	if (bp->b_errs)
581 		goto bad;
582 	memcpy(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
583 	bp = getdirblk(newblk, sblock.lfs_bsize);
584 	if (bp->b_errs)
585 		goto bad;
586 	memcpy(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
587 	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
588 	     cp < &bp->b_un.b_buf[sblock.lfs_bsize];
589 	     cp += DIRBLKSIZ)
590 		memcpy(cp, &emptydir, sizeof emptydir);
591 	dirty(bp);
592 	bp = getdirblk(dp->di_db[lastbn + 1],
593 		       (long)dblksize(&sblock, dp, lastbn + 1));
594 	if (bp->b_errs)
595 		goto bad;
596 	memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir);
597 	pwarn("NO SPACE LEFT IN %s", name);
598 	if (preen)
599 		printf(" (EXPANDED)\n");
600 	else if (reply("EXPAND") == 0)
601 		goto bad;
602 	dirty(bp);
603 	inodirty();
604 	return (1);
605 bad:
606 	dp->di_db[lastbn] = dp->di_db[lastbn + 1];
607 	dp->di_db[lastbn + 1] = 0;
608 	dp->di_size -= sblock.lfs_bsize;
609 	dp->di_blocks -= btofsb(&sblock, sblock.lfs_bsize);
610 	freeblk(newblk, sblock.lfs_frag);
611 	return (0);
612 }
613 
614 /*
615  * allocate a new directory
616  */
617 int
618 allocdir(ino_t parent, ino_t request, int mode)
619 {
620 	ino_t           ino;
621 	char           *cp;
622 	struct dinode  *dp;
623 	register struct bufarea *bp;
624 	struct dirtemplate *dirp;
625 
626 	ino = allocino(request, IFDIR | mode);
627 	if (newinofmt)
628 		dirp = &dirhead;
629 	else
630 		dirp = (struct dirtemplate *) & odirhead;
631 	dirp->dot_ino = ino;
632 	dirp->dotdot_ino = parent;
633 	dp = ginode(ino);
634 	bp = getdirblk(dp->di_db[0], sblock.lfs_fsize);
635 	if (bp->b_errs) {
636 		freeino(ino);
637 		return (0);
638 	}
639 	memcpy(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
640 	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
641 	     cp < &bp->b_un.b_buf[sblock.lfs_fsize];
642 	     cp += DIRBLKSIZ)
643 		memcpy(cp, &emptydir, sizeof emptydir);
644 	dirty(bp);
645 	dp->di_nlink = 2;
646 	inodirty();
647 	if (ino == ROOTINO) {
648 		lncntp[ino] = dp->di_nlink;
649 		cacheino(dp, ino);
650 		return (ino);
651 	}
652 	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
653 		freeino(ino);
654 		return (0);
655 	}
656 	cacheino(dp, ino);
657 	statemap[ino] = statemap[parent];
658 	if (statemap[ino] == DSTATE) {
659 		lncntp[ino] = dp->di_nlink;
660 		lncntp[parent]++;
661 	}
662 	dp = ginode(parent);
663 	dp->di_nlink++;
664 	inodirty();
665 	return (ino);
666 }
667 
668 /*
669  * free a directory inode
670  */
671 static void
672 freedir(ino_t ino, ino_t parent)
673 {
674 	struct dinode  *dp;
675 
676 	if (ino != parent) {
677 		dp = ginode(parent);
678 		dp->di_nlink--;
679 		inodirty();
680 	}
681 	freeino(ino);
682 }
683 
684 /*
685  * generate a temporary name for the lost+found directory.
686  */
687 static int
688 lftempname(char *bufp, ino_t ino)
689 {
690 	register ino_t  in;
691 	register char  *cp;
692 	int             namlen;
693 
694 	cp = bufp + 2;
695 	for (in = maxino; in > 0; in /= 10)
696 		cp++;
697 	*--cp = 0;
698 	namlen = cp - bufp;
699 	in = ino;
700 	while (cp > bufp) {
701 		*--cp = (in % 10) + '0';
702 		in /= 10;
703 	}
704 	*cp = '#';
705 	return (namlen);
706 }
707 
708 /*
709  * Get a directory block.
710  * Ensure that it is held until another is requested.
711  */
712 static struct bufarea *
713 getdirblk(daddr_t blkno, long size)
714 {
715 
716 	if (pdirbp != 0)
717 		pdirbp->b_flags &= ~B_INUSE;
718 	/* pdirbp = getdatablk(blkno, size); */
719 	pdirbp = getddblk(blkno, size);
720 	return (pdirbp);
721 }
722