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