⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ufs_lookup.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Copyright (c) 1989, 1993 *	The Regents of the University of California.  All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *	@(#)ufs_lookup.c	8.6 (Berkeley) 4/1/94 */#include <sys/param.h>#include <sys/namei.h>#include <sys/buf.h>#include <sys/file.h>#include <sys/mount.h>#include <sys/vnode.h>#include <ufs/ufs/quota.h>#include <ufs/ufs/inode.h>#include <ufs/ufs/dir.h>#include <ufs/ufs/ufsmount.h>#include <ufs/ufs/ufs_extern.h>struct	nchstats nchstats;#ifdef DIAGNOSTICint	dirchk = 1;#elseint	dirchk = 0;#endif#define FSFMT(vp)	((vp)->v_mount->mnt_maxsymlinklen <= 0)/* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. * If the file system is not maintained in a strict tree hierarchy, * this can result in a deadlock situation (see comments in code below). * * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending * on whether the name is to be looked up, created, renamed, or deleted. * When CREATE, RENAME, or DELETE is specified, information usable in * creating, renaming, or deleting a directory entry may be calculated. * If flag has LOCKPARENT or'ed into it and the target of the pathname * exists, lookup returns both the target and its parent directory locked. * When creating or renaming and LOCKPARENT is specified, the target may * not be ".".  When deleting and LOCKPARENT is specified, the target may * be "."., but the caller must check to ensure it does an vrele and vput * instead of two vputs. * * Overall outline of ufs_lookup: * *	check accessibility of directory *	look for name in cache, if found, then if at end of path *	  and deleting or creating, drop it, else return name *	search for name in directory, to found or notfound * notfound: *	if creating, return locked directory, leaving info on available slots *	else return error * found: *	if at end of path and deleting, return information to allow delete *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target *	  inode and return info to allow rewrite *	if not at end, add name to cache; if at end and neither creating *	  nor deleting, add name to cache */intufs_lookup(ap)	struct vop_lookup_args /* {		struct vnode *a_dvp;		struct vnode **a_vpp;		struct componentname *a_cnp;	} */ *ap;{	register struct vnode *vdp;	/* vnode for directory being searched */	register struct inode *dp;	/* inode for directory being searched */	struct buf *bp;			/* a buffer of directory entries */	register struct direct *ep;	/* the current directory entry */	int entryoffsetinblock;		/* offset of ep in bp's buffer */	enum {NONE, COMPACT, FOUND} slotstatus;	doff_t slotoffset;		/* offset of area with free space */	int slotsize;			/* size of area at slotoffset */	int slotfreespace;		/* amount of space free in slot */	int slotneeded;			/* size of the entry we're seeking */	int numdirpasses;		/* strategy for directory search */	doff_t endsearch;		/* offset to end directory search */	doff_t prevoff;			/* prev entry dp->i_offset */	struct vnode *pdp;		/* saved dp during symlink work */	struct vnode *tdp;		/* returned by VFS_VGET */	doff_t enduseful;		/* pointer past last used dir slot */	u_long bmask;			/* block offset mask */	int lockparent;			/* 1 => lockparent flag is set */	int wantparent;			/* 1 => wantparent or lockparent flag */	int namlen, error;	struct vnode **vpp = ap->a_vpp;	struct componentname *cnp = ap->a_cnp;	struct ucred *cred = cnp->cn_cred;	int flags = cnp->cn_flags;	int nameiop = cnp->cn_nameiop;	bp = NULL;	slotoffset = -1;	*vpp = NULL;	vdp = ap->a_dvp;	dp = VTOI(vdp);	lockparent = flags & LOCKPARENT;	wantparent = flags & (LOCKPARENT|WANTPARENT);	/*	 * Check accessiblity of directory.	 */	if ((dp->i_mode & IFMT) != IFDIR)		return (ENOTDIR);	if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc))		return (error);	/*	 * We now have a segment name to search for, and a directory to search.	 *	 * Before tediously performing a linear scan of the directory,	 * check the name cache to see if the directory/name pair	 * we are looking for is known already.	 */	if (error = cache_lookup(vdp, vpp, cnp)) {		int vpid;	/* capability number of vnode */		if (error == ENOENT)			return (error);		/*		 * Get the next vnode in the path.		 * See comment below starting `Step through' for		 * an explaination of the locking protocol.		 */		pdp = vdp;		dp = VTOI(*vpp);		vdp = *vpp;		vpid = vdp->v_id;		if (pdp == vdp) {   /* lookup on "." */			VREF(vdp);			error = 0;		} else if (flags & ISDOTDOT) {			VOP_UNLOCK(pdp);			error = vget(vdp, 1);			if (!error && lockparent && (flags & ISLASTCN))				error = VOP_LOCK(pdp);		} else {			error = vget(vdp, 1);			if (!lockparent || error || !(flags & ISLASTCN))				VOP_UNLOCK(pdp);		}		/*		 * Check that the capability number did not change		 * while we were waiting for the lock.		 */		if (!error) {			if (vpid == vdp->v_id)				return (0);			vput(vdp);			if (lockparent && pdp != vdp && (flags & ISLASTCN))				VOP_UNLOCK(pdp);		}		if (error = VOP_LOCK(pdp))			return (error);		vdp = pdp;		dp = VTOI(pdp);		*vpp = NULL;	}	/*	 * Suppress search for slots unless creating	 * file and at end of pathname, in which case	 * we watch for a place to put the new file in	 * case it doesn't already exist.	 */	slotstatus = FOUND;	slotfreespace = slotsize = slotneeded = 0;	if ((nameiop == CREATE || nameiop == RENAME) &&	    (flags & ISLASTCN)) {		slotstatus = NONE;		slotneeded = (sizeof(struct direct) - MAXNAMLEN +			cnp->cn_namelen + 3) &~ 3;	}	/*	 * If there is cached information on a previous search of	 * this directory, pick up where we last left off.	 * We cache only lookups as these are the most common	 * and have the greatest payoff. Caching CREATE has little	 * benefit as it usually must search the entire directory	 * to determine that the entry does not exist. Caching the	 * location of the last DELETE or RENAME has not reduced	 * profiling time and hence has been removed in the interest	 * of simplicity.	 */	bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;	if (nameiop != LOOKUP || dp->i_diroff == 0 ||	    dp->i_diroff > dp->i_size) {		entryoffsetinblock = 0;		dp->i_offset = 0;		numdirpasses = 1;	} else {		dp->i_offset = dp->i_diroff;		if ((entryoffsetinblock = dp->i_offset & bmask) &&		    (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))			return (error);		numdirpasses = 2;		nchstats.ncs_2passes++;	}	prevoff = dp->i_offset;	endsearch = roundup(dp->i_size, DIRBLKSIZ);	enduseful = 0;searchloop:	while (dp->i_offset < endsearch) {		/*		 * If necessary, get the next directory block.		 */		if ((dp->i_offset & bmask) == 0) {			if (bp != NULL)				brelse(bp);			if (error =			    VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))				return (error);			entryoffsetinblock = 0;		}		/*		 * If still looking for a slot, and at a DIRBLKSIZE		 * boundary, have to start looking for free space again.		 */		if (slotstatus == NONE &&		    (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {			slotoffset = -1;			slotfreespace = 0;		}		/*		 * Get pointer to next entry.		 * Full validation checks are slow, so we only check		 * enough to insure forward progress through the		 * directory. Complete checks can be run by patching		 * "dirchk" to be true.		 */		ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);		if (ep->d_reclen == 0 ||		    dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) {			int i;			ufs_dirbad(dp, dp->i_offset, "mangled entry");			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));			dp->i_offset += i;			entryoffsetinblock += i;			continue;		}		/*		 * If an appropriate sized slot has not yet been found,		 * check to see if one is available. Also accumulate space		 * in the current block so that we can determine if		 * compaction is viable.		 */		if (slotstatus != FOUND) {			int size = ep->d_reclen;			if (ep->d_ino != 0)				size -= DIRSIZ(FSFMT(vdp), ep);			if (size > 0) {				if (size >= slotneeded) {					slotstatus = FOUND;					slotoffset = dp->i_offset;					slotsize = ep->d_reclen;				} else if (slotstatus == NONE) {					slotfreespace += size;					if (slotoffset == -1)						slotoffset = dp->i_offset;					if (slotfreespace >= slotneeded) {						slotstatus = COMPACT;						slotsize = dp->i_offset +						      ep->d_reclen - slotoffset;					}				}			}		}		/*		 * Check for a name match.		 */		if (ep->d_ino) {#			if (BYTE_ORDER == LITTLE_ENDIAN)				if (vdp->v_mount->mnt_maxsymlinklen > 0)					namlen = ep->d_namlen;				else					namlen = ep->d_type;#			else				namlen = ep->d_namlen;#			endif			if (namlen == cnp->cn_namelen &&			    !bcmp(cnp->cn_nameptr, ep->d_name,				(unsigned)namlen)) {				/*				 * Save directory entry's inode number and				 * reclen in ndp->ni_ufs area, and release				 * directory buffer.				 */				dp->i_ino = ep->d_ino;				dp->i_reclen = ep->d_reclen;				brelse(bp);				goto found;			}		}		prevoff = dp->i_offset;		dp->i_offset += ep->d_reclen;		entryoffsetinblock += ep->d_reclen;		if (ep->d_ino)			enduseful = dp->i_offset;	}/* notfound: */	/*	 * If we started in the middle of the directory and failed	 * to find our target, we must check the beginning as well.	 */	if (numdirpasses == 2) {		numdirpasses--;		dp->i_offset = 0;		endsearch = dp->i_diroff;		goto searchloop;	}	if (bp != NULL)		brelse(bp);	/*	 * If creating, and at end of pathname and current	 * directory has not been removed, then can consider	 * allowing file to be created.	 */	if ((nameiop == CREATE || nameiop == RENAME) &&	    (flags & ISLASTCN) && dp->i_nlink != 0) {		/*		 * Access for write is interpreted as allowing		 * creation of files in the directory.		 */		if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))			return (error);		/*		 * Return an indication of where the new directory		 * entry should be put.  If we didn't find a slot,		 * then set dp->i_count to 0 indicating		 * that the new slot belongs at the end of the		 * directory. If we found a slot, then the new entry		 * can be put in the range from dp->i_offset to		 * dp->i_offset + dp->i_count.		 */		if (slotstatus == NONE) {			dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);			dp->i_count = 0;			enduseful = dp->i_offset;		} else {			dp->i_offset = slotoffset;			dp->i_count = slotsize;			if (enduseful < slotoffset + slotsize)				enduseful = slotoffset + slotsize;		}		dp->i_endoff = roundup(enduseful, DIRBLKSIZ);		dp->i_flag |= IN_CHANGE | IN_UPDATE;		/*		 * We return with the directory locked, so that		 * the parameters we set up above will still be		 * valid if we actually decide to do a direnter().		 * We return ni_vp == NULL to indicate that the entry		 * does not currently exist; we leave a pointer to		 * the (locked) directory inode in ndp->ni_dvp.		 * The pathname buffer is saved so that the name		 * can be obtained later.		 *		 * NB - if the directory is unlocked, then this		 * information cannot be used.		 */		cnp->cn_flags |= SAVENAME;		if (!lockparent)			VOP_UNLOCK(vdp);		return (EJUSTRETURN);	}	/*	 * Insert name into cache (as non-existent) if appropriate.	 */	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)		cache_enter(vdp, *vpp, cnp);	return (ENOENT);found:	if (numdirpasses == 2)		nchstats.ncs_pass2++;	/*	 * Check that directory length properly reflects presence	 * of this entry.	 */	if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) {		ufs_dirbad(dp, dp->i_offset, "i_size too small");		dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep);		dp->i_flag |= IN_CHANGE | IN_UPDATE;	}	/*	 * Found component in pathname.	 * If the final component of path name, save information	 * in the cache as to where the entry was found.	 */	if ((flags & ISLASTCN) && nameiop == LOOKUP)		dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);	/*	 * If deleting, and at end of pathname, return	 * parameters which can be used to remove file.	 * If the wantparent flag isn't set, we return only	 * the directory (in ndp->ni_dvp), otherwise we go	 * on and lock the inode, being careful with ".".	 */	if (nameiop == DELETE && (flags & ISLASTCN)) {		/*		 * Write access to directory required to delete files.		 */		if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))			return (error);		/*		 * Return pointer to current entry in dp->i_offset,		 * and distance past previous entry (if there		 * is a previous entry in this block) in dp->i_count.		 * Save directory inode pointer in ndp->ni_dvp for dirremove().		 */		if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)			dp->i_count = 0;		else			dp->i_count = dp->i_offset - prevoff;		if (dp->i_number == dp->i_ino) {			VREF(vdp);			*vpp = vdp;			return (0);		}		if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))			return (error);		/*		 * If directory is "sticky", then user must own		 * the directory, or the file in it, else she		 * may not delete it (unless she's root). This		 * implements append-only directories.		 */		if ((dp->i_mode & ISVTX) &&		    cred->cr_uid != 0 &&		    cred->cr_uid != dp->i_uid &&		    VTOI(tdp)->i_uid != cred->cr_uid) {			vput(tdp);			return (EPERM);		}		*vpp = tdp;		if (!lockparent)			VOP_UNLOCK(vdp);		return (0);	}	/*

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -