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