The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/vfs_getcwd.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* $NetBSD: vfs_getcwd.c,v 1.42 2008/04/28 20:24:05 martin Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 1999 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Bill Sommerfeld.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   29  * POSSIBILITY OF SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __KERNEL_RCSID(0, "$NetBSD: vfs_getcwd.c,v 1.42 2008/04/28 20:24:05 martin Exp $");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/namei.h>
   38 #include <sys/filedesc.h>
   39 #include <sys/kernel.h>
   40 #include <sys/file.h>
   41 #include <sys/stat.h>
   42 #include <sys/vnode.h>
   43 #include <sys/mount.h>
   44 #include <sys/proc.h>
   45 #include <sys/uio.h>
   46 #include <sys/malloc.h>
   47 #include <sys/dirent.h>
   48 #include <sys/kauth.h>
   49 
   50 #include <ufs/ufs/dir.h>        /* XXX only for DIRBLKSIZ */
   51 
   52 #include <sys/syscallargs.h>
   53 
   54 /*
   55  * Vnode variable naming conventions in this file:
   56  *
   57  * rvp: the current root we're aiming towards.
   58  * lvp, *lvpp: the "lower" vnode
   59  * uvp, *uvpp: the "upper" vnode.
   60  *
   61  * Since all the vnodes we're dealing with are directories, and the
   62  * lookups are going *up* in the filesystem rather than *down*, the
   63  * usual "pvp" (parent) or "dvp" (directory) naming conventions are
   64  * too confusing.
   65  */
   66 
   67 /*
   68  * XXX Will infinite loop in certain cases if a directory read reliably
   69  *      returns EINVAL on last block.
   70  * XXX is EINVAL the right thing to return if a directory is malformed?
   71  */
   72 
   73 /*
   74  * XXX Untested vs. mount -o union; probably does the wrong thing.
   75  */
   76 
   77 /*
   78  * Find parent vnode of *lvpp, return in *uvpp
   79  *
   80  * If we care about the name, scan it looking for name of directory
   81  * entry pointing at lvp.
   82  *
   83  * Place the name in the buffer which starts at bufp, immediately
   84  * before *bpp, and move bpp backwards to point at the start of it.
   85  *
   86  * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
   87  * On exit, *uvpp is either NULL or is a locked vnode reference.
   88  */
   89 static int
   90 getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
   91     char *bufp, struct lwp *l)
   92 {
   93         int     error = 0;
   94         int     eofflag;
   95         off_t   off;
   96         int     tries;
   97         struct uio uio;
   98         struct iovec iov;
   99         char   *dirbuf = NULL;
  100         int     dirbuflen;
  101         ino_t   fileno;
  102         struct vattr va;
  103         struct vnode *uvp = NULL;
  104         struct vnode *lvp = *lvpp;
  105         kauth_cred_t cred = l->l_cred;
  106         struct componentname cn;
  107         int len, reclen;
  108         tries = 0;
  109 
  110         /*
  111          * If we want the filename, get some info we need while the
  112          * current directory is still locked.
  113          */
  114         if (bufp != NULL) {
  115                 error = VOP_GETATTR(lvp, &va, cred);
  116                 if (error) {
  117                         vput(lvp);
  118                         *lvpp = NULL;
  119                         *uvpp = NULL;
  120                         return error;
  121                 }
  122         }
  123 
  124         /*
  125          * Ok, we have to do it the hard way..
  126          * Next, get parent vnode using lookup of ..
  127          */
  128         cn.cn_nameiop = LOOKUP;
  129         cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
  130         cn.cn_cred = cred;
  131         cn.cn_pnbuf = NULL;
  132         cn.cn_nameptr = "..";
  133         cn.cn_namelen = 2;
  134         cn.cn_hash = 0;
  135         cn.cn_consume = 0;
  136 
  137         /*
  138          * At this point, lvp is locked.
  139          * On successful return, *uvpp will be locked
  140          */
  141         error = VOP_LOOKUP(lvp, uvpp, &cn);
  142         vput(lvp);
  143         if (error) {
  144                 *lvpp = NULL;
  145                 *uvpp = NULL;
  146                 return error;
  147         }
  148         uvp = *uvpp;
  149 
  150         /* If we don't care about the pathname, we're done */
  151         if (bufp == NULL) {
  152                 *lvpp = NULL;
  153                 return 0;
  154         }
  155 
  156         fileno = va.va_fileid;
  157 
  158         dirbuflen = DIRBLKSIZ;
  159         if (dirbuflen < va.va_blocksize)
  160                 dirbuflen = va.va_blocksize;
  161         dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
  162 
  163 #if 0
  164 unionread:
  165 #endif
  166         off = 0;
  167         do {
  168                 /* call VOP_READDIR of parent */
  169                 iov.iov_base = dirbuf;
  170                 iov.iov_len = dirbuflen;
  171 
  172                 uio.uio_iov = &iov;
  173                 uio.uio_iovcnt = 1;
  174                 uio.uio_offset = off;
  175                 uio.uio_resid = dirbuflen;
  176                 uio.uio_rw = UIO_READ;
  177                 UIO_SETUP_SYSSPACE(&uio);
  178 
  179                 eofflag = 0;
  180 
  181                 error = VOP_READDIR(uvp, &uio, cred, &eofflag, 0, 0);
  182 
  183                 off = uio.uio_offset;
  184 
  185                 /*
  186                  * Try again if NFS tosses its cookies.
  187                  * XXX this can still loop forever if the directory is busted
  188                  * such that the second or subsequent page of it always
  189                  * returns EINVAL
  190                  */
  191                 if ((error == EINVAL) && (tries < 3)) {
  192                         off = 0;
  193                         tries++;
  194                         continue;       /* once more, with feeling */
  195                 }
  196 
  197                 if (!error) {
  198                         char   *cpos;
  199                         struct dirent *dp;
  200 
  201                         cpos = dirbuf;
  202                         tries = 0;
  203 
  204                         /* scan directory page looking for matching vnode */
  205                         for (len = (dirbuflen - uio.uio_resid); len > 0;
  206                             len -= reclen) {
  207                                 dp = (struct dirent *) cpos;
  208                                 reclen = dp->d_reclen;
  209 
  210                                 /* check for malformed directory.. */
  211                                 if (reclen < _DIRENT_MINSIZE(dp)) {
  212                                         error = EINVAL;
  213                                         goto out;
  214                                 }
  215                                 /*
  216                                  * XXX should perhaps do VOP_LOOKUP to
  217                                  * check that we got back to the right place,
  218                                  * but getting the locking games for that
  219                                  * right would be heinous.
  220                                  */
  221                                 if ((dp->d_type != DT_WHT) &&
  222                                     (dp->d_fileno == fileno)) {
  223                                         char *bp = *bpp;
  224 
  225                                         bp -= dp->d_namlen;
  226                                         if (bp <= bufp) {
  227                                                 error = ERANGE;
  228                                                 goto out;
  229                                         }
  230                                         memcpy(bp, dp->d_name, dp->d_namlen);
  231                                         error = 0;
  232                                         *bpp = bp;
  233                                         goto out;
  234                                 }
  235                                 cpos += reclen;
  236                         }
  237                 } else
  238                         goto out;
  239         } while (!eofflag);
  240 #if 0
  241         /*
  242          * Deal with mount -o union, which unions only the
  243          * root directory of the mount.
  244          */
  245         if ((uvp->v_vflag & VV_ROOT) &&
  246             (uvp->v_mount->mnt_flag & MNT_UNION)) {
  247                 struct vnode *tvp = uvp;
  248 
  249                 uvp = uvp->v_mount->mnt_vnodecovered;
  250                 vput(tvp);
  251                 VREF(uvp);
  252                 *uvpp = uvp;
  253                 vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY);
  254                 goto unionread;
  255         }
  256 #endif
  257         error = ENOENT;
  258 
  259 out:
  260         *lvpp = NULL;
  261         free(dirbuf, M_TEMP);
  262         return error;
  263 }
  264 
  265 /*
  266  * Look in the vnode-to-name reverse cache to see if
  267  * we can find things the easy way.
  268  *
  269  * XXX vget failure path is untested.
  270  *
  271  * On entry, *lvpp is a locked vnode reference.
  272  * On exit, one of the following is the case:
  273  *      0) Both *lvpp and *uvpp are NULL and failure is returned.
  274  *      1) *uvpp is NULL, *lvpp remains locked and -1 is returned (cache miss)
  275  *      2) *uvpp is a locked vnode reference, *lvpp is vput and NULL'ed
  276  *         and 0 is returned (cache hit)
  277  */
  278 
  279 static int
  280 getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
  281     char *bufp)
  282 {
  283         struct vnode *lvp, *uvp = NULL;
  284         char *obp = *bpp;
  285         int error;
  286 
  287         lvp = *lvpp;
  288 
  289         /*
  290          * This returns 0 on a cache hit, -1 on a clean cache miss,
  291          * or an errno on other failure.
  292          */
  293         error = cache_revlookup(lvp, uvpp, bpp, bufp);
  294         if (error) {
  295                 if (error != -1) {
  296                         vput(lvp);
  297                         *lvpp = NULL;
  298                         *uvpp = NULL;
  299                 }
  300                 return error;
  301         }
  302         uvp = *uvpp;
  303 
  304         /*
  305          * Since we're going up, we have to release the current lock
  306          * before we take the parent lock.
  307          */
  308 
  309         VOP_UNLOCK(lvp, 0);
  310         error = vget(uvp, LK_EXCLUSIVE | LK_RETRY);
  311 
  312         /*
  313          * Verify that vget succeeded while we were waiting for the
  314          * lock.
  315          */
  316         if (error) {
  317 
  318                 /*
  319                  * Oops, we missed.  If the vget failed, get our lock back
  320                  * then rewind the `bp' and tell the caller to try things
  321                  * the hard way.
  322                  */
  323                 *uvpp = NULL;
  324                 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
  325                 *bpp = obp;
  326                 return -1;
  327         }
  328         vrele(lvp);
  329         *lvpp = NULL;
  330 
  331         return error;
  332 }
  333 
  334 /*
  335  * common routine shared by sys___getcwd() and vn_isunder()
  336  */
  337 
  338 int
  339 getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
  340     int limit, int flags, struct lwp *l)
  341 {
  342         struct cwdinfo *cwdi = l->l_proc->p_cwdi;
  343         kauth_cred_t cred = l->l_cred;
  344         struct vnode *uvp = NULL;
  345         char *bp = NULL;
  346         int error;
  347         int perms = VEXEC;
  348 
  349         error = 0;
  350         if (rvp == NULL) {
  351                 rvp = cwdi->cwdi_rdir;
  352                 if (rvp == NULL)
  353                         rvp = rootvnode;
  354         }
  355 
  356         VREF(rvp);
  357         VREF(lvp);
  358 
  359         /*
  360          * Error handling invariant:
  361          * Before a `goto out':
  362          *      lvp is either NULL, or locked and held.
  363          *      uvp is either NULL, or locked and held.
  364          */
  365 
  366         vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
  367         if (bufp)
  368                 bp = *bpp;
  369 
  370         /*
  371          * this loop will terminate when one of the following happens:
  372          *      - we hit the root
  373          *      - getdirentries or lookup fails
  374          *      - we run out of space in the buffer.
  375          */
  376         if (lvp == rvp) {
  377                 if (bp)
  378                         *(--bp) = '/';
  379                 goto out;
  380         }
  381         do {
  382                 /*
  383                  * access check here is optional, depending on
  384                  * whether or not caller cares.
  385                  */
  386                 if (flags & GETCWD_CHECK_ACCESS) {
  387                         error = VOP_ACCESS(lvp, perms, cred);
  388                         if (error)
  389                                 goto out;
  390                         perms = VEXEC|VREAD;
  391                 }
  392 
  393                 /*
  394                  * step up if we're a covered vnode..
  395                  */
  396                 while (lvp->v_vflag & VV_ROOT) {
  397                         struct vnode *tvp;
  398 
  399                         if (lvp == rvp)
  400                                 goto out;
  401 
  402                         tvp = lvp;
  403                         lvp = lvp->v_mount->mnt_vnodecovered;
  404                         vput(tvp);
  405                         /*
  406                          * hodie natus est radici frater
  407                          */
  408                         if (lvp == NULL) {
  409                                 error = ENOENT;
  410                                 goto out;
  411                         }
  412                         VREF(lvp);
  413                         error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
  414                         if (error != 0) {
  415                                 vrele(lvp);
  416                                 lvp = NULL;
  417                                 goto out;
  418                         }
  419                 }
  420                 /*
  421                  * Look in the name cache; if that fails, look in the
  422                  * directory..
  423                  */
  424                 error = getcwd_getcache(&lvp, &uvp, &bp, bufp);
  425                 if (error == -1) {
  426                         if (lvp->v_type != VDIR) {
  427                                 error = ENOTDIR;
  428                                 goto out;
  429                         }
  430                         error = getcwd_scandir(&lvp, &uvp, &bp, bufp, l);
  431                 }
  432                 if (error)
  433                         goto out;
  434 #if DIAGNOSTIC
  435                 if (lvp != NULL)
  436                         panic("getcwd: oops, forgot to null lvp");
  437                 if (bufp && (bp <= bufp)) {
  438                         panic("getcwd: oops, went back too far");
  439                 }
  440 #endif
  441                 if (bp)
  442                         *(--bp) = '/';
  443                 lvp = uvp;
  444                 uvp = NULL;
  445                 limit--;
  446         } while ((lvp != rvp) && (limit > 0));
  447 
  448 out:
  449         if (bpp)
  450                 *bpp = bp;
  451         if (uvp)
  452                 vput(uvp);
  453         if (lvp)
  454                 vput(lvp);
  455         vrele(rvp);
  456         return error;
  457 }
  458 
  459 /*
  460  * Check if one directory can be found inside another in the directory
  461  * hierarchy.
  462  *
  463  * Intended to be used in chroot, chdir, fchdir, etc., to ensure that
  464  * chroot() actually means something.
  465  */
  466 int
  467 vn_isunder(struct vnode *lvp, struct vnode *rvp, struct lwp *l)
  468 {
  469         int error;
  470 
  471         error = getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN / 2, 0, l);
  472 
  473         if (!error)
  474                 return 1;
  475         else
  476                 return 0;
  477 }
  478 
  479 /*
  480  * Returns true if proc p1's root directory equal to or under p2's
  481  * root directory.
  482  *
  483  * Intended to be used from ptrace/procfs sorts of things.
  484  */
  485 
  486 int
  487 proc_isunder(struct proc *p1, struct lwp *l2)
  488 {
  489         struct vnode *r1 = p1->p_cwdi->cwdi_rdir;
  490         struct vnode *r2 = l2->l_proc->p_cwdi->cwdi_rdir;
  491 
  492         if (r1 == NULL)
  493                 return (r2 == NULL);
  494         else if (r2 == NULL)
  495                 return 1;
  496         else
  497                 return vn_isunder(r1, r2, l2);
  498 }
  499 
  500 /*
  501  * Find pathname of process's current directory.
  502  *
  503  * Use vfs vnode-to-name reverse cache; if that fails, fall back
  504  * to reading directory contents.
  505  */
  506 
  507 int
  508 sys___getcwd(struct lwp *l, const struct sys___getcwd_args *uap, register_t *retval)
  509 {
  510         /* {
  511                 syscallarg(char *) bufp;
  512                 syscallarg(size_t) length;
  513         } */
  514 
  515         int     error;
  516         char   *path;
  517         char   *bp, *bend;
  518         int     len = SCARG(uap, length);
  519         int     lenused;
  520         struct  cwdinfo *cwdi;
  521 
  522         if (len > MAXPATHLEN * 4)
  523                 len = MAXPATHLEN * 4;
  524         else if (len < 2)
  525                 return ERANGE;
  526 
  527         path = (char *)malloc(len, M_TEMP, M_WAITOK);
  528         if (!path)
  529                 return ENOMEM;
  530 
  531         bp = &path[len];
  532         bend = bp;
  533         *(--bp) = '\0';
  534 
  535         /*
  536          * 5th argument here is "max number of vnodes to traverse".
  537          * Since each entry takes up at least 2 bytes in the output buffer,
  538          * limit it to N/2 vnodes for an N byte buffer.
  539          */
  540         cwdi = l->l_proc->p_cwdi;
  541         rw_enter(&cwdi->cwdi_lock, RW_READER);
  542         error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path, 
  543             len/2, GETCWD_CHECK_ACCESS, l);
  544         rw_exit(&cwdi->cwdi_lock);
  545 
  546         if (error)
  547                 goto out;
  548         lenused = bend - bp;
  549         *retval = lenused;
  550         /* put the result into user buffer */
  551         error = copyout(bp, SCARG(uap, bufp), lenused);
  552 
  553 out:
  554         free(path, M_TEMP);
  555         return error;
  556 }
  557 
  558 /*
  559  * Try to find a pathname for a vnode. Since there is no mapping
  560  * vnode -> parent directory, this needs the NAMECACHE_ENTER_REVERSE
  561  * option to work (to make cache_revlookup succeed).
  562  */
  563 int
  564 vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl,
  565     struct proc *p)
  566 {
  567         struct proc *curp = curl->l_proc;
  568         int error, lenused, elen;
  569         char *bp, *bend;
  570         struct vnode *dvp;
  571 
  572         bp = bend = &path[len];
  573         *(--bp) = '\0';
  574 
  575         error = vget(vp, LK_EXCLUSIVE | LK_RETRY);
  576         if (error != 0)
  577                 return error;
  578         error = cache_revlookup(vp, &dvp, &bp, path);
  579         vput(vp);
  580         if (error != 0)
  581                 return (error == -1 ? ENOENT : error);
  582 
  583         error = vget(dvp, 0);
  584         if (error != 0)
  585                 return error;
  586         *(--bp) = '/';
  587         /* XXX GETCWD_CHECK_ACCESS == 0x0001 */
  588         error = getcwd_common(dvp, NULL, &bp, path, len / 2, 1, curl);
  589 
  590         /*
  591          * Strip off emulation path for emulated processes looking at
  592          * the maps file of a process of the same emulation. (Won't
  593          * work if /emul/xxx is a symlink..)
  594          */
  595         if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) {
  596                 elen = strlen(curp->p_emul->e_path);
  597                 if (!strncmp(bp, curp->p_emul->e_path, elen))
  598                         bp = &bp[elen];
  599         }
  600 
  601         lenused = bend - bp;
  602 
  603         memcpy(path, bp, lenused);
  604         path[lenused] = 0;
  605 
  606         return 0;
  607 }

Cache object: 14d2721b43ed24bc0657d59a0b22c090


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.