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