xref: /openbsd-src/sys/ufs/ext2fs/ext2fs_lookup.c (revision 479c151d3429b7cfa6228ee428d945620629789d)
1*479c151dSjsg /*	$OpenBSD: ext2fs_lookup.c,v 1.48 2024/09/20 02:00:46 jsg Exp $	*/
27f1901c5Sart /*	$NetBSD: ext2fs_lookup.c,v 1.16 2000/08/03 20:29:26 thorpej Exp $	*/
35ac2d602Sdownsj 
41f3ff51cSdownsj /*
51f3ff51cSdownsj  * Modified for NetBSD 1.2E
61f3ff51cSdownsj  * May 1997, Manuel Bouyer
71f3ff51cSdownsj  * Laboratoire d'informatique de Paris VI
81f3ff51cSdownsj  */
95ac2d602Sdownsj /*
105ac2d602Sdownsj  *  modified for Lites 1.1
115ac2d602Sdownsj  *
125ac2d602Sdownsj  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
135ac2d602Sdownsj  *  University of Utah, Department of Computer Science
145ac2d602Sdownsj  */
155ac2d602Sdownsj /*
165ac2d602Sdownsj  * Copyright (c) 1989, 1993
175ac2d602Sdownsj  *	The Regents of the University of California.  All rights reserved.
185ac2d602Sdownsj  * (c) UNIX System Laboratories, Inc.
195ac2d602Sdownsj  * All or some portions of this file are derived from material licensed
205ac2d602Sdownsj  * to the University of California by American Telephone and Telegraph
215ac2d602Sdownsj  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
225ac2d602Sdownsj  * the permission of UNIX System Laboratories, Inc.
235ac2d602Sdownsj  *
245ac2d602Sdownsj  * Redistribution and use in source and binary forms, with or without
255ac2d602Sdownsj  * modification, are permitted provided that the following conditions
265ac2d602Sdownsj  * are met:
275ac2d602Sdownsj  * 1. Redistributions of source code must retain the above copyright
285ac2d602Sdownsj  *    notice, this list of conditions and the following disclaimer.
295ac2d602Sdownsj  * 2. Redistributions in binary form must reproduce the above copyright
305ac2d602Sdownsj  *    notice, this list of conditions and the following disclaimer in the
315ac2d602Sdownsj  *    documentation and/or other materials provided with the distribution.
3229295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
335ac2d602Sdownsj  *    may be used to endorse or promote products derived from this software
345ac2d602Sdownsj  *    without specific prior written permission.
355ac2d602Sdownsj  *
365ac2d602Sdownsj  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
375ac2d602Sdownsj  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
385ac2d602Sdownsj  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
395ac2d602Sdownsj  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
405ac2d602Sdownsj  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
415ac2d602Sdownsj  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
425ac2d602Sdownsj  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
435ac2d602Sdownsj  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
445ac2d602Sdownsj  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
455ac2d602Sdownsj  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
465ac2d602Sdownsj  * SUCH DAMAGE.
475ac2d602Sdownsj  *
485ac2d602Sdownsj  *	@(#)ufs_lookup.c	8.6 (Berkeley) 4/1/94
495ac2d602Sdownsj  */
505ac2d602Sdownsj 
515ac2d602Sdownsj #include <sys/param.h>
525ac2d602Sdownsj #include <sys/systm.h>
535ac2d602Sdownsj #include <sys/namei.h>
545ac2d602Sdownsj #include <sys/buf.h>
555ac2d602Sdownsj #include <sys/mount.h>
565ac2d602Sdownsj #include <sys/vnode.h>
575ac2d602Sdownsj #include <sys/malloc.h>
585ac2d602Sdownsj #include <sys/dirent.h>
595ac2d602Sdownsj 
605ac2d602Sdownsj #include <ufs/ufs/quota.h>
615ac2d602Sdownsj #include <ufs/ufs/inode.h>
625ac2d602Sdownsj #include <ufs/ufs/ufsmount.h>
635ac2d602Sdownsj #include <ufs/ufs/ufs_extern.h>
645ac2d602Sdownsj 
655ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_extern.h>
665ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_dir.h>
675ac2d602Sdownsj #include <ufs/ext2fs/ext2fs.h>
685ac2d602Sdownsj 
695ac2d602Sdownsj extern	int dirchk;
705ac2d602Sdownsj 
71c4071fd1Smillert static void	ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir,
72c4071fd1Smillert     struct dirent *ffsdir);
73f92a609fSpelikan static int	ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de,
74c4071fd1Smillert     int entryoffsetinblock);
75f92a609fSpelikan static int	ext2fs_search_dirblock(struct inode *, void *, int *,
76f92a609fSpelikan     struct componentname *, int *, doff_t *, doff_t *,
77f92a609fSpelikan     struct ext2fs_searchslot *);
785ac2d602Sdownsj 
795ac2d602Sdownsj /*
805ac2d602Sdownsj  * the problem that is tackled below is the fact that FFS
815ac2d602Sdownsj  * includes the terminating zero on disk while EXT2FS doesn't
825ac2d602Sdownsj  * this implies that we need to introduce some padding.
835ac2d602Sdownsj  * For instance, a filename "sbin" has normally a reclen 12
845ac2d602Sdownsj  * in EXT2, but 16 in FFS.
855ac2d602Sdownsj  * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...'
865ac2d602Sdownsj  * If it wasn't for that, the complete ufs code for directories would
875ac2d602Sdownsj  * have worked w/o changes (except for the difference in DIRBLKSIZ)
885ac2d602Sdownsj  */
895ac2d602Sdownsj static void
905f64cd9cSjasper ext2fs_dirconv2ffs(struct ext2fs_direct	*e2dir, struct dirent *ffsdir)
915ac2d602Sdownsj {
927f1901c5Sart 	memset(ffsdir, 0, sizeof(struct dirent));
93f7dbefaaSpelikan 	ffsdir->d_fileno = letoh32(e2dir->e2d_ino);
945ac2d602Sdownsj 	ffsdir->d_namlen = e2dir->e2d_namlen;
955ac2d602Sdownsj 
965ac2d602Sdownsj 	ffsdir->d_type = DT_UNKNOWN;		/* don't know more here */
975ac2d602Sdownsj #ifdef DIAGNOSTIC
985ac2d602Sdownsj 	/*
99b66b9ef8Sjsg 	 * XXX Right now this can't happen, but if one day
1005ac2d602Sdownsj 	 * MAXNAMLEN != E2FS_MAXNAMLEN we should handle this more gracefully !
1015ac2d602Sdownsj 	 */
10235986c15Sgrange 	/* XXX: e2d_namlen is to small for such comparison
1037f1901c5Sart 	if (e2dir->e2d_namlen > MAXNAMLEN)
104bc84bce2Skrw 		panic("ext2fs: e2dir->e2d_namlen");
10535986c15Sgrange 	*/
1065ac2d602Sdownsj #endif
1075ac2d602Sdownsj 	strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen);
1085ac2d602Sdownsj 
1095ac2d602Sdownsj 	/* Godmar thinks: since e2dir->e2d_reclen can be big and means
1105ac2d602Sdownsj 	   nothing anyway, we compute our own reclen according to what
1115ac2d602Sdownsj 	   we think is right
1125ac2d602Sdownsj 	 */
1135ac2d602Sdownsj 	ffsdir->d_reclen = DIRENT_SIZE(ffsdir);
1145ac2d602Sdownsj }
1155ac2d602Sdownsj 
1165ac2d602Sdownsj /*
1175ac2d602Sdownsj  * Vnode op for reading directories.
1185ac2d602Sdownsj  *
1195ac2d602Sdownsj  * Convert the on-disk entries to <sys/dirent.h> entries.
1205ac2d602Sdownsj  * the problem is that the conversion will blow up some entries by four bytes,
1215ac2d602Sdownsj  * so it can't be done in place. This is too bad. Right now the conversion is
1225ac2d602Sdownsj  * done entry by entry, the converted entry is sent via uiomove.
1235ac2d602Sdownsj  *
1245ac2d602Sdownsj  * XXX allocate a buffer, convert as many entries as possible, then send
1255ac2d602Sdownsj  * the whole buffer to uiomove
1265ac2d602Sdownsj  */
1275ac2d602Sdownsj int
1285f64cd9cSjasper ext2fs_readdir(void *v)
1295ac2d602Sdownsj {
13099bc9d31Sderaadt 	struct vop_readdir_args *ap = v;
1317f1901c5Sart 	struct uio *uio = ap->a_uio;
1325ac2d602Sdownsj 	int error;
1333faca824Spedro 	size_t e2fs_count, readcnt, entries;
1347f1901c5Sart 	struct vnode *vp = ap->a_vp;
1357f1901c5Sart 	struct m_ext2fs *fs = VTOI(vp)->i_e2fs;
1365ac2d602Sdownsj 
1375ac2d602Sdownsj 	struct ext2fs_direct *dp;
1385ac2d602Sdownsj 	struct dirent dstd;
1395ac2d602Sdownsj 	struct uio auio;
1405ac2d602Sdownsj 	struct iovec aiov;
1415ac2d602Sdownsj 	caddr_t dirbuf;
1425ac2d602Sdownsj 	off_t off = uio->uio_offset;
1437f1901c5Sart 	int e2d_reclen;
1447f1901c5Sart 
1457f1901c5Sart 	if (vp->v_type != VDIR)
1467f1901c5Sart 		return (ENOTDIR);
1477f1901c5Sart 
1487f1901c5Sart 	e2fs_count = uio->uio_resid;
1493faca824Spedro 	entries = (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize - 1);
1503faca824Spedro 
1515ac2d602Sdownsj 	/* Make sure we don't return partial entries. */
1523faca824Spedro 	if (e2fs_count <= entries)
1535ac2d602Sdownsj 		return (EINVAL);
1545ac2d602Sdownsj 
1553faca824Spedro 	e2fs_count -= entries;
1565ac2d602Sdownsj 	auio = *uio;
1575ac2d602Sdownsj 	auio.uio_iov = &aiov;
1585ac2d602Sdownsj 	auio.uio_iovcnt = 1;
1595ac2d602Sdownsj 	auio.uio_segflg = UIO_SYSSPACE;
1607f1901c5Sart 	aiov.iov_len = e2fs_count;
1617f1901c5Sart 	auio.uio_resid = e2fs_count;
162f18006a9Skrw 	dirbuf = malloc(e2fs_count, M_TEMP, M_WAITOK | M_ZERO);
1635ac2d602Sdownsj 	aiov.iov_base = dirbuf;
1645ac2d602Sdownsj 
1655ac2d602Sdownsj 	error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
1667f1901c5Sart 	if (error == 0) {
1677f1901c5Sart 		readcnt = e2fs_count - auio.uio_resid;
1682d7df345Spelikan 		dp = (struct ext2fs_direct *) dirbuf;
1692d7df345Spelikan 		while ((char *) dp < (char *) dirbuf + readcnt) {
170f7dbefaaSpelikan 			e2d_reclen = letoh16(dp->e2d_reclen);
1717f1901c5Sart 			if (e2d_reclen == 0) {
1725ac2d602Sdownsj 				error = EIO;
1737f1901c5Sart 				break;
1745ac2d602Sdownsj 			}
1755ac2d602Sdownsj 			ext2fs_dirconv2ffs(dp, &dstd);
17646f7109aSclaudio 			if (memchr(dstd.d_name, '/', dstd.d_namlen) != NULL) {
17746f7109aSclaudio 				error = EINVAL;
17846f7109aSclaudio 				break;
17946f7109aSclaudio 			}
1805ac2d602Sdownsj 			if (dstd.d_reclen > uio->uio_resid) {
1815ac2d602Sdownsj 				break;
1825ac2d602Sdownsj 			}
18391a535ffSguenther 			dstd.d_off = off + e2d_reclen;
184dc30eddbSstefan 			if ((error = uiomove((caddr_t)&dstd, dstd.d_reclen, uio)) != 0) {
1857f1901c5Sart 				break;
1865ac2d602Sdownsj 			}
1877f1901c5Sart 			off = off + e2d_reclen;
1887f1901c5Sart 			/* advance dp */
1897f1901c5Sart 			dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen);
1905ac2d602Sdownsj 		}
1915ac2d602Sdownsj 		/* we need to correct uio_offset */
1925ac2d602Sdownsj 		uio->uio_offset = off;
193d145b341Scsapuntz 	}
194c9a9cb3fSpelikan 	free(dirbuf, M_TEMP, e2fs_count);
1954dcbbbb0Sniallo 	*ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset;
1965ac2d602Sdownsj 	return (error);
1975ac2d602Sdownsj }
1985ac2d602Sdownsj 
1995ac2d602Sdownsj /*
2005ac2d602Sdownsj  * Convert a component of a pathname into a pointer to a locked inode.
2015ac2d602Sdownsj  * This is a very central and rather complicated routine.
2025ac2d602Sdownsj  * If the file system is not maintained in a strict tree hierarchy,
2035ac2d602Sdownsj  * this can result in a deadlock situation (see comments in code below).
2045ac2d602Sdownsj  *
2055ac2d602Sdownsj  * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
2065ac2d602Sdownsj  * on whether the name is to be looked up, created, renamed, or deleted.
2075ac2d602Sdownsj  * When CREATE, RENAME, or DELETE is specified, information usable in
2085ac2d602Sdownsj  * creating, renaming, or deleting a directory entry may be calculated.
2095ac2d602Sdownsj  * If flag has LOCKPARENT or'ed into it and the target of the pathname
2105ac2d602Sdownsj  * exists, lookup returns both the target and its parent directory locked.
2115ac2d602Sdownsj  * When creating or renaming and LOCKPARENT is specified, the target may
2125ac2d602Sdownsj  * not be ".".  When deleting and LOCKPARENT is specified, the target may
2135ac2d602Sdownsj  * be "."., but the caller must check to ensure it does an vrele and vput
2145ac2d602Sdownsj  * instead of two vputs.
2155ac2d602Sdownsj  *
2165ac2d602Sdownsj  * Overall outline of ext2fs_lookup:
2175ac2d602Sdownsj  *
2185ac2d602Sdownsj  *	check accessibility of directory
2195ac2d602Sdownsj  *	look for name in cache, if found, then if at end of path
2205ac2d602Sdownsj  *	  and deleting or creating, drop it, else return name
2215ac2d602Sdownsj  *	search for name in directory, to found or notfound
2225ac2d602Sdownsj  * notfound:
2235ac2d602Sdownsj  *	if creating, return locked directory, leaving info on available slots
2245ac2d602Sdownsj  *	else return error
2255ac2d602Sdownsj  * found:
2265ac2d602Sdownsj  *	if at end of path and deleting, return information to allow delete
2275ac2d602Sdownsj  *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
2285ac2d602Sdownsj  *	  inode and return info to allow rewrite
2295ac2d602Sdownsj  *	if not at end, add name to cache; if at end and neither creating
2305ac2d602Sdownsj  *	  nor deleting, add name to cache
2315ac2d602Sdownsj  */
2325ac2d602Sdownsj int
2335f64cd9cSjasper ext2fs_lookup(void *v)
2345ac2d602Sdownsj {
23599bc9d31Sderaadt 	struct vop_lookup_args *ap = v;
2367f1901c5Sart 	struct vnode *vdp;	/* vnode for directory being searched */
2377f1901c5Sart 	struct inode *dp;	/* inode for directory being searched */
2385ac2d602Sdownsj 	struct buf *bp;			/* a buffer of directory entries */
2397f1901c5Sart 	struct ext2fs_direct *ep; /* the current directory entry */
2405ac2d602Sdownsj 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
2413e537b2cSpelikan 	struct ext2fs_searchslot ss;
2425ac2d602Sdownsj 	int numdirpasses;		/* strategy for directory search */
2435ac2d602Sdownsj 	doff_t endsearch;		/* offset to end directory search */
2445ac2d602Sdownsj 	doff_t prevoff;			/* prev entry dp->i_offset */
2455ac2d602Sdownsj 	struct vnode *pdp;		/* saved dp during symlink work */
2465ac2d602Sdownsj 	struct vnode *tdp;		/* returned by VFS_VGET */
2475ac2d602Sdownsj 	doff_t enduseful;		/* pointer past last used dir slot */
2485ac2d602Sdownsj 	u_long bmask;			/* block offset mask */
2495ac2d602Sdownsj 	int lockparent;			/* 1 => lockparent flag is set */
2505ac2d602Sdownsj 	int wantparent;			/* 1 => wantparent or lockparent flag */
2515ac2d602Sdownsj 	struct vnode **vpp = ap->a_vpp;
2525ac2d602Sdownsj 	struct componentname *cnp = ap->a_cnp;
2535ac2d602Sdownsj 	struct ucred *cred = cnp->cn_cred;
2545ac2d602Sdownsj 	int flags = cnp->cn_flags;
2555ac2d602Sdownsj 	int nameiop = cnp->cn_nameiop;
256f92a609fSpelikan 	int dirblksize, entry_found = 0, error;
2575ac2d602Sdownsj 
2583e537b2cSpelikan 	ss.slotstatus = FOUND;
2593e537b2cSpelikan 	ss.slotoffset = -1;
2603e537b2cSpelikan 	ss.slotfreespace = ss.slotsize = ss.slotneeded = 0;
2613e537b2cSpelikan 
2625ac2d602Sdownsj 	bp = NULL;
2635ac2d602Sdownsj 	*vpp = NULL;
2645ac2d602Sdownsj 	vdp = ap->a_dvp;
2655ac2d602Sdownsj 	dp = VTOI(vdp);
266f92a609fSpelikan 	dirblksize = dp->i_e2fs->e2fs_bsize;
2675ac2d602Sdownsj 	lockparent = flags & LOCKPARENT;
2685ac2d602Sdownsj 	wantparent = flags & (LOCKPARENT|WANTPARENT);
2692d7df345Spelikan 
2705ac2d602Sdownsj 	/*
2710d297f47Sjsg 	 * Check accessibility of directory.
2725ac2d602Sdownsj 	 */
2735ac2d602Sdownsj 	if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
2745ac2d602Sdownsj 		return (error);
2755ac2d602Sdownsj 
2767f1901c5Sart 	if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
2777f1901c5Sart 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
2787f1901c5Sart 		return (EROFS);
2797f1901c5Sart 
2805ac2d602Sdownsj 	/*
2815ac2d602Sdownsj 	 * We now have a segment name to search for, and a directory to search.
2825ac2d602Sdownsj 	 *
2835ac2d602Sdownsj 	 * Before tediously performing a linear scan of the directory,
2845ac2d602Sdownsj 	 * check the name cache to see if the directory/name pair
2855ac2d602Sdownsj 	 * we are looking for is known already.
2865ac2d602Sdownsj 	 */
28709308f32Sart 	if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
2885ac2d602Sdownsj 		return (error);
2895ac2d602Sdownsj 
2905ac2d602Sdownsj 	/*
2915ac2d602Sdownsj 	 * Suppress search for slots unless creating
2925ac2d602Sdownsj 	 * file and at end of pathname, in which case
2935ac2d602Sdownsj 	 * we watch for a place to put the new file in
2945ac2d602Sdownsj 	 * case it doesn't already exist.
2955ac2d602Sdownsj 	 */
2963e537b2cSpelikan 	if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) {
2973e537b2cSpelikan 		ss.slotstatus = NONE;
2983e537b2cSpelikan 		ss.slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
2995ac2d602Sdownsj 	}
3005ac2d602Sdownsj 
3015ac2d602Sdownsj 	/*
3025ac2d602Sdownsj 	 * If there is cached information on a previous search of
3035ac2d602Sdownsj 	 * this directory, pick up where we last left off.
3045ac2d602Sdownsj 	 * We cache only lookups as these are the most common
3055ac2d602Sdownsj 	 * and have the greatest payoff. Caching CREATE has little
3065ac2d602Sdownsj 	 * benefit as it usually must search the entire directory
3075ac2d602Sdownsj 	 * to determine that the entry does not exist. Caching the
3085ac2d602Sdownsj 	 * location of the last DELETE or RENAME has not reduced
3095ac2d602Sdownsj 	 * profiling time and hence has been removed in the interest
3105ac2d602Sdownsj 	 * of simplicity.
3115ac2d602Sdownsj 	 */
3125ac2d602Sdownsj 	bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
3135ac2d602Sdownsj 	if (nameiop != LOOKUP || dp->i_diroff == 0 ||
3144dcbbbb0Sniallo 	    dp->i_diroff > ext2fs_size(dp)) {
3155ac2d602Sdownsj 		entryoffsetinblock = 0;
3165ac2d602Sdownsj 		dp->i_offset = 0;
3175ac2d602Sdownsj 		numdirpasses = 1;
3185ac2d602Sdownsj 	} else {
3195ac2d602Sdownsj 		dp->i_offset = dp->i_diroff;
3205ac2d602Sdownsj 		if ((entryoffsetinblock = dp->i_offset & bmask) &&
321b080ad39Scsapuntz 		    (error = ext2fs_bufatoff(dp, (off_t)dp->i_offset,
322b080ad39Scsapuntz 		    NULL, &bp)))
3235ac2d602Sdownsj 			return (error);
3245ac2d602Sdownsj 		numdirpasses = 2;
3255ac2d602Sdownsj 	}
3265ac2d602Sdownsj 	prevoff = dp->i_offset;
3274dcbbbb0Sniallo 	endsearch = roundup(ext2fs_size(dp), dirblksize);
3285ac2d602Sdownsj 	enduseful = 0;
3295ac2d602Sdownsj 
3305ac2d602Sdownsj searchloop:
3315ac2d602Sdownsj 	while (dp->i_offset < endsearch) {
3325ac2d602Sdownsj 		/*
3335ac2d602Sdownsj 		 * If necessary, get the next directory block.
3345ac2d602Sdownsj 		 */
3355ac2d602Sdownsj 		if (bp != NULL)
3365ac2d602Sdownsj 			brelse(bp);
3372d7df345Spelikan 
338f92a609fSpelikan 		error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp);
3395ac2d602Sdownsj 		if (error != 0)
3405ac2d602Sdownsj 			return (error);
3415ac2d602Sdownsj 		entryoffsetinblock = 0;
342f92a609fSpelikan 
3435ac2d602Sdownsj 		/*
3445ac2d602Sdownsj 		 * If still looking for a slot, and at a dirblksize
3455ac2d602Sdownsj 		 * boundary, have to start looking for free space again.
3465ac2d602Sdownsj 		 */
347f92a609fSpelikan 		if (ss.slotstatus == NONE) {
3483e537b2cSpelikan 			ss.slotoffset = -1;
3493e537b2cSpelikan 			ss.slotfreespace = 0;
3505ac2d602Sdownsj 		}
351f92a609fSpelikan 
352f92a609fSpelikan 		error = ext2fs_search_dirblock(dp, bp->b_data, &entry_found,
353f92a609fSpelikan 		    cnp, &entryoffsetinblock, &prevoff, &enduseful, &ss);
354f92a609fSpelikan 		if (error) {
355f92a609fSpelikan 			brelse(bp);
356f92a609fSpelikan 			return (error);
357f92a609fSpelikan 		}
358f92a609fSpelikan 		if (entry_found) {
3595ac2d602Sdownsj 			ep = (struct ext2fs_direct *)
360f92a609fSpelikan 			    ((char *)bp->b_data + (entryoffsetinblock & bmask));
361f92a609fSpelikan /* foundentry: */
362f7dbefaaSpelikan 			dp->i_ino = letoh32(ep->e2d_ino);
363f7dbefaaSpelikan 			dp->i_reclen = letoh16(ep->e2d_reclen);
3645ac2d602Sdownsj 			goto found;
3655ac2d602Sdownsj 		}
3665ac2d602Sdownsj 	}
3675ac2d602Sdownsj /* notfound: */
3685ac2d602Sdownsj 	/*
3695ac2d602Sdownsj 	 * If we started in the middle of the directory and failed
3705ac2d602Sdownsj 	 * to find our target, we must check the beginning as well.
3715ac2d602Sdownsj 	 */
3725ac2d602Sdownsj 	if (numdirpasses == 2) {
3735ac2d602Sdownsj 		numdirpasses--;
3745ac2d602Sdownsj 		dp->i_offset = 0;
3755ac2d602Sdownsj 		endsearch = dp->i_diroff;
3765ac2d602Sdownsj 		goto searchloop;
3775ac2d602Sdownsj 	}
3785ac2d602Sdownsj 	if (bp != NULL)
3795ac2d602Sdownsj 		brelse(bp);
3805ac2d602Sdownsj 	/*
3815ac2d602Sdownsj 	 * If creating, and at end of pathname and current
3825ac2d602Sdownsj 	 * directory has not been removed, then can consider
3835ac2d602Sdownsj 	 * allowing file to be created.
3845ac2d602Sdownsj 	 */
3855ac2d602Sdownsj 	if ((nameiop == CREATE || nameiop == RENAME) &&
3865ac2d602Sdownsj 		(flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
3875ac2d602Sdownsj 		/*
3881f3ff51cSdownsj 		 * Creation of files on a read-only mounted file system
3891f3ff51cSdownsj 		 * is pointless, so don't proceed any further.
3901f3ff51cSdownsj 		 */
3911f3ff51cSdownsj 		if (vdp->v_mount->mnt_flag & MNT_RDONLY)
3921f3ff51cSdownsj 			return (EROFS);
3931f3ff51cSdownsj 		/*
3945ac2d602Sdownsj 		 * Access for write is interpreted as allowing
3955ac2d602Sdownsj 		 * creation of files in the directory.
3965ac2d602Sdownsj 		 */
3975ac2d602Sdownsj 		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
3985ac2d602Sdownsj 			return (error);
3995ac2d602Sdownsj 		/*
4005ac2d602Sdownsj 		 * Return an indication of where the new directory
4015ac2d602Sdownsj 		 * entry should be put.  If we didn't find a slot,
4025ac2d602Sdownsj 		 * then set dp->i_count to 0 indicating
4035ac2d602Sdownsj 		 * that the new slot belongs at the end of the
4045ac2d602Sdownsj 		 * directory. If we found a slot, then the new entry
4055ac2d602Sdownsj 		 * can be put in the range from dp->i_offset to
4065ac2d602Sdownsj 		 * dp->i_offset + dp->i_count.
4075ac2d602Sdownsj 		 */
4083e537b2cSpelikan 		if (ss.slotstatus == NONE) {
4094dcbbbb0Sniallo 			dp->i_offset = roundup(ext2fs_size(dp), dirblksize);
4105ac2d602Sdownsj 			dp->i_count = 0;
4115ac2d602Sdownsj 			enduseful = dp->i_offset;
4125ac2d602Sdownsj 		} else {
4133e537b2cSpelikan 			dp->i_offset = ss.slotoffset;
4143e537b2cSpelikan 			dp->i_count = ss.slotsize;
4153e537b2cSpelikan 			if (enduseful < ss.slotoffset + ss.slotsize)
4163e537b2cSpelikan 				enduseful = ss.slotoffset + ss.slotsize;
4175ac2d602Sdownsj 		}
4185ac2d602Sdownsj 		dp->i_endoff = roundup(enduseful, dirblksize);
4195ac2d602Sdownsj 		dp->i_flag |= IN_CHANGE | IN_UPDATE;
4205ac2d602Sdownsj 		/*
4215ac2d602Sdownsj 		 * We return with the directory locked, so that
4225ac2d602Sdownsj 		 * the parameters we set up above will still be
4235ac2d602Sdownsj 		 * valid if we actually decide to do a direnter().
4245ac2d602Sdownsj 		 * We return ni_vp == NULL to indicate that the entry
4255ac2d602Sdownsj 		 * does not currently exist; we leave a pointer to
4265ac2d602Sdownsj 		 * the (locked) directory inode in ndp->ni_dvp.
4275ac2d602Sdownsj 		 * The pathname buffer is saved so that the name
4285ac2d602Sdownsj 		 * can be obtained later.
4295ac2d602Sdownsj 		 *
4305ac2d602Sdownsj 		 * NB - if the directory is unlocked, then this
4315ac2d602Sdownsj 		 * information cannot be used.
4325ac2d602Sdownsj 		 */
4335ac2d602Sdownsj 		cnp->cn_flags |= SAVENAME;
43409308f32Sart 		if (!lockparent) {
43536bb23f1Svisa 			VOP_UNLOCK(vdp);
43609308f32Sart 			cnp->cn_flags |= PDIRUNLOCK;
43709308f32Sart 		}
4385ac2d602Sdownsj 		return (EJUSTRETURN);
4395ac2d602Sdownsj 	}
4405ac2d602Sdownsj 	/*
4415ac2d602Sdownsj 	 * Insert name into cache (as non-existent) if appropriate.
4425ac2d602Sdownsj 	 */
4435ac2d602Sdownsj 	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
4445ac2d602Sdownsj 		cache_enter(vdp, *vpp, cnp);
4455ac2d602Sdownsj 	return (ENOENT);
4465ac2d602Sdownsj 
4475ac2d602Sdownsj found:
4485ac2d602Sdownsj 	/*
4495ac2d602Sdownsj 	 * Check that directory length properly reflects presence
4505ac2d602Sdownsj 	 * of this entry.
4515ac2d602Sdownsj 	 */
4525ac2d602Sdownsj 	if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen)
4534dcbbbb0Sniallo 	    > ext2fs_size(dp)) {
4545ac2d602Sdownsj 		ufs_dirbad(dp, dp->i_offset, "i_size too small");
4554dcbbbb0Sniallo 		error = ext2fs_setsize(dp,
4564dcbbbb0Sniallo 			entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen));
4574dcbbbb0Sniallo 		if (error) {
4584dcbbbb0Sniallo 			brelse(bp);
4594dcbbbb0Sniallo 			return(error);
4604dcbbbb0Sniallo 		}
4615ac2d602Sdownsj 		dp->i_flag |= IN_CHANGE | IN_UPDATE;
4625ac2d602Sdownsj 	}
463f92a609fSpelikan 	brelse(bp);
4645ac2d602Sdownsj 
4655ac2d602Sdownsj 	/*
4665ac2d602Sdownsj 	 * Found component in pathname.
4675ac2d602Sdownsj 	 * If the final component of path name, save information
4685ac2d602Sdownsj 	 * in the cache as to where the entry was found.
4695ac2d602Sdownsj 	 */
4705ac2d602Sdownsj 	if ((flags & ISLASTCN) && nameiop == LOOKUP)
4715ac2d602Sdownsj 		dp->i_diroff = dp->i_offset &~ (dirblksize - 1);
4725ac2d602Sdownsj 
4735ac2d602Sdownsj 	/*
4745ac2d602Sdownsj 	 * If deleting, and at end of pathname, return
4755ac2d602Sdownsj 	 * parameters which can be used to remove file.
4765ac2d602Sdownsj 	 * If the wantparent flag isn't set, we return only
4775ac2d602Sdownsj 	 * the directory (in ndp->ni_dvp), otherwise we go
4785ac2d602Sdownsj 	 * on and lock the inode, being careful with ".".
4795ac2d602Sdownsj 	 */
4805ac2d602Sdownsj 	if (nameiop == DELETE && (flags & ISLASTCN)) {
4815ac2d602Sdownsj 		/*
4825ac2d602Sdownsj 		 * Write access to directory required to delete files.
4835ac2d602Sdownsj 		 */
4845ac2d602Sdownsj 		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
4855ac2d602Sdownsj 			return (error);
4865ac2d602Sdownsj 		/*
4875ac2d602Sdownsj 		 * Return pointer to current entry in dp->i_offset,
4885ac2d602Sdownsj 		 * and distance past previous entry (if there
4895ac2d602Sdownsj 		 * is a previous entry in this block) in dp->i_count.
4905ac2d602Sdownsj 		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
4915ac2d602Sdownsj 		 */
4925ac2d602Sdownsj 		if ((dp->i_offset & (dirblksize - 1)) == 0)
4935ac2d602Sdownsj 			dp->i_count = 0;
4945ac2d602Sdownsj 		else
4955ac2d602Sdownsj 			dp->i_count = dp->i_offset - prevoff;
4965ac2d602Sdownsj 		if (dp->i_number == dp->i_ino) {
497627b2c48Sthib 			vref(vdp);
4985ac2d602Sdownsj 			*vpp = vdp;
4995ac2d602Sdownsj 			return (0);
5005ac2d602Sdownsj 		}
5015ac2d602Sdownsj 		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
5025ac2d602Sdownsj 			return (error);
5035ac2d602Sdownsj 		/*
5045ac2d602Sdownsj 		 * If directory is "sticky", then user must own
5055ac2d602Sdownsj 		 * the directory, or the file in it, else she
5065ac2d602Sdownsj 		 * may not delete it (unless she's root). This
5075ac2d602Sdownsj 		 * implements append-only directories.
5085ac2d602Sdownsj 		 */
5095ac2d602Sdownsj 		if ((dp->i_e2fs_mode & ISVTX) &&
5105ac2d602Sdownsj 			cred->cr_uid != 0 &&
5115ac2d602Sdownsj 			cred->cr_uid != dp->i_e2fs_uid &&
5125ac2d602Sdownsj 			VTOI(tdp)->i_e2fs_uid != cred->cr_uid) {
5135ac2d602Sdownsj 			vput(tdp);
5145ac2d602Sdownsj 			return (EPERM);
5155ac2d602Sdownsj 		}
5165ac2d602Sdownsj 		*vpp = tdp;
51709308f32Sart 		if (!lockparent) {
51836bb23f1Svisa 			VOP_UNLOCK(vdp);
51909308f32Sart 			cnp->cn_flags |= PDIRUNLOCK;
52009308f32Sart 		}
5215ac2d602Sdownsj 		return (0);
5225ac2d602Sdownsj 	}
5235ac2d602Sdownsj 
5245ac2d602Sdownsj 	/*
5255ac2d602Sdownsj 	 * If rewriting (RENAME), return the inode and the
5265ac2d602Sdownsj 	 * information required to rewrite the present directory
5275ac2d602Sdownsj 	 * Must get inode of directory entry to verify it's a
5285ac2d602Sdownsj 	 * regular file, or empty directory.
5295ac2d602Sdownsj 	 */
5303e537b2cSpelikan 	if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
5315ac2d602Sdownsj 		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
5325ac2d602Sdownsj 			return (error);
5335ac2d602Sdownsj 		/*
5345ac2d602Sdownsj 		 * Careful about locking second inode.
5355ac2d602Sdownsj 		 * This can only occur if the target is ".".
5365ac2d602Sdownsj 		 */
5375ac2d602Sdownsj 		if (dp->i_number == dp->i_ino)
5385ac2d602Sdownsj 			return (EISDIR);
5395ac2d602Sdownsj 		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
5405ac2d602Sdownsj 			return (error);
5415ac2d602Sdownsj 		*vpp = tdp;
5425ac2d602Sdownsj 		cnp->cn_flags |= SAVENAME;
54309308f32Sart 		if (!lockparent) {
54436bb23f1Svisa 			VOP_UNLOCK(vdp);
54509308f32Sart 			cnp->cn_flags |= PDIRUNLOCK;
54609308f32Sart 		}
5475ac2d602Sdownsj 		return (0);
5485ac2d602Sdownsj 	}
5495ac2d602Sdownsj 
5505ac2d602Sdownsj 	/*
5515ac2d602Sdownsj 	 * Step through the translation in the name.  We do not `vput' the
5525ac2d602Sdownsj 	 * directory because we may need it again if a symbolic link
5535ac2d602Sdownsj 	 * is relative to the current directory.  Instead we save it
5545ac2d602Sdownsj 	 * unlocked as "pdp".  We must get the target inode before unlocking
5555ac2d602Sdownsj 	 * the directory to insure that the inode will not be removed
5565ac2d602Sdownsj 	 * before we get it.  We prevent deadlock by always fetching
5575ac2d602Sdownsj 	 * inodes from the root, moving down the directory tree. Thus
5585ac2d602Sdownsj 	 * when following backward pointers ".." we must unlock the
5595ac2d602Sdownsj 	 * parent directory before getting the requested directory.
5605ac2d602Sdownsj 	 * There is a potential race condition here if both the current
5615ac2d602Sdownsj 	 * and parent directories are removed before the VFS_VGET for the
5625ac2d602Sdownsj 	 * inode associated with ".." returns.  We hope that this occurs
5635ac2d602Sdownsj 	 * infrequently since we cannot avoid this race condition without
5645ac2d602Sdownsj 	 * implementing a sophisticated deadlock detection algorithm.
5655ac2d602Sdownsj 	 * Note also that this simple deadlock detection scheme will not
5665ac2d602Sdownsj 	 * work if the file system has any hard links other than ".."
5675ac2d602Sdownsj 	 * that point backwards in the directory structure.
5685ac2d602Sdownsj 	 */
5695ac2d602Sdownsj 	pdp = vdp;
5705ac2d602Sdownsj 	if (flags & ISDOTDOT) {
57136bb23f1Svisa 		VOP_UNLOCK(pdp);	/* race to get the inode */
57209308f32Sart 		cnp->cn_flags |= PDIRUNLOCK;
5735ac2d602Sdownsj 		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) {
5746e880534Svisa 			if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0)
57509308f32Sart 				cnp->cn_flags &= ~PDIRUNLOCK;
5765ac2d602Sdownsj 			return (error);
5775ac2d602Sdownsj 		}
57809308f32Sart 		if (lockparent && (flags & ISLASTCN)) {
5796e880534Svisa 			if ((error = vn_lock(pdp, LK_EXCLUSIVE)) != 0) {
5805ac2d602Sdownsj 				vput(tdp);
5815ac2d602Sdownsj 				return (error);
5825ac2d602Sdownsj 			}
58309308f32Sart 			cnp->cn_flags &= ~PDIRUNLOCK;
58409308f32Sart 		}
5855ac2d602Sdownsj 		*vpp = tdp;
5865ac2d602Sdownsj 	} else if (dp->i_number == dp->i_ino) {
587627b2c48Sthib 		vref(vdp);	/* we want ourself, ie "." */
5885ac2d602Sdownsj 		*vpp = vdp;
5895ac2d602Sdownsj 	} else {
5905ac2d602Sdownsj 		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
5915ac2d602Sdownsj 			return (error);
59209308f32Sart 		if (!lockparent || !(flags & ISLASTCN)) {
59336bb23f1Svisa 			VOP_UNLOCK(pdp);
59409308f32Sart 			cnp->cn_flags |= PDIRUNLOCK;
59509308f32Sart 		}
5965ac2d602Sdownsj 		*vpp = tdp;
5975ac2d602Sdownsj 	}
5985ac2d602Sdownsj 
5995ac2d602Sdownsj 	/*
6005ac2d602Sdownsj 	 * Insert name into cache if appropriate.
6015ac2d602Sdownsj 	 */
6025ac2d602Sdownsj 	if (cnp->cn_flags & MAKEENTRY)
6035ac2d602Sdownsj 		cache_enter(vdp, *vpp, cnp);
6045ac2d602Sdownsj 	return (0);
6055ac2d602Sdownsj }
6065ac2d602Sdownsj 
607f92a609fSpelikan int
608f92a609fSpelikan ext2fs_search_dirblock(struct inode *ip, void *data, int *foundp,
609f92a609fSpelikan     struct componentname *cnp, int *entryoffsetinblockp,
610f92a609fSpelikan     doff_t *prevoffp, doff_t *endusefulp, struct ext2fs_searchslot *ssp)
611f92a609fSpelikan {
612f92a609fSpelikan 	struct ext2fs_direct *ep, *lim;
613f92a609fSpelikan 	struct vnode *vdp;
614f92a609fSpelikan 	int offset = *entryoffsetinblockp;
615f92a609fSpelikan 	int dirblksize = ip->i_e2fs->e2fs_bsize;
616f92a609fSpelikan 	size_t namlen;
617f92a609fSpelikan 
618f92a609fSpelikan 	vdp = ITOV(ip);
619f92a609fSpelikan 
620f92a609fSpelikan 	lim = (struct ext2fs_direct *)
621f92a609fSpelikan 	    ((char *)data + dirblksize - EXT2FS_DIRSIZ(0));
622f92a609fSpelikan 	ep = (struct ext2fs_direct *) ((char *)data + offset);
623f92a609fSpelikan 
624f92a609fSpelikan 	while (ep < lim) {
625f92a609fSpelikan 		/*
626f92a609fSpelikan 		 * Full validation checks are slow, so we only check
627f92a609fSpelikan 		 * enough to insure forward progress through the
628f92a609fSpelikan 		 * directory. Complete checks can be run by patching
629f92a609fSpelikan 		 * "dirchk" to be true.
630f92a609fSpelikan 		 */
631f92a609fSpelikan 		if (ep->e2d_reclen == 0 ||
632f92a609fSpelikan 		    (dirchk && ext2fs_dirbadentry(vdp, ep, offset))) {
633f92a609fSpelikan 			int i;
634f92a609fSpelikan 			ufs_dirbad(ip, ip->i_offset, "mangled entry");
635f92a609fSpelikan 			i = dirblksize - (offset & (dirblksize - 1));
636f92a609fSpelikan 			ip->i_offset += i;
637f92a609fSpelikan 			offset += i;
638f92a609fSpelikan 			continue;
639f92a609fSpelikan 		}
640f92a609fSpelikan 
641f92a609fSpelikan 		/*
642f92a609fSpelikan 		 * If an appropriate sized slot has not yet been found,
643f92a609fSpelikan 		 * check to see if one is available. Also accumulate space
644f92a609fSpelikan 		 * in the current block so that we can determine if
645f92a609fSpelikan 		 * compaction is viable.
646f92a609fSpelikan 		 */
647f92a609fSpelikan 		if (ssp->slotstatus != FOUND) {
648f7dbefaaSpelikan 			int size = letoh16(ep->e2d_reclen);
649f92a609fSpelikan 
650f92a609fSpelikan 			if (ep->e2d_ino != 0)
651f92a609fSpelikan 				size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
652f92a609fSpelikan 			if (size > 0) {
653f92a609fSpelikan 				if (size >= ssp->slotneeded) {
654f92a609fSpelikan 					ssp->slotstatus = FOUND;
655f92a609fSpelikan 					ssp->slotoffset = ip->i_offset;
656f7dbefaaSpelikan 					ssp->slotsize = letoh16(ep->e2d_reclen);
657f92a609fSpelikan 				} else if (ssp->slotstatus == NONE) {
658f92a609fSpelikan 					ssp->slotfreespace += size;
659f92a609fSpelikan 					if (ssp->slotoffset == -1)
660f92a609fSpelikan 						ssp->slotoffset = ip->i_offset;
661f92a609fSpelikan 					if (ssp->slotfreespace >= ssp->slotneeded) {
662f92a609fSpelikan 						ssp->slotstatus = COMPACT;
663f92a609fSpelikan 						ssp->slotsize = ip->i_offset +
664f7dbefaaSpelikan 							  letoh16(ep->e2d_reclen) - ssp->slotoffset;
665f92a609fSpelikan 					}
666f92a609fSpelikan 				}
667f92a609fSpelikan 			}
668f92a609fSpelikan 		}
669f92a609fSpelikan 
670f92a609fSpelikan 		/*
671f92a609fSpelikan 		 * Check for a name match.
672f92a609fSpelikan 		 */
673f92a609fSpelikan 		if (ep->e2d_ino) {
674f92a609fSpelikan 			namlen = ep->e2d_namlen;
675f92a609fSpelikan 			if (namlen == cnp->cn_namelen &&
676f92a609fSpelikan 			    !memcmp(cnp->cn_nameptr, ep->e2d_name, namlen)) {
677f92a609fSpelikan 				/*
678f92a609fSpelikan 				 * Save directory entry's inode number and
679f92a609fSpelikan 				 * reclen in ndp->ni_ufs area, and release
680f92a609fSpelikan 				 * directory buffer.
681f92a609fSpelikan 				 */
682f92a609fSpelikan 				*foundp = 1;
683f92a609fSpelikan 				return (0);
684f92a609fSpelikan 			}
685f92a609fSpelikan 		}
686f92a609fSpelikan 		*prevoffp = ip->i_offset;
687f7dbefaaSpelikan 		ip->i_offset += letoh16(ep->e2d_reclen);
688f7dbefaaSpelikan 		offset += letoh16(ep->e2d_reclen);
689f92a609fSpelikan 		*entryoffsetinblockp = offset;
690f92a609fSpelikan 		if (ep->e2d_ino)
691f92a609fSpelikan 			*endusefulp = ip->i_offset;
692f92a609fSpelikan 
693f92a609fSpelikan 		/*
694f92a609fSpelikan 		 * Get pointer to the next entry.
695f92a609fSpelikan 		 */
696f92a609fSpelikan 		ep = (struct ext2fs_direct *) ((char *)data + offset);
697f92a609fSpelikan 	}
698f92a609fSpelikan 
699f92a609fSpelikan 	return (0);
700f92a609fSpelikan }
701f92a609fSpelikan 
7025ac2d602Sdownsj /*
7035ac2d602Sdownsj  * Do consistency checking on a directory entry:
7045ac2d602Sdownsj  *	record length must be multiple of 4
7055ac2d602Sdownsj  *	entry must fit in rest of its dirblksize block
7065ac2d602Sdownsj  *	record must be large enough to contain entry
7075ac2d602Sdownsj  *	name is not longer than MAXNAMLEN
7085ac2d602Sdownsj  *	name must be as long as advertised, and null terminated
7095ac2d602Sdownsj  */
7105ac2d602Sdownsj /*
7115ac2d602Sdownsj  *	changed so that it confirms to ext2fs_check_dir_entry
7125ac2d602Sdownsj  */
7135ac2d602Sdownsj static int
7145f64cd9cSjasper ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de,
7155f64cd9cSjasper     int entryoffsetinblock)
7165ac2d602Sdownsj {
7175ac2d602Sdownsj 	int dirblksize = VTOI(dp)->i_e2fs->e2fs_bsize;
7185ac2d602Sdownsj 	char *error_msg = NULL;
719f7dbefaaSpelikan 	int reclen = letoh16(de->e2d_reclen);
7207f1901c5Sart 	int namlen = de->e2d_namlen;
7215ac2d602Sdownsj 
7227f1901c5Sart 	if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */
7235ac2d602Sdownsj 		error_msg = "rec_len is smaller than minimal";
7247f1901c5Sart 	else if (reclen % 4 != 0)
7255ac2d602Sdownsj 		error_msg = "rec_len % 4 != 0";
7267f1901c5Sart 	else if (reclen < EXT2FS_DIRSIZ(namlen))
7275ac2d602Sdownsj 		error_msg = "reclen is too small for name_len";
7287f1901c5Sart 	else if (entryoffsetinblock + reclen > dirblksize)
7295ac2d602Sdownsj 		error_msg = "directory entry across blocks";
730f7dbefaaSpelikan 	else if (letoh32(de->e2d_ino) > VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
7315ac2d602Sdownsj 		error_msg = "inode out of bounds";
7325ac2d602Sdownsj 
7335ac2d602Sdownsj 	if (error_msg != NULL) {
7345ac2d602Sdownsj 		printf("bad directory entry: %s\n"
735f7dbefaaSpelikan 		    "offset=%d, inode=%u, rec_len=%d, name_len=%d \n",
736f7dbefaaSpelikan 		    error_msg, entryoffsetinblock, letoh32(de->e2d_ino),
7377f1901c5Sart 		    reclen, namlen);
738f7dbefaaSpelikan 		panic(__func__);
7395ac2d602Sdownsj 	}
740f7dbefaaSpelikan 	return (0);
7415ac2d602Sdownsj }
7425ac2d602Sdownsj 
7435ac2d602Sdownsj /*
7445ac2d602Sdownsj  * Write a directory entry after a call to namei, using the parameters
7455ac2d602Sdownsj  * that it left in nameidata.  The argument ip is the inode which the new
7465ac2d602Sdownsj  * directory entry will refer to.  Dvp is a pointer to the directory to
7475ac2d602Sdownsj  * be written, which was left locked by namei. Remaining parameters
7485ac2d602Sdownsj  * (dp->i_offset, dp->i_count) indicate how the space for the new
7495ac2d602Sdownsj  * entry is to be obtained.
7505ac2d602Sdownsj  */
7515ac2d602Sdownsj int
7525f64cd9cSjasper ext2fs_direnter(struct inode *ip, struct vnode *dvp,
7535f64cd9cSjasper     struct componentname *cnp)
7545ac2d602Sdownsj {
7557f1901c5Sart 	struct ext2fs_direct *ep, *nep;
7567f1901c5Sart 	struct inode *dp;
7575ac2d602Sdownsj 	struct buf *bp;
7585ac2d602Sdownsj 	struct ext2fs_direct newdir;
7595ac2d602Sdownsj 	struct iovec aiov;
7605ac2d602Sdownsj 	struct uio auio;
7615ac2d602Sdownsj 	u_int dsize;
7625ac2d602Sdownsj 	int error, loc, newentrysize, spacefree;
7635ac2d602Sdownsj 	char *dirbuf;
7645ac2d602Sdownsj 	int dirblksize = ip->i_e2fs->e2fs_bsize;
7655ac2d602Sdownsj 
7665ac2d602Sdownsj 
7675ac2d602Sdownsj #ifdef DIAGNOSTIC
7685ac2d602Sdownsj 	if ((cnp->cn_flags & SAVENAME) == 0)
7695ac2d602Sdownsj 		panic("direnter: missing name");
7705ac2d602Sdownsj #endif
7715ac2d602Sdownsj 	dp = VTOI(dvp);
772f7dbefaaSpelikan 	newdir.e2d_ino = htole32(ip->i_number);
7735ac2d602Sdownsj 	newdir.e2d_namlen = cnp->cn_namelen;
774f5ee6277Sjasoni 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
775f5ee6277Sjasoni 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
776fb844963Spedro 		newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
777f5ee6277Sjasoni 	} else {
778f5ee6277Sjasoni 		newdir.e2d_type = 0;
779*479c151dSjsg 	}
7807f1901c5Sart 	memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
7817f1901c5Sart 	newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
7825ac2d602Sdownsj 	if (dp->i_count == 0) {
7835ac2d602Sdownsj 		/*
7845ac2d602Sdownsj 		 * If dp->i_count is 0, then namei could find no
7855ac2d602Sdownsj 		 * space in the directory. Here, dp->i_offset will
7865ac2d602Sdownsj 		 * be on a directory block boundary and we will write the
7875ac2d602Sdownsj 		 * new entry into a fresh block.
7885ac2d602Sdownsj 		 */
7895ac2d602Sdownsj 		if (dp->i_offset & (dirblksize - 1))
7905ac2d602Sdownsj 			panic("ext2fs_direnter: newblk");
7915ac2d602Sdownsj 		auio.uio_offset = dp->i_offset;
792f7dbefaaSpelikan 		newdir.e2d_reclen = htole16(dirblksize);
7935ac2d602Sdownsj 		auio.uio_resid = newentrysize;
7945ac2d602Sdownsj 		aiov.iov_len = newentrysize;
7955ac2d602Sdownsj 		aiov.iov_base = (caddr_t)&newdir;
7965ac2d602Sdownsj 		auio.uio_iov = &aiov;
7975ac2d602Sdownsj 		auio.uio_iovcnt = 1;
7985ac2d602Sdownsj 		auio.uio_rw = UIO_WRITE;
7995ac2d602Sdownsj 		auio.uio_segflg = UIO_SYSSPACE;
80014bf419fSkrw 		auio.uio_procp = NULL;
8015ac2d602Sdownsj 		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
8025ac2d602Sdownsj 		if (dirblksize >
8035ac2d602Sdownsj 			VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
8045ac2d602Sdownsj 			/* XXX should grow with balloc() */
8055ac2d602Sdownsj 			panic("ext2fs_direnter: frag size");
8065ac2d602Sdownsj 		else if (!error) {
8074dcbbbb0Sniallo 			error = ext2fs_setsize(dp,
8084dcbbbb0Sniallo 				roundup(ext2fs_size(dp), dirblksize));
8094dcbbbb0Sniallo 			if (error)
8104dcbbbb0Sniallo 				return (error);
8115ac2d602Sdownsj 			dp->i_flag |= IN_CHANGE;
8125ac2d602Sdownsj 		}
8135ac2d602Sdownsj 		return (error);
8145ac2d602Sdownsj 	}
8155ac2d602Sdownsj 
8165ac2d602Sdownsj 	/*
8175ac2d602Sdownsj 	 * If dp->i_count is non-zero, then namei found space
8185ac2d602Sdownsj 	 * for the new entry in the range dp->i_offset to
8195ac2d602Sdownsj 	 * dp->i_offset + dp->i_count in the directory.
8205ac2d602Sdownsj 	 * To use this space, we may have to compact the entries located
8215ac2d602Sdownsj 	 * there, by copying them together towards the beginning of the
8225ac2d602Sdownsj 	 * block, leaving the free space in one usable chunk at the end.
8235ac2d602Sdownsj 	 */
8245ac2d602Sdownsj 
8255ac2d602Sdownsj 	/*
8265ac2d602Sdownsj 	 * Get the block containing the space for the new directory entry.
8275ac2d602Sdownsj 	 */
828b080ad39Scsapuntz 	if ((error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, &dirbuf, &bp))
829b080ad39Scsapuntz 	    != 0)
8305ac2d602Sdownsj 		return (error);
8315ac2d602Sdownsj 	/*
8325ac2d602Sdownsj 	 * Find space for the new entry. In the simple case, the entry at
8335ac2d602Sdownsj 	 * offset base will have the space. If it does not, then namei
8345ac2d602Sdownsj 	 * arranged that compacting the region dp->i_offset to
8355ac2d602Sdownsj 	 * dp->i_offset + dp->i_count would yield the
8365ac2d602Sdownsj 	 * space.
8375ac2d602Sdownsj 	 */
8385ac2d602Sdownsj 	ep = (struct ext2fs_direct *)dirbuf;
8395ac2d602Sdownsj 	dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
840f7dbefaaSpelikan 	spacefree = letoh16(ep->e2d_reclen) - dsize;
841f7dbefaaSpelikan 	for (loc = letoh16(ep->e2d_reclen); loc < dp->i_count; ) {
8425ac2d602Sdownsj 		nep = (struct ext2fs_direct *)(dirbuf + loc);
8435ac2d602Sdownsj 		if (ep->e2d_ino) {
8445ac2d602Sdownsj 			/* trim the existing slot */
845f7dbefaaSpelikan 			ep->e2d_reclen = htole16(dsize);
8465ac2d602Sdownsj 			ep = (struct ext2fs_direct *)((char *)ep + dsize);
8475ac2d602Sdownsj 		} else {
8485ac2d602Sdownsj 			/* overwrite; nothing there; header is ours */
8495ac2d602Sdownsj 			spacefree += dsize;
8505ac2d602Sdownsj 		}
8515ac2d602Sdownsj 		dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
852f7dbefaaSpelikan 		spacefree += letoh16(nep->e2d_reclen) - dsize;
853f7dbefaaSpelikan 		loc += letoh16(nep->e2d_reclen);
8540f5c6c8bStedu 		memcpy(ep, nep, dsize);
8555ac2d602Sdownsj 	}
8565ac2d602Sdownsj 	/*
8575ac2d602Sdownsj 	 * Update the pointer fields in the previous entry (if any),
8585ac2d602Sdownsj 	 * copy in the new entry, and write out the block.
8595ac2d602Sdownsj 	 */
8605ac2d602Sdownsj 	if (ep->e2d_ino == 0) {
8615ac2d602Sdownsj #ifdef DIAGNOSTIC
8625ac2d602Sdownsj 		if (spacefree + dsize < newentrysize)
8635ac2d602Sdownsj 			panic("ext2fs_direnter: compact1");
8645ac2d602Sdownsj #endif
865f7dbefaaSpelikan 		newdir.e2d_reclen = htole16(spacefree + dsize);
8665ac2d602Sdownsj 	} else {
8675ac2d602Sdownsj #ifdef DIAGNOSTIC
8685ac2d602Sdownsj 		if (spacefree < newentrysize) {
8695ac2d602Sdownsj 			printf("ext2fs_direnter: compact2 %u %u",
8705ac2d602Sdownsj 			    (u_int)spacefree, (u_int)newentrysize);
8715ac2d602Sdownsj 			panic("ext2fs_direnter: compact2");
8725ac2d602Sdownsj 		}
8735ac2d602Sdownsj #endif
874f7dbefaaSpelikan 		newdir.e2d_reclen = htole16(spacefree);
875f7dbefaaSpelikan 		ep->e2d_reclen = htole16(dsize);
8765ac2d602Sdownsj 		ep = (struct ext2fs_direct *)((char *)ep + dsize);
8775ac2d602Sdownsj 	}
8780f5c6c8bStedu 	memcpy(ep, &newdir, newentrysize);
8795ac2d602Sdownsj 	error = VOP_BWRITE(bp);
8805ac2d602Sdownsj 	dp->i_flag |= IN_CHANGE | IN_UPDATE;
8814dcbbbb0Sniallo 	if (!error && dp->i_endoff && dp->i_endoff < ext2fs_size(dp))
882b080ad39Scsapuntz 		error = ext2fs_truncate(dp, (off_t)dp->i_endoff, IO_SYNC,
883b080ad39Scsapuntz 		    cnp->cn_cred);
8845ac2d602Sdownsj 	return (error);
8855ac2d602Sdownsj }
8865ac2d602Sdownsj 
8875ac2d602Sdownsj /*
8885ac2d602Sdownsj  * Remove a directory entry after a call to namei, using
8895ac2d602Sdownsj  * the parameters which it left in nameidata. The entry
8905ac2d602Sdownsj  * dp->i_offset contains the offset into the directory of the
8915ac2d602Sdownsj  * entry to be eliminated.  The dp->i_count field contains the
8925ac2d602Sdownsj  * size of the previous record in the directory.  If this
8935ac2d602Sdownsj  * is 0, the first entry is being deleted, so we need only
8945ac2d602Sdownsj  * zero the inode number to mark the entry as free.  If the
8955ac2d602Sdownsj  * entry is not the first in the directory, we must reclaim
8965ac2d602Sdownsj  * the space of the now empty record by adding the record size
8975ac2d602Sdownsj  * to the size of the previous entry.
8985ac2d602Sdownsj  */
8995ac2d602Sdownsj int
9005f64cd9cSjasper ext2fs_dirremove(struct vnode *dvp, struct componentname *cnp)
9015ac2d602Sdownsj {
9027f1901c5Sart 	struct inode *dp;
9035ac2d602Sdownsj 	struct ext2fs_direct *ep;
9045ac2d602Sdownsj 	struct buf *bp;
9055ac2d602Sdownsj 	int error;
9065ac2d602Sdownsj 
9075ac2d602Sdownsj 	dp = VTOI(dvp);
9085ac2d602Sdownsj 	if (dp->i_count == 0) {
9095ac2d602Sdownsj 		/*
9105ac2d602Sdownsj 		 * First entry in block: set d_ino to zero.
9115ac2d602Sdownsj 		 */
912b080ad39Scsapuntz 		error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep,
913b080ad39Scsapuntz 		    &bp);
9145ac2d602Sdownsj 		if (error != 0)
9155ac2d602Sdownsj 			return (error);
9165ac2d602Sdownsj 		ep->e2d_ino = 0;
9175ac2d602Sdownsj 		error = VOP_BWRITE(bp);
9185ac2d602Sdownsj 		dp->i_flag |= IN_CHANGE | IN_UPDATE;
9195ac2d602Sdownsj 		return (error);
9205ac2d602Sdownsj 	}
9215ac2d602Sdownsj 	/*
9225ac2d602Sdownsj 	 * Collapse new free space into previous entry.
9235ac2d602Sdownsj 	 */
924b080ad39Scsapuntz 	error = ext2fs_bufatoff(dp, (off_t)(dp->i_offset - dp->i_count),
9255ac2d602Sdownsj 	    (char **)&ep, &bp);
9265ac2d602Sdownsj 	if (error != 0)
9275ac2d602Sdownsj 		return (error);
928f7dbefaaSpelikan 	ep->e2d_reclen = htole16(letoh16(ep->e2d_reclen) + dp->i_reclen);
9295ac2d602Sdownsj 	error = VOP_BWRITE(bp);
9305ac2d602Sdownsj 	dp->i_flag |= IN_CHANGE | IN_UPDATE;
9315ac2d602Sdownsj 	return (error);
9325ac2d602Sdownsj }
9335ac2d602Sdownsj 
9345ac2d602Sdownsj /*
9355ac2d602Sdownsj  * Rewrite an existing directory entry to point at the inode
9365ac2d602Sdownsj  * supplied.  The parameters describing the directory entry are
9375ac2d602Sdownsj  * set up by a call to namei.
9385ac2d602Sdownsj  */
9395ac2d602Sdownsj int
9405f64cd9cSjasper ext2fs_dirrewrite(struct inode *dp, struct inode *ip,
9415f64cd9cSjasper     struct componentname *cnp)
9425ac2d602Sdownsj {
9435ac2d602Sdownsj 	struct buf *bp;
9445ac2d602Sdownsj 	struct ext2fs_direct *ep;
9455ac2d602Sdownsj 	int error;
9465ac2d602Sdownsj 
947b080ad39Scsapuntz 	error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, &bp);
9485ac2d602Sdownsj 	if (error != 0)
9495ac2d602Sdownsj 		return (error);
950f7dbefaaSpelikan 	ep->e2d_ino = htole32(ip->i_number);
951f5ee6277Sjasoni 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
952f5ee6277Sjasoni 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
953fb844963Spedro 		ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
954f5ee6277Sjasoni 	} else {
955f5ee6277Sjasoni 		ep->e2d_type = 0;
956f5ee6277Sjasoni 	}
9575ac2d602Sdownsj 	error = VOP_BWRITE(bp);
9585ac2d602Sdownsj 	dp->i_flag |= IN_CHANGE | IN_UPDATE;
9595ac2d602Sdownsj 	return (error);
9605ac2d602Sdownsj }
9615ac2d602Sdownsj 
9625ac2d602Sdownsj /*
9635ac2d602Sdownsj  * Check if a directory is empty or not.
9645ac2d602Sdownsj  * Inode supplied must be locked.
9655ac2d602Sdownsj  *
9665ac2d602Sdownsj  * Using a struct dirtemplate here is not precisely
9675ac2d602Sdownsj  * what we want, but better than using a struct ext2fs_direct.
9685ac2d602Sdownsj  *
9695ac2d602Sdownsj  * NB: does not handle corrupted directories.
9705ac2d602Sdownsj  */
9715ac2d602Sdownsj int
972e012d6d3Sguenther ext2fs_dirempty(struct inode *ip, ufsino_t parentino, struct ucred *cred)
9735ac2d602Sdownsj {
9747f1901c5Sart 	off_t off;
9755ac2d602Sdownsj 	struct ext2fs_dirtemplate dbuf;
9767f1901c5Sart 	struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf;
9779b355cb2Smillert 	int error, namlen;
9789b355cb2Smillert 	size_t count;
9795ac2d602Sdownsj 
9805ac2d602Sdownsj #define	MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)
9815ac2d602Sdownsj 
982f7dbefaaSpelikan 	for (off = 0; off < ext2fs_size(ip); off += letoh16(dp->e2d_reclen)) {
9835ac2d602Sdownsj 		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
984717ea6e5Soga 		   UIO_SYSSPACE, IO_NODELOCKED, cred, &count, curproc);
9855ac2d602Sdownsj 		/*
9865ac2d602Sdownsj 		 * Since we read MINDIRSIZ, residual must
9875ac2d602Sdownsj 		 * be 0 unless we're at end of file.
9885ac2d602Sdownsj 		 */
9895ac2d602Sdownsj 		if (error || count != 0)
9905ac2d602Sdownsj 			return (0);
9915ac2d602Sdownsj 		/* avoid infinite loops */
9925ac2d602Sdownsj 		if (dp->e2d_reclen == 0)
9935ac2d602Sdownsj 			return (0);
9945ac2d602Sdownsj 		/* skip empty entries */
9955ac2d602Sdownsj 		if (dp->e2d_ino == 0)
9965ac2d602Sdownsj 			continue;
9975ac2d602Sdownsj 		/* accept only "." and ".." */
9985ac2d602Sdownsj 		namlen = dp->e2d_namlen;
9995ac2d602Sdownsj 		if (namlen > 2)
10005ac2d602Sdownsj 			return (0);
10015ac2d602Sdownsj 		if (dp->e2d_name[0] != '.')
10025ac2d602Sdownsj 			return (0);
10035ac2d602Sdownsj 		/*
10045ac2d602Sdownsj 		 * At this point namlen must be 1 or 2.
10055ac2d602Sdownsj 		 * 1 implies ".", 2 implies ".." if second
10065ac2d602Sdownsj 		 * char is also "."
10075ac2d602Sdownsj 		 */
10085ac2d602Sdownsj 		if (namlen == 1)
10095ac2d602Sdownsj 			continue;
1010f7dbefaaSpelikan 		if (dp->e2d_name[1] == '.' && letoh32(dp->e2d_ino) == parentino)
10115ac2d602Sdownsj 			continue;
10125ac2d602Sdownsj 		return (0);
10135ac2d602Sdownsj 	}
10145ac2d602Sdownsj 	return (1);
10155ac2d602Sdownsj }
10165ac2d602Sdownsj 
10175ac2d602Sdownsj /*
10185ac2d602Sdownsj  * Check if source directory is in the path of the target directory.
10195ac2d602Sdownsj  * Target is supplied locked, source is unlocked.
10205ac2d602Sdownsj  * The target is always vput before returning.
10215ac2d602Sdownsj  */
10225ac2d602Sdownsj int
10235f64cd9cSjasper ext2fs_checkpath(struct inode *source, struct inode *target,
10245f64cd9cSjasper    struct ucred *cred)
10255ac2d602Sdownsj {
10265ac2d602Sdownsj 	struct vnode *vp;
10275ac2d602Sdownsj 	int error, rootino, namlen;
10285ac2d602Sdownsj 	struct ext2fs_dirtemplate dirbuf;
10297f1901c5Sart 	u_int32_t ino;
10305ac2d602Sdownsj 
10315ac2d602Sdownsj 	vp = ITOV(target);
10325ac2d602Sdownsj 	if (target->i_number == source->i_number) {
10335ac2d602Sdownsj 		error = EEXIST;
10345ac2d602Sdownsj 		goto out;
10355ac2d602Sdownsj 	}
10365ac2d602Sdownsj 	rootino = ROOTINO;
10375ac2d602Sdownsj 	error = 0;
10385ac2d602Sdownsj 	if (target->i_number == rootino)
10395ac2d602Sdownsj 		goto out;
10405ac2d602Sdownsj 
10415ac2d602Sdownsj 	for (;;) {
10425ac2d602Sdownsj 		if (vp->v_type != VDIR) {
10435ac2d602Sdownsj 			error = ENOTDIR;
10445ac2d602Sdownsj 			break;
10455ac2d602Sdownsj 		}
10465ac2d602Sdownsj 		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
10477f1901c5Sart 			sizeof (struct ext2fs_dirtemplate), (off_t)0,
1048717ea6e5Soga 			UIO_SYSSPACE, IO_NODELOCKED, cred, NULL,
1049717ea6e5Soga 			curproc);
10505ac2d602Sdownsj 		if (error != 0)
10515ac2d602Sdownsj 			break;
10525ac2d602Sdownsj 		namlen = dirbuf.dotdot_namlen;
10535ac2d602Sdownsj 		if (namlen != 2 ||
10545ac2d602Sdownsj 			dirbuf.dotdot_name[0] != '.' ||
10555ac2d602Sdownsj 			dirbuf.dotdot_name[1] != '.') {
10565ac2d602Sdownsj 			error = ENOTDIR;
10575ac2d602Sdownsj 			break;
10585ac2d602Sdownsj 		}
1059f7dbefaaSpelikan 		ino = letoh32(dirbuf.dotdot_ino);
10607f1901c5Sart 		if (ino == source->i_number) {
10615ac2d602Sdownsj 			error = EINVAL;
10625ac2d602Sdownsj 			break;
10635ac2d602Sdownsj 		}
10647f1901c5Sart 		if (ino == rootino)
10655ac2d602Sdownsj 			break;
10665ac2d602Sdownsj 		vput(vp);
10677f1901c5Sart 		error = VFS_VGET(vp->v_mount, ino, &vp);
10685ac2d602Sdownsj 		if (error != 0) {
10695ac2d602Sdownsj 			vp = NULL;
10705ac2d602Sdownsj 			break;
10715ac2d602Sdownsj 		}
10725ac2d602Sdownsj 	}
10735ac2d602Sdownsj 
10745ac2d602Sdownsj out:
10755ac2d602Sdownsj 	if (error == ENOTDIR) {
10765ac2d602Sdownsj 		printf("checkpath: .. not a directory\n");
10775ac2d602Sdownsj 		panic("checkpath");
10785ac2d602Sdownsj 	}
10795ac2d602Sdownsj 	if (vp != NULL)
10805ac2d602Sdownsj 		vput(vp);
10815ac2d602Sdownsj 	return (error);
10825ac2d602Sdownsj }
1083