ext2_lookup.c revision 144299
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/gnu/fs/ext2fs/ext2_lookup.c 144299 2005-03-29 13:09:42Z jeff $
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>
4912115Sdyson#include <sys/mount.h>
5012115Sdyson#include <sys/vnode.h>
5112115Sdyson#include <sys/malloc.h>
5212115Sdyson#include <sys/dirent.h>
5396753Siedowse#include <sys/sysctl.h>
5412115Sdyson
5512115Sdyson#include <ufs/ufs/dir.h>
5612115Sdyson
5796749Siedowse#include <gnu/ext2fs/inode.h>
5896749Siedowse#include <gnu/ext2fs/ext2_mount.h>
5912115Sdyson#include <gnu/ext2fs/ext2_extern.h>
6012115Sdyson#include <gnu/ext2fs/ext2_fs.h>
6112115Sdyson#include <gnu/ext2fs/ext2_fs_sb.h>
6212115Sdyson
6396753Siedowse#ifdef DIAGNOSTIC
6496753Siedowsestatic int dirchk = 1;
6596753Siedowse#else
6696753Siedowsestatic int dirchk = 0;
6796753Siedowse#endif
6896753Siedowse
69141633Sphkstatic SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem");
7096753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
7196753Siedowse
72111742Sdes/*
7312115Sdyson   DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
7412115Sdyson   while it is the native blocksize in ext2fs - thus, a #define
7512115Sdyson   is no longer appropriate
7612115Sdyson*/
7712115Sdyson#undef  DIRBLKSIZ
7812115Sdyson
7955477Sbdestatic u_char ext2_ft_to_dt[] = {
8055477Sbde	DT_UNKNOWN,		/* EXT2_FT_UNKNOWN */
8155477Sbde	DT_REG,			/* EXT2_FT_REG_FILE */
8255477Sbde	DT_DIR,			/* EXT2_FT_DIR */
8355477Sbde	DT_CHR,			/* EXT2_FT_CHRDEV */
8455477Sbde	DT_BLK,			/* EXT2_FT_BLKDEV */
8555477Sbde	DT_FIFO,		/* EXT2_FT_FIFO */
8655477Sbde	DT_SOCK,		/* EXT2_FT_SOCK */
8755477Sbde	DT_LNK,			/* EXT2_FT_SYMLINK */
8855477Sbde};
8955477Sbde#define	FTTODT(ft)						\
9055477Sbde    ((ft) > sizeof(ext2_ft_to_dt) / sizeof(ext2_ft_to_dt[0]) ?	\
9155477Sbde    DT_UNKNOWN : ext2_ft_to_dt[(ft)])
9255477Sbde
9355477Sbdestatic u_char dt_to_ext2_ft[] = {
9455477Sbde	EXT2_FT_UNKNOWN,	/* DT_UNKNOWN */
9555477Sbde	EXT2_FT_FIFO,		/* DT_FIFO */
9655477Sbde	EXT2_FT_CHRDEV,		/* DT_CHR */
9755477Sbde	EXT2_FT_UNKNOWN,	/* unused */
9855477Sbde	EXT2_FT_DIR,		/* DT_DIR */
9955477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10055477Sbde	EXT2_FT_BLKDEV,		/* DT_BLK */
10155477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10255477Sbde	EXT2_FT_REG_FILE,	/* DT_REG */
10355477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10455477Sbde	EXT2_FT_SYMLINK,	/* DT_LNK */
10555477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10655477Sbde	EXT2_FT_SOCK,		/* DT_SOCK */
10755477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10855477Sbde	EXT2_FT_UNKNOWN,	/* DT_WHT */
10955477Sbde};
11055477Sbde#define	DTTOFT(dt)						\
11155477Sbde    ((dt) > sizeof(dt_to_ext2_ft) / sizeof(dt_to_ext2_ft[0]) ?	\
11255477Sbde    EXT2_FT_UNKNOWN : dt_to_ext2_ft[(dt)])
11355477Sbde
11493014Sbdestatic int	ext2_dirbadentry(struct vnode *dp, struct ext2_dir_entry_2 *de,
11593014Sbde		    int entryoffsetinblock);
11612159Sbde
11712115Sdyson/*
11812115Sdyson * Vnode op for reading directories.
11912115Sdyson *
12012115Sdyson * The routine below assumes that the on-disk format of a directory
12112115Sdyson * is the same as that defined by <sys/dirent.h>. If the on-disk
12212115Sdyson * format changes, then it will be necessary to do a conversion
12312115Sdyson * from the on-disk format that read returns to the format defined
12412115Sdyson * by <sys/dirent.h>.
12512115Sdyson */
12612115Sdyson/*
12712115Sdyson * this is exactly what we do here - the problem is that the conversion
12812115Sdyson * will blow up some entries by four bytes, so it can't be done in place.
12912115Sdyson * This is too bad. Right now the conversion is done entry by entry, the
130111742Sdes * converted entry is sent via uiomove.
13112115Sdyson *
13212115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send
13312115Sdyson * the whole buffer to uiomove
13412115Sdyson */
13512115Sdysonint
13612115Sdysonext2_readdir(ap)
137111742Sdes	struct vop_readdir_args /* {
138111742Sdes		struct vnode *a_vp;
139111742Sdes		struct uio *a_uio;
140111742Sdes		struct ucred *a_cred;
141111742Sdes	} */ *ap;
14212115Sdyson{
143111742Sdes	struct uio *uio = ap->a_uio;
144111742Sdes	int count, error;
14512115Sdyson
14655477Sbde	struct ext2_dir_entry_2 *edp, *dp;
14724649Sdfr	int ncookies;
14812115Sdyson	struct dirent dstdp;
14912115Sdyson	struct uio auio;
15012115Sdyson	struct iovec aiov;
15112115Sdyson	caddr_t dirbuf;
15265780Sbde	int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize;
15312115Sdyson	int readcnt;
15465780Sbde	off_t startoffset = uio->uio_offset;
15512115Sdyson
15665780Sbde	count = uio->uio_resid;
15765780Sbde	/*
15865780Sbde	 * Avoid complications for partial directory entries by adjusting
15965780Sbde	 * the i/o to end at a block boundary.  Don't give up (like ufs
16065780Sbde	 * does) if the initial adjustment gives a negative count, since
16165780Sbde	 * many callers don't supply a large enough buffer.  The correct
16265780Sbde	 * size is a little larger than DIRBLKSIZ to allow for expansion
16365780Sbde	 * of directory entries, but some callers just use 512.
16465780Sbde	 */
16565780Sbde	count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
16665780Sbde	if (count <= 0)
16765780Sbde		count += DIRBLKSIZ;
16812115Sdyson
16965780Sbde#ifdef EXT2FS_DEBUG
170111742Sdes	printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n",
17165780Sbde	    uio->uio_offset, uio->uio_resid, count);
17212115Sdyson#endif
17312115Sdyson
17412115Sdyson	auio = *uio;
17512115Sdyson	auio.uio_iov = &aiov;
17612115Sdyson	auio.uio_iovcnt = 1;
17765780Sbde	auio.uio_resid = count;
17812115Sdyson	auio.uio_segflg = UIO_SYSSPACE;
17912115Sdyson	aiov.iov_len = count;
180111119Simp	MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
18112115Sdyson	aiov.iov_base = dirbuf;
18212115Sdyson	error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
18312115Sdyson	if (error == 0) {
18412115Sdyson		readcnt = count - auio.uio_resid;
18555477Sbde		edp = (struct ext2_dir_entry_2 *)&dirbuf[readcnt];
18624649Sdfr		ncookies = 0;
18755477Sbde		bzero(&dstdp, offsetof(struct dirent, d_name));
188111742Sdes		for (dp = (struct ext2_dir_entry_2 *)dirbuf;
18955477Sbde		    !error && uio->uio_resid > 0 && dp < edp; ) {
19055477Sbde			/*-
19155477Sbde			 * "New" ext2fs directory entries differ in 3 ways
19255477Sbde			 * from ufs on-disk ones:
19355477Sbde			 * - the name is not necessarily NUL-terminated.
19455477Sbde			 * - the file type field always exists and always
195125843Sbde			 *   follows the name length field.
19655477Sbde			 * - the file type is encoded in a different way.
19755477Sbde			 *
19855477Sbde			 * "Old" ext2fs directory entries need no special
199125843Sbde			 * conversions, since they are binary compatible
200125843Sbde			 * with "new" entries having a file type of 0 (i.e.,
20155477Sbde			 * EXT2_FT_UNKNOWN).  Splitting the old name length
20255477Sbde			 * field didn't make a mess like it did in ufs,
203125843Sbde			 * because ext2fs uses a machine-independent disk
20455477Sbde			 * layout.
20555477Sbde			 */
20655477Sbde			dstdp.d_fileno = dp->inode;
20755477Sbde			dstdp.d_type = FTTODT(dp->file_type);
20855477Sbde			dstdp.d_namlen = dp->name_len;
20955477Sbde			dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
21055477Sbde			bcopy(dp->name, dstdp.d_name, dstdp.d_namlen);
21155477Sbde			bzero(dstdp.d_name + dstdp.d_namlen,
21255477Sbde			    dstdp.d_reclen - offsetof(struct dirent, d_name) -
21355477Sbde			    dstdp.d_namlen);
21455477Sbde
21512115Sdyson			if (dp->rec_len > 0) {
21612115Sdyson				if(dstdp.d_reclen <= uio->uio_resid) {
21712115Sdyson					/* advance dp */
21855477Sbde					dp = (struct ext2_dir_entry_2 *)
219111742Sdes					    ((char *)dp + dp->rec_len);
220111742Sdes					error =
221111741Sdes					  uiomove(&dstdp, dstdp.d_reclen, uio);
22224649Sdfr					if (!error)
22324649Sdfr						ncookies++;
22412115Sdyson				} else
22512115Sdyson					break;
22612115Sdyson			} else {
22712115Sdyson				error = EIO;
22812115Sdyson				break;
22912115Sdyson			}
23012115Sdyson		}
23112115Sdyson		/* we need to correct uio_offset */
23212115Sdyson		uio->uio_offset = startoffset + (caddr_t)dp - dirbuf;
23324649Sdfr
23424649Sdfr		if (!error && ap->a_ncookies != NULL) {
23565780Sbde			u_long *cookiep, *cookies, *ecookies;
23624649Sdfr			off_t off;
23724649Sdfr
23824649Sdfr			if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
239125843Sbde				panic("ext2_readdir: unexpected uio from NFS server");
24024649Sdfr			MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
241111119Simp			       M_WAITOK);
24224649Sdfr			off = startoffset;
24365780Sbde			for (dp = (struct ext2_dir_entry_2 *)dirbuf,
24465780Sbde			     cookiep = cookies, ecookies = cookies + ncookies;
24565780Sbde			     cookiep < ecookies;
24655477Sbde			     dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) {
24724649Sdfr				off += dp->rec_len;
24824649Sdfr				*cookiep++ = (u_long) off;
24924649Sdfr			}
25024649Sdfr			*ap->a_ncookies = ncookies;
25124649Sdfr			*ap->a_cookies = cookies;
25224649Sdfr		}
25312115Sdyson	}
25412115Sdyson	FREE(dirbuf, M_TEMP);
25524649Sdfr	if (ap->a_eofflag)
25624649Sdfr		*ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
257111742Sdes	return (error);
25812115Sdyson}
25912115Sdyson
26012115Sdyson/*
26112115Sdyson * Convert a component of a pathname into a pointer to a locked inode.
26212115Sdyson * This is a very central and rather complicated routine.
26312115Sdyson * If the file system is not maintained in a strict tree hierarchy,
26412115Sdyson * this can result in a deadlock situation (see comments in code below).
26512115Sdyson *
26612115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
26712115Sdyson * on whether the name is to be looked up, created, renamed, or deleted.
26812115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in
26912115Sdyson * creating, renaming, or deleting a directory entry may be calculated.
27012115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname
27112115Sdyson * exists, lookup returns both the target and its parent directory locked.
27212115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may
27312115Sdyson * not be ".".  When deleting and LOCKPARENT is specified, the target may
27412115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput
27512115Sdyson * instead of two vputs.
27612115Sdyson *
277125843Sbde * Overall outline of ext2_lookup:
27812115Sdyson *
27912115Sdyson *	search for name in directory, to found or notfound
28012115Sdyson * notfound:
28112115Sdyson *	if creating, return locked directory, leaving info on available slots
28212115Sdyson *	else return error
28312115Sdyson * found:
28412115Sdyson *	if at end of path and deleting, return information to allow delete
28512115Sdyson *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
28612115Sdyson *	  inode and return info to allow rewrite
28712115Sdyson *	if not at end, add name to cache; if at end and neither creating
28812115Sdyson *	  nor deleting, add name to cache
28912115Sdyson */
29012115Sdysonint
29112115Sdysonext2_lookup(ap)
29228787Sphk	struct vop_cachedlookup_args /* {
29312115Sdyson		struct vnode *a_dvp;
29412115Sdyson		struct vnode **a_vpp;
29512115Sdyson		struct componentname *a_cnp;
29612115Sdyson	} */ *ap;
29712115Sdyson{
29896752Siedowse	struct vnode *vdp;		/* vnode for directory being searched */
29996752Siedowse	struct inode *dp;		/* inode for directory being searched */
30012115Sdyson	struct buf *bp;			/* a buffer of directory entries */
30196752Siedowse	struct ext2_dir_entry_2 *ep;	/* the current directory entry */
30212115Sdyson	int entryoffsetinblock;		/* offset of ep in bp's buffer */
30312115Sdyson	enum {NONE, COMPACT, FOUND} slotstatus;
30412115Sdyson	doff_t slotoffset;		/* offset of area with free space */
30512115Sdyson	int slotsize;			/* size of area at slotoffset */
30612115Sdyson	int slotfreespace;		/* amount of space free in slot */
30712115Sdyson	int slotneeded;			/* size of the entry we're seeking */
30812115Sdyson	int numdirpasses;		/* strategy for directory search */
30912115Sdyson	doff_t endsearch;		/* offset to end directory search */
31012115Sdyson	doff_t prevoff;			/* prev entry dp->i_offset */
31112115Sdyson	struct vnode *pdp;		/* saved dp during symlink work */
31212115Sdyson	struct vnode *tdp;		/* returned by VFS_VGET */
31312115Sdyson	doff_t enduseful;		/* pointer past last used dir slot */
31412115Sdyson	u_long bmask;			/* block offset mask */
31512115Sdyson	int namlen, error;
31612115Sdyson	struct vnode **vpp = ap->a_vpp;
31712115Sdyson	struct componentname *cnp = ap->a_cnp;
31812115Sdyson	struct ucred *cred = cnp->cn_cred;
31912115Sdyson	int flags = cnp->cn_flags;
32012115Sdyson	int nameiop = cnp->cn_nameiop;
32183366Sjulian	struct thread *td = cnp->cn_thread;
32212115Sdyson
32312115Sdyson	int	DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize;
32412115Sdyson
32512115Sdyson	bp = NULL;
32612115Sdyson	slotoffset = -1;
32712115Sdyson	*vpp = NULL;
32812115Sdyson	vdp = ap->a_dvp;
32912115Sdyson	dp = VTOI(vdp);
33012115Sdyson	/*
33112115Sdyson	 * We now have a segment name to search for, and a directory to search.
33212115Sdyson	 */
33312115Sdyson
33412115Sdyson	/*
33512115Sdyson	 * Suppress search for slots unless creating
33612115Sdyson	 * file and at end of pathname, in which case
33712115Sdyson	 * we watch for a place to put the new file in
33812115Sdyson	 * case it doesn't already exist.
33912115Sdyson	 */
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;
36312115Sdyson	if (nameiop != LOOKUP || dp->i_diroff == 0 ||
36412115Sdyson	    dp->i_diroff > dp->i_size) {
36512115Sdyson		entryoffsetinblock = 0;
36612115Sdyson		dp->i_offset = 0;
36712115Sdyson		numdirpasses = 1;
36812115Sdyson	} else {
36912115Sdyson		dp->i_offset = dp->i_diroff;
37012115Sdyson		if ((entryoffsetinblock = dp->i_offset & bmask) &&
37196749Siedowse		    (error = ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL,
37296749Siedowse		    &bp)))
37312115Sdyson			return (error);
37412115Sdyson		numdirpasses = 2;
37512115Sdyson		nchstats.ncs_2passes++;
37612115Sdyson	}
37712115Sdyson	prevoff = dp->i_offset;
37812115Sdyson	endsearch = roundup(dp->i_size, DIRBLKSIZ);
37912115Sdyson	enduseful = 0;
38012115Sdyson
38112115Sdysonsearchloop:
38212115Sdyson	while (dp->i_offset < endsearch) {
38312115Sdyson		/*
38412115Sdyson		 * If necessary, get the next directory block.
38512115Sdyson		 */
38612115Sdyson		if ((dp->i_offset & bmask) == 0) {
38712115Sdyson			if (bp != NULL)
38812115Sdyson				brelse(bp);
38943301Sdillon			if ((error =
39096749Siedowse			    ext2_blkatoff(vdp, (off_t)dp->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		 */
41155477Sbde		ep = (struct ext2_dir_entry_2 *)
41212115Sdyson			((char *)bp->b_data + entryoffsetinblock);
41312115Sdyson		if (ep->rec_len == 0 ||
41412147Sdyson		    (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) {
41512115Sdyson			int i;
41696749Siedowse			ext2_dirbad(dp, dp->i_offset, "mangled entry");
41712115Sdyson			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
41812115Sdyson			dp->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) {
43012115Sdyson			int size = ep->rec_len;
43112115Sdyson
43212115Sdyson			if (ep->inode != 0)
43312115Sdyson				size -= EXT2_DIR_REC_LEN(ep->name_len);
43412115Sdyson			if (size > 0) {
43512115Sdyson				if (size >= slotneeded) {
43612115Sdyson					slotstatus = FOUND;
43712115Sdyson					slotoffset = dp->i_offset;
43812115Sdyson					slotsize = ep->rec_len;
43912115Sdyson				} else if (slotstatus == NONE) {
44012115Sdyson					slotfreespace += size;
44112115Sdyson					if (slotoffset == -1)
44212115Sdyson						slotoffset = dp->i_offset;
44312115Sdyson					if (slotfreespace >= slotneeded) {
44412115Sdyson						slotstatus = COMPACT;
44512115Sdyson						slotsize = dp->i_offset +
44612115Sdyson						      ep->rec_len - slotoffset;
44712115Sdyson					}
44812115Sdyson				}
44912115Sdyson			}
45012115Sdyson		}
45112115Sdyson
45212115Sdyson		/*
45312115Sdyson		 * Check for a name match.
45412115Sdyson		 */
45512115Sdyson		if (ep->inode) {
45612115Sdyson			namlen = ep->name_len;
45712115Sdyson			if (namlen == cnp->cn_namelen &&
45812115Sdyson			    !bcmp(cnp->cn_nameptr, ep->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				 */
46512115Sdyson				dp->i_ino = ep->inode;
46612115Sdyson				dp->i_reclen = ep->rec_len;
46712115Sdyson				goto found;
46812115Sdyson			}
46912115Sdyson		}
47012115Sdyson		prevoff = dp->i_offset;
47112115Sdyson		dp->i_offset += ep->rec_len;
47212115Sdyson		entryoffsetinblock += ep->rec_len;
47312115Sdyson		if (ep->inode)
47412115Sdyson			enduseful = dp->i_offset;
47512115Sdyson	}
47612115Sdyson/* notfound: */
47712115Sdyson	/*
47812115Sdyson	 * If we started in the middle of the directory and failed
47912115Sdyson	 * to find our target, we must check the beginning as well.
48012115Sdyson	 */
48112115Sdyson	if (numdirpasses == 2) {
48212115Sdyson		numdirpasses--;
48312115Sdyson		dp->i_offset = 0;
48412115Sdyson		endsearch = dp->i_diroff;
48512115Sdyson		goto searchloop;
48612115Sdyson	}
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) {
51212115Sdyson			dp->i_offset = roundup(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		}
52112115Sdyson		dp->i_endoff = roundup(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	 */
55312115Sdyson	if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len)
55412115Sdyson		> dp->i_size) {
55596749Siedowse		ext2_dirbad(dp, dp->i_offset, "i_size too small");
55612115Sdyson		dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len);
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)
56712115Sdyson		dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
56812115Sdyson
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;
58912115Sdyson		if (dp->i_number == dp->i_ino) {
59012115Sdyson			VREF(vdp);
59112115Sdyson			*vpp = vdp;
59212115Sdyson			return (0);
59312115Sdyson		}
59492462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_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		 */
62712115Sdyson		if (dp->i_number == dp->i_ino)
62812115Sdyson			return (EISDIR);
62992462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_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) {
65883366Sjulian		VOP_UNLOCK(pdp, 0, td);	/* race to get the inode */
65992462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE,
66092462Smckusick		    &tdp)) != 0) {
66183366Sjulian			vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
66212115Sdyson			return (error);
66312115Sdyson		}
66412115Sdyson		*vpp = tdp;
66512115Sdyson	} else if (dp->i_number == dp->i_ino) {
66612115Sdyson		VREF(vdp);	/* we want ourself, ie "." */
66712115Sdyson		*vpp = vdp;
66812115Sdyson	} else {
66992462Smckusick		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE,
67092462Smckusick		    &tdp)) != 0)
67112115Sdyson			return (error);
67212115Sdyson		*vpp = tdp;
67312115Sdyson	}
67412115Sdyson
67512115Sdyson	/*
67612115Sdyson	 * Insert name into cache if appropriate.
67712115Sdyson	 */
67812115Sdyson	if (cnp->cn_flags & MAKEENTRY)
67912115Sdyson		cache_enter(vdp, *vpp, cnp);
68012115Sdyson	return (0);
68112115Sdyson}
68212115Sdyson
68396749Siedowsevoid
68496749Siedowseext2_dirbad(ip, offset, how)
68596749Siedowse	struct inode *ip;
68696749Siedowse	doff_t offset;
68796749Siedowse	char *how;
68896749Siedowse{
68996749Siedowse	struct mount *mp;
69096749Siedowse
69196749Siedowse	mp = ITOV(ip)->v_mount;
69296749Siedowse	(void)printf("%s: bad dir ino %lu at offset %ld: %s\n",
69396749Siedowse	    mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how);
69496749Siedowse	if ((mp->mnt_flag & MNT_RDONLY) == 0)
69596749Siedowse		panic("ext2_dirbad: bad dir");
69696749Siedowse}
69796749Siedowse
69812115Sdyson/*
69912115Sdyson * Do consistency checking on a directory entry:
70012115Sdyson *	record length must be multiple of 4
70112115Sdyson *	entry must fit in rest of its DIRBLKSIZ block
70212115Sdyson *	record must be large enough to contain entry
70312115Sdyson *	name is not longer than MAXNAMLEN
70412115Sdyson *	name must be as long as advertised, and null terminated
70512115Sdyson */
70612115Sdyson/*
70712115Sdyson *	changed so that it confirms to ext2_check_dir_entry
70812115Sdyson */
70912159Sbdestatic int
71012115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock)
71112115Sdyson	struct vnode *dp;
71296752Siedowse	struct ext2_dir_entry_2 *de;
71312115Sdyson	int entryoffsetinblock;
71412115Sdyson{
71512115Sdyson	int	DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize;
71612115Sdyson
717111742Sdes	char * error_msg = NULL;
71812115Sdyson
719111742Sdes	if (de->rec_len < EXT2_DIR_REC_LEN(1))
720111742Sdes		error_msg = "rec_len is smaller than minimal";
721111742Sdes	else if (de->rec_len % 4 != 0)
722111742Sdes		error_msg = "rec_len % 4 != 0";
723111742Sdes	else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
724111742Sdes		error_msg = "reclen is too small for name_len";
725111742Sdes	else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ)
726111742Sdes		error_msg = "directory entry across blocks";
727111742Sdes	/* else LATER
72812115Sdyson	     if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
729111742Sdes		error_msg = "inode out of bounds";
73012115Sdyson	*/
73112115Sdyson
732111742Sdes	if (error_msg != NULL) {
733111742Sdes		printf("bad directory entry: %s\n", error_msg);
734111742Sdes		printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
73524492Sbde			entryoffsetinblock, (unsigned long)de->inode,
73624492Sbde			de->rec_len, de->name_len);
737111742Sdes	}
738111742Sdes	return error_msg == NULL ? 0 : 1;
73912115Sdyson}
74012115Sdyson
74112115Sdyson/*
74212115Sdyson * Write a directory entry after a call to namei, using the parameters
74312115Sdyson * that it left in nameidata.  The argument ip is the inode which the new
74412115Sdyson * directory entry will refer to.  Dvp is a pointer to the directory to
74512115Sdyson * be written, which was left locked by namei. Remaining parameters
74612115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new
74712115Sdyson * entry is to be obtained.
74812115Sdyson */
74912115Sdysonint
75012115Sdysonext2_direnter(ip, dvp, cnp)
75112115Sdyson	struct inode *ip;
75212115Sdyson	struct vnode *dvp;
75396752Siedowse	struct componentname *cnp;
75412115Sdyson{
75596752Siedowse	struct ext2_dir_entry_2 *ep, *nep;
75696752Siedowse	struct inode *dp;
75712115Sdyson	struct buf *bp;
75855477Sbde	struct ext2_dir_entry_2 newdir;
75912115Sdyson	struct iovec aiov;
76012115Sdyson	struct uio auio;
76112115Sdyson	u_int dsize;
76212115Sdyson	int error, loc, newentrysize, spacefree;
76312115Sdyson	char *dirbuf;
76412115Sdyson	int     DIRBLKSIZ = ip->i_e2fs->s_blocksize;
76512115Sdyson
76612115Sdyson
76712115Sdyson#if DIAGNOSTIC
76812115Sdyson	if ((cnp->cn_flags & SAVENAME) == 0)
76912115Sdyson		panic("direnter: missing name");
77012115Sdyson#endif
77112115Sdyson	dp = VTOI(dvp);
77212115Sdyson	newdir.inode = ip->i_number;
77312115Sdyson	newdir.name_len = cnp->cn_namelen;
77455477Sbde	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
77555477Sbde	    EXT2_FEATURE_INCOMPAT_FILETYPE))
77655477Sbde		newdir.file_type = DTTOFT(IFTODT(ip->i_mode));
77755477Sbde	else
77855477Sbde		newdir.file_type = EXT2_FT_UNKNOWN;
77912115Sdyson	bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1);
78012115Sdyson	newentrysize = EXT2_DIR_REC_LEN(newdir.name_len);
78112115Sdyson	if (dp->i_count == 0) {
78212115Sdyson		/*
78312115Sdyson		 * If dp->i_count is 0, then namei could find no
78412115Sdyson		 * space in the directory. Here, dp->i_offset will
78512115Sdyson		 * be on a directory block boundary and we will write the
78612115Sdyson		 * new entry into a fresh block.
78712115Sdyson		 */
78812115Sdyson		if (dp->i_offset & (DIRBLKSIZ - 1))
78912115Sdyson			panic("ext2_direnter: newblk");
79012115Sdyson		auio.uio_offset = dp->i_offset;
79112115Sdyson		newdir.rec_len = DIRBLKSIZ;
79212115Sdyson		auio.uio_resid = newentrysize;
79312115Sdyson		aiov.iov_len = newentrysize;
79412115Sdyson		aiov.iov_base = (caddr_t)&newdir;
79512115Sdyson		auio.uio_iov = &aiov;
79612115Sdyson		auio.uio_iovcnt = 1;
79712115Sdyson		auio.uio_rw = UIO_WRITE;
79812115Sdyson		auio.uio_segflg = UIO_SYSSPACE;
79983366Sjulian		auio.uio_td = (struct thread *)0;
80012115Sdyson		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
80112115Sdyson		if (DIRBLKSIZ >
80296749Siedowse		    VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
80312115Sdyson			/* XXX should grow with balloc() */
80412115Sdyson			panic("ext2_direnter: frag size");
80512115Sdyson		else if (!error) {
80612115Sdyson			dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
80712115Sdyson			dp->i_flag |= IN_CHANGE;
80812115Sdyson		}
80912115Sdyson		return (error);
81012115Sdyson	}
81112115Sdyson
81212115Sdyson	/*
81312115Sdyson	 * If dp->i_count is non-zero, then namei found space
81412115Sdyson	 * for the new entry in the range dp->i_offset to
81512115Sdyson	 * dp->i_offset + dp->i_count in the directory.
81612115Sdyson	 * To use this space, we may have to compact the entries located
81712115Sdyson	 * there, by copying them together towards the beginning of the
81812115Sdyson	 * block, leaving the free space in one usable chunk at the end.
81912115Sdyson	 */
82012115Sdyson
82112115Sdyson	/*
82212115Sdyson	 * Increase size of directory if entry eats into new space.
82312115Sdyson	 * This should never push the size past a new multiple of
82412115Sdyson	 * DIRBLKSIZE.
82512115Sdyson	 *
82612115Sdyson	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
82712115Sdyson	 */
82812115Sdyson	if (dp->i_offset + dp->i_count > dp->i_size)
82912115Sdyson		dp->i_size = dp->i_offset + dp->i_count;
83012115Sdyson	/*
83112115Sdyson	 * Get the block containing the space for the new directory entry.
83212115Sdyson	 */
83396749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf,
83496749Siedowse	    &bp)) != 0)
83512115Sdyson		return (error);
83612115Sdyson	/*
83712115Sdyson	 * Find space for the new entry. In the simple case, the entry at
83812115Sdyson	 * offset base will have the space. If it does not, then namei
83912115Sdyson	 * arranged that compacting the region dp->i_offset to
84012115Sdyson	 * dp->i_offset + dp->i_count would yield the
84112115Sdyson	 * space.
84212115Sdyson	 */
84355477Sbde	ep = (struct ext2_dir_entry_2 *)dirbuf;
84412115Sdyson	dsize = EXT2_DIR_REC_LEN(ep->name_len);
84512115Sdyson	spacefree = ep->rec_len - dsize;
84612115Sdyson	for (loc = ep->rec_len; loc < dp->i_count; ) {
84755477Sbde		nep = (struct ext2_dir_entry_2 *)(dirbuf + loc);
84812115Sdyson		if (ep->inode) {
84912115Sdyson			/* trim the existing slot */
85012115Sdyson			ep->rec_len = dsize;
85155477Sbde			ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
85212115Sdyson		} else {
85312115Sdyson			/* overwrite; nothing there; header is ours */
85412115Sdyson			spacefree += dsize;
85512115Sdyson		}
85619541Sbde		dsize = EXT2_DIR_REC_LEN(nep->name_len);
85712115Sdyson		spacefree += nep->rec_len - dsize;
85812115Sdyson		loc += nep->rec_len;
85912115Sdyson		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
86012115Sdyson	}
86112115Sdyson	/*
86212115Sdyson	 * Update the pointer fields in the previous entry (if any),
86312115Sdyson	 * copy in the new entry, and write out the block.
86412115Sdyson	 */
86512115Sdyson	if (ep->inode == 0) {
86612115Sdyson		if (spacefree + dsize < newentrysize)
86712115Sdyson			panic("ext2_direnter: compact1");
86812115Sdyson		newdir.rec_len = spacefree + dsize;
86912115Sdyson	} else {
87012115Sdyson		if (spacefree < newentrysize)
87112115Sdyson			panic("ext2_direnter: compact2");
87212115Sdyson		newdir.rec_len = spacefree;
87312115Sdyson		ep->rec_len = dsize;
87455477Sbde		ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
87512115Sdyson	}
87612115Sdyson	bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
877126853Sphk	error = bwrite(bp);
87812115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
87912115Sdyson	if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
88096749Siedowse		error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
88183366Sjulian		    cnp->cn_cred, cnp->cn_thread);
88212115Sdyson	return (error);
88312115Sdyson}
88412115Sdyson
88512115Sdyson/*
88612115Sdyson * Remove a directory entry after a call to namei, using
88712115Sdyson * the parameters which it left in nameidata. The entry
88812115Sdyson * dp->i_offset contains the offset into the directory of the
88912115Sdyson * entry to be eliminated.  The dp->i_count field contains the
89012115Sdyson * size of the previous record in the directory.  If this
89112115Sdyson * is 0, the first entry is being deleted, so we need only
89212115Sdyson * zero the inode number to mark the entry as free.  If the
89312115Sdyson * entry is not the first in the directory, we must reclaim
89412115Sdyson * the space of the now empty record by adding the record size
89512115Sdyson * to the size of the previous entry.
89612115Sdyson */
89712115Sdysonint
89812115Sdysonext2_dirremove(dvp, cnp)
89912115Sdyson	struct vnode *dvp;
90012115Sdyson	struct componentname *cnp;
90112115Sdyson{
90296752Siedowse	struct inode *dp;
90355477Sbde	struct ext2_dir_entry_2 *ep;
90412115Sdyson	struct buf *bp;
90512115Sdyson	int error;
906111742Sdes
90712115Sdyson	dp = VTOI(dvp);
90812115Sdyson	if (dp->i_count == 0) {
90912115Sdyson		/*
91012115Sdyson		 * First entry in block: set d_ino to zero.
91112115Sdyson		 */
91243301Sdillon		if ((error =
91396749Siedowse		    ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep,
91496749Siedowse		    &bp)) != 0)
91512115Sdyson			return (error);
91612115Sdyson		ep->inode = 0;
917126853Sphk		error = bwrite(bp);
91812115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
91912115Sdyson		return (error);
92012115Sdyson	}
92112115Sdyson	/*
92212115Sdyson	 * Collapse new free space into previous entry.
92312115Sdyson	 */
92496749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
92543301Sdillon	    (char **)&ep, &bp)) != 0)
92612115Sdyson		return (error);
92712115Sdyson	ep->rec_len += dp->i_reclen;
928126853Sphk	error = bwrite(bp);
92912115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
93012115Sdyson	return (error);
93112115Sdyson}
93212115Sdyson
93312115Sdyson/*
93412115Sdyson * Rewrite an existing directory entry to point at the inode
93512115Sdyson * supplied.  The parameters describing the directory entry are
93612115Sdyson * set up by a call to namei.
93712115Sdyson */
93812115Sdysonint
93912115Sdysonext2_dirrewrite(dp, ip, cnp)
94012115Sdyson	struct inode *dp, *ip;
94112115Sdyson	struct componentname *cnp;
94212115Sdyson{
94312115Sdyson	struct buf *bp;
94455477Sbde	struct ext2_dir_entry_2 *ep;
94512115Sdyson	struct vnode *vdp = ITOV(dp);
94612115Sdyson	int error;
94712115Sdyson
94896749Siedowse	if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep,
94996749Siedowse	    &bp)) != 0)
95012115Sdyson		return (error);
95112115Sdyson	ep->inode = ip->i_number;
95255477Sbde	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
95355477Sbde	    EXT2_FEATURE_INCOMPAT_FILETYPE))
95455477Sbde		ep->file_type = DTTOFT(IFTODT(ip->i_mode));
95555477Sbde	else
95655477Sbde		ep->file_type = EXT2_FT_UNKNOWN;
957126853Sphk	error = bwrite(bp);
95812115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
95912115Sdyson	return (error);
96012115Sdyson}
96112115Sdyson
96212115Sdyson/*
96312115Sdyson * Check if a directory is empty or not.
96412115Sdyson * Inode supplied must be locked.
96512115Sdyson *
96612115Sdyson * Using a struct dirtemplate here is not precisely
96712115Sdyson * what we want, but better than using a struct direct.
96812115Sdyson *
96912115Sdyson * NB: does not handle corrupted directories.
97012115Sdyson */
97112115Sdysonint
97212115Sdysonext2_dirempty(ip, parentino, cred)
97396752Siedowse	struct inode *ip;
97412115Sdyson	ino_t parentino;
97512115Sdyson	struct ucred *cred;
97612115Sdyson{
97796752Siedowse	off_t off;
97812115Sdyson	struct dirtemplate dbuf;
97996752Siedowse	struct ext2_dir_entry_2 *dp = (struct ext2_dir_entry_2 *)&dbuf;
98012115Sdyson	int error, count, namlen;
98112115Sdyson#define	MINDIRSIZ (sizeof (struct dirtemplate) / 2)
98212115Sdyson
98312115Sdyson	for (off = 0; off < ip->i_size; off += dp->rec_len) {
984101744Srwatson		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
985101744Srwatson		    off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
986101941Srwatson		    NOCRED, &count, (struct thread *)0);
98712115Sdyson		/*
98812115Sdyson		 * Since we read MINDIRSIZ, residual must
98912115Sdyson		 * be 0 unless we're at end of file.
99012115Sdyson		 */
99112115Sdyson		if (error || count != 0)
99212115Sdyson			return (0);
99312115Sdyson		/* avoid infinite loops */
99412115Sdyson		if (dp->rec_len == 0)
99512115Sdyson			return (0);
99612115Sdyson		/* skip empty entries */
99712115Sdyson		if (dp->inode == 0)
99812115Sdyson			continue;
99912115Sdyson		/* accept only "." and ".." */
100012115Sdyson		namlen = dp->name_len;
100112115Sdyson		if (namlen > 2)
100212115Sdyson			return (0);
100312115Sdyson		if (dp->name[0] != '.')
100412115Sdyson			return (0);
100512115Sdyson		/*
100612115Sdyson		 * At this point namlen must be 1 or 2.
100712115Sdyson		 * 1 implies ".", 2 implies ".." if second
100812115Sdyson		 * char is also "."
100912115Sdyson		 */
101012115Sdyson		if (namlen == 1)
101112115Sdyson			continue;
101212115Sdyson		if (dp->name[1] == '.' && dp->inode == parentino)
101312115Sdyson			continue;
101412115Sdyson		return (0);
101512115Sdyson	}
101612115Sdyson	return (1);
101712115Sdyson}
101812115Sdyson
101912115Sdyson/*
102012115Sdyson * Check if source directory is in the path of the target directory.
102112115Sdyson * Target is supplied locked, source is unlocked.
102212115Sdyson * The target is always vput before returning.
102312115Sdyson */
102412115Sdysonint
102512115Sdysonext2_checkpath(source, target, cred)
102612115Sdyson	struct inode *source, *target;
102712115Sdyson	struct ucred *cred;
102812115Sdyson{
102912115Sdyson	struct vnode *vp;
103012115Sdyson	int error, rootino, namlen;
103112115Sdyson	struct dirtemplate dirbuf;
103212115Sdyson
103312115Sdyson	vp = ITOV(target);
103412115Sdyson	if (target->i_number == source->i_number) {
103512115Sdyson		error = EEXIST;
103612115Sdyson		goto out;
103712115Sdyson	}
103812115Sdyson	rootino = ROOTINO;
103912115Sdyson	error = 0;
104012115Sdyson	if (target->i_number == rootino)
104112115Sdyson		goto out;
104212115Sdyson
104312115Sdyson	for (;;) {
104412115Sdyson		if (vp->v_type != VDIR) {
104512115Sdyson			error = ENOTDIR;
104612115Sdyson			break;
104712115Sdyson		}
104812115Sdyson		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
104912115Sdyson			sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1050101941Srwatson			IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, (int *)0,
1051101744Srwatson			(struct thread *)0);
105212115Sdyson		if (error != 0)
105312115Sdyson			break;
105457710Sbde		namlen = dirbuf.dotdot_type;	/* like ufs little-endian */
105512115Sdyson		if (namlen != 2 ||
105612115Sdyson		    dirbuf.dotdot_name[0] != '.' ||
105712115Sdyson		    dirbuf.dotdot_name[1] != '.') {
105812115Sdyson			error = ENOTDIR;
105912115Sdyson			break;
106012115Sdyson		}
106112115Sdyson		if (dirbuf.dotdot_ino == source->i_number) {
106212115Sdyson			error = EINVAL;
106312115Sdyson			break;
106412115Sdyson		}
106512115Sdyson		if (dirbuf.dotdot_ino == rootino)
106612115Sdyson			break;
106712115Sdyson		vput(vp);
106892462Smckusick		if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
106992462Smckusick		    LK_EXCLUSIVE, &vp)) != 0) {
107012115Sdyson			vp = NULL;
107112115Sdyson			break;
107212115Sdyson		}
107312115Sdyson	}
107412115Sdyson
107512115Sdysonout:
107612115Sdyson	if (error == ENOTDIR)
107712115Sdyson		printf("checkpath: .. not a directory\n");
107812115Sdyson	if (vp != NULL)
107912115Sdyson		vput(vp);
108012115Sdyson	return (error);
108112115Sdyson}
1082