From owner-p4-projects@FreeBSD.ORG Fri May 14 16:17:14 2010 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 395991065675; Fri, 14 May 2010 16:17:14 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id F1D031065674 for ; Fri, 14 May 2010 16:17:13 +0000 (UTC) (envelope-from gpf@FreeBSD.org) Received: from repoman.freebsd.org (unknown [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id DF2F98FC1A for ; Fri, 14 May 2010 16:17:13 +0000 (UTC) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.3/8.14.3) with ESMTP id o4EGHDii023246 for ; Fri, 14 May 2010 16:17:13 GMT (envelope-from gpf@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.3/8.14.3/Submit) id o4EGHDj2023244 for perforce@freebsd.org; Fri, 14 May 2010 16:17:13 GMT (envelope-from gpf@FreeBSD.org) Date: Fri, 14 May 2010 16:17:13 GMT Message-Id: <201005141617.o4EGHDj2023244@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to gpf@FreeBSD.org using -f From: Efstratios Karatzas To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 178254 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 14 May 2010 16:17:14 -0000 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 #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 */