Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 14 May 2010 16:17:13 GMT
From:      Efstratios Karatzas <gpf@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 178254 for review
Message-ID:  <201005141617.o4EGHDj2023244@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@178254?ac=10

Change 178254 by gpf@gpf_desktop on 2010/05/14 16:16:47

	- implemented exhaustive search(?):
	I tried to use the mnt_nvnodelist field of the
	mounted fs that the vnode belongs to and working my way 
	through the entire list of vnodes till I can find
	a directory that contains the vnode that is our target.
	Seemed the fastest way that I could go about doing this 
	search buuut,
	
	It seems that acquiring a ilock 
	on the mp thus: "MNT_ILOCK(mp);" will cause a kernel panic
	when the thread will try to sleep, e.g. because of VOP_READDIR.
	The thread can't sleep while holding this lock.
	
	So, unless there's a way to work around this issue, I think 
	the only solution left is to implement a recursive function 
	that will visit each directory of the fs, this sucks.
	Anyhoo, I did the effort so here it is.
	
	- fixed a tinny error at the license; should have been more 
	careful when I c/p although I'm not even sure if I 
	have to include a license since this is a temporary file.
	
	- I cut off the code that searches a directory through its 
	vnodep to find a file with a certain id, and I created another 
	function, that does this dir_ilookup().
	
	- Made sure that all locks are released and all reference 
	counters are ok no matter what the case. Fixed a few 
	bugs on this matter.
	
	- functions now return relative errors and the path is now returned 
	through fullpath, freepath can be used to check if something 
	useful exists in fullpath and then free the buffer, example in
	vfn_entry().
	
	- removed a few debuging comments & uprintf()s.

Affected files ...

.. //depot/projects/soc2010/gpf_audit/vn_fullpath_nocache.c#2 edit

Differences ...

==== //depot/projects/soc2010/gpf_audit/vn_fullpath_nocache.c#2 (text+ko) ====

@@ -13,7 +13,7 @@
  *     notice, this list of conditions and the following disclaimer in the
  *     documentation and/or other materials provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * THIS SOFTWARE IS PROVIDED BY me AND ITS 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 APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
@@ -46,7 +46,100 @@
 #include <ufs/ufs/inode.h>
 
 #define PARENT_HINT 0x0001
+#define EXHAUSTIVE_SEARCH 0x0002
+
+/*
+ * find the name that is used to reference vp inside the directory vnode dvp 
+ * locks: dvp must be locked on entry and will still be locked on exit
+ * 
+ * works for UFS
+ * 
+ * returns:
+ * 	- ENOENT	a file that corresponds to vp was not found inside dvp
+ * 	- EIO		error occured while reading the directory
+ * 	- EOVERFLOW	result does not fit in buffer "name"
+ */
+static int
+dir_ilookup(struct vnode *vp, struct vnode *dvp, char *name, int *namelen)
+{
+	struct uio io;
+	struct iovec iov;
+	struct dirent *dp, *edp;
+	struct thread *td;
+	char *dirbuf;	
+	u_int64_t dirbuflen;
+	int error, eofflag;
+	char foundit;
+	
+	KASSERT("vp != NULL", "dir_ilookup: vp == NULL");
+	KASSERT("dvp != NULL", "dir_ilookup: dvp == NULL");
+	KASSERT("name != NULL", "dir_ilookup: name == NULL");
+
+	foundit = 0;
+	dirbuflen = ((struct inode *)dvp->v_data)->i_size;
+	dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
+	td = curthread;
+	
+	iov.iov_base = dirbuf;
+	iov.iov_len = dirbuflen;
+	io.uio_iov = &iov;
+	io.uio_iovcnt = 1;
+	io.uio_offset = 0;
+	io.uio_resid = dirbuflen;
+	io.uio_segflg = UIO_SYSSPACE;
+	io.uio_rw = UIO_READ;
+	io.uio_td = td;
+	eofflag = 0;
+	
+	error = VOP_READDIR(dvp, &io, td->td_ucred, &eofflag, NULL, NULL);
+	if (error) {
+		uprintf("VOP_READDIR failure %d\n", error);
+		error = EIO;
+		goto out;
+	}
+	
+	/* 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 (dp->d_reclen > 0) {	
+			/* found it */
+			if ( ((struct inode *)vp->v_data)->i_number == ((struct dirent *)dp)->d_fileno) {
+				char *pch;
+				int len;
+
+				pch = ((struct dirent *)dp)->d_name;
+				len = strlen(pch);
+				
+				if (len >= *namelen) {
+					error = EOVERFLOW;
+					goto out;
+				}
+
+				strlcpy(name, ((struct dirent *)dp)->d_name, *namelen);
+				*namelen -= len + 1;
+				foundit = 1;
+				break;
+			}
+			dp = (struct dirent *)((char *)dp + dp->d_reclen);
+		} 
+		else {
+			error = EIO;
+			break;
+		}
+	}
 
