dir.c revision 1.18
1/*	$NetBSD: dir.c,v 1.18 1996/06/11 07:07:52 mycroft 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#ifndef lint
37#if 0
38static char sccsid[] = "@(#)dir.c	8.5 (Berkeley) 12/8/94";
39#else
40static char rcsid[] = "$NetBSD: dir.c,v 1.18 1996/06/11 07:07:52 mycroft Exp $";
41#endif
42#endif /* not lint */
43
44#include <sys/param.h>
45#include <sys/time.h>
46#include <ufs/ufs/dinode.h>
47#include <ufs/ufs/dir.h>
48#include <ufs/ffs/fs.h>
49
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53
54#include "fsck.h"
55#include "extern.h"
56
57char	*lfname = "lost+found";
58int	lfmode = 01777;
59struct	dirtemplate emptydir = { 0, DIRBLKSIZ };
60struct	dirtemplate dirhead = {
61	0, 12, DT_DIR, 1, ".",
62	0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
63};
64struct	odirtemplate odirhead = {
65	0, 12, 1, ".",
66	0, DIRBLKSIZ - 12, 2, ".."
67};
68
69int expanddir __P((struct dinode *, char *));
70void freedir __P((ino_t, ino_t));
71struct direct *fsck_readdir();
72struct bufarea *getdirblk();
73int lftempname __P((char *, ino_t));
74
75/*
76 * Propagate connected state through the tree.
77 */
78void
79propagate()
80{
81	register struct inoinfo **inpp, *inp, *pinp;
82	struct inoinfo **inpend;
83
84	/*
85	 * Create a list of children for each directory.
86	 */
87	inpend = &inpsort[inplast];
88	for (inpp = inpsort; inpp < inpend; inpp++) {
89		inp = *inpp;
90		if (inp->i_parent == 0 ||
91		    inp->i_number == ROOTINO)
92			continue;
93		pinp = getinoinfo(inp->i_parent);
94		inp->i_parentp = pinp;
95		inp->i_sibling = pinp->i_child;
96		pinp->i_child = inp;
97	}
98	inp = getinoinfo(ROOTINO);
99	while (inp) {
100		statemap[inp->i_number] = DFOUND;
101		if (inp->i_child &&
102		    statemap[inp->i_child->i_number] == DSTATE)
103			inp = inp->i_child;
104		else if (inp->i_sibling)
105			inp = inp->i_sibling;
106		else
107			inp = inp->i_parentp;
108	}
109}
110
111/*
112 * Scan each entry in a directory block.
113 */
114int
115dirscan(idesc)
116	register struct inodesc *idesc;
117{
118	register struct direct *dp;
119	register struct bufarea *bp;
120	int dsize, n;
121	long blksiz;
122	char dbuf[DIRBLKSIZ];
123
124	if (idesc->id_type != DATA)
125		errexit("wrong type to dirscan %d\n", idesc->id_type);
126	if (idesc->id_entryno == 0 &&
127	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
128		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
129	blksiz = idesc->id_numfrags * sblock.fs_fsize;
130	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
131		idesc->id_filesize -= blksiz;
132		return (SKIP);
133	}
134	idesc->id_loc = 0;
135	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
136		dsize = dp->d_reclen;
137		memcpy(dbuf, dp, (size_t)dsize);
138#		if (BYTE_ORDER == LITTLE_ENDIAN)
139			if (!newinofmt) {
140				struct direct *tdp = (struct direct *)dbuf;
141				u_char tmp;
142
143				tmp = tdp->d_namlen;
144				tdp->d_namlen = tdp->d_type;
145				tdp->d_type = tmp;
146			}
147#		endif
148		idesc->id_dirp = (struct direct *)dbuf;
149		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
150#			if (BYTE_ORDER == LITTLE_ENDIAN)
151				if (!newinofmt && !doinglevel2) {
152					struct direct *tdp;
153					u_char tmp;
154
155					tdp = (struct direct *)dbuf;
156					tmp = tdp->d_namlen;
157					tdp->d_namlen = tdp->d_type;
158					tdp->d_type = tmp;
159				}
160#			endif
161			bp = getdirblk(idesc->id_blkno, blksiz);
162			memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
163			    (size_t)dsize);
164			dirty(bp);
165			sbdirty();
166		}
167		if (n & STOP)
168			return (n);
169	}
170	return (idesc->id_filesize > 0 ? KEEPON : STOP);
171}
172
173/*
174 * get next entry in a directory.
175 */
176struct direct *
177fsck_readdir(idesc)
178	register struct inodesc *idesc;
179{
180	register struct direct *dp, *ndp;
181	register struct bufarea *bp;
182	long size, blksiz, fix, dploc;
183
184	blksiz = idesc->id_numfrags * sblock.fs_fsize;
185	bp = getdirblk(idesc->id_blkno, blksiz);
186	if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
187	    idesc->id_loc < blksiz) {
188		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
189		if (dircheck(idesc, dp))
190			goto dpok;
191		if (idesc->id_fix == IGNORE)
192			return (0);
193		fix = dofix(idesc, "DIRECTORY CORRUPTED");
194		bp = getdirblk(idesc->id_blkno, blksiz);
195		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
196		dp->d_reclen = DIRBLKSIZ;
197		dp->d_ino = 0;
198		dp->d_type = 0;
199		dp->d_namlen = 0;
200		dp->d_name[0] = '\0';
201		if (fix)
202			dirty(bp);
203		idesc->id_loc += DIRBLKSIZ;
204		idesc->id_filesize -= DIRBLKSIZ;
205		return (dp);
206	}
207dpok:
208	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
209		return NULL;
210	dploc = idesc->id_loc;
211	dp = (struct direct *)(bp->b_un.b_buf + dploc);
212	idesc->id_loc += dp->d_reclen;
213	idesc->id_filesize -= dp->d_reclen;
214	if ((idesc->id_loc % DIRBLKSIZ) == 0)
215		return (dp);
216	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
217	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
218	    dircheck(idesc, ndp) == 0) {
219		size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
220		idesc->id_loc += size;
221		idesc->id_filesize -= size;
222		if (idesc->id_fix == IGNORE)
223			return (0);
224		fix = dofix(idesc, "DIRECTORY CORRUPTED");
225		bp = getdirblk(idesc->id_blkno, blksiz);
226		dp = (struct direct *)(bp->b_un.b_buf + dploc);
227		dp->d_reclen += size;
228		if (fix)
229			dirty(bp);
230	}
231	return (dp);
232}
233
234/*
235 * Verify that a directory entry is valid.
236 * This is a superset of the checks made in the kernel.
237 */
238int
239dircheck(idesc, dp)
240	struct inodesc *idesc;
241	register struct direct *dp;
242{
243	register int size;
244	register char *cp;
245	u_char namlen, type;
246	int spaceleft;
247
248	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
249	if (dp->d_ino >= maxino ||
250	    dp->d_reclen == 0 ||
251	    dp->d_reclen > spaceleft ||
252	    (dp->d_reclen & 0x3) != 0)
253		return (0);
254	if (dp->d_ino == 0)
255		return (1);
256	size = DIRSIZ(!newinofmt, dp);
257#	if (BYTE_ORDER == LITTLE_ENDIAN)
258		if (!newinofmt) {
259			type = dp->d_namlen;
260			namlen = dp->d_type;
261		} else {
262			namlen = dp->d_namlen;
263			type = dp->d_type;
264		}
265#	else
266		namlen = dp->d_namlen;
267		type = dp->d_type;
268#	endif
269	if (dp->d_reclen < size ||
270	    idesc->id_filesize < size ||
271	    namlen > MAXNAMLEN ||
272	    type > 15)
273		return (0);
274	for (cp = dp->d_name, size = 0; size < namlen; size++)
275		if (*cp == '\0' || (*cp++ == '/'))
276			return (0);
277	if (*cp != '\0')
278		return (0);
279	return (1);
280}
281
282void
283direrror(ino, errmesg)
284	ino_t ino;
285	char *errmesg;
286{
287
288	fileerror(ino, ino, errmesg);
289}
290
291void
292fileerror(cwd, ino, errmesg)
293	ino_t cwd, ino;
294	char *errmesg;
295{
296	register struct dinode *dp;
297	char pathbuf[MAXPATHLEN + 1];
298
299	pwarn("%s ", errmesg);
300	pinode(ino);
301	printf("\n");
302	getpathname(pathbuf, cwd, ino);
303	if (ino < ROOTINO || ino > maxino) {
304		pfatal("NAME=%s\n", pathbuf);
305		return;
306	}
307	dp = ginode(ino);
308	if (ftypeok(dp))
309		pfatal("%s=%s\n",
310		    (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
311	else
312		pfatal("NAME=%s\n", pathbuf);
313}
314
315void
316adjust(idesc, lcnt)
317	register struct inodesc *idesc;
318	short lcnt;
319{
320	register struct dinode *dp;
321
322	dp = ginode(idesc->id_number);
323	if (dp->di_nlink == lcnt) {
324		if (linkup(idesc->id_number, (ino_t)0) == 0)
325			clri(idesc, "UNREF", 0);
326	} else {
327		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
328			((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"));
329		pinode(idesc->id_number);
330		printf(" COUNT %d SHOULD BE %d",
331			dp->di_nlink, dp->di_nlink - lcnt);
332		if (preen) {
333			if (lcnt < 0) {
334				printf("\n");
335				pfatal("LINK COUNT INCREASING");
336			}
337			printf(" (ADJUSTED)\n");
338		}
339		if (preen || reply("ADJUST") == 1) {
340			dp->di_nlink -= lcnt;
341			inodirty();
342		}
343	}
344}
345
346int
347mkentry(idesc)
348	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);
356	if (dirp->d_ino != 0)
357		oldlen = DIRSIZ(0, dirp);
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
391int
392chgino(idesc)
393	struct inodesc *idesc;
394{
395	register struct direct *dirp = idesc->id_dirp;
396
397	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
398		return (KEEPON);
399	dirp->d_ino = idesc->id_parent;
400	if (newinofmt)
401		dirp->d_type = typemap[idesc->id_parent];
402	else
403		dirp->d_type = 0;
404	return (ALTERED|STOP);
405}
406
407int
408linkup(orphan, parentdir)
409	ino_t orphan;
410	ino_t parentdir;
411{
412	register struct dinode *dp;
413	int lostdir;
414	ino_t oldlfdir;
415	struct inodesc idesc;
416	char tempname[BUFSIZ];
417
418	memset(&idesc, 0, sizeof(struct inodesc));
419	dp = ginode(orphan);
420	lostdir = (dp->di_mode & IFMT) == IFDIR;
421	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
422	pinode(orphan);
423	if (preen && dp->di_size == 0)
424		return (0);
425	if (preen)
426		printf(" (RECONNECTED)\n");
427	else
428		if (reply("RECONNECT") == 0)
429			return (0);
430	if (lfdir == 0) {
431		dp = ginode(ROOTINO);
432		idesc.id_name = lfname;
433		idesc.id_type = DATA;
434		idesc.id_func = findino;
435		idesc.id_number = ROOTINO;
436		if ((ckinode(dp, &idesc) & FOUND) != 0) {
437			lfdir = idesc.id_parent;
438		} else {
439			pwarn("NO lost+found DIRECTORY");
440			if (preen || reply("CREATE")) {
441				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
442				if (lfdir != 0) {
443					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
444						if (preen)
445							printf(" (CREATED)\n");
446					} else {
447						freedir(lfdir, ROOTINO);
448						lfdir = 0;
449						if (preen)
450							printf("\n");
451					}
452				}
453			}
454		}
455		if (lfdir == 0) {
456			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
457			printf("\n\n");
458			return (0);
459		}
460	}
461	dp = ginode(lfdir);
462	if ((dp->di_mode & IFMT) != IFDIR) {
463		pfatal("lost+found IS NOT A DIRECTORY");
464		if (reply("REALLOCATE") == 0)
465			return (0);
466		oldlfdir = lfdir;
467		if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
468			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
469			return (0);
470		}
471		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
472			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
473			return (0);
474		}
475		inodirty();
476		idesc.id_type = ADDR;
477		idesc.id_func = pass4check;
478		idesc.id_number = oldlfdir;
479		adjust(&idesc, lncntp[oldlfdir] + 1);
480		lncntp[oldlfdir] = 0;
481		dp = ginode(lfdir);
482	}
483	if (statemap[lfdir] != DFOUND) {
484		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
485		return (0);
486	}
487	(void)lftempname(tempname, orphan);
488	if (makeentry(lfdir, orphan, tempname) == 0) {
489		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
490		printf("\n\n");
491		return (0);
492	}
493	lncntp[orphan]--;
494	if (lostdir) {
495		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
496		    parentdir != (ino_t)-1)
497			(void)makeentry(orphan, lfdir, "..");
498		dp = ginode(lfdir);
499		dp->di_nlink++;
500		inodirty();
501		lncntp[lfdir]++;
502		pwarn("DIR I=%lu CONNECTED. ", orphan);
503		if (parentdir != (ino_t)-1)
504			printf("PARENT WAS I=%lu\n", parentdir);
505		if (preen == 0)
506			printf("\n");
507	}
508	return (1);
509}
510
511/*
512 * fix an entry in a directory.
513 */
514int
515changeino(dir, name, newnum)
516	ino_t dir;
517	char *name;
518	ino_t newnum;
519{
520	struct inodesc idesc;
521
522	memset(&idesc, 0, sizeof(struct inodesc));
523	idesc.id_type = DATA;
524	idesc.id_func = chgino;
525	idesc.id_number = dir;
526	idesc.id_fix = DONTKNOW;
527	idesc.id_name = name;
528	idesc.id_parent = newnum;	/* new value for name */
529	return (ckinode(ginode(dir), &idesc));
530}
531
532/*
533 * make an entry in a directory
534 */
535int
536makeentry(parent, ino, name)
537	ino_t parent, ino;
538	char *name;
539{
540	struct dinode *dp;
541	struct inodesc idesc;
542	char pathbuf[MAXPATHLEN + 1];
543
544	if (parent < ROOTINO || parent >= maxino ||
545	    ino < ROOTINO || ino >= maxino)
546		return (0);
547	memset(&idesc, 0, sizeof(struct inodesc));
548	idesc.id_type = DATA;
549	idesc.id_func = mkentry;
550	idesc.id_number = parent;
551	idesc.id_parent = ino;	/* this is the inode to enter */
552	idesc.id_fix = DONTKNOW;
553	idesc.id_name = name;
554	dp = ginode(parent);
555	if (dp->di_size % DIRBLKSIZ) {
556		dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
557		inodirty();
558	}
559	if ((ckinode(dp, &idesc) & ALTERED) != 0)
560		return (1);
561	getpathname(pathbuf, parent, parent);
562	dp = ginode(parent);
563	if (expanddir(dp, pathbuf) == 0)
564		return (0);
565	return (ckinode(dp, &idesc) & ALTERED);
566}
567
568/*
569 * Attempt to expand the size of a directory
570 */
571int
572expanddir(dp, name)
573	register struct dinode *dp;
574	char *name;
575{
576	daddr_t lastbn, newblk;
577	register struct bufarea *bp;
578	char *cp, firstblk[DIRBLKSIZ];
579
580	lastbn = lblkno(&sblock, dp->di_size);
581	if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0)
582		return (0);
583	if ((newblk = allocblk(sblock.fs_frag)) == 0)
584		return (0);
585	dp->di_db[lastbn + 1] = dp->di_db[lastbn];
586	dp->di_db[lastbn] = newblk;
587	dp->di_size += sblock.fs_bsize;
588	dp->di_blocks += btodb(sblock.fs_bsize);
589	bp = getdirblk(dp->di_db[lastbn + 1],
590		(long)dblksize(&sblock, dp, lastbn + 1));
591	if (bp->b_errs)
592		goto bad;
593	memcpy(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
594	bp = getdirblk(newblk, sblock.fs_bsize);
595	if (bp->b_errs)
596		goto bad;
597	memcpy(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
598	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
599	     cp < &bp->b_un.b_buf[sblock.fs_bsize];
600	     cp += DIRBLKSIZ)
601		memcpy(cp, &emptydir, sizeof emptydir);
602	dirty(bp);
603	bp = getdirblk(dp->di_db[lastbn + 1],
604		(long)dblksize(&sblock, dp, lastbn + 1));
605	if (bp->b_errs)
606		goto bad;
607	memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir);
608	pwarn("NO SPACE LEFT IN %s", name);
609	if (preen)
610		printf(" (EXPANDED)\n");
611	else if (reply("EXPAND") == 0)
612		goto bad;
613	dirty(bp);
614	inodirty();
615	return (1);
616bad:
617	dp->di_db[lastbn] = dp->di_db[lastbn + 1];
618	dp->di_db[lastbn + 1] = 0;
619	dp->di_size -= sblock.fs_bsize;
620	dp->di_blocks -= btodb(sblock.fs_bsize);
621	freeblk(newblk, sblock.fs_frag);
622	return (0);
623}
624
625/*
626 * allocate a new directory
627 */
628int
629allocdir(parent, request, mode)
630	ino_t parent, request;
631	int mode;
632{
633	ino_t ino;
634	char *cp;
635	struct dinode *dp;
636	register struct bufarea *bp;
637	struct dirtemplate *dirp;
638
639	ino = allocino(request, IFDIR|mode);
640	if (newinofmt)
641		dirp = &dirhead;
642	else
643		dirp = (struct dirtemplate *)&odirhead;
644	dirp->dot_ino = ino;
645	dirp->dotdot_ino = parent;
646	dp = ginode(ino);
647	bp = getdirblk(dp->di_db[0], sblock.fs_fsize);
648	if (bp->b_errs) {
649		freeino(ino);
650		return (0);
651	}
652	memcpy(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
653	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
654	     cp < &bp->b_un.b_buf[sblock.fs_fsize];
655	     cp += DIRBLKSIZ)
656		memcpy(cp, &emptydir, sizeof emptydir);
657	dirty(bp);
658	dp->di_nlink = 2;
659	inodirty();
660	if (ino == ROOTINO) {
661		lncntp[ino] = dp->di_nlink;
662		cacheino(dp, ino);
663		return(ino);
664	}
665	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
666		freeino(ino);
667		return (0);
668	}
669	cacheino(dp, ino);
670	statemap[ino] = statemap[parent];
671	if (statemap[ino] == DSTATE) {
672		lncntp[ino] = dp->di_nlink;
673		lncntp[parent]++;
674	}
675	dp = ginode(parent);
676	dp->di_nlink++;
677	inodirty();
678	return (ino);
679}
680
681/*
682 * free a directory inode
683 */
684void
685freedir(ino, parent)
686	ino_t ino, parent;
687{
688	struct dinode *dp;
689
690	if (ino != parent) {
691		dp = ginode(parent);
692		dp->di_nlink--;
693		inodirty();
694	}
695	freeino(ino);
696}
697
698/*
699 * generate a temporary name for the lost+found directory.
700 */
701int
702lftempname(bufp, ino)
703	char *bufp;
704	ino_t ino;
705{
706	register ino_t in;
707	register char *cp;
708	int namlen;
709
710	cp = bufp + 2;
711	for (in = maxino; in > 0; in /= 10)
712		cp++;
713	*--cp = 0;
714	namlen = cp - bufp;
715	in = ino;
716	while (cp > bufp) {
717		*--cp = (in % 10) + '0';
718		in /= 10;
719	}
720	*cp = '#';
721	return (namlen);
722}
723
724/*
725 * Get a directory block.
726 * Insure that it is held until another is requested.
727 */
728struct bufarea *
729getdirblk(blkno, size)
730	daddr_t blkno;
731	long size;
732{
733
734	if (pdirbp != 0)
735		pdirbp->b_flags &= ~B_INUSE;
736	pdirbp = getdatablk(blkno, size);
737	return (pdirbp);
738}
739