PERFORCE change 179180 for review

Efstratios Karatzas gpf at FreeBSD.org
Fri Jun 4 16:04:19 UTC 2010


http://p4web.freebsd.org/@@179180?ac=10

Change 179180 by gpf at gpf_desktop on 2010/06/04 16:04:06

	- Implemented Exhaustive Search for UFS (yeah it works this time)
	
	I actually changed how VOP_GETPARENT works internally, meaning 
	ffs_getparent() and dir_ilookup(). Now, a depth first search is
	performed if we supply the EXHAUSTSEARCH flag. I am not going 
	to change NFS code so that this flag is given to vn_fullpath_nocache()
	because it is a huge penalty to performance. Perhaps we should 
	add a new mount_nfs option so that a sys/admin can choose if he 
	wants this functionality or not.
	
	Anyhoo, tested vn_fullpath_nocache() with a lkm and this seems 
	to work fine. Please refer to the updated comment headers of the 
	functions for more info.
	
	ta ta

Affected files ...

.. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/ufs/ffs/ffs_vnops.c#5 edit

Differences ...

==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/ufs/ffs/ffs_vnops.c#5 (text+ko) ====

@@ -1802,13 +1802,24 @@
 #define WANTNAME 0x0004
 
 /*
- * XXXgpf: used by VOP_GETPARENT
+ * XXXgpf: this function is used by VOP_GETPARENT
  * 
- * find the name that is used to reference vp inside the directory vnode dvp.
+ * find 'a' name that is used to reference vp inside some parent directory.
  * flags should be set to WANTNAME if the filename should be copied to
  * the supplied buffer.
  * 
- * locks: dvp must be locked on entry and will still be locked on exit
+ * flags should be set to EXHAUSTSEARCH if want to perform a depth first search 
+ * on the filesystem that contains vp. In this case, tmpdvp should be the root vnode
+ * of the filesystem.  If a parent directory is found, dvp will point to it, unless the 
+ * third parameter is NULL which is a bad idea because that vnode will be locked/referenced on success
+ * and needs to be vput()d by the caller.
+ * 
+ * If flags is not set to EXHAUSTSEARCH, only tmpdvp will be searched for the vnode in question - vp.
+ * In this case, it is acceptable to provide NULL as the third parameter.
+ * 
+ * locks: tmpdvp must be locked on entry and will still be locked on exit.
+ * If we are performing an exhaustive search, dvp will be locked and have its reference count incremented 
+ * on success.
  * 
  * returns:
  * 	- ENOENT	a file that corresponds to vp was not found inside dvp,
@@ -1817,23 +1828,26 @@
  * 	- EOVERFLOW	result does not fit in buffer "name"
  */
 static int
