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