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