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.61 2021/06/29 22:39:21 dholland Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 1999, 2020 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.61 2021/06/29 22:39:21 dholland 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/kmem.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 *lvp, 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         kauth_cred_t cred = l->l_cred;
  105         struct componentname cn;
  106         int len, reclen;
  107         tries = 0;
  108 
  109         /* Need exclusive for UFS VOP_GETATTR (itimes) & VOP_LOOKUP. */
  110         KASSERT(VOP_ISLOCKED(lvp) == LK_EXCLUSIVE);
  111 
  112         /*
  113          * If we want the filename, get some info we need while the
  114          * current directory is still locked.
  115          */
  116         if (bufp != NULL) {
  117                 error = VOP_GETATTR(lvp, &va, cred);
  118                 if (error) {
  119                         VOP_UNLOCK(lvp);
  120                         *uvpp = NULL;
  121                         return error;
  122                 }
  123         }
  124 
  125         /*
  126          * Ok, we have to do it the hard way..
  127          * Next, get parent vnode using lookup of ..
  128          */
  129         cn.cn_nameiop = LOOKUP;
  130         cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
  131         cn.cn_cred = cred;
  132         cn.cn_nameptr = "..";
  133         cn.cn_namelen = 2;
  134 
  135         /* At this point, lvp is locked  */
  136         error = VOP_LOOKUP(lvp, uvpp, &cn);
  137         VOP_UNLOCK(lvp);
  138         if (error) {
  139                 *uvpp = NULL;
  140                 return error;
  141         }
  142         uvp = *uvpp;
  143         /* If we don't care about the pathname, we're done */
  144         if (bufp == NULL) {
  145                 return 0;
  146         }
  147 
  148         fileno = va.va_fileid;
  149 
  150         /* I guess UFS_DIRBLKSIZ is a good guess at a good size to use? */
  151         dirbuflen = UFS_DIRBLKSIZ;
  152         if (dirbuflen < va.va_blocksize)
  153                 dirbuflen = va.va_blocksize;
  154         dirbuf = kmem_alloc(dirbuflen, KM_SLEEP);
  155 
  156         /* Now lvp is unlocked, try to lock uvp */
  157         error = vn_lock(uvp, LK_SHARED);
  158         if (error) {
  159                 vrele(uvp);
  160                 *uvpp = NULL;
  161                 return error;
  162         }
  163 
  164 #if 0
  165 unionread:
  166 #endif
  167         off = 0;
  168         do {
  169                 /* call VOP_READDIR of parent */
  170                 iov.iov_base = dirbuf;
  171                 iov.iov_len = dirbuflen;
  172 
  173                 uio.uio_iov = &iov;
  174                 uio.uio_iovcnt = 1;
  175                 uio.uio_offset = off;
  176                 uio.uio_resid = dirbuflen;
  177                 uio.uio_rw = UIO_READ;
  178                 UIO_SETUP_SYSSPACE(&uio);
  179 
  180                 eofflag = 0;
  181 
  182                 error = VOP_READDIR(uvp, &uio, cred, &eofflag, 0, 0);
  183 
  184                 off = uio.uio_offset;
  185 
  186                 /*
  187                  * Try again if NFS tosses its cookies.
  188                  * XXX this can still loop forever if the directory is busted
  189                  * such that the second or subsequent page of it always
  190                  * returns EINVAL
  191                  */
  192                 if ((error == EINVAL) && (tries < 3)) {
  193                         off = 0;
  194                         tries++;
  195                         continue;       /* once more, with feeling */
  196                 }
  197 
  198                 if (!error) {
  199                         char   *cpos;
  200                         struct dirent *dp;
  201 
  202                         cpos = dirbuf;
  203                         tries = 0;
  204 
  205                         /* scan directory page looking for matching vnode */
  206                         for (len = (dirbuflen - uio.uio_resid); len > 0;
  207                             len -= reclen) {
  208                                 dp = (struct dirent *) cpos;
  209                                 reclen = dp->d_reclen;
  210 
  211                                 /* check for malformed directory.. */
  212                                 if (reclen < _DIRENT_MINSIZE(dp) ||
  213                                     reclen > len) {
  214                                         error = EINVAL;
  215                                         goto out;
  216                                 }
  217                                 /*
  218                                  * XXX should perhaps do VOP_LOOKUP to
  219                                  * check that we got back to the right place,
  220                                  * but getting the locking games for that
  221                                  * right would be heinous.
  222                                  */
  223                                 if ((dp->d_type != DT_WHT) &&
  224                                     (dp->d_fileno == fileno)) {
  225                                         char *bp = *bpp;
  226 
  227                                         bp -= dp->d_namlen;
  228                                         if (bp <= bufp) {
  229                                                 error = ERANGE;
  230                                                 goto out;
  231                                         }
  232                                         memcpy(bp, dp->d_name, dp->d_namlen);
  233                                         error = 0;
  234                                         *bpp = bp;
  235                                         goto out;
  236                                 }
  237                                 cpos += reclen;
  238                         }
  239                 } else
  240                         goto out;
  241         } while (!eofflag);
  242 #if 0
  243         /*
  244          * Deal with mount -o union, which unions only the
  245          * root directory of the mount.
  246          */
  247         if ((uvp->v_vflag & VV_ROOT) &&
  248             (uvp->v_mount->mnt_flag & MNT_UNION)) {
  249                 struct vnode *tvp = uvp;
  250 
  251                 uvp = uvp->v_mount->mnt_vnodecovered;
  252                 vput(tvp);
  253                 vref(uvp);
  254                 *uvpp = uvp;
  255                 vn_lock(uvp, LK_SHARED | LK_RETRY);
  256                 goto unionread;
  257         }
  258 #endif
  259         error = ENOENT;
  260 
  261 out:
  262         VOP_UNLOCK(uvp);
  263         kmem_free(dirbuf, dirbuflen);
  264         return error;
  265 }
  266 
  267 /*
  268  * common routine shared by sys___getcwd() and vn_isunder()
  269  */
  270 int
  271 getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
  272     int limit, int flags, struct lwp *l)
  273 {
  274         struct cwdinfo *cwdi = l->l_proc->p_cwdi;
  275         kauth_cred_t cred = l->l_cred;
  276         struct vnode *uvp = NULL;
  277         char *bp = NULL;
  278         int error;
  279         accmode_t accmode = VEXEC;
  280 
  281         error = 0;
  282         if (rvp == NULL) {
  283                 rvp = cwdi->cwdi_rdir;
  284                 if (rvp == NULL)
  285                         rvp = rootvnode;
  286         }
  287 
  288         vref(rvp);
  289         vref(lvp);
  290 
  291         /*
  292          * Error handling invariant:
  293          * Before a `goto out':
  294          *      lvp is either NULL, or held.
  295          *      uvp is either NULL, or held.
  296          */
  297 
  298         if (bufp)
  299                 bp = *bpp;
  300 
  301         /*
  302          * this loop will terminate when one of the following happens:
  303          *      - we hit the root
  304          *      - getdirentries or lookup fails
  305          *      - we run out of space in the buffer.
  306          */
  307         if (lvp == rvp) {
  308                 if (bp)
  309                         *(--bp) = '/';
  310                 goto out;
  311         }
  312         do {
  313                 /*
  314                  * access check here is optional, depending on
  315                  * whether or not caller cares.
  316                  */
  317                 int chkaccess = (flags & GETCWD_CHECK_ACCESS);
  318                 bool locked = false;
  319 
  320                 /*
  321                  * step up if we're a covered vnode..
  322                  * check access on the first vnode only.
  323                  */
  324                 if (lvp->v_vflag & VV_ROOT) {
  325                         vn_lock(lvp, LK_SHARED | LK_RETRY);
  326                         if (chkaccess) {
  327                                 error = VOP_ACCESS(lvp, accmode, cred);
  328                                 if (error) {
  329                                         VOP_UNLOCK(lvp);
  330                                         goto out;
  331                                 }
  332                                 chkaccess = 0;
  333                         }
  334                         while (lvp->v_vflag & VV_ROOT) {
  335                                 struct vnode *tvp;
  336 
  337                                 if (lvp == rvp) {
  338                                         VOP_UNLOCK(lvp);
  339                                         goto out;
  340                                 }
  341 
  342                                 tvp = lvp->v_mount->mnt_vnodecovered;
  343                                 /*
  344                                  * hodie natus est radici frater
  345                                  */
  346                                 if (tvp == NULL) {
  347                                         VOP_UNLOCK(lvp);
  348                                         error = ENOENT;
  349                                         goto out;
  350                                 }
  351                                 vref(tvp);
  352                                 vput(lvp);
  353                                 lvp = tvp;
  354                                 if (lvp->v_vflag & VV_ROOT)
  355                                         vn_lock(lvp, LK_SHARED | LK_RETRY);
  356                         }
  357                 }
  358 
  359                 /* Do we need to check access to the directory? */
  360                 if (chkaccess && !cache_have_id(lvp)) {
  361                         /* Need exclusive for UFS VOP_GETATTR (itimes) & VOP_LOOKUP. */
  362                         vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
  363                         error = VOP_ACCESS(lvp, accmode, cred);
  364                         if (error) {
  365                                 VOP_UNLOCK(lvp);
  366                                 goto out;
  367                         }
  368                         chkaccess = 0;
  369                         locked = true;
  370                 }
  371 
  372                 /*
  373                  * Look in the name cache; if that fails, look in the
  374                  * directory..
  375                  */
  376                 error = cache_revlookup(lvp, &uvp, &bp, bufp, chkaccess,
  377                     accmode);
  378                 if (error == -1) {
  379                         if (!locked) {
  380                                 locked = true;
  381                                 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
  382                         }
  383                         if (lvp->v_type != VDIR) {
  384                                 VOP_UNLOCK(lvp);
  385                                 error = ENOTDIR;
  386                                 goto out;
  387                         }
  388                         error = getcwd_scandir(lvp, &uvp, &bp, bufp, l);
  389                         /* lvp now unlocked */
  390                 } else if (locked) {
  391                         VOP_UNLOCK(lvp);
  392                 }
  393                 if (error)
  394                         goto out;
  395 #if DIAGNOSTIC
  396                 if (bufp && (bp <= bufp)) {
  397                         panic("getcwd: oops, went back too far");
  398                 }
  399 #endif
  400                 accmode = VEXEC | VREAD;
  401                 if (bp)
  402                         *(--bp) = '/';
  403                 vrele(lvp);
  404                 lvp = uvp;
  405                 uvp = NULL;
  406                 limit--;
  407         } while ((lvp != rvp) && (limit > 0));
  408 
  409 out:
  410         if (bpp)
  411                 *bpp = bp;
  412         if (uvp)
  413                 vrele(uvp);
  414         if (lvp)
  415                 vrele(lvp);
  416         vrele(rvp);
  417         return error;
  418 }
  419 
  420 /*
  421  * Check if one directory can be found inside another in the directory
  422  * hierarchy.
  423  *
  424  * Intended to be used in chroot, chdir, fchdir, etc., to ensure that
  425  * chroot() actually means something.
  426  */
  427 int
  428 vn_isunder(struct vnode *lvp, struct vnode *rvp, struct lwp *l)
  429 {
  430         int error;
  431 
  432         error = getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN / 2, 0, l);
  433 
  434         if (!error)
  435                 return 1;
  436         else
  437                 return 0;
  438 }
  439 
  440 /*
  441  * Returns true if proc p1's root directory equal to or under p2's
  442  * root directory.
  443  *
  444  * Intended to be used from ptrace/procfs sorts of things.
  445  */
  446 
  447 int
  448 proc_isunder(struct proc *p1, struct lwp *l2)
  449 {
  450         struct vnode *r1 = p1->p_cwdi->cwdi_rdir;
  451         struct vnode *r2 = l2->l_proc->p_cwdi->cwdi_rdir;
  452 
  453         if (r1 == NULL)
  454                 return (r2 == NULL);
  455         else if (r2 == NULL)
  456                 return 1;
  457         else
  458                 return vn_isunder(r1, r2, l2);
  459 }
  460 
  461 /*
  462  * Find pathname of process's current directory.
  463  *
  464  * Use vfs vnode-to-name reverse cache; if that fails, fall back
  465  * to reading directory contents.
  466  */
  467 
  468 int
  469 sys___getcwd(struct lwp *l, const struct sys___getcwd_args *uap, register_t *retval)
  470 {
  471         /* {
  472                 syscallarg(char *) bufp;
  473                 syscallarg(size_t) length;
  474         } */
  475 
  476         int     error;
  477         char   *path;
  478         char   *bp, *bend;
  479         int     len = SCARG(uap, length);
  480         int     lenused;
  481         struct  cwdinfo *cwdi;
  482 
  483         if (len > MAXPATHLEN * 4)
  484                 len = MAXPATHLEN * 4;
  485         else if (len < 2)
  486                 return ERANGE;
  487 
  488         path = kmem_alloc(len, KM_SLEEP);
  489         bp = &path[len];
  490         bend = bp;
  491         *(--bp) = '\0';
  492 
  493         /*
  494          * 5th argument here is "max number of vnodes to traverse".
  495          * Since each entry takes up at least 2 bytes in the output buffer,
  496          * limit it to N/2 vnodes for an N byte buffer.
  497          */
  498         cwdi = l->l_proc->p_cwdi;
  499         rw_enter(&cwdi->cwdi_lock, RW_READER);
  500         error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path, 
  501             len/2, GETCWD_CHECK_ACCESS, l);
  502         rw_exit(&cwdi->cwdi_lock);
  503 
  504         if (error)
  505                 goto out;
  506         lenused = bend - bp;
  507         *retval = lenused;
  508         /* put the result into user buffer */
  509         error = copyout(bp, SCARG(uap, bufp), lenused);
  510 
  511 out:
  512         kmem_free(path, len);
  513         return error;
  514 }
  515 
  516 /*
  517  * Try to find a pathname for a vnode.  Since there is no mapping vnode ->
  518  * parent directory, this needs the namecache to succeed.  Caller holds a
  519  * reference to the vnode.
  520  */
  521 int
  522 vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl,
  523     struct proc *p)
  524 {
  525         struct proc *curp = curl->l_proc;
  526         int error, lenused, elen;
  527         char *bp, *bend;
  528         struct vnode *dvp;
  529 
  530         KASSERT(vrefcnt(vp) > 0);
  531 
  532         bp = bend = &path[len];
  533         *(--bp) = '\0';
  534 
  535         error = cache_revlookup(vp, &dvp, &bp, path, false, 0);
  536         if (error != 0)
  537                 return (error == -1 ? ENOENT : error);
  538 
  539         *(--bp) = '/';
  540         error = getcwd_common(dvp, NULL, &bp, path, len / 2,
  541             GETCWD_CHECK_ACCESS, curl);
  542         vrele(dvp);
  543         if (error != 0)
  544                 return error;
  545 
  546         /*
  547          * Strip off emulation path for emulated processes looking at
  548          * the maps file of a process of the same emulation. (Won't
  549          * work if /emul/xxx is a symlink..)
  550          */
  551         if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) {
  552                 elen = strlen(curp->p_emul->e_path);
  553                 if (!strncmp(bp, curp->p_emul->e_path, elen))
  554                         bp = &bp[elen];
  555         }
  556 
  557         lenused = bend - bp;
  558 
  559         memcpy(path, bp, lenused);
  560         path[lenused] = '\0';
  561 
  562         return 0;
  563 }

Cache object: 9567b379afe84bff60412f50f3e1d233


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