1331722Seadler/*
21558Srgrimes * Copyright (c) 1980, 1986, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
30114589Sobrien#if 0
311558Srgrimes#ifndef lint
3223675Speterstatic const char sccsid[] = "@(#)dir.c	8.8 (Berkeley) 4/28/95";
33114589Sobrien#endif /* not lint */
3441477Sjulian#endif
35114589Sobrien#include <sys/cdefs.h>
36114589Sobrien__FBSDID("$FreeBSD: stable/11/sbin/fsck_ffs/dir.c 348260 2019-05-25 00:22:07Z mckusick $");
371558Srgrimes
381558Srgrimes#include <sys/param.h>
3941474Sjulian#include <sys/time.h>
40275030Sbapt#include <sys/types.h>
4174556Smckusick#include <sys/sysctl.h>
4223675Speter
431558Srgrimes#include <ufs/ufs/dinode.h>
441558Srgrimes#include <ufs/ufs/dir.h>
451558Srgrimes#include <ufs/ffs/fs.h>
4623796Sbde
4723675Speter#include <err.h>
481558Srgrimes#include <string.h>
4923675Speter
501558Srgrimes#include "fsck.h"
511558Srgrimes
52260068Sscottlstatic struct	dirtemplate emptydir = {
5374556Smckusick	0, DIRBLKSIZ, DT_UNKNOWN, 0, "",
5474556Smckusick	0, 0, DT_UNKNOWN, 0, ""
5574556Smckusick};
56260068Sscottlstatic struct	dirtemplate dirhead = {
571558Srgrimes	0, 12, DT_DIR, 1, ".",
581558Srgrimes	0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
591558Srgrimes};
601558Srgrimes
6192839Simpstatic int chgino(struct inodesc *);
62348260Smckusickstatic int dircheck(struct inodesc *, struct bufarea *, struct direct *);
6398542Smckusickstatic int expanddir(union dinode *dp, char *name);
6492839Simpstatic void freedir(ino_t ino, ino_t parent);
6592839Simpstatic struct direct *fsck_readdir(struct inodesc *);
6698542Smckusickstatic struct bufarea *getdirblk(ufs2_daddr_t blkno, long size);
6792839Simpstatic int lftempname(char *bufp, ino_t ino);
6892839Simpstatic int mkentry(struct inodesc *);
691558Srgrimes
701558Srgrimes/*
711558Srgrimes * Propagate connected state through the tree.
721558Srgrimes */
737585Sbdevoid
7492839Simppropagate(void)
751558Srgrimes{
7692806Sobrien	struct inoinfo **inpp, *inp;
771558Srgrimes	struct inoinfo **inpend;
781558Srgrimes	long change;
791558Srgrimes
801558Srgrimes	inpend = &inpsort[inplast];
811558Srgrimes	do {
821558Srgrimes		change = 0;
831558Srgrimes		for (inpp = inpsort; inpp < inpend; inpp++) {
841558Srgrimes			inp = *inpp;
851558Srgrimes			if (inp->i_parent == 0)
861558Srgrimes				continue;
8741474Sjulian			if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
88136281Struckman			    INO_IS_DUNFOUND(inp->i_number)) {
8941474Sjulian				inoinfo(inp->i_number)->ino_state = DFOUND;
901558Srgrimes				change++;
911558Srgrimes			}
921558Srgrimes		}
931558Srgrimes	} while (change > 0);
941558Srgrimes}
951558Srgrimes
961558Srgrimes/*
971558Srgrimes * Scan each entry in a directory block.
981558Srgrimes */
997585Sbdeint
10092839Simpdirscan(struct inodesc *idesc)
1011558Srgrimes{
10292806Sobrien	struct direct *dp;
10392806Sobrien	struct bufarea *bp;
10474556Smckusick	u_int dsize, n;
1051558Srgrimes	long blksiz;
1061558Srgrimes	char dbuf[DIRBLKSIZ];
1071558Srgrimes
1081558Srgrimes	if (idesc->id_type != DATA)
10923675Speter		errx(EEXIT, "wrong type to dirscan %d", idesc->id_type);
1101558Srgrimes	if (idesc->id_entryno == 0 &&
1111558Srgrimes	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
1121558Srgrimes		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
1131558Srgrimes	blksiz = idesc->id_numfrags * sblock.fs_fsize;
1141558Srgrimes	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
1151558Srgrimes		idesc->id_filesize -= blksiz;
1161558Srgrimes		return (SKIP);
1171558Srgrimes	}
1181558Srgrimes	idesc->id_loc = 0;
1191558Srgrimes	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
1201558Srgrimes		dsize = dp->d_reclen;
12141474Sjulian		if (dsize > sizeof(dbuf))
12241474Sjulian			dsize = sizeof(dbuf);
12323675Speter		memmove(dbuf, dp, (size_t)dsize);
1241558Srgrimes		idesc->id_dirp = (struct direct *)dbuf;
1251558Srgrimes		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
1261558Srgrimes			bp = getdirblk(idesc->id_blkno, blksiz);
12723675Speter			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
1281558Srgrimes			    (size_t)dsize);
1291558Srgrimes			dirty(bp);
1301558Srgrimes			sbdirty();
131260068Sscottl			rerun = 1;
1321558Srgrimes		}
1338871Srgrimes		if (n & STOP)
1341558Srgrimes			return (n);
1351558Srgrimes	}
1361558Srgrimes	return (idesc->id_filesize > 0 ? KEEPON : STOP);
1371558Srgrimes}
1381558Srgrimes
1391558Srgrimes/*
140348260Smckusick * Get and verify the next entry in a directory.
141348260Smckusick * We also verify that if there is another entry in the block that it is
142348260Smckusick * valid, so if it is not valid it can be subsumed into the current entry.
1431558Srgrimes */
14423675Speterstatic struct direct *
14592839Simpfsck_readdir(struct inodesc *idesc)
1461558Srgrimes{
14792806Sobrien	struct direct *dp, *ndp;
14892806Sobrien	struct bufarea *bp;
149348260Smckusick	long size, blksiz, subsume_ndp;
1501558Srgrimes
151348260Smckusick	subsume_ndp = 0;
1521558Srgrimes	blksiz = idesc->id_numfrags * sblock.fs_fsize;
153348260Smckusick	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
154348260Smckusick		return (NULL);
1551558Srgrimes	bp = getdirblk(idesc->id_blkno, blksiz);
156348260Smckusick	dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
157348260Smckusick	/*
158348260Smckusick	 * Only need to check current entry if it is the first in the
159348260Smckusick	 * the block, as later entries will have been checked in the
160348260Smckusick	 * previous call to this function.
161348260Smckusick	 */
162348260Smckusick	if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) {
163348260Smckusick		/*
164348260Smckusick		 * Current entry is good, update to point at next.
165348260Smckusick		 */
166348260Smckusick		idesc->id_loc += dp->d_reclen;
167348260Smckusick		idesc->id_filesize -= dp->d_reclen;
168348260Smckusick		/*
169348260Smckusick		 * If at end of directory block, just return this entry.
170348260Smckusick		 */
171348260Smckusick		if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz ||
172348260Smckusick		    idesc->id_loc % DIRBLKSIZ == 0)
173348260Smckusick			return (dp);
174348260Smckusick		/*
175348260Smckusick		 * If the next entry good, return this entry.
176348260Smckusick		 */
177348260Smckusick		ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
178348260Smckusick		if (dircheck(idesc, bp, ndp) != 0)
179348260Smckusick			return (dp);
180348260Smckusick		/*
181348260Smckusick		 * The next entry is bad, so subsume it and the remainder
182348260Smckusick		 * of this directory block into this entry.
183348260Smckusick		 */
184348260Smckusick		subsume_ndp = 1;
1851558Srgrimes	}
186348260Smckusick	/*
187348260Smckusick	 * Current or next entry is bad. Zap current entry or
188348260Smckusick	 * subsume next entry into current entry as appropriate.
189348260Smckusick	 */
190348260Smckusick	size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
191348260Smckusick	idesc->id_loc += size;
192348260Smckusick	idesc->id_filesize -= size;
193348260Smckusick	if (idesc->id_fix == IGNORE)
194348260Smckusick		return (NULL);
195348260Smckusick	if (subsume_ndp) {
196348260Smckusick		memset(ndp, 0, size);
197348260Smckusick		dp->d_reclen += size;
198348260Smckusick	} else {
199348260Smckusick		memset(dp, 0, size);
200348260Smckusick		dp->d_reclen = size;
2011558Srgrimes	}
202348260Smckusick	if (dofix(idesc, "DIRECTORY CORRUPTED"))
203348260Smckusick		dirty(bp);
2041558Srgrimes	return (dp);
2051558Srgrimes}
2061558Srgrimes
2071558Srgrimes/*
2081558Srgrimes * Verify that a directory entry is valid.
2091558Srgrimes * This is a superset of the checks made in the kernel.
210347475Smckusick * Also optionally clears padding and unused directory space.
211347475Smckusick *
212348260Smckusick * Returns 0 if the entry is bad, 1 if the entry is good.
2131558Srgrimes */
21423675Speterstatic int
215348260Smckusickdircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp)
2161558Srgrimes{
217114589Sobrien	size_t size;
21892806Sobrien	char *cp;
219225338Sdelphij	u_int8_t namlen;
220347475Smckusick	int spaceleft, modified, unused;
2211558Srgrimes
22223675Speter	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
223348260Smckusick	size = DIRSIZ(0, dp);
22441474Sjulian	if (dp->d_reclen == 0 ||
22523675Speter	    dp->d_reclen > spaceleft ||
226348260Smckusick	    dp->d_reclen < size ||
227348260Smckusick	    idesc->id_filesize < size ||
228347475Smckusick	    (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0)
22962668Smckusick		goto bad;
230348260Smckusick	modified = 0;
231347475Smckusick	if (dp->d_ino == 0) {
232348260Smckusick		if (!zflag || fswritefd < 0)
233348260Smckusick			return (1);
234347475Smckusick		/*
235348260Smckusick		 * Special case of an unused directory entry. Normally only
236348260Smckusick		 * occurs at the beginning of a directory block when the block
237348260Smckusick		 * contains no entries. Other than the first entry in a
238348260Smckusick		 * directory block, the kernel coalesces unused space with
239348260Smckusick		 * the previous entry by extending its d_reclen. However,
240348260Smckusick		 * when cleaning up a directory, fsck may set d_ino to zero
241348260Smckusick		 * in the middle of a directory block. If we're clearing out
242348260Smckusick		 * directory cruft (-z flag), then make sure that all directory
243348260Smckusick		 * space in entries with d_ino == 0 gets fully cleared.
244347475Smckusick		 */
245348260Smckusick		if (dp->d_type != 0) {
246348260Smckusick			dp->d_type = 0;
247348260Smckusick			modified = 1;
248348260Smckusick		}
249348260Smckusick		if (dp->d_namlen != 0) {
250348260Smckusick			dp->d_namlen = 0;
251348260Smckusick			modified = 1;
252348260Smckusick		}
253348260Smckusick		unused = dp->d_reclen - __offsetof(struct direct, d_name);
254348260Smckusick		for (cp = dp->d_name; unused > 0; unused--, cp++) {
255348260Smckusick			if (*cp != '\0') {
256348260Smckusick				*cp = '\0';
257347475Smckusick				modified = 1;
258347475Smckusick			}
259347475Smckusick		}
260348260Smckusick		if (modified)
261348260Smckusick			dirty(bp);
262348260Smckusick		return (1);
263347475Smckusick	}
264348260Smckusick	/*
265348260Smckusick	 * The d_type field should not be tested here. A bad type is an error
266348260Smckusick	 * in the entry itself but is not a corruption of the directory
267348260Smckusick	 * structure itself. So blowing away all the remaining entries in the
268348260Smckusick	 * directory block is inappropriate. Rather the type error should be
269348260Smckusick	 * checked in pass1 and fixed there.
270348260Smckusick	 *
271348260Smckusick	 * The name validation should also be done in pass1 although the
272348260Smckusick	 * check to see if the name is longer than fits in the space
273348260Smckusick	 * allocated for it (i.e., the *cp != '\0' fails after exiting the
274348260Smckusick	 * loop below) then it really is a structural error that requires
275348260Smckusick	 * the stronger action taken here.
276348260Smckusick	 */
27796483Sphk	namlen = dp->d_namlen;
278348260Smckusick	if (namlen == 0 || dp->d_type > 15)
27962668Smckusick		goto bad;
280348260Smckusick	for (cp = dp->d_name, size = 0; size < namlen; size++) {
281348260Smckusick		if (*cp == '\0' || *cp++ == '/')
28262668Smckusick			goto bad;
283348260Smckusick	}
28423675Speter	if (*cp != '\0')
28562668Smckusick		goto bad;
286347475Smckusick	if (zflag && fswritefd >= 0) {
287347475Smckusick		/*
288347475Smckusick		 * Clear unused directory entry space, including the d_name
289347475Smckusick		 * padding.
290347475Smckusick		 */
291347475Smckusick		/* First figure the number of pad bytes. */
292347475Smckusick		unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1);
293347475Smckusick
294347475Smckusick		/* Add in the free space to the end of the record. */
295347475Smckusick		unused += dp->d_reclen - DIRSIZ(0, dp);
296347475Smckusick
297347475Smckusick		/*
298347475Smckusick		 * Now clear out the unused space, keeping track if we actually
299347475Smckusick		 * changed anything.
300347475Smckusick		 */
301347475Smckusick		for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) {
302347475Smckusick			if (*cp != '\0') {
303347475Smckusick				*cp = '\0';
304347475Smckusick				modified = 1;
305347475Smckusick			}
306347475Smckusick		}
307347475Smckusick
308348260Smckusick		if (modified)
309348260Smckusick			dirty(bp);
310347475Smckusick	}
31123675Speter	return (1);
312347475Smckusick
31362668Smckusickbad:
31462668Smckusick	if (debug)
31562668Smckusick		printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
31662668Smckusick		    dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type,
31762668Smckusick		    dp->d_name);
31862668Smckusick	return (0);
3191558Srgrimes}
3201558Srgrimes
3217585Sbdevoid
322100935Sphkdirerror(ino_t ino, const char *errmesg)
3231558Srgrimes{
3241558Srgrimes
3251558Srgrimes	fileerror(ino, ino, errmesg);
3261558Srgrimes}
3271558Srgrimes
3287585Sbdevoid
329100935Sphkfileerror(ino_t cwd, ino_t ino, const char *errmesg)
3301558Srgrimes{
33198542Smckusick	union dinode *dp;
3321558Srgrimes	char pathbuf[MAXPATHLEN + 1];
3331558Srgrimes
3341558Srgrimes	pwarn("%s ", errmesg);
3351558Srgrimes	pinode(ino);
3361558Srgrimes	printf("\n");
3371558Srgrimes	getpathname(pathbuf, cwd, ino);
3381558Srgrimes	if (ino < ROOTINO || ino > maxino) {
3391558Srgrimes		pfatal("NAME=%s\n", pathbuf);
3401558Srgrimes		return;
3411558Srgrimes	}
3421558Srgrimes	dp = ginode(ino);
3431558Srgrimes	if (ftypeok(dp))
3441558Srgrimes		pfatal("%s=%s\n",
34598542Smckusick		    (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE",
34698542Smckusick		    pathbuf);
3471558Srgrimes	else
3481558Srgrimes		pfatal("NAME=%s\n", pathbuf);
3491558Srgrimes}
3501558Srgrimes
3517585Sbdevoid
35292839Simpadjust(struct inodesc *idesc, int lcnt)
3531558Srgrimes{
35498542Smckusick	union dinode *dp;
35541474Sjulian	int saveresolved;
3561558Srgrimes
3571558Srgrimes	dp = ginode(idesc->id_number);
35898542Smckusick	if (DIP(dp, di_nlink) == lcnt) {
35941474Sjulian		/*
36041474Sjulian		 * If we have not hit any unresolved problems, are running
361102231Strhodes		 * in preen mode, and are on a file system using soft updates,
36241474Sjulian		 * then just toss any partially allocated files.
36341474Sjulian		 */
36474556Smckusick		if (resolved && (preen || bkgrdflag) && usedsoftdep) {
36541474Sjulian			clri(idesc, "UNREF", 1);
36641474Sjulian			return;
36741474Sjulian		} else {
36841474Sjulian			/*
369102231Strhodes			 * The file system can be marked clean even if
37041474Sjulian			 * a file is not linked up, but is cleared.
37141474Sjulian			 * Hence, resolved should not be cleared when
37241474Sjulian			 * linkup is answered no, but clri is answered yes.
37341474Sjulian			 */
37441474Sjulian			saveresolved = resolved;
37541474Sjulian			if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
37641474Sjulian				resolved = saveresolved;
37741474Sjulian				clri(idesc, "UNREF", 0);
37841474Sjulian				return;
37941474Sjulian			}
38041474Sjulian			/*
38141474Sjulian			 * Account for the new reference created by linkup().
38241474Sjulian			 */
38341474Sjulian			dp = ginode(idesc->id_number);
38441474Sjulian			lcnt--;
38541474Sjulian		}
38641474Sjulian	}
38741474Sjulian	if (lcnt != 0) {
3881558Srgrimes		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
38998542Smckusick			((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
3901558Srgrimes		pinode(idesc->id_number);
3911558Srgrimes		printf(" COUNT %d SHOULD BE %d",
39298542Smckusick			DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt);
39334266Sjulian		if (preen || usedsoftdep) {
3941558Srgrimes			if (lcnt < 0) {
3951558Srgrimes				printf("\n");
3961558Srgrimes				pfatal("LINK COUNT INCREASING");
3971558Srgrimes			}
39834266Sjulian			if (preen)
39934266Sjulian				printf(" (ADJUSTED)\n");
4001558Srgrimes		}
4011558Srgrimes		if (preen || reply("ADJUST") == 1) {
40274556Smckusick			if (bkgrdflag == 0) {
403134589Sscottl				DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt);
404344887Skib				inodirty(dp);
40574556Smckusick			} else {
40674556Smckusick				cmd.value = idesc->id_number;
40774556Smckusick				cmd.size = -lcnt;
40874556Smckusick				if (debug)
409100935Sphk					printf("adjrefcnt ino %ld amt %lld\n",
410100935Sphk					    (long)cmd.value,
411100935Sphk					    (long long)cmd.size);
41274556Smckusick				if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
41374556Smckusick				    &cmd, sizeof cmd) == -1)
41474556Smckusick					rwerror("ADJUST INODE", cmd.value);
41574556Smckusick			}
4161558Srgrimes		}
4171558Srgrimes	}
4181558Srgrimes}
4191558Srgrimes
42023675Speterstatic int
42192839Simpmkentry(struct inodesc *idesc)
4221558Srgrimes{
42392806Sobrien	struct direct *dirp = idesc->id_dirp;
4241558Srgrimes	struct direct newent;
4251558Srgrimes	int newlen, oldlen;
4261558Srgrimes
4271558Srgrimes	newent.d_namlen = strlen(idesc->id_name);
4281558Srgrimes	newlen = DIRSIZ(0, &newent);
4291558Srgrimes	if (dirp->d_ino != 0)
4301558Srgrimes		oldlen = DIRSIZ(0, dirp);
4311558Srgrimes	else
4321558Srgrimes		oldlen = 0;
4331558Srgrimes	if (dirp->d_reclen - oldlen < newlen)
4341558Srgrimes		return (KEEPON);
4351558Srgrimes	newent.d_reclen = dirp->d_reclen - oldlen;
4361558Srgrimes	dirp->d_reclen = oldlen;
4371558Srgrimes	dirp = (struct direct *)(((char *)dirp) + oldlen);
4381558Srgrimes	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
43923675Speter	dirp->d_reclen = newent.d_reclen;
44096483Sphk	dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
44123675Speter	dirp->d_namlen = newent.d_namlen;
44223675Speter	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
4431558Srgrimes	return (ALTERED|STOP);
4441558Srgrimes}
4451558Srgrimes
44623675Speterstatic int
44792839Simpchgino(struct inodesc *idesc)
4481558Srgrimes{
44992806Sobrien	struct direct *dirp = idesc->id_dirp;
4501558Srgrimes
45123675Speter	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
4521558Srgrimes		return (KEEPON);
4531558Srgrimes	dirp->d_ino = idesc->id_parent;
45496483Sphk	dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
4551558Srgrimes	return (ALTERED|STOP);
4561558Srgrimes}
4571558Srgrimes
4587585Sbdeint
45992839Simplinkup(ino_t orphan, ino_t parentdir, char *name)
4601558Srgrimes{
46198542Smckusick	union dinode *dp;
4621558Srgrimes	int lostdir;
4631558Srgrimes	ino_t oldlfdir;
4641558Srgrimes	struct inodesc idesc;
4651558Srgrimes	char tempname[BUFSIZ];
4661558Srgrimes
46723675Speter	memset(&idesc, 0, sizeof(struct inodesc));
4681558Srgrimes	dp = ginode(orphan);
46998542Smckusick	lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR;
4701558Srgrimes	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
4711558Srgrimes	pinode(orphan);
47298542Smckusick	if (preen && DIP(dp, di_size) == 0)
4731558Srgrimes		return (0);
47474556Smckusick	if (cursnapshot != 0) {
47574556Smckusick		pfatal("FILE LINKUP IN SNAPSHOT");
47674556Smckusick		return (0);
47774556Smckusick	}
4781558Srgrimes	if (preen)
4791558Srgrimes		printf(" (RECONNECTED)\n");
4801558Srgrimes	else
4811558Srgrimes		if (reply("RECONNECT") == 0)
4821558Srgrimes			return (0);
4831558Srgrimes	if (lfdir == 0) {
4841558Srgrimes		dp = ginode(ROOTINO);
485100935Sphk		idesc.id_name = strdup(lfname);
4861558Srgrimes		idesc.id_type = DATA;
4871558Srgrimes		idesc.id_func = findino;
4881558Srgrimes		idesc.id_number = ROOTINO;
4891558Srgrimes		if ((ckinode(dp, &idesc) & FOUND) != 0) {
4901558Srgrimes			lfdir = idesc.id_parent;
4911558Srgrimes		} else {
4921558Srgrimes			pwarn("NO lost+found DIRECTORY");
4931558Srgrimes			if (preen || reply("CREATE")) {
4941558Srgrimes				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
4951558Srgrimes				if (lfdir != 0) {
4961558Srgrimes					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
49741474Sjulian						numdirs++;
4981558Srgrimes						if (preen)
4991558Srgrimes							printf(" (CREATED)\n");
5001558Srgrimes					} else {
5011558Srgrimes						freedir(lfdir, ROOTINO);
5021558Srgrimes						lfdir = 0;
5031558Srgrimes						if (preen)
5041558Srgrimes							printf("\n");
5051558Srgrimes					}
5061558Srgrimes				}
5071558Srgrimes			}
5081558Srgrimes		}
5091558Srgrimes		if (lfdir == 0) {
5101558Srgrimes			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
5111558Srgrimes			printf("\n\n");
5121558Srgrimes			return (0);
5131558Srgrimes		}
5141558Srgrimes	}
5151558Srgrimes	dp = ginode(lfdir);
51698542Smckusick	if ((DIP(dp, di_mode) & IFMT) != IFDIR) {
5171558Srgrimes		pfatal("lost+found IS NOT A DIRECTORY");
5181558Srgrimes		if (reply("REALLOCATE") == 0)
5191558Srgrimes			return (0);
5201558Srgrimes		oldlfdir = lfdir;
5211558Srgrimes		if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
5221558Srgrimes			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
5231558Srgrimes			return (0);
5241558Srgrimes		}
5251558Srgrimes		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
5261558Srgrimes			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
5271558Srgrimes			return (0);
5281558Srgrimes		}
529344887Skib		inodirty(dp);
5301558Srgrimes		idesc.id_type = ADDR;
5311558Srgrimes		idesc.id_func = pass4check;
5321558Srgrimes		idesc.id_number = oldlfdir;
53341474Sjulian		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
53441474Sjulian		inoinfo(oldlfdir)->ino_linkcnt = 0;
5351558Srgrimes		dp = ginode(lfdir);
5361558Srgrimes	}
53741474Sjulian	if (inoinfo(lfdir)->ino_state != DFOUND) {
5381558Srgrimes		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
5391558Srgrimes		return (0);
5401558Srgrimes	}
5411558Srgrimes	(void)lftempname(tempname, orphan);
54241474Sjulian	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
5431558Srgrimes		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
5441558Srgrimes		printf("\n\n");
5451558Srgrimes		return (0);
5461558Srgrimes	}
54741474Sjulian	inoinfo(orphan)->ino_linkcnt--;
5481558Srgrimes	if (lostdir) {
5491558Srgrimes		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
5501558Srgrimes		    parentdir != (ino_t)-1)
5511558Srgrimes			(void)makeentry(orphan, lfdir, "..");
5521558Srgrimes		dp = ginode(lfdir);
553134589Sscottl		DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
554344887Skib		inodirty(dp);
55541474Sjulian		inoinfo(lfdir)->ino_linkcnt++;
55686514Siedowse		pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
55715699Snate		if (parentdir != (ino_t)-1) {
55837236Sbde			printf("PARENT WAS I=%lu\n", (u_long)parentdir);
55941477Sjulian			/*
56041477Sjulian			 * The parent directory, because of the ordering
56141477Sjulian			 * guarantees, has had the link count incremented
56241477Sjulian			 * for the child, but no entry was made.  This
56341477Sjulian			 * fixes the parent link count so that fsck does
56441477Sjulian			 * not need to be rerun.
56541477Sjulian			 */
56641474Sjulian			inoinfo(parentdir)->ino_linkcnt++;
56715699Snate		}
5681558Srgrimes		if (preen == 0)
5691558Srgrimes			printf("\n");
5701558Srgrimes	}
5711558Srgrimes	return (1);
5721558Srgrimes}
5731558Srgrimes
5741558Srgrimes/*
5751558Srgrimes * fix an entry in a directory.
5761558Srgrimes */
5777585Sbdeint
578100935Sphkchangeino(ino_t dir, const char *name, ino_t newnum)
5791558Srgrimes{
5801558Srgrimes	struct inodesc idesc;
5811558Srgrimes
58223675Speter	memset(&idesc, 0, sizeof(struct inodesc));
5831558Srgrimes	idesc.id_type = DATA;
5841558Srgrimes	idesc.id_func = chgino;
5851558Srgrimes	idesc.id_number = dir;
5861558Srgrimes	idesc.id_fix = DONTKNOW;
587100935Sphk	idesc.id_name = strdup(name);
5881558Srgrimes	idesc.id_parent = newnum;	/* new value for name */
5891558Srgrimes	return (ckinode(ginode(dir), &idesc));
5901558Srgrimes}
5911558Srgrimes
5921558Srgrimes/*
5931558Srgrimes * make an entry in a directory
5941558Srgrimes */
5957585Sbdeint
596100935Sphkmakeentry(ino_t parent, ino_t ino, const char *name)
5971558Srgrimes{
59898542Smckusick	union dinode *dp;
5991558Srgrimes	struct inodesc idesc;
6001558Srgrimes	char pathbuf[MAXPATHLEN + 1];
6018871Srgrimes
6021558Srgrimes	if (parent < ROOTINO || parent >= maxino ||
6031558Srgrimes	    ino < ROOTINO || ino >= maxino)
6041558Srgrimes		return (0);
60523675Speter	memset(&idesc, 0, sizeof(struct inodesc));
6061558Srgrimes	idesc.id_type = DATA;
6071558Srgrimes	idesc.id_func = mkentry;
6081558Srgrimes	idesc.id_number = parent;
6091558Srgrimes	idesc.id_parent = ino;	/* this is the inode to enter */
6101558Srgrimes	idesc.id_fix = DONTKNOW;
611100935Sphk	idesc.id_name = strdup(name);
6121558Srgrimes	dp = ginode(parent);
61398542Smckusick	if (DIP(dp, di_size) % DIRBLKSIZ) {
614134589Sscottl		DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ));
615344887Skib		inodirty(dp);
6161558Srgrimes	}
6171558Srgrimes	if ((ckinode(dp, &idesc) & ALTERED) != 0)
6181558Srgrimes		return (1);
6191558Srgrimes	getpathname(pathbuf, parent, parent);
6201558Srgrimes	dp = ginode(parent);
6211558Srgrimes	if (expanddir(dp, pathbuf) == 0)
6221558Srgrimes		return (0);
6231558Srgrimes	return (ckinode(dp, &idesc) & ALTERED);
6241558Srgrimes}
6251558Srgrimes
6261558Srgrimes/*
6271558Srgrimes * Attempt to expand the size of a directory
6281558Srgrimes */
62923675Speterstatic int
63098542Smckusickexpanddir(union dinode *dp, char *name)
6311558Srgrimes{
63298542Smckusick	ufs2_daddr_t lastbn, newblk;
63392806Sobrien	struct bufarea *bp;
6341558Srgrimes	char *cp, firstblk[DIRBLKSIZ];
6351558Srgrimes
63698542Smckusick	lastbn = lblkno(&sblock, DIP(dp, di_size));
63798542Smckusick	if (lastbn >= NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 ||
63898542Smckusick	    DIP(dp, di_size) == 0)
6391558Srgrimes		return (0);
6401558Srgrimes	if ((newblk = allocblk(sblock.fs_frag)) == 0)
6411558Srgrimes		return (0);
642134589Sscottl	DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn]));
643134589Sscottl	DIP_SET(dp, di_db[lastbn], newblk);
644134589Sscottl	DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize);
645134589Sscottl	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
64698542Smckusick	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
64798542Smckusick		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
6481558Srgrimes	if (bp->b_errs)
6491558Srgrimes		goto bad;
65023675Speter	memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
6511558Srgrimes	bp = getdirblk(newblk, sblock.fs_bsize);
6521558Srgrimes	if (bp->b_errs)
6531558Srgrimes		goto bad;
65423675Speter	memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
6551558Srgrimes	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
6561558Srgrimes	     cp < &bp->b_un.b_buf[sblock.fs_bsize];
6571558Srgrimes	     cp += DIRBLKSIZ)
65823675Speter		memmove(cp, &emptydir, sizeof emptydir);
6591558Srgrimes	dirty(bp);
66098542Smckusick	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
66198542Smckusick		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
6621558Srgrimes	if (bp->b_errs)
6631558Srgrimes		goto bad;
66423675Speter	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
6651558Srgrimes	pwarn("NO SPACE LEFT IN %s", name);
6661558Srgrimes	if (preen)
6671558Srgrimes		printf(" (EXPANDED)\n");
6681558Srgrimes	else if (reply("EXPAND") == 0)
6691558Srgrimes		goto bad;
6701558Srgrimes	dirty(bp);
671344887Skib	inodirty(dp);
6721558Srgrimes	return (1);
6731558Srgrimesbad:
674134589Sscottl	DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1]));
675134589Sscottl	DIP_SET(dp, di_db[lastbn + 1], 0);
676134589Sscottl	DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize);
677134589Sscottl	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize));
6781558Srgrimes	freeblk(newblk, sblock.fs_frag);
6791558Srgrimes	return (0);
6801558Srgrimes}
6811558Srgrimes
6821558Srgrimes/*
6831558Srgrimes * allocate a new directory
6841558Srgrimes */
6857586Sbdeino_t
68692839Simpallocdir(ino_t parent, ino_t request, int mode)
6871558Srgrimes{
6881558Srgrimes	ino_t ino;
6891558Srgrimes	char *cp;
69098542Smckusick	union dinode *dp;
69163810Smckusick	struct bufarea *bp;
69263810Smckusick	struct inoinfo *inp;
6931558Srgrimes	struct dirtemplate *dirp;
6941558Srgrimes
6951558Srgrimes	ino = allocino(request, IFDIR|mode);
69696483Sphk	dirp = &dirhead;
6971558Srgrimes	dirp->dot_ino = ino;
6981558Srgrimes	dirp->dotdot_ino = parent;
6991558Srgrimes	dp = ginode(ino);
70098542Smckusick	bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize);
7011558Srgrimes	if (bp->b_errs) {
7021558Srgrimes		freeino(ino);
7031558Srgrimes		return (0);
7041558Srgrimes	}
70523675Speter	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
7061558Srgrimes	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
7071558Srgrimes	     cp < &bp->b_un.b_buf[sblock.fs_fsize];
7081558Srgrimes	     cp += DIRBLKSIZ)
70923675Speter		memmove(cp, &emptydir, sizeof emptydir);
7101558Srgrimes	dirty(bp);
711134589Sscottl	DIP_SET(dp, di_nlink, 2);
712344887Skib	inodirty(dp);
7131558Srgrimes	if (ino == ROOTINO) {
71498542Smckusick		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
7151558Srgrimes		cacheino(dp, ino);
7161558Srgrimes		return(ino);
7171558Srgrimes	}
718136281Struckman	if (!INO_IS_DVALID(parent)) {
7191558Srgrimes		freeino(ino);
7201558Srgrimes		return (0);
7211558Srgrimes	}
7221558Srgrimes	cacheino(dp, ino);
72363810Smckusick	inp = getinoinfo(ino);
72463810Smckusick	inp->i_parent = parent;
72563810Smckusick	inp->i_dotdot = parent;
72641474Sjulian	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
72741474Sjulian	if (inoinfo(ino)->ino_state == DSTATE) {
72898542Smckusick		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
72941474Sjulian		inoinfo(parent)->ino_linkcnt++;
7301558Srgrimes	}
7311558Srgrimes	dp = ginode(parent);
732134589Sscottl	DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
733344887Skib	inodirty(dp);
7341558Srgrimes	return (ino);
7351558Srgrimes}
7361558Srgrimes
7371558Srgrimes/*
7381558Srgrimes * free a directory inode
7391558Srgrimes */
7407585Sbdestatic void
74192839Simpfreedir(ino_t ino, ino_t parent)
7421558Srgrimes{
74398542Smckusick	union dinode *dp;
7441558Srgrimes
7451558Srgrimes	if (ino != parent) {
7461558Srgrimes		dp = ginode(parent);
747134589Sscottl		DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1);
748344887Skib		inodirty(dp);
7491558Srgrimes	}
7501558Srgrimes	freeino(ino);
7511558Srgrimes}
7521558Srgrimes
7531558Srgrimes/*
7541558Srgrimes * generate a temporary name for the lost+found directory.
7551558Srgrimes */
75623675Speterstatic int
75792839Simplftempname(char *bufp, ino_t ino)
7581558Srgrimes{
75992806Sobrien	ino_t in;
76092806Sobrien	char *cp;
7611558Srgrimes	int namlen;
7621558Srgrimes
7631558Srgrimes	cp = bufp + 2;
7641558Srgrimes	for (in = maxino; in > 0; in /= 10)
7651558Srgrimes		cp++;
7661558Srgrimes	*--cp = 0;
7671558Srgrimes	namlen = cp - bufp;
7681558Srgrimes	in = ino;
7691558Srgrimes	while (cp > bufp) {
7701558Srgrimes		*--cp = (in % 10) + '0';
7711558Srgrimes		in /= 10;
7721558Srgrimes	}
7731558Srgrimes	*cp = '#';
7741558Srgrimes	return (namlen);
7751558Srgrimes}
7761558Srgrimes
7771558Srgrimes/*
7781558Srgrimes * Get a directory block.
7791558Srgrimes * Insure that it is held until another is requested.
7801558Srgrimes */
78123675Speterstatic struct bufarea *
78298542Smckusickgetdirblk(ufs2_daddr_t blkno, long size)
7831558Srgrimes{
7841558Srgrimes
785297886Spfg	if (pdirbp != NULL)
7861558Srgrimes		pdirbp->b_flags &= ~B_INUSE;
787247212Smckusick	pdirbp = getdatablk(blkno, size, BT_DIRDATA);
7881558Srgrimes	return (pdirbp);
7891558Srgrimes}
790