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