+out:	
+	if (dirbuf != NULL) {
+		free(dirbuf, M_TEMP);
+	}
+	
+	if (foundit == 0 && error != 0) {
+		error = ENOENT;
+	}
+	
+	return error;
+}
+
 /*
  * vn_fullpath_nocache
  * 
@@ -55,36 +148,37 @@
  * - A directory hint (UFS file_id of the directory that contains the vnode) may be 
  *   supplied to facilitate the search is our target is not a directory itself.
  * - flags should be set to PARENT_HINT, if the directory hint is supplied
+ *   and to EXHAUSTIVE_SEARCH, if we are willing to go intro great trouble to get this path
  * 
  * Author's note: This only works for UFS filesystems (for now).
+ * Oh, also EXHAUSTIVE_SEARCH will kernel panic :-D
  */
 static int
 vn_fullpath_nocache(struct vnode *vp, char **fullpath, char **freepath, ino_t directory_hint, char flags)
-{
-	struct uio io;
-	struct iovec iov;
-	struct dirent *dp, *edp;
+{	
 	struct vnode *dvp, *upper_dvp;
+	struct mount *mp;
 	struct thread * td;
-	char *buf;
-	char *dirbuf;
-	int error, buflen, eofflag, vfslocked;
-	u_int64_t dirbuflen;
+	char *buf, *pch;
+	char fname[MNAMELEN];
+	int error, buflen, vfslocked, fnamelen;
 
 	KASSERT("vp != NULL", "vn_fullpath_nocache: vp == NULL");
 	
 	dvp = NULL;
+	buf = NULL;
 	*freepath = NULL;
-	dirbuf = NULL;
 	
 	if (vp->v_type == VBAD) {
-		/* XXXgpf: should return an actual error code */
-		return 1;
+		error = ENOENT;
+		goto out;
 	}
 	
 	vref(vp);
 	error = 0;
 	td = curthread;
+	mp = vp->v_mount;
+	fnamelen = sizeof(fname);
 	buf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
 	buflen = MAXPATHLEN - 1;
 	buf[MAXPATHLEN-1] = '\0';
@@ -93,10 +187,7 @@
 	 * - If our target is a directory, move on to the part where we traverse the '..' entries.
 	 * - If not, either use the directory_hint if it's available or do an exhaustive search on the fs (xD) so
 	 *   that we can connect the vp with 'a' parent directory.
-	 * 
-	 * XXXgpf: for now, let's assume that a parent_hint is available if our target is not a dir
-	 */
-	
+	 */	
 	if (vp->v_type != VDIR) {
 		/* grab the parent directory using the directory_hint */
 		if ((flags & PARENT_HINT) && vp->v_type != VDIR) {
@@ -107,67 +198,70 @@
 				uprintf("VFS_VGET failure %d\n", error);
 				dvp = NULL;
 			}
-		}
+			else {
+				/* grab the name that is being used to reference vp */
+				error = dir_ilookup(vp, dvp, fname, &fnamelen);
+				if (error) {
+					vput(dvp);
+					goto out;
+				}
+			}
+		}		
 		
 		/* 
 		 * if our target is not a directory and we haven't found 'a' parent directory, 
 		 * do an exhaustive search on the filesystem
 		 */
-		if (dvp == NULL && vp->v_type != VDIR) {
-			uprintf("todo: implement exhaustive search, lalalalalala\n");
-			/*temp*/ error = 1;
-			goto out;
-			/* XXXgpf: if even this fails, quit */
+		if ((flags & EXHAUSTIVE_SEARCH) && 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 >.<
+			 */
+			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_EXCLUSIVE);
+						/* grab the name that is being used to reference vp */
+						error = dir_ilookup(vp, tvp, fname, &fnamelen);
+						
+						/* found it */
+						if (error == 0) {
+							dvp = tvp;
+							vref(dvp);
+							break;
+						}
+						VOP_UNLOCK(tvp, 0);
+					}
+				}
+			}
+			MNT_IUNLOCK(mp);
+			
+			/* we failed to find a directory that contains the vnode, exit */
+			if (error != 0) {
+				error = ENOENT;
+				goto out;
+			}
 		}
