ext2_lookup.c revision 247055
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: stable/9/sys/fs/ext2fs/ext2_lookup.c 247055 2013-02-20 20:56:07Z pfg $
4212115Sdyson */
4312115Sdyson
4412115Sdyson#include <sys/param.h>
4512159Sbde#include <sys/systm.h>
4612115Sdyson#include <sys/namei.h>
4760041Sphk#include <sys/bio.h>
4812115Sdyson#include <sys/buf.h>
49193377Sstas#include <sys/endian.h>
5012115Sdyson#include <sys/mount.h>
5112115Sdyson#include <sys/vnode.h>
5212115Sdyson#include <sys/malloc.h>
5312115Sdyson#include <sys/dirent.h>
5496753Siedowse#include <sys/sysctl.h>
5512115Sdyson
5612115Sdyson#include <ufs/ufs/dir.h>
5712115Sdyson
58202283Slulf#include <fs/ext2fs/inode.h>
59202283Slulf#include <fs/ext2fs/ext2_mount.h>
60202283Slulf#include <fs/ext2fs/ext2fs.h>
61221128Sjhb#include <fs/ext2fs/ext2_dinode.h>
62202283Slulf#include <fs/ext2fs/ext2_dir.h>
63221128Sjhb#include <fs/ext2fs/ext2_extern.h>
6412115Sdyson
6596753Siedowse#ifdef DIAGNOSTIC
6696753Siedowsestatic int dirchk = 1;
6796753Siedowse#else
6896753Siedowsestatic int dirchk = 0;
6996753Siedowse#endif
7096753Siedowse
71141633Sphkstatic SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem");
7296753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
7396753Siedowse
74111742Sdes/*
7512115Sdyson   DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
7612115Sdyson   while it is the native blocksize in ext2fs - thus, a #define
7712115Sdyson   is no longer appropriate
7812115Sdyson*/
7912115Sdyson#undef  DIRBLKSIZ
8012115Sdyson
8155477Sbdestatic u_char ext2_ft_to_dt[] = {
8255477Sbde	DT_UNKNOWN,		/* EXT2_FT_UNKNOWN */
8355477Sbde	DT_REG,			/* EXT2_FT_REG_FILE */
8455477Sbde	DT_DIR,			/* EXT2_FT_DIR */
8555477Sbde	DT_CHR,			/* EXT2_FT_CHRDEV */
8655477Sbde	DT_BLK,			/* EXT2_FT_BLKDEV */
8755477Sbde	DT_FIFO,		/* EXT2_FT_FIFO */
8855477Sbde	DT_SOCK,		/* EXT2_FT_SOCK */
8955477Sbde	DT_LNK,			/* EXT2_FT_SYMLINK */
9055477Sbde};
91247055Spfg#define	FTTODT(ft) \
92247055Spfg    ((ft) < nitems(ext2_ft_to_dt) ? ext2_ft_to_dt[(ft)] : DT_UNKNOWN)
9355477Sbde
9455477Sbdestatic u_char dt_to_ext2_ft[] = {
9555477Sbde	EXT2_FT_UNKNOWN,	/* DT_UNKNOWN */
9655477Sbde	EXT2_FT_FIFO,		/* DT_FIFO */
9755477Sbde	EXT2_FT_CHRDEV,		/* DT_CHR */
9855477Sbde	EXT2_FT_UNKNOWN,	/* unused */
9955477Sbde	EXT2_FT_DIR,		/* DT_DIR */
10055477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10155477Sbde	EXT2_FT_BLKDEV,		/* DT_BLK */
10255477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10355477Sbde	EXT2_FT_REG_FILE,	/* DT_REG */
10455477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10555477Sbde	EXT2_FT_SYMLINK,	/* DT_LNK */
10655477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10755477Sbde	EXT2_FT_SOCK,		/* DT_SOCK */
10855477Sbde	EXT2_FT_UNKNOWN,	/* unused */
10955477Sbde	EXT2_FT_UNKNOWN,	/* DT_WHT */
11055477Sbde};
111247055Spfg#define	DTTOFT(dt) \
112247055Spfg    ((dt) < nitems(dt_to_ext2_ft) ? dt_to_ext2_ft[(dt)] : EXT2_FT_UNKNOWN)
11355477Sbde
114202283Slulfstatic int	ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
11593014Sbde		    int entryoffsetinblock);
116235820Spfgstatic int	ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp,
117235820Spfg		    struct componentname *cnp, ino_t *dd_ino);
11812159Sbde
11912115Sdyson/*
12012115Sdyson * Vnode op for reading directories.
12112115Sdyson *
122221867Sjhb * This function has to convert directory entries from the on-disk
123221867Sjhb * format to the format defined by <sys/dirent.h>.  Unfortunately, the
124221867Sjhb * conversion will blow up some entries by four bytes, so it can't be
125221867Sjhb * done in place.  Instead, the conversion is done entry by entry and
126221867Sjhb * the converted entry is sent via uiomove.
12712115Sdyson *
12812115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send
12912115Sdyson * the whole buffer to uiomove
13012115Sdyson */
13112115Sdysonint
13212115Sdysonext2_readdir(ap)
133111742Sdes	struct vop_readdir_args /* {
134111742Sdes		struct vnode *a_vp;
135111742Sdes		struct uio *a_uio;
136111742Sdes		struct ucred *a_cred;
137111742Sdes	} */ *ap;
13812115Sdyson{
139111742Sdes	struct uio *uio = ap->a_uio;
140111742Sdes	int count, error;
14112115Sdyson
142202283Slulf	struct ext2fs_direct_2 *edp, *dp;
14324649Sdfr	int ncookies;
14412115Sdyson	struct dirent dstdp;
14512115Sdyson	struct uio auio;
14612115Sdyson	struct iovec aiov;
14712115Sdyson	caddr_t dirbuf;
148202283Slulf	int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize;
14912115Sdyson	int readcnt;
15065780Sbde	off_t startoffset = uio->uio_offset;
15112115Sdyson
15265780Sbde	count = uio->uio_resid;
15365780Sbde	/*
15465780Sbde	 * Avoid complications for partial directory entries by adjusting
15565780Sbde	 * the i/o to end at a block boundary.  Don't give up (like ufs
15665780Sbde	 * does) if the initial adjustment gives a negative count, since
15765780Sbde	 * many callers don't supply a large enough buffer.  The correct
15865780Sbde	 * size is a little larger than DIRBLKSIZ to allow for expansion
15965780Sbde	 * of directory entries, but some callers just use 512.
16065780Sbde	 */
16165780Sbde	count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
16265780Sbde	if (count <= 0)
16365780Sbde		count += DIRBLKSIZ;
16412115Sdyson	auio = *uio;
16512115Sdyson	auio.uio_iov = &aiov;
16612115Sdyson	auio.uio_iovcnt = 1;
16765780Sbde	auio.uio_resid = count;
16812115Sdyson	auio.uio_segflg = UIO_SYSSPACE;
16912115Sdyson	aiov.iov_len = count;
170184205Sdes	dirbuf = malloc(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;
175202283Slulf		edp = (struct ext2fs_direct_2 *)&dirbuf[readcnt];
17624649Sdfr		ncookies = 0;
17755477Sbde		bzero(&dstdp, offsetof(struct dirent, d_name));
178202283Slulf		for (dp = (struct ext2fs_direct_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
185125843Sbde			 *   follows the name length field.
18655477Sbde			 * - the file type is encoded in a different way.
18755477Sbde			 *
18855477Sbde			 * "Old" ext2fs directory entries need no special
189125843Sbde			 * conversions, since they are binary compatible
190125843Sbde			 * with "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,
193125843Sbde			 * because ext2fs uses a machine-independent disk
19455477Sbde			 * layout.
19555477Sbde			 */
196202283Slulf			dstdp.d_fileno = dp->e2d_ino;
197202283Slulf			dstdp.d_type = FTTODT(dp->e2d_type);
198202283Slulf			dstdp.d_namlen = dp->e2d_namlen;
19955477Sbde			dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
200202283Slulf			bcopy(dp->e2d_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
205202283Slulf			if (dp->e2d_reclen > 0) {
20612115Sdyson				if(dstdp.d_reclen <= uio->uio_resid) {
20712115Sdyson					/* advance dp */
208202283Slulf					dp = (struct ext2fs_direct_2 *)
209202283Slulf					    ((char *)dp + dp->e2d_reclen);
210111742Sdes					error =
211111741Sdes					  uiomove(&dstdp, dstdp.d_reclen, uio);
21224649Sdfr					if (!error)
21324649Sdfr						ncookies++;
21412115Sdyson				} else
21512115Sdyson					break;
21612115Sdyson			} else {
21712115Sdyson				error = EIO;
21812115Sdyson				break;
21912115Sdyson			}
22012115Sdyson		}
22112115Sdyson		/* we need to correct uio_offset */
22212115Sdyson		uio->uio_offset = startoffset + (caddr_t)dp - dirbuf;
22324649Sdfr
22424649Sdfr		if (!error && ap->a_ncookies != NULL) {
22565780Sbde			u_long *cookiep, *cookies, *ecookies;
22624649Sdfr			off_t off;
22724649Sdfr
22824649Sdfr			if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
229125843Sbde				panic("ext2_readdir: unexpected uio from NFS server");
230184205Sdes			cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
231111119Simp			       M_WAITOK);
23224649Sdfr			off = startoffset;
233202283Slulf			for (dp = (struct ext2fs_direct_2 *)dirbuf,
23465780Sbde			     cookiep = cookies, ecookies = cookies + ncookies;
23565780Sbde			     cookiep < ecookies;
236202283Slulf			     dp = (struct ext2fs_direct_2 *)((caddr_t) dp + dp->e2d_reclen)) {
237202283Slulf				off += dp->e2d_reclen;
23824649Sdfr				*cookiep++ = (u_long) off;
23924649Sdfr			}
24024649Sdfr			*ap->a_ncookies = ncookies;
24124649Sdfr			*ap->a_cookies = cookies;
24224649Sdfr		}
24312115Sdyson	}
244184205Sdes	free(dirbuf, M_TEMP);
24524649Sdfr	if (ap->a_eofflag)
24624649Sdfr		*ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
247111742Sdes	return (error);
24812115Sdyson}
24912115Sdyson
25012115Sdyson/*
25112115Sdyson * Convert a component of a pathname into a pointer to a locked inode.
25212115Sdyson * This is a very central and rather complicated routine.
25312115Sdyson * If the file system is not maintained in a strict tree hierarchy,
25412115Sdyson * this can result in a deadlock situation (see comments in code below).
25512115Sdyson *
25612115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
25712115Sdyson * on whether the name is to be looked up, created, renamed, or deleted.
25812115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in
25912115Sdyson * creating, renaming, or deleting a directory entry may be calculated.
26012115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname
26112115Sdyson * exists, lookup returns both the target and its parent directory locked.
26212115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may
26312115Sdyson * not be ".".  When deleting and LOCKPARENT is specified, the target may
26412115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput
26512115Sdyson * instead of two vputs.
26612115Sdyson *
267125843Sbde * Overall outline of ext2_lookup:
26812115Sdyson *
26912115Sdyson *	search for name in directory, to found or notfound
27012115Sdyson * notfound:
27112115Sdyson *	if creating, return locked directory, leaving info on available slots
27212115Sdyson *	else return error
27312115Sdyson * found:
27412115Sdyson *	if at end of path and deleting, return information to allow delete
27512115Sdyson *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
27612115Sdyson *	  inode and return info to allow rewrite
27712115Sdyson *	if not at end, add name to cache; if at end and neither creating
27812115Sdyson *	  nor deleting, add name to cache
27912115Sdyson */
28012115Sdysonint
28112115Sdysonext2_lookup(ap)
28228787Sphk	struct vop_cachedlookup_args /* {
28312115Sdyson		struct vnode *a_dvp;
28412115Sdyson		struct vnode **a_vpp;
28512115Sdyson		struct componentname *a_cnp;
28612115Sdyson	} */ *ap;
28712115Sdyson{
288235820Spfg
289235820Spfg	return (ext2_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL));
290235820Spfg}
291235820Spfg
292235820Spfgstatic int
293235820Spfgext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
294235820Spfg    ino_t *dd_ino)
295235820Spfg{
29696752Siedowse	struct inode *dp;		/* inode for directory being searched */
29712115Sdyson	struct buf *bp;			/* a buffer of directory entries */
298202283Slulf	struct ext2fs_direct_2 *ep;	/* the current directory entry */
29912115Sdyson	int entryoffsetinblock;		/* offset of ep in bp's buffer */
30012115Sdyson	enum {NONE, COMPACT, FOUND} slotstatus;
30112115Sdyson	doff_t slotoffset;		/* offset of area with free space */
30212115Sdyson	int slotsize;			/* size of area at slotoffset */
303202283Slulf	doff_t i_diroff;		/* cached i_diroff value */
304202283Slulf	doff_t i_offset;		/* cached i_offset value */
30512115Sdyson	int slotfreespace;		/* amount of space free in slot */
30612115Sdyson	int slotneeded;			/* size of the entry we're seeking */
30712115Sdyson	int numdirpasses;		/* strategy for directory search */
30812115Sdyson	doff_t endsearch;		/* offset to end directory search */
30912115Sdyson	doff_t prevoff;			/* prev entry dp->i_offset */
31012115Sdyson	struct vnode *pdp;		/* saved dp during symlink work */
31112115Sdyson	struct vnode *tdp;		/* returned by VFS_VGET */
31212115Sdyson	doff_t enduseful;		/* pointer past last used dir slot */
31312115Sdyson	u_long bmask;			/* block offset mask */
31412115Sdyson	int namlen, error;
31512115Sdyson	struct ucred *cred = cnp->cn_cred;
31612115Sdyson	int flags = cnp->cn_flags;
31712115Sdyson	int nameiop = cnp->cn_nameiop;
318235820Spfg	ino_t ino, ino1;
319202283Slulf	int ltype;
32012115Sdyson
321235820Spfg	int	DIRBLKSIZ = VTOI(vdp)->i_e2fs->e2fs_bsize;
32212115Sdyson
323235820Spfg	if (vpp != NULL)
324235820Spfg		*vpp = NULL;
325235820Spfg
326235820Spfg	dp = VTOI(vdp);
327235820Spfg	bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
328235820Spfgrestart:
32912115Sdyson	bp = NULL;
33012115Sdyson	slotoffset = -1;
331217584Sjhb
33212115Sdyson	/*
33312115Sdyson	 * We now have a segment name to search for, and a directory to search.
33412115Sdyson	 */
33512115Sdyson
33612115Sdyson	/*
33712115Sdyson	 * Suppress search for slots unless creating
33812115Sdyson	 * file and at end of pathname, in which case
33912115Sdyson	 * we watch for a place to put the new file in
34012115Sdyson	 * case it doesn't already exist.
34112115Sdyson	 */
342202283Slulf	ino = 0;
343202283Slulf	i_diroff = dp->i_diroff;
34412115Sdyson	slotstatus = FOUND;
34512115Sdyson	slotfreespace = slotsize = slotneeded = 0;
34612115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
34712115Sdyson	    (flags & ISLASTCN)) {
34812115Sdyson		slotstatus = NONE;
349111742Sdes		slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen);
35012115Sdyson		/* was
35112115Sdyson		slotneeded = (sizeof(struct direct) - MAXNAMLEN +
35212115Sdyson			cnp->cn_namelen + 3) &~ 3; */
35312115Sdyson	}
35412115Sdyson
35512115Sdyson	/*
35612115Sdyson	 * If there is cached information on a previous search of
35712115Sdyson	 * this directory, pick up where we last left off.
35812115Sdyson	 * We cache only lookups as these are the most common
35912115Sdyson	 * and have the greatest payoff. Caching CREATE has little
36012115Sdyson	 * benefit as it usually must search the entire directory
36112115Sdyson	 * to determine that the entry does not exist. Caching the
36212115Sdyson	 * location of the last DELETE or RENAME has not reduced
36312115Sdyson	 * profiling time and hence has been removed in the interest
36412115Sdyson	 * of simplicity.
36512115Sdyson	 */
366202283Slulf	if (nameiop != LOOKUP || i_diroff == 0 ||
367202283Slulf	    i_diroff > dp->i_size) {
36812115Sdyson		entryoffsetinblock = 0;
369202283Slulf		i_offset = 0;
37012115Sdyson		numdirpasses = 1;
37112115Sdyson	} else {
372202283Slulf		i_offset = i_diroff;
373202283Slulf		if ((entryoffsetinblock = i_offset & bmask) &&
374202283Slulf		    (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL,
37596749Siedowse		    &bp)))
37612115Sdyson			return (error);
37712115Sdyson		numdirpasses = 2;
37812115Sdyson		nchstats.ncs_2passes++;
37912115Sdyson	}
380202283Slulf	prevoff = i_offset;
381202283Slulf	endsearch = roundup2(dp->i_size, DIRBLKSIZ);
38212115Sdyson	enduseful = 0;
38312115Sdyson
38412115Sdysonsearchloop:
385202283Slulf	while (i_offset < endsearch) {
38612115Sdyson		/*
38712115Sdyson		 * If necessary, get the next directory block.
38812115Sdyson		 */
389202283Slulf		if ((i_offset & bmask) == 0) {
39012115Sdyson			if (bp != NULL)
39112115Sdyson				brelse(bp);
39243301Sdillon			if ((error =
393202283Slulf			    ext2_blkatoff(vdp, (off_t)i_offset, NULL,
39496749Siedowse			    &bp)) != 0)
39512115Sdyson				return (error);
39612115Sdyson			entryoffsetinblock = 0;
39712115Sdyson		}
39812115Sdyson		/*
39912115Sdyson		 * If still looking for a slot, and at a DIRBLKSIZE
40012115Sdyson		 * boundary, have to start looking for free space again.
40112115Sdyson		 */
40212115Sdyson		if (slotstatus == NONE &&
40312115Sdyson		    (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
40412115Sdyson			slotoffset = -1;
40512115Sdyson			slotfreespace = 0;
40612115Sdyson		}
40712115Sdyson		/*
40812115Sdyson		 * Get pointer to next entry.
40912115Sdyson		 * Full validation checks are slow, so we only check
41012115Sdyson		 * enough to insure forward progress through the
41196753Siedowse		 * directory. Complete checks can be run by setting
41296753Siedowse		 * "vfs.e2fs.dirchk" to be true.
41312115Sdyson		 */
414202283Slulf		ep = (struct ext2fs_direct_2 *)
41512115Sdyson			((char *)bp->b_data + entryoffsetinblock);
416202283Slulf		if (ep->e2d_reclen == 0 ||
41712147Sdyson		    (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) {
41812115Sdyson			int i;
419202283Slulf			ext2_dirbad(dp, i_offset, "mangled entry");
42012115Sdyson			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
421202283Slulf			i_offset += i;
42212115Sdyson			entryoffsetinblock += i;
42312115Sdyson			continue;
42412115Sdyson		}
42512115Sdyson
42612115Sdyson		/*
42712115Sdyson		 * If an appropriate sized slot has not yet been found,
42812115Sdyson		 * check to see if one is available. Also accumulate space
42912115Sdyson		 * in the current block so that we can determine if
43012115Sdyson		 * compaction is viable.
43112115Sdyson		 */
43212115Sdyson		if (slotstatus != FOUND) {
433202283Slulf			int size = ep->e2d_reclen;
43412115Sdyson
435202283Slulf			if (ep->e2d_ino != 0)
436202283Slulf				size -= EXT2_DIR_REC_LEN(ep->e2d_namlen);
43712115Sdyson			if (size > 0) {
43812115Sdyson				if (size >= slotneeded) {
43912115Sdyson					slotstatus = FOUND;
440202283Slulf					slotoffset = i_offset;
441202283Slulf					slotsize = ep->e2d_reclen;
44212115Sdyson				} else if (slotstatus == NONE) {
44312115Sdyson					slotfreespace += size;
44412115Sdyson					if (slotoffset == -1)
445202283Slulf						slotoffset = i_offset;
44612115Sdyson					if (slotfreespace >= slotneeded) {
44712115Sdyson						slotstatus = COMPACT;
448202283Slulf						slotsize = i_offset +
449202283Slulf						      ep->e2d_reclen - slotoffset;
45012115Sdyson					}
45112115Sdyson				}
45212115Sdyson			}
45312115Sdyson		}
45412115Sdyson
45512115Sdyson		/*
45612115Sdyson		 * Check for a name match.
45712115Sdyson		 */
458202283Slulf		if (ep->e2d_ino) {
459202283Slulf			namlen = ep->e2d_namlen;
46012115Sdyson			if (namlen == cnp->cn_namelen &&
461202283Slulf			    !bcmp(cnp->cn_nameptr, ep->e2d_name,
46212115Sdyson				(unsigned)namlen)) {
46312115Sdyson				/*
46412115Sdyson				 * Save directory entry's inode number and
46512115Sdyson				 * reclen in ndp->ni_ufs area, and release
46612115Sdyson				 * directory buffer.
46712115Sdyson				 */
468202283Slulf				ino = ep->e2d_ino;
46912115Sdyson				goto found;
47012115Sdyson			}
47112115Sdyson		}
472202283Slulf		prevoff = i_offset;
473202283Slulf		i_offset += ep->e2d_reclen;
474202283Slulf		entryoffsetinblock += ep->e2d_reclen;
475202283Slulf		if (ep->e2d_ino)
476202283Slulf			enduseful = i_offset;
47712115Sdyson	}
47812115Sdyson/* notfound: */
47912115Sdyson	/*
48012115Sdyson	 * If we started in the middle of the directory and failed
48112115Sdyson	 * to find our target, we must check the beginning as well.
48212115Sdyson	 */
48312115Sdyson	if (numdirpasses == 2) {
48412115Sdyson		numdirpasses--;
485202283Slulf		i_offset = 0;
486202283Slulf		endsearch = i_diroff;
48712115Sdyson		goto searchloop;
48812115Sdyson	}
489202283Slulf	dp->i_offset = i_offset;
49012115Sdyson	if (bp != NULL)
49112115Sdyson		brelse(bp);
49212115Sdyson	/*
49312115Sdyson	 * If creating, and at end of pathname and current
49412115Sdyson	 * directory has not been removed, then can consider
49512115Sdyson	 * allowing file to be created.
49612115Sdyson	 */
49712115Sdyson	if ((nameiop == CREATE || nameiop == RENAME) &&
49812115Sdyson	    (flags & ISLASTCN) && dp->i_nlink != 0) {
49912115Sdyson		/*
50012115Sdyson		 * Access for write is interpreted as allowing
50112115Sdyson		 * creation of files in the directory.
50212115Sdyson		 */
50383366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
50412115Sdyson			return (error);
50512115Sdyson		/*
50612115Sdyson		 * Return an indication of where the new directory
50712115Sdyson		 * entry should be put.  If we didn't find a slot,
50812115Sdyson		 * then set dp->i_count to 0 indicating
50912115Sdyson		 * that the new slot belongs at the end of the
51012115Sdyson		 * directory. If we found a slot, then the new entry
51112115Sdyson		 * can be put in the range from dp->i_offset to
51212115Sdyson		 * dp->i_offset + dp->i_count.
51312115Sdyson		 */
51412115Sdyson		if (slotstatus == NONE) {
515202283Slulf			dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ);
51612115Sdyson			dp->i_count = 0;
51712115Sdyson			enduseful = dp->i_offset;
51812115Sdyson		} else {
51912115Sdyson			dp->i_offset = slotoffset;
52012115Sdyson			dp->i_count = slotsize;
52112115Sdyson			if (enduseful < slotoffset + slotsize)
52212115Sdyson				enduseful = slotoffset + slotsize;
52312115Sdyson		}
524202283Slulf		dp->i_endoff = roundup2(enduseful, DIRBLKSIZ);
52512115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
52612115Sdyson		/*
52712115Sdyson		 * We return with the directory locked, so that
52812115Sdyson		 * the parameters we set up above will still be
52912115Sdyson		 * valid if we actually decide to do a direnter().
53012115Sdyson		 * We return ni_vp == NULL to indicate that the entry
53112115Sdyson		 * does not currently exist; we leave a pointer to
53212115Sdyson		 * the (locked) directory inode in ndp->ni_dvp.
53312115Sdyson		 * The pathname buffer is saved so that the name
53412115Sdyson		 * can be obtained later.
53512115Sdyson		 *
53612115Sdyson		 * NB - if the directory is unlocked, then this
53712115Sdyson		 * information cannot be used.
53812115Sdyson		 */
53912115Sdyson		cnp->cn_flags |= SAVENAME;
54012115Sdyson		return (EJUSTRETURN);
54112115Sdyson	}
54212115Sdyson	/*
54312115Sdyson	 * Insert name into cache (as non-existent) if appropriate.
54412115Sdyson	 */
54512115Sdyson	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
546235820Spfg		cache_enter(vdp, NULL, cnp);
54712115Sdyson	return (ENOENT);
54812115Sdyson
54912115Sdysonfound:
550235820Spfg	if (dd_ino != NULL)
551235820Spfg		*dd_ino = ino;
55212115Sdyson	if (numdirpasses == 2)
55312115Sdyson		nchstats.ncs_pass2++;
55412115Sdyson	/*
55512115Sdyson	 * Check that directory length properly reflects presence
55612115Sdyson	 * of this entry.
55712115Sdyson	 */
558202283Slulf	if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen)
55912115Sdyson		> dp->i_size) {
560202283Slulf		ext2_dirbad(dp, i_offset, "i_size too small");
561202283Slulf		dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->e2d_namlen);
56212115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
56312115Sdyson	}
564105420Sbde	brelse(bp);
56512115Sdyson
56612115Sdyson	/*
56712115Sdyson	 * Found component in pathname.
56812115Sdyson	 * If the final component of path name, save information
56912115Sdyson	 * in the cache as to where the entry was found.
57012115Sdyson	 */
57112115Sdyson	if ((flags & ISLASTCN) && nameiop == LOOKUP)
572202283Slulf		dp->i_diroff = i_offset &~ (DIRBLKSIZ - 1);
573202283Slulf	dp->i_offset = i_offset;
57412115Sdyson	/*
57512115Sdyson	 * If deleting, and at end of pathname, return
57612115Sdyson	 * parameters which can be used to remove file.
57712115Sdyson	 */
57812115Sdyson	if (nameiop == DELETE && (flags & ISLASTCN)) {
57912115Sdyson		/*
58012115Sdyson		 * Write access to directory required to delete files.
58112115Sdyson		 */
58283366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
58312115Sdyson			return (error);
58412115Sdyson		/*
58512115Sdyson		 * Return pointer to current entry in dp->i_offset,
58612115Sdyson		 * and distance past previous entry (if there
58712115Sdyson		 * is a previous entry in this block) in dp->i_count.
58812115Sdyson		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
58912115Sdyson		 */
59012115Sdyson		if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
59112115Sdyson			dp->i_count = 0;
59212115Sdyson		else
59312115Sdyson			dp->i_count = dp->i_offset - prevoff;
594235820Spfg		if (dd_ino != NULL)
595235820Spfg			return (0);
596202283Slulf		if (dp->i_number == ino) {
59712115Sdyson			VREF(vdp);
59812115Sdyson			*vpp = vdp;
59912115Sdyson			return (0);
60012115Sdyson		}
601202283Slulf		if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
60292462Smckusick		    &tdp)) != 0)
60312115Sdyson			return (error);
60412115Sdyson		/*
60512115Sdyson		 * If directory is "sticky", then user must own
60612115Sdyson		 * the directory, or the file in it, else she
60712115Sdyson		 * may not delete it (unless she's root). This
60812115Sdyson		 * implements append-only directories.
60912115Sdyson		 */
61012115Sdyson		if ((dp->i_mode & ISVTX) &&
61112115Sdyson		    cred->cr_uid != 0 &&
61212115Sdyson		    cred->cr_uid != dp->i_uid &&
61312115Sdyson		    VTOI(tdp)->i_uid != cred->cr_uid) {
61412115Sdyson			vput(tdp);
61512115Sdyson			return (EPERM);
61612115Sdyson		}
61712115Sdyson		*vpp = tdp;
61812115Sdyson		return (0);
61912115Sdyson	}
62012115Sdyson
62112115Sdyson	/*
62212115Sdyson	 * If rewriting (RENAME), return the inode and the
62312115Sdyson	 * information required to rewrite the present directory
62412115Sdyson	 * Must get inode of directory entry to verify it's a
62512115Sdyson	 * regular file, or empty directory.
62612115Sdyson	 */
627144299Sjeff	if (nameiop == RENAME && (flags & ISLASTCN)) {
62883366Sjulian		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
62912115Sdyson			return (error);
63012115Sdyson		/*
63112115Sdyson		 * Careful about locking second inode.
63212115Sdyson		 * This can only occur if the target is ".".
63312115Sdyson		 */
634202283Slulf		if (dp->i_number == ino)
63512115Sdyson			return (EISDIR);
636235820Spfg		if (dd_ino != NULL)
637235820Spfg			return (0);
638202283Slulf		if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
63992462Smckusick		    &tdp)) != 0)
64012115Sdyson			return (error);
64112115Sdyson		*vpp = tdp;
64212115Sdyson		cnp->cn_flags |= SAVENAME;
64312115Sdyson		return (0);
64412115Sdyson	}
645235820Spfg	if (dd_ino != NULL)
646235820Spfg		return (0);
64712115Sdyson
64812115Sdyson	/*
64912115Sdyson	 * Step through the translation in the name.  We do not `vput' the
65012115Sdyson	 * directory because we may need it again if a symbolic link
65112115Sdyson	 * is relative to the current directory.  Instead we save it
65212115Sdyson	 * unlocked as "pdp".  We must get the target inode before unlocking
65312115Sdyson	 * the directory to insure that the inode will not be removed
65412115Sdyson	 * before we get it.  We prevent deadlock by always fetching
65512115Sdyson	 * inodes from the root, moving down the directory tree. Thus
65612115Sdyson	 * when following backward pointers ".." we must unlock the
65712115Sdyson	 * parent directory before getting the requested directory.
65812115Sdyson	 * There is a potential race condition here if both the current
65912115Sdyson	 * and parent directories are removed before the VFS_VGET for the
66012115Sdyson	 * inode associated with ".." returns.  We hope that this occurs
66112115Sdyson	 * infrequently since we cannot avoid this race condition without
66212115Sdyson	 * implementing a sophisticated deadlock detection algorithm.
66312115Sdyson	 * Note also that this simple deadlock detection scheme will not
66412115Sdyson	 * work if the file system has any hard links other than ".."
66512115Sdyson	 * that point backwards in the directory structure.
66612115Sdyson	 */
66712115Sdyson	pdp = vdp;
66812115Sdyson	if (flags & ISDOTDOT) {
669202283Slulf		ltype = VOP_ISLOCKED(pdp);
670175294Sattilio		VOP_UNLOCK(pdp, 0);	/* race to get the inode */
671202283Slulf		error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, &tdp);
672202283Slulf		vn_lock(pdp, ltype | LK_RETRY);
673235820Spfg		if (pdp->v_iflag & VI_DOOMED) {
674235820Spfg			if (error == 0)
675235820Spfg				vput(tdp);
676235820Spfg			error = ENOENT;
677235820Spfg		}
678235820Spfg		if (error)
67912115Sdyson			return (error);
680235820Spfg		/*
681235820Spfg		 * Recheck that ".." entry in the vdp directory points
682235820Spfg		 * to the inode we looked up before vdp lock was
683235820Spfg		 * dropped.
684235820Spfg		 */
685235820Spfg		error = ext2_lookup_ino(pdp, NULL, cnp, &ino1);
686235820Spfg		if (error) {
687235820Spfg			vput(tdp);
688235820Spfg			return (error);
689235820Spfg		}
690235820Spfg		if (ino1 != ino) {
691235820Spfg			vput(tdp);
692235820Spfg			goto restart;
693235820Spfg		}
69412115Sdyson		*vpp = tdp;
695202283Slulf	} else if (dp->i_number == ino) {
69612115Sdyson		VREF(vdp);	/* we want ourself, ie "." */
697202283Slulf		/*
698202283Slulf		 * When we lookup "." we still can be asked to lock it
699202283Slulf		 * differently.
700202283Slulf		 */
701202283Slulf		ltype = cnp->cn_lkflags & LK_TYPE_MASK;
702202283Slulf		if (ltype != VOP_ISLOCKED(vdp)) {
703202283Slulf			if (ltype == LK_EXCLUSIVE)
704202283Slulf				vn_lock(vdp, LK_UPGRADE | LK_RETRY);
705202283Slulf			else /* if (ltype == LK_SHARED) */
706202283Slulf				vn_lock(vdp, LK_DOWNGRADE | LK_RETRY);
707202283Slulf		}
70812115Sdyson		*vpp = vdp;
70912115Sdyson	} else {
710202283Slulf		if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags,
71192462Smckusick		    &tdp)) != 0)
71212115Sdyson			return (error);
71312115Sdyson		*vpp = tdp;
71412115Sdyson	}
71512115Sdyson
71612115Sdyson	/*
71712115Sdyson	 * Insert name into cache if appropriate.
71812115Sdyson	 */
71912115Sdyson	if (cnp->cn_flags & MAKEENTRY)
72012115Sdyson		cache_enter(vdp, *vpp, cnp);
72112115Sdyson	return (0);
72212115Sdyson}
72312115Sdyson
72496749Siedowsevoid
72596749Siedowseext2_dirbad(ip, offset, how)
72696749Siedowse	struct inode *ip;
72796749Siedowse	doff_t offset;
72896749Siedowse	char *how;
72996749Siedowse{
73096749Siedowse	struct mount *mp;
73196749Siedowse
73296749Siedowse	mp = ITOV(ip)->v_mount;
733202283Slulf	if ((mp->mnt_flag & MNT_RDONLY) == 0)
734202283Slulf		panic("ext2_dirbad: %s: bad dir ino %lu at offset %ld: %s\n",
735202283Slulf			mp->mnt_stat.f_mntonname, (u_long)ip->i_number,(long)offset, how);
736202283Slulf	else
73796749Siedowse	(void)printf("%s: bad dir ino %lu at offset %ld: %s\n",
738202283Slulf            mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how);
739202283Slulf
74096749Siedowse}
74196749Siedowse
74212115Sdyson/*
74312115Sdyson * Do consistency checking on a directory entry:
74412115Sdyson *	record length must be multiple of 4
74512115Sdyson *	entry must fit in rest of its DIRBLKSIZ block
74612115Sdyson *	record must be large enough to contain entry
74712115Sdyson *	name is not longer than MAXNAMLEN
74812115Sdyson *	name must be as long as advertised, and null terminated
74912115Sdyson */
75012115Sdyson/*
75112115Sdyson *	changed so that it confirms to ext2_check_dir_entry
75212115Sdyson */
75312159Sbdestatic int
75412115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock)
75512115Sdyson	struct vnode *dp;
756202283Slulf	struct ext2fs_direct_2 *de;
75712115Sdyson	int entryoffsetinblock;
75812115Sdyson{
759202283Slulf	int	DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize;
76012115Sdyson
761111742Sdes	char * error_msg = NULL;
76212115Sdyson
763202283Slulf	if (de->e2d_reclen < EXT2_DIR_REC_LEN(1))
764111742Sdes		error_msg = "rec_len is smaller than minimal";
765202283Slulf	else if (de->e2d_reclen % 4 != 0)
766111742Sdes		error_msg = "rec_len % 4 != 0";
767202283Slulf	else if (de->e2d_reclen < EXT2_DIR_REC_LEN(de->e2d_namlen))
768111742Sdes		error_msg = "reclen is too small for name_len";
769202283Slulf	else if (entryoffsetinblock + de->e2d_reclen > DIRBLKSIZ)
770111742Sdes		error_msg = "directory entry across blocks";
771111742Sdes	/* else LATER
77212115Sdyson	     if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
773111742Sdes		error_msg = "inode out of bounds";
77412115Sdyson	*/
77512115Sdyson
776111742Sdes	if (error_msg != NULL) {
777111742Sdes		printf("bad directory entry: %s\n", error_msg);
778111742Sdes		printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
779202283Slulf			entryoffsetinblock, (unsigned long)de->e2d_ino,
780202283Slulf			de->e2d_reclen, de->e2d_namlen);
781111742Sdes	}
782111742Sdes	return error_msg == NULL ? 0 : 1;
78312115Sdyson}
78412115Sdyson
78512115Sdyson/*
78612115Sdyson * Write a directory entry after a call to namei, using the parameters
78712115Sdyson * that it left in nameidata.  The argument ip is the inode which the new
78812115Sdyson * directory entry will refer to.  Dvp is a pointer to the directory to
78912115Sdyson * be written, which was left locked by namei. Remaining parameters
79012115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new
79112115Sdyson * entry is to be obtained.
79212115Sdyson */
79312115Sdysonint
79412115Sdysonext2_direnter(ip, dvp, cnp)
79512115Sdyson	struct inode *ip;
79612115Sdyson	struct vnode *dvp;
79796752Siedowse	struct componentname *cnp;
79812115Sdyson{
799202283Slulf	struct ext2fs_direct_2 *ep, *nep;
80096752Siedowse	struct inode *dp;
80112115Sdyson	struct buf *bp;
802202283Slulf	struct ext2fs_direct_2 newdir;
80312115Sdyson	struct iovec aiov;
80412115Sdyson	struct uio auio;
80512115Sdyson	u_int dsize;
80612115Sdyson	int error, loc, newentrysize, spacefree;
80712115Sdyson	char *dirbuf;
808202283Slulf	int     DIRBLKSIZ = ip->i_e2fs->e2fs_bsize;
80912115Sdyson
81012115Sdyson
811153110Sru#ifdef DIAGNOSTIC
81212115Sdyson	if ((cnp->cn_flags & SAVENAME) == 0)
813246892Spfg		panic("ext2_direnter: missing name");
81412115Sdyson#endif
81512115Sdyson	dp = VTOI(dvp);
816202283Slulf	newdir.e2d_ino = ip->i_number;
817202283Slulf	newdir.e2d_namlen = cnp->cn_namelen;
818193377Sstas	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
819202283Slulf	    EXT2F_INCOMPAT_FTYPE))
820202283Slulf		newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode));
82155477Sbde	else
822202283Slulf		newdir.e2d_type = EXT2_FT_UNKNOWN;
823202283Slulf	bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1);
824202283Slulf	newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen);
82512115Sdyson	if (dp->i_count == 0) {
82612115Sdyson		/*
82712115Sdyson		 * If dp->i_count is 0, then namei could find no
82812115Sdyson		 * space in the directory. Here, dp->i_offset will
82912115Sdyson		 * be on a directory block boundary and we will write the
83012115Sdyson		 * new entry into a fresh block.
83112115Sdyson		 */
83212115Sdyson		if (dp->i_offset & (DIRBLKSIZ - 1))
83312115Sdyson			panic("ext2_direnter: newblk");
83412115Sdyson		auio.uio_offset = dp->i_offset;
835202283Slulf		newdir.e2d_reclen = DIRBLKSIZ;
83612115Sdyson		auio.uio_resid = newentrysize;
83712115Sdyson		aiov.iov_len = newentrysize;
83812115Sdyson		aiov.iov_base = (caddr_t)&newdir;
83912115Sdyson		auio.uio_iov = &aiov;
84012115Sdyson		auio.uio_iovcnt = 1;
84112115Sdyson		auio.uio_rw = UIO_WRITE;
84212115Sdyson		auio.uio_segflg = UIO_SYSSPACE;
84383366Sjulian		auio.uio_td = (struct thread *)0;
84412115Sdyson		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
84512115Sdyson		if (DIRBLKSIZ >
84696749Siedowse		    VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
84712115Sdyson			/* XXX should grow with balloc() */
84812115Sdyson			panic("ext2_direnter: frag size");
84912115Sdyson		else if (!error) {
850202283Slulf			dp->i_size = roundup2(dp->i_size, DIRBLKSIZ);
85112115Sdyson			dp->i_flag |= IN_CHANGE;
85212115Sdyson		}
85312115Sdyson		return (error);
85412115Sdyson	}
85512115Sdyson
85612115Sdyson	/*
85712115Sdyson	 * If dp->i_count is non-zero, then namei found space
85812115Sdyson	 * for the new entry in the range dp->i_offset to
85912115Sdyson	 * dp->i_offset + dp->i_count in the directory.
86012115Sdyson	 * To use this space, we may have to compact the entries located
86112115Sdyson	 * there, by copying them together towards the beginning of the
86212115Sdyson	 * block, leaving the free space in one usable chunk at the end.
86312115Sdyson	 */
86412115Sdyson
86512115Sdyson	/*
86612115Sdyson	 * Increase size of directory if entry eats into new space.
86712115Sdyson	 * This should never push the size past a new multiple of
86812115Sdyson	 * DIRBLKSIZE.
86912115Sdyson	 *
87012115Sdyson	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
87112115Sdyson	 */
87212115Sdyson	if (dp->i_offset + dp->i_count > dp->i_size)
87312115Sdyson		dp->i_size = dp->i_offset + dp->i_count;
87412115Sdyson	/*
87512115Sdyson	 * Get the block containing the space for the new directory entry.
87612115Sdyson	 */
87796749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf,
87896749Siedowse	    &bp)) != 0)
87912115Sdyson		return (error);
88012115Sdyson	/*
88112115Sdyson	 * Find space for the new entry. In the simple case, the entry at
88212115Sdyson	 * offset base will have the space. If it does not, then namei
88312115Sdyson	 * arranged that compacting the region dp->i_offset to
88412115Sdyson	 * dp->i_offset + dp->i_count would yield the
88512115Sdyson	 * space.
88612115Sdyson	 */
887202283Slulf	ep = (struct ext2fs_direct_2 *)dirbuf;
888202283Slulf	dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen);
889202283Slulf	spacefree = ep->e2d_reclen - dsize;
890202283Slulf	for (loc = ep->e2d_reclen; loc < dp->i_count; ) {
891202283Slulf		nep = (struct ext2fs_direct_2 *)(dirbuf + loc);
892202283Slulf		if (ep->e2d_ino) {
89312115Sdyson			/* trim the existing slot */
894202283Slulf			ep->e2d_reclen = dsize;
895202283Slulf			ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
89612115Sdyson		} else {
89712115Sdyson			/* overwrite; nothing there; header is ours */
89812115Sdyson			spacefree += dsize;
89912115Sdyson		}
900202283Slulf		dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen);
901202283Slulf		spacefree += nep->e2d_reclen - dsize;
902202283Slulf		loc += nep->e2d_reclen;
90312115Sdyson		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
90412115Sdyson	}
90512115Sdyson	/*
90612115Sdyson	 * Update the pointer fields in the previous entry (if any),
90712115Sdyson	 * copy in the new entry, and write out the block.
90812115Sdyson	 */
909202283Slulf	if (ep->e2d_ino == 0) {
91012115Sdyson		if (spacefree + dsize < newentrysize)
91112115Sdyson			panic("ext2_direnter: compact1");
912202283Slulf		newdir.e2d_reclen = spacefree + dsize;
91312115Sdyson	} else {
91412115Sdyson		if (spacefree < newentrysize)
91512115Sdyson			panic("ext2_direnter: compact2");
916202283Slulf		newdir.e2d_reclen = spacefree;
917202283Slulf		ep->e2d_reclen = dsize;
918202283Slulf		ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
91912115Sdyson	}
92012115Sdyson	bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
921221166Sjhb	if (DOINGASYNC(dvp)) {
922221166Sjhb		bdwrite(bp);
923221166Sjhb		error = 0;
924221166Sjhb	} else {
925221166Sjhb		error = bwrite(bp);
926221166Sjhb	}
92712115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
92812115Sdyson	if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
92996749Siedowse		error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
93083366Sjulian		    cnp->cn_cred, cnp->cn_thread);
93112115Sdyson	return (error);
93212115Sdyson}
93312115Sdyson
93412115Sdyson/*
93512115Sdyson * Remove a directory entry after a call to namei, using
93612115Sdyson * the parameters which it left in nameidata. The entry
93712115Sdyson * dp->i_offset contains the offset into the directory of the
93812115Sdyson * entry to be eliminated.  The dp->i_count field contains the
93912115Sdyson * size of the previous record in the directory.  If this
94012115Sdyson * is 0, the first entry is being deleted, so we need only
94112115Sdyson * zero the inode number to mark the entry as free.  If the
94212115Sdyson * entry is not the first in the directory, we must reclaim
94312115Sdyson * the space of the now empty record by adding the record size
94412115Sdyson * to the size of the previous entry.
94512115Sdyson */
94612115Sdysonint
94712115Sdysonext2_dirremove(dvp, cnp)
94812115Sdyson	struct vnode *dvp;
94912115Sdyson	struct componentname *cnp;
95012115Sdyson{
95196752Siedowse	struct inode *dp;
952202283Slulf	struct ext2fs_direct_2 *ep, *rep;
95312115Sdyson	struct buf *bp;
95412115Sdyson	int error;
955111742Sdes
95612115Sdyson	dp = VTOI(dvp);
95712115Sdyson	if (dp->i_count == 0) {
95812115Sdyson		/*
95912115Sdyson		 * First entry in block: set d_ino to zero.
96012115Sdyson		 */
96143301Sdillon		if ((error =
96296749Siedowse		    ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep,
96396749Siedowse		    &bp)) != 0)
96412115Sdyson			return (error);
965202283Slulf		ep->e2d_ino = 0;
966126853Sphk		error = bwrite(bp);
96712115Sdyson		dp->i_flag |= IN_CHANGE | IN_UPDATE;
96812115Sdyson		return (error);
96912115Sdyson	}
97012115Sdyson	/*
97112115Sdyson	 * Collapse new free space into previous entry.
97212115Sdyson	 */
97396749Siedowse	if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
97443301Sdillon	    (char **)&ep, &bp)) != 0)
97512115Sdyson		return (error);
976202283Slulf
977202283Slulf	/* Set 'rep' to the entry being removed. */
978202283Slulf	if (dp->i_count == 0)
979202283Slulf		rep = ep;
980202283Slulf	else
981202283Slulf		rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen);
982202283Slulf	ep->e2d_reclen += rep->e2d_reclen;
983221166Sjhb	if (DOINGASYNC(dvp) && dp->i_count != 0)
984221166Sjhb		bdwrite(bp);
985221166Sjhb	else
986221166Sjhb		error = bwrite(bp);
98712115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
98812115Sdyson	return (error);
98912115Sdyson}
99012115Sdyson
99112115Sdyson/*
99212115Sdyson * Rewrite an existing directory entry to point at the inode
99312115Sdyson * supplied.  The parameters describing the directory entry are
99412115Sdyson * set up by a call to namei.
99512115Sdyson */
99612115Sdysonint
99712115Sdysonext2_dirrewrite(dp, ip, cnp)
99812115Sdyson	struct inode *dp, *ip;
99912115Sdyson	struct componentname *cnp;
100012115Sdyson{
100112115Sdyson	struct buf *bp;
1002202283Slulf	struct ext2fs_direct_2 *ep;
100312115Sdyson	struct vnode *vdp = ITOV(dp);
100412115Sdyson	int error;
100512115Sdyson
100696749Siedowse	if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep,
100796749Siedowse	    &bp)) != 0)
100812115Sdyson		return (error);
1009202283Slulf	ep->e2d_ino = ip->i_number;
1010193377Sstas	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
1011202283Slulf	    EXT2F_INCOMPAT_FTYPE))
1012202283Slulf		ep->e2d_type = DTTOFT(IFTODT(ip->i_mode));
101355477Sbde	else
1014202283Slulf		ep->e2d_type = EXT2_FT_UNKNOWN;
1015126853Sphk	error = bwrite(bp);
101612115Sdyson	dp->i_flag |= IN_CHANGE | IN_UPDATE;
101712115Sdyson	return (error);
101812115Sdyson}
101912115Sdyson
102012115Sdyson/*
102112115Sdyson * Check if a directory is empty or not.
102212115Sdyson * Inode supplied must be locked.
102312115Sdyson *
102412115Sdyson * Using a struct dirtemplate here is not precisely
102512115Sdyson * what we want, but better than using a struct direct.
102612115Sdyson *
102712115Sdyson * NB: does not handle corrupted directories.
102812115Sdyson */
102912115Sdysonint
103012115Sdysonext2_dirempty(ip, parentino, cred)
103196752Siedowse	struct inode *ip;
103212115Sdyson	ino_t parentino;
103312115Sdyson	struct ucred *cred;
103412115Sdyson{
103596752Siedowse	off_t off;
103612115Sdyson	struct dirtemplate dbuf;
1037202283Slulf	struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf;
1038233353Skib	int error, namlen;
1039233353Skib	ssize_t count;
1040229549Spfg#define	MINDIRSIZ (sizeof(struct dirtemplate) / 2)
104112115Sdyson
1042202283Slulf	for (off = 0; off < ip->i_size; off += dp->e2d_reclen) {
1043101744Srwatson		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
1044101744Srwatson		    off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
1045101941Srwatson		    NOCRED, &count, (struct thread *)0);
104612115Sdyson		/*
104712115Sdyson		 * Since we read MINDIRSIZ, residual must
104812115Sdyson		 * be 0 unless we're at end of file.
104912115Sdyson		 */
105012115Sdyson		if (error || count != 0)
105112115Sdyson			return (0);
105212115Sdyson		/* avoid infinite loops */
1053202283Slulf		if (dp->e2d_reclen == 0)
105412115Sdyson			return (0);
105512115Sdyson		/* skip empty entries */
1056202283Slulf		if (dp->e2d_ino == 0)
105712115Sdyson			continue;
105812115Sdyson		/* accept only "." and ".." */
1059202283Slulf		namlen = dp->e2d_namlen;
106012115Sdyson		if (namlen > 2)
106112115Sdyson			return (0);
1062202283Slulf		if (dp->e2d_name[0] != '.')
106312115Sdyson			return (0);
106412115Sdyson		/*
106512115Sdyson		 * At this point namlen must be 1 or 2.
106612115Sdyson		 * 1 implies ".", 2 implies ".." if second
106712115Sdyson		 * char is also "."
106812115Sdyson		 */
106912115Sdyson		if (namlen == 1)
107012115Sdyson			continue;
1071202283Slulf		if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino)
107212115Sdyson			continue;
107312115Sdyson		return (0);
107412115Sdyson	}
107512115Sdyson	return (1);
107612115Sdyson}
107712115Sdyson
107812115Sdyson/*
107912115Sdyson * Check if source directory is in the path of the target directory.
108012115Sdyson * Target is supplied locked, source is unlocked.
108112115Sdyson * The target is always vput before returning.
108212115Sdyson */
108312115Sdysonint
108412115Sdysonext2_checkpath(source, target, cred)
108512115Sdyson	struct inode *source, *target;
108612115Sdyson	struct ucred *cred;
108712115Sdyson{
108812115Sdyson	struct vnode *vp;
1089247055Spfg	int error, namlen;
109012115Sdyson	struct dirtemplate dirbuf;
109112115Sdyson
109212115Sdyson	vp = ITOV(target);
109312115Sdyson	if (target->i_number == source->i_number) {
109412115Sdyson		error = EEXIST;
109512115Sdyson		goto out;
109612115Sdyson	}
1097247055Spfg	if (target->i_number == EXT2_ROOTINO) {
1098247055Spfg		error = 0;
109912115Sdyson		goto out;
1100247055Spfg	}
110112115Sdyson
110212115Sdyson	for (;;) {
110312115Sdyson		if (vp->v_type != VDIR) {
110412115Sdyson			error = ENOTDIR;
110512115Sdyson			break;
110612115Sdyson		}
110712115Sdyson		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
1108229549Spfg			sizeof(struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1109194296Skib			IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL,
1110194296Skib			NULL);
111112115Sdyson		if (error != 0)
111212115Sdyson			break;
111357710Sbde		namlen = dirbuf.dotdot_type;	/* like ufs little-endian */
111412115Sdyson		if (namlen != 2 ||
111512115Sdyson		    dirbuf.dotdot_name[0] != '.' ||
111612115Sdyson		    dirbuf.dotdot_name[1] != '.') {
111712115Sdyson			error = ENOTDIR;
111812115Sdyson			break;
111912115Sdyson		}
112012115Sdyson		if (dirbuf.dotdot_ino == source->i_number) {
112112115Sdyson			error = EINVAL;
112212115Sdyson			break;
112312115Sdyson		}
1124247055Spfg		if (dirbuf.dotdot_ino == EXT2_ROOTINO)
112512115Sdyson			break;
112612115Sdyson		vput(vp);
112792462Smckusick		if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
112892462Smckusick		    LK_EXCLUSIVE, &vp)) != 0) {
112912115Sdyson			vp = NULL;
113012115Sdyson			break;
113112115Sdyson		}
113212115Sdyson	}
113312115Sdyson
113412115Sdysonout:
113512115Sdyson	if (error == ENOTDIR)
113612115Sdyson		printf("checkpath: .. not a directory\n");
113712115Sdyson	if (vp != NULL)
113812115Sdyson		vput(vp);
113912115Sdyson	return (error);
114012115Sdyson}
1141