1/*	$NetBSD$	*/
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. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)dir.c	8.8 (Berkeley) 4/28/95";
36#else
37__RCSID("$NetBSD$");
38#endif
39#endif /* not lint */
40
41#include <sys/param.h>
42#include <sys/time.h>
43
44#include <ufs/ufs/dinode.h>
45#include <ufs/ufs/dir.h>
46#include <ufs/ffs/fs.h>
47#include <ufs/ffs/ffs_extern.h>
48
49#include <err.h>
50#include <stdio.h>
51#include <string.h>
52
53#include "fsck.h"
54#include "fsutil.h"
55#include "extern.h"
56
57const char	*lfname = "lost+found";
58int	lfmode = 01700;
59ino_t	lfdir;
60struct	dirtemplate emptydir = {
61	.dot_ino = 0,
62	.dot_reclen = DIRBLKSIZ,
63};
64struct	dirtemplate dirhead = {
65	.dot_ino = 0,
66	.dot_reclen = 12,
67	.dot_type = DT_DIR,
68	.dot_namlen = 1,
69	.dot_name = ".",
70	.dotdot_ino = 0,
71	.dotdot_reclen = DIRBLKSIZ - 12,
72	.dotdot_type = DT_DIR,
73	.dotdot_namlen = 2,
74	.dotdot_name = "..",
75};
76struct	odirtemplate odirhead = {
77	.dot_ino = 0,
78	.dot_reclen = 12,
79	.dot_namlen = 1,
80	.dot_name = ".",
81	.dotdot_ino = 0,
82	.dotdot_reclen = DIRBLKSIZ - 12,
83	.dotdot_namlen = 2,
84	.dotdot_name = "..",
85};
86
87static int chgino(struct  inodesc *);
88static int dircheck(struct inodesc *, struct direct *);
89static int expanddir(union dinode *, char *);
90static void freedir(ino_t, ino_t);
91static struct direct *fsck_readdir(struct inodesc *);
92static struct bufarea *getdirblk(daddr_t, long);
93static int lftempname(char *, ino_t);
94static int mkentry(struct inodesc *);
95void reparent(ino_t, ino_t);
96
97/*
98 * Propagate connected state through the tree.
99 */
100void
101propagate(ino_t inumber)
102{
103	struct inoinfo *inp;
104
105	inp = getinoinfo(inumber);
106
107	for (;;) {
108		inoinfo(inp->i_number)->ino_state = DMARK;
109		if (inp->i_child &&
110		    inoinfo(inp->i_child->i_number)->ino_state != DMARK)
111			inp = inp->i_child;
112		else if (inp->i_number == inumber)
113			break;
114		else if (inp->i_sibling)
115			inp = inp->i_sibling;
116		else
117			inp = getinoinfo(inp->i_parent);
118	}
119
120	for (;;) {
121		inoinfo(inp->i_number)->ino_state = DFOUND;
122		if (inp->i_child &&
123		    inoinfo(inp->i_child->i_number)->ino_state != DFOUND)
124			inp = inp->i_child;
125		else if (inp->i_number == inumber)
126			break;
127		else if (inp->i_sibling)
128			inp = inp->i_sibling;
129		else
130			inp = getinoinfo(inp->i_parent);
131	}
132}
133
134void
135reparent(ino_t inumber, ino_t parent)
136{
137	struct inoinfo *inp, *pinp;
138
139	inp = getinoinfo(inumber);
140	inp->i_parent = inp->i_dotdot = parent;
141	pinp = getinoinfo(parent);
142	inp->i_sibling = pinp->i_child;
143	pinp->i_child = inp;
144	propagate(inumber);
145}
146
147/*
148 * Scan each entry in a directory block.
149 */
150int
151dirscan(struct inodesc *idesc)
152{
153	struct direct *dp;
154	struct bufarea *bp;
155	int dsize, n;
156	long blksiz;
157#if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
158	char dbuf[DIRBLKSIZ];
159#else
160	char dbuf[APPLEUFS_DIRBLKSIZ];
161#endif
162
163	if (idesc->id_type != DATA)
164		errexit("wrong type to dirscan %d", idesc->id_type);
165	if (idesc->id_entryno == 0 &&
166	    (idesc->id_filesize & (dirblksiz - 1)) != 0)
167		idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz);
168	blksiz = idesc->id_numfrags * sblock->fs_fsize;
169	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
170		idesc->id_filesize -= blksiz;
171		return (SKIP);
172	}
173
174	/*
175	 * If we are are swapping byte order in directory entries, just swap
176	 * this block and return.
177	 */
178	if (do_dirswap) {
179		int off;
180		bp = getdirblk(idesc->id_blkno, blksiz);
181		for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) {
182			dp = (struct direct *)(bp->b_un.b_buf + off);
183			dp->d_ino = bswap32(dp->d_ino);
184			dp->d_reclen = bswap16(dp->d_reclen);
185			if (!newinofmt) {
186				u_int8_t tmp = dp->d_namlen;
187				dp->d_namlen = dp->d_type;
188				dp->d_type = tmp;
189			}
190			if (dp->d_reclen == 0)
191				break;
192		}
193		dirty(bp);
194		idesc->id_filesize -= blksiz;
195		return (idesc->id_filesize > 0 ? KEEPON : STOP);
196	}
197
198	idesc->id_loc = 0;
199	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
200		dsize = iswap16(dp->d_reclen);
201		if (dsize > (int)sizeof dbuf)
202			dsize = sizeof dbuf;
203		memmove(dbuf, dp, (size_t)dsize);
204#		if (BYTE_ORDER == LITTLE_ENDIAN)
205			if (!newinofmt && !needswap) {
206#		else
207			if (!newinofmt && needswap) {
208#		endif
209				struct direct *tdp = (struct direct *)dbuf;
210				u_char tmp;
211
212				tmp = tdp->d_namlen;
213				tdp->d_namlen = tdp->d_type;
214				tdp->d_type = tmp;
215			}
216		idesc->id_dirp = (struct direct *)dbuf;
217		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
218#			if (BYTE_ORDER == LITTLE_ENDIAN)
219				if (!newinofmt && !doinglevel2 && !needswap) {
220#			else
221				if (!newinofmt && !doinglevel2 && needswap) {
222#			endif
223					struct direct *tdp;
224					u_char tmp;
225
226					tdp = (struct direct *)dbuf;
227					tmp = tdp->d_namlen;
228					tdp->d_namlen = tdp->d_type;
229					tdp->d_type = tmp;
230				}
231			bp = getdirblk(idesc->id_blkno, blksiz);
232			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
233			    (size_t)dsize);
234			dirty(bp);
235			sbdirty();
236		}
237		if (n & STOP)
238			return (n);
239	}
240	return (idesc->id_filesize > 0 ? KEEPON : STOP);
241}
242
243/*
244 * get next entry in a directory.
245 */
246static struct direct *
247fsck_readdir(struct inodesc *idesc)
248{
249	struct direct *dp, *ndp;
250	struct bufarea *bp;
251	long size, blksiz, fix, dploc;
252
253	blksiz = idesc->id_numfrags * sblock->fs_fsize;
254	bp = getdirblk(idesc->id_blkno, blksiz);
255	if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
256	    idesc->id_loc < blksiz) {
257		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
258		if (dircheck(idesc, dp))
259			goto dpok;
260		if (idesc->id_fix == IGNORE)
261			return (0);
262		fix = dofix(idesc, "DIRECTORY CORRUPTED");
263		bp = getdirblk(idesc->id_blkno, blksiz);
264		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
265		dp->d_reclen = iswap16(dirblksiz);
266		dp->d_ino = 0;
267		dp->d_type = 0;
268		dp->d_namlen = 0;
269		dp->d_name[0] = '\0';
270		if (fix)
271			dirty(bp);
272		else
273			markclean = 0;
274		idesc->id_loc += dirblksiz;
275		idesc->id_filesize -= dirblksiz;
276		return (dp);
277	}
278dpok:
279	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
280		return NULL;
281	dploc = idesc->id_loc;
282	dp = (struct direct *)(bp->b_un.b_buf + dploc);
283	idesc->id_loc += iswap16(dp->d_reclen);
284	idesc->id_filesize -= iswap16(dp->d_reclen);
285	if ((idesc->id_loc % dirblksiz) == 0)
286		return (dp);
287	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
288	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
289	    dircheck(idesc, ndp) == 0) {
290		size = dirblksiz - (idesc->id_loc % dirblksiz);
291		idesc->id_loc += size;
292		idesc->id_filesize -= size;
293		if (idesc->id_fix == IGNORE)
294			return (0);
295		fix = dofix(idesc, "DIRECTORY CORRUPTED");
296		bp = getdirblk(idesc->id_blkno, blksiz);
297		dp = (struct direct *)(bp->b_un.b_buf + dploc);
298		dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
299		if (fix)
300			dirty(bp);
301		else
302			markclean = 0;
303	}
304	return (dp);
305}
306
307/*
308 * Verify that a directory entry is valid.
309 * This is a superset of the checks made in the kernel.
310 */
311static int
312dircheck(struct inodesc *idesc, struct direct *dp)
313{
314	int size;
315	char *cp;
316	u_char namlen, type;
317	int spaceleft;
318
319	spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
320	if (iswap32(dp->d_ino) >= maxino ||
321	    dp->d_reclen == 0 ||
322	    iswap16(dp->d_reclen) > spaceleft ||
323	    (iswap16(dp->d_reclen) & 0x3) != 0)
324		return (0);
325	if (dp->d_ino == 0)
326		return (1);
327	size = DIRSIZ(!newinofmt, dp, needswap);
328#	if (BYTE_ORDER == LITTLE_ENDIAN)
329		if (!newinofmt && !needswap) {
330#	else
331		if (!newinofmt && needswap) {
332#	endif
333			type = dp->d_namlen;
334			namlen = dp->d_type;
335		} else {
336			namlen = dp->d_namlen;
337			type = dp->d_type;
338		}
339	if (iswap16(dp->d_reclen) < size ||
340	    idesc->id_filesize < size ||
341	    /* namlen > MAXNAMLEN || */
342	    type > 15)
343		return (0);
344	for (cp = dp->d_name, size = 0; size < namlen; size++)
345		if (*cp == '\0' || (*cp++ == '/'))
346			return (0);
347	if (*cp != '\0')
348		return (0);
349	return (1);
350}
351
352void
353direrror(ino_t ino, const char *errmesg)
354{
355
356	fileerror(ino, ino, errmesg);
357}
358
359void
360fileerror(ino_t cwd, ino_t ino, const char *errmesg)
361{
362	union dinode *dp;
363	char pathbuf[MAXPATHLEN + 1];
364	uint16_t mode;
365
366	pwarn("%s ", errmesg);
367	pinode(ino);
368	printf("\n");
369	getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
370	if (ino < ROOTINO || ino > maxino) {
371		pfatal("NAME=%s\n", pathbuf);
372		return;
373	}
374	dp = ginode(ino);
375	if (ftypeok(dp)) {
376		mode = DIP(dp, mode);
377		pfatal("%s=%s\n",
378		    (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
379	}
380	else
381		pfatal("NAME=%s\n", pathbuf);
382}
383
384void
385adjust(struct inodesc *idesc, int lcnt)
386{
387	union dinode *dp;
388	int16_t nlink;
389	int saveresolved;
390
391	dp = ginode(idesc->id_number);
392	nlink = iswap16(DIP(dp, nlink));
393	if (nlink == lcnt) {
394		/*
395		 * If we have not hit any unresolved problems, are running
396		 * in preen mode, and are on a file system using soft updates,
397		 * then just toss any partially allocated files.
398		 */
399		if (resolved && preen && usedsoftdep) {
400			clri(idesc, "UNREF", 1);
401			return;
402		} else {
403			/*
404			 * The file system can be marked clean even if
405			 * a file is not linked up, but is cleared.
406			 * Hence, resolved should not be cleared when
407			 * linkup is answered no, but clri is answered yes.
408			 */
409			saveresolved = resolved;
410			if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
411				resolved = saveresolved;
412				clri(idesc, "UNREF", 0);
413				return;
414			}
415			/*
416			 * Account for the new reference created by linkup().
417			 */
418			dp = ginode(idesc->id_number);
419			lcnt--;
420		}
421	}
422	if (lcnt != 0) {
423		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
424			((iswap16(DIP(dp, mode)) & IFMT) == IFDIR ?
425			"DIR" : "FILE"));
426		pinode(idesc->id_number);
427		printf(" COUNT %d SHOULD BE %d",
428			nlink, nlink - lcnt);
429		if (preen || usedsoftdep) {
430			if (lcnt < 0) {
431				printf("\n");
432				pfatal("LINK COUNT INCREASING");
433			}
434			if (preen)
435				printf(" (ADJUSTED)\n");
436		}
437		if (preen || reply("ADJUST") == 1) {
438			DIP_SET(dp, nlink, iswap16(nlink - lcnt));
439			inodirty();
440		} else
441			markclean = 0;
442	}
443}
444
445static int
446mkentry(struct inodesc *idesc)
447{
448	struct direct *dirp = idesc->id_dirp;
449	struct direct newent;
450	int newlen, oldlen;
451
452	newent.d_namlen = strlen(idesc->id_name);
453	newlen = DIRSIZ(0, &newent, 0);
454	if (dirp->d_ino != 0)
455		oldlen = DIRSIZ(0, dirp, 0);
456	else
457		oldlen = 0;
458	if (iswap16(dirp->d_reclen) - oldlen < newlen)
459		return (KEEPON);
460	newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen);
461	dirp->d_reclen = iswap16(oldlen);
462	dirp = (struct direct *)(((char *)dirp) + oldlen);
463	/* ino to be entered is in id_parent */
464	dirp->d_ino = iswap32(idesc->id_parent);
465	dirp->d_reclen = newent.d_reclen;
466	if (newinofmt)
467		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
468	else
469		dirp->d_type = 0;
470	dirp->d_namlen = newent.d_namlen;
471	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
472#	if (BYTE_ORDER == LITTLE_ENDIAN)
473		/*
474		 * If the entry was split, dirscan() will only reverse the byte
475		 * order of the original entry, and not the new one, before
476		 * writing it back out.  So, we reverse the byte order here if
477		 * necessary.
478		 */
479		if (oldlen != 0 && !newinofmt && !doinglevel2 && !needswap) {
480#	else
481		if (oldlen != 0 && !newinofmt && !doinglevel2 && needswap) {
482#	endif
483			u_char tmp;
484
485			tmp = dirp->d_namlen;
486			dirp->d_namlen = dirp->d_type;
487			dirp->d_type = tmp;
488		}
489	return (ALTERED|STOP);
490}
491
492static int
493chgino(struct inodesc *idesc)
494{
495	struct direct *dirp = idesc->id_dirp;
496
497	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
498		return (KEEPON);
499	dirp->d_ino = iswap32(idesc->id_parent);
500	if (newinofmt)
501		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
502	else
503		dirp->d_type = 0;
504	return (ALTERED|STOP);
505}
506
507int
508linkup(ino_t orphan, ino_t parentdir, char *name)
509{
510	union dinode *dp;
511	int lostdir;
512	ino_t oldlfdir;
513	struct inodesc idesc;
514	char tempname[BUFSIZ];
515	int16_t nlink;
516	uint16_t mode;
517
518	memset(&idesc, 0, sizeof(struct inodesc));
519	dp = ginode(orphan);
520	mode = iswap16(DIP(dp, mode));
521	nlink = iswap16(DIP(dp, nlink));
522	lostdir = (mode & IFMT) == IFDIR;
523	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
524	pinode(orphan);
525	if (preen  && DIP(dp, size) == 0)
526		return (0);
527	if (preen)
528		printf(" (RECONNECTED)\n");
529	else
530		if (reply("RECONNECT") == 0) {
531			markclean = 0;
532			return (0);
533		}
534	if (parentdir != 0)
535		inoinfo(parentdir)->ino_linkcnt++;
536	if (lfdir == 0) {
537		dp = ginode(ROOTINO);
538		idesc.id_name = lfname;
539		idesc.id_type = DATA;
540		idesc.id_func = findino;
541		idesc.id_number = ROOTINO;
542		idesc.id_uid = iswap32(DIP(dp, uid));
543		idesc.id_gid = iswap32(DIP(dp, gid));
544		if ((ckinode(dp, &idesc) & FOUND) != 0) {
545			lfdir = idesc.id_parent;
546		} else {
547			pwarn("NO lost+found DIRECTORY");
548			if (preen || reply("CREATE")) {
549				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
550				if (lfdir != 0) {
551					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
552						numdirs++;
553						if (preen)
554							printf(" (CREATED)\n");
555					} else {
556						freedir(lfdir, ROOTINO);
557						lfdir = 0;
558						if (preen)
559							printf("\n");
560					}
561				}
562				if (lfdir != 0) {
563					reparent(lfdir, ROOTINO);
564				}
565			}
566		}
567		if (lfdir == 0) {
568			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
569			markclean = 0;
570			return (0);
571		}
572	}
573	dp = ginode(lfdir);
574	mode = DIP(dp, mode);
575	mode = iswap16(mode);
576	if ((mode & IFMT) != IFDIR) {
577		pfatal("lost+found IS NOT A DIRECTORY");
578		if (reply("REALLOCATE") == 0) {
579			markclean = 0;
580			return (0);
581		}
582		oldlfdir = lfdir;
583		lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
584		if (lfdir == 0) {
585			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
586			markclean = 0;
587			return (0);
588		}
589		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
590			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
591			markclean = 0;
592			return (0);
593		}
594		inodirty();
595		reparent(lfdir, ROOTINO);
596		idesc.id_type = ADDR;
597		idesc.id_func = pass4check;
598		idesc.id_number = oldlfdir;
599		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
600		inoinfo(oldlfdir)->ino_linkcnt = 0;
601		dp = ginode(lfdir);
602	}
603	if (inoinfo(lfdir)->ino_state != DFOUND) {
604		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
605		markclean = 0;
606		return (0);
607	}
608	(void)lftempname(tempname, orphan);
609	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
610		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
611		printf("\n\n");
612		markclean = 0;
613		return (0);
614	}
615	inoinfo(orphan)->ino_linkcnt--;
616	if (lostdir) {
617		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
618		    parentdir != (ino_t)-1)
619			(void)makeentry(orphan, lfdir, "..");
620		dp = ginode(lfdir);
621		nlink = DIP(dp, nlink);
622		DIP_SET(dp, nlink, iswap16(iswap16(nlink) + 1));
623		inodirty();
624		inoinfo(lfdir)->ino_linkcnt++;
625		reparent(orphan, lfdir);
626		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
627		if (parentdir != (ino_t)-1)
628			printf("PARENT WAS I=%llu\n",
629			    (unsigned long long)parentdir);
630		if (preen == 0)
631			printf("\n");
632	}
633	return (1);
634}
635
636/*
637 * fix an entry in a directory.
638 */
639int
640changeino(ino_t dir, const char *name, ino_t newnum)
641{
642	struct inodesc idesc;
643	union dinode *dp;
644
645	dp = ginode(dir);
646	memset(&idesc, 0, sizeof(struct inodesc));
647	idesc.id_type = DATA;
648	idesc.id_func = chgino;
649	idesc.id_number = dir;
650	idesc.id_fix = DONTKNOW;
651	idesc.id_name = name;
652	idesc.id_parent = newnum;	/* new value for name */
653	idesc.id_uid = iswap32(DIP(dp, uid));
654	idesc.id_gid = iswap32(DIP(dp, gid));
655	return (ckinode(dp, &idesc));
656}
657
658/*
659 * make an entry in a directory
660 */
661int
662makeentry(ino_t parent, ino_t ino, const char *name)
663{
664	union dinode *dp;
665	struct inodesc idesc;
666	char pathbuf[MAXPATHLEN + 1];
667
668	if (parent < ROOTINO || parent >= maxino ||
669	    ino < ROOTINO || ino >= maxino)
670		return (0);
671	dp = ginode(parent);
672	memset(&idesc, 0, sizeof(struct inodesc));
673	idesc.id_type = DATA;
674	idesc.id_func = mkentry;
675	idesc.id_number = parent;
676	idesc.id_parent = ino;	/* this is the inode to enter */
677	idesc.id_fix = DONTKNOW;
678	idesc.id_name = name;
679	idesc.id_uid = iswap32(DIP(dp, uid));
680	idesc.id_gid = iswap32(DIP(dp, gid));
681	if (iswap64(DIP(dp, size)) % dirblksiz) {
682		DIP_SET(dp, size,
683		    iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz)));
684		inodirty();
685	}
686	if ((ckinode(dp, &idesc) & ALTERED) != 0)
687		return (1);
688	getpathname(pathbuf, sizeof(pathbuf), parent, parent);
689	dp = ginode(parent);
690	if (expanddir(dp, pathbuf) == 0)
691		return (0);
692	update_uquot(idesc.id_number, idesc.id_uid, idesc.id_gid,
693	    btodb(sblock->fs_bsize), 0);
694	return (ckinode(dp, &idesc) & ALTERED);
695}
696
697/*
698 * Attempt to expand the size of a directory
699 */
700static int
701expanddir(union dinode *dp, char *name)
702{
703	daddr_t lastbn, newblk, dirblk;
704	struct bufarea *bp;
705	char *cp;
706#if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
707	char firstblk[DIRBLKSIZ];
708#else
709	char firstblk[APPLEUFS_DIRBLKSIZ];
710#endif
711	struct ufs1_dinode *dp1 = NULL;
712	struct ufs2_dinode *dp2 = NULL;
713
714	if (is_ufs2)
715		dp2 = &dp->dp2;
716	else
717		dp1 = &dp->dp1;
718
719	lastbn = lblkno(sblock, iswap64(DIP(dp, size)));
720	if (lastbn >= NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
721	    DIP(dp, size) == 0)
722		return (0);
723	if ((newblk = allocblk(sblock->fs_frag)) == 0)
724		return (0);
725	if (is_ufs2) {
726		dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
727		dp2->di_db[lastbn] = iswap64(newblk);
728		dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
729		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
730		    btodb(sblock->fs_bsize));
731		dirblk = iswap64(dp2->di_db[lastbn + 1]);
732	} else {
733		dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
734		dp1->di_db[lastbn] = iswap32((int32_t)newblk);
735		dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
736		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
737		    btodb(sblock->fs_bsize));
738		dirblk = iswap32(dp1->di_db[lastbn + 1]);
739	}
740	bp = getdirblk(dirblk, sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
741	if (bp->b_errs)
742		goto bad;
743	memmove(firstblk, bp->b_un.b_buf, dirblksiz);
744	bp = getdirblk(newblk, sblock->fs_bsize);
745	if (bp->b_errs)
746		goto bad;
747	memmove(bp->b_un.b_buf, firstblk, dirblksiz);
748	emptydir.dot_reclen = iswap16(dirblksiz);
749	for (cp = &bp->b_un.b_buf[dirblksiz];
750	     cp < &bp->b_un.b_buf[sblock->fs_bsize];
751	     cp += dirblksiz)
752		memmove(cp, &emptydir, sizeof emptydir);
753	dirty(bp);
754	bp = getdirblk(dirblk, sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
755	if (bp->b_errs)
756		goto bad;
757	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
758	pwarn("NO SPACE LEFT IN %s", name);
759	if (preen)
760		printf(" (EXPANDED)\n");
761	else if (reply("EXPAND") == 0)
762		goto bad;
763	dirty(bp);
764	inodirty();
765	return (1);
766bad:
767	if (is_ufs2) {
768		dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
769		dp2->di_db[lastbn + 1] = 0;
770		dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
771		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
772		    btodb(sblock->fs_bsize));
773	} else {
774		dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
775		dp1->di_db[lastbn + 1] = 0;
776		dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
777		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
778		    btodb(sblock->fs_bsize));
779	}
780	freeblk(newblk, sblock->fs_frag);
781	markclean = 0;
782	return (0);
783}
784
785/*
786 * allocate a new directory
787 */
788ino_t
789allocdir(ino_t parent, ino_t request, int mode)
790{
791	ino_t ino;
792	char *cp;
793	union dinode *dp;
794	struct bufarea *bp;
795	struct inoinfo *inp;
796	struct dirtemplate *dirp;
797	daddr_t dirblk;
798
799	ino = allocino(request, IFDIR|mode);
800	if (ino < ROOTINO)
801		return 0;
802	update_uquot(ino, 0, 0, btodb(sblock->fs_fsize), 1);
803	dirhead.dot_reclen = iswap16(12);
804	dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
805	odirhead.dot_reclen = iswap16(12);
806	odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
807	odirhead.dot_namlen = iswap16(1);
808	odirhead.dotdot_namlen = iswap16(2);
809	if (newinofmt)
810		dirp = &dirhead;
811	else
812		dirp = (struct dirtemplate *)&odirhead;
813	dirp->dot_ino = iswap32(ino);
814	dirp->dotdot_ino = iswap32(parent);
815	dp = ginode(ino);
816	dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
817		    : iswap32(dp->dp1.di_db[0]);
818	bp = getdirblk(dirblk, sblock->fs_fsize);
819	if (bp->b_errs) {
820		freeino(ino);
821		return (0);
822	}
823	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
824	emptydir.dot_reclen = iswap16(dirblksiz);
825	for (cp = &bp->b_un.b_buf[dirblksiz];
826	     cp < &bp->b_un.b_buf[sblock->fs_fsize];
827	     cp += dirblksiz)
828		memmove(cp, &emptydir, sizeof emptydir);
829	dirty(bp);
830	DIP_SET(dp, nlink, iswap16(2));
831	inodirty();
832	if (ino == ROOTINO) {
833		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
834		cacheino(dp, ino);
835		return(ino);
836	}
837	if (inoinfo(parent)->ino_state != DSTATE &&
838	    inoinfo(parent)->ino_state != DFOUND) {
839		freeino(ino);
840		return (0);
841	}
842	cacheino(dp, ino);
843	inp = getinoinfo(ino);
844	inp->i_parent = parent;
845	inp->i_dotdot = parent;
846	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
847	if (inoinfo(ino)->ino_state == DSTATE) {
848		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
849		inoinfo(parent)->ino_linkcnt++;
850	}
851	dp = ginode(parent);
852	DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1));
853	inodirty();
854	return (ino);
855}
856
857/*
858 * free a directory inode
859 */
860static void
861freedir(ino_t ino, ino_t parent)
862{
863	union dinode *dp;
864
865	if (ino != parent) {
866		dp = ginode(parent);
867		DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) - 1));
868		inodirty();
869	}
870	freeino(ino);
871}
872
873/*
874 * generate a temporary name for the lost+found directory.
875 */
876static int
877lftempname(char *bufp, ino_t ino)
878{
879	ino_t in;
880	char *cp;
881	int namlen;
882
883	cp = bufp + 2;
884	for (in = maxino; in > 0; in /= 10)
885		cp++;
886	*--cp = 0;
887	namlen = cp - bufp;
888	in = ino;
889	while (cp > bufp) {
890		*--cp = (in % 10) + '0';
891		in /= 10;
892	}
893	*cp = '#';
894	return (namlen);
895}
896
897/*
898 * Get a directory block.
899 * Insure that it is held until another is requested.
900 */
901static struct bufarea *
902getdirblk(daddr_t blkno, long size)
903{
904
905	if (pdirbp != 0)
906		pdirbp->b_flags &= ~B_INUSE;
907	pdirbp = getdatablk(blkno, size);
908	return (pdirbp);
909}
910