ext2_lookup.c revision 101744
112115Sdyson/*
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 */
712115Sdyson/*
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 * 3. All advertising materials mentioning features or use of this software
2512115Sdyson *    must display the following acknowledgement:
2612115Sdyson *	This product includes software developed by the University of
2712115Sdyson *	California, Berkeley and its contributors.
2812115Sdyson * 4. Neither the name of the University nor the names of its contributors
2912115Sdyson *    may be used to endorse or promote products derived from this software
3012115Sdyson *    without specific prior written permission.
3112115Sdyson *
3212115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3312115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3412115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3512115Sdyson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3612115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3712115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3812115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3912115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4012115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4112115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4212115Sdyson * SUCH DAMAGE.
4312115Sdyson *
4412115Sdyson *	@(#)ufs_lookup.c	8.6 (Berkeley) 4/1/94
4593016Sbde * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_lookup.c 101744 2002-08-12 16:43:04Z rwatson $
4612115Sdyson */
4712115Sdyson
4812115Sdyson#include <sys/param.h>
4912159Sbde#include <sys/systm.h>
5012115Sdyson#include <sys/namei.h>
5160041Sphk#include <sys/bio.h>
5212115Sdyson#include <sys/buf.h>
5312115Sdyson#include <sys/mount.h>
5412115Sdyson#include <sys/vnode.h>
5512115Sdyson#include <sys/malloc.h>
5612115Sdyson#include <sys/dirent.h>
5796753Siedowse#include <sys/sysctl.h>
5812115Sdyson
5912115Sdyson#include <ufs/ufs/dir.h>
6012115Sdyson
6196749Siedowse#include <gnu/ext2fs/inode.h>
6296749Siedowse#include <gnu/ext2fs/ext2_mount.h>
6312115Sdyson#include <gnu/ext2fs/ext2_extern.h>
6412115Sdyson#include <gnu/ext2fs/ext2_fs.h>
6512115Sdyson#include <gnu/ext2fs/ext2_fs_sb.h>
6612115Sdyson
6796753Siedowse#ifdef DIAGNOSTIC
6896753Siedowsestatic int dirchk = 1;
6996753Siedowse#else
7096753Siedowsestatic int dirchk = 0;
7196753Siedowse#endif
7296753Siedowse
7396753SiedowseSYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem");
7496753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
7596753Siedowse
7612115Sdyson/*
7712115Sdyson   DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
7812115Sdyson   while it is the native blocksize in ext2fs - thus, a #define
7912115Sdyson   is no longer appropriate
8012115Sdyson*/
8112115Sdyson#undef  DIRBLKSIZ
8212115Sdyson
8355477Sbdestatic u_char ext2_ft_to_dt[] = {
8455477Sbde	DT_UNKNOWN,		/* EXT2_FT_UNKNOWN */
8555477Sbde	DT_REG,			/* EXT2_FT_REG_FILE */
8655477Sbde	DT_DIR,			/* EXT2_FT_DIR */
8755477Sbde	DT_CHR,			/* EXT2_FT_CHRDEV */
8855477Sbde	DT_BLK,			/* EXT2_FT_BLKDEV */
8955477Sbde	DT_FIFO,		/* EXT2_FT_FIFO */
9055477Sbde	DT_SOCK,		/* EXT2_FT_SOCK */
9155477Sbde	DT_LNK,			/* EXT2_FT_SYMLINK */
9255477Sbde};
9355477Sbde#define	FTTODT(ft)						\
9455477Sbde    ((ft) > sizeof(ext2_ft_to_dt) / sizeof(ext2_ft_to_dt[0]) ?	\
9555477Sbde    DT_UNKNOWN : ext2_ft_to_dt[(ft)])
9655477Sbde
9755477Sbdestatic u_char dt_to_ext2_ft[] = {
9855477Sbde	EXT2_FT_UNKNOWN,	/* DT_UNKNOWN */
9955477Sbde	EXT2_FT_FIFO,		/* DT_FIFO */
10055477Sbde	EXT2_FT_CHRDEV,		/* DT_CHR */
10155477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10255477Sbde	EXT2_FT_DIR,		/* DT_DIR */
10355477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10455477Sbde	EXT2_FT_BLKDEV,		/* DT_BLK */
10555477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10655477Sbde	EXT2_FT_REG_FILE,	/* DT_REG */
10755477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10855477Sbde	EXT2_FT_SYMLINK,	/* DT_LNK */
10955477Sbde	EXT2_FT_UNKNOWN,	/* unused */
11055477Sbde	EXT2_FT_SOCK,		/* DT_SOCK */
11155477Sbde	EXT2_FT_UNKNOWN,	/* unused */
11255477Sbde	EXT2_FT_UNKNOWN,	/* DT_WHT */
11355477Sbde};
11455477Sbde#define	DTTOFT(dt)						\
11555477Sbde    ((dt) > sizeof(dt_to_ext2_ft) / sizeof(dt_to_ext2_ft[0]) ?	\
11655477Sbde    EXT2_FT_UNKNOWN : dt_to_ext2_ft[(dt)])
11755477Sbde
11893014Sbdestatic int	ext2_dirbadentry(struct vnode *dp, struct ext2_dir_entry_2 *de,
11993014Sbde		    int entryoffsetinblock);
12012159Sbde
12112115Sdyson/*
12212115Sdyson * Vnode op for reading directories.
12312115Sdyson *
12412115Sdyson * The routine below assumes that the on-disk format of a directory
12512115Sdyson * is the same as that defined by <sys/dirent.h>. If the on-disk
12612115Sdyson * format changes, then it will be necessary to do a conversion
12712115Sdyson * from the on-disk format that read returns to the format defined
12812115Sdyson * by <sys/dirent.h>.
12912115Sdyson */
13012115Sdyson/*
13112115Sdyson * this is exactly what we do here - the problem is that the conversion
13212115Sdyson * will blow up some entries by four bytes, so it can't be done in place.
13312115Sdyson * This is too bad. Right now the conversion is done entry by entry, the
13412115Sdyson * converted entry is sent via uiomove.
13512115Sdyson *
13612115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send
13712115Sdyson * the whole buffer to uiomove
13812115Sdyson */
13912115Sdysonint
14012115Sdysonext2_readdir(ap)
14112115Sdyson        struct vop_readdir_args /* {
14212115Sdyson                struct vnode *a_vp;
14312115Sdyson                struct uio *a_uio;
14412115Sdyson                struct ucred *a_cred;
14512115Sdyson        } */ *ap;
14612115Sdyson{
14796752Siedowse        struct uio *uio = ap->a_uio;
14812147Sdyson        int count, error;
14912115Sdyson
15055477Sbde	struct ext2_dir_entry_2 *edp, *dp;
15124649Sdfr	int ncookies;
15212115Sdyson	struct dirent dstdp;
15312115Sdyson	struct uio auio;
15412115Sdyson	struct iovec aiov;
15512115Sdyson	caddr_t dirbuf;
15665780Sbde	int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize;
15712115Sdyson	int readcnt;
15865780Sbde	off_t startoffset = uio->uio_offset;
15912115Sdyson
16065780Sbde	count = uio->uio_resid;
16165780Sbde	/*
16265780Sbde	 * Avoid complications for partial directory entries by adjusting
16365780Sbde	 * the i/o to end at a block boundary.  Don't give up (like ufs
16465780Sbde	 * does) if the initial adjustment gives a negative count, since
16565780Sbde	 * many callers don't supply a large enough buffer.  The correct
16665780Sbde	 * size is a little larger than DIRBLKSIZ to allow for expansion
16765780Sbde	 * of directory entries, but some callers just use 512.
16865780Sbde	 */
16965780Sbde	count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
17065780Sbde	if (count <= 0)
17165780Sbde		count += DIRBLKSIZ;
17212115Sdyson
17365780Sbde#ifdef EXT2FS_DEBUG
17465780Sbde	printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n",
17565780Sbde	    uio->uio_offset, uio->uio_resid, count);
17612115Sdyson#endif
17712115Sdyson
17812115Sdyson	auio = *uio;
17912115Sdyson	auio.uio_iov = &aiov;
18012115Sdyson	auio.uio_iovcnt = 1;
18165780Sbde	auio.uio_resid = count;
18212115Sdyson	auio.uio_segflg = UIO_SYSSPACE;
18312115Sdyson	aiov.iov_len = count;
18412115Sdyson	MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
18512115Sdyson	aiov.iov_base = dirbuf;
18612115Sdyson	error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
18712115Sdyson	if (error == 0) {
18812115Sdyson		readcnt = count - auio.uio_resid;
18955477Sbde		edp = (struct ext2_dir_entry_2 *)&dirbuf[readcnt];
19024649Sdfr		ncookies = 0;
19155477Sbde		bzero(&dstdp, offsetof(struct dirent, d_name));
19255477Sbde		for (dp = (struct ext2_dir_entry_2 *)dirbuf;
19355477Sbde		    !error && uio->uio_resid > 0 && dp < edp; ) {
19455477Sbde			/*-
19555477Sbde			 * "New" ext2fs directory entries differ in 3 ways
19655477Sbde			 * from ufs on-disk ones:
19755477Sbde			 * - the name is not necessarily NUL-terminated.
19855477Sbde			 * - the file type field always exists and always
19955477Sbde			 * follows the name length field.
20055477Sbde			 * - the file type is encoded in a different way.
20155477Sbde			 *
20255477Sbde			 * "Old" ext2fs directory entries need no special
20355477Sbde			 * conversions, since they binary compatible with
20455477Sbde			 * "new" entries having a file type of 0 (i.e.,
20555477Sbde			 * EXT2_FT_UNKNOWN).  Splitting the old name length
20655477Sbde			 * field didn't make a mess like it did in ufs,
20755477Sbde			 * because ext2fs uses a machine-dependent disk
20855477Sbde			 * layout.
20955477Sbde			 */
21055477Sbde			dstdp.d_fileno = dp->inode;
21155477Sbde			dstdp.d_type = FTTODT(dp->file_type);
21255477Sbde			dstdp.d_namlen = dp->name_len;
21355477Sbde			dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
21455477Sbde			bcopy(dp->name, dstdp.d_name, dstdp.d_namlen);
21555477Sbde			bzero(dstdp.d_name + dstdp.d_namlen,
21655477Sbde			    dstdp.d_reclen - offsetof(struct dirent, d_name) -
21755477Sbde			    dstdp.d_namlen);
21855477Sbde
21912115Sdyson			if (dp->rec_len > 0) {
22012115Sdyson				if(dstdp.d_reclen <= uio->uio_resid) {
22112115Sdyson					/* advance dp */
22255477Sbde					dp = (struct ext2_dir_entry_2 *)
22312115Sdyson					    ((char *)dp + dp->rec_len);
22412115Sdyson					error =
22512159Sbde					  uiomove((caddr_t)&dstdp,
22612159Sbde						  dstdp.d_reclen, uio);
22724649Sdfr					if (!error)
22824649Sdfr						ncookies++;
22912115Sdyson				} else
23012115Sdyson					break;
23112115Sdyson			} else {
23212115Sdyson				error = EIO;
23312115Sdyson				break;
23412115Sdyson			}
23512115Sdyson		}
23612115Sdyson		/* we need to correct uio_offset */
23712115Sdyson		uio->uio_offset = startoffset + (caddr_t)dp - dirbuf;
23824649Sdfr
23924649Sdfr		if (!error && ap->a_ncookies != NULL) {
24065780Sbde			u_long *cookiep, *cookies, *ecookies;
24124649Sdfr			off_t off;
24224649Sdfr
24324649Sdfr			if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
24424649Sdfr				panic("ext2fs_readdir: unexpected uio from NFS server");
24524649Sdfr			MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
24624649Sdfr			       M_WAITOK);
24724649Sdfr			off = startoffset;
24865780Sbde			for (dp = (struct ext2_dir_entry_2 *)dirbuf,
24965780Sbde			     cookiep = cookies, ecookies = cookies + ncookies;
25065780Sbde			     cookiep < ecookies;
25155477Sbde			     dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) {
25224649Sdfr				off += dp->rec_len;
25324649Sdfr				*cookiep++ = (u_long) off;
25424649Sdfr			}
25524649Sdfr			*ap->a_ncookies = ncookies;
25624649Sdfr			*ap->a_cookies = cookies;
25724649Sdfr		}
25812115Sdyson	}
25912115Sdyson	FREE(dirbuf, M_TEMP);
26024649Sdfr	if (ap->a_eofflag)
26124649Sdfr		*ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
26212115Sdyson        return (error);
26312115Sdyson}
26412115Sdyson
26512115Sdyson/*
26612115Sdyson * Convert a component of a pathname into a pointer to a locked inode.
26712115Sdyson * This is a very central and rather complicated routine.
26812115Sdyson * If the file system is not maintained in a strict tree hierarchy,
26912115Sdyson * this can result in a deadlock situation (see comments in code below).
27012115Sdyson *
27112115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
27212115Sdyson * on whether the name is to be looked up, created, renamed, or deleted.
27312115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in
27412115Sdyson * creating, renaming, or deleting a directory entry may be calculated.
27512115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname
27612115Sdyson * exists, lookup returns both the target and its parent directory locked.
27712115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may
27812115Sdyson * not be ".".  When deleting and LOCKPARENT is specified, the target may
27912115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput
28012115Sdyson * instead of two vputs.
28112115Sdyson *
28212115Sdyson * Overall outline of ufs_lookup:
28312115Sdyson *
28412115Sdyson *	search for name in directory, to found or notfound
28512115Sdyson * notfound:
28612115Sdyson *	if creating, return locked directory, leaving info on available slots
28712115Sdyson *	else return error
28812115Sdyson * found:
28912115Sdyson *	if at end of path and deleting, return information to allow delete
29012115Sdyson *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
29112115Sdyson *	  inode and return info to allow rewrite
29212115Sdyson *	if not at end, add name to cache; if at end and neither creating
29312115Sdyson *	  nor deleting, add name to cache
29412115Sdyson */
29512115Sdysonint
29612115Sdysonext2_lookup(ap)
29728787Sphk	struct vop_cachedlookup_args /* {
29812115Sdyson		struct vnode *a_dvp;
29912115Sdyson		struct vnode **a_vpp;
30012115Sdyson		struct componentname *a_cnp;
30112115Sdyson	} */ *ap;
30212115Sdyson{
30396752Siedowse	struct vnode *vdp;		/* vnode for directory being searched */
30496752Siedowse	struct inode *dp;		/* inode for directory being searched */
30512115Sdyson	struct buf *bp;			/* a buffer of directory entries */
30696752Siedowse	struct ext2_dir_entry_2 *ep;	/* the current directory entry */
30712115Sdyson	int entryoffsetinblock;		/* offset of ep in bp's buffer */
30812115Sdyson	enum {NONE, COMPACT, FOUND} slotstatus;
30912115Sdyson	doff_t slotoffset;		/* offset of area with free space */
31012115Sdyson	int slotsize;			/* size of area at slotoffset */
31112115Sdyson	int slotfreespace;		/* amount of space free in slot */
31212115Sdyson	int slotneeded;			/* size of the entry we're seeking */
31312115Sdyson	int numdirpasses;		/* strategy for directory search */
31412115Sdyson	doff_t endsearch;		/* offset to end directory search */
31512115Sdyson	doff_t prevoff;			/* prev entry dp->i_offset */
31612115Sdyson	struct vnode *pdp;		/* saved dp during symlink work */
31712115Sdyson	struct vnode *tdp;		/* returned by VFS_VGET */
31812115Sdyson	doff_t enduseful;		/* pointer past last used dir slot */
31912115Sdyson	u_long bmask;			/* block offset mask */
32012115Sdyson	int lockparent;			/* 1 => lockparent flag is set */
32112115Sdyson	int wantparent;			/* 1 => wantparent or lockparent flag */
32212115Sdyson	int namlen, error;
32312115Sdyson	struct vnode **vpp = ap->a_vpp;
32412115Sdyson	struct componentname *cnp = ap->a_cnp;
32512115Sdyson	struct ucred *cred = cnp->cn_cred;
32612115Sdyson	int flags = cnp->cn_flags;
32712115Sdyson	int nameiop = cnp->cn_nameiop;
32883366Sjulian	struct thread *td = cnp->cn_thread;
32912115Sdyson
33012115Sdyson	int	DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize;
33112115Sdyson
33212115Sdyson	bp = NULL;
33312115Sdyson	slotoffset = -1;
33412115Sdyson	*vpp = NULL;
33512115Sdyson	vdp = ap->a_dvp;
33612115Sdyson	dp = VTOI(vdp);
33712115Sdyson	lockparent = flags & LOCKPARENT;
33812115Sdyson	wantparent = flags & (LOCKPARENT|WANTPARENT);
33912115Sdyson
34012115Sdyson	/*
34112115Sdyson	 * We now have a segment name to search for, and a directory to search.
34212115Sdyson	 */
34312115Sdyson
34412115Sdyson	/*
34512115Sdyson	 * Suppress search for slots unless creating
34612115Sdyson	 * file and at end of pathname, in which case
34712115Sdyson	 * we watch for a place to put the new file in
34812115Sdyson	 * case it doesn't already exist.
34912115Sdyson	 */
35012115Sdyson	slotstatus = FOUND;
35112115Sdyson	slotfreespace = slotsize = slotneeded = 0;
35212115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
35312115Sdyson	    (flags & ISLASTCN)) {
35412115Sdyson		slotstatus = NONE;
35512115Sdyson		slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen);
35612115Sdyson		/* was
35712115Sdyson		slotneeded = (sizeof(struct direct) - MAXNAMLEN +
35812115Sdyson			cnp->cn_namelen + 3) &~ 3; */
35912115Sdyson	}
36012115Sdyson
36112115Sdyson	/*
36212115Sdyson	 * If there is cached information on a previous search of
36312115Sdyson	 * this directory, pick up where we last left off.
36412115Sdyson	 * We cache only lookups as these are the most common
36512115Sdyson	 * and have the greatest payoff. Caching CREATE has little
36612115Sdyson	 * benefit as it usually must search the entire directory
36712115Sdyson	 * to determine that the entry does not exist. Caching the
36812115Sdyson	 * location of the last DELETE or RENAME has not reduced
36912115Sdyson	 * profiling time and hence has been removed in the interest
37012115Sdyson	 * of simplicity.
37112115Sdyson	 */
37296749Siedowse	bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
37312115Sdyson	if (nameiop != LOOKUP || dp->i_diroff == 0 ||
37412115Sdyson	    dp->i_diroff > dp->i_size) {
37512115Sdyson		entryoffsetinblock = 0;
37612115Sdyson		dp->i_offset = 0;
37712115Sdyson		numdirpasses = 1;
37812115Sdyson	} else {
37912115Sdyson		dp->i_offset = dp->i_diroff;
38012115Sdyson		if ((entryoffsetinblock = dp->i_offset & bmask) &&
38196749Siedowse		    (error = ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL,
38296749Siedowse		    &bp)))
38312115Sdyson			return (error);
38412115Sdyson		numdirpasses = 2;
38512115Sdyson		nchstats.ncs_2passes++;
38612115Sdyson	}
38712115Sdyson	prevoff = dp->i_offset;
38812115Sdyson	endsearch = roundup(dp->i_size, DIRBLKSIZ);
38912115Sdyson	enduseful = 0;
39012115Sdyson
39112115Sdysonsearchloop:
39212115Sdyson	while (dp->i_offset < endsearch) {
39312115Sdyson		/*
39412115Sdyson		 * If necessary, get the next directory block.
39512115Sdyson		 */
39612115Sdyson		if ((dp->i_offset & bmask) == 0) {
39712115Sdyson			if (bp != NULL)
39812115Sdyson				brelse(bp);
39943301Sdillon			if ((error =
40096749Siedowse			    ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL,
40196749Siedowse			    &bp)) != 0)
40212115Sdyson				return (error);
40312115Sdyson			entryoffsetinblock = 0;
40412115Sdyson		}
40512115Sdyson		/*
40612115Sdyson		 * If still looking for a slot, and at a DIRBLKSIZE
40712115Sdyson		 * boundary, have to start looking for free space again.
40812115Sdyson		 */
40912115Sdyson		if (slotstatus == NONE &&
41012115Sdyson		    (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
41112115Sdyson			slotoffset = -1;
41212115Sdyson			slotfreespace = 0;
41312115Sdyson		}
41412115Sdyson		/*
41512115Sdyson		 * Get pointer to next entry.
41612115Sdyson		 * Full validation checks are slow, so we only check
41712115Sdyson		 * enough to insure forward progress through the
41896753Siedowse		 * directory. Complete checks can be run by setting
41996753Siedowse		 * "vfs.e2fs.dirchk" to be true.
42012115Sdyson		 */
42155477Sbde		ep = (struct ext2_dir_entry_2 *)
42212115Sdyson			((char *)bp->b_data + entryoffsetinblock);
42312115Sdyson		if (ep->rec_len == 0 ||
42412147Sdyson		    (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) {
42512115Sdyson			int i;
42696749Siedowse			ext2_dirbad(dp, dp->i_offset, "mangled entry");
42712115Sdyson			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
42812115Sdyson			dp->i_offset += i;
42912115Sdyson			entryoffsetinblock += i;
43012115Sdyson			continue;
43112115Sdyson		}
43212115Sdyson
43312115Sdyson		/*
43412115Sdyson		 * If an appropriate sized slot has not yet been found,
43512115Sdyson		 * check to see if one is available. Also accumulate space
43612115Sdyson		 * in the current block so that we can determine if
43712115Sdyson		 * compaction is viable.
43812115Sdyson		 */
43912115Sdyson		if (slotstatus != FOUND) {
44012115Sdyson			int size = ep->rec_len;
44112115Sdyson
44212115Sdyson			if (ep->inode != 0)
44312115Sdyson				size -= EXT2_DIR_REC_LEN(ep->name_len);
44412115Sdyson			if (size > 0) {
44512115Sdyson				if (size >= slotneeded) {
44612115Sdyson					slotstatus = FOUND;
44712115Sdyson					slotoffset = dp->i_offset;
44812115Sdyson					slotsize = ep->rec_len;
44912115Sdyson				} else if (slotstatus == NONE) {
45012115Sdyson					slotfreespace += size;
45112115Sdyson					if (slotoffset == -1)
45212115Sdyson						slotoffset = dp->i_offset;
45312115Sdyson					if (slotfreespace >= slotneeded) {
45412115Sdyson						slotstatus = COMPACT;
45512115Sdyson						slotsize = dp->i_offset +
45612115Sdyson						      ep->rec_len - slotoffset;
45712115Sdyson					}
45812115Sdyson				}
45912115Sdyson			}
46012115Sdyson		}
46112115Sdyson
46212115Sdyson		/*
46312115Sdyson		 * Check for a name match.
46412115Sdyson		 */
46512115Sdyson		if (ep->inode) {
46612115Sdyson			namlen = ep->name_len;
46712115Sdyson			if (namlen == cnp->cn_namelen &&
46812115Sdyson			    !bcmp(cnp->cn_nameptr, ep->name,
46912115Sdyson				(unsigned)namlen)) {
47012115Sdyson				/*
47112115Sdyson				 * Save directory entry's inode number and
47212115Sdyson				 * reclen in ndp->ni_ufs area, and release
47312115Sdyson				 * directory buffer.
47412115Sdyson				 */
47512115Sdyson				dp->i_ino = ep->inode;
47612115Sdyson				dp->i_reclen = ep->rec_len;
47712115Sdyson				brelse(bp);
47812115Sdyson				goto found;
47912115Sdyson			}
48012115Sdyson		}
48112115Sdyson		prevoff = dp->i_offset;
48212115Sdyson		dp->i_offset += ep->rec_len;
48312115Sdyson		entryoffsetinblock += ep->rec_len;
48412115Sdyson		if (ep->inode)
48512115Sdyson			enduseful = dp->i_offset;
48612115Sdyson	}
48712115Sdyson/* notfound: */
48812115Sdyson	/*
48912115Sdyson	 * If we started in the middle of the directory and failed
49012115Sdyson	 * to find our target, we must check the beginning as well.
49112115Sdyson	 */
49212115Sdyson	if (numdirpasses == 2) {
49312115Sdyson		numdirpasses--;
49412115Sdyson		dp->i_offset = 0;
49512115Sdyson		endsearch = dp->i_diroff;
49612115Sdyson		goto searchloop;
49712115Sdyson	}
49812115Sdyson	if (bp != NULL)
49912115Sdyson		brelse(bp);
50012115Sdyson	/*
50112115Sdyson	 * If creating, and at end of pathname and current
50212115Sdyson	 * directory has not been removed, then can consider
50312115Sdyson	 * allowing file to be created.
50412115Sdyson	 */
50512115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
50612115Sdyson	    (flags & ISLASTCN) && dp->i_nlink != 0) {
50712115Sdyson		/*
50812115Sdyson		 * Access for write is interpreted as allowing
50912115Sdyson		 * creation of files in the directory.
51012115Sdyson		 */
51183366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
51212115Sdyson			return (error);
51312115Sdyson		/*
51412115Sdyson		 * Return an indication of where the new directory
51512115Sdyson		 * entry should be put.  If we didn't find a slot,
51612115Sdyson		 * then set dp->i_count to 0 indicating
51712115Sdyson		 * that the new slot belongs at the end of the
51812115Sdyson		 * directory. If we found a slot, then the new entry
51912115Sdyson		 * can be put in the range from dp->i_offset to
52012115Sdyson		 * dp->i_offset + dp->i_count.
52112115Sdyson		 */
52212115Sdyson		if (slotstatus == NONE) {
52312115Sdyson			dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);
52412115Sdyson			dp->i_count = 0;
52512115Sdyson			enduseful = dp->i_offset;
52612115Sdyson		} else {
52712115Sdyson			dp->i_offset = slotoffset;
52812115Sdyson			dp->i_count = slotsize;
52912115Sdyson			if (enduseful < slotoffset + slotsize)
53012115Sdyson				enduseful = slotoffset + slotsize;
53112115Sdyson		}
53212115Sdyson		dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
53312115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
53412115Sdyson		/*
53512115Sdyson		 * We return with the directory locked, so that
53612115Sdyson		 * the parameters we set up above will still be
53712115Sdyson		 * valid if we actually decide to do a direnter().
53812115Sdyson		 * We return ni_vp == NULL to indicate that the entry
53912115Sdyson		 * does not currently exist; we leave a pointer to
54012115Sdyson		 * the (locked) directory inode in ndp->ni_dvp.
54112115Sdyson		 * The pathname buffer is saved so that the name
54212115Sdyson		 * can be obtained later.
54312115Sdyson		 *
54412115Sdyson		 * NB - if the directory is unlocked, then this
54512115Sdyson		 * information cannot be used.
54612115Sdyson		 */
54712115Sdyson		cnp->cn_flags |= SAVENAME;
54812115Sdyson		if (!lockparent)
54983366Sjulian			VOP_UNLOCK(vdp, 0, td);
55012115Sdyson		return (EJUSTRETURN);
55112115Sdyson	}
55212115Sdyson	/*
55312115Sdyson	 * Insert name into cache (as non-existent) if appropriate.
55412115Sdyson	 */
55512115Sdyson	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
55612115Sdyson		cache_enter(vdp, *vpp, cnp);
55712115Sdyson	return (ENOENT);
55812115Sdyson
55912115Sdysonfound:
56012115Sdyson	if (numdirpasses == 2)
56112115Sdyson		nchstats.ncs_pass2++;
56212115Sdyson	/*
56312115Sdyson	 * Check that directory length properly reflects presence
56412115Sdyson	 * of this entry.
56512115Sdyson	 */
56612115Sdyson	if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len)
56712115Sdyson		> dp->i_size) {
56896749Siedowse		ext2_dirbad(dp, dp->i_offset, "i_size too small");
56912115Sdyson		dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len);
57012115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
57112115Sdyson	}
57212115Sdyson
57312115Sdyson	/*
57412115Sdyson	 * Found component in pathname.
57512115Sdyson	 * If the final component of path name, save information
57612115Sdyson	 * in the cache as to where the entry was found.
57712115Sdyson	 */
57812115Sdyson	if ((flags & ISLASTCN) && nameiop == LOOKUP)
57912115Sdyson		dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
58012115Sdyson
58112115Sdyson	/*
58212115Sdyson	 * If deleting, and at end of pathname, return
58312115Sdyson	 * parameters which can be used to remove file.
58412115Sdyson	 * If the wantparent flag isn't set, we return only
58512115Sdyson	 * the directory (in ndp->ni_dvp), otherwise we go
58612115Sdyson	 * on and lock the inode, being careful with ".".
58712115Sdyson	 */
58812115Sdyson	if (nameiop == DELETE && (flags & ISLASTCN)) {
58912115Sdyson		/*
59012115Sdyson		 * Write access to directory required to delete files.
59112115Sdyson		 */
59283366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
59312115Sdyson			return (error);
59412115Sdyson		/*
59512115Sdyson		 * Return pointer to current entry in dp->i_offset,
59612115Sdyson		 * and distance past previous entry (if there
59712115Sdyson		 * is a previous entry in this block) in dp->i_count.
59812115Sdyson		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
59912115Sdyson		 */
60012115Sdyson		if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
60112115Sdyson			dp->i_count = 0;
60212115Sdyson		else
60312115Sdyson			dp->i_count = dp->i_offset - prevoff;
60412115Sdyson		if (dp->i_number == dp->i_ino) {
60512115Sdyson			VREF(vdp);
60612115Sdyson			*vpp = vdp;
60712115Sdyson			return (0);
60812115Sdyson		}
60992462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE,
61092462Smckusick		    &tdp)) != 0)
61112115Sdyson			return (error);
61212115Sdyson		/*
61312115Sdyson		 * If directory is "sticky", then user must own
61412115Sdyson		 * the directory, or the file in it, else she
61512115Sdyson		 * may not delete it (unless she's root). This
61612115Sdyson		 * implements append-only directories.
61712115Sdyson		 */
61812115Sdyson		if ((dp->i_mode & ISVTX) &&
61912115Sdyson		    cred->cr_uid != 0 &&
62012115Sdyson		    cred->cr_uid != dp->i_uid &&
62112115Sdyson		    VTOI(tdp)->i_uid != cred->cr_uid) {
62212115Sdyson			vput(tdp);
62312115Sdyson			return (EPERM);
62412115Sdyson		}
62512115Sdyson		*vpp = tdp;
62612115Sdyson		if (!lockparent)
62783366Sjulian			VOP_UNLOCK(vdp, 0, td);
62812115Sdyson		return (0);
62912115Sdyson	}
63012115Sdyson
63112115Sdyson	/*
63212115Sdyson	 * If rewriting (RENAME), return the inode and the
63312115Sdyson	 * information required to rewrite the present directory
63412115Sdyson	 * Must get inode of directory entry to verify it's a
63512115Sdyson	 * regular file, or empty directory.
63612115Sdyson	 */
63712115Sdyson	if (nameiop == RENAME && wantparent &&
63812115Sdyson	    (flags & ISLASTCN)) {
63983366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
64012115Sdyson			return (error);
64112115Sdyson		/*
64212115Sdyson		 * Careful about locking second inode.
64312115Sdyson		 * This can only occur if the target is ".".
64412115Sdyson		 */
64512115Sdyson		if (dp->i_number == dp->i_ino)
64612115Sdyson			return (EISDIR);
64792462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE,
64892462Smckusick		    &tdp)) != 0)
64912115Sdyson			return (error);
65012115Sdyson		*vpp = tdp;
65112115Sdyson		cnp->cn_flags |= SAVENAME;
65212115Sdyson		if (!lockparent)
65383366Sjulian			VOP_UNLOCK(vdp, 0, td);
65412115Sdyson		return (0);
65512115Sdyson	}
65612115Sdyson
65712115Sdyson	/*
65812115Sdyson	 * Step through the translation in the name.  We do not `vput' the
65912115Sdyson	 * directory because we may need it again if a symbolic link
66012115Sdyson	 * is relative to the current directory.  Instead we save it
66112115Sdyson	 * unlocked as "pdp".  We must get the target inode before unlocking
66212115Sdyson	 * the directory to insure that the inode will not be removed
66312115Sdyson	 * before we get it.  We prevent deadlock by always fetching
66412115Sdyson	 * inodes from the root, moving down the directory tree. Thus
66512115Sdyson	 * when following backward pointers ".." we must unlock the
66612115Sdyson	 * parent directory before getting the requested directory.
66712115Sdyson	 * There is a potential race condition here if both the current
66812115Sdyson	 * and parent directories are removed before the VFS_VGET for the
66912115Sdyson	 * inode associated with ".." returns.  We hope that this occurs
67012115Sdyson	 * infrequently since we cannot avoid this race condition without
67112115Sdyson	 * implementing a sophisticated deadlock detection algorithm.
67212115Sdyson	 * Note also that this simple deadlock detection scheme will not
67312115Sdyson	 * work if the file system has any hard links other than ".."
67412115Sdyson	 * that point backwards in the directory structure.
67512115Sdyson	 */
67612115Sdyson	pdp = vdp;
67712115Sdyson	if (flags & ISDOTDOT) {
67883366Sjulian		VOP_UNLOCK(pdp, 0, td);	/* race to get the inode */
67992462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE,
68092462Smckusick		    &tdp)) != 0) {
68183366Sjulian			vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
68212115Sdyson			return (error);
68312115Sdyson		}
68412115Sdyson		if (lockparent && (flags & ISLASTCN) &&
68583366Sjulian		    (error = vn_lock(pdp, LK_EXCLUSIVE, td))) {
68612115Sdyson			vput(tdp);
68712115Sdyson			return (error);
68812115Sdyson		}
68912115Sdyson		*vpp = tdp;
69012115Sdyson	} else if (dp->i_number == dp->i_ino) {
69112115Sdyson		VREF(vdp);	/* we want ourself, ie "." */
69212115Sdyson		*vpp = vdp;
69312115Sdyson	} else {
69492462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE,
69592462Smckusick		    &tdp)) != 0)
69612115Sdyson			return (error);
69712115Sdyson		if (!lockparent || !(flags & ISLASTCN))
69883366Sjulian			VOP_UNLOCK(pdp, 0, td);
69912115Sdyson		*vpp = tdp;
70012115Sdyson	}
70112115Sdyson
70212115Sdyson	/*
70312115Sdyson	 * Insert name into cache if appropriate.
70412115Sdyson	 */
70512115Sdyson	if (cnp->cn_flags & MAKEENTRY)
70612115Sdyson		cache_enter(vdp, *vpp, cnp);
70712115Sdyson	return (0);
70812115Sdyson}
70912115Sdyson
71096749Siedowsevoid
71196749Siedowseext2_dirbad(ip, offset, how)
71296749Siedowse	struct inode *ip;
71396749Siedowse	doff_t offset;
71496749Siedowse	char *how;
71596749Siedowse{
71696749Siedowse	struct mount *mp;
71796749Siedowse
71896749Siedowse	mp = ITOV(ip)->v_mount;
71996749Siedowse	(void)printf("%s: bad dir ino %lu at offset %ld: %s\n",
72096749Siedowse	    mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how);
72196749Siedowse	if ((mp->mnt_flag & MNT_RDONLY) == 0)
72296749Siedowse		panic("ext2_dirbad: bad dir");
72396749Siedowse}
72496749Siedowse
72512115Sdyson/*
72612115Sdyson * Do consistency checking on a directory entry:
72712115Sdyson *	record length must be multiple of 4
72812115Sdyson *	entry must fit in rest of its DIRBLKSIZ block
72912115Sdyson *	record must be large enough to contain entry
73012115Sdyson *	name is not longer than MAXNAMLEN
73112115Sdyson *	name must be as long as advertised, and null terminated
73212115Sdyson */
73312115Sdyson/*
73412115Sdyson *	changed so that it confirms to ext2_check_dir_entry
73512115Sdyson */
73612159Sbdestatic int
73712115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock)
73812115Sdyson	struct vnode *dp;
73996752Siedowse	struct ext2_dir_entry_2 *de;
74012115Sdyson	int entryoffsetinblock;
74112115Sdyson{
74212115Sdyson	int	DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize;
74312115Sdyson
74412115Sdyson        char * error_msg = NULL;
74512115Sdyson
74612115Sdyson        if (de->rec_len < EXT2_DIR_REC_LEN(1))
74712115Sdyson                error_msg = "rec_len is smaller than minimal";
74812115Sdyson        else if (de->rec_len % 4 != 0)
74912115Sdyson                error_msg = "rec_len % 4 != 0";
75012115Sdyson        else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
75112115Sdyson                error_msg = "reclen is too small for name_len";
75212115Sdyson        else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ)
75312115Sdyson                error_msg = "directory entry across blocks";
75412115Sdyson        /* else LATER
75512115Sdyson	     if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
75612115Sdyson                error_msg = "inode out of bounds";
75712115Sdyson	*/
75812115Sdyson
75924492Sbde        if (error_msg != NULL) {
76024492Sbde                printf("bad directory entry: %s\n", error_msg);
76137555Sbde                printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
76224492Sbde			entryoffsetinblock, (unsigned long)de->inode,
76324492Sbde			de->rec_len, de->name_len);
76424492Sbde        }
76512115Sdyson        return error_msg == NULL ? 0 : 1;
76612115Sdyson}
76712115Sdyson
76812115Sdyson/*
76912115Sdyson * Write a directory entry after a call to namei, using the parameters
77012115Sdyson * that it left in nameidata.  The argument ip is the inode which the new
77112115Sdyson * directory entry will refer to.  Dvp is a pointer to the directory to
77212115Sdyson * be written, which was left locked by namei. Remaining parameters
77312115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new
77412115Sdyson * entry is to be obtained.
77512115Sdyson */
77612115Sdysonint
77712115Sdysonext2_direnter(ip, dvp, cnp)
77812115Sdyson	struct inode *ip;
77912115Sdyson	struct vnode *dvp;
78096752Siedowse	struct componentname *cnp;
78112115Sdyson{
78296752Siedowse	struct ext2_dir_entry_2 *ep, *nep;
78396752Siedowse	struct inode *dp;
78412115Sdyson	struct buf *bp;
78555477Sbde	struct ext2_dir_entry_2 newdir;
78612115Sdyson	struct iovec aiov;
78712115Sdyson	struct uio auio;
78812115Sdyson	u_int dsize;
78912115Sdyson	int error, loc, newentrysize, spacefree;
79012115Sdyson	char *dirbuf;
79112115Sdyson	int     DIRBLKSIZ = ip->i_e2fs->s_blocksize;
79212115Sdyson
79312115Sdyson
79412115Sdyson#if DIAGNOSTIC
79512115Sdyson	if ((cnp->cn_flags & SAVENAME) == 0)
79612115Sdyson		panic("direnter: missing name");
79712115Sdyson#endif
79812115Sdyson	dp = VTOI(dvp);
79912115Sdyson	newdir.inode = ip->i_number;
80012115Sdyson	newdir.name_len = cnp->cn_namelen;
80155477Sbde	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
80255477Sbde	    EXT2_FEATURE_INCOMPAT_FILETYPE))
80355477Sbde		newdir.file_type = DTTOFT(IFTODT(ip->i_mode));
80455477Sbde	else
80555477Sbde		newdir.file_type = EXT2_FT_UNKNOWN;
80612115Sdyson	bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1);
80712115Sdyson	newentrysize = EXT2_DIR_REC_LEN(newdir.name_len);
80812115Sdyson	if (dp->i_count == 0) {
80912115Sdyson		/*
81012115Sdyson		 * If dp->i_count is 0, then namei could find no
81112115Sdyson		 * space in the directory. Here, dp->i_offset will
81212115Sdyson		 * be on a directory block boundary and we will write the
81312115Sdyson		 * new entry into a fresh block.
81412115Sdyson		 */
81512115Sdyson		if (dp->i_offset & (DIRBLKSIZ - 1))
81612115Sdyson			panic("ext2_direnter: newblk");
81712115Sdyson		auio.uio_offset = dp->i_offset;
81812115Sdyson		newdir.rec_len = DIRBLKSIZ;
81912115Sdyson		auio.uio_resid = newentrysize;
82012115Sdyson		aiov.iov_len = newentrysize;
82112115Sdyson		aiov.iov_base = (caddr_t)&newdir;
82212115Sdyson		auio.uio_iov = &aiov;
82312115Sdyson		auio.uio_iovcnt = 1;
82412115Sdyson		auio.uio_rw = UIO_WRITE;
82512115Sdyson		auio.uio_segflg = UIO_SYSSPACE;
82683366Sjulian		auio.uio_td = (struct thread *)0;
82712115Sdyson		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
82812115Sdyson		if (DIRBLKSIZ >
82996749Siedowse		    VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
83012115Sdyson			/* XXX should grow with balloc() */
83112115Sdyson			panic("ext2_direnter: frag size");
83212115Sdyson		else if (!error) {
83312115Sdyson			dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
83412115Sdyson			dp->i_flag |= IN_CHANGE;
83512115Sdyson		}
83612115Sdyson		return (error);
83712115Sdyson	}
83812115Sdyson
83912115Sdyson	/*
84012115Sdyson	 * If dp->i_count is non-zero, then namei found space
84112115Sdyson	 * for the new entry in the range dp->i_offset to
84212115Sdyson	 * dp->i_offset + dp->i_count in the directory.
84312115Sdyson	 * To use this space, we may have to compact the entries located
84412115Sdyson	 * there, by copying them together towards the beginning of the
84512115Sdyson	 * block, leaving the free space in one usable chunk at the end.
84612115Sdyson	 */
84712115Sdyson
84812115Sdyson	/*
84912115Sdyson	 * Increase size of directory if entry eats into new space.
85012115Sdyson	 * This should never push the size past a new multiple of
85112115Sdyson	 * DIRBLKSIZE.
85212115Sdyson	 *
85312115Sdyson	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
85412115Sdyson	 */
85512115Sdyson	if (dp->i_offset + dp->i_count > dp->i_size)
85612115Sdyson		dp->i_size = dp->i_offset + dp->i_count;
85712115Sdyson	/*
85812115Sdyson	 * Get the block containing the space for the new directory entry.
85912115Sdyson	 */
86096749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf,
86196749Siedowse	    &bp)) != 0)
86212115Sdyson		return (error);
86312115Sdyson	/*
86412115Sdyson	 * Find space for the new entry. In the simple case, the entry at
86512115Sdyson	 * offset base will have the space. If it does not, then namei
86612115Sdyson	 * arranged that compacting the region dp->i_offset to
86712115Sdyson	 * dp->i_offset + dp->i_count would yield the
86812115Sdyson	 * space.
86912115Sdyson	 */
87055477Sbde	ep = (struct ext2_dir_entry_2 *)dirbuf;
87112115Sdyson	dsize = EXT2_DIR_REC_LEN(ep->name_len);
87212115Sdyson	spacefree = ep->rec_len - dsize;
87312115Sdyson	for (loc = ep->rec_len; loc < dp->i_count; ) {
87455477Sbde		nep = (struct ext2_dir_entry_2 *)(dirbuf + loc);
87512115Sdyson		if (ep->inode) {
87612115Sdyson			/* trim the existing slot */
87712115Sdyson			ep->rec_len = dsize;
87855477Sbde			ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
87912115Sdyson		} else {
88012115Sdyson			/* overwrite; nothing there; header is ours */
88112115Sdyson			spacefree += dsize;
88212115Sdyson		}
88319541Sbde		dsize = EXT2_DIR_REC_LEN(nep->name_len);
88412115Sdyson		spacefree += nep->rec_len - dsize;
88512115Sdyson		loc += nep->rec_len;
88612115Sdyson		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
88712115Sdyson	}
88812115Sdyson	/*
88912115Sdyson	 * Update the pointer fields in the previous entry (if any),
89012115Sdyson	 * copy in the new entry, and write out the block.
89112115Sdyson	 */
89212115Sdyson	if (ep->inode == 0) {
89312115Sdyson		if (spacefree + dsize < newentrysize)
89412115Sdyson			panic("ext2_direnter: compact1");
89512115Sdyson		newdir.rec_len = spacefree + dsize;
89612115Sdyson	} else {
89712115Sdyson		if (spacefree < newentrysize)
89812115Sdyson			panic("ext2_direnter: compact2");
89912115Sdyson		newdir.rec_len = spacefree;
90012115Sdyson		ep->rec_len = dsize;
90155477Sbde		ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
90212115Sdyson	}
90312115Sdyson	bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
90458349Sphk	error = BUF_WRITE(bp);
90512115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
90612115Sdyson	if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
90796749Siedowse		error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
90883366Sjulian		    cnp->cn_cred, cnp->cn_thread);
90912115Sdyson	return (error);
91012115Sdyson}
91112115Sdyson
91212115Sdyson/*
91312115Sdyson * Remove a directory entry after a call to namei, using
91412115Sdyson * the parameters which it left in nameidata. The entry
91512115Sdyson * dp->i_offset contains the offset into the directory of the
91612115Sdyson * entry to be eliminated.  The dp->i_count field contains the
91712115Sdyson * size of the previous record in the directory.  If this
91812115Sdyson * is 0, the first entry is being deleted, so we need only
91912115Sdyson * zero the inode number to mark the entry as free.  If the
92012115Sdyson * entry is not the first in the directory, we must reclaim
92112115Sdyson * the space of the now empty record by adding the record size
92212115Sdyson * to the size of the previous entry.
92312115Sdyson */
92412115Sdysonint
92512115Sdysonext2_dirremove(dvp, cnp)
92612115Sdyson	struct vnode *dvp;
92712115Sdyson	struct componentname *cnp;
92812115Sdyson{
92996752Siedowse	struct inode *dp;
93055477Sbde	struct ext2_dir_entry_2 *ep;
93112115Sdyson	struct buf *bp;
93212115Sdyson	int error;
93312115Sdyson
93412115Sdyson	dp = VTOI(dvp);
93512115Sdyson	if (dp->i_count == 0) {
93612115Sdyson		/*
93712115Sdyson		 * First entry in block: set d_ino to zero.
93812115Sdyson		 */
93943301Sdillon		if ((error =
94096749Siedowse		    ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep,
94196749Siedowse		    &bp)) != 0)
94212115Sdyson			return (error);
94312115Sdyson		ep->inode = 0;
94458349Sphk		error = BUF_WRITE(bp);
94512115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
94612115Sdyson		return (error);
94712115Sdyson	}
94812115Sdyson	/*
94912115Sdyson	 * Collapse new free space into previous entry.
95012115Sdyson	 */
95196749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
95243301Sdillon	    (char **)&ep, &bp)) != 0)
95312115Sdyson		return (error);
95412115Sdyson	ep->rec_len += dp->i_reclen;
95558349Sphk	error = BUF_WRITE(bp);
95612115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
95712115Sdyson	return (error);
95812115Sdyson}
95912115Sdyson
96012115Sdyson/*
96112115Sdyson * Rewrite an existing directory entry to point at the inode
96212115Sdyson * supplied.  The parameters describing the directory entry are
96312115Sdyson * set up by a call to namei.
96412115Sdyson */
96512115Sdysonint
96612115Sdysonext2_dirrewrite(dp, ip, cnp)
96712115Sdyson	struct inode *dp, *ip;
96812115Sdyson	struct componentname *cnp;
96912115Sdyson{
97012115Sdyson	struct buf *bp;
97155477Sbde	struct ext2_dir_entry_2 *ep;
97212115Sdyson	struct vnode *vdp = ITOV(dp);
97312115Sdyson	int error;
97412115Sdyson
97596749Siedowse	if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep,
97696749Siedowse	    &bp)) != 0)
97712115Sdyson		return (error);
97812115Sdyson	ep->inode = ip->i_number;
97955477Sbde	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
98055477Sbde	    EXT2_FEATURE_INCOMPAT_FILETYPE))
98155477Sbde		ep->file_type = DTTOFT(IFTODT(ip->i_mode));
98255477Sbde	else
98355477Sbde		ep->file_type = EXT2_FT_UNKNOWN;
98458349Sphk	error = BUF_WRITE(bp);
98512115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
98612115Sdyson	return (error);
98712115Sdyson}
98812115Sdyson
98912115Sdyson/*
99012115Sdyson * Check if a directory is empty or not.
99112115Sdyson * Inode supplied must be locked.
99212115Sdyson *
99312115Sdyson * Using a struct dirtemplate here is not precisely
99412115Sdyson * what we want, but better than using a struct direct.
99512115Sdyson *
99612115Sdyson * NB: does not handle corrupted directories.
99712115Sdyson */
99812115Sdysonint
99912115Sdysonext2_dirempty(ip, parentino, cred)
100096752Siedowse	struct inode *ip;
100112115Sdyson	ino_t parentino;
100212115Sdyson	struct ucred *cred;
100312115Sdyson{
100496752Siedowse	off_t off;
100512115Sdyson	struct dirtemplate dbuf;
100696752Siedowse	struct ext2_dir_entry_2 *dp = (struct ext2_dir_entry_2 *)&dbuf;
100712115Sdyson	int error, count, namlen;
100812115Sdyson
100912115Sdyson#define	MINDIRSIZ (sizeof (struct dirtemplate) / 2)
101012115Sdyson
101112115Sdyson	for (off = 0; off < ip->i_size; off += dp->rec_len) {
1012101744Srwatson		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
1013101744Srwatson		    off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
1014101744Srwatson		    &count, (struct thread *)0);
101512115Sdyson		/*
101612115Sdyson		 * Since we read MINDIRSIZ, residual must
101712115Sdyson		 * be 0 unless we're at end of file.
101812115Sdyson		 */
101912115Sdyson		if (error || count != 0)
102012115Sdyson			return (0);
102112115Sdyson		/* avoid infinite loops */
102212115Sdyson		if (dp->rec_len == 0)
102312115Sdyson			return (0);
102412115Sdyson		/* skip empty entries */
102512115Sdyson		if (dp->inode == 0)
102612115Sdyson			continue;
102712115Sdyson		/* accept only "." and ".." */
102812115Sdyson		namlen = dp->name_len;
102912115Sdyson		if (namlen > 2)
103012115Sdyson			return (0);
103112115Sdyson		if (dp->name[0] != '.')
103212115Sdyson			return (0);
103312115Sdyson		/*
103412115Sdyson		 * At this point namlen must be 1 or 2.
103512115Sdyson		 * 1 implies ".", 2 implies ".." if second
103612115Sdyson		 * char is also "."
103712115Sdyson		 */
103812115Sdyson		if (namlen == 1)
103912115Sdyson			continue;
104012115Sdyson		if (dp->name[1] == '.' && dp->inode == parentino)
104112115Sdyson			continue;
104212115Sdyson		return (0);
104312115Sdyson	}
104412115Sdyson	return (1);
104512115Sdyson}
104612115Sdyson
104712115Sdyson/*
104812115Sdyson * Check if source directory is in the path of the target directory.
104912115Sdyson * Target is supplied locked, source is unlocked.
105012115Sdyson * The target is always vput before returning.
105112115Sdyson */
105212115Sdysonint
105312115Sdysonext2_checkpath(source, target, cred)
105412115Sdyson	struct inode *source, *target;
105512115Sdyson	struct ucred *cred;
105612115Sdyson{
105712115Sdyson	struct vnode *vp;
105812115Sdyson	int error, rootino, namlen;
105912115Sdyson	struct dirtemplate dirbuf;
106012115Sdyson
106112115Sdyson	vp = ITOV(target);
106212115Sdyson	if (target->i_number == source->i_number) {
106312115Sdyson		error = EEXIST;
106412115Sdyson		goto out;
106512115Sdyson	}
106612115Sdyson	rootino = ROOTINO;
106712115Sdyson	error = 0;
106812115Sdyson	if (target->i_number == rootino)
106912115Sdyson		goto out;
107012115Sdyson
107112115Sdyson	for (;;) {
107212115Sdyson		if (vp->v_type != VDIR) {
107312115Sdyson			error = ENOTDIR;
107412115Sdyson			break;
107512115Sdyson		}
107612115Sdyson		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
107712115Sdyson			sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1078101744Srwatson			IO_NODELOCKED | IO_NOMACCHECK, cred, (int *)0,
1079101744Srwatson			(struct thread *)0);
108012115Sdyson		if (error != 0)
108112115Sdyson			break;
108257710Sbde		namlen = dirbuf.dotdot_type;	/* like ufs little-endian */
108312115Sdyson		if (namlen != 2 ||
108412115Sdyson		    dirbuf.dotdot_name[0] != '.' ||
108512115Sdyson		    dirbuf.dotdot_name[1] != '.') {
108612115Sdyson			error = ENOTDIR;
108712115Sdyson			break;
108812115Sdyson		}
108912115Sdyson		if (dirbuf.dotdot_ino == source->i_number) {
109012115Sdyson			error = EINVAL;
109112115Sdyson			break;
109212115Sdyson		}
109312115Sdyson		if (dirbuf.dotdot_ino == rootino)
109412115Sdyson			break;
109512115Sdyson		vput(vp);
109692462Smckusick		if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
109792462Smckusick		    LK_EXCLUSIVE, &vp)) != 0) {
109812115Sdyson			vp = NULL;
109912115Sdyson			break;
110012115Sdyson		}
110112115Sdyson	}
110212115Sdyson
110312115Sdysonout:
110412115Sdyson	if (error == ENOTDIR)
110512115Sdyson		printf("checkpath: .. not a directory\n");
110612115Sdyson	if (vp != NULL)
110712115Sdyson		vput(vp);
110812115Sdyson	return (error);
110912115Sdyson}
111012115Sdyson
1111