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

Cache object: 588a242215c8dd8ce4eb0b993f30925a


[ 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.