root/kern/vfs_getcwd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vfs_getcwd_scandir
  2. vfs_getcwd_getcache
  3. vfs_getcwd_common
  4. proc_isunder
  5. sys___getcwd

    1 /* $OpenBSD: vfs_getcwd.c,v 1.12 2007/08/07 07:41:59 thib Exp $ */
    2 /* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
    3 
    4 /*
    5  * Copyright (c) 1999 The NetBSD Foundation, Inc.
    6  * All rights reserved.
    7  *
    8  * This code is derived from software contributed to The NetBSD Foundation
    9  * by Bill Sommerfeld.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. All advertising materials mentioning features or use of this software
   20  *    must display the following acknowledgement:
   21  *        This product includes software developed by the NetBSD
   22  *        Foundation, Inc. and its contributors.
   23  * 4. Neither the name of The NetBSD Foundation nor the names of its
   24  *    contributors may be used to endorse or promote products derived
   25  *    from this software without specific prior written permission.
   26  *
   27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   37  * POSSIBILITY OF SUCH DAMAGE.
   38  */
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/namei.h>
   43 #include <sys/filedesc.h>
   44 #include <sys/kernel.h>
   45 #include <sys/file.h>
   46 #include <sys/stat.h>
   47 #include <sys/vnode.h>
   48 #include <sys/mount.h>
   49 #include <sys/proc.h>
   50 #include <sys/uio.h>
   51 #include <sys/malloc.h>
   52 #include <sys/dirent.h>
   53 #include <ufs/ufs/dir.h>        /* only for DIRBLKSIZ */
   54 
   55 #include <sys/syscallargs.h>
   56 
   57 #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN + 1) + 4)
   58 
   59 /* Find parent vnode of *lvpp, return in *uvpp */
   60 int
   61 vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
   62     char *bufp, struct proc *p)
   63 {
   64         int eofflag, tries, dirbuflen, len, reclen, error = 0;
   65         off_t off;
   66         struct uio uio;
   67         struct iovec iov;
   68         char *dirbuf = NULL;
   69         ino_t fileno;
   70         struct vattr va;
   71         struct vnode *uvp = NULL;
   72         struct vnode *lvp = *lvpp;      
   73         struct componentname cn;
   74 
   75         tries = 0;
   76 
   77         /*
   78          * If we want the filename, get some info we need while the
   79          * current directory is still locked.
   80          */
   81         if (bufp != NULL) {
   82                 error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
   83                 if (error) {
   84                         vput(lvp);
   85                         *lvpp = NULL;
   86                         *uvpp = NULL;
   87                         return (error);
   88                 }
   89         }
   90 
   91         cn.cn_nameiop = LOOKUP;
   92         cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
   93         cn.cn_proc = p;
   94         cn.cn_cred = p->p_ucred;
   95         cn.cn_pnbuf = NULL;
   96         cn.cn_nameptr = "..";
   97         cn.cn_namelen = 2;
   98         cn.cn_hash = 0;
   99         cn.cn_consume = 0;
  100 
  101         /* Get parent vnode using lookup of '..' */
  102         error = VOP_LOOKUP(lvp, uvpp, &cn);
  103         if (error) {
  104                 vput(lvp);
  105                 *lvpp = NULL;
  106                 *uvpp = NULL;
  107                 return (error);
  108         }
  109 
  110         uvp = *uvpp;
  111 
  112         /* If we don't care about the pathname, we're done */
  113         if (bufp == NULL) {
  114                 vrele(lvp);
  115                 *lvpp = NULL;
  116                 return (0);
  117         }
  118 
  119         fileno = va.va_fileid;
  120 
  121         dirbuflen = DIRBLKSIZ;
  122 
  123         if (dirbuflen < va.va_blocksize)
  124                 dirbuflen = va.va_blocksize;
  125 
  126         dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
  127 
  128         off = 0;
  129 
  130         do {
  131                 char   *cpos;
  132                 struct dirent *dp;
  133 
  134                 iov.iov_base = dirbuf;
  135                 iov.iov_len = dirbuflen;
  136 
  137                 uio.uio_iov = &iov;
  138                 uio.uio_iovcnt = 1;
  139                 uio.uio_offset = off;
  140                 uio.uio_resid = dirbuflen;
  141                 uio.uio_segflg = UIO_SYSSPACE;
  142                 uio.uio_rw = UIO_READ;
  143                 uio.uio_procp = p;
  144 
  145                 eofflag = 0;
  146 
  147                 /* Call VOP_READDIR of parent */
  148                 error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag, 0, 0);
  149 
  150                 off = uio.uio_offset;
  151 
  152                 /* Try again if NFS tosses its cookies */
  153                 if (error == EINVAL && tries < 3) {
  154                         tries++;
  155                         off = 0;
  156                         continue;
  157                 } else if (error) {
  158                         goto out; /* Old userland getcwd() behaviour */
  159                 }
  160 
  161                 cpos = dirbuf;
  162                 tries = 0;
  163 
  164                 /* Scan directory page looking for matching vnode */ 
  165                 for (len = (dirbuflen - uio.uio_resid); len > 0;
  166                      len -= reclen) {
  167                         dp = (struct dirent *)cpos;
  168                         reclen = dp->d_reclen;
  169 
  170                         /* Check for malformed directory */
  171                         if (reclen < DIRENT_MINSIZE) {
  172                                 error = EINVAL;
  173                                 goto out;
  174                         }
  175 
  176                         if (dp->d_fileno == fileno) {
  177                                 char *bp = *bpp;
  178                                 bp -= dp->d_namlen;
  179 
  180                                 if (bp <= bufp) {
  181                                         error = ERANGE;
  182                                         goto out;
  183                                 }
  184 
  185                                 bcopy(dp->d_name, bp, dp->d_namlen);
  186                                 error = 0;
  187                                 *bpp = bp;
  188 
  189                                 goto out;
  190                         }
  191 
  192                         cpos += reclen;
  193                 }
  194 
  195         } while (!eofflag);
  196 
  197         error = ENOENT;
  198 
  199 out:
  200 
  201         vrele(lvp);
  202         *lvpp = NULL;
  203 
  204         free(dirbuf, M_TEMP);
  205 
  206         return (error);
  207 }
  208 
  209 /* Do a lookup in the vnode-to-name reverse */
  210 int
  211 vfs_getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
  212     char *bufp)
  213 {
  214         struct vnode *lvp, *uvp = NULL;
  215         struct proc *p = curproc;
  216         char *obp;
  217         int error, vpid;
  218 
  219         lvp = *lvpp;
  220         obp = *bpp;     /* Save orginal position to restore to on error */
  221 
  222         error = cache_revlookup(lvp, uvpp, bpp, bufp);
  223         if (error) {
  224                 if (error != -1) {
  225                         vput(lvp);
  226                         *lvpp = NULL;
  227                         *uvpp = NULL;
  228                 }
  229 
  230                 return (error);
  231         }
  232 
  233         uvp = *uvpp;
  234         vpid = uvp->v_id;
  235 
  236 
  237         /* Release current lock before acquiring the parent lock */
  238         VOP_UNLOCK(lvp, 0, p);
  239 
  240         error = vget(uvp, LK_EXCLUSIVE | LK_RETRY, p);
  241         if (error)
  242                 *uvpp = NULL;
  243 
  244         /*
  245          * Verify that vget() succeeded, and check that vnode capability
  246          * didn't change while we were waiting for the lock.
  247          */
  248         if (error || (vpid != uvp->v_id)) {
  249                 /*
  250                  * Try to get our lock back. If that works, tell the caller to
  251                  * try things the hard way, otherwise give up.
  252                  */
  253                 if (!error)
  254                         vput(uvp);
  255 
  256                 *uvpp = NULL;
  257                 
  258                 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
  259                 if (!error) {
  260                         *bpp = obp; /* restore the buffer */
  261                         return (-1);
  262                 }
  263         }
  264 
  265         vrele(lvp);
  266         *lvpp = NULL;
  267 
  268         return (error);
  269 }
  270 
  271 #define GETCWD_CHECK_ACCESS 0x0001
  272 
  273 /* Common routine shared by sys___getcwd() and vn_isunder() */
  274 int
  275 vfs_getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
  276     int limit, int flags, struct proc *p)
  277 {
  278         struct filedesc *fdp = p->p_fd;
  279         struct vnode *uvp = NULL;
  280         char *bp = NULL;
  281         int error, perms = VEXEC;
  282 
  283         if (rvp == NULL) {
  284                 rvp = fdp->fd_rdir;
  285                 if (rvp == NULL)
  286                         rvp = rootvnode;
  287         }
  288 
  289         VREF(rvp);
  290         VREF(lvp);
  291 
  292         error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
  293         if (error) {
  294                 vrele(lvp);
  295                 lvp = NULL;
  296                 goto out;
  297         }
  298 
  299         if (bufp)
  300                 bp = *bpp;
  301 
  302         if (lvp == rvp) {
  303                 if (bp)
  304                         *(--bp) = '/';
  305                 goto out;
  306         }
  307 
  308         /*
  309          * This loop will terminate when we hit the root, VOP_READDIR() or
  310          * VOP_LOOKUP() fails, or we run out of space in the user buffer.
  311          */
  312         do {
  313                 if (lvp->v_type != VDIR) {
  314                         error = ENOTDIR;
  315                         goto out;
  316                 }
  317 
  318                 /* Check for access if caller cares */
  319                 if (flags & GETCWD_CHECK_ACCESS) {
  320                         error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
  321                         if (error)
  322                                 goto out;
  323                         perms = VEXEC|VREAD;
  324                 }
  325 
  326                 /* Step up if we're a covered vnode */
  327                 while (lvp->v_flag & VROOT) {
  328                         struct vnode *tvp;
  329 
  330                         if (lvp == rvp)
  331                                 goto out;
  332                         
  333                         tvp = lvp;
  334                         lvp = lvp->v_mount->mnt_vnodecovered;
  335 
  336                         vput(tvp);
  337 
  338                         if (lvp == NULL) {
  339                                 error = ENOENT;
  340                                 goto out;
  341                         }
  342 
  343                         VREF(lvp);
  344 
  345                         error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
  346                         if (error) {
  347                                 vrele(lvp);
  348                                 lvp = NULL;
  349                                 goto out;
  350                         }
  351                 }
  352 
  353                 /* Look in the name cache */
  354                 error = vfs_getcwd_getcache(&lvp, &uvp, &bp, bufp);
  355 
  356                 if (error == -1) {
  357                         /* If that fails, look in the directory */
  358                         error = vfs_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
  359                 }
  360 
  361                 if (error)
  362                         goto out;
  363 
  364 #ifdef DIAGNOSTIC
  365                 if (lvp != NULL)
  366                         panic("getcwd: oops, forgot to null lvp");
  367                 if (bufp && (bp <= bufp)) {
  368                         panic("getcwd: oops, went back too far");
  369                 }
  370 #endif
  371 
  372                 if (bp)
  373                         *(--bp) = '/';
  374 
  375                 lvp = uvp;
  376                 uvp = NULL;
  377                 limit--;
  378 
  379         } while ((lvp != rvp) && (limit > 0)); 
  380 
  381 out:
  382 
  383         if (bpp)
  384                 *bpp = bp;
  385 
  386         if (uvp)
  387                 vput(uvp);
  388 
  389         if (lvp)
  390                 vput(lvp);
  391 
  392         vrele(rvp);
  393 
  394         return (error);
  395 }
  396 
  397 /* True if p1's root directory is equal to or under p2's root directory */
  398 int
  399 proc_isunder(struct proc *p1, struct proc *p2)
  400 {
  401         struct vnode *r1 = p1->p_fd->fd_rdir;
  402         struct vnode *r2 = p2->p_fd->fd_rdir;
  403 
  404         if (r1 == NULL)
  405                 return (r2 == NULL);
  406 
  407         if (r2 == NULL)
  408                 return (1);
  409 
  410         return (vn_isunder(r1, r2, p2));
  411 }
  412 
  413 /* Find pathname of a process's current directory */
  414 int
  415 sys___getcwd(struct proc *p, void *v, register_t *retval) 
  416 {
  417         struct sys___getcwd_args *uap = v;
  418         int error, lenused, len = SCARG(uap, len);
  419         char *path, *bp, *bend;
  420 
  421         if (len > MAXPATHLEN * 4)
  422                 len = MAXPATHLEN * 4;
  423         else if (len < 2)
  424                 return (ERANGE);
  425 
  426         path = malloc(len, M_TEMP, M_WAITOK);
  427 
  428         bp = &path[len];
  429         bend = bp;
  430         *(--bp) = '\0';
  431 
  432         /*
  433          * 5th argument here is "max number of vnodes to traverse".
  434          * Since each entry takes up at least 2 bytes in the output
  435          * buffer, limit it to N/2 vnodes for an N byte buffer.
  436          */
  437         error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, len/2,
  438             GETCWD_CHECK_ACCESS, p);
  439 
  440         if (error)
  441                 goto out;
  442 
  443         lenused = bend - bp;
  444         *retval = lenused;
  445 
  446         /* Put the result into user buffer */
  447         error = copyout(bp, SCARG(uap, buf), lenused);
  448 
  449 out:
  450         free(path, M_TEMP);
  451 
  452         return (error);
  453 }

/* [<][>][^][v][top][bottom][index][help] */