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

Cache object: 995723f7fef707399c8e1723dfc136e5


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