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