ext2_lookup.c revision 202283
1139778Simp/*-
212115Sdyson *  modified for Lites 1.1
312115Sdyson *
412115Sdyson *  Aug 1995, Godmar Back (gback@cs.utah.edu)
512115Sdyson *  University of Utah, Department of Computer Science
612115Sdyson */
7139778Simp/*-
812115Sdyson * Copyright (c) 1989, 1993
912115Sdyson *	The Regents of the University of California.  All rights reserved.
1012115Sdyson * (c) UNIX System Laboratories, Inc.
1112115Sdyson * All or some portions of this file are derived from material licensed
1212115Sdyson * to the University of California by American Telephone and Telegraph
1312115Sdyson * Co. or Unix System Laboratories, Inc. and are reproduced herein with
1412115Sdyson * the permission of UNIX System Laboratories, Inc.
1512115Sdyson *
1612115Sdyson * Redistribution and use in source and binary forms, with or without
1712115Sdyson * modification, are permitted provided that the following conditions
1812115Sdyson * are met:
1912115Sdyson * 1. Redistributions of source code must retain the above copyright
2012115Sdyson *    notice, this list of conditions and the following disclaimer.
2112115Sdyson * 2. Redistributions in binary form must reproduce the above copyright
2212115Sdyson *    notice, this list of conditions and the following disclaimer in the
2312115Sdyson *    documentation and/or other materials provided with the distribution.
2412115Sdyson * 4. Neither the name of the University nor the names of its contributors
2512115Sdyson *    may be used to endorse or promote products derived from this software
2612115Sdyson *    without specific prior written permission.
2712115Sdyson *
2812115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2912115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3012115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3112115Sdyson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3212115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3312115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3412115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3512115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3612115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3712115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3812115Sdyson * SUCH DAMAGE.
3912115Sdyson *
4012115Sdyson *	@(#)ufs_lookup.c	8.6 (Berkeley) 4/1/94
4193016Sbde * $FreeBSD: head/sys/fs/ext2fs/ext2_lookup.c 202283 2010-01-14 14:30:54Z lulf $
4212115Sdyson */
4312115Sdyson
4412115Sdyson#include <sys/param.h>
4512159Sbde#include <sys/systm.h>
4612115Sdyson#include <sys/namei.h>
4760041Sphk#include <sys/bio.h>
4812115Sdyson#include <sys/buf.h>
49193377Sstas#include <sys/endian.h>
5012115Sdyson#include <sys/mount.h>
5112115Sdyson#include <sys/vnode.h>
5212115Sdyson#include <sys/malloc.h>
5312115Sdyson#include <sys/dirent.h>
5496753Siedowse#include <sys/sysctl.h>
5512115Sdyson
5612115Sdyson#include <ufs/ufs/dir.h>
5712115Sdyson
58202283Slulf#include <fs/ext2fs/inode.h>
59202283Slulf#include <fs/ext2fs/ext2_mount.h>
60202283Slulf#include <fs/ext2fs/ext2_extern.h>
61202283Slulf#include <fs/ext2fs/ext2fs.h>
62202283Slulf#include <fs/ext2fs/ext2_dir.h>
6312115Sdyson
6496753Siedowse#ifdef DIAGNOSTIC
6596753Siedowsestatic int dirchk = 1;
6696753Siedowse#else
6796753Siedowsestatic int dirchk = 0;
6896753Siedowse#endif
6996753Siedowse
70141633Sphkstatic SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem");
7196753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
7296753Siedowse
73111742Sdes/*
7412115Sdyson   DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
7512115Sdyson   while it is the native blocksize in ext2fs - thus, a #define
7612115Sdyson   is no longer appropriate
7712115Sdyson*/
7812115Sdyson#undef  DIRBLKSIZ
7912115Sdyson
8055477Sbdestatic u_char ext2_ft_to_dt[] = {
8155477Sbde	DT_UNKNOWN,		/* EXT2_FT_UNKNOWN */
8255477Sbde	DT_REG,			/* EXT2_FT_REG_FILE */
8355477Sbde	DT_DIR,			/* EXT2_FT_DIR */
8455477Sbde	DT_CHR,			/* EXT2_FT_CHRDEV */
8555477Sbde	DT_BLK,			/* EXT2_FT_BLKDEV */
8655477Sbde	DT_FIFO,		/* EXT2_FT_FIFO */
8755477Sbde	DT_SOCK,		/* EXT2_FT_SOCK */
8855477Sbde	DT_LNK,			/* EXT2_FT_SYMLINK */
8955477Sbde};
9055477Sbde#define	FTTODT(ft)						\
9155477Sbde    ((ft) > sizeof(ext2_ft_to_dt) / sizeof(ext2_ft_to_dt[0]) ?	\
9255477Sbde    DT_UNKNOWN : ext2_ft_to_dt[(ft)])
9355477Sbde
9455477Sbdestatic u_char dt_to_ext2_ft[] = {
9555477Sbde	EXT2_FT_UNKNOWN,	/* DT_UNKNOWN */
9655477Sbde	EXT2_FT_FIFO,		/* DT_FIFO */
9755477Sbde	EXT2_FT_CHRDEV,		/* DT_CHR */
9855477Sbde	EXT2_FT_UNKNOWN,	/* unused */
9955477Sbde	EXT2_FT_DIR,		/* DT_DIR */
10055477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10155477Sbde	EXT2_FT_BLKDEV,		/* DT_BLK */
10255477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10355477Sbde	EXT2_FT_REG_FILE,	/* DT_REG */
10455477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10555477Sbde	EXT2_FT_SYMLINK,	/* DT_LNK */
10655477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10755477Sbde	EXT2_FT_SOCK,		/* DT_SOCK */
10855477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10955477Sbde	EXT2_FT_UNKNOWN,	/* DT_WHT */
11055477Sbde};
11155477Sbde#define	DTTOFT(dt)						\
11255477Sbde    ((dt) > sizeof(dt_to_ext2_ft) / sizeof(dt_to_ext2_ft[0]) ?	\
11355477Sbde    EXT2_FT_UNKNOWN : dt_to_ext2_ft[(dt)])
11455477Sbde
115202283Slulfstatic int	ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
11693014Sbde		    int entryoffsetinblock);
11712159Sbde
11812115Sdyson/*
11912115Sdyson * Vnode op for reading directories.
12012115Sdyson *
12112115Sdyson * The routine below assumes that the on-disk format of a directory
12212115Sdyson * is the same as that defined by <sys/dirent.h>. If the on-disk
12312115Sdyson * format changes, then it will be necessary to do a conversion
12412115Sdyson * from the on-disk format that read returns to the format defined
12512115Sdyson * by <sys/dirent.h>.
12612115Sdyson */
12712115Sdyson/*
12812115Sdyson * this is exactly what we do here - the problem is that the conversion
12912115Sdyson * will blow up some entries by four bytes, so it can't be done in place.
13012115Sdyson * This is too bad. Right now the conversion is done entry by entry, the
131111742Sdes * converted entry is sent via uiomove.
13212115Sdyson *
13312115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send
13412115Sdyson * the whole buffer to uiomove
13512115Sdyson */
13612115Sdysonint
13712115Sdysonext2_readdir(ap)
138111742Sdes	struct vop_readdir_args /* {
139111742Sdes		struct vnode *a_vp;
140111742Sdes		struct uio *a_uio;
141111742Sdes		struct ucred *a_cred;
142111742Sdes	} */ *ap;
14312115Sdyson{
144111742Sdes	struct uio *uio = ap->a_uio;
145111742Sdes	int count, error;
14612115Sdyson
147202283Slulf	struct ext2fs_direct_2 *edp, *dp;
14824649Sdfr	int ncookies;
14912115Sdyson	struct dirent dstdp;
15012115Sdyson	struct uio auio;
15112115Sdyson	struct iovec aiov;
15212115Sdyson	caddr_t dirbuf;
153202283Slulf	int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize;
15412115Sdyson	int readcnt;
15565780Sbde	off_t startoffset = uio->uio_offset;
15612115Sdyson
15765780Sbde	count = uio->uio_resid;
15865780Sbde	/*
15965780Sbde	 * Avoid complications for partial directory entries by adjusting
16065780Sbde	 * the i/o to end at a block boundary.  Don't give up (like ufs
16165780Sbde	 * does) if the initial adjustment gives a negative count, since
16265780Sbde	 * many callers don't supply a large enough buffer.  The correct
16365780Sbde	 * size is a little larger than DIRBLKSIZ to allow for expansion
16465780Sbde	 * of directory entries, but some callers just use 512.
16565780Sbde	 */
16665780Sbde	count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
16765780Sbde	if (count <= 0)
16865780Sbde		count += DIRBLKSIZ;
16912115Sdyson	auio = *uio;
17012115Sdyson	auio.uio_iov = &aiov;
17112115Sdyson	auio.uio_iovcnt = 1;
17265780Sbde	auio.uio_resid = count;
17312115Sdyson	auio.uio_segflg = UIO_SYSSPACE;
17412115Sdyson	aiov.iov_len = count;
175184205Sdes	dirbuf = malloc(count, M_TEMP, M_WAITOK);
17612115Sdyson	aiov.iov_base = dirbuf;
17712115Sdyson	error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
17812115Sdyson	if (error == 0) {
17912115Sdyson		readcnt = count - auio.uio_resid;
180202283Slulf		edp = (struct ext2fs_direct_2 *)&dirbuf[readcnt];
18124649Sdfr		ncookies = 0;
18255477Sbde		bzero(&dstdp, offsetof(struct dirent, d_name));
183202283Slulf		for (dp = (struct ext2fs_direct_2 *)dirbuf;
18455477Sbde		    !error && uio->uio_resid > 0 && dp < edp; ) {
18555477Sbde			/*-
18655477Sbde			 * "New" ext2fs directory entries differ in 3 ways
18755477Sbde			 * from ufs on-disk ones:
18855477Sbde			 * - the name is not necessarily NUL-terminated.
18955477Sbde			 * - the file type field always exists and always
190125843Sbde			 *   follows the name length field.
19155477Sbde			 * - the file type is encoded in a different way.
19255477Sbde			 *
19355477Sbde			 * "Old" ext2fs directory entries need no special
194125843Sbde			 * conversions, since they are binary compatible
195125843Sbde			 * with "new" entries having a file type of 0 (i.e.,
19655477Sbde			 * EXT2_FT_UNKNOWN).  Splitting the old name length
19755477Sbde			 * field didn't make a mess like it did in ufs,
198125843Sbde			 * because ext2fs uses a machine-independent disk
19955477Sbde			 * layout.
20055477Sbde			 */
201202283Slulf			dstdp.d_fileno = dp->e2d_ino;
202202283Slulf			dstdp.d_type = FTTODT(dp->e2d_type);
203202283Slulf			dstdp.d_namlen = dp->e2d_namlen;
20455477Sbde			dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
205202283Slulf			bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen);
20655477Sbde			bzero(dstdp.d_name + dstdp.d_namlen,
20755477Sbde			    dstdp.d_reclen - offsetof(struct dirent, d_name) -
20855477Sbde			    dstdp.d_namlen);
20955477Sbde
210202283Slulf			if (dp->e2d_reclen > 0) {
21112115Sdyson				if(dstdp.d_reclen <= uio->uio_resid) {
21212115Sdyson					/* advance dp */
213202283Slulf					dp = (struct ext2fs_direct_2 *)
214202283Slulf					    ((char *)dp + dp->e2d_reclen);
215111742Sdes					error =
216111741Sdes					  uiomove(&dstdp, dstdp.d_reclen, uio);
21724649Sdfr					if (!error)
21824649Sdfr						ncookies++;
21912115Sdyson				} else
22012115Sdyson					break;
22112115Sdyson			} else {
22212115Sdyson				error = EIO;
22312115Sdyson				break;
22412115Sdyson			}
22512115Sdyson		}
22612115Sdyson		/* we need to correct uio_offset */
22712115Sdyson		uio->uio_offset = startoffset + (caddr_t)dp - dirbuf;
22824649Sdfr
22924649Sdfr		if (!error && ap->a_ncookies != NULL) {
23065780Sbde			u_long *cookiep, *cookies, *ecookies;
23124649Sdfr			off_t off;
23224649Sdfr
23324649Sdfr			if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
234125843Sbde				panic("ext2_readdir: unexpected uio from NFS server");
235184205Sdes			cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
236111119Simp			       M_WAITOK);
23724649Sdfr			off = startoffset;
238202283Slulf			for (dp = (struct ext2fs_direct_2 *)dirbuf,
23965780Sbde			     cookiep = cookies, ecookies = cookies + ncookies;
24065780Sbde			     cookiep < ecookies;
241202283Slulf			     dp = (struct ext2fs_direct_2 *)((caddr_t) dp + dp->e2d_reclen)) {
242202283Slulf				off += dp->e2d_reclen;
24324649Sdfr				*cookiep++ = (u_long) off;
24424649Sdfr			}
24524649Sdfr			*ap->a_ncookies = ncookies;
24624649Sdfr			*ap->a_cookies = cookies;
24724649Sdfr		}
24812115Sdyson	}
249184205Sdes	free(dirbuf, M_TEMP);
25024649Sdfr	if (ap->a_eofflag)
25124649Sdfr		*ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
252111742Sdes	return (error);
25312115Sdyson}
25412115Sdyson
25512115Sdyson/*
25612115Sdyson * Convert a component of a pathname into a pointer to a locked inode.
25712115Sdyson * This is a very central and rather complicated routine.
25812115Sdyson * If the file system is not maintained in a strict tree hierarchy,
25912115Sdyson * this can result in a deadlock situation (see comments in code below).
26012115Sdyson *
26112115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
26212115Sdyson * on whether the name is to be looked up, created, renamed, or deleted.
26312115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in
26412115Sdyson * creating, renaming, or deleting a directory entry may be calculated.
26512115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname
26612115Sdyson * exists, lookup returns both the target and its parent directory locked.
26712115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may
26812115Sdyson * not be ".".  When deleting and LOCKPARENT is specified, the target may
26912115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput
27012115Sdyson * instead of two vputs.
27112115Sdyson *
272125843Sbde * Overall outline of ext2_lookup:
27312115Sdyson *
27412115Sdyson *	search for name in directory, to found or notfound
27512115Sdyson * notfound:
27612115Sdyson *	if creating, return locked directory, leaving info on available slots
27712115Sdyson *	else return error
27812115Sdyson * found:
27912115Sdyson *	if at end of path and deleting, return information to allow delete
28012115Sdyson *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
28112115Sdyson *	  inode and return info to allow rewrite
28212115Sdyson *	if not at end, add name to cache; if at end and neither creating
28312115Sdyson *	  nor deleting, add name to cache
28412115Sdyson */
28512115Sdysonint
28612115Sdysonext2_lookup(ap)
28728787Sphk	struct vop_cachedlookup_args /* {
28812115Sdyson		struct vnode *a_dvp;
28912115Sdyson		struct vnode **a_vpp;
29012115Sdyson		struct componentname *a_cnp;
29112115Sdyson	} */ *ap;
29212115Sdyson{
29396752Siedowse	struct vnode *vdp;		/* vnode for directory being searched */
29496752Siedowse	struct inode *dp;		/* inode for directory being searched */
29512115Sdyson	struct buf *bp;			/* a buffer of directory entries */
296202283Slulf	struct ext2fs_direct_2 *ep;	/* the current directory entry */
29712115Sdyson	int entryoffsetinblock;		/* offset of ep in bp's buffer */
29812115Sdyson	enum {NONE, COMPACT, FOUND} slotstatus;
29912115Sdyson	doff_t slotoffset;		/* offset of area with free space */
30012115Sdyson	int slotsize;			/* size of area at slotoffset */
301202283Slulf	doff_t i_diroff;		/* cached i_diroff value */
302202283Slulf	doff_t i_offset;		/* cached i_offset value */
30312115Sdyson	int slotfreespace;		/* amount of space free in slot */
30412115Sdyson	int slotneeded;			/* size of the entry we're seeking */
30512115Sdyson	int numdirpasses;		/* strategy for directory search */
30612115Sdyson	doff_t endsearch;		/* offset to end directory search */
30712115Sdyson	doff_t prevoff;			/* prev entry dp->i_offset */
30812115Sdyson	struct vnode *pdp;		/* saved dp during symlink work */
30912115Sdyson	struct vnode *tdp;		/* returned by VFS_VGET */
31012115Sdyson	doff_t enduseful;		/* pointer past last used dir slot */
31112115Sdyson	u_long bmask;			/* block offset mask */
31212115Sdyson	int namlen, error;
31312115Sdyson	struct vnode **vpp = ap->a_vpp;
31412115Sdyson	struct componentname *cnp = ap->a_cnp;
31512115Sdyson	struct ucred *cred = cnp->cn_cred;
31612115Sdyson	int flags = cnp->cn_flags;
31712115Sdyson	int nameiop = cnp->cn_nameiop;
318202283Slulf	ino_t ino;
319202283Slulf	int ltype;
32012115Sdyson
321202283Slulf	int	DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->e2fs_bsize;
32212115Sdyson
32312115Sdyson	bp = NULL;
32412115Sdyson	slotoffset = -1;
32512115Sdyson	*vpp = NULL;
32612115Sdyson	vdp = ap->a_dvp;
32712115Sdyson	dp = VTOI(vdp);
32812115Sdyson	/*
32912115Sdyson	 * We now have a segment name to search for, and a directory to search.
33012115Sdyson	 */
33112115Sdyson
33212115Sdyson	/*
33312115Sdyson	 * Suppress search for slots unless creating
33412115Sdyson	 * file and at end of pathname, in which case
33512115Sdyson	 * we watch for a place to put the new file in
33612115Sdyson	 * case it doesn't already exist.
33712115Sdyson	 */
338202283Slulf	ino = 0;
339202283Slulf	i_diroff = dp->i_diroff;
34012115Sdyson	slotstatus = FOUND;
34112115Sdyson	slotfreespace = slotsize = slotneeded = 0;
34212115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
34312115Sdyson	    (flags & ISLASTCN)) {
34412115Sdyson		slotstatus = NONE;
345111742Sdes		slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen);
34612115Sdyson		/* was
34712115Sdyson		slotneeded = (sizeof(struct direct) - MAXNAMLEN +
34812115Sdyson			cnp->cn_namelen + 3) &~ 3; */
34912115Sdyson	}
35012115Sdyson
35112115Sdyson	/*
35212115Sdyson	 * If there is cached information on a previous search of
35312115Sdyson	 * this directory, pick up where we last left off.
35412115Sdyson	 * We cache only lookups as these are the most common
35512115Sdyson	 * and have the greatest payoff. Caching CREATE has little
35612115Sdyson	 * benefit as it usually must search the entire directory
35712115Sdyson	 * to determine that the entry does not exist. Caching the
35812115Sdyson	 * location of the last DELETE or RENAME has not reduced
35912115Sdyson	 * profiling time and hence has been removed in the interest
36012115Sdyson	 * of simplicity.
36112115Sdyson	 */
36296749Siedowse	bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
363202283Slulf	if (nameiop != LOOKUP || i_diroff == 0 ||
364202283Slulf	    i_diroff > dp->i_size) {
36512115Sdyson		entryoffsetinblock = 0;
366202283Slulf		i_offset = 0;
36712115Sdyson		numdirpasses = 1;
36812115Sdyson	} else {
369202283Slulf		i_offset = i_diroff;
370202283Slulf		if ((entryoffsetinblock = i_offset & bmask) &&
371202283Slulf		    (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL,
37296749Siedowse		    &bp)))
37312115Sdyson			return (error);
37412115Sdyson		numdirpasses = 2;
37512115Sdyson		nchstats.ncs_2passes++;
37612115Sdyson	}
377202283Slulf	prevoff = i_offset;
378202283Slulf	endsearch = roundup2(dp->i_size, DIRBLKSIZ);
37912115Sdyson	enduseful = 0;
38012115Sdyson
38112115Sdysonsearchloop:
382202283Slulf	while (i_offset < endsearch) {
38312115Sdyson		/*
38412115Sdyson		 * If necessary, get the next directory block.
38512115Sdyson		 */
386202283Slulf		if ((i_offset & bmask) == 0) {
38712115Sdyson			if (bp != NULL)
38812115Sdyson				brelse(bp);
38943301Sdillon			if ((error =
390202283Slulf			    ext2_blkatoff(vdp, (off_t)i_offset, NULL,
39196749Siedowse			    &bp)) != 0)
39212115Sdyson				return (error);
39312115Sdyson			entryoffsetinblock = 0;
39412115Sdyson		}
39512115Sdyson		/*
39612115Sdyson		 * If still looking for a slot, and at a DIRBLKSIZE
39712115Sdyson		 * boundary, have to start looking for free space again.
39812115Sdyson		 */
39912115Sdyson		if (slotstatus == NONE &&
40012115Sdyson		    (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
40112115Sdyson			slotoffset = -1;
40212115Sdyson			slotfreespace = 0;
40312115Sdyson		}
40412115Sdyson		/*
40512115Sdyson		 * Get pointer to next entry.
40612115Sdyson		 * Full validation checks are slow, so we only check
40712115Sdyson		 * enough to insure forward progress through the
40896753Siedowse		 * directory. Complete checks can be run by setting
40996753Siedowse		 * "vfs.e2fs.dirchk" to be true.
41012115Sdyson		 */
411202283Slulf		ep = (struct ext2fs_direct_2 *)
41212115Sdyson			((char *)bp->b_data + entryoffsetinblock);
413202283Slulf		if (ep->e2d_reclen == 0 ||
41412147Sdyson		    (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) {
41512115Sdyson			int i;
416202283Slulf			ext2_dirbad(dp, i_offset, "mangled entry");
41712115Sdyson			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
418202283Slulf			i_offset += i;
41912115Sdyson			entryoffsetinblock += i;
42012115Sdyson			continue;
42112115Sdyson		}
42212115Sdyson
42312115Sdyson		/*
42412115Sdyson		 * If an appropriate sized slot has not yet been found,
42512115Sdyson		 * check to see if one is available. Also accumulate space
42612115Sdyson		 * in the current block so that we can determine if
42712115Sdyson		 * compaction is viable.
42812115Sdyson		 */
42912115Sdyson		if (slotstatus != FOUND) {
430202283Slulf			int size = ep->e2d_reclen;
43112115Sdyson
432202283Slulf			if (ep->e2d_ino != 0)
433202283Slulf				size -= EXT2_DIR_REC_LEN(ep->e2d_namlen);
43412115Sdyson			if (size > 0) {
43512115Sdyson				if (size >= slotneeded) {
43612115Sdyson					slotstatus = FOUND;
437202283Slulf					slotoffset = i_offset;
438202283Slulf					slotsize = ep->e2d_reclen;
43912115Sdyson				} else if (slotstatus == NONE) {
44012115Sdyson					slotfreespace += size;
44112115Sdyson					if (slotoffset == -1)
442202283Slulf						slotoffset = i_offset;
44312115Sdyson					if (slotfreespace >= slotneeded) {
44412115Sdyson						slotstatus = COMPACT;
445202283Slulf						slotsize = i_offset +
446202283Slulf						      ep->e2d_reclen - slotoffset;
44712115Sdyson					}
44812115Sdyson				}
44912115Sdyson			}
45012115Sdyson		}
45112115Sdyson
45212115Sdyson		/*
45312115Sdyson		 * Check for a name match.
45412115Sdyson		 */
455202283Slulf		if (ep->e2d_ino) {
456202283Slulf			namlen = ep->e2d_namlen;
45712115Sdyson			if (namlen == cnp->cn_namelen &&
458202283Slulf			    !bcmp(cnp->cn_nameptr, ep->e2d_name,
45912115Sdyson				(unsigned)namlen)) {
46012115Sdyson				/*
46112115Sdyson				 * Save directory entry's inode number and
46212115Sdyson				 * reclen in ndp->ni_ufs area, and release
46312115Sdyson				 * directory buffer.
46412115Sdyson				 */
465202283Slulf				ino = ep->e2d_ino;
46612115Sdyson				goto found;
46712115Sdyson			}
46812115Sdyson		}
469202283Slulf		prevoff = i_offset;
470202283Slulf		i_offset += ep->e2d_reclen;
471202283Slulf		entryoffsetinblock += ep->e2d_reclen;
472202283Slulf		if (ep->e2d_ino)
473202283Slulf			enduseful = i_offset;
47412115Sdyson	}
47512115Sdyson/* notfound: */
47612115Sdyson	/*
47712115Sdyson	 * If we started in the middle of the directory and failed
47812115Sdyson	 * to find our target, we must check the beginning as well.
47912115Sdyson	 */
48012115Sdyson	if (numdirpasses == 2) {
48112115Sdyson		numdirpasses--;
482202283Slulf		i_offset = 0;
483202283Slulf		endsearch = i_diroff;
48412115Sdyson		goto searchloop;
48512115Sdyson	}
486202283Slulf	dp->i_offset = i_offset;
48712115Sdyson	if (bp != NULL)
48812115Sdyson		brelse(bp);
48912115Sdyson	/*
49012115Sdyson	 * If creating, and at end of pathname and current
49112115Sdyson	 * directory has not been removed, then can consider
49212115Sdyson	 * allowing file to be created.
49312115Sdyson	 */
49412115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
49512115Sdyson	    (flags & ISLASTCN) && dp->i_nlink != 0) {
49612115Sdyson		/*
49712115Sdyson		 * Access for write is interpreted as allowing
49812115Sdyson		 * creation of files in the directory.
49912115Sdyson		 */
50083366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
50112115Sdyson			return (error);
50212115Sdyson		/*
50312115Sdyson		 * Return an indication of where the new directory
50412115Sdyson		 * entry should be put.  If we didn't find a slot,
50512115Sdyson		 * then set dp->i_count to 0 indicating
50612115Sdyson		 * that the new slot belongs at the end of the
50712115Sdyson		 * directory. If we found a slot, then the new entry
50812115Sdyson		 * can be put in the range from dp->i_offset to
50912115Sdyson		 * dp->i_offset + dp->i_count.
51012115Sdyson		 */
51112115Sdyson		if (slotstatus == NONE) {
512202283Slulf			dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ);
51312115Sdyson			dp->i_count = 0;
51412115Sdyson			enduseful = dp->i_offset;
51512115Sdyson		} else {
51612115Sdyson			dp->i_offset = slotoffset;
51712115Sdyson			dp->i_count = slotsize;
51812115Sdyson			if (enduseful < slotoffset + slotsize)
51912115Sdyson				enduseful = slotoffset + slotsize;
52012115Sdyson		}
521202283Slulf		dp->i_endoff = roundup2(enduseful, DIRBLKSIZ);
52212115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
52312115Sdyson		/*
52412115Sdyson		 * We return with the directory locked, so that
52512115Sdyson		 * the parameters we set up above will still be
52612115Sdyson		 * valid if we actually decide to do a direnter().
52712115Sdyson		 * We return ni_vp == NULL to indicate that the entry
52812115Sdyson		 * does not currently exist; we leave a pointer to
52912115Sdyson		 * the (locked) directory inode in ndp->ni_dvp.
53012115Sdyson		 * The pathname buffer is saved so that the name
53112115Sdyson		 * can be obtained later.
53212115Sdyson		 *
53312115Sdyson		 * NB - if the directory is unlocked, then this
53412115Sdyson		 * information cannot be used.
53512115Sdyson		 */
53612115Sdyson		cnp->cn_flags |= SAVENAME;
53712115Sdyson		return (EJUSTRETURN);
53812115Sdyson	}
53912115Sdyson	/*
54012115Sdyson	 * Insert name into cache (as non-existent) if appropriate.
54112115Sdyson	 */
54212115Sdyson	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
54312115Sdyson		cache_enter(vdp, *vpp, cnp);
54412115Sdyson	return (ENOENT);
54512115Sdyson
54612115Sdysonfound:
54712115Sdyson	if (numdirpasses == 2)
54812115Sdyson		nchstats.ncs_pass2++;
54912115Sdyson	/*
55012115Sdyson	 * Check that directory length properly reflects presence
55112115Sdyson	 * of this entry.
55212115Sdyson	 */
553202283Slulf	if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen)
55412115Sdyson		> dp->i_size) {
555202283Slulf		ext2_dirbad(dp, i_offset, "i_size too small");
556202283Slulf		dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->e2d_namlen);
55712115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
55812115Sdyson	}
559105420Sbde	brelse(bp);
56012115Sdyson
56112115Sdyson	/*
56212115Sdyson	 * Found component in pathname.
56312115Sdyson	 * If the final component of path name, save information
56412115Sdyson	 * in the cache as to where the entry was found.
56512115Sdyson	 */
56612115Sdyson	if ((flags & ISLASTCN) && nameiop == LOOKUP)
567202283Slulf		dp->i_diroff = i_offset &~ (DIRBLKSIZ - 1);
568202283Slulf	dp->i_offset = i_offset;
56912115Sdyson	/*
57012115Sdyson	 * If deleting, and at end of pathname, return
57112115Sdyson	 * parameters which can be used to remove file.
57212115Sdyson	 */
57312115Sdyson	if (nameiop == DELETE && (flags & ISLASTCN)) {
57412115Sdyson		/*
57512115Sdyson		 * Write access to directory required to delete files.
57612115Sdyson		 */
57783366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
57812115Sdyson			return (error);
57912115Sdyson		/*
58012115Sdyson		 * Return pointer to current entry in dp->i_offset,
58112115Sdyson		 * and distance past previous entry (if there
58212115Sdyson		 * is a previous entry in this block) in dp->i_count.
58312115Sdyson		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
58412115Sdyson		 */
58512115Sdyson		if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
58612115Sdyson			dp->i_count = 0;
58712115Sdyson		else
58812115Sdyson			dp->i_count = dp->i_offset - prevoff;
589202283Slulf		if (dp->i_number == ino) {
59012115Sdyson			VREF(vdp);
59112115Sdyson			*vpp = vdp;
59212115Sdyson			return (0);
59312115Sdyson		}
594202283Slulf		if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
59592462Smckusick		    &tdp)) != 0)
59612115Sdyson			return (error);
59712115Sdyson		/*
59812115Sdyson		 * If directory is "sticky", then user must own
59912115Sdyson		 * the directory, or the file in it, else she
60012115Sdyson		 * may not delete it (unless she's root). This
60112115Sdyson		 * implements append-only directories.
60212115Sdyson		 */
60312115Sdyson		if ((dp->i_mode & ISVTX) &&
60412115Sdyson		    cred->cr_uid != 0 &&
60512115Sdyson		    cred->cr_uid != dp->i_uid &&
60612115Sdyson		    VTOI(tdp)->i_uid != cred->cr_uid) {
60712115Sdyson			vput(tdp);
60812115Sdyson			return (EPERM);
60912115Sdyson		}
61012115Sdyson		*vpp = tdp;
61112115Sdyson		return (0);
61212115Sdyson	}
61312115Sdyson
61412115Sdyson	/*
61512115Sdyson	 * If rewriting (RENAME), return the inode and the
61612115Sdyson	 * information required to rewrite the present directory
61712115Sdyson	 * Must get inode of directory entry to verify it's a
61812115Sdyson	 * regular file, or empty directory.
61912115Sdyson	 */
620144299Sjeff	if (nameiop == RENAME && (flags & ISLASTCN)) {
62183366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
62212115Sdyson			return (error);
62312115Sdyson		/*
62412115Sdyson		 * Careful about locking second inode.
62512115Sdyson		 * This can only occur if the target is ".".
62612115Sdyson		 */
627202283Slulf		if (dp->i_number == ino)
62812115Sdyson			return (EISDIR);
629202283Slulf		if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
63092462Smckusick		    &tdp)) != 0)
63112115Sdyson			return (error);
63212115Sdyson		*vpp = tdp;
63312115Sdyson		cnp->cn_flags |= SAVENAME;
63412115Sdyson		return (0);
63512115Sdyson	}
63612115Sdyson
63712115Sdyson	/*
63812115Sdyson	 * Step through the translation in the name.  We do not `vput' the
63912115Sdyson	 * directory because we may need it again if a symbolic link
64012115Sdyson	 * is relative to the current directory.  Instead we save it
64112115Sdyson	 * unlocked as "pdp".  We must get the target inode before unlocking
64212115Sdyson	 * the directory to insure that the inode will not be removed
64312115Sdyson	 * before we get it.  We prevent deadlock by always fetching
64412115Sdyson	 * inodes from the root, moving down the directory tree. Thus
64512115Sdyson	 * when following backward pointers ".." we must unlock the
64612115Sdyson	 * parent directory before getting the requested directory.
64712115Sdyson	 * There is a potential race condition here if both the current
64812115Sdyson	 * and parent directories are removed before the VFS_VGET for the
64912115Sdyson	 * inode associated with ".." returns.  We hope that this occurs
65012115Sdyson	 * infrequently since we cannot avoid this race condition without
65112115Sdyson	 * implementing a sophisticated deadlock detection algorithm.
65212115Sdyson	 * Note also that this simple deadlock detection scheme will not
65312115Sdyson	 * work if the file system has any hard links other than ".."
65412115Sdyson	 * that point backwards in the directory structure.
65512115Sdyson	 */
65612115Sdyson	pdp = vdp;
65712115Sdyson	if (flags & ISDOTDOT) {
658202283Slulf		ltype = VOP_ISLOCKED(pdp);
659175294Sattilio		VOP_UNLOCK(pdp, 0);	/* race to get the inode */
660202283Slulf		error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, &tdp);
661202283Slulf		vn_lock(pdp, ltype | LK_RETRY);
662145006Sjeff		if (error != 0)
66312115Sdyson			return (error);
66412115Sdyson		*vpp = tdp;
665202283Slulf	} else if (dp->i_number == ino) {
66612115Sdyson		VREF(vdp);	/* we want ourself, ie "." */
667202283Slulf		/*
668202283Slulf		 * When we lookup "." we still can be asked to lock it
669202283Slulf		 * differently.
670202283Slulf		 */
671202283Slulf		ltype = cnp->cn_lkflags & LK_TYPE_MASK;
672202283Slulf		if (ltype != VOP_ISLOCKED(vdp)) {
673202283Slulf			if (ltype == LK_EXCLUSIVE)
674202283Slulf				vn_lock(vdp, LK_UPGRADE | LK_RETRY);
675202283Slulf			else /* if (ltype == LK_SHARED) */
676202283Slulf				vn_lock(vdp, LK_DOWNGRADE | LK_RETRY);
677202283Slulf		}
67812115Sdyson		*vpp = vdp;
67912115Sdyson	} else {
680202283Slulf		if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags,
68192462Smckusick		    &tdp)) != 0)
68212115Sdyson			return (error);
68312115Sdyson		*vpp = tdp;
68412115Sdyson	}
68512115Sdyson
68612115Sdyson	/*
68712115Sdyson	 * Insert name into cache if appropriate.
68812115Sdyson	 */
68912115Sdyson	if (cnp->cn_flags & MAKEENTRY)
69012115Sdyson		cache_enter(vdp, *vpp, cnp);
69112115Sdyson	return (0);
69212115Sdyson}
69312115Sdyson
69496749Siedowsevoid
69596749Siedowseext2_dirbad(ip, offset, how)
69696749Siedowse	struct inode *ip;
69796749Siedowse	doff_t offset;
69896749Siedowse	char *how;
69996749Siedowse{
70096749Siedowse	struct mount *mp;
70196749Siedowse
70296749Siedowse	mp = ITOV(ip)->v_mount;
703202283Slulf	if ((mp->mnt_flag & MNT_RDONLY) == 0)
704202283Slulf		panic("ext2_dirbad: %s: bad dir ino %lu at offset %ld: %s\n",
705202283Slulf			mp->mnt_stat.f_mntonname, (u_long)ip->i_number,(long)offset, how);
706202283Slulf	else
70796749Siedowse	(void)printf("%s: bad dir ino %lu at offset %ld: %s\n",
708202283Slulf            mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how);
709202283Slulf
71096749Siedowse}
71196749Siedowse
71212115Sdyson/*
71312115Sdyson * Do consistency checking on a directory entry:
71412115Sdyson *	record length must be multiple of 4
71512115Sdyson *	entry must fit in rest of its DIRBLKSIZ block
71612115Sdyson *	record must be large enough to contain entry
71712115Sdyson *	name is not longer than MAXNAMLEN
71812115Sdyson *	name must be as long as advertised, and null terminated
71912115Sdyson */
72012115Sdyson/*
72112115Sdyson *	changed so that it confirms to ext2_check_dir_entry
72212115Sdyson */
72312159Sbdestatic int
72412115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock)
72512115Sdyson	struct vnode *dp;
726202283Slulf	struct ext2fs_direct_2 *de;
72712115Sdyson	int entryoffsetinblock;
72812115Sdyson{
729202283Slulf	int	DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize;
73012115Sdyson
731111742Sdes	char * error_msg = NULL;
73212115Sdyson
733202283Slulf	if (de->e2d_reclen < EXT2_DIR_REC_LEN(1))
734111742Sdes		error_msg = "rec_len is smaller than minimal";
735202283Slulf	else if (de->e2d_reclen % 4 != 0)
736111742Sdes		error_msg = "rec_len % 4 != 0";
737202283Slulf	else if (de->e2d_reclen < EXT2_DIR_REC_LEN(de->e2d_namlen))
738111742Sdes		error_msg = "reclen is too small for name_len";
739202283Slulf	else if (entryoffsetinblock + de->e2d_reclen > DIRBLKSIZ)
740111742Sdes		error_msg = "directory entry across blocks";
741111742Sdes	/* else LATER
74212115Sdyson	     if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
743111742Sdes		error_msg = "inode out of bounds";
74412115Sdyson	*/
74512115Sdyson
746111742Sdes	if (error_msg != NULL) {
747111742Sdes		printf("bad directory entry: %s\n", error_msg);
748111742Sdes		printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
749202283Slulf			entryoffsetinblock, (unsigned long)de->e2d_ino,
750202283Slulf			de->e2d_reclen, de->e2d_namlen);
751111742Sdes	}
752111742Sdes	return error_msg == NULL ? 0 : 1;
75312115Sdyson}
75412115Sdyson
75512115Sdyson/*
75612115Sdyson * Write a directory entry after a call to namei, using the parameters
75712115Sdyson * that it left in nameidata.  The argument ip is the inode which the new
75812115Sdyson * directory entry will refer to.  Dvp is a pointer to the directory to
75912115Sdyson * be written, which was left locked by namei. Remaining parameters
76012115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new
76112115Sdyson * entry is to be obtained.
76212115Sdyson */
76312115Sdysonint
76412115Sdysonext2_direnter(ip, dvp, cnp)
76512115Sdyson	struct inode *ip;
76612115Sdyson	struct vnode *dvp;
76796752Siedowse	struct componentname *cnp;
76812115Sdyson{
769202283Slulf	struct ext2fs_direct_2 *ep, *nep;
77096752Siedowse	struct inode *dp;
77112115Sdyson	struct buf *bp;
772202283Slulf	struct ext2fs_direct_2 newdir;
77312115Sdyson	struct iovec aiov;
77412115Sdyson	struct uio auio;
77512115Sdyson	u_int dsize;
77612115Sdyson	int error, loc, newentrysize, spacefree;
77712115Sdyson	char *dirbuf;
778202283Slulf	int     DIRBLKSIZ = ip->i_e2fs->e2fs_bsize;
77912115Sdyson
78012115Sdyson
781153110Sru#ifdef DIAGNOSTIC
78212115Sdyson	if ((cnp->cn_flags & SAVENAME) == 0)
78312115Sdyson		panic("direnter: missing name");
78412115Sdyson#endif
78512115Sdyson	dp = VTOI(dvp);
786202283Slulf	newdir.e2d_ino = ip->i_number;
787202283Slulf	newdir.e2d_namlen = cnp->cn_namelen;
788193377Sstas	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
789202283Slulf	    EXT2F_INCOMPAT_FTYPE))
790202283Slulf		newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode));
79155477Sbde	else
792202283Slulf		newdir.e2d_type = EXT2_FT_UNKNOWN;
793202283Slulf	bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1);
794202283Slulf	newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen);
79512115Sdyson	if (dp->i_count == 0) {
79612115Sdyson		/*
79712115Sdyson		 * If dp->i_count is 0, then namei could find no
79812115Sdyson		 * space in the directory. Here, dp->i_offset will
79912115Sdyson		 * be on a directory block boundary and we will write the
80012115Sdyson		 * new entry into a fresh block.
80112115Sdyson		 */
80212115Sdyson		if (dp->i_offset & (DIRBLKSIZ - 1))
80312115Sdyson			panic("ext2_direnter: newblk");
80412115Sdyson		auio.uio_offset = dp->i_offset;
805202283Slulf		newdir.e2d_reclen = DIRBLKSIZ;
80612115Sdyson		auio.uio_resid = newentrysize;
80712115Sdyson		aiov.iov_len = newentrysize;
80812115Sdyson		aiov.iov_base = (caddr_t)&newdir;
80912115Sdyson		auio.uio_iov = &aiov;
81012115Sdyson		auio.uio_iovcnt = 1;
81112115Sdyson		auio.uio_rw = UIO_WRITE;
81212115Sdyson		auio.uio_segflg = UIO_SYSSPACE;
81383366Sjulian		auio.uio_td = (struct thread *)0;
81412115Sdyson		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
81512115Sdyson		if (DIRBLKSIZ >
81696749Siedowse		    VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
81712115Sdyson			/* XXX should grow with balloc() */
81812115Sdyson			panic("ext2_direnter: frag size");
81912115Sdyson		else if (!error) {
820202283Slulf			dp->i_size = roundup2(dp->i_size, DIRBLKSIZ);
82112115Sdyson			dp->i_flag |= IN_CHANGE;
82212115Sdyson		}
82312115Sdyson		return (error);
82412115Sdyson	}
82512115Sdyson
82612115Sdyson	/*
82712115Sdyson	 * If dp->i_count is non-zero, then namei found space
82812115Sdyson	 * for the new entry in the range dp->i_offset to
82912115Sdyson	 * dp->i_offset + dp->i_count in the directory.
83012115Sdyson	 * To use this space, we may have to compact the entries located
83112115Sdyson	 * there, by copying them together towards the beginning of the
83212115Sdyson	 * block, leaving the free space in one usable chunk at the end.
83312115Sdyson	 */
83412115Sdyson
83512115Sdyson	/*
83612115Sdyson	 * Increase size of directory if entry eats into new space.
83712115Sdyson	 * This should never push the size past a new multiple of
83812115Sdyson	 * DIRBLKSIZE.
83912115Sdyson	 *
84012115Sdyson	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
84112115Sdyson	 */
84212115Sdyson	if (dp->i_offset + dp->i_count > dp->i_size)
84312115Sdyson		dp->i_size = dp->i_offset + dp->i_count;
84412115Sdyson	/*
84512115Sdyson	 * Get the block containing the space for the new directory entry.
84612115Sdyson	 */
84796749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf,
84896749Siedowse	    &bp)) != 0)
84912115Sdyson		return (error);
85012115Sdyson	/*
85112115Sdyson	 * Find space for the new entry. In the simple case, the entry at
85212115Sdyson	 * offset base will have the space. If it does not, then namei
85312115Sdyson	 * arranged that compacting the region dp->i_offset to
85412115Sdyson	 * dp->i_offset + dp->i_count would yield the
85512115Sdyson	 * space.
85612115Sdyson	 */
857202283Slulf	ep = (struct ext2fs_direct_2 *)dirbuf;
858202283Slulf	dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen);
859202283Slulf	spacefree = ep->e2d_reclen - dsize;
860202283Slulf	for (loc = ep->e2d_reclen; loc < dp->i_count; ) {
861202283Slulf		nep = (struct ext2fs_direct_2 *)(dirbuf + loc);
862202283Slulf		if (ep->e2d_ino) {
86312115Sdyson			/* trim the existing slot */
864202283Slulf			ep->e2d_reclen = dsize;
865202283Slulf			ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
86612115Sdyson		} else {
86712115Sdyson			/* overwrite; nothing there; header is ours */
86812115Sdyson			spacefree += dsize;
86912115Sdyson		}
870202283Slulf		dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen);
871202283Slulf		spacefree += nep->e2d_reclen - dsize;
872202283Slulf		loc += nep->e2d_reclen;
87312115Sdyson		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
87412115Sdyson	}
87512115Sdyson	/*
87612115Sdyson	 * Update the pointer fields in the previous entry (if any),
87712115Sdyson	 * copy in the new entry, and write out the block.
87812115Sdyson	 */
879202283Slulf	if (ep->e2d_ino == 0) {
88012115Sdyson		if (spacefree + dsize < newentrysize)
88112115Sdyson			panic("ext2_direnter: compact1");
882202283Slulf		newdir.e2d_reclen = spacefree + dsize;
88312115Sdyson	} else {
88412115Sdyson		if (spacefree < newentrysize)
88512115Sdyson			panic("ext2_direnter: compact2");
886202283Slulf		newdir.e2d_reclen = spacefree;
887202283Slulf		ep->e2d_reclen = dsize;
888202283Slulf		ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
88912115Sdyson	}
89012115Sdyson	bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
891126853Sphk	error = bwrite(bp);
89212115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
89312115Sdyson	if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
89496749Siedowse		error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
89583366Sjulian		    cnp->cn_cred, cnp->cn_thread);
89612115Sdyson	return (error);
89712115Sdyson}
89812115Sdyson
89912115Sdyson/*
90012115Sdyson * Remove a directory entry after a call to namei, using
90112115Sdyson * the parameters which it left in nameidata. The entry
90212115Sdyson * dp->i_offset contains the offset into the directory of the
90312115Sdyson * entry to be eliminated.  The dp->i_count field contains the
90412115Sdyson * size of the previous record in the directory.  If this
90512115Sdyson * is 0, the first entry is being deleted, so we need only
90612115Sdyson * zero the inode number to mark the entry as free.  If the
90712115Sdyson * entry is not the first in the directory, we must reclaim
90812115Sdyson * the space of the now empty record by adding the record size
90912115Sdyson * to the size of the previous entry.
91012115Sdyson */
91112115Sdysonint
91212115Sdysonext2_dirremove(dvp, cnp)
91312115Sdyson	struct vnode *dvp;
91412115Sdyson	struct componentname *cnp;
91512115Sdyson{
91696752Siedowse	struct inode *dp;
917202283Slulf	struct ext2fs_direct_2 *ep, *rep;
91812115Sdyson	struct buf *bp;
91912115Sdyson	int error;
920111742Sdes
92112115Sdyson	dp = VTOI(dvp);
92212115Sdyson	if (dp->i_count == 0) {
92312115Sdyson		/*
92412115Sdyson		 * First entry in block: set d_ino to zero.
92512115Sdyson		 */
92643301Sdillon		if ((error =
92796749Siedowse		    ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep,
92896749Siedowse		    &bp)) != 0)
92912115Sdyson			return (error);
930202283Slulf		ep->e2d_ino = 0;
931126853Sphk		error = bwrite(bp);
93212115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
93312115Sdyson		return (error);
93412115Sdyson	}
93512115Sdyson	/*
93612115Sdyson	 * Collapse new free space into previous entry.
93712115Sdyson	 */
93896749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
93943301Sdillon	    (char **)&ep, &bp)) != 0)
94012115Sdyson		return (error);
941202283Slulf
942202283Slulf	/* Set 'rep' to the entry being removed. */
943202283Slulf	if (dp->i_count == 0)
944202283Slulf		rep = ep;
945202283Slulf	else
946202283Slulf		rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen);
947202283Slulf	ep->e2d_reclen += rep->e2d_reclen;
948126853Sphk	error = bwrite(bp);
94912115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
95012115Sdyson	return (error);
95112115Sdyson}
95212115Sdyson
95312115Sdyson/*
95412115Sdyson * Rewrite an existing directory entry to point at the inode
95512115Sdyson * supplied.  The parameters describing the directory entry are
95612115Sdyson * set up by a call to namei.
95712115Sdyson */
95812115Sdysonint
95912115Sdysonext2_dirrewrite(dp, ip, cnp)
96012115Sdyson	struct inode *dp, *ip;
96112115Sdyson	struct componentname *cnp;
96212115Sdyson{
96312115Sdyson	struct buf *bp;
964202283Slulf	struct ext2fs_direct_2 *ep;
96512115Sdyson	struct vnode *vdp = ITOV(dp);
96612115Sdyson	int error;
96712115Sdyson
96896749Siedowse	if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep,
96996749Siedowse	    &bp)) != 0)
97012115Sdyson		return (error);
971202283Slulf	ep->e2d_ino = ip->i_number;
972193377Sstas	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
973202283Slulf	    EXT2F_INCOMPAT_FTYPE))
974202283Slulf		ep->e2d_type = DTTOFT(IFTODT(ip->i_mode));
97555477Sbde	else
976202283Slulf		ep->e2d_type = EXT2_FT_UNKNOWN;
977126853Sphk	error = bwrite(bp);
97812115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
97912115Sdyson	return (error);
98012115Sdyson}
98112115Sdyson
98212115Sdyson/*
98312115Sdyson * Check if a directory is empty or not.
98412115Sdyson * Inode supplied must be locked.
98512115Sdyson *
98612115Sdyson * Using a struct dirtemplate here is not precisely
98712115Sdyson * what we want, but better than using a struct direct.
98812115Sdyson *
98912115Sdyson * NB: does not handle corrupted directories.
99012115Sdyson */
99112115Sdysonint
99212115Sdysonext2_dirempty(ip, parentino, cred)
99396752Siedowse	struct inode *ip;
99412115Sdyson	ino_t parentino;
99512115Sdyson	struct ucred *cred;
99612115Sdyson{
99796752Siedowse	off_t off;
99812115Sdyson	struct dirtemplate dbuf;
999202283Slulf	struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf;
100012115Sdyson	int error, count, namlen;
100112115Sdyson#define	MINDIRSIZ (sizeof (struct dirtemplate) / 2)
100212115Sdyson
1003202283Slulf	for (off = 0; off < ip->i_size; off += dp->e2d_reclen) {
1004101744Srwatson		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
1005101744Srwatson		    off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
1006101941Srwatson		    NOCRED, &count, (struct thread *)0);
100712115Sdyson		/*
100812115Sdyson		 * Since we read MINDIRSIZ, residual must
100912115Sdyson		 * be 0 unless we're at end of file.
101012115Sdyson		 */
101112115Sdyson		if (error || count != 0)
101212115Sdyson			return (0);
101312115Sdyson		/* avoid infinite loops */
1014202283Slulf		if (dp->e2d_reclen == 0)
101512115Sdyson			return (0);
101612115Sdyson		/* skip empty entries */
1017202283Slulf		if (dp->e2d_ino == 0)
101812115Sdyson			continue;
101912115Sdyson		/* accept only "." and ".." */
1020202283Slulf		namlen = dp->e2d_namlen;
102112115Sdyson		if (namlen > 2)
102212115Sdyson			return (0);
1023202283Slulf		if (dp->e2d_name[0] != '.')
102412115Sdyson			return (0);
102512115Sdyson		/*
102612115Sdyson		 * At this point namlen must be 1 or 2.
102712115Sdyson		 * 1 implies ".", 2 implies ".." if second
102812115Sdyson		 * char is also "."
102912115Sdyson		 */
103012115Sdyson		if (namlen == 1)
103112115Sdyson			continue;
1032202283Slulf		if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino)
103312115Sdyson			continue;
103412115Sdyson		return (0);
103512115Sdyson	}
103612115Sdyson	return (1);
103712115Sdyson}
103812115Sdyson
103912115Sdyson/*
104012115Sdyson * Check if source directory is in the path of the target directory.
104112115Sdyson * Target is supplied locked, source is unlocked.
104212115Sdyson * The target is always vput before returning.
104312115Sdyson */
104412115Sdysonint
104512115Sdysonext2_checkpath(source, target, cred)
104612115Sdyson	struct inode *source, *target;
104712115Sdyson	struct ucred *cred;
104812115Sdyson{
104912115Sdyson	struct vnode *vp;
105012115Sdyson	int error, rootino, namlen;
105112115Sdyson	struct dirtemplate dirbuf;
105212115Sdyson
105312115Sdyson	vp = ITOV(target);
105412115Sdyson	if (target->i_number == source->i_number) {
105512115Sdyson		error = EEXIST;
105612115Sdyson		goto out;
105712115Sdyson	}
105812115Sdyson	rootino = ROOTINO;
105912115Sdyson	error = 0;
106012115Sdyson	if (target->i_number == rootino)
106112115Sdyson		goto out;
106212115Sdyson
106312115Sdyson	for (;;) {
106412115Sdyson		if (vp->v_type != VDIR) {
106512115Sdyson			error = ENOTDIR;
106612115Sdyson			break;
106712115Sdyson		}
106812115Sdyson		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
106912115Sdyson			sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1070194296Skib			IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL,
1071194296Skib			NULL);
107212115Sdyson		if (error != 0)
107312115Sdyson			break;
107457710Sbde		namlen = dirbuf.dotdot_type;	/* like ufs little-endian */
107512115Sdyson		if (namlen != 2 ||
107612115Sdyson		    dirbuf.dotdot_name[0] != '.' ||
107712115Sdyson		    dirbuf.dotdot_name[1] != '.') {
107812115Sdyson			error = ENOTDIR;
107912115Sdyson			break;
108012115Sdyson		}
108112115Sdyson		if (dirbuf.dotdot_ino == source->i_number) {
108212115Sdyson			error = EINVAL;
108312115Sdyson			break;
108412115Sdyson		}
108512115Sdyson		if (dirbuf.dotdot_ino == rootino)
108612115Sdyson			break;
108712115Sdyson		vput(vp);
108892462Smckusick		if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
108992462Smckusick		    LK_EXCLUSIVE, &vp)) != 0) {
109012115Sdyson			vp = NULL;
109112115Sdyson			break;
109212115Sdyson		}
109312115Sdyson	}
109412115Sdyson
109512115Sdysonout:
109612115Sdyson	if (error == ENOTDIR)
109712115Sdyson		printf("checkpath: .. not a directory\n");
109812115Sdyson	if (vp != NULL)
109912115Sdyson		vput(vp);
110012115Sdyson	return (error);
110112115Sdyson}
1102