xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/dir.c (revision 2a8bcb4e)
17c478bd9Sstevel@tonic-gate /*
2355d6bb5Sswilcox  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
77c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
87c478bd9Sstevel@tonic-gate 
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.
237c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
247c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
257c478bd9Sstevel@tonic-gate  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/param.h>
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
317c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
32355d6bb5Sswilcox #include <string.h>
33355d6bb5Sswilcox #include <stdarg.h>
347c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
357c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
367c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
377c478bd9Sstevel@tonic-gate #define	_KERNEL
387c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
397c478bd9Sstevel@tonic-gate #undef _KERNEL
407c478bd9Sstevel@tonic-gate #include "fsck.h"
417c478bd9Sstevel@tonic-gate 
42355d6bb5Sswilcox struct rc_queue {
43355d6bb5Sswilcox 	struct rc_queue	*rc_next;
44355d6bb5Sswilcox 	fsck_ino_t	rc_orphan;
45355d6bb5Sswilcox 	fsck_ino_t	rc_parent;
46355d6bb5Sswilcox 	caddr_t		rc_name;
47355d6bb5Sswilcox };
48355d6bb5Sswilcox 
49355d6bb5Sswilcox caddr_t lfname = "lost+found";		/* name to use for l+f dir */
50355d6bb5Sswilcox static int lfmode = 01700;		/* mode to use when creating l+f dir */
51355d6bb5Sswilcox static struct dirtemplate emptydir = { 0, DIRBLKSIZ };
52355d6bb5Sswilcox static struct dirtemplate dirhead = {
53355d6bb5Sswilcox 	0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".."
54355d6bb5Sswilcox };
55355d6bb5Sswilcox 
56355d6bb5Sswilcox static void lftempname(char *, fsck_ino_t);
57355d6bb5Sswilcox static int do_reconnect(fsck_ino_t, fsck_ino_t, caddr_t);
58355d6bb5Sswilcox static caddr_t mkuniqname(caddr_t, caddr_t, fsck_ino_t, fsck_ino_t);
59355d6bb5Sswilcox static int chgino(struct inodesc *);
60355d6bb5Sswilcox static int dircheck(struct inodesc *, struct direct *);
61355d6bb5Sswilcox static int expanddir(fsck_ino_t, char *);
62355d6bb5Sswilcox static void freedir(fsck_ino_t, fsck_ino_t);
63355d6bb5Sswilcox static struct direct *fsck_readdir(struct inodesc *);
64355d6bb5Sswilcox static struct bufarea *getdirblk(daddr32_t, size_t);
65355d6bb5Sswilcox static int mkentry(struct inodesc *);
66355d6bb5Sswilcox static fsck_ino_t newdir(fsck_ino_t, fsck_ino_t, int, caddr_t);
67355d6bb5Sswilcox static fsck_ino_t reallocdir(fsck_ino_t, fsck_ino_t, int, caddr_t);
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * Propagate connected state through the tree.
717c478bd9Sstevel@tonic-gate  */
72355d6bb5Sswilcox void
propagate(void)73355d6bb5Sswilcox propagate(void)
747c478bd9Sstevel@tonic-gate {
757c478bd9Sstevel@tonic-gate 	struct inoinfo **inpp, *inp;
767c478bd9Sstevel@tonic-gate 	struct inoinfo **inpend;
77355d6bb5Sswilcox 	int change, inorphan;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 	inpend = &inpsort[inplast];
807c478bd9Sstevel@tonic-gate 	do {
817c478bd9Sstevel@tonic-gate 		change = 0;
827c478bd9Sstevel@tonic-gate 		for (inpp = inpsort; inpp < inpend; inpp++) {
837c478bd9Sstevel@tonic-gate 			inp = *inpp;
847c478bd9Sstevel@tonic-gate 			if (inp->i_parent == 0)
857c478bd9Sstevel@tonic-gate 				continue;
867c478bd9Sstevel@tonic-gate 			if (statemap[inp->i_parent] == DFOUND &&
87355d6bb5Sswilcox 			    INO_IS_DUNFOUND(inp->i_number)) {
88355d6bb5Sswilcox 				inorphan = statemap[inp->i_number] & INORPHAN;
89355d6bb5Sswilcox 				statemap[inp->i_number] = DFOUND | inorphan;
907c478bd9Sstevel@tonic-gate 				change++;
917c478bd9Sstevel@tonic-gate 			}
927c478bd9Sstevel@tonic-gate 		}
937c478bd9Sstevel@tonic-gate 	} while (change > 0);
947c478bd9Sstevel@tonic-gate }
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate  * Scan each entry in a directory block.
987c478bd9Sstevel@tonic-gate  */
99355d6bb5Sswilcox int
dirscan(struct inodesc * idesc)100355d6bb5Sswilcox dirscan(struct inodesc *idesc)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate 	struct direct *dp;
1037c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
104355d6bb5Sswilcox 	uint_t dsize, n;
105355d6bb5Sswilcox 	size_t blksiz;
106355d6bb5Sswilcox 	union {			/* keep lint happy about alignment */
107355d6bb5Sswilcox 		char dbuf[DIRBLKSIZ];
108355d6bb5Sswilcox 		struct direct dir;
109355d6bb5Sswilcox 	} u;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	if (idesc->id_type != DATA)
1127c478bd9Sstevel@tonic-gate 		errexit("wrong type to dirscan %d\n", idesc->id_type);
1137c478bd9Sstevel@tonic-gate 	if (idesc->id_entryno == 0 &&
1147c478bd9Sstevel@tonic-gate 	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
1157c478bd9Sstevel@tonic-gate 		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
1167c478bd9Sstevel@tonic-gate 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
1177c478bd9Sstevel@tonic-gate 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
1187c478bd9Sstevel@tonic-gate 		idesc->id_filesize -= (offset_t)blksiz;
1197c478bd9Sstevel@tonic-gate 		return (SKIP);
1207c478bd9Sstevel@tonic-gate 	}
1217c478bd9Sstevel@tonic-gate 	idesc->id_loc = 0;
1227c478bd9Sstevel@tonic-gate 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
1237c478bd9Sstevel@tonic-gate 		/*
1247c478bd9Sstevel@tonic-gate 		 * If we were just passed a corrupt directory entry with
125355d6bb5Sswilcox 		 * d_reclen > DIRBLKSIZ, we don't want to memmove() all over
126355d6bb5Sswilcox 		 * our stack.  This directory gets cleaned up later.
1277c478bd9Sstevel@tonic-gate 		 */
128355d6bb5Sswilcox 		dsize = MIN(dp->d_reclen, sizeof (u.dbuf));
129355d6bb5Sswilcox 		(void) memmove((void *)u.dbuf, (void *)dp, (size_t)dsize);
130355d6bb5Sswilcox 		idesc->id_dirp = &u.dir;
1317c478bd9Sstevel@tonic-gate 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
132355d6bb5Sswilcox 			/*
133355d6bb5Sswilcox 			 * We can ignore errors from getdirblk() here,
134355d6bb5Sswilcox 			 * as the block is still in memory thanks to
135355d6bb5Sswilcox 			 * buffering and fsck_readdir().  If there was
136355d6bb5Sswilcox 			 * an error reading it before, then all decisions
137355d6bb5Sswilcox 			 * leading to getting us here were based on the
138355d6bb5Sswilcox 			 * resulting zeros.  As such, we have nothing
139355d6bb5Sswilcox 			 * to worry about at this point.
140355d6bb5Sswilcox 			 */
1417c478bd9Sstevel@tonic-gate 			bp = getdirblk(idesc->id_blkno, blksiz);
142355d6bb5Sswilcox 			(void) memmove((void *)(bp->b_un.b_buf +
143355d6bb5Sswilcox 			    idesc->id_loc - dsize),
144355d6bb5Sswilcox 			    (void *)u.dbuf, (size_t)dsize);
1457c478bd9Sstevel@tonic-gate 			dirty(bp);
1467c478bd9Sstevel@tonic-gate 			sbdirty();
1477c478bd9Sstevel@tonic-gate 		}
1487c478bd9Sstevel@tonic-gate 		if (n & STOP)
1497c478bd9Sstevel@tonic-gate 			return (n);
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
1527c478bd9Sstevel@tonic-gate }
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate  * Get current entry in a directory (and peek at the next entry).
1567c478bd9Sstevel@tonic-gate  */
157355d6bb5Sswilcox static struct direct *
fsck_readdir(struct inodesc * idesc)158355d6bb5Sswilcox fsck_readdir(struct inodesc *idesc)
1597c478bd9Sstevel@tonic-gate {
1607c478bd9Sstevel@tonic-gate 	struct direct *dp, *ndp = 0;
1617c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
162355d6bb5Sswilcox 	ushort_t size;		/* of directory entry */
163355d6bb5Sswilcox 	size_t blksiz;
1647c478bd9Sstevel@tonic-gate 	int dofixret;
165355d6bb5Sswilcox 	int salvaged;		/* when to report SALVAGED in preen mode */
1667c478bd9Sstevel@tonic-gate 	int origloc	= idesc->id_loc;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
1697c478bd9Sstevel@tonic-gate 	/*
170355d6bb5Sswilcox 	 * Sanity check id_filesize and id_loc fields.  The latter
171355d6bb5Sswilcox 	 * has to be within the block we're looking at, as well as
172355d6bb5Sswilcox 	 * aligned to a four-byte boundary.  The alignment is due to
173355d6bb5Sswilcox 	 * a struct direct containing four-byte integers.  It's
174355d6bb5Sswilcox 	 * unfortunate that the four is a magic number, but there's
175355d6bb5Sswilcox 	 * really no good way to derive it from the ufs header files.
1767c478bd9Sstevel@tonic-gate 	 */
177355d6bb5Sswilcox 	if ((idesc->id_filesize <= 0) || (idesc->id_loc >= blksiz) ||
178355d6bb5Sswilcox 	    ((idesc->id_loc & 3) != 0))
1797c478bd9Sstevel@tonic-gate 		return (NULL);
180355d6bb5Sswilcox 	/*
181355d6bb5Sswilcox 	 * We don't have to worry about holes in the directory's
182355d6bb5Sswilcox 	 * block list, because that was checked for when the
183355d6bb5Sswilcox 	 * inode was first encountered during pass1.  We never
184355d6bb5Sswilcox 	 * scan a directory until after we've vetted its block list.
185355d6bb5Sswilcox 	 */
186355d6bb5Sswilcox 	/*
187355d6bb5Sswilcox 	 * We can ignore errors from getdirblk() here, as dircheck()
188355d6bb5Sswilcox 	 * will reject any entries that would have been in the bad
189355d6bb5Sswilcox 	 * sectors (fsck_bread() fills in zeros on failures).  The main
190355d6bb5Sswilcox 	 * reject keys are that d_reclen would be zero and/or that it
191355d6bb5Sswilcox 	 * is less than the minimal size of a directory entry.  Since
192355d6bb5Sswilcox 	 * entries can't span sectors, there's no worry about having
193355d6bb5Sswilcox 	 * a good beginning in one sector and the rest in the next,
194355d6bb5Sswilcox 	 * where that second sector was unreadable and therefore
195355d6bb5Sswilcox 	 * replaced with zeros.
196355d6bb5Sswilcox 	 */
1977c478bd9Sstevel@tonic-gate 	bp = getdirblk(idesc->id_blkno, blksiz);
198355d6bb5Sswilcox 	/* LINTED b_buf is aligned and id_loc was verified above */
1997c478bd9Sstevel@tonic-gate 	dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	/*
2027c478bd9Sstevel@tonic-gate 	 * Check the current entry in the directory.
2037c478bd9Sstevel@tonic-gate 	 */
2047c478bd9Sstevel@tonic-gate 	if (dircheck(idesc, dp) == 0) {
2057c478bd9Sstevel@tonic-gate 		/*
2067c478bd9Sstevel@tonic-gate 		 * If we are in here, then either the current directory
2077c478bd9Sstevel@tonic-gate 		 * entry is bad or the next directory entry is bad.
2087c478bd9Sstevel@tonic-gate 		 */
2097c478bd9Sstevel@tonic-gate next_is_bad:
2107c478bd9Sstevel@tonic-gate 		/*
2117c478bd9Sstevel@tonic-gate 		 * Find the amount of space left to the end of the
2127c478bd9Sstevel@tonic-gate 		 * directory block for either directory entry.
2137c478bd9Sstevel@tonic-gate 		 */
2147c478bd9Sstevel@tonic-gate 		size = DIRBLKSIZ - (idesc->id_loc & (DIRBLKSIZ - 1));
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 		/*
2177c478bd9Sstevel@tonic-gate 		 * Advance to the end of the directory block.
2187c478bd9Sstevel@tonic-gate 		 */
2197c478bd9Sstevel@tonic-gate 		idesc->id_loc += size;
2207c478bd9Sstevel@tonic-gate 		idesc->id_filesize -= (offset_t)size;
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 		/*
2237c478bd9Sstevel@tonic-gate 		 * Ask the question before we fix the in-core directory
2247c478bd9Sstevel@tonic-gate 		 * block because dofix() may reuse the buffer.
2257c478bd9Sstevel@tonic-gate 		 */
226355d6bb5Sswilcox 		salvaged = (idesc->id_fix == DONTKNOW);
2277c478bd9Sstevel@tonic-gate 		dofixret = dofix(idesc, "DIRECTORY CORRUPTED");
228355d6bb5Sswilcox 
229355d6bb5Sswilcox 		/*
230355d6bb5Sswilcox 		 * If there was an error reading the block, then that
231355d6bb5Sswilcox 		 * same error can reasonably be expected to have occurred
232355d6bb5Sswilcox 		 * when it was read previously.  As such, the decision
233355d6bb5Sswilcox 		 * to come here was based on the results of that partially-
234355d6bb5Sswilcox 		 * zerod block, and so anything we change should be
235355d6bb5Sswilcox 		 * based on it as well.  Upshot: no need to check for
236355d6bb5Sswilcox 		 * errors here.
237355d6bb5Sswilcox 		 */
2387c478bd9Sstevel@tonic-gate 		bp = getdirblk(idesc->id_blkno, blksiz);
239355d6bb5Sswilcox 		/* LINTED b_buf is aligned and id_loc/origloc was verified */
2407c478bd9Sstevel@tonic-gate 		dp = (struct direct *)(bp->b_un.b_buf + origloc);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 		/*
2437c478bd9Sstevel@tonic-gate 		 * This is the current directory entry and since it is
2447c478bd9Sstevel@tonic-gate 		 * corrupt we cannot trust the rest of the directory
2457c478bd9Sstevel@tonic-gate 		 * block so change the current directory entry to
2467c478bd9Sstevel@tonic-gate 		 * contain nothing and encompass the rest of the block.
2477c478bd9Sstevel@tonic-gate 		 */
2487c478bd9Sstevel@tonic-gate 		if (ndp == NULL) {
2497c478bd9Sstevel@tonic-gate 			dp->d_reclen = size;
2507c478bd9Sstevel@tonic-gate 			dp->d_ino = 0;
2517c478bd9Sstevel@tonic-gate 			dp->d_namlen = 0;
2527c478bd9Sstevel@tonic-gate 			dp->d_name[0] = '\0';
2537c478bd9Sstevel@tonic-gate 		}
2547c478bd9Sstevel@tonic-gate 		/*
2557c478bd9Sstevel@tonic-gate 		 * This is the next directory entry, i.e., we got here
256355d6bb5Sswilcox 		 * via a "goto next_is_bad".  That directory entry is
257355d6bb5Sswilcox 		 * corrupt.  However, the current directory entry is okay
258355d6bb5Sswilcox 		 * so if we are in fix mode, just extend its record size
259355d6bb5Sswilcox 		 * to encompass the rest of the block.
2607c478bd9Sstevel@tonic-gate 		 */
2617c478bd9Sstevel@tonic-gate 		else if (dofixret) {
2627c478bd9Sstevel@tonic-gate 			dp->d_reclen += size;
2637c478bd9Sstevel@tonic-gate 		}
2647c478bd9Sstevel@tonic-gate 		/*
2657c478bd9Sstevel@tonic-gate 		 * If the user said to fix the directory corruption, then
2667c478bd9Sstevel@tonic-gate 		 * mark the block as dirty.  Otherwise, our "repairs" only
2677c478bd9Sstevel@tonic-gate 		 * apply to the in-core copy so we don't hand back trash
2687c478bd9Sstevel@tonic-gate 		 * to the caller.
2697c478bd9Sstevel@tonic-gate 		 *
2707c478bd9Sstevel@tonic-gate 		 * Note: It is possible that saying "no" to a change in
2717c478bd9Sstevel@tonic-gate 		 * one part of the I/O buffer and "yes" to a later change
2727c478bd9Sstevel@tonic-gate 		 * in the same I/O buffer may still flush the change to
273355d6bb5Sswilcox 		 * which we said "no". This is the pathological case and
2747c478bd9Sstevel@tonic-gate 		 * no fix is planned at this time.
2757c478bd9Sstevel@tonic-gate 		 */
276355d6bb5Sswilcox 		if (dofixret) {
2777c478bd9Sstevel@tonic-gate 			dirty(bp);
278355d6bb5Sswilcox 			if (preen && salvaged)
279355d6bb5Sswilcox 				(void) printf(" (SALVAGED)\n");
280355d6bb5Sswilcox 			if (idesc->id_number == lfdir)
281355d6bb5Sswilcox 				lfdir = 0;
282355d6bb5Sswilcox 		}
283355d6bb5Sswilcox 
284355d6bb5Sswilcox 		/*
285355d6bb5Sswilcox 		 * dp points into bp, which will get re-used at some
286355d6bb5Sswilcox 		 * arbitrary time in the future.  We rely on the fact
287355d6bb5Sswilcox 		 * that we're singled-threaded, and that we'll be done
288355d6bb5Sswilcox 		 * with this directory entry by the time the next one
289355d6bb5Sswilcox 		 * is needed.
290355d6bb5Sswilcox 		 */
2917c478bd9Sstevel@tonic-gate 		return (dp);
2927c478bd9Sstevel@tonic-gate 	}
2937c478bd9Sstevel@tonic-gate 	/*
2947c478bd9Sstevel@tonic-gate 	 * The current directory entry checked out so advance past it.
2957c478bd9Sstevel@tonic-gate 	 */
2967c478bd9Sstevel@tonic-gate 	idesc->id_loc += dp->d_reclen;
2977c478bd9Sstevel@tonic-gate 	idesc->id_filesize -= (offset_t)dp->d_reclen;
2987c478bd9Sstevel@tonic-gate 	/*
2997c478bd9Sstevel@tonic-gate 	 * If we are not at the directory block boundary, then peek
3007c478bd9Sstevel@tonic-gate 	 * at the next directory entry and if it is bad we can add
3017c478bd9Sstevel@tonic-gate 	 * its space to the current directory entry (compression).
3027c478bd9Sstevel@tonic-gate 	 * Again, we sanity check the id_loc and id_filesize fields
3037c478bd9Sstevel@tonic-gate 	 * since we modified them above.
3047c478bd9Sstevel@tonic-gate 	 */
305355d6bb5Sswilcox 	if ((idesc->id_loc & (DIRBLKSIZ - 1)) &&	/* not at start */
306355d6bb5Sswilcox 	    (idesc->id_loc < blksiz) &&			/* within block */
307355d6bb5Sswilcox 	    ((idesc->id_loc & 3) == 0) &&		/* properly aligned */
308355d6bb5Sswilcox 	    (idesc->id_filesize > 0)) {			/* data follows */
309355d6bb5Sswilcox 		/* LINTED b_buf is aligned and id_loc verified to be ok */
3107c478bd9Sstevel@tonic-gate 		ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
3117c478bd9Sstevel@tonic-gate 		if (dircheck(idesc, ndp) == 0)
3127c478bd9Sstevel@tonic-gate 			goto next_is_bad;
3137c478bd9Sstevel@tonic-gate 	}
3147c478bd9Sstevel@tonic-gate 
315355d6bb5Sswilcox 	/*
316355d6bb5Sswilcox 	 * See comment above about dp pointing into bp.
317355d6bb5Sswilcox 	 */
3187c478bd9Sstevel@tonic-gate 	return (dp);
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate /*
3227c478bd9Sstevel@tonic-gate  * Verify that a directory entry is valid.
3237c478bd9Sstevel@tonic-gate  * This is a superset of the checks made in the kernel.
3247c478bd9Sstevel@tonic-gate  */
325355d6bb5Sswilcox static int
dircheck(struct inodesc * idesc,struct direct * dp)326355d6bb5Sswilcox dircheck(struct inodesc *idesc, struct direct *dp)
3277c478bd9Sstevel@tonic-gate {
328355d6bb5Sswilcox 	size_t size;
3297c478bd9Sstevel@tonic-gate 	char *cp;
3307c478bd9Sstevel@tonic-gate 	int spaceleft;
3317c478bd9Sstevel@tonic-gate 
332355d6bb5Sswilcox 	/*
333355d6bb5Sswilcox 	 * Recall that id_filesize is the number of bytes left to
334355d6bb5Sswilcox 	 * process in the directory.  We check id_filesize >= size
335355d6bb5Sswilcox 	 * instead of id_filesize >= d_reclen because all that the
336355d6bb5Sswilcox 	 * directory is actually required to contain is the entry
337355d6bb5Sswilcox 	 * itself (and it's how the kernel does the allocation).
338355d6bb5Sswilcox 	 *
339355d6bb5Sswilcox 	 * We indirectly check for d_reclen going past the end of
340355d6bb5Sswilcox 	 * the allocated space by comparing it against spaceleft.
341355d6bb5Sswilcox 	 */
3427c478bd9Sstevel@tonic-gate 	size = DIRSIZ(dp);
3437c478bd9Sstevel@tonic-gate 	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
3447c478bd9Sstevel@tonic-gate 	if (dp->d_ino < maxino &&
3457c478bd9Sstevel@tonic-gate 	    dp->d_reclen != 0 &&
3467c478bd9Sstevel@tonic-gate 	    (int)dp->d_reclen <= spaceleft &&
3477c478bd9Sstevel@tonic-gate 	    (dp->d_reclen & 0x3) == 0 &&
3487c478bd9Sstevel@tonic-gate 	    (int)dp->d_reclen >= size &&
3497c478bd9Sstevel@tonic-gate 	    idesc->id_filesize >= (offset_t)size &&
3507c478bd9Sstevel@tonic-gate 	    dp->d_namlen <= MAXNAMLEN) {
3517c478bd9Sstevel@tonic-gate 		if (dp->d_ino == 0)
3527c478bd9Sstevel@tonic-gate 			return (1);
353355d6bb5Sswilcox 		for (cp = dp->d_name, size = 0; size < (size_t)dp->d_namlen;
3547c478bd9Sstevel@tonic-gate 								size++, cp++)
355355d6bb5Sswilcox 			if ((*cp == '\0') || (*cp == '/'))
356355d6bb5Sswilcox 				goto bad;
357355d6bb5Sswilcox 		if (*cp == '\0')
3587c478bd9Sstevel@tonic-gate 			return (1);
3597c478bd9Sstevel@tonic-gate 	}
360355d6bb5Sswilcox bad:
361355d6bb5Sswilcox 	if (debug) {
362355d6bb5Sswilcox 		(void) printf("Bad dir in inode %d at lbn %d, loc %d:\n",
363355d6bb5Sswilcox 		    idesc->id_number, idesc->id_lbn, idesc->id_loc);
364355d6bb5Sswilcox 		(void) printf("    ino %d reclen %d namlen %d name `%s'\n",
365355d6bb5Sswilcox 		    dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_name);
3667c478bd9Sstevel@tonic-gate 	}
367355d6bb5Sswilcox 	return (0);
3687c478bd9Sstevel@tonic-gate }
3697c478bd9Sstevel@tonic-gate 
370355d6bb5Sswilcox void
adjust(struct inodesc * idesc,int lcnt)371355d6bb5Sswilcox adjust(struct inodesc *idesc, int lcnt)
3727c478bd9Sstevel@tonic-gate {
3737c478bd9Sstevel@tonic-gate 	struct dinode *dp;
374355d6bb5Sswilcox 	caddr_t flow;
375355d6bb5Sswilcox 	int saveiscorrupt;
376355d6bb5Sswilcox 	struct inodesc lcidesc;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	dp = ginode(idesc->id_number);
3797c478bd9Sstevel@tonic-gate 	if (dp->di_nlink == lcnt) {
380355d6bb5Sswilcox 		/*
381355d6bb5Sswilcox 		 * If we have not hit any unresolved problems, are running
382355d6bb5Sswilcox 		 * in preen mode, and are on a file system using logging,
383355d6bb5Sswilcox 		 * then just toss any partially allocated files, as they are
384355d6bb5Sswilcox 		 * an expected occurrence.
385355d6bb5Sswilcox 		 */
386355d6bb5Sswilcox 		if (!iscorrupt && preen && islog) {
387355d6bb5Sswilcox 			clri(idesc, "UNREF", CLRI_VERBOSE, CLRI_NOP_OK);
3887c478bd9Sstevel@tonic-gate 			return;
389355d6bb5Sswilcox 		} else {
390355d6bb5Sswilcox 			/*
391355d6bb5Sswilcox 			 * The file system can be considered clean even if
392355d6bb5Sswilcox 			 * a file is not linked up, but is cleared.  In
393355d6bb5Sswilcox 			 * other words, the kernel won't panic over it.
394355d6bb5Sswilcox 			 * Hence, iscorrupt should not be set when
395355d6bb5Sswilcox 			 * linkup is answered no, but clri is answered yes.
396355d6bb5Sswilcox 			 *
397355d6bb5Sswilcox 			 * If neither is answered yes, then we have a
398355d6bb5Sswilcox 			 * non-panic-inducing known corruption that the
399355d6bb5Sswilcox 			 * user needs to be reminded of when we exit.
400355d6bb5Sswilcox 			 */
401355d6bb5Sswilcox 			saveiscorrupt = iscorrupt;
402355d6bb5Sswilcox 			if (linkup(idesc->id_number, (fsck_ino_t)0,
403355d6bb5Sswilcox 			    NULL) == 0) {
404355d6bb5Sswilcox 				iscorrupt = saveiscorrupt;
405355d6bb5Sswilcox 				clri(idesc, "UNREF", CLRI_QUIET, CLRI_NOP_OK);
406355d6bb5Sswilcox 				if (statemap[idesc->id_number] != USTATE)
407355d6bb5Sswilcox 					iscorrupt = 1;
408355d6bb5Sswilcox 				return;
409355d6bb5Sswilcox 			}
410355d6bb5Sswilcox 			dp = ginode(idesc->id_number);
4117c478bd9Sstevel@tonic-gate 		}
412355d6bb5Sswilcox 		lcnt = lncntp[idesc->id_number];
413355d6bb5Sswilcox 	}
414355d6bb5Sswilcox 
415355d6bb5Sswilcox 	/*
416355d6bb5Sswilcox 	 * It doesn't happen often, but it's possible to get a true
417355d6bb5Sswilcox 	 * excess of links (especially if a lot of directories got
418355d6bb5Sswilcox 	 * orphaned and reattached to lost+found).  Instead of wrapping
419355d6bb5Sswilcox 	 * around, do something semi-useful (i.e., give progress towards
420355d6bb5Sswilcox 	 * a less-broken filesystem) when this happens.
421355d6bb5Sswilcox 	 */
422355d6bb5Sswilcox 	LINK_RANGE(flow, dp->di_nlink, -lcnt);
423355d6bb5Sswilcox 	if (flow != NULL) {
424355d6bb5Sswilcox 		LINK_CLEAR(flow, idesc->id_number, dp->di_mode, &lcidesc);
425355d6bb5Sswilcox 		if (statemap[idesc->id_number] == USTATE)
426355d6bb5Sswilcox 			return;
4277c478bd9Sstevel@tonic-gate 	}
428355d6bb5Sswilcox 
429355d6bb5Sswilcox 	dp = ginode(idesc->id_number);
4307c478bd9Sstevel@tonic-gate 	if (lcnt && dp->di_nlink != lcnt) {
4317c478bd9Sstevel@tonic-gate 		pwarn("LINK COUNT %s",
432355d6bb5Sswilcox 		    file_id(idesc->id_number, dp->di_mode));
4337c478bd9Sstevel@tonic-gate 		pinode(idesc->id_number);
434355d6bb5Sswilcox 		dp = ginode(idesc->id_number);
435355d6bb5Sswilcox 		(void) printf(" COUNT %d SHOULD BE %d",
4367c478bd9Sstevel@tonic-gate 		    dp->di_nlink, dp->di_nlink - lcnt);
437355d6bb5Sswilcox 		/*
438355d6bb5Sswilcox 		 * Even lost+found is subject to this, as whenever
439355d6bb5Sswilcox 		 * we modify it, we update both the in-memory and
440355d6bb5Sswilcox 		 * on-disk counts.  Thus, they should still be in
441355d6bb5Sswilcox 		 * sync.
442355d6bb5Sswilcox 		 */
4437c478bd9Sstevel@tonic-gate 		if (preen) {
4447c478bd9Sstevel@tonic-gate 			if (lcnt < 0) {
445355d6bb5Sswilcox 				(void) printf("\n");
4467c478bd9Sstevel@tonic-gate 				if ((dp->di_mode & IFMT) == IFSHAD)
4477c478bd9Sstevel@tonic-gate 					pwarn("LINK COUNT INCREASING");
4487c478bd9Sstevel@tonic-gate 				else
4497c478bd9Sstevel@tonic-gate 					pfatal("LINK COUNT INCREASING");
4507c478bd9Sstevel@tonic-gate 			}
4517c478bd9Sstevel@tonic-gate 		}
4527c478bd9Sstevel@tonic-gate 		if (preen || reply("ADJUST") == 1) {
4537c478bd9Sstevel@tonic-gate 			dp->di_nlink -= lcnt;
4547c478bd9Sstevel@tonic-gate 			inodirty();
455355d6bb5Sswilcox 			if (preen)
456355d6bb5Sswilcox 				(void) printf(" (ADJUSTED)\n");
457355d6bb5Sswilcox 		} else if (((dp->di_mode & IFMT) == IFDIR) ||
458355d6bb5Sswilcox 		    ((dp->di_mode & IFMT) == IFATTRDIR)) {
459355d6bb5Sswilcox 			/*
460355d6bb5Sswilcox 			 * File counts can be off relatively harmlessly,
461355d6bb5Sswilcox 			 * but a bad directory count can cause the
462355d6bb5Sswilcox 			 * kernel to lose its mind.
463355d6bb5Sswilcox 			 */
464355d6bb5Sswilcox 			iscorrupt = 1;
4657c478bd9Sstevel@tonic-gate 		}
4667c478bd9Sstevel@tonic-gate 	}
4677c478bd9Sstevel@tonic-gate }
4687c478bd9Sstevel@tonic-gate 
469355d6bb5Sswilcox static int
mkentry(struct inodesc * idesc)470355d6bb5Sswilcox mkentry(struct inodesc *idesc)
4717c478bd9Sstevel@tonic-gate {
4727c478bd9Sstevel@tonic-gate 	struct direct *dirp = idesc->id_dirp;
4737c478bd9Sstevel@tonic-gate 	struct direct newent;
4747c478bd9Sstevel@tonic-gate 	int newlen, oldlen;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	newent.d_namlen = strlen(idesc->id_name);
4777c478bd9Sstevel@tonic-gate 	newlen = DIRSIZ(&newent);
4787c478bd9Sstevel@tonic-gate 	if (dirp->d_ino != 0)
4797c478bd9Sstevel@tonic-gate 		oldlen = DIRSIZ(dirp);
4807c478bd9Sstevel@tonic-gate 	else
4817c478bd9Sstevel@tonic-gate 		oldlen = 0;
4827c478bd9Sstevel@tonic-gate 	if ((int)dirp->d_reclen - oldlen < newlen)
4837c478bd9Sstevel@tonic-gate 		return (KEEPON);
4847c478bd9Sstevel@tonic-gate 	newent.d_reclen = dirp->d_reclen - (ushort_t)oldlen;
4857c478bd9Sstevel@tonic-gate 	dirp->d_reclen = (ushort_t)oldlen;
486355d6bb5Sswilcox 
487355d6bb5Sswilcox 	/* LINTED dirp is aligned and DIRSIZ() forces oldlen to be aligned */
4887c478bd9Sstevel@tonic-gate 	dirp = (struct direct *)(((char *)dirp) + oldlen);
4897c478bd9Sstevel@tonic-gate 	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
4907c478bd9Sstevel@tonic-gate 	dirp->d_reclen = newent.d_reclen;
4917c478bd9Sstevel@tonic-gate 	dirp->d_namlen = newent.d_namlen;
492355d6bb5Sswilcox 	(void) memmove(dirp->d_name, idesc->id_name,
493355d6bb5Sswilcox 	    (size_t)newent.d_namlen + 1);
494355d6bb5Sswilcox 
4957c478bd9Sstevel@tonic-gate 	return (ALTERED|STOP);
4967c478bd9Sstevel@tonic-gate }
4977c478bd9Sstevel@tonic-gate 
498355d6bb5Sswilcox static int
chgino(struct inodesc * idesc)499355d6bb5Sswilcox chgino(struct inodesc *idesc)
5007c478bd9Sstevel@tonic-gate {
5017c478bd9Sstevel@tonic-gate 	struct direct *dirp = idesc->id_dirp;
5027c478bd9Sstevel@tonic-gate 
503355d6bb5Sswilcox 	if (memcmp(dirp->d_name, idesc->id_name,
504355d6bb5Sswilcox 	    (size_t)dirp->d_namlen + 1) != 0)
5057c478bd9Sstevel@tonic-gate 		return (KEEPON);
5067c478bd9Sstevel@tonic-gate 	dirp->d_ino = idesc->id_parent;
5077c478bd9Sstevel@tonic-gate 	return (ALTERED|STOP);
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate 
510355d6bb5Sswilcox int
linkup(fsck_ino_t orphan,fsck_ino_t parentdir,caddr_t name)511355d6bb5Sswilcox linkup(fsck_ino_t orphan, fsck_ino_t parentdir, caddr_t name)
5127c478bd9Sstevel@tonic-gate {
513355d6bb5Sswilcox 	int rval;
5147c478bd9Sstevel@tonic-gate 	struct dinode *dp;
5157c478bd9Sstevel@tonic-gate 	int lostdir;
5167c478bd9Sstevel@tonic-gate 	int lostshadow;
517355d6bb5Sswilcox 	fsck_ino_t oldlfdir;
518355d6bb5Sswilcox 	fsck_ino_t *intree;
5197c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
5207c478bd9Sstevel@tonic-gate 
521355d6bb5Sswilcox 	init_inodesc(&idesc);
5227c478bd9Sstevel@tonic-gate 	dp = ginode(orphan);
5237c478bd9Sstevel@tonic-gate 	lostdir = (((dp->di_mode & IFMT) == IFDIR) ||
5247c478bd9Sstevel@tonic-gate 	    ((dp->di_mode & IFMT) == IFATTRDIR));
525355d6bb5Sswilcox 	if (debug && lostdir && dp->di_nlink <= 0 && lncntp[orphan] == -1)
526355d6bb5Sswilcox 		(void) printf(
527355d6bb5Sswilcox 		    "old fsck would have left inode %d for reclaim thread\n",
528355d6bb5Sswilcox 		    orphan);
5297c478bd9Sstevel@tonic-gate 	lostshadow = (dp->di_mode & IFMT) == IFSHAD;
530355d6bb5Sswilcox 	pwarn("UNREF %s ", file_id(orphan, dp->di_mode));
5317c478bd9Sstevel@tonic-gate 	pinode(orphan);
5327c478bd9Sstevel@tonic-gate 	if (lostshadow || (dp->di_size == 0 && dp->di_oeftflag == 0))
5337c478bd9Sstevel@tonic-gate 		return (0);
534355d6bb5Sswilcox 	if (!preen && (reply("RECONNECT") == 0))
535355d6bb5Sswilcox 		goto noconnect;
536355d6bb5Sswilcox 
5377c478bd9Sstevel@tonic-gate 	if (lfdir == 0) {
5387c478bd9Sstevel@tonic-gate 		dp = ginode(UFSROOTINO);
5397c478bd9Sstevel@tonic-gate 		idesc.id_name = lfname;
5407c478bd9Sstevel@tonic-gate 		idesc.id_type = DATA;
5417c478bd9Sstevel@tonic-gate 		idesc.id_func = findino;
5427c478bd9Sstevel@tonic-gate 		idesc.id_number = UFSROOTINO;
5437c478bd9Sstevel@tonic-gate 		idesc.id_fix = DONTKNOW;
544355d6bb5Sswilcox 		if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) != 0) {
5457c478bd9Sstevel@tonic-gate 			lfdir = idesc.id_parent;
5467c478bd9Sstevel@tonic-gate 		} else {
547355d6bb5Sswilcox 			pwarn("NO %s DIRECTORY", lfname);
548355d6bb5Sswilcox 			if (preen || reply("CREATE") == 1) {
549355d6bb5Sswilcox 				lfdir = newdir(UFSROOTINO, (fsck_ino_t)0,
550355d6bb5Sswilcox 				    lfmode, lfname);
5517c478bd9Sstevel@tonic-gate 				if (lfdir != 0) {
552355d6bb5Sswilcox 					if (preen)
553355d6bb5Sswilcox 						(void) printf(" (CREATED)\n");
554355d6bb5Sswilcox 					else
555355d6bb5Sswilcox 						(void) printf("\n");
556355d6bb5Sswilcox 					statemap[lfdir] |= INFOUND;
557355d6bb5Sswilcox 					/*
558355d6bb5Sswilcox 					 * XXX What if we allocate an inode
559355d6bb5Sswilcox 					 * that's already been scanned?  Then
560355d6bb5Sswilcox 					 * we need to leave lnctnp[] alone.
561355d6bb5Sswilcox 					 */
562355d6bb5Sswilcox 					TRACK_LNCNTP(UFSROOTINO,
563355d6bb5Sswilcox 					    lncntp[UFSROOTINO]++);
5647c478bd9Sstevel@tonic-gate 				}
5657c478bd9Sstevel@tonic-gate 			}
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 		if (lfdir == 0) {
568355d6bb5Sswilcox 			pfatal("SORRY. CANNOT CREATE %s DIRECTORY\n", lfname);
569355d6bb5Sswilcox 			pwarn("Could not reconnect inode %d\n", orphan);
570355d6bb5Sswilcox 			goto noconnect;
571355d6bb5Sswilcox 		} else {
572355d6bb5Sswilcox 			/*
573355d6bb5Sswilcox 			 * We searched for it via the namespace, so by
574355d6bb5Sswilcox 			 * definition it's been found.  We have to do this
575355d6bb5Sswilcox 			 * because it is possible that we're called before
576355d6bb5Sswilcox 			 * the full namespace mapping is complete (especially
577355d6bb5Sswilcox 			 * from pass 1, if it encounters a corrupt directory
578355d6bb5Sswilcox 			 * that has to be cleared).
579355d6bb5Sswilcox 			 */
580355d6bb5Sswilcox 			statemap[lfdir] |= INFOUND;
5817c478bd9Sstevel@tonic-gate 		}
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 	dp = ginode(lfdir);
5847c478bd9Sstevel@tonic-gate 	if ((dp->di_mode & IFMT) != IFDIR) {
585355d6bb5Sswilcox 		pfatal("%s IS NOT A DIRECTORY", lfname);
586355d6bb5Sswilcox 		if (reply("REALLOCATE") == 0) {
587355d6bb5Sswilcox 			iscorrupt = 1;
588355d6bb5Sswilcox 			goto noconnect;
5897c478bd9Sstevel@tonic-gate 		}
590355d6bb5Sswilcox 		oldlfdir = lfdir;
591355d6bb5Sswilcox 		lfdir = reallocdir(UFSROOTINO, (fsck_ino_t)0, lfmode, lfname);
592355d6bb5Sswilcox 		if (lfdir == 0) {
593355d6bb5Sswilcox 			iscorrupt = 1;
594355d6bb5Sswilcox 			pfatal("SORRY. CANNOT CREATE %s DIRECTORY\n\n",
595355d6bb5Sswilcox 			    lfname);
596355d6bb5Sswilcox 			goto noconnect;
5977c478bd9Sstevel@tonic-gate 		}
5987c478bd9Sstevel@tonic-gate 		inodirty();
599355d6bb5Sswilcox 		statemap[lfdir] |= INFOUND;
600355d6bb5Sswilcox 		freeino(oldlfdir, TI_PARENT);
6017c478bd9Sstevel@tonic-gate 	}
6027c478bd9Sstevel@tonic-gate 	if (statemap[lfdir] != DFOUND) {
603355d6bb5Sswilcox 		/*
604355d6bb5Sswilcox 		 * Not a consistency problem of the sort that'll
605355d6bb5Sswilcox 		 * cause the kernel heartburn, so don't set iscorrupt.
606355d6bb5Sswilcox 		 */
607355d6bb5Sswilcox 		if (debug)
608355d6bb5Sswilcox 			(void) printf("lfdir %d is in state 0x%x\n",
609355d6bb5Sswilcox 			    lfdir, (int)statemap[lfdir]);
610355d6bb5Sswilcox 		lfdir = 0;
611355d6bb5Sswilcox 		pfatal("SORRY. %s DIRECTORY DISAPPEARED\n\n", lfname);
612355d6bb5Sswilcox 		pwarn("Could not reconnect inode %d\n", orphan);
613355d6bb5Sswilcox 		goto noconnect;
6147c478bd9Sstevel@tonic-gate 	}
615355d6bb5Sswilcox 
616355d6bb5Sswilcox 	rval = do_reconnect(orphan, parentdir, name);
617355d6bb5Sswilcox 
618355d6bb5Sswilcox 	return (rval);
619355d6bb5Sswilcox 
620355d6bb5Sswilcox 	/*
621355d6bb5Sswilcox 	 * Leaving things unconnected is harmless as far as trying to
622355d6bb5Sswilcox 	 * use the filesystem later, so don't set iscorrupt yet (it's
623355d6bb5Sswilcox 	 * just lost blocks and inodes, after all).
624355d6bb5Sswilcox 	 *
625355d6bb5Sswilcox 	 * Lost directories get noted for reporting after all checks
626355d6bb5Sswilcox 	 * are done - they may get cleared later.
627355d6bb5Sswilcox 	 */
628355d6bb5Sswilcox noconnect:
629355d6bb5Sswilcox 	if (lostdir) {
630355d6bb5Sswilcox 		intree = tsearch((void *)orphan, &limbo_dirs,
631355d6bb5Sswilcox 		    ino_t_cmp);
632355d6bb5Sswilcox 		if (intree == NULL)
633355d6bb5Sswilcox 			errexit("linkup: out of memory");
634355d6bb5Sswilcox 	}
635355d6bb5Sswilcox 	return (0);
636355d6bb5Sswilcox }
637355d6bb5Sswilcox 
638355d6bb5Sswilcox /*
639355d6bb5Sswilcox  * Connect an orphaned inode to lost+found.
640355d6bb5Sswilcox  *
641355d6bb5Sswilcox  * Returns non-zero for success, zero for failure.
642355d6bb5Sswilcox  */
643355d6bb5Sswilcox static int
do_reconnect(fsck_ino_t orphan,fsck_ino_t parentdir,caddr_t name)644355d6bb5Sswilcox do_reconnect(fsck_ino_t orphan, fsck_ino_t parentdir, caddr_t name)
645355d6bb5Sswilcox {
646355d6bb5Sswilcox 	caddr_t flow_msg;
647355d6bb5Sswilcox 	struct dinode *dp;
648355d6bb5Sswilcox 	int lostdir;
649355d6bb5Sswilcox 	mode_t mode;
650355d6bb5Sswilcox 	fsck_ino_t *intree;
651355d6bb5Sswilcox 	struct inodesc idesc;
652355d6bb5Sswilcox 
653355d6bb5Sswilcox 	dp = ginode(orphan);
654355d6bb5Sswilcox 	mode = dp->di_mode & IFMT;
655355d6bb5Sswilcox 	lostdir = (mode == IFDIR) || (mode == IFATTRDIR);
656355d6bb5Sswilcox 
657355d6bb5Sswilcox 	name = mkuniqname(name, lfname, lfdir, orphan);
658355d6bb5Sswilcox 	if (name == NULL)
659355d6bb5Sswilcox 		goto noconnect;
660355d6bb5Sswilcox 	if (makeentry(lfdir, orphan, name) == 0) {
661355d6bb5Sswilcox 		pfatal("SORRY. NO SPACE IN %s DIRECTORY\n", lfname);
662355d6bb5Sswilcox 		pwarn("Could not reconnect inode %d\n", orphan);
663355d6bb5Sswilcox 		goto noconnect;
6647c478bd9Sstevel@tonic-gate 	}
665355d6bb5Sswilcox 
666355d6bb5Sswilcox 	dp = ginode(orphan);
667355d6bb5Sswilcox 	LINK_RANGE(flow_msg, lncntp[orphan], -1);
668355d6bb5Sswilcox 	if (flow_msg != NULL) {
669355d6bb5Sswilcox 		LINK_CLEAR(flow_msg, orphan, dp->di_mode, &idesc);
670355d6bb5Sswilcox 		if (statemap[orphan] == USTATE)
671355d6bb5Sswilcox 			goto noconnect;
672355d6bb5Sswilcox 	}
673355d6bb5Sswilcox 	TRACK_LNCNTP(orphan, lncntp[orphan]--);
674355d6bb5Sswilcox 
675355d6bb5Sswilcox 	/*
676355d6bb5Sswilcox 	 * Make sure that anything we put into the normal namespace
677355d6bb5Sswilcox 	 * looks like it belongs there.  Attributes can only be in
678355d6bb5Sswilcox 	 * attribute directories, not the normal directory lost+found.
679355d6bb5Sswilcox 	 */
680355d6bb5Sswilcox 	maybe_convert_attrdir_to_dir(orphan);
681355d6bb5Sswilcox 
6827c478bd9Sstevel@tonic-gate 	if (lostdir) {
683355d6bb5Sswilcox 		/*
684355d6bb5Sswilcox 		 * Can't be creating a duplicate entry with makeentry(),
685355d6bb5Sswilcox 		 * because changeino() will succeed if ".." already
686355d6bb5Sswilcox 		 * exists.
687355d6bb5Sswilcox 		 */
6887c478bd9Sstevel@tonic-gate 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
689355d6bb5Sswilcox 		    parentdir != (fsck_ino_t)-1)
6907c478bd9Sstevel@tonic-gate 			(void) makeentry(orphan, lfdir, "..");
6917c478bd9Sstevel@tonic-gate 		/*
6927c478bd9Sstevel@tonic-gate 		 * If we were half-detached, don't try to get
6937c478bd9Sstevel@tonic-gate 		 * inode 0 later on.
6947c478bd9Sstevel@tonic-gate 		 */
6957c478bd9Sstevel@tonic-gate 		if (parentdir == 0)
6967c478bd9Sstevel@tonic-gate 			parentdir = -1;
697355d6bb5Sswilcox 		/*
698355d6bb5Sswilcox 		 * Fix up link counts.
699355d6bb5Sswilcox 		 *
700355d6bb5Sswilcox 		 * XXX This section is getting pretty byzantine, espcially
701355d6bb5Sswilcox 		 * when combined with changeino()/chgino()'s link manipulation.
702355d6bb5Sswilcox 		 */
703355d6bb5Sswilcox 		LFDIR_LINK_RANGE_RVAL(flow_msg, lncntp[lfdir], 1, &idesc, 0);
704355d6bb5Sswilcox 		TRACK_LNCNTP(lfdir, lncntp[lfdir]--);
705355d6bb5Sswilcox 		pwarn("DIR I=%lu CONNECTED. ", (long)orphan);
706355d6bb5Sswilcox 		reattached_dir = 1;
707355d6bb5Sswilcox 		if (parentdir != (fsck_ino_t)-1) {
708355d6bb5Sswilcox 			/*
709355d6bb5Sswilcox 			 * Have to clear the parent's reference.  Otherwise,
710355d6bb5Sswilcox 			 * if it's an orphan, then we may clear this orphan
711355d6bb5Sswilcox 			 * in pass 4 even though we've reconnected it.
712355d6bb5Sswilcox 			 *
713355d6bb5Sswilcox 			 * We already have the reference count
714355d6bb5Sswilcox 			 * allowing for a parent link, so undo the
715355d6bb5Sswilcox 			 * adjustment done above.  Otherwise we come
716355d6bb5Sswilcox 			 * out high by one.
717355d6bb5Sswilcox 			 */
718355d6bb5Sswilcox 			(void) printf("PARENT WAS I=%lu\n", (long)parentdir);
719355d6bb5Sswilcox 			(void) cleardirentry(parentdir, orphan);
7207c478bd9Sstevel@tonic-gate 		}
721355d6bb5Sswilcox 		if (!preen)
722355d6bb5Sswilcox 			(void) printf("\n");
723355d6bb5Sswilcox 	} else if (preen) {
724355d6bb5Sswilcox 		(void) printf(" (RECONNECTED)\n");
7257c478bd9Sstevel@tonic-gate 	}
726355d6bb5Sswilcox 
727355d6bb5Sswilcox 	statemap[orphan] &= ~INDELAYD;
7287c478bd9Sstevel@tonic-gate 	return (1);
729355d6bb5Sswilcox 
730355d6bb5Sswilcox 	/*
731355d6bb5Sswilcox 	 * Leaving things unconnected is harmless as far as trying to
732355d6bb5Sswilcox 	 * use the filesystem later, so don't set iscorrupt yet (it's
733355d6bb5Sswilcox 	 * just lost blocks and inodes, after all).
734355d6bb5Sswilcox 	 *
735355d6bb5Sswilcox 	 * Lost directories get noted for reporting after all checks
736355d6bb5Sswilcox 	 * are done - they may get cleared later.
737355d6bb5Sswilcox 	 */
738355d6bb5Sswilcox noconnect:
739355d6bb5Sswilcox 	if (lostdir) {
740355d6bb5Sswilcox 		intree = tsearch((void *)orphan, &limbo_dirs,
741355d6bb5Sswilcox 		    ino_t_cmp);
742355d6bb5Sswilcox 		if (intree == NULL)
743355d6bb5Sswilcox 		errexit("linkup: out of memory");
744355d6bb5Sswilcox 	}
745355d6bb5Sswilcox 	return (0);
7467c478bd9Sstevel@tonic-gate }
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate /*
7497c478bd9Sstevel@tonic-gate  * fix an entry in a directory.
7507c478bd9Sstevel@tonic-gate  */
751355d6bb5Sswilcox int
changeino(fsck_ino_t dir,char * name,fsck_ino_t newnum)752355d6bb5Sswilcox changeino(fsck_ino_t dir, char *name, fsck_ino_t newnum)
7537c478bd9Sstevel@tonic-gate {
7547c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
7557c478bd9Sstevel@tonic-gate 
756355d6bb5Sswilcox 	init_inodesc(&idesc);
7577c478bd9Sstevel@tonic-gate 	idesc.id_type = DATA;
7587c478bd9Sstevel@tonic-gate 	idesc.id_func = chgino;
7597c478bd9Sstevel@tonic-gate 	idesc.id_number = dir;
7607c478bd9Sstevel@tonic-gate 	idesc.id_fix = DONTKNOW;
7617c478bd9Sstevel@tonic-gate 	idesc.id_name = name;
7627c478bd9Sstevel@tonic-gate 	idesc.id_parent = newnum;	/* new value for name */
763355d6bb5Sswilcox 	return (ckinode(ginode(dir), &idesc, CKI_TRAVERSE));
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate /*
7677c478bd9Sstevel@tonic-gate  * make an entry in a directory
7687c478bd9Sstevel@tonic-gate  */
769355d6bb5Sswilcox int
makeentry(fsck_ino_t parent,fsck_ino_t ino,char * name)770355d6bb5Sswilcox makeentry(fsck_ino_t parent, fsck_ino_t ino, char *name)
7717c478bd9Sstevel@tonic-gate {
772355d6bb5Sswilcox 	int repeat;
7737c478bd9Sstevel@tonic-gate 	struct dinode *dp;
774355d6bb5Sswilcox 	struct inoinfo *iip;
7757c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
7767c478bd9Sstevel@tonic-gate 	char pathbuf[MAXPATHLEN + 1];
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 	if (parent < UFSROOTINO || parent >= maxino ||
7797c478bd9Sstevel@tonic-gate 	    ino < UFSROOTINO || ino >= maxino)
7807c478bd9Sstevel@tonic-gate 		return (0);
781355d6bb5Sswilcox 	init_inodesc(&idesc);
7827c478bd9Sstevel@tonic-gate 	idesc.id_type = DATA;
7837c478bd9Sstevel@tonic-gate 	idesc.id_func = mkentry;
7847c478bd9Sstevel@tonic-gate 	idesc.id_number = parent;
7857c478bd9Sstevel@tonic-gate 	idesc.id_parent = ino;	/* this is the inode to enter */
7867c478bd9Sstevel@tonic-gate 	idesc.id_fix = DONTKNOW;
7877c478bd9Sstevel@tonic-gate 	idesc.id_name = name;
788355d6bb5Sswilcox 
789355d6bb5Sswilcox 	repeat = 0;
790355d6bb5Sswilcox again:
7917c478bd9Sstevel@tonic-gate 	dp = ginode(parent);
792355d6bb5Sswilcox 	if ((dp->di_size % DIRBLKSIZ) != 0) {
7937c478bd9Sstevel@tonic-gate 		dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
7947c478bd9Sstevel@tonic-gate 		inodirty();
795355d6bb5Sswilcox 
796355d6bb5Sswilcox 		iip = getinoinfo(ino);
797355d6bb5Sswilcox 		if (iip != NULL)
798355d6bb5Sswilcox 			iip->i_isize = dp->di_size;
7997c478bd9Sstevel@tonic-gate 	}
800355d6bb5Sswilcox 
801355d6bb5Sswilcox 	if ((ckinode(dp, &idesc, CKI_TRAVERSE) & ALTERED) != 0) {
802355d6bb5Sswilcox 		iip = getinoinfo(ino);
803355d6bb5Sswilcox 		if (iip != NULL)
804355d6bb5Sswilcox 			iip->i_isize = dp->di_size;
805355d6bb5Sswilcox 
8067c478bd9Sstevel@tonic-gate 		return (1);
807355d6bb5Sswilcox 	}
808355d6bb5Sswilcox 
809355d6bb5Sswilcox 	if (repeat == 0) {
810355d6bb5Sswilcox 		getpathname(pathbuf, parent, parent);
811355d6bb5Sswilcox 		if (expanddir(parent, pathbuf) == 0)
812355d6bb5Sswilcox 			return (0);
813355d6bb5Sswilcox 
814355d6bb5Sswilcox 		repeat = 1;
815355d6bb5Sswilcox 		goto again;
816355d6bb5Sswilcox 	}
817355d6bb5Sswilcox 
818355d6bb5Sswilcox 	return (0);
8197c478bd9Sstevel@tonic-gate }
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate /*
8227c478bd9Sstevel@tonic-gate  * Attempt to expand the size of a directory
8237c478bd9Sstevel@tonic-gate  */
824355d6bb5Sswilcox static int
expanddir(fsck_ino_t ino,char * name)825355d6bb5Sswilcox expanddir(fsck_ino_t ino, char *name)
8267c478bd9Sstevel@tonic-gate {
8277c478bd9Sstevel@tonic-gate 	struct bufarea *bpback, *bp[2];
8287c478bd9Sstevel@tonic-gate 	daddr32_t nxtibn, nxtbn;
8297c478bd9Sstevel@tonic-gate 	daddr32_t newblk[2];
830355d6bb5Sswilcox 	struct dinode *dp;
8317c478bd9Sstevel@tonic-gate 	char *cp;
832355d6bb5Sswilcox 	int bc, f;
833355d6bb5Sswilcox 	int n;
8347c478bd9Sstevel@tonic-gate 	int allocIndir;
835355d6bb5Sswilcox 	int frag2blks;
8367c478bd9Sstevel@tonic-gate 	int lffragsz = 0;
8377c478bd9Sstevel@tonic-gate 	int c = 0;
838355d6bb5Sswilcox 	int retval = 0;
8397c478bd9Sstevel@tonic-gate 
840355d6bb5Sswilcox 	bp[0] = bp[1] = NULL;
841355d6bb5Sswilcox 
842355d6bb5Sswilcox 	dp = ginode(ino);
843355d6bb5Sswilcox 	if (dp->di_size == 0) {
844355d6bb5Sswilcox 		goto bail;
845355d6bb5Sswilcox 	}
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate 	nxtbn = lblkno(&sblock, dp->di_size - 1) + 1;
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	/*
850355d6bb5Sswilcox 	 *  Check that none of the nominally in-use direct block
851355d6bb5Sswilcox 	 *  addresses for the directory are bogus.
8527c478bd9Sstevel@tonic-gate 	 */
8537c478bd9Sstevel@tonic-gate 	for (bc = 0; ((nxtbn > 0) && (bc < nxtbn) && (bc < NDADDR)); bc++) {
854355d6bb5Sswilcox 		if (dp->di_db[bc] == 0) {
855355d6bb5Sswilcox 			goto bail;
856355d6bb5Sswilcox 		}
8577c478bd9Sstevel@tonic-gate 	}
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	/*
8607c478bd9Sstevel@tonic-gate 	 * Determine our data block allocation needs.  We always need to
8617c478bd9Sstevel@tonic-gate 	 * allocate at least one data block.  We may need a second, the
8627c478bd9Sstevel@tonic-gate 	 * indirect block itself.
8637c478bd9Sstevel@tonic-gate 	 */
8647c478bd9Sstevel@tonic-gate 	allocIndir = 0;
8657c478bd9Sstevel@tonic-gate 	nxtibn = -1;
866355d6bb5Sswilcox 	n = 0;
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	if (nxtbn <= NDADDR)  {
8697c478bd9Sstevel@tonic-gate 		/*
8707c478bd9Sstevel@tonic-gate 		 * Still in direct blocks.  Check for the unlikely
8717c478bd9Sstevel@tonic-gate 		 * case where the last block is a frag rather than
8727c478bd9Sstevel@tonic-gate 		 * a full block.  This would only happen if someone had
8737c478bd9Sstevel@tonic-gate 		 * created a file in lost+found, and then that caused
8747c478bd9Sstevel@tonic-gate 		 * the dynamic directory shrinking capabilities of ufs
8757c478bd9Sstevel@tonic-gate 		 * to kick in.
876355d6bb5Sswilcox 		 *
877355d6bb5Sswilcox 		 * Note that we test nxtbn <= NDADDR, as it's the
878355d6bb5Sswilcox 		 * next block (i.e., one greater than the current/
879355d6bb5Sswilcox 		 * actual block being examined).
8807c478bd9Sstevel@tonic-gate 		 */
8817c478bd9Sstevel@tonic-gate 		lffragsz = dp->di_size % sblock.fs_bsize;
8827c478bd9Sstevel@tonic-gate 	}
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	if (nxtbn >= NDADDR && !lffragsz) {
885355d6bb5Sswilcox 		n = sblock.fs_bsize / sizeof (daddr32_t);
8867c478bd9Sstevel@tonic-gate 		nxtibn = nxtbn - NDADDR;
8877c478bd9Sstevel@tonic-gate 		/*
8887c478bd9Sstevel@tonic-gate 		 * Only go one level of indirection
8897c478bd9Sstevel@tonic-gate 		 */
890355d6bb5Sswilcox 		if (nxtibn >= n) {
891355d6bb5Sswilcox 			goto bail;
892355d6bb5Sswilcox 		}
893355d6bb5Sswilcox 		/*
894355d6bb5Sswilcox 		 * First indirect block means we need to pick up
895355d6bb5Sswilcox 		 * the actual indirect pointer block as well.
896355d6bb5Sswilcox 		 */
8977c478bd9Sstevel@tonic-gate 		if (nxtibn == 0)
8987c478bd9Sstevel@tonic-gate 			allocIndir++;
8997c478bd9Sstevel@tonic-gate 	}
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	/*
9027c478bd9Sstevel@tonic-gate 	 * Allocate all the new blocks we need.
9037c478bd9Sstevel@tonic-gate 	 */
904355d6bb5Sswilcox 	if ((newblk[0] = allocblk(sblock.fs_frag)) == 0) {
9057c478bd9Sstevel@tonic-gate 		goto bail;
906355d6bb5Sswilcox 	}
9077c478bd9Sstevel@tonic-gate 	c++;
9087c478bd9Sstevel@tonic-gate 	if (allocIndir) {
909355d6bb5Sswilcox 		if ((newblk[1] = allocblk(sblock.fs_frag)) == 0) {
9107c478bd9Sstevel@tonic-gate 			goto bail;
911355d6bb5Sswilcox 		}
9127c478bd9Sstevel@tonic-gate 		c++;
9137c478bd9Sstevel@tonic-gate 	}
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	/*
9167c478bd9Sstevel@tonic-gate 	 * Take care of the block that will hold new directory entries.
9177c478bd9Sstevel@tonic-gate 	 * This one is always allocated.
9187c478bd9Sstevel@tonic-gate 	 */
919355d6bb5Sswilcox 	bp[0] = getdirblk(newblk[0], (size_t)sblock.fs_bsize);
920355d6bb5Sswilcox 	if (bp[0]->b_errs) {
9217c478bd9Sstevel@tonic-gate 		goto bail;
922355d6bb5Sswilcox 	}
923355d6bb5Sswilcox 
9247c478bd9Sstevel@tonic-gate 	if (lffragsz) {
925355d6bb5Sswilcox 		/*
926355d6bb5Sswilcox 		 * Preserve the partially-populated existing directory.
927355d6bb5Sswilcox 		 */
9287c478bd9Sstevel@tonic-gate 		bpback = getdirblk(dp->di_db[nxtbn - 1],
929355d6bb5Sswilcox 		    (size_t)dblksize(&sblock, dp, nxtbn - 1));
930355d6bb5Sswilcox 		if (!bpback->b_errs) {
931355d6bb5Sswilcox 			(void) memmove(bp[0]->b_un.b_buf, bpback->b_un.b_buf,
932355d6bb5Sswilcox 			    (size_t)lffragsz);
933355d6bb5Sswilcox 		}
934355d6bb5Sswilcox 	}
935355d6bb5Sswilcox 
936355d6bb5Sswilcox 	/*
937355d6bb5Sswilcox 	 * Initialize the new fragments.  lffragsz is zero if this
938355d6bb5Sswilcox 	 * is a completely-new block.
939355d6bb5Sswilcox 	 */
940355d6bb5Sswilcox 	for (cp = &(bp[0]->b_un.b_buf[lffragsz]);
941355d6bb5Sswilcox 	    cp < &(bp[0]->b_un.b_buf[sblock.fs_bsize]);
942355d6bb5Sswilcox 	    cp += DIRBLKSIZ) {
943355d6bb5Sswilcox 		(void) memmove(cp, (char *)&emptydir,
944355d6bb5Sswilcox 		    sizeof (emptydir));
9457c478bd9Sstevel@tonic-gate 	}
9467c478bd9Sstevel@tonic-gate 	dirty(bp[0]);
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	/*
9497c478bd9Sstevel@tonic-gate 	 * If we allocated the indirect block, zero it out. Otherwise
9507c478bd9Sstevel@tonic-gate 	 * read it in if we're using one.
9517c478bd9Sstevel@tonic-gate 	 */
9527c478bd9Sstevel@tonic-gate 	if (allocIndir) {
953355d6bb5Sswilcox 		bp[1] = getdatablk(newblk[1], (size_t)sblock.fs_bsize);
954355d6bb5Sswilcox 		if (bp[1]->b_errs) {
9557c478bd9Sstevel@tonic-gate 			goto bail;
956355d6bb5Sswilcox 		}
957355d6bb5Sswilcox 		(void) memset(bp[1]->b_un.b_buf, 0, sblock.fs_bsize);
9587c478bd9Sstevel@tonic-gate 		dirty(bp[1]);
9597c478bd9Sstevel@tonic-gate 	} else if (nxtibn >= 0) {
9607c478bd9Sstevel@tonic-gate 		/* Check that the indirect block pointer looks okay */
961355d6bb5Sswilcox 		if (dp->di_ib[0] == 0) {
9627c478bd9Sstevel@tonic-gate 			goto bail;
963355d6bb5Sswilcox 		}
964355d6bb5Sswilcox 		bp[1] = getdatablk(dp->di_ib[0], (size_t)sblock.fs_bsize);
965355d6bb5Sswilcox 		if (bp[1]->b_errs) {
9667c478bd9Sstevel@tonic-gate 			goto bail;
967355d6bb5Sswilcox 		}
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 		for (bc = 0; ((bc < nxtibn) && (bc < n)); bc++) {
970355d6bb5Sswilcox 			/* LINTED pointer cast alignment */
971355d6bb5Sswilcox 			if (((daddr32_t *)bp[1]->b_un.b_buf)[bc] == 0) {
9727c478bd9Sstevel@tonic-gate 				goto bail;
973355d6bb5Sswilcox 			}
9747c478bd9Sstevel@tonic-gate 		}
9757c478bd9Sstevel@tonic-gate 	}
9767c478bd9Sstevel@tonic-gate 
977355d6bb5Sswilcox 	/*
978355d6bb5Sswilcox 	 * Since the filesystem's consistency isn't affected by
979355d6bb5Sswilcox 	 * whether or not we actually do the expansion, iscorrupt
980355d6bb5Sswilcox 	 * is left alone for any of the approval paths.
981355d6bb5Sswilcox 	 */
9827c478bd9Sstevel@tonic-gate 	pwarn("NO SPACE LEFT IN %s", name);
983355d6bb5Sswilcox 	if (!preen && (reply("EXPAND") == 0))
9847c478bd9Sstevel@tonic-gate 		goto bail;
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	/*
987355d6bb5Sswilcox 	 * Now that everything we need is gathered up and the
988355d6bb5Sswilcox 	 * necessary approvals acquired, we can make our provisional
989355d6bb5Sswilcox 	 * changes permanent.
9907c478bd9Sstevel@tonic-gate 	 */
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	if (lffragsz) {
993355d6bb5Sswilcox 		/*
994355d6bb5Sswilcox 		 * We've saved the data from the old end fragment(s) in
995355d6bb5Sswilcox 		 * our new block, so we can just swap the new one in.
996355d6bb5Sswilcox 		 * Make sure the size reflects the expansion of the
997355d6bb5Sswilcox 		 * final fragments/block.
998355d6bb5Sswilcox 		 */
9997c478bd9Sstevel@tonic-gate 		frag2blks = roundup(lffragsz, sblock.fs_fsize);
1000355d6bb5Sswilcox 		freeblk(ino, dp->di_db[nxtbn - 1],
10017c478bd9Sstevel@tonic-gate 		    frag2blks / sblock.fs_fsize);
10027c478bd9Sstevel@tonic-gate 		frag2blks = btodb(frag2blks);
10037c478bd9Sstevel@tonic-gate 		dp->di_size -= (u_offset_t)lffragsz;
10047c478bd9Sstevel@tonic-gate 		dp->di_blocks = dp->di_blocks - frag2blks;
10057c478bd9Sstevel@tonic-gate 		dp->di_db[nxtbn - 1] = newblk[0];
10067c478bd9Sstevel@tonic-gate 		dp->di_size += (u_offset_t)sblock.fs_bsize;
10077c478bd9Sstevel@tonic-gate 		dp->di_blocks += btodb(sblock.fs_bsize);
10087c478bd9Sstevel@tonic-gate 		inodirty();
1009355d6bb5Sswilcox 		retval = 1;
1010355d6bb5Sswilcox 		goto done;
10117c478bd9Sstevel@tonic-gate 	}
10127c478bd9Sstevel@tonic-gate 
1013355d6bb5Sswilcox 	/*
1014355d6bb5Sswilcox 	 * Full-block addition's much easier.  It's just an append.
1015355d6bb5Sswilcox 	 */
10167c478bd9Sstevel@tonic-gate 	dp->di_size += (u_offset_t)sblock.fs_bsize;
10177c478bd9Sstevel@tonic-gate 	dp->di_blocks += btodb(sblock.fs_bsize);
10187c478bd9Sstevel@tonic-gate 	if (allocIndir) {
10197c478bd9Sstevel@tonic-gate 		dp->di_blocks += btodb(sblock.fs_bsize);
10207c478bd9Sstevel@tonic-gate 	}
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	inodirty();
10237c478bd9Sstevel@tonic-gate 	if (nxtibn < 0) {
10247c478bd9Sstevel@tonic-gate 		/*
10257c478bd9Sstevel@tonic-gate 		 * Still in direct blocks
10267c478bd9Sstevel@tonic-gate 		 */
10277c478bd9Sstevel@tonic-gate 		dp->di_db[nxtbn] = newblk[0];
10287c478bd9Sstevel@tonic-gate 	} else {
10297c478bd9Sstevel@tonic-gate 		/*
10307c478bd9Sstevel@tonic-gate 		 * Last indirect is always going to point at the
10317c478bd9Sstevel@tonic-gate 		 * new directory buffer
10327c478bd9Sstevel@tonic-gate 		 */
10337c478bd9Sstevel@tonic-gate 		if (allocIndir)
10347c478bd9Sstevel@tonic-gate 			dp->di_ib[0] = newblk[1];
1035355d6bb5Sswilcox 		/* LINTED pointer case alignment */
10367c478bd9Sstevel@tonic-gate 		((daddr32_t *)bp[1]->b_un.b_buf)[nxtibn] = newblk[0];
10377c478bd9Sstevel@tonic-gate 		dirty(bp[1]);
10387c478bd9Sstevel@tonic-gate 	}
1039355d6bb5Sswilcox 
1040355d6bb5Sswilcox 	if (preen)
1041355d6bb5Sswilcox 		(void) printf(" (EXPANDED)\n");
1042355d6bb5Sswilcox 
1043355d6bb5Sswilcox 	retval = 1;
1044355d6bb5Sswilcox 	goto done;
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate bail:
10477c478bd9Sstevel@tonic-gate 	for (f = 0; f < c; f++)
1048355d6bb5Sswilcox 		freeblk(ino, newblk[f], sblock.fs_frag);
1049355d6bb5Sswilcox done:
1050355d6bb5Sswilcox 	/*
1051355d6bb5Sswilcox 	 * bp[0] is handled by the directory cache's auto-release.
1052355d6bb5Sswilcox 	 */
1053355d6bb5Sswilcox 	if (bp[1] != NULL)
1054355d6bb5Sswilcox 		brelse(bp[1]);
1055355d6bb5Sswilcox 
1056355d6bb5Sswilcox 	return (retval);
1057355d6bb5Sswilcox }
1058355d6bb5Sswilcox 
1059355d6bb5Sswilcox static fsck_ino_t
newdir(fsck_ino_t parent,fsck_ino_t request,int mode,caddr_t name)1060355d6bb5Sswilcox newdir(fsck_ino_t parent, fsck_ino_t request, int mode, caddr_t name)
1061355d6bb5Sswilcox {
1062355d6bb5Sswilcox 	fsck_ino_t dino;
1063355d6bb5Sswilcox 	char pname[BUFSIZ];
1064355d6bb5Sswilcox 
1065355d6bb5Sswilcox 	/*
1066355d6bb5Sswilcox 	 * This function creates a new directory and populates it with
1067355d6bb5Sswilcox 	 * "." and "..", then links to it as NAME in PARENT.
1068355d6bb5Sswilcox 	 */
1069355d6bb5Sswilcox 	dino = allocdir(parent, request, mode, 1);
1070355d6bb5Sswilcox 	if (dino != 0) {
1071355d6bb5Sswilcox 		getpathname(pname, parent, parent);
1072355d6bb5Sswilcox 		name = mkuniqname(name, pname, parent, dino);
1073355d6bb5Sswilcox 		/*
1074355d6bb5Sswilcox 		 * We don't touch numdirs, because it's just a cache of
1075355d6bb5Sswilcox 		 * what the filesystem claimed originally and is used
1076355d6bb5Sswilcox 		 * to calculate hash keys.
1077355d6bb5Sswilcox 		 */
1078355d6bb5Sswilcox 		if (makeentry(parent, dino, name) == 0) {
1079355d6bb5Sswilcox 			freedir(dino, parent);
1080355d6bb5Sswilcox 			dino = 0;
1081355d6bb5Sswilcox 		}
1082355d6bb5Sswilcox 	}
1083355d6bb5Sswilcox 
1084355d6bb5Sswilcox 	return (dino);
1085355d6bb5Sswilcox }
1086355d6bb5Sswilcox 
1087355d6bb5Sswilcox /*
1088355d6bb5Sswilcox  * Replace whatever NAME refers to in PARENT with a new directory.
1089355d6bb5Sswilcox  * Note that if the old inode REQUEST is a directory, all of its
1090355d6bb5Sswilcox  * contents will be freed and reaped.
1091355d6bb5Sswilcox  */
1092355d6bb5Sswilcox static fsck_ino_t
reallocdir(fsck_ino_t parent,fsck_ino_t request,int mode,caddr_t name)1093355d6bb5Sswilcox reallocdir(fsck_ino_t parent, fsck_ino_t request, int mode, caddr_t name)
1094355d6bb5Sswilcox {
1095355d6bb5Sswilcox 	int retval;
1096355d6bb5Sswilcox 	fsck_ino_t newino;
1097355d6bb5Sswilcox 
1098355d6bb5Sswilcox 	if ((request != 0) && (statemap[request] != USTATE))
1099355d6bb5Sswilcox 		freeino(request, TI_PARENT);
1100355d6bb5Sswilcox 
1101355d6bb5Sswilcox 	newino = allocdir(parent, request, mode, 0);
1102355d6bb5Sswilcox 	if (newino != 0) {
1103355d6bb5Sswilcox 		retval = changeino(parent, name, newino);
1104355d6bb5Sswilcox 		if ((retval & ALTERED) == 0) {
1105355d6bb5Sswilcox 			/*
1106355d6bb5Sswilcox 			 * No change made, so name doesn't exist, so
1107355d6bb5Sswilcox 			 * unwind allocation rather than leak it.
1108355d6bb5Sswilcox 			 */
1109355d6bb5Sswilcox 			freedir(newino, parent);
1110355d6bb5Sswilcox 			newino = 0;
1111355d6bb5Sswilcox 		}
1112355d6bb5Sswilcox 	}
1113355d6bb5Sswilcox 
1114355d6bb5Sswilcox 	return (newino);
11157c478bd9Sstevel@tonic-gate }
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate /*
11187c478bd9Sstevel@tonic-gate  * allocate a new directory
11197c478bd9Sstevel@tonic-gate  */
1120355d6bb5Sswilcox fsck_ino_t
allocdir(fsck_ino_t parent,fsck_ino_t request,int mode,int update_parent)1121355d6bb5Sswilcox allocdir(fsck_ino_t parent, fsck_ino_t request, int mode, int update_parent)
11227c478bd9Sstevel@tonic-gate {
1123355d6bb5Sswilcox 	fsck_ino_t ino;
1124355d6bb5Sswilcox 	caddr_t cp;
1125355d6bb5Sswilcox 	caddr_t flow;
11267c478bd9Sstevel@tonic-gate 	struct dinode *dp;
11277c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
1128355d6bb5Sswilcox 	struct inoinfo *inp;
1129355d6bb5Sswilcox 	struct inodesc idesc;
1130355d6bb5Sswilcox 	struct dirtemplate *dirp;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	ino = allocino(request, IFDIR|mode);
11337c478bd9Sstevel@tonic-gate 	if (ino == 0)
11347c478bd9Sstevel@tonic-gate 		return (0);
1135355d6bb5Sswilcox 	dirp = &dirhead;
1136355d6bb5Sswilcox 	dirp->dot_ino = ino;
1137355d6bb5Sswilcox 	dirp->dotdot_ino = parent;
11387c478bd9Sstevel@tonic-gate 	dp = ginode(ino);
1139355d6bb5Sswilcox 	bp = getdirblk(dp->di_db[0], (size_t)sblock.fs_fsize);
11407c478bd9Sstevel@tonic-gate 	if (bp->b_errs) {
1141355d6bb5Sswilcox 		freeino(ino, TI_PARENT);
11427c478bd9Sstevel@tonic-gate 		return (0);
11437c478bd9Sstevel@tonic-gate 	}
1144355d6bb5Sswilcox 	(void) memmove(bp->b_un.b_buf, (void *)dirp,
1145355d6bb5Sswilcox 	    sizeof (struct dirtemplate));
11467c478bd9Sstevel@tonic-gate 	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
11477c478bd9Sstevel@tonic-gate 	    cp < &bp->b_un.b_buf[sblock.fs_fsize];
11487c478bd9Sstevel@tonic-gate 	    cp += DIRBLKSIZ)
1149355d6bb5Sswilcox 		(void) memmove(cp, (void *)&emptydir, sizeof (emptydir));
11507c478bd9Sstevel@tonic-gate 	dirty(bp);
11517c478bd9Sstevel@tonic-gate 	dp->di_nlink = 2;
11527c478bd9Sstevel@tonic-gate 	inodirty();
11537c478bd9Sstevel@tonic-gate 	if (!inocached(ino)) {
11547c478bd9Sstevel@tonic-gate 		cacheino(dp, ino);
11557c478bd9Sstevel@tonic-gate 	} else {
11567c478bd9Sstevel@tonic-gate 		/*
11577c478bd9Sstevel@tonic-gate 		 * re-using an old directory inode
11587c478bd9Sstevel@tonic-gate 		 */
11597c478bd9Sstevel@tonic-gate 		inp = getinoinfo(ino);
1160355d6bb5Sswilcox 		if (inp == NULL) {
1161355d6bb5Sswilcox 			if (debug)
1162355d6bb5Sswilcox 				errexit("allocdir got NULL from getinoinfo "
1163355d6bb5Sswilcox 					"for existing entry I=%d\n",
1164355d6bb5Sswilcox 					ino);
1165355d6bb5Sswilcox 			cacheino(dp, ino);
1166355d6bb5Sswilcox 		} else {
1167355d6bb5Sswilcox 			init_inoinfo(inp, dp, ino);
1168355d6bb5Sswilcox 			inp->i_parent = parent;
1169355d6bb5Sswilcox 			inp->i_dotdot = parent;
1170355d6bb5Sswilcox 		}
11717c478bd9Sstevel@tonic-gate 	}
1172355d6bb5Sswilcox 
1173355d6bb5Sswilcox 	/*
1174355d6bb5Sswilcox 	 * Short-circuit all the dancing around below if it's the
1175355d6bb5Sswilcox 	 * root inode.  The net effect's the same.
1176355d6bb5Sswilcox 	 */
11777c478bd9Sstevel@tonic-gate 	if (ino == UFSROOTINO) {
1178355d6bb5Sswilcox 		TRACK_LNCNTP(ino, lncntp[ino] = dp->di_nlink);
11797c478bd9Sstevel@tonic-gate 		return (ino);
11807c478bd9Sstevel@tonic-gate 	}
1181355d6bb5Sswilcox 
1182355d6bb5Sswilcox 	if (!update_parent)
1183355d6bb5Sswilcox 		return (ino);
1184355d6bb5Sswilcox 
1185355d6bb5Sswilcox 	/*
1186355d6bb5Sswilcox 	 * We never create attribute directories, which can have
1187355d6bb5Sswilcox 	 * non-directory parents.  So, the parent of the directory
1188355d6bb5Sswilcox 	 * we're creating must itself be a directory.
1189355d6bb5Sswilcox 	 */
1190355d6bb5Sswilcox 	if (!INO_IS_DVALID(parent)) {
1191355d6bb5Sswilcox 		freeino(ino, TI_PARENT);
11927c478bd9Sstevel@tonic-gate 		return (0);
11937c478bd9Sstevel@tonic-gate 	}
1194355d6bb5Sswilcox 
1195355d6bb5Sswilcox 	/*
1196355d6bb5Sswilcox 	 * Make sure the parent can handle another link.
1197355d6bb5Sswilcox 	 * Since we might only update one version of the
1198355d6bb5Sswilcox 	 * count (disk versus in-memory), we have to check both.
1199355d6bb5Sswilcox 	 */
1200355d6bb5Sswilcox 	LINK_RANGE(flow, lncntp[parent], -1);
1201355d6bb5Sswilcox 	if (flow == NULL)
1202355d6bb5Sswilcox 		LINK_RANGE(flow, (int)dp->di_nlink, 1);
1203355d6bb5Sswilcox 
1204355d6bb5Sswilcox 	if (flow != NULL) {
1205355d6bb5Sswilcox 		LINK_CLEAR(flow, parent, dp->di_mode, &idesc);
1206355d6bb5Sswilcox 		if (statemap[parent] == USTATE) {
1207355d6bb5Sswilcox 				/*
1208355d6bb5Sswilcox 				 * No parent any more, so bail out.  Callers
1209355d6bb5Sswilcox 				 * are expected to handle this possibility.
1210355d6bb5Sswilcox 				 * Since most just throw up their hands if
1211355d6bb5Sswilcox 				 * we return 0, this just happens to work.
1212355d6bb5Sswilcox 				 */
1213355d6bb5Sswilcox 			freeino(ino, TI_PARENT);
1214355d6bb5Sswilcox 			return (0);
1215355d6bb5Sswilcox 		}
1216355d6bb5Sswilcox 	}
1217355d6bb5Sswilcox 
1218355d6bb5Sswilcox 	/*
1219355d6bb5Sswilcox 	 * We've created a directory with two entries, "." and "..",
1220355d6bb5Sswilcox 	 * and a link count of two ("." and one from its parent).  If
1221355d6bb5Sswilcox 	 * the parent's not been scanned yet, which means this inode
1222355d6bb5Sswilcox 	 * will get scanned later as well, then make our in-core count
1223355d6bb5Sswilcox 	 * match what we pushed out to disk.  Similarly, update the
1224355d6bb5Sswilcox 	 * parent.  On the other hand, if the parent's already been
1225355d6bb5Sswilcox 	 * looked at (statemap[ino] == DFOUND), the discrepancy
1226355d6bb5Sswilcox 	 * between lncntp[] and di_nlink will be noted later, with
1227*b9a41fd3Sswilcox 	 * appropriate reporting and propagation, in pass2.
1228355d6bb5Sswilcox 	 *
1229355d6bb5Sswilcox 	 * We're explicitly skipping where the parent was DZLINK or
1230355d6bb5Sswilcox 	 * DFOUND.  If it has zero links, it can't be gotten to, so
1231*b9a41fd3Sswilcox 	 * we want a discrepancy set up that will be caught in pass2.
1232355d6bb5Sswilcox 	 * DFOUND was discussed above.
1233355d6bb5Sswilcox 	 *
1234355d6bb5Sswilcox 	 * Regarding the claim of a link from the parent: we've not
1235355d6bb5Sswilcox 	 * done anything to create such a link here.  We depend on the
1236355d6bb5Sswilcox 	 * semantics of our callers attaching the inode we return to
1237355d6bb5Sswilcox 	 * an existing entry in the directory or creating the entry
1238355d6bb5Sswilcox 	 * themselves, but in either case, not modifying the link
1239355d6bb5Sswilcox 	 * count.
1240355d6bb5Sswilcox 	 *
1241355d6bb5Sswilcox 	 * Note that setting lncntp[ino] to zero means that both claimed
1242355d6bb5Sswilcox 	 * links have been ``found''.
1243355d6bb5Sswilcox 	 */
12447c478bd9Sstevel@tonic-gate 	statemap[ino] = statemap[parent];
1245355d6bb5Sswilcox 	if (INO_IS_DVALID(parent)) {
1246355d6bb5Sswilcox 		TRACK_LNCNTP(ino, lncntp[ino] = 0);
1247355d6bb5Sswilcox 		TRACK_LNCNTP(parent, lncntp[parent]--);
12487c478bd9Sstevel@tonic-gate 	}
12497c478bd9Sstevel@tonic-gate 	dp = ginode(parent);
12507c478bd9Sstevel@tonic-gate 	dp->di_nlink++;
12517c478bd9Sstevel@tonic-gate 	inodirty();
12527c478bd9Sstevel@tonic-gate 	return (ino);
12537c478bd9Sstevel@tonic-gate }
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate /*
12567c478bd9Sstevel@tonic-gate  * free a directory inode
12577c478bd9Sstevel@tonic-gate  */
1258355d6bb5Sswilcox static void
freedir(fsck_ino_t ino,fsck_ino_t parent)1259355d6bb5Sswilcox freedir(fsck_ino_t ino, fsck_ino_t parent)
12607c478bd9Sstevel@tonic-gate {
1261355d6bb5Sswilcox 	struct inoinfo *iip;
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 	if (ino != parent) {
1264355d6bb5Sswilcox 		/*
1265355d6bb5Sswilcox 		 * Make sure that the desired parent gets a link
1266355d6bb5Sswilcox 		 * count update from freeino()/truncino().  If
1267355d6bb5Sswilcox 		 * we can't look it up, then it's not really a
1268355d6bb5Sswilcox 		 * directory, so there's nothing to worry about.
1269355d6bb5Sswilcox 		 */
1270355d6bb5Sswilcox 		iip = getinoinfo(ino);
1271355d6bb5Sswilcox 		if (iip != NULL)
1272355d6bb5Sswilcox 			iip->i_parent = parent;
12737c478bd9Sstevel@tonic-gate 	}
1274355d6bb5Sswilcox 	freeino(ino, TI_PARENT);
12757c478bd9Sstevel@tonic-gate }
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate /*
1278355d6bb5Sswilcox  * generate a temporary name for use in the lost+found directory.
12797c478bd9Sstevel@tonic-gate  */
1280355d6bb5Sswilcox static void
lftempname(char * bufp,fsck_ino_t ino)1281355d6bb5Sswilcox lftempname(char *bufp, fsck_ino_t ino)
12827c478bd9Sstevel@tonic-gate {
1283355d6bb5Sswilcox 	fsck_ino_t in;
1284355d6bb5Sswilcox 	caddr_t cp;
12857c478bd9Sstevel@tonic-gate 	int namlen;
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 	cp = bufp + 2;
12887c478bd9Sstevel@tonic-gate 	for (in = maxino; in > 0; in /= 10)
12897c478bd9Sstevel@tonic-gate 		cp++;
1290355d6bb5Sswilcox 	*--cp = '\0';
1291355d6bb5Sswilcox 	/* LINTED difference will not overflow an int */
12927c478bd9Sstevel@tonic-gate 	namlen = cp - bufp;
1293355d6bb5Sswilcox 	if ((namlen > BUFSIZ) || (namlen > MAXPATHLEN)) {
1294355d6bb5Sswilcox 		errexit("buffer overflow in lftempname()\n");
1295355d6bb5Sswilcox 	}
1296355d6bb5Sswilcox 
12977c478bd9Sstevel@tonic-gate 	in = ino;
12987c478bd9Sstevel@tonic-gate 	while (cp > bufp) {
12997c478bd9Sstevel@tonic-gate 		*--cp = (in % 10) + '0';
13007c478bd9Sstevel@tonic-gate 		in /= 10;
13017c478bd9Sstevel@tonic-gate 	}
13027c478bd9Sstevel@tonic-gate 	*cp = '#';
13037c478bd9Sstevel@tonic-gate }
13047c478bd9Sstevel@tonic-gate 
13057c478bd9Sstevel@tonic-gate /*
13067c478bd9Sstevel@tonic-gate  * Get a directory block.
13077c478bd9Sstevel@tonic-gate  * Insure that it is held until another is requested.
1308355d6bb5Sswilcox  *
1309355d6bb5Sswilcox  * Our callers are expected to check for errors and/or be
1310355d6bb5Sswilcox  * prepared to handle blocks of zeros in the middle of a
1311355d6bb5Sswilcox  * directory.
13127c478bd9Sstevel@tonic-gate  */
1313355d6bb5Sswilcox static struct bufarea *
getdirblk(daddr32_t blkno,size_t size)1314355d6bb5Sswilcox getdirblk(daddr32_t blkno, size_t size)
13157c478bd9Sstevel@tonic-gate {
13167c478bd9Sstevel@tonic-gate 	if (pdirbp != 0) {
13177c478bd9Sstevel@tonic-gate 		brelse(pdirbp);
13187c478bd9Sstevel@tonic-gate 	}
13197c478bd9Sstevel@tonic-gate 	pdirbp = getdatablk(blkno, size);
13207c478bd9Sstevel@tonic-gate 	return (pdirbp);
13217c478bd9Sstevel@tonic-gate }
1322355d6bb5Sswilcox 
1323355d6bb5Sswilcox /*
1324355d6bb5Sswilcox  * Create a unique name for INODE to be created in directory PARENT.
1325355d6bb5Sswilcox  * Use NAME if it is provided (non-NULL) and doesn't already exist.
1326355d6bb5Sswilcox  * Returning NULL indicates no unique name could be generated.
1327355d6bb5Sswilcox  *
1328355d6bb5Sswilcox  * If we were given a name, and it conflicts with an existing
1329355d6bb5Sswilcox  * entry, use our usual temp name instead.  Without this check,
1330355d6bb5Sswilcox  * we could end up creating duplicate entries for multiple
1331355d6bb5Sswilcox  * orphaned directories in lost+found with the same name (but
1332355d6bb5Sswilcox  * different parents).  Of course, our usual name might already
1333355d6bb5Sswilcox  * be in use as well, so be paranoid.
1334355d6bb5Sswilcox  *
1335355d6bb5Sswilcox  * We could do something like keep tacking something onto the
1336355d6bb5Sswilcox  * end of tempname until we come up with something that's not
1337355d6bb5Sswilcox  * in use, but that has liabilities as well.  This is a
1338355d6bb5Sswilcox  * sufficiently rare case that it's not worth going that
1339355d6bb5Sswilcox  * overboard for.
1340355d6bb5Sswilcox  */
1341355d6bb5Sswilcox static caddr_t
mkuniqname(caddr_t name,caddr_t pname,fsck_ino_t parent,fsck_ino_t inode)1342355d6bb5Sswilcox mkuniqname(caddr_t name, caddr_t pname, fsck_ino_t parent, fsck_ino_t inode)
1343355d6bb5Sswilcox {
1344355d6bb5Sswilcox 	fsck_ino_t oldino;
1345355d6bb5Sswilcox 	struct dinode *dp;
1346355d6bb5Sswilcox 	caddr_t flow_msg;
1347355d6bb5Sswilcox 	struct inodesc idesc;
1348355d6bb5Sswilcox 	static char tempname[BUFSIZ];
1349355d6bb5Sswilcox 
1350355d6bb5Sswilcox 	lftempname(tempname, inode);
1351355d6bb5Sswilcox 	if ((name != NULL) &&
1352355d6bb5Sswilcox 	    (lookup_named_ino(parent, name) != 0)) {
1353355d6bb5Sswilcox 		name = NULL;
1354355d6bb5Sswilcox 	}
1355355d6bb5Sswilcox 	if (name == NULL) {
1356355d6bb5Sswilcox 		/*
1357355d6bb5Sswilcox 		 * No name given, or it wasn't unique.
1358355d6bb5Sswilcox 		 */
1359355d6bb5Sswilcox 		name = tempname;
1360355d6bb5Sswilcox 		if ((oldino = lookup_named_ino(parent, name)) != 0) {
1361355d6bb5Sswilcox 			pfatal(
1362355d6bb5Sswilcox 			    "Name ``%s'' for inode %d already exists in %s \n",
1363355d6bb5Sswilcox 			    name, oldino, pname);
1364355d6bb5Sswilcox 			if (reply("REMOVE OLD ENTRY") == 0) {
1365355d6bb5Sswilcox 				if (parent == lfdir)
1366355d6bb5Sswilcox 					pwarn(
1367355d6bb5Sswilcox 					    "Could not reconnect inode %d\n\n",
1368355d6bb5Sswilcox 					    inode);
1369355d6bb5Sswilcox 				else
1370355d6bb5Sswilcox 					pwarn(
1371355d6bb5Sswilcox 					    "Could not create entry for %d\n\n",
1372355d6bb5Sswilcox 					    inode);
1373355d6bb5Sswilcox 				name = NULL;
1374355d6bb5Sswilcox 				goto noconnect;
1375355d6bb5Sswilcox 			}
1376355d6bb5Sswilcox 			(void) changeino(parent, name, inode);
1377355d6bb5Sswilcox 			LINK_RANGE(flow_msg, lncntp[oldino], 1);
1378355d6bb5Sswilcox 			if (flow_msg != NULL) {
1379355d6bb5Sswilcox 				/*
1380355d6bb5Sswilcox 				 * Do a best-effort, but if we're not
1381355d6bb5Sswilcox 				 * allowed to do the clear, the fs is
1382355d6bb5Sswilcox 				 * corrupt in any case, so just carry on.
1383355d6bb5Sswilcox 				 */
1384355d6bb5Sswilcox 				dp = ginode(oldino);
1385355d6bb5Sswilcox 				LINK_CLEAR(flow_msg, oldino, dp->di_mode,
1386355d6bb5Sswilcox 				    &idesc);
1387355d6bb5Sswilcox 				if (statemap[oldino] != USTATE)
1388355d6bb5Sswilcox 					iscorrupt = 1;
1389355d6bb5Sswilcox 			} else {
1390355d6bb5Sswilcox 				TRACK_LNCNTP(oldino, lncntp[oldino]++);
1391355d6bb5Sswilcox 			}
1392355d6bb5Sswilcox 		}
1393355d6bb5Sswilcox 	}
1394355d6bb5Sswilcox 
1395355d6bb5Sswilcox noconnect:
1396355d6bb5Sswilcox 	return (name);
1397355d6bb5Sswilcox }
1398