-		
-		/* find the name that is used to reference the file inside this directory */
-		dirbuflen = ((struct inode *)dvp->v_data)->i_size;
-		dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
-		
-		iov.iov_base = dirbuf;
-		iov.iov_len = dirbuflen;
-		io.uio_iov = &iov;
-		io.uio_iovcnt = 1;
-		io.uio_offset = 0;
-		io.uio_resid = dirbuflen;
-		io.uio_segflg = UIO_SYSSPACE;
-		io.uio_rw = UIO_READ;
-		io.uio_td = td;
-		eofflag = 0;
-		
-		error = VOP_READDIR(dvp, &io, td->td_ucred, &eofflag, NULL, NULL);
-		if (error) {
-			uprintf("VOP_READDIR failure %d\n", error);
-			vput(dvp);
+		/* we failed to find a directory that contains the vnode, exit */
+		else if (dvp == NULL) {
+			error = ENOENT;
 			goto out;
 		}
-		
-		/* 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 (dp->d_reclen > 0) {	
-				/* found it */
-				if ( ((struct inode *)vp->v_data)->i_number == ((struct dirent *)dp)->d_fileno) {
-					char *pch;
-					
-					pch = buf + buflen - ((struct dirent *)dp)->d_namlen;
-					if (pch < buf) {
-						/* should return a real error code */
-						error = 1;
-						goto out;
-					}
-					strcpy(pch, ((struct dirent *)dp)->d_name);
-					buflen -= ((struct dirent *)dp)->d_namlen;
-					buf[--buflen] = '/';
-				}
-				dp = (struct dirent *)((char *)dp + dp->d_reclen);
-			} 
-			else {
-				error = EIO;
+
+		/* we have found a parent directory and a name for our vnode, save the name */
+		pch = buf + buflen - strlen(fname);
+		if (pch < buf) {
+			error = EOVERFLOW;
+			if (dvp != NULL)
 				vput(dvp);
-				goto out;
-			}
+			goto out;
 		}
+		strcpy(pch, fname);
+		buflen -= strlen(fname);
+		buf[--buflen] = '/';		
 	} /* if not VDIR */
 	/* if our target is a dir, do the initial preparation */
 	else {
@@ -193,8 +287,7 @@
 			int fs_path_len;
 			
 			vfslocked = VFS_LOCK_GIANT(dvp->v_mount);
-			
-			uprintf("bazinga!\t");
+
 			*fullpath = buf + buflen;
 			
 			fs_path = dvp->v_mount->mnt_stat.f_mntonname;
@@ -202,8 +295,8 @@
 			
 			if (buflen - fs_path_len - 1 < 0) {
 				vput(dvp);
-				/* todo: return an actual error code */
-				error = 1;
+				error = EOVERFLOW;
+				VFS_UNLOCK_GIANT(vfslocked);
 				goto out;
 			}
 			
@@ -229,27 +322,26 @@
 		dvp = upper_dvp;
 		vn_lock(dvp, LK_EXCLUSIVE);
 		vref(dvp);
-	}
+	} /* while */
 
-	uprintf("out!\n");
 	vput(dvp);	
 	*fullpath = buf + buflen;
-	/* XXXgpf: for now, just print the path here */
-	uprintf("path %s\n", *fullpath);
+	*freepath = buf;
 
 out:
-	if (dirbuf != NULL)
-		free(dirbuf, M_TEMP);
-
-	/* XXXgpf: no matter the case, we release this buffer - this is just temporary */
-	if (buf != NULL)
-		free(buf, M_TEMP);
-	
+	if (error != 0) {
+		*freepath = NULL;
+		if (buf != NULL)
+			free(buf, M_TEMP);
+	}	
 	vrele(vp);
 		
 	return error;
 }
 
+/*
+ * entry point for the lkm - preps & makes a call to vn_fullpath_nocache()
+ */
 static int
 vfn_entry(module_t mod, int what, void *arg)
 {
@@ -291,7 +383,13 @@
 	
 	error = vn_fullpath_nocache(vp, &fullpath, &freepath, directory_hint, PARENT_HINT);
 
-	return error;
+	uprintf("vn_fullpath_nocache() returned %d\n", error);
+	if (freepath != NULL) {
+		uprintf("path: %s\n", fullpath);
+		free(freepath, M_TEMP);
+	}
+
+	return 0;
 }
 
 /* Basic module data */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201005141617.o4EGHDj2023244>