ext2_lookup.c revision 57710
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
655477Sbde *
755477Sbde * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_lookup.c 57710 2000-03-03 08:00:27Z bde $
812115Sdyson */
912115Sdyson/*
1012115Sdyson * Copyright (c) 1989, 1993
1112115Sdyson *	The Regents of the University of California.  All rights reserved.
1212115Sdyson * (c) UNIX System Laboratories, Inc.
1312115Sdyson * All or some portions of this file are derived from material licensed
1412115Sdyson * to the University of California by American Telephone and Telegraph
1512115Sdyson * Co. or Unix System Laboratories, Inc. and are reproduced herein with
1612115Sdyson * the permission of UNIX System Laboratories, Inc.
1712115Sdyson *
1812115Sdyson * Redistribution and use in source and binary forms, with or without
1912115Sdyson * modification, are permitted provided that the following conditions
2012115Sdyson * are met:
2112115Sdyson * 1. Redistributions of source code must retain the above copyright
2212115Sdyson *    notice, this list of conditions and the following disclaimer.
2312115Sdyson * 2. Redistributions in binary form must reproduce the above copyright
2412115Sdyson *    notice, this list of conditions and the following disclaimer in the
2512115Sdyson *    documentation and/or other materials provided with the distribution.
2612115Sdyson * 3. All advertising materials mentioning features or use of this software
2712115Sdyson *    must display the following acknowledgement:
2812115Sdyson *	This product includes software developed by the University of
2912115Sdyson *	California, Berkeley and its contributors.
3012115Sdyson * 4. Neither the name of the University nor the names of its contributors
3112115Sdyson *    may be used to endorse or promote products derived from this software
3212115Sdyson *    without specific prior written permission.
3312115Sdyson *
3412115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3512115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3612115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3712115Sdyson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3812115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3912115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4012115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4112115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4212115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4312115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4412115Sdyson * SUCH DAMAGE.
4512115Sdyson *
4612115Sdyson *	@(#)ufs_lookup.c	8.6 (Berkeley) 4/1/94
4712115Sdyson */
4812115Sdyson
4955477Sbde#include <stddef.h>
5055477Sbde
5112115Sdyson#include <sys/param.h>
5212159Sbde#include <sys/systm.h>
5312115Sdyson#include <sys/namei.h>
5412115Sdyson#include <sys/buf.h>
5512115Sdyson#include <sys/mount.h>
5612115Sdyson#include <sys/vnode.h>
5712115Sdyson#include <sys/malloc.h>
5812115Sdyson#include <sys/dirent.h>
5912115Sdyson
6012115Sdyson#include <ufs/ufs/quota.h>
6112115Sdyson#include <ufs/ufs/inode.h>
6212115Sdyson#include <ufs/ufs/dir.h>
6312115Sdyson#include <ufs/ufs/ufsmount.h>
6412147Sdyson#include <ufs/ufs/ufs_extern.h>
6512115Sdyson
6612115Sdyson#include <gnu/ext2fs/ext2_extern.h>
6712115Sdyson#include <gnu/ext2fs/ext2_fs.h>
6812115Sdyson#include <gnu/ext2fs/ext2_fs_sb.h>
6912115Sdyson
7012115Sdyson/*
7112115Sdyson   DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
7212115Sdyson   while it is the native blocksize in ext2fs - thus, a #define
7312115Sdyson   is no longer appropriate
7412115Sdyson*/
7512115Sdyson#undef  DIRBLKSIZ
7612115Sdyson
7712147Sdysonextern	int dirchk;
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
11412159Sbdestatic int	ext2_dirbadentry __P((struct vnode *dp,
11555477Sbde				      struct ext2_dir_entry_2 *de,
11612159Sbde				      int entryoffsetinblock));
11712159Sbde
11812115Sdyson/*
11912115Sdyson * Vnode op for reading directories.
12012115Sdyson *
12112115Sdyson * The routine below assumes that the on-disk format of a directory
12212115Sdyson * is the same as that defined by <sys/dirent.h>. If the on-disk
12312115Sdyson * format changes, then it will be necessary to do a conversion
12412115Sdyson * from the on-disk format that read returns to the format defined
12512115Sdyson * by <sys/dirent.h>.
12612115Sdyson */
12712115Sdyson/*
12812115Sdyson * this is exactly what we do here - the problem is that the conversion
12912115Sdyson * will blow up some entries by four bytes, so it can't be done in place.
13012115Sdyson * This is too bad. Right now the conversion is done entry by entry, the
13112115Sdyson * converted entry is sent via uiomove.
13212115Sdyson *
13312115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send
13412115Sdyson * the whole buffer to uiomove
13512115Sdyson */
13612115Sdysonint
13712115Sdysonext2_readdir(ap)
13812115Sdyson        struct vop_readdir_args /* {
13912115Sdyson                struct vnode *a_vp;
14012115Sdyson                struct uio *a_uio;
14112115Sdyson                struct ucred *a_cred;
14212115Sdyson        } */ *ap;
14312115Sdyson{
14412115Sdyson        register struct uio *uio = ap->a_uio;
14512147Sdyson        int count, error;
14612115Sdyson
14755477Sbde	struct ext2_dir_entry_2 *edp, *dp;
14824649Sdfr	int ncookies;
14912115Sdyson	struct dirent dstdp;
15012115Sdyson	struct uio auio;
15112115Sdyson	struct iovec aiov;
15212115Sdyson	caddr_t dirbuf;
15312115Sdyson	int readcnt;
15412115Sdyson	u_quad_t startoffset = uio->uio_offset;
15512115Sdyson
15612115Sdyson        count = uio->uio_resid;		/* legyenek boldogok akik akarnak ... */
15712115Sdyson        uio->uio_resid = count;
15812115Sdyson        uio->uio_iov->iov_len = count;
15912115Sdyson
16012115Sdyson#if 0
16112115Sdysonprintf("ext2_readdir called uio->uio_offset %d uio->uio_resid %d count %d \n",
16212115Sdyson	(int)uio->uio_offset, (int)uio->uio_resid, (int)count);
16312115Sdyson#endif
16412115Sdyson
16512115Sdyson	auio = *uio;
16612115Sdyson	auio.uio_iov = &aiov;
16712115Sdyson	auio.uio_iovcnt = 1;
16812115Sdyson	auio.uio_segflg = UIO_SYSSPACE;
16912115Sdyson	aiov.iov_len = count;
17012115Sdyson	MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
17112115Sdyson	aiov.iov_base = dirbuf;
17212115Sdyson	error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
17312115Sdyson	if (error == 0) {
17412115Sdyson		readcnt = count - auio.uio_resid;
17555477Sbde		edp = (struct ext2_dir_entry_2 *)&dirbuf[readcnt];
17624649Sdfr		ncookies = 0;
17755477Sbde		bzero(&dstdp, offsetof(struct dirent, d_name));
17855477Sbde		for (dp = (struct ext2_dir_entry_2 *)dirbuf;
17955477Sbde		    !error && uio->uio_resid > 0 && dp < edp; ) {
18055477Sbde			/*-
18155477Sbde			 * "New" ext2fs directory entries differ in 3 ways
18255477Sbde			 * from ufs on-disk ones:
18355477Sbde			 * - the name is not necessarily NUL-terminated.
18455477Sbde			 * - the file type field always exists and always
18555477Sbde			 * follows the name length field.
18655477Sbde			 * - the file type is encoded in a different way.
18755477Sbde			 *
18855477Sbde			 * "Old" ext2fs directory entries need no special
18955477Sbde			 * conversions, since they binary compatible with
19055477Sbde			 * "new" entries having a file type of 0 (i.e.,
19155477Sbde			 * EXT2_FT_UNKNOWN).  Splitting the old name length
19255477Sbde			 * field didn't make a mess like it did in ufs,
19355477Sbde			 * because ext2fs uses a machine-dependent disk
19455477Sbde			 * layout.
19555477Sbde			 */
19655477Sbde			dstdp.d_fileno = dp->inode;
19755477Sbde			dstdp.d_type = FTTODT(dp->file_type);
19855477Sbde			dstdp.d_namlen = dp->name_len;
19955477Sbde			dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
20055477Sbde			bcopy(dp->name, dstdp.d_name, dstdp.d_namlen);
20155477Sbde			bzero(dstdp.d_name + dstdp.d_namlen,
20255477Sbde			    dstdp.d_reclen - offsetof(struct dirent, d_name) -
20355477Sbde			    dstdp.d_namlen);
20455477Sbde
20512115Sdyson			if (dp->rec_len > 0) {
20612115Sdyson				if(dstdp.d_reclen <= uio->uio_resid) {
20712115Sdyson					/* advance dp */
20855477Sbde					dp = (struct ext2_dir_entry_2 *)
20912115Sdyson					    ((char *)dp + dp->rec_len);
21012115Sdyson					error =
21112159Sbde					  uiomove((caddr_t)&dstdp,
21212159Sbde						  dstdp.d_reclen, uio);
21324649Sdfr					if (!error)
21424649Sdfr						ncookies++;
21512115Sdyson				} else
21612115Sdyson					break;
21712115Sdyson			} else {
21812115Sdyson				error = EIO;
21912115Sdyson				break;
22012115Sdyson			}
22112115Sdyson		}
22212115Sdyson		/* we need to correct uio_offset */
22312115Sdyson		uio->uio_offset = startoffset + (caddr_t)dp - dirbuf;
22424649Sdfr
22524649Sdfr		if (!error && ap->a_ncookies != NULL) {
22624649Sdfr			u_long *cookies;
22724649Sdfr			u_long *cookiep;
22824649Sdfr			off_t off;
22924649Sdfr
23024649Sdfr			if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
23124649Sdfr				panic("ext2fs_readdir: unexpected uio from NFS server");
23224649Sdfr			MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
23324649Sdfr			       M_WAITOK);
23424649Sdfr			off = startoffset;
23555477Sbde			for (dp = (struct ext2_dir_entry_2 *)dirbuf, cookiep = cookies;
23624649Sdfr			     dp < edp;
23755477Sbde			     dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) {
23824649Sdfr				off += dp->rec_len;
23924649Sdfr				*cookiep++ = (u_long) off;
24024649Sdfr			}
24124649Sdfr			*ap->a_ncookies = ncookies;
24224649Sdfr			*ap->a_cookies = cookies;
24324649Sdfr		}
24412115Sdyson	}
24512115Sdyson	FREE(dirbuf, M_TEMP);
24624649Sdfr	if (ap->a_eofflag)
24724649Sdfr		*ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
24812115Sdyson        return (error);
24912115Sdyson}
25012115Sdyson
25112115Sdyson/*
25212115Sdyson * Convert a component of a pathname into a pointer to a locked inode.
25312115Sdyson * This is a very central and rather complicated routine.
25412115Sdyson * If the file system is not maintained in a strict tree hierarchy,
25512115Sdyson * this can result in a deadlock situation (see comments in code below).
25612115Sdyson *
25712115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
25812115Sdyson * on whether the name is to be looked up, created, renamed, or deleted.
25912115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in
26012115Sdyson * creating, renaming, or deleting a directory entry may be calculated.
26112115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname
26212115Sdyson * exists, lookup returns both the target and its parent directory locked.
26312115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may
26412115Sdyson * not be ".".  When deleting and LOCKPARENT is specified, the target may
26512115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput
26612115Sdyson * instead of two vputs.
26712115Sdyson *
26812115Sdyson * Overall outline of ufs_lookup:
26912115Sdyson *
27012115Sdyson *	search for name in directory, to found or notfound
27112115Sdyson * notfound:
27212115Sdyson *	if creating, return locked directory, leaving info on available slots
27312115Sdyson *	else return error
27412115Sdyson * found:
27512115Sdyson *	if at end of path and deleting, return information to allow delete
27612115Sdyson *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
27712115Sdyson *	  inode and return info to allow rewrite
27812115Sdyson *	if not at end, add name to cache; if at end and neither creating
27912115Sdyson *	  nor deleting, add name to cache
28012115Sdyson */
28112115Sdysonint
28212115Sdysonext2_lookup(ap)
28328787Sphk	struct vop_cachedlookup_args /* {
28412115Sdyson		struct vnode *a_dvp;
28512115Sdyson		struct vnode **a_vpp;
28612115Sdyson		struct componentname *a_cnp;
28712115Sdyson	} */ *ap;
28812115Sdyson{
28912115Sdyson	register struct vnode *vdp;	/* vnode for directory being searched */
29012115Sdyson	register struct inode *dp;	/* inode for directory being searched */
29112115Sdyson	struct buf *bp;			/* a buffer of directory entries */
29255477Sbde	register struct ext2_dir_entry_2 *ep; /* the current directory entry */
29312115Sdyson	int entryoffsetinblock;		/* offset of ep in bp's buffer */
29412115Sdyson	enum {NONE, COMPACT, FOUND} slotstatus;
29512115Sdyson	doff_t slotoffset;		/* offset of area with free space */
29612115Sdyson	int slotsize;			/* size of area at slotoffset */
29712115Sdyson	int slotfreespace;		/* amount of space free in slot */
29812115Sdyson	int slotneeded;			/* size of the entry we're seeking */
29912115Sdyson	int numdirpasses;		/* strategy for directory search */
30012115Sdyson	doff_t endsearch;		/* offset to end directory search */
30112115Sdyson	doff_t prevoff;			/* prev entry dp->i_offset */
30212115Sdyson	struct vnode *pdp;		/* saved dp during symlink work */
30312115Sdyson	struct vnode *tdp;		/* returned by VFS_VGET */
30412115Sdyson	doff_t enduseful;		/* pointer past last used dir slot */
30512115Sdyson	u_long bmask;			/* block offset mask */
30612115Sdyson	int lockparent;			/* 1 => lockparent flag is set */
30712115Sdyson	int wantparent;			/* 1 => wantparent or lockparent flag */
30812115Sdyson	int namlen, error;
30912115Sdyson	struct vnode **vpp = ap->a_vpp;
31012115Sdyson	struct componentname *cnp = ap->a_cnp;
31112115Sdyson	struct ucred *cred = cnp->cn_cred;
31212115Sdyson	int flags = cnp->cn_flags;
31312115Sdyson	int nameiop = cnp->cn_nameiop;
31422521Sdyson	struct proc *p = cnp->cn_proc;
31512115Sdyson
31612115Sdyson	int	DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize;
31712115Sdyson
31812115Sdyson	bp = NULL;
31912115Sdyson	slotoffset = -1;
32012115Sdyson	*vpp = NULL;
32112115Sdyson	vdp = ap->a_dvp;
32212115Sdyson	dp = VTOI(vdp);
32312115Sdyson	lockparent = flags & LOCKPARENT;
32412115Sdyson	wantparent = flags & (LOCKPARENT|WANTPARENT);
32512115Sdyson
32612115Sdyson	/*
32712115Sdyson	 * We now have a segment name to search for, and a directory to search.
32812115Sdyson	 */
32912115Sdyson
33012115Sdyson	/*
33112115Sdyson	 * Suppress search for slots unless creating
33212115Sdyson	 * file and at end of pathname, in which case
33312115Sdyson	 * we watch for a place to put the new file in
33412115Sdyson	 * case it doesn't already exist.
33512115Sdyson	 */
33612115Sdyson	slotstatus = FOUND;
33712115Sdyson	slotfreespace = slotsize = slotneeded = 0;
33812115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
33912115Sdyson	    (flags & ISLASTCN)) {
34012115Sdyson		slotstatus = NONE;
34112115Sdyson		slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen);
34212115Sdyson		/* was
34312115Sdyson		slotneeded = (sizeof(struct direct) - MAXNAMLEN +
34412115Sdyson			cnp->cn_namelen + 3) &~ 3; */
34512115Sdyson	}
34612115Sdyson
34712115Sdyson	/*
34812115Sdyson	 * If there is cached information on a previous search of
34912115Sdyson	 * this directory, pick up where we last left off.
35012115Sdyson	 * We cache only lookups as these are the most common
35112115Sdyson	 * and have the greatest payoff. Caching CREATE has little
35212115Sdyson	 * benefit as it usually must search the entire directory
35312115Sdyson	 * to determine that the entry does not exist. Caching the
35412115Sdyson	 * location of the last DELETE or RENAME has not reduced
35512115Sdyson	 * profiling time and hence has been removed in the interest
35612115Sdyson	 * of simplicity.
35712115Sdyson	 */
35812115Sdyson	bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
35912115Sdyson	if (nameiop != LOOKUP || dp->i_diroff == 0 ||
36012115Sdyson	    dp->i_diroff > dp->i_size) {
36112115Sdyson		entryoffsetinblock = 0;
36212115Sdyson		dp->i_offset = 0;
36312115Sdyson		numdirpasses = 1;
36412115Sdyson	} else {
36512115Sdyson		dp->i_offset = dp->i_diroff;
36612115Sdyson		if ((entryoffsetinblock = dp->i_offset & bmask) &&
36730474Sphk		    (error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
36812115Sdyson			return (error);
36912115Sdyson		numdirpasses = 2;
37012115Sdyson		nchstats.ncs_2passes++;
37112115Sdyson	}
37212115Sdyson	prevoff = dp->i_offset;
37312115Sdyson	endsearch = roundup(dp->i_size, DIRBLKSIZ);
37412115Sdyson	enduseful = 0;
37512115Sdyson
37612115Sdysonsearchloop:
37712115Sdyson	while (dp->i_offset < endsearch) {
37812115Sdyson		/*
37912115Sdyson		 * If necessary, get the next directory block.
38012115Sdyson		 */
38112115Sdyson		if ((dp->i_offset & bmask) == 0) {
38212115Sdyson			if (bp != NULL)
38312115Sdyson				brelse(bp);
38443301Sdillon			if ((error =
38543301Sdillon			    UFS_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) != 0)
38612115Sdyson				return (error);
38712115Sdyson			entryoffsetinblock = 0;
38812115Sdyson		}
38912115Sdyson		/*
39012115Sdyson		 * If still looking for a slot, and at a DIRBLKSIZE
39112115Sdyson		 * boundary, have to start looking for free space again.
39212115Sdyson		 */
39312115Sdyson		if (slotstatus == NONE &&
39412115Sdyson		    (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
39512115Sdyson			slotoffset = -1;
39612115Sdyson			slotfreespace = 0;
39712115Sdyson		}
39812115Sdyson		/*
39912115Sdyson		 * Get pointer to next entry.
40012115Sdyson		 * Full validation checks are slow, so we only check
40112115Sdyson		 * enough to insure forward progress through the
40212115Sdyson		 * directory. Complete checks can be run by patching
40312115Sdyson		 * "dirchk" to be true.
40412115Sdyson		 */
40555477Sbde		ep = (struct ext2_dir_entry_2 *)
40612115Sdyson			((char *)bp->b_data + entryoffsetinblock);
40712115Sdyson		if (ep->rec_len == 0 ||
40812147Sdyson		    (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) {
40912115Sdyson			int i;
41012115Sdyson			ufs_dirbad(dp, dp->i_offset, "mangled entry");
41112115Sdyson			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
41212115Sdyson			dp->i_offset += i;
41312115Sdyson			entryoffsetinblock += i;
41412115Sdyson			continue;
41512115Sdyson		}
41612115Sdyson
41712115Sdyson		/*
41812115Sdyson		 * If an appropriate sized slot has not yet been found,
41912115Sdyson		 * check to see if one is available. Also accumulate space
42012115Sdyson		 * in the current block so that we can determine if
42112115Sdyson		 * compaction is viable.
42212115Sdyson		 */
42312115Sdyson		if (slotstatus != FOUND) {
42412115Sdyson			int size = ep->rec_len;
42512115Sdyson
42612115Sdyson			if (ep->inode != 0)
42712115Sdyson				size -= EXT2_DIR_REC_LEN(ep->name_len);
42812115Sdyson			if (size > 0) {
42912115Sdyson				if (size >= slotneeded) {
43012115Sdyson					slotstatus = FOUND;
43112115Sdyson					slotoffset = dp->i_offset;
43212115Sdyson					slotsize = ep->rec_len;
43312115Sdyson				} else if (slotstatus == NONE) {
43412115Sdyson					slotfreespace += size;
43512115Sdyson					if (slotoffset == -1)
43612115Sdyson						slotoffset = dp->i_offset;
43712115Sdyson					if (slotfreespace >= slotneeded) {
43812115Sdyson						slotstatus = COMPACT;
43912115Sdyson						slotsize = dp->i_offset +
44012115Sdyson						      ep->rec_len - slotoffset;
44112115Sdyson					}
44212115Sdyson				}
44312115Sdyson			}
44412115Sdyson		}
44512115Sdyson
44612115Sdyson		/*
44712115Sdyson		 * Check for a name match.
44812115Sdyson		 */
44912115Sdyson		if (ep->inode) {
45012115Sdyson			namlen = ep->name_len;
45112115Sdyson			if (namlen == cnp->cn_namelen &&
45212115Sdyson			    !bcmp(cnp->cn_nameptr, ep->name,
45312115Sdyson				(unsigned)namlen)) {
45412115Sdyson				/*
45512115Sdyson				 * Save directory entry's inode number and
45612115Sdyson				 * reclen in ndp->ni_ufs area, and release
45712115Sdyson				 * directory buffer.
45812115Sdyson				 */
45912115Sdyson				dp->i_ino = ep->inode;
46012115Sdyson				dp->i_reclen = ep->rec_len;
46112115Sdyson				brelse(bp);
46212115Sdyson				goto found;
46312115Sdyson			}
46412115Sdyson		}
46512115Sdyson		prevoff = dp->i_offset;
46612115Sdyson		dp->i_offset += ep->rec_len;
46712115Sdyson		entryoffsetinblock += ep->rec_len;
46812115Sdyson		if (ep->inode)
46912115Sdyson			enduseful = dp->i_offset;
47012115Sdyson	}
47112115Sdyson/* notfound: */
47212115Sdyson	/*
47312115Sdyson	 * If we started in the middle of the directory and failed
47412115Sdyson	 * to find our target, we must check the beginning as well.
47512115Sdyson	 */
47612115Sdyson	if (numdirpasses == 2) {
47712115Sdyson		numdirpasses--;
47812115Sdyson		dp->i_offset = 0;
47912115Sdyson		endsearch = dp->i_diroff;
48012115Sdyson		goto searchloop;
48112115Sdyson	}
48212115Sdyson	if (bp != NULL)
48312115Sdyson		brelse(bp);
48412115Sdyson	/*
48512115Sdyson	 * If creating, and at end of pathname and current
48612115Sdyson	 * directory has not been removed, then can consider
48712115Sdyson	 * allowing file to be created.
48812115Sdyson	 */
48912115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
49012115Sdyson	    (flags & ISLASTCN) && dp->i_nlink != 0) {
49112115Sdyson		/*
49212115Sdyson		 * Access for write is interpreted as allowing
49312115Sdyson		 * creation of files in the directory.
49412115Sdyson		 */
49543301Sdillon		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
49612115Sdyson			return (error);
49712115Sdyson		/*
49812115Sdyson		 * Return an indication of where the new directory
49912115Sdyson		 * entry should be put.  If we didn't find a slot,
50012115Sdyson		 * then set dp->i_count to 0 indicating
50112115Sdyson		 * that the new slot belongs at the end of the
50212115Sdyson		 * directory. If we found a slot, then the new entry
50312115Sdyson		 * can be put in the range from dp->i_offset to
50412115Sdyson		 * dp->i_offset + dp->i_count.
50512115Sdyson		 */
50612115Sdyson		if (slotstatus == NONE) {
50712115Sdyson			dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);
50812115Sdyson			dp->i_count = 0;
50912115Sdyson			enduseful = dp->i_offset;
51012115Sdyson		} else {
51112115Sdyson			dp->i_offset = slotoffset;
51212115Sdyson			dp->i_count = slotsize;
51312115Sdyson			if (enduseful < slotoffset + slotsize)
51412115Sdyson				enduseful = slotoffset + slotsize;
51512115Sdyson		}
51612115Sdyson		dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
51712115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
51812115Sdyson		/*
51912115Sdyson		 * We return with the directory locked, so that
52012115Sdyson		 * the parameters we set up above will still be
52112115Sdyson		 * valid if we actually decide to do a direnter().
52212115Sdyson		 * We return ni_vp == NULL to indicate that the entry
52312115Sdyson		 * does not currently exist; we leave a pointer to
52412115Sdyson		 * the (locked) directory inode in ndp->ni_dvp.
52512115Sdyson		 * The pathname buffer is saved so that the name
52612115Sdyson		 * can be obtained later.
52712115Sdyson		 *
52812115Sdyson		 * NB - if the directory is unlocked, then this
52912115Sdyson		 * information cannot be used.
53012115Sdyson		 */
53112115Sdyson		cnp->cn_flags |= SAVENAME;
53212115Sdyson		if (!lockparent)
53322521Sdyson			VOP_UNLOCK(vdp, 0, p);
53412115Sdyson		return (EJUSTRETURN);
53512115Sdyson	}
53612115Sdyson	/*
53712115Sdyson	 * Insert name into cache (as non-existent) if appropriate.
53812115Sdyson	 */
53912115Sdyson	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
54012115Sdyson		cache_enter(vdp, *vpp, cnp);
54112115Sdyson	return (ENOENT);
54212115Sdyson
54312115Sdysonfound:
54412115Sdyson	if (numdirpasses == 2)
54512115Sdyson		nchstats.ncs_pass2++;
54612115Sdyson	/*
54712115Sdyson	 * Check that directory length properly reflects presence
54812115Sdyson	 * of this entry.
54912115Sdyson	 */
55012115Sdyson	if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len)
55112115Sdyson		> dp->i_size) {
55212115Sdyson		ufs_dirbad(dp, dp->i_offset, "i_size too small");
55312115Sdyson		dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len);
55412115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
55512115Sdyson	}
55612115Sdyson
55712115Sdyson	/*
55812115Sdyson	 * Found component in pathname.
55912115Sdyson	 * If the final component of path name, save information
56012115Sdyson	 * in the cache as to where the entry was found.
56112115Sdyson	 */
56212115Sdyson	if ((flags & ISLASTCN) && nameiop == LOOKUP)
56312115Sdyson		dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
56412115Sdyson
56512115Sdyson	/*
56612115Sdyson	 * If deleting, and at end of pathname, return
56712115Sdyson	 * parameters which can be used to remove file.
56812115Sdyson	 * If the wantparent flag isn't set, we return only
56912115Sdyson	 * the directory (in ndp->ni_dvp), otherwise we go
57012115Sdyson	 * on and lock the inode, being careful with ".".
57112115Sdyson	 */
57212115Sdyson	if (nameiop == DELETE && (flags & ISLASTCN)) {
57312115Sdyson		/*
57412115Sdyson		 * Write access to directory required to delete files.
57512115Sdyson		 */
57643301Sdillon		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
57712115Sdyson			return (error);
57812115Sdyson		/*
57912115Sdyson		 * Return pointer to current entry in dp->i_offset,
58012115Sdyson		 * and distance past previous entry (if there
58112115Sdyson		 * is a previous entry in this block) in dp->i_count.
58212115Sdyson		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
58312115Sdyson		 */
58412115Sdyson		if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
58512115Sdyson			dp->i_count = 0;
58612115Sdyson		else
58712115Sdyson			dp->i_count = dp->i_offset - prevoff;
58812115Sdyson		if (dp->i_number == dp->i_ino) {
58912115Sdyson			VREF(vdp);
59012115Sdyson			*vpp = vdp;
59112115Sdyson			return (0);
59212115Sdyson		}
59343301Sdillon		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
59412115Sdyson			return (error);
59512115Sdyson		/*
59612115Sdyson		 * If directory is "sticky", then user must own
59712115Sdyson		 * the directory, or the file in it, else she
59812115Sdyson		 * may not delete it (unless she's root). This
59912115Sdyson		 * implements append-only directories.
60012115Sdyson		 */
60112115Sdyson		if ((dp->i_mode & ISVTX) &&
60212115Sdyson		    cred->cr_uid != 0 &&
60312115Sdyson		    cred->cr_uid != dp->i_uid &&
60412115Sdyson		    VTOI(tdp)->i_uid != cred->cr_uid) {
60512115Sdyson			vput(tdp);
60612115Sdyson			return (EPERM);
60712115Sdyson		}
60812115Sdyson		*vpp = tdp;
60912115Sdyson		if (!lockparent)
61022521Sdyson			VOP_UNLOCK(vdp, 0, p);
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	 */
62012115Sdyson	if (nameiop == RENAME && wantparent &&
62112115Sdyson	    (flags & ISLASTCN)) {
62243301Sdillon		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
62312115Sdyson			return (error);
62412115Sdyson		/*
62512115Sdyson		 * Careful about locking second inode.
62612115Sdyson		 * This can only occur if the target is ".".
62712115Sdyson		 */
62812115Sdyson		if (dp->i_number == dp->i_ino)
62912115Sdyson			return (EISDIR);
63043301Sdillon		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
63112115Sdyson			return (error);
63212115Sdyson		*vpp = tdp;
63312115Sdyson		cnp->cn_flags |= SAVENAME;
63412115Sdyson		if (!lockparent)
63522521Sdyson			VOP_UNLOCK(vdp, 0, p);
63612115Sdyson		return (0);
63712115Sdyson	}
63812115Sdyson
63912115Sdyson	/*
64012115Sdyson	 * Step through the translation in the name.  We do not `vput' the
64112115Sdyson	 * directory because we may need it again if a symbolic link
64212115Sdyson	 * is relative to the current directory.  Instead we save it
64312115Sdyson	 * unlocked as "pdp".  We must get the target inode before unlocking
64412115Sdyson	 * the directory to insure that the inode will not be removed
64512115Sdyson	 * before we get it.  We prevent deadlock by always fetching
64612115Sdyson	 * inodes from the root, moving down the directory tree. Thus
64712115Sdyson	 * when following backward pointers ".." we must unlock the
64812115Sdyson	 * parent directory before getting the requested directory.
64912115Sdyson	 * There is a potential race condition here if both the current
65012115Sdyson	 * and parent directories are removed before the VFS_VGET for the
65112115Sdyson	 * inode associated with ".." returns.  We hope that this occurs
65212115Sdyson	 * infrequently since we cannot avoid this race condition without
65312115Sdyson	 * implementing a sophisticated deadlock detection algorithm.
65412115Sdyson	 * Note also that this simple deadlock detection scheme will not
65512115Sdyson	 * work if the file system has any hard links other than ".."
65612115Sdyson	 * that point backwards in the directory structure.
65712115Sdyson	 */
65812115Sdyson	pdp = vdp;
65912115Sdyson	if (flags & ISDOTDOT) {
66022521Sdyson		VOP_UNLOCK(pdp, 0, p);	/* race to get the inode */
66143301Sdillon		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) {
66222521Sdyson			vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p);
66312115Sdyson			return (error);
66412115Sdyson		}
66512115Sdyson		if (lockparent && (flags & ISLASTCN) &&
66622521Sdyson		    (error = vn_lock(pdp, LK_EXCLUSIVE, p))) {
66712115Sdyson			vput(tdp);
66812115Sdyson			return (error);
66912115Sdyson		}
67012115Sdyson		*vpp = tdp;
67112115Sdyson	} else if (dp->i_number == dp->i_ino) {
67212115Sdyson		VREF(vdp);	/* we want ourself, ie "." */
67312115Sdyson		*vpp = vdp;
67412115Sdyson	} else {
67543301Sdillon		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
67612115Sdyson			return (error);
67712115Sdyson		if (!lockparent || !(flags & ISLASTCN))
67822521Sdyson			VOP_UNLOCK(pdp, 0, p);
67912115Sdyson		*vpp = tdp;
68012115Sdyson	}
68112115Sdyson
68212115Sdyson	/*
68312115Sdyson	 * Insert name into cache if appropriate.
68412115Sdyson	 */
68512115Sdyson	if (cnp->cn_flags & MAKEENTRY)
68612115Sdyson		cache_enter(vdp, *vpp, cnp);
68712115Sdyson	return (0);
68812115Sdyson}
68912115Sdyson
69012115Sdyson/*
69112115Sdyson * Do consistency checking on a directory entry:
69212115Sdyson *	record length must be multiple of 4
69312115Sdyson *	entry must fit in rest of its DIRBLKSIZ block
69412115Sdyson *	record must be large enough to contain entry
69512115Sdyson *	name is not longer than MAXNAMLEN
69612115Sdyson *	name must be as long as advertised, and null terminated
69712115Sdyson */
69812115Sdyson/*
69912115Sdyson *	changed so that it confirms to ext2_check_dir_entry
70012115Sdyson */
70112159Sbdestatic int
70212115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock)
70312115Sdyson	struct vnode *dp;
70455477Sbde	register struct ext2_dir_entry_2 *de;
70512115Sdyson	int entryoffsetinblock;
70612115Sdyson{
70712115Sdyson	int	DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize;
70812115Sdyson
70912115Sdyson        char * error_msg = NULL;
71012115Sdyson
71112115Sdyson        if (de->rec_len < EXT2_DIR_REC_LEN(1))
71212115Sdyson                error_msg = "rec_len is smaller than minimal";
71312115Sdyson        else if (de->rec_len % 4 != 0)
71412115Sdyson                error_msg = "rec_len % 4 != 0";
71512115Sdyson        else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
71612115Sdyson                error_msg = "reclen is too small for name_len";
71712115Sdyson        else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ)
71812115Sdyson                error_msg = "directory entry across blocks";
71912115Sdyson        /* else LATER
72012115Sdyson	     if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
72112115Sdyson                error_msg = "inode out of bounds";
72212115Sdyson	*/
72312115Sdyson
72424492Sbde        if (error_msg != NULL) {
72524492Sbde                printf("bad directory entry: %s\n", error_msg);
72637555Sbde                printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
72724492Sbde			entryoffsetinblock, (unsigned long)de->inode,
72824492Sbde			de->rec_len, de->name_len);
72924492Sbde        }
73012115Sdyson        return error_msg == NULL ? 0 : 1;
73112115Sdyson}
73212115Sdyson
73312115Sdyson/*
73412115Sdyson * Write a directory entry after a call to namei, using the parameters
73512115Sdyson * that it left in nameidata.  The argument ip is the inode which the new
73612115Sdyson * directory entry will refer to.  Dvp is a pointer to the directory to
73712115Sdyson * be written, which was left locked by namei. Remaining parameters
73812115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new
73912115Sdyson * entry is to be obtained.
74012115Sdyson */
74112115Sdysonint
74212115Sdysonext2_direnter(ip, dvp, cnp)
74312115Sdyson	struct inode *ip;
74412115Sdyson	struct vnode *dvp;
74512115Sdyson	register struct componentname *cnp;
74612115Sdyson{
74755477Sbde	register struct ext2_dir_entry_2 *ep, *nep;
74812115Sdyson	register struct inode *dp;
74912115Sdyson	struct buf *bp;
75055477Sbde	struct ext2_dir_entry_2 newdir;
75112115Sdyson	struct iovec aiov;
75212115Sdyson	struct uio auio;
75312115Sdyson	u_int dsize;
75412115Sdyson	int error, loc, newentrysize, spacefree;
75512115Sdyson	char *dirbuf;
75612115Sdyson	int     DIRBLKSIZ = ip->i_e2fs->s_blocksize;
75712115Sdyson
75812115Sdyson
75912115Sdyson#if DIAGNOSTIC
76012115Sdyson	if ((cnp->cn_flags & SAVENAME) == 0)
76112115Sdyson		panic("direnter: missing name");
76212115Sdyson#endif
76312115Sdyson	dp = VTOI(dvp);
76412115Sdyson	newdir.inode = ip->i_number;
76512115Sdyson	newdir.name_len = cnp->cn_namelen;
76655477Sbde	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
76755477Sbde	    EXT2_FEATURE_INCOMPAT_FILETYPE))
76855477Sbde		newdir.file_type = DTTOFT(IFTODT(ip->i_mode));
76955477Sbde	else
77055477Sbde		newdir.file_type = EXT2_FT_UNKNOWN;
77112115Sdyson	bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1);
77212115Sdyson	newentrysize = EXT2_DIR_REC_LEN(newdir.name_len);
77312115Sdyson	if (dp->i_count == 0) {
77412115Sdyson		/*
77512115Sdyson		 * If dp->i_count is 0, then namei could find no
77612115Sdyson		 * space in the directory. Here, dp->i_offset will
77712115Sdyson		 * be on a directory block boundary and we will write the
77812115Sdyson		 * new entry into a fresh block.
77912115Sdyson		 */
78012115Sdyson		if (dp->i_offset & (DIRBLKSIZ - 1))
78112115Sdyson			panic("ext2_direnter: newblk");
78212115Sdyson		auio.uio_offset = dp->i_offset;
78312115Sdyson		newdir.rec_len = DIRBLKSIZ;
78412115Sdyson		auio.uio_resid = newentrysize;
78512115Sdyson		aiov.iov_len = newentrysize;
78612115Sdyson		aiov.iov_base = (caddr_t)&newdir;
78712115Sdyson		auio.uio_iov = &aiov;
78812115Sdyson		auio.uio_iovcnt = 1;
78912115Sdyson		auio.uio_rw = UIO_WRITE;
79012115Sdyson		auio.uio_segflg = UIO_SYSSPACE;
79112115Sdyson		auio.uio_procp = (struct proc *)0;
79212115Sdyson		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
79312115Sdyson		if (DIRBLKSIZ >
79412115Sdyson		    VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
79512115Sdyson			/* XXX should grow with balloc() */
79612115Sdyson			panic("ext2_direnter: frag size");
79712115Sdyson		else if (!error) {
79812115Sdyson			dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
79912115Sdyson			dp->i_flag |= IN_CHANGE;
80012115Sdyson		}
80112115Sdyson		return (error);
80212115Sdyson	}
80312115Sdyson
80412115Sdyson	/*
80512115Sdyson	 * If dp->i_count is non-zero, then namei found space
80612115Sdyson	 * for the new entry in the range dp->i_offset to
80712115Sdyson	 * dp->i_offset + dp->i_count in the directory.
80812115Sdyson	 * To use this space, we may have to compact the entries located
80912115Sdyson	 * there, by copying them together towards the beginning of the
81012115Sdyson	 * block, leaving the free space in one usable chunk at the end.
81112115Sdyson	 */
81212115Sdyson
81312115Sdyson	/*
81412115Sdyson	 * Increase size of directory if entry eats into new space.
81512115Sdyson	 * This should never push the size past a new multiple of
81612115Sdyson	 * DIRBLKSIZE.
81712115Sdyson	 *
81812115Sdyson	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
81912115Sdyson	 */
82012115Sdyson	if (dp->i_offset + dp->i_count > dp->i_size)
82112115Sdyson		dp->i_size = dp->i_offset + dp->i_count;
82212115Sdyson	/*
82312115Sdyson	 * Get the block containing the space for the new directory entry.
82412115Sdyson	 */
82543301Sdillon	if ((error = UFS_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0)
82612115Sdyson		return (error);
82712115Sdyson	/*
82812115Sdyson	 * Find space for the new entry. In the simple case, the entry at
82912115Sdyson	 * offset base will have the space. If it does not, then namei
83012115Sdyson	 * arranged that compacting the region dp->i_offset to
83112115Sdyson	 * dp->i_offset + dp->i_count would yield the
83212115Sdyson	 * space.
83312115Sdyson	 */
83455477Sbde	ep = (struct ext2_dir_entry_2 *)dirbuf;
83512115Sdyson	dsize = EXT2_DIR_REC_LEN(ep->name_len);
83612115Sdyson	spacefree = ep->rec_len - dsize;
83712115Sdyson	for (loc = ep->rec_len; loc < dp->i_count; ) {
83855477Sbde		nep = (struct ext2_dir_entry_2 *)(dirbuf + loc);
83912115Sdyson		if (ep->inode) {
84012115Sdyson			/* trim the existing slot */
84112115Sdyson			ep->rec_len = dsize;
84255477Sbde			ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
84312115Sdyson		} else {
84412115Sdyson			/* overwrite; nothing there; header is ours */
84512115Sdyson			spacefree += dsize;
84612115Sdyson		}
84719541Sbde		dsize = EXT2_DIR_REC_LEN(nep->name_len);
84812115Sdyson		spacefree += nep->rec_len - dsize;
84912115Sdyson		loc += nep->rec_len;
85012115Sdyson		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
85112115Sdyson	}
85212115Sdyson	/*
85312115Sdyson	 * Update the pointer fields in the previous entry (if any),
85412115Sdyson	 * copy in the new entry, and write out the block.
85512115Sdyson	 */
85612115Sdyson	if (ep->inode == 0) {
85712115Sdyson		if (spacefree + dsize < newentrysize)
85812115Sdyson			panic("ext2_direnter: compact1");
85912115Sdyson		newdir.rec_len = spacefree + dsize;
86012115Sdyson	} else {
86112115Sdyson		if (spacefree < newentrysize)
86212115Sdyson			panic("ext2_direnter: compact2");
86312115Sdyson		newdir.rec_len = spacefree;
86412115Sdyson		ep->rec_len = dsize;
86555477Sbde		ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
86612115Sdyson	}
86712115Sdyson	bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
86847964Smckusick	error = VOP_BWRITE(bp->b_vp, bp);
86912115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
87012115Sdyson	if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
87130474Sphk		error = UFS_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC,
87212115Sdyson		    cnp->cn_cred, cnp->cn_proc);
87312115Sdyson	return (error);
87412115Sdyson}
87512115Sdyson
87612115Sdyson/*
87712115Sdyson * Remove a directory entry after a call to namei, using
87812115Sdyson * the parameters which it left in nameidata. The entry
87912115Sdyson * dp->i_offset contains the offset into the directory of the
88012115Sdyson * entry to be eliminated.  The dp->i_count field contains the
88112115Sdyson * size of the previous record in the directory.  If this
88212115Sdyson * is 0, the first entry is being deleted, so we need only
88312115Sdyson * zero the inode number to mark the entry as free.  If the
88412115Sdyson * entry is not the first in the directory, we must reclaim
88512115Sdyson * the space of the now empty record by adding the record size
88612115Sdyson * to the size of the previous entry.
88712115Sdyson */
88812115Sdysonint
88912115Sdysonext2_dirremove(dvp, cnp)
89012115Sdyson	struct vnode *dvp;
89112115Sdyson	struct componentname *cnp;
89212115Sdyson{
89312115Sdyson	register struct inode *dp;
89455477Sbde	struct ext2_dir_entry_2 *ep;
89512115Sdyson	struct buf *bp;
89612115Sdyson	int error;
89712115Sdyson
89812115Sdyson	dp = VTOI(dvp);
89912115Sdyson	if (dp->i_count == 0) {
90012115Sdyson		/*
90112115Sdyson		 * First entry in block: set d_ino to zero.
90212115Sdyson		 */
90343301Sdillon		if ((error =
90443301Sdillon		    UFS_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0)
90512115Sdyson			return (error);
90612115Sdyson		ep->inode = 0;
90747964Smckusick		error = VOP_BWRITE(bp->b_vp, bp);
90812115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
90912115Sdyson		return (error);
91012115Sdyson	}
91112115Sdyson	/*
91212115Sdyson	 * Collapse new free space into previous entry.
91312115Sdyson	 */
91443301Sdillon	if ((error = UFS_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count),
91543301Sdillon	    (char **)&ep, &bp)) != 0)
91612115Sdyson		return (error);
91712115Sdyson	ep->rec_len += dp->i_reclen;
91847964Smckusick	error = VOP_BWRITE(bp->b_vp, bp);
91912115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
92012115Sdyson	return (error);
92112115Sdyson}
92212115Sdyson
92312115Sdyson/*
92412115Sdyson * Rewrite an existing directory entry to point at the inode
92512115Sdyson * supplied.  The parameters describing the directory entry are
92612115Sdyson * set up by a call to namei.
92712115Sdyson */
92812115Sdysonint
92912115Sdysonext2_dirrewrite(dp, ip, cnp)
93012115Sdyson	struct inode *dp, *ip;
93112115Sdyson	struct componentname *cnp;
93212115Sdyson{
93312115Sdyson	struct buf *bp;
93455477Sbde	struct ext2_dir_entry_2 *ep;
93512115Sdyson	struct vnode *vdp = ITOV(dp);
93612115Sdyson	int error;
93712115Sdyson
93843301Sdillon	if ((error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0)
93912115Sdyson		return (error);
94012115Sdyson	ep->inode = ip->i_number;
94155477Sbde	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
94255477Sbde	    EXT2_FEATURE_INCOMPAT_FILETYPE))
94355477Sbde		ep->file_type = DTTOFT(IFTODT(ip->i_mode));
94455477Sbde	else
94555477Sbde		ep->file_type = EXT2_FT_UNKNOWN;
94647964Smckusick	error = VOP_BWRITE(bp->b_vp, bp);
94712115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
94812115Sdyson	return (error);
94912115Sdyson}
95012115Sdyson
95112115Sdyson/*
95212115Sdyson * Check if a directory is empty or not.
95312115Sdyson * Inode supplied must be locked.
95412115Sdyson *
95512115Sdyson * Using a struct dirtemplate here is not precisely
95612115Sdyson * what we want, but better than using a struct direct.
95712115Sdyson *
95812115Sdyson * NB: does not handle corrupted directories.
95912115Sdyson */
96012115Sdysonint
96112115Sdysonext2_dirempty(ip, parentino, cred)
96212115Sdyson	register struct inode *ip;
96312115Sdyson	ino_t parentino;
96412115Sdyson	struct ucred *cred;
96512115Sdyson{
96612115Sdyson	register off_t off;
96712115Sdyson	struct dirtemplate dbuf;
96855477Sbde	register struct ext2_dir_entry_2 *dp = (struct ext2_dir_entry_2 *)&dbuf;
96912115Sdyson	int error, count, namlen;
97012115Sdyson
97112115Sdyson#define	MINDIRSIZ (sizeof (struct dirtemplate) / 2)
97212115Sdyson
97312115Sdyson	for (off = 0; off < ip->i_size; off += dp->rec_len) {
97412115Sdyson		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
97512115Sdyson		   UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
97612115Sdyson		/*
97712115Sdyson		 * Since we read MINDIRSIZ, residual must
97812115Sdyson		 * be 0 unless we're at end of file.
97912115Sdyson		 */
98012115Sdyson		if (error || count != 0)
98112115Sdyson			return (0);
98212115Sdyson		/* avoid infinite loops */
98312115Sdyson		if (dp->rec_len == 0)
98412115Sdyson			return (0);
98512115Sdyson		/* skip empty entries */
98612115Sdyson		if (dp->inode == 0)
98712115Sdyson			continue;
98812115Sdyson		/* accept only "." and ".." */
98912115Sdyson		namlen = dp->name_len;
99012115Sdyson		if (namlen > 2)
99112115Sdyson			return (0);
99212115Sdyson		if (dp->name[0] != '.')
99312115Sdyson			return (0);
99412115Sdyson		/*
99512115Sdyson		 * At this point namlen must be 1 or 2.
99612115Sdyson		 * 1 implies ".", 2 implies ".." if second
99712115Sdyson		 * char is also "."
99812115Sdyson		 */
99912115Sdyson		if (namlen == 1)
100012115Sdyson			continue;
100112115Sdyson		if (dp->name[1] == '.' && dp->inode == parentino)
100212115Sdyson			continue;
100312115Sdyson		return (0);
100412115Sdyson	}
100512115Sdyson	return (1);
100612115Sdyson}
100712115Sdyson
100812115Sdyson/*
100912115Sdyson * Check if source directory is in the path of the target directory.
101012115Sdyson * Target is supplied locked, source is unlocked.
101112115Sdyson * The target is always vput before returning.
101212115Sdyson */
101312115Sdysonint
101412115Sdysonext2_checkpath(source, target, cred)
101512115Sdyson	struct inode *source, *target;
101612115Sdyson	struct ucred *cred;
101712115Sdyson{
101812115Sdyson	struct vnode *vp;
101912115Sdyson	int error, rootino, namlen;
102012115Sdyson	struct dirtemplate dirbuf;
102112115Sdyson
102212115Sdyson	vp = ITOV(target);
102312115Sdyson	if (target->i_number == source->i_number) {
102412115Sdyson		error = EEXIST;
102512115Sdyson		goto out;
102612115Sdyson	}
102712115Sdyson	rootino = ROOTINO;
102812115Sdyson	error = 0;
102912115Sdyson	if (target->i_number == rootino)
103012115Sdyson		goto out;
103112115Sdyson
103212115Sdyson	for (;;) {
103312115Sdyson		if (vp->v_type != VDIR) {
103412115Sdyson			error = ENOTDIR;
103512115Sdyson			break;
103612115Sdyson		}
103712115Sdyson		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
103812115Sdyson			sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
103912115Sdyson			IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
104012115Sdyson		if (error != 0)
104112115Sdyson			break;
104257710Sbde		namlen = dirbuf.dotdot_type;	/* like ufs little-endian */
104312115Sdyson		if (namlen != 2 ||
104412115Sdyson		    dirbuf.dotdot_name[0] != '.' ||
104512115Sdyson		    dirbuf.dotdot_name[1] != '.') {
104612115Sdyson			error = ENOTDIR;
104712115Sdyson			break;
104812115Sdyson		}
104912115Sdyson		if (dirbuf.dotdot_ino == source->i_number) {
105012115Sdyson			error = EINVAL;
105112115Sdyson			break;
105212115Sdyson		}
105312115Sdyson		if (dirbuf.dotdot_ino == rootino)
105412115Sdyson			break;
105512115Sdyson		vput(vp);
105643301Sdillon		if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) != 0) {
105712115Sdyson			vp = NULL;
105812115Sdyson			break;
105912115Sdyson		}
106012115Sdyson	}
106112115Sdyson
106212115Sdysonout:
106312115Sdyson	if (error == ENOTDIR)
106412115Sdyson		printf("checkpath: .. not a directory\n");
106512115Sdyson	if (vp != NULL)
106612115Sdyson		vput(vp);
106712115Sdyson	return (error);
106812115Sdyson}
106912115Sdyson
1070