-dir_ilookup(struct vnode *vp, struct vnode *dvp, char *name, int *namelen, int flags)
+dir_ilookup(struct vnode *vp, struct vnode *tmpdvp, struct vnode **dvp, char *name, int *namelen, int flags)
 {
 	struct uio io;
 	struct iovec iov;
 	struct dirent *dp, *edp;
 	struct thread *td;
+	struct mount *mp;
+	struct vnode *childvp;
 	char *dirbuf;	
 	u_int64_t dirbuflen;
 	int error, eofflag;
 	char foundit;
-	
-	if (dvp->v_type != VDIR) {
+
+	if (tmpdvp->v_type != VDIR) {
 		return ENOENT;
 	}
-
+	
 	foundit = 0;
-	dirbuflen = ((struct inode *)dvp->v_data)->i_size;
+	mp = vp->v_mount;
+	dirbuflen = ((struct inode *)tmpdvp->v_data)->i_size;
 	dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
 	td = curthread;
 	
@@ -1849,7 +1863,7 @@
 	io.uio_td = td;
 	eofflag = 0;
 
-	error = VOP_READDIR(dvp, &io, td->td_ucred, &eofflag, NULL, NULL);
+	error = VOP_READDIR(tmpdvp, &io, td->td_ucred, &eofflag, NULL, NULL);
 	if (error) {
 		error = EIO;
 		goto out;
@@ -1858,12 +1872,21 @@
 	/* search for the correct inode number inside the directory */
 	edp = (struct dirent *)&dirbuf[dirbuflen - io.uio_resid];
 	for (dp = (struct dirent *)dirbuf; dp < edp; ) {
+		if (!strcmp(((struct dirent *)dp)->d_name, ".") || 
+		    !strcmp(((struct dirent *)dp)->d_name, "..")) {
+			dp = (struct dirent *)((char *)dp + dp->d_reclen);
+			continue;
+		}
+
 		if (dp->d_reclen > 0) {	
 			/* found it */
 			if ( ((struct inode *)vp->v_data)->i_number == ((struct dirent *)dp)->d_fileno) {
 				char *pch;
 				int len;
 				
+				if (dvp != NULL)
+					*dvp = tmpdvp;
+
 				if (flags & WANTNAME) { 
 					pch = ((struct dirent *)dp)->d_name;
 					len = strlen(pch);
@@ -1876,10 +1899,32 @@
 					strlcpy(name, ((struct dirent *)dp)->d_name, *namelen);
 					*namelen -= len + 1;
 				}
-				
+
 				foundit = 1;
+				error = 0;
 				break;
 			}
+			/* recusivly traverse the fs if we have the EXHAUSTSEARCH flag set */
+			else if ((flags & EXHAUSTSEARCH) && ((struct dirent *)dp)->d_type == DT_DIR) {
+				error = VFS_VGET(mp, ((struct dirent *)dp)->d_fileno, LK_SHARED, &childvp);
+				/* no reason to traverse other filesystems */
+				if (!error && !(childvp->v_vflag & VV_ROOT)) {
+					error = dir_ilookup(vp, childvp, dvp, name, namelen, flags);
+					if (error) {
+						vput(childvp);
+					}
+					/* don't vput the directory vnode that contains vp */
+					else if (childvp != *dvp) {
+						vput(childvp);
+					}
+					
+					if (!error) {
+						foundit = 1;
+						error = 0;
+						break;
+					}
+				}
+			}
 			dp = (struct dirent *)((char *)dp + dp->d_reclen);
 		} 
 		else {
@@ -1893,7 +1938,7 @@
 		free(dirbuf, M_TEMP);
 	}
 	
-	if (foundit == 0 && error != 0) {
+	if (foundit == 0 && error == 0) {
 		error = ENOENT;
 	}
 	
@@ -1932,17 +1977,21 @@
 */
 {
 	struct mount *mp;
-	struct vnode *vp, *dvp;
-	int error, flags;	
+	struct vnode *vp, *dvp, *startdvp;
+	int error, flags, tmpflags;	
 	
 	error = 0;
 	vp = ap->a_vp;
 	mp = vp->v_mount;
+	dvp = NULL;
 	flags = ap->a_flags;
 
 	KASSERT(vp != NULL, ("VOP_GEPARENT: null vp"));
 	if (flags & WANTNAME)
 		KASSERT(ap->a_buf != NULL, ("VOP_GEPARENT: null buffer"));
+
+	MNT_REF(mp);
+
 	/* XXXgpf:is this check necessary? */
 	if (vp->v_type == VBAD) {
 		error = ENOENT;
@@ -1957,51 +2006,32 @@
 		if (error) {
 			dvp = NULL;
 		}
-		else if (flags & WANTNAME) {
-			/* grab the name that is being used to reference vp */
-			error = dir_ilookup(vp, dvp, ap->a_buf, ap->a_buflen, flags);
+		/* make sure this directory contains vp */
+		else {
+			/* avoid exhaustive search for now */
+			tmpflags = flags & (PARENTHINT | WANTNAME);
+			error = dir_ilookup(vp, dvp, NULL, ap->a_buf, ap->a_buflen, tmpflags);
 			if (error) {
 				vput(dvp);
 				dvp = NULL;
 			}
 		}
-	}		
-	
+	}
+
 	/* 
 	 * if our target is not a directory and we haven't found 'a' parent directory, 
 	 * do an exhaustive search on the filesystem
 	 */
 	if ((flags & EXHAUSTSEARCH) && dvp == NULL) {
-		/*
-		 * XXXgpf: this actually does not work because when the thread will try to sleep,
-		 * e.g. in VOP_READDIR, the kernel will panic because we have ilocked mp >.<
-		 * 
-		 * Not, it also kernel panics because we have locked vp >.<'
-		 */
-		MNT_ILOCK(mp);
-		if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) {
-			struct vnode *tvp;
-
-			TAILQ_FOREACH(tvp, &mp->mnt_nvnodelist, v_nmntvnodes) {
-				if (tvp->v_type == VDIR) {
-					vn_lock(tvp, LK_SHARED);
-					/* grab the name that is being used to reference vp */
-					error = dir_ilookup(vp, tvp, ap->a_buf, ap->a_buflen, flags);
-					
-					/* found it */
-					if (error == 0) {
-						dvp = tvp;
-						vref(dvp);
-						break;
-					}
-					VOP_UNLOCK(tvp, 0);
-				}
+		error = VFS_VGET(mp, ROOTINO, LK_SHARED, &startdvp);
+		if (error == 0) {
+			error = dir_ilookup(vp, startdvp, &dvp, ap->a_buf, ap->a_buflen, flags);
+			/* dont vput if the parent vnode is the root vnode */
+			if (error || startdvp != dvp) {
+				vput(startdvp);
 			}
 		}
-		MNT_IUNLOCK(mp);
-		
-		/* we failed to find a directory that contains the vnode, exit */
-		if (error != 0) {
+		if (error) {
 			error = ENOENT;
 		}
 	}	
@@ -2017,6 +2047,8 @@
 	else {
 		*(ap->a_vpp) = NULL;
 	}
+	
+	MNT_REL(mp);
 
 	return error;
 }


More information about the p4-projects mailing list