Home | History | Annotate | Line # | Download | only in filecorefs
filecore_lookup.c revision 1.1
      1 /*	$NetBSD: filecore_lookup.c,v 1.1 2002/12/23 17:30:40 jdolecek Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998 Andrew McMurry
      5  * Copyright (c) 1989, 1993, 1994 The Regents of the University of California.
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by the University of
     19  *	California, Berkeley and its contributors.
     20  * 4. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  *
     36  *	filecore_lookup.c	1.1	1998/6/26
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 __KERNEL_RCSID(0, "$NetBSD: filecore_lookup.c,v 1.1 2002/12/23 17:30:40 jdolecek Exp $");
     41 
     42 #include <sys/param.h>
     43 #include <sys/namei.h>
     44 #include <sys/buf.h>
     45 #include <sys/file.h>
     46 #include <sys/vnode.h>
     47 #include <sys/mount.h>
     48 #include <sys/systm.h>
     49 
     50 #include <fs/filecorefs/filecore.h>
     51 #include <fs/filecorefs/filecore_extern.h>
     52 #include <fs/filecorefs/filecore_node.h>
     53 
     54 struct	nchstats filecore_nchstats;
     55 
     56 /*
     57  * Convert a component of a pathname into a pointer to a locked inode.
     58  * This is a very central and rather complicated routine.
     59  * If the file system is not maintained in a strict tree hierarchy,
     60  * this can result in a deadlock situation (see comments in code below).
     61  *
     62  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
     63  * whether the name is to be looked up, created, renamed, or deleted.
     64  * When CREATE, RENAME, or DELETE is specified, information usable in
     65  * creating, renaming, or deleting a directory entry may be calculated.
     66  * If flag has LOCKPARENT or'ed into it and the target of the pathname
     67  * exists, lookup returns both the target and its parent directory locked.
     68  * When creating or renaming and LOCKPARENT is specified, the target may
     69  * not be ".".  When deleting and LOCKPARENT is specified, the target may
     70  * be "."., but the caller must check to ensure it does an vrele and iput
     71  * instead of two iputs.
     72  *
     73  * Overall outline of ufs_lookup:
     74  *
     75  *	check accessibility of directory
     76  *	look for name in cache, if found, then if at end of path
     77  *	  and deleting or creating, drop it, else return name
     78  *	search for name in directory, to found or notfound
     79  * notfound:
     80  *	if creating, return locked directory, leaving info on available slots
     81  *	else return error
     82  * found:
     83  *	if at end of path and deleting, return information to allow delete
     84  *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
     85  *	  inode and return info to allow rewrite
     86  *	if not at end, add name to cache; if at end and neither creating
     87  *	  nor deleting, add name to cache
     88  *
     89  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
     90  */
     91 int
     92 filecore_lookup(v)
     93 	void *v;
     94 {
     95 	struct vop_lookup_args /* {
     96 		struct vnode *a_dvp;
     97 		struct vnode **a_vpp;
     98 		struct componentname *a_cnp;
     99 	} */ *ap = v;
    100 	struct vnode *vdp;		/* vnode for directory being searched */
    101 	struct filecore_node *dp;	/* inode for directory being searched */
    102 	struct filecore_mnt *fcmp;	/* file system that directory is in */
    103 	struct buf *bp;			/* a buffer of directory entries */
    104 	struct filecore_direntry *de;
    105 	int numdirpasses;		/* strategy for directory search */
    106 	struct vnode *pdp;		/* saved dp during symlink work */
    107 	struct vnode *tdp;		/* returned by filecore_vget_internal */
    108 	int lockparent;			/* 1 => lockparent flag is set */
    109 	int wantparent;			/* 1 => wantparent or lockparent flag */
    110 	int error;
    111 	u_short namelen;
    112 	int res;
    113 	const char *name;
    114 	struct vnode **vpp = ap->a_vpp;
    115 	struct componentname *cnp = ap->a_cnp;
    116 	struct ucred *cred = cnp->cn_cred;
    117 	int flags;
    118 	int nameiop = cnp->cn_nameiop;
    119 	int i, endsearch;
    120 
    121 	cnp->cn_flags &= ~PDIRUNLOCK;
    122 	flags = cnp->cn_flags;
    123 
    124 	bp = NULL;
    125 	*vpp = NULL;
    126 	vdp = ap->a_dvp;
    127 	dp = VTOI(vdp);
    128 	fcmp = dp->i_mnt;
    129 	lockparent = flags & LOCKPARENT;
    130 	wantparent = flags & (LOCKPARENT|WANTPARENT);
    131 
    132 	/*
    133 	 * Check accessiblity of directory.
    134 	 */
    135 	if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
    136 		return (error);
    137 
    138 	if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
    139 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
    140 		return (EROFS);
    141 
    142 	/*
    143 	 * We now have a segment name to search for, and a directory to search.
    144 	 *
    145 	 * Before tediously performing a linear scan of the directory,
    146 	 * check the name cache to see if the directory/name pair
    147 	 * we are looking for is known already.
    148 	 */
    149 	if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
    150 		return (error);
    151 
    152 	name = cnp->cn_nameptr;
    153 	namelen = cnp->cn_namelen;
    154 
    155 	/*
    156 	 * If there is cached information on a previous search of
    157 	 * this directory, pick up where we last left off.
    158 	 * We cache only lookups as these are the most common
    159 	 * and have the greatest payoff. Caching CREATE has little
    160 	 * benefit as it usually must search the entire directory
    161 	 * to determine that the entry does not exist. Caching the
    162 	 * location of the last DELETE or RENAME has not reduced
    163 	 * profiling time and hence has been removed in the interest
    164 	 * of simplicity.
    165 	 */
    166 	if (nameiop != LOOKUP || dp->i_diroff == 0 ||
    167 	    dp->i_diroff >= FILECORE_MAXDIRENTS) {
    168 		i = 0;
    169 		numdirpasses = 1;
    170 	} else {
    171 		i = dp->i_diroff;
    172 		numdirpasses = 2;
    173 		filecore_nchstats.ncs_2passes++;
    174 	}
    175 	endsearch = FILECORE_MAXDIRENTS;
    176 
    177 	if ((flags & ISDOTDOT) || (name[0] == '.' && namelen == 1))
    178 		goto found;
    179 
    180 	error = filecore_dbread(dp, &bp);
    181 	if (error) {
    182 		brelse(bp);
    183 		return error;
    184 	}
    185 
    186 	de = fcdirentry(bp->b_data, i);
    187 
    188 searchloop:
    189 	while (de->name[0] != 0 && i < endsearch) {
    190 		/*
    191 		 * Check for a name match.
    192 		 */
    193 		res = filecore_fncmp(de->name, name, namelen);
    194 
    195 		if (res == 0)
    196 			goto found;
    197 		if (res < 0)
    198 			goto notfound;
    199 
    200 		i++;
    201 		de++;
    202 	}
    203 
    204 notfound:
    205 	/*
    206 	 * If we started in the middle of the directory and failed
    207 	 * to find our target, we must check the beginning as well.
    208 	 */
    209 	if (numdirpasses == 2) {
    210 		numdirpasses--;
    211 		i = 0;
    212 		de = fcdirentry(bp->b_data, i);
    213 		endsearch = dp->i_diroff;
    214 		goto searchloop;
    215 	}
    216 	if (bp != NULL) {
    217 #ifdef FILECORE_DEBUG_BR
    218 			printf("brelse(%p) lo1\n", bp);
    219 #endif
    220 		brelse(bp);
    221 	}
    222 
    223 	/*
    224 	 * Insert name into cache (as non-existent) if appropriate.
    225 	 */
    226 	if (cnp->cn_flags & MAKEENTRY)
    227 		cache_enter(vdp, *vpp, cnp);
    228 	if (nameiop == CREATE || nameiop == RENAME)
    229 		return (EROFS);
    230 	return (ENOENT);
    231 
    232 found:
    233 	if (numdirpasses == 2)
    234 		filecore_nchstats.ncs_pass2++;
    235 
    236 	/*
    237 	 * Found component in pathname.
    238 	 * If the final component of path name, save information
    239 	 * in the cache as to where the entry was found.
    240 	 */
    241 	if ((flags & ISLASTCN) && nameiop == LOOKUP)
    242 		dp->i_diroff = i;
    243 
    244 	/*
    245 	 * Step through the translation in the name.  We do not `iput' the
    246 	 * directory because we may need it again if a symbolic link
    247 	 * is relative to the current directory.  Instead we save it
    248 	 * unlocked as "pdp".  We must get the target inode before unlocking
    249 	 * the directory to insure that the inode will not be removed
    250 	 * before we get it.  We prevent deadlock by always fetching
    251 	 * inodes from the root, moving down the directory tree. Thus
    252 	 * when following backward pointers ".." we must unlock the
    253 	 * parent directory before getting the requested directory.
    254 	 * There is a potential race condition here if both the current
    255 	 * and parent directories are removed before the `iget' for the
    256 	 * inode associated with ".." returns.  We hope that this occurs
    257 	 * infrequently since we cannot avoid this race condition without
    258 	 * implementing a sophisticated deadlock detection algorithm.
    259 	 * Note also that this simple deadlock detection scheme will not
    260 	 * work if the file system has any hard links other than ".."
    261 	 * that point backwards in the directory structure.
    262 	 */
    263 	pdp = vdp;
    264 	/*
    265 	 * If ino is different from dp->i_ino,
    266 	 * it's a relocated directory.
    267 	 */
    268 	if (flags & ISDOTDOT) {
    269 		ino_t pin = filecore_getparent(dp);
    270 		VOP_UNLOCK(pdp, 0);	/* race to get the inode */
    271 		cnp->cn_flags |= PDIRUNLOCK;
    272 		error = VFS_VGET(vdp->v_mount, pin, &tdp);
    273 		if (error) {
    274 			if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0)
    275 				cnp->cn_flags &= ~PDIRUNLOCK;
    276 			return (error);
    277 		}
    278 		if (lockparent && (flags & ISLASTCN)) {
    279 			if ((error = vn_lock(pdp, LK_EXCLUSIVE))) {
    280 				vput(tdp);
    281 				return (error);
    282 			}
    283 			cnp->cn_flags &= ~PDIRUNLOCK;
    284 		}
    285 		*vpp = tdp;
    286 	} else if (name[0] == '.' && namelen == 1) {
    287 		VREF(vdp);	/* we want ourself, ie "." */
    288 		*vpp = vdp;
    289 	} else {
    290 #ifdef FILECORE_DEBUG_BR
    291 			printf("brelse(%p) lo4\n", bp);
    292 #endif
    293 		brelse(bp);
    294 		error = VFS_VGET(vdp->v_mount, dp->i_dirent.addr |
    295 		    (i << FILECORE_INO_INDEX), &tdp);
    296 		if (error)
    297 			return (error);
    298 		if (!lockparent || !(flags & ISLASTCN)) {
    299 			VOP_UNLOCK(pdp, 0);
    300 			cnp->cn_flags |= PDIRUNLOCK;
    301 		}
    302 		*vpp = tdp;
    303 	}
    304 
    305 	/*
    306 	 * Insert name into cache if appropriate.
    307 	 */
    308 	if (cnp->cn_flags & MAKEENTRY)
    309 		cache_enter(vdp, *vpp, cnp);
    310 	return (0);
    311 }
    312