xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/dir.c (revision b9a41fd3)
17c478bd9Sstevel@tonic-gate /*
2355d6bb5Sswilcox  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
67c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
77c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
97c478bd9Sstevel@tonic-gate /*
107c478bd9Sstevel@tonic-gate  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
117c478bd9Sstevel@tonic-gate  * All rights reserved.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms are permitted
147c478bd9Sstevel@tonic-gate  * provided that: (1) source distributions retain this entire copyright
157c478bd9Sstevel@tonic-gate  * notice and comment, and (2) distributions including binaries display
167c478bd9Sstevel@tonic-gate  * the following acknowledgement:  ``This product includes software
177c478bd9Sstevel@tonic-gate  * developed by the University of California, Berkeley and its contributors''
187c478bd9Sstevel@tonic-gate  * in the documentation or other materials provided with the distribution
197c478bd9Sstevel@tonic-gate  * and in all advertising materials mentioning features or use of this
207c478bd9Sstevel@tonic-gate  * software. Neither the name of the University nor the names of its
217c478bd9Sstevel@tonic-gate  * contributors may be used to endorse or promote products derived
227c478bd9Sstevel@tonic-gate  * from this software without specific prior written permission.
267c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
337c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
34355d6bb5Sswilcox #include <string.h>
35355d6bb5Sswilcox #include <stdarg.h>
367c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
377c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
387c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
397c478bd9Sstevel@tonic-gate #define	_KERNEL
407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
417c478bd9Sstevel@tonic-gate #undef _KERNEL
427c478bd9Sstevel@tonic-gate #include "fsck.h"
44355d6bb5Sswilcox struct rc_queue {
45355d6bb5Sswilcox 	struct rc_queue	*rc_next;
46355d6bb5Sswilcox 	fsck_ino_t	rc_orphan;
47355d6bb5Sswilcox 	fsck_ino_t	rc_parent;
48355d6bb5Sswilcox 	caddr_t		rc_name;
49355d6bb5Sswilcox };
51355d6bb5Sswilcox caddr_t lfname = "lost+found";		/* name to use for l+f dir */
52355d6bb5Sswilcox static int lfmode = 01700;		/* mode to use when creating l+f dir */
53355d6bb5Sswilcox static struct dirtemplate emptydir = { 0, DIRBLKSIZ };
54355d6bb5Sswilcox static struct dirtemplate dirhead = {
55355d6bb5Sswilcox 	0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".."
56355d6bb5Sswilcox };
58355d6bb5Sswilcox static void lftempname(char *, fsck_ino_t);
59355d6bb5Sswilcox static int do_reconnect(fsck_ino_t, fsck_ino_t, caddr_t);
60355d6bb5Sswilcox static caddr_t mkuniqname(caddr_t, caddr_t, fsck_ino_t, fsck_ino_t);
61355d6bb5Sswilcox static int chgino(struct inodesc *);
62355d6bb5Sswilcox static int dircheck(struct inodesc *, struct direct *);
63355d6bb5Sswilcox static int expanddir(fsck_ino_t, char *);
64355d6bb5Sswilcox static void freedir(fsck_ino_t, fsck_ino_t);
65355d6bb5Sswilcox static struct direct *fsck_readdir(struct inodesc *);
66355d6bb5Sswilcox static struct bufarea *getdirblk(daddr32_t, size_t);
67355d6bb5Sswilcox static int mkentry(struct inodesc *);
68355d6bb5Sswilcox static fsck_ino_t newdir(fsck_ino_t, fsck_ino_t, int, caddr_t);
69355d6bb5Sswilcox static fsck_ino_t reallocdir(fsck_ino_t, fsck_ino_t, int, caddr_t);
717c478bd9Sstevel@tonic-gate /*
727c478bd9Sstevel@tonic-gate  * Propagate connected state through the tree.
737c478bd9Sstevel@tonic-gate  */
74355d6bb5Sswilcox void
propagate(void)75355d6bb5Sswilcox propagate(void)
767c478bd9Sstevel@tonic-gate {
777c478bd9Sstevel@tonic-gate 	struct inoinfo **inpp, *inp;
787c478bd9Sstevel@tonic-gate 	struct inoinfo **inpend;
79355d6bb5Sswilcox 	int change, inorphan;
817c478bd9Sstevel@tonic-gate 	inpend = &inpsort[inplast];
827c478bd9Sstevel@tonic-gate 	do {
837c478bd9Sstevel@tonic-gate 		change = 0;
847c478bd9Sstevel@tonic-gate 		for (inpp = inpsort; inpp < inpend; inpp++) {
857c478bd9Sstevel@tonic-gate 			inp = *inpp;
867c478bd9Sstevel@tonic-gate 			if (inp->i_parent == 0)
877c478bd9Sstevel@tonic-gate 				continue;
887c478bd9Sstevel@tonic-gate 			if (statemap[inp->i_parent] == DFOUND &&
89355d6bb5Sswilcox 			    INO_IS_DUNFOUND(inp->i_number)) {
90355d6bb5Sswilcox 				inorphan = statemap[inp->i_number] & INORPHAN;
91355d6bb5Sswilcox 				statemap[inp->i_number] = DFOUND | inorphan;
927c478bd9Sstevel@tonic-gate 				change++;
937c478bd9Sstevel@tonic-gate 			}
947c478bd9Sstevel@tonic-gate 		}
957c478bd9Sstevel@tonic-gate 	} while (change > 0);
967c478bd9Sstevel@tonic-gate }
987c478bd9Sstevel@tonic-gate /*
997c478bd9Sstevel@tonic-gate  * Scan each entry in a directory block.
1007c478bd9Sstevel@tonic-gate  */
101355d6bb5Sswilcox int
dirscan(struct inodesc * idesc)102355d6bb5Sswilcox dirscan(struct inodesc *idesc)
1037c478bd9Sstevel@tonic-gate {
1047c478bd9Sstevel@tonic-gate 	struct direct *dp;
1057c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
106355d6bb5Sswilcox 	uint_t dsize, n;
107355d6bb5Sswilcox 	size_t blksiz;
108355d6bb5Sswilcox 	union {			/* keep lint happy about alignment */
109355d6bb5Sswilcox 		char dbuf[DIRBLKSIZ];
110355d6bb5Sswilcox 		struct direct dir;
111355d6bb5Sswilcox 	} u;
1137c478bd9Sstevel@tonic-gate 	if (idesc->id_type != DATA)
1147c478bd9Sstevel@tonic-gate 		errexit("wrong type to dirscan %d\n", idesc->id_type);
1157c478bd9Sstevel@tonic-gate 	if (idesc->id_entryno == 0 &&
1167c478bd9Sstevel@tonic-gate 	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
1177c478bd9Sstevel@tonic-gate 		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
1187c478bd9Sstevel@tonic-gate 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
1197c478bd9Sstevel@tonic-gate 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
1207c478bd9Sstevel@tonic-gate 		idesc->id_filesize -= (offset_t)blksiz;
1217c478bd9Sstevel@tonic-gate 		return (SKIP);
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 	idesc->id_loc = 0;
1247c478bd9Sstevel@tonic-gate 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
1257c478bd9Sstevel@tonic-gate 		/*
1267c478bd9Sstevel@tonic-gate 		 * If we were just passed a corrupt directory entry with
127355d6bb5Sswilcox 		 * d_reclen > DIRBLKSIZ, we don't want to memmove() all over
128355d6bb5Sswilcox 		 * our stack.  This directory gets cleaned up later.
1297c478bd9Sstevel@tonic-gate 		 */
130355d6bb5Sswilcox 		dsize = MIN(dp->d_reclen, sizeof (u.dbuf));
131355d6bb5Sswilcox 		(void) memmove((void *)u.dbuf, (void *)dp, (size_t)dsize);
132355d6bb5Sswilcox 		idesc->id_dirp = &u.dir;
1337c478bd9Sstevel@tonic-gate 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
134355d6bb5Sswilcox 			/*
135355d6bb5Sswilcox 			 * We can ignore errors from getdirblk() here,
136355d6bb5Sswilcox 			 * as the block is still in memory thanks to
137355d6bb5Sswilcox 			 * buffering and fsck_readdir().  If there was
138355d6bb5Sswilcox 			 * an error reading it before, then all decisions
139355d6bb5Sswilcox 			 * leading to getting us here were based on the
140355d6bb5Sswilcox 			 * resulting zeros.  As such, we have nothing
141355d6bb5Sswilcox 			 * to worry about at this point.
142355d6bb5Sswilcox 			 */
1437c478bd9Sstevel@tonic-gate 			bp = getdirblk(idesc->id_blkno, blksiz);
144355d6bb5Sswilcox 			(void) memmove((void *)(bp->b_un.b_buf +
145355d6bb5Sswilcox 			    idesc->id_loc - dsize),
146355d6bb5Sswilcox 			    (void *)u.dbuf, (size_t)dsize);
1477c478bd9Sstevel@tonic-gate 			dirty(bp);
1487c478bd9Sstevel@tonic-gate 			sbdirty();
1497c478bd9Sstevel@tonic-gate 		}
1507c478bd9Sstevel@tonic-gate 		if (n & STOP)
1517c478bd9Sstevel@tonic-gate 			return (n);
1527c478bd9Sstevel@tonic-gate 	}
1537c478bd9Sstevel@tonic-gate 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
1547c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate /*
1577c478bd9Sstevel@tonic-gate  * Get current entry in a directory (and peek at the next entry).
1587c478bd9Sstevel@tonic-gate  */
159355d6bb5Sswilcox static struct direct *
fsck_readdir(struct inodesc * idesc)160355d6bb5Sswilcox fsck_readdir(struct inodesc *idesc)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate 	struct direct *dp, *ndp = 0;
1637c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
164355d6bb5Sswilcox 	ushort_t size;		/* of directory entry */
165355d6bb5Sswilcox 	size_t blksiz;
1667c478bd9Sstevel@tonic-gate 	int dofixret;
167355d6bb5Sswilcox 	int salvaged;		/* when to report SALVAGED in preen mode */
1687c478bd9Sstevel@tonic-gate 	int origloc	= idesc->id_loc;
1707c478bd9Sstevel@tonic-gate 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
1717c478bd9Sstevel@tonic-gate 	/*
172355d6bb5Sswilcox 	 * Sanity check id_filesize and id_loc fields.  The latter
173355d6bb5Sswilcox 	 * has to be within the block we're looking at, as well as
174355d6bb5Sswilcox 	 * aligned to a four-byte boundary.  The alignment is due to
175355d6bb5Sswilcox 	 * a struct direct containing four-byte integers.  It's
176355d6bb5Sswilcox 	 * unfortunate that the four is a magic number, but there's
177355d6bb5Sswilcox 	 * really no good way to derive it from the ufs header files.
1787c478bd9Sstevel@tonic-gate 	 */
179355d6bb5Sswilcox 	if ((idesc->id_filesize <= 0) || (idesc->id_loc >= blksiz) ||
180355d6bb5Sswilcox 	    ((idesc->id_loc & 3) != 0))
1817c478bd9Sstevel@tonic-gate 		return (NULL);
182355d6bb5Sswilcox 	/*
183355d6bb5Sswilcox 	 * We don't have to worry about holes in the directory's
184355d6bb5Sswilcox 	 * block list, because that was checked for when the
185355d6bb5Sswilcox 	 * inode was first encountered during pass1.  We never
186355d6bb5Sswilcox 	 * scan a directory until after we've vetted its block list.
187355d6bb5Sswilcox 	 */
188355d6bb5Sswilcox 	/*
189355d6bb5Sswilcox 	 * We can ignore errors from getdirblk() here, as dircheck()
190355d6bb5Sswilcox 	 * will reject any entries that would have been in the bad
191355d6bb5Sswilcox 	 * sectors (fsck_bread() fills in zeros on failures).  The main
192355d6bb5Sswilcox 	 * reject keys are that d_reclen would be zero and/or that it
193355d6bb5Sswilcox 	 * is less than the minimal size of a directory entry.  Since
194355d6bb5Sswilcox 	 * entries can't span sectors, there's no worry about having
195355d6bb5Sswilcox 	 * a good beginning in one sector and the rest in the next,
196355d6bb5Sswilcox 	 * where that second sector was unreadable and therefore
197355d6bb5Sswilcox 	 * replaced with zeros.
198355d6bb5Sswilcox 	 */
1997c478bd9Sstevel@tonic-gate 	bp = getdirblk(idesc->id_blkno, blksiz);
200355d6bb5Sswilcox 	/* LINTED b_buf is aligned and id_loc was verified above */
2017c478bd9Sstevel@tonic-gate 	dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
2037c478bd9Sstevel@tonic-gate 	/*
2047c478bd9Sstevel@tonic-gate 	 * Check the current entry in the directory.
2057c478bd9Sstevel@tonic-gate 	 */
2067c478bd9Sstevel@tonic-gate 	if (dircheck(idesc, dp) == 0) {
2077c478bd9Sstevel@tonic-gate 		/*
2087c478bd9Sstevel@tonic-gate 		 * If we are in here, then either the current directory
2097c478bd9Sstevel@tonic-gate 		 * entry is bad or the next directory entry is bad.
2107c478bd9Sstevel@tonic-gate 		 */
2117c478bd9Sstevel@tonic-gate next_is_bad:
2127c478bd9Sstevel@tonic-gate 		/*
2137c478bd9Sstevel@tonic-gate 		 * Find the amount of space left to the end of the
2147c478bd9Sstevel@tonic-gate 		 * directory block for either directory entry.
2157c478bd9Sstevel@tonic-gate 		 */
2167c478bd9Sstevel@tonic-gate 		size = DIRBLKSIZ - (idesc->id_loc & (DIRBLKSIZ - 1));
2187c478bd9Sstevel@tonic-gate 		/*
2197c478bd9Sstevel@tonic-gate 		 * Advance to the end of the directory block.
2207c478bd9Sstevel@tonic-gate 		 */
2217c478bd9Sstevel@tonic-gate 		idesc->id_loc += size;
2227c478bd9Sstevel@tonic-gate 		idesc->id_filesize -= (offset_t)size;
2247c478bd9Sstevel@tonic-gate 		/*
2257c478bd9Sstevel@tonic-gate 		 * Ask the question before we fix the in-core directory
2267c478bd9Sstevel@tonic-gate 		 * block because dofix() may reuse the buffer.
2277c478bd9Sstevel@tonic-gate 		 */
228355d6bb5Sswilcox 		salvaged = (idesc->id_fix == DONTKNOW);
2297c478bd9Sstevel@tonic-gate 		dofixret = dofix(idesc, "DIRECTORY CORRUPTED");
231355d6bb5Sswilcox 		/*
232355d6bb5Sswilcox 		 * If there was an error reading the block, then that
233355d6bb5Sswilcox 		 * same error can reasonably be expected to have occurred
234355d6bb5Sswilcox 		 * when it was read previously.  As such, the decision
235355d6bb5Sswilcox 		 * to come here was based on the results of that partially-
236355d6bb5Sswilcox 		 * zerod block, and so anything we change should be
237355d6bb5Sswilcox 		 * based on it as well.  Upshot: no need to check for
238355d6bb5Sswilcox 		 * errors here.
239355d6bb5Sswilcox 		 */
2407c478bd9Sstevel@tonic-gate 		bp = getdirblk(idesc->id_blkno, blksiz);
241355d6bb5Sswilcox 		/* LINTED b_buf is aligned and id_loc/origloc was verified */
2427c478bd9Sstevel@tonic-gate 		dp = (struct direct *)(bp->b_un.b_buf + origloc);
2447c478bd9Sstevel@tonic-gate 		/*
2457c478bd9Sstevel@tonic-gate 		 * This is the current directory entry and since it is
2467c478bd9Sstevel@tonic-gate 		 * corrupt we cannot trust the rest of the directory
2477c478bd9Sstevel@tonic-gate 		 * block so change the current directory entry to
2487c478bd9Sstevel@tonic-gate 		 * contain nothing and encompass the rest of the block.
2497c478bd9Sstevel@tonic-gate 		 */
2507c478bd9Sstevel@tonic-gate 		if (ndp == NULL) {
2517c478bd9Sstevel@tonic-gate 			dp->d_reclen = size;
2527c478bd9Sstevel@tonic-gate 			dp->d_ino = 0;
2537c478bd9Sstevel@tonic-gate 			dp->d_namlen = 0;
2547c478bd9Sstevel@tonic-gate 			dp->d_name[0] = '\0';
2557c478bd9Sstevel@tonic-gate 		}
2567c478bd9Sstevel@tonic-gate 		/*
2577c478bd9Sstevel@tonic-gate 		 * This is the next directory entry, i.e., we got here
258355d6bb5Sswilcox 		 * via a "goto next_is_bad".  That directory entry is
259355d6bb5Sswilcox 		 * corrupt.  However, the current directory entry is okay
260355d6bb5Sswilcox 		 * so if we are in fix mode, just extend its record size
261355d6bb5Sswilcox 		 * to encompass the rest of the block.
2627c478bd9Sstevel@tonic-gate 		 */
2637c478bd9Sstevel@tonic-gate 		else if (dofixret) {
2647c478bd9Sstevel@tonic-gate 			dp->d_reclen += size;
2657c478bd9Sstevel@tonic-gate 		}
2667c478bd9Sstevel@tonic-gate 		/*
2677c478bd9Sstevel@tonic-gate 		 * If the user said to fix the directory corruption, then
2687c478bd9Sstevel@tonic-gate 		 * mark the block as dirty.  Otherwise, our "repairs" only
2697c478bd9Sstevel@tonic-gate 		 * apply to the in-core copy so we don't hand back trash
2707c478bd9Sstevel@tonic-gate 		 * to the caller.
2717c478bd9Sstevel@tonic-gate 		 *
2727c478bd9Sstevel@tonic-gate 		 * Note: It is possible that saying "no" to a change in
2737c478bd9Sstevel@tonic-gate 		 * one part of the I/O buffer and "yes" to a later change
2747c478bd9Sstevel@tonic-gate 		 * in the same I/O buffer may still flush the change to
275355d6bb5Sswilcox 		 * which we said "no". This is the pathological case and
2767c478bd9Sstevel@tonic-gate 		 * no fix is planned at this time.
2777c478bd9Sstevel@tonic-gate 		 */
278355d6bb5Sswilcox 		if (dofixret) {
2797c478bd9Sstevel@tonic-gate 			dirty(bp);
280355d6bb5Sswilcox 			if (preen && salvaged)
281355d6bb5Sswilcox 				(void) printf(" (SALVAGED)\n");
282355d6bb5Sswilcox 			if (idesc->id_number == lfdir)
283355d6bb5Sswilcox 				lfdir = 0;
284355d6bb5Sswilcox 		}
286355d6bb5Sswilcox 		/*
287355d6bb5Sswilcox 		 * dp points into bp, which will get re-used at some
288355d6bb5Sswilcox 		 * arbitrary time in the future.  We rely on the fact
289355d6bb5Sswilcox 		 * that we're singled-threaded, and that we'll be done
290355d6bb5Sswilcox 		 * with this directory entry by the time the next one
291355d6bb5Sswilcox 		 * is needed.
292355d6bb5Sswilcox 		 */
2937c478bd9Sstevel@tonic-gate 		return (dp);
2947c478bd9Sstevel@tonic-gate 	}
2957c478bd9Sstevel@tonic-gate 	/*
2967c478bd9Sstevel@tonic-gate 	 * The current directory entry checked out so advance past it.
2977c478bd9Sstevel@tonic-gate 	 */
2987c478bd9Sstevel@tonic-gate 	idesc->id_loc += dp->d_reclen;
2997c478bd9Sstevel@tonic-gate 	idesc->id_filesize -= (offset_t)dp->d_reclen;
3007c478bd9Sstevel@tonic-gate 	/*
3017c478bd9Sstevel@tonic-gate 	 * If we are not at the directory block boundary, then peek
3027c478bd9Sstevel@tonic-gate 	 * at the next directory entry and if it is bad we can add
3037c478bd9Sstevel@tonic-gate 	 * its space to the current directory entry (compression).
3047c478bd9Sstevel@tonic-gate 	 * Again, we sanity check the id_loc and id_filesize fields
3057c478bd9Sstevel@tonic-gate 	 * since we modified them above.
3067c478bd9Sstevel@tonic-gate 	 */
307355d6bb5Sswilcox 	if ((idesc->id_loc & (DIRBLKSIZ - 1)) &&	/* not at start */
308355d6bb5Sswilcox 	    (idesc->id_loc < blksiz) &&			/* within block */
309355d6bb5Sswilcox 	    ((idesc->id_loc & 3) == 0) &&		/* properly aligned */
310355d6bb5Sswilcox 	    (idesc->id_filesize > 0)) {			/* data follows */
311355d6bb5Sswilcox 		/* LINTED b_buf is aligned and id_loc verified to be ok */
3127c478bd9Sstevel@tonic-gate 		ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
3137c478bd9Sstevel@tonic-gate 		if (dircheck(idesc, ndp) == 0)
3147c478bd9Sstevel@tonic-gate 			goto next_is_bad;
3157c478bd9Sstevel@tonic-gate 	}
317355d6bb5Sswilcox 	/*
318355d6bb5Sswilcox 	 * See comment above about dp pointing into bp.
319355d6bb5Sswilcox 	 */
3207c478bd9Sstevel@tonic-gate 	return (dp);
3217c478bd9Sstevel@tonic-gate }
3237c478bd9Sstevel@tonic-gate /*
3247c478bd9Sstevel@tonic-gate  * Verify that a directory entry is valid.
3257c478bd9Sstevel@tonic-gate  * This is a superset of the checks made in the kernel.
3267c478bd9Sstevel@tonic-gate  */
327355d6bb5Sswilcox static int
dircheck(struct inodesc * idesc,struct direct * dp)328355d6bb5Sswilcox dircheck(struct inodesc *idesc, struct direct *dp)
3297c478bd9Sstevel@tonic-gate {
330355d6bb5Sswilcox 	size_t size;
3317c478bd9Sstevel@tonic-gate 	char *cp;
3327c478bd9Sstevel@tonic-gate 	int spaceleft;
334355d6bb5Sswilcox 	/*
335355d6bb5Sswilcox 	 * Recall that id_filesize is the number of bytes left to
336355d6bb5Sswilcox 	 * process in the directory.  We check id_filesize >= size
337355d6bb5Sswilcox 	 * instead of id_filesize >= d_reclen because all that the
338355d6bb5Sswilcox 	 * directory is actually required to contain is the entry
339355d6bb5Sswilcox 	 * itself (and it's how the kernel does the allocation).
340355d6bb5Sswilcox 	 *
341355d6bb5Sswilcox 	 * We indirectly check for d_reclen going past the end of
342355d6bb5Sswilcox 	 * the allocated space by comparing it against spaceleft.
343355d6bb5Sswilcox 	 */
3447c478bd9Sstevel@tonic-gate 	size = DIRSIZ(dp);
3457c478bd9Sstevel@tonic-gate 	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
3467c478bd9Sstevel@tonic-gate 	if (dp->d_ino < maxino &&
3477c478bd9Sstevel@tonic-gate 	    dp->d_reclen != 0 &&
3487c478bd9Sstevel@tonic-gate 	    (int)dp->d_reclen <= spaceleft &&
3497c478bd9Sstevel@tonic-gate 	    (dp->d_reclen & 0x3) == 0 &&
3507c478bd9Sstevel@tonic-gate 	    (int)dp->d_reclen >= size &&
3517c478bd9Sstevel@tonic-gate 	    idesc->id_filesize >= (offset_t)size &&
3527c478bd9Sstevel@tonic-gate 	    dp->d_namlen <= MAXNAMLEN) {
3537c478bd9Sstevel@tonic-gate 		if (dp->d_ino == 0)
3547c478bd9Sstevel@tonic-gate 			return (1);
355355d6bb5Sswilcox 		for (cp = dp->d_name, size = 0; size < (size_t)dp->d_namlen;
3567c478bd9Sstevel@tonic-gate 								size++, cp++)
357355d6bb5Sswilcox 			if ((*cp == '\0') || (*cp == '/'))
358355d6bb5Sswilcox 				goto bad;
359355d6bb5Sswilcox 		if (*cp == '\0')
3607c478bd9Sstevel@tonic-gate 			return (1);
3617c478bd9Sstevel@tonic-gate 	}
362355d6bb5Sswilcox bad:
363355d6bb5Sswilcox 	if (debug) {
364355d6bb5Sswilcox 		(void) printf("Bad dir in inode %d at lbn %d, loc %d:\n",
365355d6bb5Sswilcox 		    idesc->id_number, idesc->id_lbn, idesc->id_loc);
366355d6bb5Sswilcox 		(void) printf("    ino %d reclen %d namlen %d name `%s'\n",
367355d6bb5Sswilcox 		    dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_name);
3687c478bd9Sstevel@tonic-gate 	}
369355d6bb5Sswilcox 	return (0);
3707c478bd9Sstevel@tonic-gate }
372355d6bb5Sswilcox void
adjust(struct inodesc * idesc,int lcnt)373355d6bb5Sswilcox adjust(struct inodesc *idesc, int lcnt)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	struct dinode *dp;
376355d6bb5Sswilcox 	caddr_t flow;
377355d6bb5Sswilcox 	int saveiscorrupt;
378355d6bb5Sswilcox 	struct inodesc lcidesc;
3807c478bd9Sstevel@tonic-gate 	dp = ginode(idesc->id_number);
3817c478bd9Sstevel@tonic-gate 	if (dp->di_nlink == lcnt) {
382355d6bb5Sswilcox 		/*
383355d6bb5Sswilcox 		 * If we have not hit any unresolved problems, are running
384355d6bb5Sswilcox 		 * in preen mode, and are on a file system using logging,
385355d6bb5Sswilcox 		 * then just toss any partially allocated files, as they are
386355d6bb5Sswilcox 		 * an expected occurrence.
387355d6bb5Sswilcox 		 */
388355d6bb5Sswilcox 		if (!iscorrupt && preen && islog) {
389355d6bb5Sswilcox 			clri(idesc, "UNREF", CLRI_VERBOSE, CLRI_NOP_OK);
3907c478bd9Sstevel@tonic-gate 			return;
391355d6bb5Sswilcox 		} else {
392355d6bb5Sswilcox 			/*
393355d6bb5Sswilcox 			 * The file system can be considered clean even if
394355d6bb5Sswilcox 			 * a file is not linked up, but is cleared.  In
395355d6bb5Sswilcox 			 * other words, the kernel won't panic over it.
396355d6bb5Sswilcox 			 * Hence, iscorrupt should not be set when
397355d6bb5Sswilcox 			 * linkup is answered no, but clri is answered yes.
398355d6bb5Sswilcox 			 *
399355d6bb5Sswilcox 			 * If neither is answered yes, then we have a
400355d6bb5Sswilcox 			 * non-panic-inducing known corruption that the
401355d6bb5Sswilcox 			 * user needs to be reminded of when we exit.
402355d6bb5Sswilcox 			 */
403355d6bb5Sswilcox 			saveiscorrupt = iscorrupt;
404355d6bb5Sswilcox 			if (linkup(idesc->id_number, (fsck_ino_t)0,
405355d6bb5Sswilcox 			    NULL) == 0) {
406355d6bb5Sswilcox 				iscorrupt = saveiscorrupt;
407355d6bb5Sswilcox 				clri(idesc, "UNREF", CLRI_QUIET, CLRI_NOP_OK);
408355d6bb5Sswilcox 				if (statemap[idesc->id_number] != USTATE)
409355d6bb5Sswilcox 					iscorrupt = 1;
410355d6bb5Sswilcox 				return;
411355d6bb5Sswilcox 			}
412355d6bb5Sswilcox 			dp = ginode(idesc->id_number);
4137c478bd9Sstevel@tonic-gate 		}
414355d6bb5Sswilcox 		lcnt = lncntp[idesc->id_number];
415355d6bb5Sswilcox 	}
417355d6bb5Sswilcox 	/*
418355d6bb5Sswilcox 	 * It doesn't happen often, but it's possible to get a true
419355d6bb5Sswilcox 	 * excess of links (especially if a lot of directories got
420355d6bb5Sswilcox 	 * orphaned and reattached to lost+found).  Instead of wrapping
421355d6bb5Sswilcox 	 * around, do something semi-useful (i.e., give progress towards
422355d6bb5Sswilcox 	 * a less-broken filesystem) when this happens.
423355d6bb5Sswilcox 	 */
424355d6bb5Sswilcox 	LINK_RANGE(flow, dp->di_nlink, -lcnt);
425355d6bb5Sswilcox 	if (flow != NULL) {
426355d6bb5Sswilcox 		LINK_CLEAR(flow, idesc->id_number, dp->di_mode, &lcidesc);
427355d6bb5Sswilcox 		if (statemap[idesc->id_number] == USTATE)
428355d6bb5Sswilcox 			return;
4297c478bd9Sstevel@tonic-gate 	}
431355d6bb5Sswilcox 	dp = ginode(idesc->id_number);
4327c478bd9Sstevel@tonic-gate 	if (lcnt && dp->di_nlink != lcnt) {
4337c478bd9Sstevel@tonic-gate 		pwarn("LINK COUNT %s",
434355d6bb5Sswilcox 		    file_id(idesc->id_number, dp->di_mode));
4357c478bd9Sstevel@tonic-gate 		pinode(idesc->id_number);
436355d6bb5Sswilcox 		dp = ginode(idesc->id_number);
437355d6bb5Sswilcox 		(void) printf(" COUNT %d SHOULD BE %d",
4387c478bd9Sstevel@tonic-gate 		    dp->di_nlink, dp->di_nlink - lcnt);
439355d6bb5Sswilcox 		/*
440355d6bb5Sswilcox 		 * Even lost+found is subject to this, as whenever
441355d6bb5Sswilcox 		 * we modify it, we update both the in-memory and
442355d6bb5Sswilcox 		 * on-disk counts.  Thus, they should still be in
443355d6bb5Sswilcox 		 * sync.
444355d6bb5Sswilcox 		 */
4457c478bd9Sstevel@tonic-gate 		if (preen) {
4467c478bd9Sstevel@tonic-gate 			if (lcnt < 0) {
447355d6bb5Sswilcox 				(void) printf("\n");
4487c478bd9Sstevel@tonic-gate 				if ((dp->di_mode & IFMT) == IFSHAD)
4497c478bd9Sstevel@tonic-gate 					pwarn("LINK COUNT INCREASING");
4507c478bd9Sstevel@tonic-gate 				else
4517c478bd9Sstevel@tonic-gate 					pfatal("LINK COUNT INCREASING");
4527c478bd9Sstevel@tonic-gate 			}
4537c478bd9Sstevel@tonic-gate 		}
4547c478bd9Sstevel@tonic-gate 		if (preen || reply("ADJUST") == 1) {
4557c478bd9Sstevel@tonic-gate 			dp->di_nlink -= lcnt;
4567c478bd9Sstevel@tonic-gate 			inodirty();
457355d6bb5Sswilcox 			if (preen)
458355d6bb5Sswilcox 				(void) printf(" (ADJUSTED)\n");
459355d6bb5Sswilcox 		} else if (((dp->di_mode & IFMT) == IFDIR) ||
460355d6bb5Sswilcox 		    ((dp->di_mode & IFMT) == IFATTRDIR)) {
461355d6bb5Sswilcox 			/*
462355d6bb5Sswilcox 			 * File counts can be off relatively harmlessly,
463355d6bb5Sswilcox 			 * but a bad directory count can cause the
464355d6bb5Sswilcox 			 * kernel to lose its mind.
465355d6bb5Sswilcox 			 */
466355d6bb5Sswilcox 			iscorrupt = 1;
4677c478bd9Sstevel@tonic-gate 		}
4687c478bd9Sstevel@tonic-gate 	}
4697c478bd9Sstevel@tonic-gate }
471355d6bb5Sswilcox static int
mkentry(struct inodesc * idesc)472355d6bb5Sswilcox mkentry(struct inodesc *idesc)
4737c478bd9Sstevel@tonic-gate {
4747c478bd9Sstevel@tonic-gate 	struct direct *dirp = idesc->id_dirp;
4757c478bd9Sstevel@tonic-gate 	struct direct newent;
4767c478bd9Sstevel@tonic-gate 	int newlen, oldlen;
4787c478bd9Sstevel@tonic-gate 	newent.d_namlen = strlen(idesc->id_name);
4797c478bd9Sstevel@tonic-gate 	newlen = DIRSIZ(&newent);
4807c478bd9Sstevel@tonic-gate 	if (dirp->d_ino != 0)
4817c478bd9Sstevel@tonic-gate 		oldlen = DIRSIZ(dirp);
4827c478bd9Sstevel@tonic-gate 	else
4837c478bd9Sstevel@tonic-gate 		oldlen = 0;
4847c478bd9Sstevel@tonic-gate 	if ((int)dirp->d_reclen - oldlen < newlen)
4857c478bd9Sstevel@tonic-gate 		return (KEEPON);
4867c478bd9Sstevel@tonic-gate 	newent.d_reclen = dirp->d_reclen - (ushort_t)oldlen;
4877c478bd9Sstevel@tonic-gate 	dirp->d_reclen = (ushort_t)oldlen;
489355d6bb5Sswilcox 	/* LINTED dirp is aligned and DIRSIZ() forces oldlen to be aligned */
4907c478bd9Sstevel@tonic-gate 	dirp = (struct direct *)(((char *)dirp) + oldlen);
4917c478bd9Sstevel@tonic-gate 	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
4927c478bd9Sstevel@tonic-gate 	dirp->d_reclen = newent.d_reclen;
4937c478bd9Sstevel@tonic-gate 	dirp->d_namlen = newent.d_namlen;
494355d6bb5Sswilcox 	(void) memmove(dirp->d_name, idesc->id_name,
495355d6bb5Sswilcox 	    (size_t)newent.d_namlen + 1);
4977c478bd9Sstevel@tonic-gate 	return (ALTERED|STOP);
4987c478bd9Sstevel@tonic-gate }
500355d6bb5Sswilcox static int
chgino(struct inodesc * idesc)501355d6bb5Sswilcox chgino(struct inodesc *idesc)
5027c478bd9Sstevel@tonic-gate {
5037c478bd9Sstevel@tonic-gate 	struct direct *dirp = idesc->id_dirp;
505355d6bb5Sswilcox 	if (memcmp(dirp->d_name, idesc->id_name,
506355d6bb5Sswilcox 	    (size_t)dirp->d_namlen + 1) != 0)
5077c478bd9Sstevel@tonic-gate 		return (KEEPON);
5087c478bd9Sstevel@tonic-gate 	dirp->d_ino = idesc->id_parent;
5097c478bd9Sstevel@tonic-gate 	return (ALTERED|STOP);
5107c478bd9Sstevel@tonic-gate }
512355d6bb5Sswilcox int
linkup(fsck_ino_t orphan,fsck_ino_t parentdir,caddr_t name)513355d6bb5Sswilcox linkup(fsck_ino_t orphan, fsck_ino_t parentdir, caddr_t name)
5147c478bd9Sstevel@tonic-gate {
515355d6bb5Sswilcox 	int rval;
5167c478bd9Sstevel@tonic-gate 	struct dinode *dp;
5177c478bd9Sstevel@tonic-gate 	int lostdir;
5187c478bd9Sstevel@tonic-gate 	int lostshadow;
519355d6bb5Sswilcox 	fsck_ino_t oldlfdir;
520355d6bb5Sswilcox 	fsck_ino_t *intree;
5217c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
523355d6bb5Sswilcox 	init_inodesc(&idesc);
5247c478bd9Sstevel@tonic-gate 	dp = ginode(orphan);
5257c478bd9Sstevel@tonic-gate 	lostdir = (((dp->di_mode & IFMT) == IFDIR) ||
5267c478bd9Sstevel@tonic-gate 	    ((dp->di_mode & IFMT) == IFATTRDIR));
527355d6bb5Sswilcox 	if (debug && lostdir && dp->di_nlink <= 0 && lncntp[orphan] == -1)
528355d6bb5Sswilcox 		(void) printf(
529355d6bb5Sswilcox 		    "old fsck would have left inode %d for reclaim thread\n",
530355d6bb5Sswilcox 		    orphan);
5317c478bd9Sstevel@tonic-gate 	lostshadow = (dp->di_mode & IFMT) == IFSHAD;
532355d6bb5Sswilcox 	pwarn("UNREF %s ", file_id(orphan, dp->di_mode));
5337c478bd9Sstevel@tonic-gate 	pinode(orphan);
5347c478bd9Sstevel@tonic-gate 	if (lostshadow || (dp->di_size == 0 && dp->di_oeftflag == 0))
5357c478bd9Sstevel@tonic-gate 		return (0);
536355d6bb5Sswilcox 	if (!preen && (reply("RECONNECT") == 0))
537355d6bb5Sswilcox 		goto noconnect;
5397c478bd9Sstevel@tonic-gate 	if (lfdir == 0) {
5407c478bd9Sstevel@tonic-gate 		dp = ginode(UFSROOTINO);
5417c478bd9Sstevel@tonic-gate 		idesc.id_name = lfname;
5427c478bd9Sstevel@tonic-gate 		idesc.id_type = DATA;
5437c478bd9Sstevel@tonic-gate 		idesc.id_func = findino;
5447c478bd9Sstevel@tonic-gate 		idesc.id_number = UFSROOTINO;
5457c478bd9Sstevel@tonic-gate 		idesc.id_fix = DONTKNOW;
546355d6bb5Sswilcox 		if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) != 0) {
5477c478bd9Sstevel@tonic-gate 			lfdir = idesc.id_parent;
5487c478bd9Sstevel@tonic-gate 		} else {
549355d6bb5Sswilcox 			pwarn("NO %s DIRECTORY", lfname);
550355d6bb5Sswilcox 			if (preen || reply("CREATE") == 1) {
551355d6bb5Sswilcox 				lfdir = newdir(UFSROOTINO, (fsck_ino_t)0,
552355d6bb5Sswilcox 				    lfmode, lfname);
5537c478bd9Sstevel@tonic-gate 				if (lfdir != 0) {
554355d6bb5Sswilcox 					if (preen)
555355d6bb5Sswilcox 						(void) printf(" (CREATED)\n");
556355d6bb5Sswilcox 					else
557355d6bb5Sswilcox 						(void) printf("\n");
558355d6bb5Sswilcox 					statemap[lfdir] |= INFOUND;
559355d6bb5Sswilcox 					/*
560355d6bb5Sswilcox 					 * XXX What if we allocate an inode
561355d6bb5Sswilcox 					 * that's already been scanned?  Then
562355d6bb5Sswilcox 					 * we need to leave lnctnp[] alone.
563355d6bb5Sswilcox 					 */
564355d6bb5Sswilcox 					TRACK_LNCNTP(UFSROOTINO,
565355d6bb5Sswilcox 					    lncntp[UFSROOTINO]++);
5667c478bd9Sstevel@tonic-gate 				}
5677c478bd9Sstevel@tonic-gate 			}
5687c478bd9Sstevel@tonic-gate 		}
5697c478bd9Sstevel@tonic-gate 		if (lfdir == 0) {
570355d6bb5Sswilcox 			pfatal("SORRY. CANNOT CREATE %s DIRECTORY\n", lfname);
571355d6bb5Sswilcox 			pwarn("Could not reconnect inode %d\n", orphan);
572355d6bb5Sswilcox 			goto noconnect;
573355d6bb5Sswilcox 		} else {
574355d6bb5Sswilcox 			/*
575355d6bb5Sswilcox 			 * We searched for it via the namespace, so by
576355d6bb5Sswilcox 			 * definition it's been found.  We have to do this
577355d6bb5Sswilcox 			 * because it is possible that we're called before