dir.c revision 7586
1130812Smarcel/*
2130812Smarcel * Copyright (c) 1980, 1986, 1993
3130812Smarcel *	The Regents of the University of California.  All rights reserved.
4130812Smarcel *
5130812Smarcel * Redistribution and use in source and binary forms, with or without
6130812Smarcel * modification, are permitted provided that the following conditions
7130812Smarcel * are met:
8130812Smarcel * 1. Redistributions of source code must retain the above copyright
9130812Smarcel *    notice, this list of conditions and the following disclaimer.
10130812Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11130812Smarcel *    notice, this list of conditions and the following disclaimer in the
12130812Smarcel *    documentation and/or other materials provided with the distribution.
13130812Smarcel * 3. All advertising materials mentioning features or use of this software
14130812Smarcel *    must display the following acknowledgement:
15130812Smarcel *	This product includes software developed by the University of
16130812Smarcel *	California, Berkeley and its contributors.
17130812Smarcel * 4. Neither the name of the University nor the names of its contributors
18130812Smarcel *    may be used to endorse or promote products derived from this software
19130812Smarcel *    without specific prior written permission.
20130812Smarcel *
21130812Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22130812Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23130812Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24130812Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25130812Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26130812Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27130812Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28130812Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29130812Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30130812Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31130812Smarcel * SUCH DAMAGE.
32130812Smarcel */
33130812Smarcel
34130812Smarcel#ifndef lint
35130812Smarcelstatic const char sccsid[] = "@(#)dir.c	8.1 (Berkeley) 6/5/93";
36130812Smarcel#endif /* not lint */
37130812Smarcel
38130812Smarcel#include <sys/param.h>
39130812Smarcel#include <sys/time.h>
40130812Smarcel#include <ufs/ufs/dinode.h>
41130812Smarcel#include <ufs/ufs/dir.h>
42130812Smarcel#include <ufs/ffs/fs.h>
43130812Smarcel#include <stdio.h>
44130812Smarcel#include <stdlib.h>
45130812Smarcel#include <string.h>
46130812Smarcel#include "fsck.h"
47130812Smarcel
48130812Smarcelchar	*lfname = "lost+found";
49130812Smarcelint	lfmode = 01777;
50130812Smarcelstruct	dirtemplate emptydir = { 0, DIRBLKSIZ };
51130812Smarcelstruct	dirtemplate dirhead = {
52130812Smarcel	0, 12, DT_DIR, 1, ".",
53130812Smarcel	0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
54130812Smarcel};
55130812Smarcelstruct	odirtemplate odirhead = {
56130812Smarcel	0, 12, 1, ".",
57130812Smarcel	0, DIRBLKSIZ - 12, 2, ".."
58130812Smarcel};
59130812Smarcel
60130812Smarcel
61130812Smarcelstatic int	chgino __P((struct inodesc *idesc));
62130812Smarcelstatic int	dircheck __P((struct inodesc *idesc, struct direct *dp));
63130812Smarcelstatic int	expanddir __P((struct dinode *dp, char *name));
64130812Smarcelstatic void	freedir __P((ino_t ino, ino_t parent));
65130812Smarcelstatic struct direct * fsck_readdir __P((struct inodesc *idesc));
66130812Smarcelstatic struct bufarea * getdirblk __P((daddr_t blkno, long size));
67130812Smarcelstatic int	lftempname __P((char *bufp, ino_t ino));
68130812Smarcelstatic int	mkentry __P((struct inodesc *idesc));
69130812Smarcel
70130812Smarcel/*
71130812Smarcel * Propagate connected state through the tree.
72130812Smarcel */
73130812Smarcelvoid
74130812Smarcelpropagate()
75130812Smarcel{
76130812Smarcel	register struct inoinfo **inpp, *inp;
77130812Smarcel	struct inoinfo **inpend;
78130812Smarcel	long change;
79130812Smarcel
80130812Smarcel	inpend = &inpsort[inplast];
81130812Smarcel	do {
82130812Smarcel		change = 0;
83130812Smarcel		for (inpp = inpsort; inpp < inpend; inpp++) {
84130812Smarcel			inp = *inpp;
85130812Smarcel			if (inp->i_parent == 0)
86130812Smarcel				continue;
87130812Smarcel			if (statemap[inp->i_parent] == DFOUND &&
88130812Smarcel			    statemap[inp->i_number] == DSTATE) {
89130812Smarcel				statemap[inp->i_number] = DFOUND;
90130812Smarcel				change++;
91130812Smarcel			}
92130812Smarcel		}
93130812Smarcel	} while (change > 0);
94130812Smarcel}
95130812Smarcel
96130812Smarcel/*
97130812Smarcel * Scan each entry in a directory block.
98130812Smarcel */
99130812Smarcelint
100130812Smarceldirscan(idesc)
101130812Smarcel	register struct inodesc *idesc;
102130812Smarcel{
103130812Smarcel	register struct direct *dp;
104130812Smarcel	register struct bufarea *bp;
105130812Smarcel	int dsize, n;
106130812Smarcel	long blksiz;
107130812Smarcel	char dbuf[DIRBLKSIZ];
108130812Smarcel
109130812Smarcel	if (idesc->id_type != DATA)
110130812Smarcel		errexit("wrong type to dirscan %d\n", idesc->id_type);
111130812Smarcel	if (idesc->id_entryno == 0 &&
112130812Smarcel	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
113130812Smarcel		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
114130812Smarcel	blksiz = idesc->id_numfrags * sblock.fs_fsize;
115130812Smarcel	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
116130812Smarcel		idesc->id_filesize -= blksiz;
117130812Smarcel		return (SKIP);
118130812Smarcel	}
119130812Smarcel	idesc->id_loc = 0;
120130812Smarcel	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
121130812Smarcel		dsize = dp->d_reclen;
122130812Smarcel		bcopy((char *)dp, dbuf, (size_t)dsize);
123130812Smarcel#		if (BYTE_ORDER == LITTLE_ENDIAN)
124130812Smarcel			if (!newinofmt) {
125130812Smarcel				struct direct *tdp = (struct direct *)dbuf;
126130812Smarcel				u_char tmp;
127130812Smarcel
128130812Smarcel				tmp = tdp->d_namlen;
129130812Smarcel				tdp->d_namlen = tdp->d_type;
130130812Smarcel				tdp->d_type = tmp;
131130812Smarcel			}
132130812Smarcel#		endif
133130812Smarcel		idesc->id_dirp = (struct direct *)dbuf;
134130812Smarcel		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
135130812Smarcel#			if (BYTE_ORDER == LITTLE_ENDIAN)
136130812Smarcel				if (!newinofmt && !doinglevel2) {
137130812Smarcel					struct direct *tdp;
138130812Smarcel					u_char tmp;
139130812Smarcel
140130812Smarcel					tdp = (struct direct *)dbuf;
141130812Smarcel					tmp = tdp->d_namlen;
142130812Smarcel					tdp->d_namlen = tdp->d_type;
143130812Smarcel					tdp->d_type = tmp;
144130812Smarcel				}
145130812Smarcel#			endif
146130812Smarcel			bp = getdirblk(idesc->id_blkno, blksiz);
147130812Smarcel			bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize,
148130812Smarcel			    (size_t)dsize);
149130812Smarcel			dirty(bp);
150130812Smarcel			sbdirty();
151130812Smarcel		}
152130812Smarcel		if (n & STOP)
153130812Smarcel			return (n);
154130812Smarcel	}
155130812Smarcel	return (idesc->id_filesize > 0 ? KEEPON : STOP);
156130812Smarcel}
157130812Smarcel
158130812Smarcel/*
159130812Smarcel * get next entry in a directory.
160130812Smarcel */
161130812Smarcelstruct direct *
162130812Smarcelfsck_readdir(idesc)
163130812Smarcel	register struct inodesc *idesc;
164130812Smarcel{
165130812Smarcel	register struct direct *dp, *ndp;
166130812Smarcel	register struct bufarea *bp;
167130812Smarcel	long size, blksiz, fix, dploc;
168130812Smarcel
169130812Smarcel	blksiz = idesc->id_numfrags * sblock.fs_fsize;
170130812Smarcel	bp = getdirblk(idesc->id_blkno, blksiz);
171130812Smarcel	if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
172130812Smarcel	    idesc->id_loc < blksiz) {
173130812Smarcel		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
174130812Smarcel		if (dircheck(idesc, dp))
175130812Smarcel			goto dpok;
176130812Smarcel		fix = dofix(idesc, "DIRECTORY CORRUPTED");
177130812Smarcel		bp = getdirblk(idesc->id_blkno, blksiz);
178130812Smarcel		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
179130812Smarcel		dp->d_reclen = DIRBLKSIZ;
180130812Smarcel		dp->d_ino = 0;
181130812Smarcel		dp->d_type = 0;
182130812Smarcel		dp->d_namlen = 0;
183130812Smarcel		dp->d_name[0] = '\0';
184130812Smarcel		if (fix)
185130812Smarcel			dirty(bp);
186130812Smarcel		idesc->id_loc += DIRBLKSIZ;
187130812Smarcel		idesc->id_filesize -= DIRBLKSIZ;
188130812Smarcel		return (dp);
189130812Smarcel	}
190130812Smarceldpok:
191130812Smarcel	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
192130812Smarcel		return NULL;
193130812Smarcel	dploc = idesc->id_loc;
194130812Smarcel	dp = (struct direct *)(bp->b_un.b_buf + dploc);
195130812Smarcel	idesc->id_loc += dp->d_reclen;
196130812Smarcel	idesc->id_filesize -= dp->d_reclen;
197130812Smarcel	if ((idesc->id_loc % DIRBLKSIZ) == 0)
198130812Smarcel		return (dp);
199130812Smarcel	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
200130812Smarcel	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
201130812Smarcel	    dircheck(idesc, ndp) == 0) {
202130812Smarcel		size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
203130812Smarcel		idesc->id_loc += size;
204130812Smarcel		idesc->id_filesize -= size;
205130812Smarcel		fix = dofix(idesc, "DIRECTORY CORRUPTED");
206130812Smarcel		bp = getdirblk(idesc->id_blkno, blksiz);
207130812Smarcel		dp = (struct direct *)(bp->b_un.b_buf + dploc);
208130812Smarcel		dp->d_reclen += size;
209130812Smarcel		if (fix)
210130812Smarcel			dirty(bp);
211130812Smarcel	}
212130812Smarcel	return (dp);
213130812Smarcel}
214130812Smarcel
215130812Smarcel/*
216130812Smarcel * Verify that a directory entry is valid.
217130812Smarcel * This is a superset of the checks made in the kernel.
218130812Smarcel */
219130812Smarcelint
220130812Smarceldircheck(idesc, dp)
221130812Smarcel	struct inodesc *idesc;
222130812Smarcel	register struct direct *dp;
223130812Smarcel{
224130812Smarcel	register int size;
225130812Smarcel	register char *cp;
226130812Smarcel	u_char namlen, type;
227130812Smarcel	int spaceleft;
228130812Smarcel
229130812Smarcel	size = DIRSIZ(!newinofmt, dp);
230130812Smarcel	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
231130812Smarcel#	if (BYTE_ORDER == LITTLE_ENDIAN)
232130812Smarcel		if (!newinofmt) {
233130812Smarcel			type = dp->d_namlen;
234130812Smarcel			namlen = dp->d_type;
235130812Smarcel		} else {
236130812Smarcel			namlen = dp->d_namlen;
237130812Smarcel			type = dp->d_type;
238130812Smarcel		}
239130812Smarcel#	else
240130812Smarcel		namlen = dp->d_namlen;
241130812Smarcel		type = dp->d_type;
242130812Smarcel#	endif
243130812Smarcel	if (dp->d_ino < maxino &&
244130812Smarcel	    dp->d_reclen != 0 &&
245130812Smarcel	    dp->d_reclen <= spaceleft &&
246130812Smarcel	    (dp->d_reclen & 0x3) == 0 &&
247130812Smarcel	    dp->d_reclen >= size &&
248130812Smarcel	    idesc->id_filesize >= size &&
249130812Smarcel	    namlen <= MAXNAMLEN &&
250130812Smarcel	    type <= 15) {
251130812Smarcel		if (dp->d_ino == 0)
252130812Smarcel			return (1);
253130812Smarcel		for (cp = dp->d_name, size = 0; size < namlen; size++)
254130812Smarcel			if (*cp == 0 || (*cp++ == '/'))
255130812Smarcel				return (0);
256130812Smarcel		if (*cp == 0)
257130812Smarcel			return (1);
258130812Smarcel	}
259130812Smarcel	return (0);
260130812Smarcel}
261130812Smarcel
262130812Smarcelvoid
263130812Smarceldirerror(ino, errmesg)
264130812Smarcel	ino_t ino;
265130812Smarcel	char *errmesg;
266130812Smarcel{
267130812Smarcel
268130812Smarcel	fileerror(ino, ino, errmesg);
269130812Smarcel}
270130812Smarcel
271130812Smarcelvoid
272130812Smarcelfileerror(cwd, ino, errmesg)
273130812Smarcel	ino_t cwd, ino;
274130812Smarcel	char *errmesg;
275130812Smarcel{
276130812Smarcel	register struct dinode *dp;
277130812Smarcel	char pathbuf[MAXPATHLEN + 1];
278130812Smarcel
279130812Smarcel	pwarn("%s ", errmesg);
280130812Smarcel	pinode(ino);
281130812Smarcel	printf("\n");
282130812Smarcel	getpathname(pathbuf, cwd, ino);
283130812Smarcel	if (ino < ROOTINO || ino > maxino) {
284130812Smarcel		pfatal("NAME=%s\n", pathbuf);
285130812Smarcel		return;
286130812Smarcel	}
287130812Smarcel	dp = ginode(ino);
288130812Smarcel	if (ftypeok(dp))
289130812Smarcel		pfatal("%s=%s\n",
290130812Smarcel		    (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
291130812Smarcel	else
292130812Smarcel		pfatal("NAME=%s\n", pathbuf);
293130812Smarcel}
294130812Smarcel
295130812Smarcelvoid
296130812Smarceladjust(idesc, lcnt)
297130812Smarcel	register struct inodesc *idesc;
298130812Smarcel	short lcnt;
299130812Smarcel{
300130812Smarcel	register struct dinode *dp;
301130812Smarcel
302130812Smarcel	dp = ginode(idesc->id_number);
303130812Smarcel	if (dp->di_nlink == lcnt) {
304130812Smarcel		if (linkup(idesc->id_number, (ino_t)0) == 0)
305130812Smarcel			clri(idesc, "UNREF", 0);
306130812Smarcel	} else {
307130812Smarcel		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
308130812Smarcel			((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"));
309130812Smarcel		pinode(idesc->id_number);
310130812Smarcel		printf(" COUNT %d SHOULD BE %d",
311130812Smarcel			dp->di_nlink, dp->di_nlink - lcnt);
312130812Smarcel		if (preen) {
313130812Smarcel			if (lcnt < 0) {
314130812Smarcel				printf("\n");
315130812Smarcel				pfatal("LINK COUNT INCREASING");
316130812Smarcel			}
317130812Smarcel			printf(" (ADJUSTED)\n");
318130812Smarcel		}
319130812Smarcel		if (preen || reply("ADJUST") == 1) {
320130812Smarcel			dp->di_nlink -= lcnt;
321130812Smarcel			inodirty();
322130812Smarcel		}
323130812Smarcel	}
324130812Smarcel}
325130812Smarcel
326130812Smarcelint
327130812Smarcelmkentry(idesc)
328130812Smarcel	struct inodesc *idesc;
329130812Smarcel{
330130812Smarcel	register struct direct *dirp = idesc->id_dirp;
331130812Smarcel	struct direct newent;
332130812Smarcel	int newlen, oldlen;
333130812Smarcel
334130812Smarcel	newent.d_namlen = strlen(idesc->id_name);
335130812Smarcel	newlen = DIRSIZ(0, &newent);
336130812Smarcel	if (dirp->d_ino != 0)
337130812Smarcel		oldlen = DIRSIZ(0, dirp);
338130812Smarcel	else
339130812Smarcel		oldlen = 0;
340130812Smarcel	if (dirp->d_reclen - oldlen < newlen)
341130812Smarcel		return (KEEPON);
342130812Smarcel	newent.d_reclen = dirp->d_reclen - oldlen;
343130812Smarcel	dirp->d_reclen = oldlen;
344130812Smarcel	dirp = (struct direct *)(((char *)dirp) + oldlen);
345130812Smarcel	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
346130812Smarcel	if (newinofmt) {
347130812Smarcel		dirp->d_type = typemap[idesc->id_parent];
348130812Smarcel		dirp->d_namlen = newent.d_namlen;
349130812Smarcel	} else {
350130812Smarcel#		if (BYTE_ORDER == LITTLE_ENDIAN)
351130812Smarcel			dirp->d_type = newent.d_namlen;
352130812Smarcel			dirp->d_namlen = 0;
353130812Smarcel#		else
354130812Smarcel			dirp->d_type = 0;
355130812Smarcel			dirp->d_namlen = newent.d_namlen;
356130812Smarcel#		endif
357130812Smarcel	}
358130812Smarcel	dirp->d_reclen = newent.d_reclen;
359130812Smarcel	bcopy(idesc->id_name, dirp->d_name, (size_t)newent.d_namlen + 1);
360130812Smarcel	return (ALTERED|STOP);
361130812Smarcel}
362130812Smarcel
363130812Smarcelint
364130812Smarcelchgino(idesc)
365130812Smarcel	struct inodesc *idesc;
366130812Smarcel{
367130812Smarcel	register struct direct *dirp = idesc->id_dirp;
368130812Smarcel
369130812Smarcel	if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
370130812Smarcel		return (KEEPON);
371130812Smarcel	dirp->d_ino = idesc->id_parent;
372130812Smarcel	if (newinofmt)
373130812Smarcel		dirp->d_type = typemap[idesc->id_parent];
374130812Smarcel	else
375130812Smarcel		dirp->d_type = 0;
376130812Smarcel	return (ALTERED|STOP);
377130812Smarcel}
378130812Smarcel
379130812Smarcelint
380130812Smarcellinkup(orphan, parentdir)
381130812Smarcel	ino_t orphan;
382130812Smarcel	ino_t parentdir;
383130812Smarcel{
384130812Smarcel	register struct dinode *dp;
385130812Smarcel	int lostdir;
386130812Smarcel	ino_t oldlfdir;
387130812Smarcel	struct inodesc idesc;
388130812Smarcel	char tempname[BUFSIZ];
389130812Smarcel
390130812Smarcel	bzero((char *)&idesc, sizeof(struct inodesc));
391130812Smarcel	dp = ginode(orphan);
392130812Smarcel	lostdir = (dp->di_mode & IFMT) == IFDIR;
393130812Smarcel	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
394130812Smarcel	pinode(orphan);
395130812Smarcel	if (preen && dp->di_size == 0)
396130812Smarcel		return (0);
397130812Smarcel	if (preen)
398130812Smarcel		printf(" (RECONNECTED)\n");
399130812Smarcel	else
400130812Smarcel		if (reply("RECONNECT") == 0)
401130812Smarcel			return (0);
402130812Smarcel	if (lfdir == 0) {
403130812Smarcel		dp = ginode(ROOTINO);
404130812Smarcel		idesc.id_name = lfname;
405130812Smarcel		idesc.id_type = DATA;
406130812Smarcel		idesc.id_func = findino;
407130812Smarcel		idesc.id_number = ROOTINO;
408130812Smarcel		if ((ckinode(dp, &idesc) & FOUND) != 0) {
409130812Smarcel			lfdir = idesc.id_parent;
410130812Smarcel		} else {
411130812Smarcel			pwarn("NO lost+found DIRECTORY");
412130812Smarcel			if (preen || reply("CREATE")) {
413130812Smarcel				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
414130812Smarcel				if (lfdir != 0) {
415130812Smarcel					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
416130812Smarcel						if (preen)
417130812Smarcel							printf(" (CREATED)\n");
418130812Smarcel					} else {
419130812Smarcel						freedir(lfdir, ROOTINO);
420130812Smarcel						lfdir = 0;
421130812Smarcel						if (preen)
422130812Smarcel							printf("\n");
423130812Smarcel					}
424130812Smarcel				}
425130812Smarcel			}
426130812Smarcel		}
427130812Smarcel		if (lfdir == 0) {
428130812Smarcel			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
429130812Smarcel			printf("\n\n");
430130812Smarcel			return (0);
431130812Smarcel		}
432130812Smarcel	}
433130812Smarcel	dp = ginode(lfdir);
434130812Smarcel	if ((dp->di_mode & IFMT) != IFDIR) {
435130812Smarcel		pfatal("lost+found IS NOT A DIRECTORY");
436130812Smarcel		if (reply("REALLOCATE") == 0)
437130812Smarcel			return (0);
438130812Smarcel		oldlfdir = lfdir;
439130812Smarcel		if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
440130812Smarcel			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
441130812Smarcel			return (0);
442130812Smarcel		}
443130812Smarcel		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
444130812Smarcel			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
445130812Smarcel			return (0);
446130812Smarcel		}
447130812Smarcel		inodirty();
448130812Smarcel		idesc.id_type = ADDR;
449130812Smarcel		idesc.id_func = pass4check;
450130812Smarcel		idesc.id_number = oldlfdir;
451130812Smarcel		adjust(&idesc, lncntp[oldlfdir] + 1);
452130812Smarcel		lncntp[oldlfdir] = 0;
453130812Smarcel		dp = ginode(lfdir);
454130812Smarcel	}
455130812Smarcel	if (statemap[lfdir] != DFOUND) {
456130812Smarcel		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
457130812Smarcel		return (0);
458130812Smarcel	}
459130812Smarcel	(void)lftempname(tempname, orphan);
460130812Smarcel	if (makeentry(lfdir, orphan, tempname) == 0) {
461130812Smarcel		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
462130812Smarcel		printf("\n\n");
463130812Smarcel		return (0);
464130812Smarcel	}
465130812Smarcel	lncntp[orphan]--;
466130812Smarcel	if (lostdir) {
467130812Smarcel		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
468130812Smarcel		    parentdir != (ino_t)-1)
469130812Smarcel			(void)makeentry(orphan, lfdir, "..");
470130812Smarcel		dp = ginode(lfdir);
471130812Smarcel		dp->di_nlink++;
472130812Smarcel		inodirty();
473130812Smarcel		lncntp[lfdir]++;
474130812Smarcel		pwarn("DIR I=%lu CONNECTED. ", orphan);
475130812Smarcel		if (parentdir != (ino_t)-1)
476130812Smarcel			printf("PARENT WAS I=%lu\n", parentdir);
477130812Smarcel		if (preen == 0)
478130812Smarcel			printf("\n");
479130812Smarcel	}
480130812Smarcel	return (1);
481130812Smarcel}
482130812Smarcel
483130812Smarcel/*
484130812Smarcel * fix an entry in a directory.
485130812Smarcel */
486130812Smarcelint
487130812Smarcelchangeino(dir, name, newnum)
488130812Smarcel	ino_t dir;
489130812Smarcel	char *name;
490130812Smarcel	ino_t newnum;
491130812Smarcel{
492130812Smarcel	struct inodesc idesc;
493130812Smarcel
494130812Smarcel	bzero((char *)&idesc, sizeof(struct inodesc));
495130812Smarcel	idesc.id_type = DATA;
496130812Smarcel	idesc.id_func = chgino;
497130812Smarcel	idesc.id_number = dir;
498130812Smarcel	idesc.id_fix = DONTKNOW;
499130812Smarcel	idesc.id_name = name;
500130812Smarcel	idesc.id_parent = newnum;	/* new value for name */
501130812Smarcel	return (ckinode(ginode(dir), &idesc));
502130812Smarcel}
503130812Smarcel
504130812Smarcel/*
505130812Smarcel * make an entry in a directory
506130812Smarcel */
507130812Smarcelint
508130812Smarcelmakeentry(parent, ino, name)
509130812Smarcel	ino_t parent, ino;
510130812Smarcel	char *name;
511130812Smarcel{
512130812Smarcel	struct dinode *dp;
513130812Smarcel	struct inodesc idesc;
514130812Smarcel	char pathbuf[MAXPATHLEN + 1];
515130812Smarcel
516130812Smarcel	if (parent < ROOTINO || parent >= maxino ||
517130812Smarcel	    ino < ROOTINO || ino >= maxino)
518130812Smarcel		return (0);
519130812Smarcel	bzero((char *)&idesc, sizeof(struct inodesc));
520130812Smarcel	idesc.id_type = DATA;
521130812Smarcel	idesc.id_func = mkentry;
522130812Smarcel	idesc.id_number = parent;
523130812Smarcel	idesc.id_parent = ino;	/* this is the inode to enter */
524130812Smarcel	idesc.id_fix = DONTKNOW;
525130812Smarcel	idesc.id_name = name;
526130812Smarcel	dp = ginode(parent);
527130812Smarcel	if (dp->di_size % DIRBLKSIZ) {
528130812Smarcel		dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
529130812Smarcel		inodirty();
530130812Smarcel	}
531130812Smarcel	if ((ckinode(dp, &idesc) & ALTERED) != 0)
532130812Smarcel		return (1);
533130812Smarcel	getpathname(pathbuf, parent, parent);
534130812Smarcel	dp = ginode(parent);
535130812Smarcel	if (expanddir(dp, pathbuf) == 0)
536130812Smarcel		return (0);
537130812Smarcel	return (ckinode(dp, &idesc) & ALTERED);
538130812Smarcel}
539130812Smarcel
540130812Smarcel/*
541130812Smarcel * Attempt to expand the size of a directory
542130812Smarcel */
543130812Smarcelint
544130812Smarcelexpanddir(dp, name)
545130812Smarcel	register struct dinode *dp;
546130812Smarcel	char *name;
547130812Smarcel{
548130812Smarcel	daddr_t lastbn, newblk;
549130812Smarcel	register struct bufarea *bp;
550130812Smarcel	char *cp, firstblk[DIRBLKSIZ];
551130812Smarcel
552130812Smarcel	lastbn = lblkno(&sblock, dp->di_size);
553130812Smarcel	if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0)
554130812Smarcel		return (0);
555130812Smarcel	if ((newblk = allocblk(sblock.fs_frag)) == 0)
556130812Smarcel		return (0);
557130812Smarcel	dp->di_db[lastbn + 1] = dp->di_db[lastbn];
558130812Smarcel	dp->di_db[lastbn] = newblk;
559130812Smarcel	dp->di_size += sblock.fs_bsize;
560130812Smarcel	dp->di_blocks += btodb(sblock.fs_bsize);
561130812Smarcel	bp = getdirblk(dp->di_db[lastbn + 1],
562130812Smarcel		(long)dblksize(&sblock, dp, lastbn + 1));
563130812Smarcel	if (bp->b_errs)
564130812Smarcel		goto bad;
565130812Smarcel	bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
566130812Smarcel	bp = getdirblk(newblk, sblock.fs_bsize);
567130812Smarcel	if (bp->b_errs)
568130812Smarcel		goto bad;
569130812Smarcel	bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
570130812Smarcel	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
571130812Smarcel	     cp < &bp->b_un.b_buf[sblock.fs_bsize];
572130812Smarcel	     cp += DIRBLKSIZ)
573130812Smarcel		bcopy((char *)&emptydir, cp, sizeof emptydir);
574130812Smarcel	dirty(bp);
575130812Smarcel	bp = getdirblk(dp->di_db[lastbn + 1],
576130812Smarcel		(long)dblksize(&sblock, dp, lastbn + 1));
577130812Smarcel	if (bp->b_errs)
578130812Smarcel		goto bad;
579130812Smarcel	bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir);
580130812Smarcel	pwarn("NO SPACE LEFT IN %s", name);
581130812Smarcel	if (preen)
582130812Smarcel		printf(" (EXPANDED)\n");
583130812Smarcel	else if (reply("EXPAND") == 0)
584130812Smarcel		goto bad;
585130812Smarcel	dirty(bp);
586130812Smarcel	inodirty();
587130812Smarcel	return (1);
588130812Smarcelbad:
589130812Smarcel	dp->di_db[lastbn] = dp->di_db[lastbn + 1];
590130812Smarcel	dp->di_db[lastbn + 1] = 0;
591130812Smarcel	dp->di_size -= sblock.fs_bsize;
592130812Smarcel	dp->di_blocks -= btodb(sblock.fs_bsize);
593130812Smarcel	freeblk(newblk, sblock.fs_frag);
594130812Smarcel	return (0);
595130812Smarcel}
596130812Smarcel
597130812Smarcel/*
598130812Smarcel * allocate a new directory
599130812Smarcel */
600130812Smarcelino_t
601130812Smarcelallocdir(parent, request, mode)
602130812Smarcel	ino_t parent, request;
603130812Smarcel	int mode;
604130812Smarcel{
605130812Smarcel	ino_t ino;
606130812Smarcel	char *cp;
607130812Smarcel	struct dinode *dp;
608130812Smarcel	register struct bufarea *bp;
609130812Smarcel	struct dirtemplate *dirp;
610130812Smarcel
611130812Smarcel	ino = allocino(request, IFDIR|mode);
612130812Smarcel	if (newinofmt)
613130812Smarcel		dirp = &dirhead;
614130812Smarcel	else
615130812Smarcel		dirp = (struct dirtemplate *)&odirhead;
616130812Smarcel	dirp->dot_ino = ino;
617130812Smarcel	dirp->dotdot_ino = parent;
618130812Smarcel	dp = ginode(ino);
619130812Smarcel	bp = getdirblk(dp->di_db[0], sblock.fs_fsize);
620130812Smarcel	if (bp->b_errs) {
621130812Smarcel		freeino(ino);
622130812Smarcel		return (0);
623130812Smarcel	}
624130812Smarcel	bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate));
625130812Smarcel	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
626130812Smarcel	     cp < &bp->b_un.b_buf[sblock.fs_fsize];
627130812Smarcel	     cp += DIRBLKSIZ)
628130812Smarcel		bcopy((char *)&emptydir, cp, sizeof emptydir);
629130812Smarcel	dirty(bp);
630130812Smarcel	dp->di_nlink = 2;
631130812Smarcel	inodirty();
632130812Smarcel	if (ino == ROOTINO) {
633130812Smarcel		lncntp[ino] = dp->di_nlink;
634130812Smarcel		cacheino(dp, ino);
635130812Smarcel		return(ino);
636130812Smarcel	}
637130812Smarcel	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
638130812Smarcel		freeino(ino);
639130812Smarcel		return (0);
640130812Smarcel	}
641130812Smarcel	cacheino(dp, ino);
642130812Smarcel	statemap[ino] = statemap[parent];
643130812Smarcel	if (statemap[ino] == DSTATE) {
644130812Smarcel		lncntp[ino] = dp->di_nlink;
645130812Smarcel		lncntp[parent]++;
646130812Smarcel	}
647130812Smarcel	dp = ginode(parent);
648130812Smarcel	dp->di_nlink++;
649130812Smarcel	inodirty();
650130812Smarcel	return (ino);
651130812Smarcel}
652130812Smarcel
653130812Smarcel/*
654130812Smarcel * free a directory inode
655130812Smarcel */
656130812Smarcelstatic void
657130812Smarcelfreedir(ino, parent)
658130812Smarcel	ino_t ino, parent;
659130812Smarcel{
660130812Smarcel	struct dinode *dp;
661130812Smarcel
662130812Smarcel	if (ino != parent) {
663130812Smarcel		dp = ginode(parent);
664130812Smarcel		dp->di_nlink--;
665130812Smarcel		inodirty();
666130812Smarcel	}
667130812Smarcel	freeino(ino);
668130812Smarcel}
669130812Smarcel
670130812Smarcel/*
671130812Smarcel * generate a temporary name for the lost+found directory.
672130812Smarcel */
673130812Smarcelint
674130812Smarcellftempname(bufp, ino)
675130812Smarcel	char *bufp;
676130812Smarcel	ino_t ino;
677130812Smarcel{
678130812Smarcel	register ino_t in;
679130812Smarcel	register char *cp;
680130812Smarcel	int namlen;
681130812Smarcel
682130812Smarcel	cp = bufp + 2;
683130812Smarcel	for (in = maxino; in > 0; in /= 10)
684130812Smarcel		cp++;
685130812Smarcel	*--cp = 0;
686130812Smarcel	namlen = cp - bufp;
687130812Smarcel	in = ino;
688130812Smarcel	while (cp > bufp) {
689130812Smarcel		*--cp = (in % 10) + '0';
690130812Smarcel		in /= 10;
691130812Smarcel	}
692130812Smarcel	*cp = '#';
693130812Smarcel	return (namlen);
694130812Smarcel}
695130812Smarcel
696130812Smarcel/*
697130812Smarcel * Get a directory block.
698130812Smarcel * Insure that it is held until another is requested.
699130812Smarcel */
700130812Smarcelstruct bufarea *
701130812Smarcelgetdirblk(blkno, size)
702130812Smarcel	daddr_t blkno;
703130812Smarcel	long size;
704130812Smarcel{
705130812Smarcel
706130812Smarcel	if (pdirbp != 0)
707130812Smarcel		pdirbp->b_flags &= ~B_INUSE;
708130812Smarcel	pdirbp = getdatablk(blkno, size);
709130812Smarcel	return (pdirbp);
710130812Smarcel}
711130812Smarcel