LCOV - code coverage report
Current view: top level - kern - vfs_getcwd.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 193 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 4 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $OpenBSD: vfs_getcwd.c,v 1.31 2018/05/27 06:02:14 visa 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/kernel.h>
      38             : #include <sys/stat.h>
      39             : #include <sys/lock.h>
      40             : #include <sys/vnode.h>
      41             : #include <sys/mount.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           0 : vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
      54             :     char *bufp, struct proc *p)
      55             : {
      56           0 :         int eofflag, tries, dirbuflen = 0, len, reclen, error = 0;
      57             :         off_t off;
      58           0 :         struct uio uio;
      59           0 :         struct iovec iov;
      60             :         char *dirbuf = NULL;
      61             :         ino_t fileno;
      62           0 :         struct vattr va;
      63             :         struct vnode *uvp = NULL;
      64           0 :         struct vnode *lvp = *lvpp;      
      65           0 :         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           0 :         if (bufp != NULL) {
      74           0 :                 error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
      75           0 :                 if (error) {
      76           0 :                         vput(lvp);
      77           0 :                         *lvpp = NULL;
      78           0 :                         *uvpp = NULL;
      79           0 :                         return (error);
      80             :                 }
      81             :         }
      82             : 
      83           0 :         cn.cn_nameiop = LOOKUP;
      84           0 :         cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
      85           0 :         cn.cn_proc = p;
      86           0 :         cn.cn_cred = p->p_ucred;
      87           0 :         cn.cn_pnbuf = NULL;
      88           0 :         cn.cn_nameptr = "..";
      89           0 :         cn.cn_namelen = 2;
      90           0 :         cn.cn_consume = 0;
      91             : 
      92             :         /* Get parent vnode using lookup of '..' */
      93           0 :         error = VOP_LOOKUP(lvp, uvpp, &cn);
      94           0 :         if (error) {
      95           0 :                 vput(lvp);
      96           0 :                 *lvpp = NULL;
      97           0 :                 *uvpp = NULL;
      98           0 :                 return (error);
      99             :         }
     100             : 
     101           0 :         uvp = *uvpp;
     102             : 
     103             :         /* If we don't care about the pathname, we're done */
     104           0 :         if (bufp == NULL) {
     105             :                 error = 0;
     106           0 :                 goto out;
     107             :         }
     108             : 
     109           0 :         fileno = va.va_fileid;
     110             : 
     111             :         dirbuflen = DIRBLKSIZ;
     112           0 :         if (dirbuflen < va.va_blocksize)
     113           0 :                 dirbuflen = va.va_blocksize;
     114             :         /* XXX we need some limit for fuse, 1 MB should be enough */
     115           0 :         if (dirbuflen > 0xfffff) {
     116             :                 error = EINVAL;
     117           0 :                 goto out;
     118             :         }
     119           0 :         dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
     120             : 
     121             :         off = 0;
     122             : 
     123           0 :         do {
     124             :                 char   *cpos;
     125             :                 struct dirent *dp;
     126             : 
     127           0 :                 iov.iov_base = dirbuf;
     128           0 :                 iov.iov_len = dirbuflen;
     129             : 
     130           0 :                 uio.uio_iov = &iov;
     131           0 :                 uio.uio_iovcnt = 1;
     132           0 :                 uio.uio_offset = off;
     133           0 :                 uio.uio_resid = dirbuflen;
     134           0 :                 uio.uio_segflg = UIO_SYSSPACE;
     135           0 :                 uio.uio_rw = UIO_READ;
     136           0 :                 uio.uio_procp = p;
     137             : 
     138           0 :                 eofflag = 0;
     139             : 
     140             :                 /* Call VOP_READDIR of parent */
     141           0 :                 error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag);
     142             : 
     143           0 :                 off = uio.uio_offset;
     144             : 
     145             :                 /* Try again if NFS tosses its cookies */
     146           0 :                 if (error == EINVAL && tries < 3) {
     147           0 :                         tries++;
     148             :                         off = 0;
     149           0 :                         continue;
     150           0 :                 } else if (error) {
     151           0 :                         goto out; /* Old userland getcwd() behaviour */
     152             :                 }
     153             : 
     154             :                 cpos = dirbuf;
     155             :                 tries = 0;
     156             : 
     157             :                 /* Scan directory page looking for matching vnode */ 
     158           0 :                 for (len = (dirbuflen - uio.uio_resid); len > 0;
     159           0 :                      len -= reclen) {
     160           0 :                         dp = (struct dirent *)cpos;
     161           0 :                         reclen = dp->d_reclen;
     162             : 
     163             :                         /* Check for malformed directory */
     164           0 :                         if (reclen < DIRENT_RECSIZE(1) || reclen > len) {
     165             :                                 error = EINVAL;
     166           0 :                                 goto out;
     167             :                         }
     168             : 
     169           0 :                         if (dp->d_fileno == fileno) {
     170           0 :                                 char *bp = *bpp;
     171             : 
     172           0 :                                 if (offsetof(struct dirent, d_name) +
     173           0 :                                     dp->d_namlen > reclen) {
     174             :                                         error = EINVAL;
     175           0 :                                         goto out;
     176             :                                 }
     177           0 :                                 bp -= dp->d_namlen;
     178           0 :                                 if (bp <= bufp) {
     179             :                                         error = ERANGE;
     180           0 :                                         goto out;
     181             :                                 }
     182             : 
     183           0 :                                 memmove(bp, dp->d_name, dp->d_namlen);
     184             :                                 error = 0;
     185           0 :                                 *bpp = bp;
     186             : 
     187           0 :                                 goto out;
     188             :                         }
     189             : 
     190           0 :                         cpos += reclen;
     191             :                 }
     192             : 
     193           0 :         } while (!eofflag);
     194             : 
     195           0 :         error = ENOENT;
     196             : 
     197             : out:
     198             : 
     199           0 :         vrele(lvp);
     200           0 :         *lvpp = NULL;
     201             : 
     202           0 :         free(dirbuf, M_TEMP, dirbuflen);
     203             : 
     204           0 :         return (error);
     205           0 : }
     206             : 
     207             : /* Do a lookup in the vnode-to-name reverse */
     208             : int
     209           0 : 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           0 :         lvp = *lvpp;
     217           0 :         obp = *bpp;     /* Save original position to restore to on error */
     218             : 
     219           0 :         error = cache_revlookup(lvp, uvpp, bpp, bufp);
     220           0 :         if (error) {
     221           0 :                 if (error != -1) {
     222           0 :                         vput(lvp);
     223           0 :                         *lvpp = NULL;
     224           0 :                         *uvpp = NULL;
     225           0 :                 }
     226             : 
     227           0 :                 return (error);
     228             :         }
     229             : 
     230           0 :         uvp = *uvpp;
     231           0 :         vpid = uvp->v_id;
     232             : 
     233             : 
     234             :         /* Release current lock before acquiring the parent lock */
     235           0 :         VOP_UNLOCK(lvp);
     236             : 
     237           0 :         error = vget(uvp, LK_EXCLUSIVE | LK_RETRY);
     238           0 :         if (error)
     239           0 :                 *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           0 :         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           0 :                 if (!error)
     251           0 :                         vput(uvp);
     252             : 
     253           0 :                 *uvpp = NULL;
     254             :                 
     255           0 :                 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
     256           0 :                 if (!error) {
     257           0 :                         *bpp = obp; /* restore the buffer */
     258           0 :                         return (-1);
     259             :                 }
     260             :         }
     261             : 
     262           0 :         vrele(lvp);
     263           0 :         *lvpp = NULL;
     264             : 
     265           0 :         return (error);
     266           0 : }
     267             : 
     268             : /* Common routine shared by sys___getcwd() and vn_isunder() */
     269             : int
     270           0 : vfs_getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
     271             :     int limit, int flags, struct proc *p)
     272             : {
     273           0 :         struct filedesc *fdp = p->p_fd;
     274           0 :         struct vnode *uvp = NULL;
     275           0 :         char *bp = NULL;
     276             :         int error, perms = VEXEC;
     277             : 
     278           0 :         if (rvp == NULL) {
     279           0 :                 rvp = fdp->fd_rdir;
     280           0 :                 if (rvp == NULL)
     281           0 :                         rvp = rootvnode;
     282             :         }
     283             : 
     284           0 :         vref(rvp);
     285           0 :         vref(lvp);
     286             : 
     287           0 :         error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
     288           0 :         if (error) {
     289           0 :                 vrele(lvp);
     290           0 :                 lvp = NULL;
     291           0 :                 goto out;
     292             :         }
     293             : 
     294           0 :         if (bufp)
     295           0 :                 bp = *bpp;
     296             : 
     297           0 :         if (lvp == rvp) {
     298           0 :                 if (bp)
     299           0 :                         *(--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           0 :         do {
     308           0 :                 if (lvp->v_type != VDIR) {
     309             :                         error = ENOTDIR;
     310           0 :                         goto out;
     311             :                 }
     312             : 
     313             :                 /* Check for access if caller cares */
     314           0 :                 if (flags & GETCWD_CHECK_ACCESS) {
     315           0 :                         error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
     316           0 :                         if (error)
     317             :                                 goto out;
     318             :                         perms = VEXEC|VREAD;
     319           0 :                 }
     320             : 
     321             :                 /* Step up if we're a covered vnode */
     322           0 :                 while (lvp->v_flag & VROOT) {
     323             :                         struct vnode *tvp;
     324             : 
     325           0 :                         if (lvp == rvp)
     326           0 :                                 goto out;
     327             :                         
     328             :                         tvp = lvp;
     329           0 :                         lvp = lvp->v_mount->mnt_vnodecovered;
     330             : 
     331           0 :                         vput(tvp);
     332             : 
     333           0 :                         if (lvp == NULL) {
     334             :                                 error = ENOENT;
     335           0 :                                 goto out;
     336             :                         }
     337             : 
     338           0 :                         vref(lvp);
     339             : 
     340           0 :                         error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
     341           0 :                         if (error) {
     342           0 :                                 vrele(lvp);
     343           0 :                                 lvp = NULL;
     344           0 :                                 goto out;
     345             :                         }
     346           0 :                 }
     347             : 
     348             :                 /* Look in the name cache */
     349           0 :                 error = vfs_getcwd_getcache(&lvp, &uvp, &bp, bufp);
     350             : 
     351           0 :                 if (error == -1) {
     352             :                         /* If that fails, look in the directory */
     353           0 :                         error = vfs_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
     354           0 :                 }
     355             : 
     356           0 :                 if (error)
     357             :                         goto out;
     358             : 
     359             : #ifdef DIAGNOSTIC
     360           0 :                 if (lvp != NULL)
     361           0 :                         panic("getcwd: oops, forgot to null lvp");
     362           0 :                 if (bufp && (bp <= bufp)) {
     363           0 :                         panic("getcwd: oops, went back too far");
     364             :                 }
     365             : #endif
     366             : 
     367           0 :                 if (bp)
     368           0 :                         *(--bp) = '/';
     369             : 
     370           0 :                 lvp = uvp;
     371           0 :                 uvp = NULL;
     372           0 :                 limit--;
     373             : 
     374           0 :         } while ((lvp != rvp) && (limit > 0)); 
     375             : 
     376             : out:
     377             : 
     378           0 :         if (bpp)
     379           0 :                 *bpp = bp;
     380             : 
     381           0 :         if (uvp)
     382           0 :                 vput(uvp);
     383             : 
     384           0 :         if (lvp)
     385           0 :                 vput(lvp);
     386             : 
     387           0 :         vrele(rvp);
     388             : 
     389           0 :         return (error);
     390           0 : }
     391             : 
     392             : /* Find pathname of a process's current directory */
     393             : int
     394           0 : sys___getcwd(struct proc *p, void *v, register_t *retval) 
     395             : {
     396           0 :         struct sys___getcwd_args *uap = v;
     397           0 :         int error, lenused, len = SCARG(uap, len);
     398           0 :         char *path, *bp, *bend;
     399             : 
     400           0 :         if (len > MAXPATHLEN * 4)
     401           0 :                 len = MAXPATHLEN * 4;
     402           0 :         else if (len < 2)
     403           0 :                 return (ERANGE);
     404             : 
     405           0 :         path = malloc(len, M_TEMP, M_WAITOK);
     406             : 
     407           0 :         bp = &path[len];
     408             :         bend = bp;
     409           0 :         *(--bp) = '\0';
     410             : 
     411             :         /*
     412             :          * 5th argument here is "max number of vnodes to traverse".
     413             :          * Since each entry takes up at least 2 bytes in the output
     414             :          * buffer, limit it to N/2 vnodes for an N byte buffer.
     415             :          */
     416           0 :         error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, len/2,
     417             :             GETCWD_CHECK_ACCESS, p);
     418             : 
     419           0 :         if (error)
     420             :                 goto out;
     421             : 
     422           0 :         lenused = bend - bp;
     423           0 :         *retval = lenused;
     424             : 
     425             :         /* Put the result into user buffer */
     426           0 :         error = copyout(bp, SCARG(uap, buf), lenused);
     427             : 
     428             : out:
     429           0 :         free(path, M_TEMP, len);
     430             : 
     431           0 :         return (error);
     432           0 : }

Generated by: LCOV version 1.13