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/fs/fdescfs/fdesc_vnops.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 /*-
    2  * Copyright (c) 1992, 1993
    3  *      The Regents of the University of California.  All rights reserved.
    4  *
    5  * This code is derived from software donated to Berkeley by
    6  * Jan-Simon Pendry.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the University nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  *
   32  *      @(#)fdesc_vnops.c       8.9 (Berkeley) 1/21/94
   33  *
   34  * $FreeBSD: releng/11.0/sys/fs/fdescfs/fdesc_vnops.c 298853 2016-04-30 16:01:37Z emaste $
   35  */
   36 
   37 /*
   38  * /dev/fd Filesystem
   39  */
   40 
   41 #include <sys/param.h>
   42 #include <sys/systm.h>
   43 #include <sys/capsicum.h>
   44 #include <sys/conf.h>
   45 #include <sys/dirent.h>
   46 #include <sys/filedesc.h>
   47 #include <sys/kernel.h> /* boottime */
   48 #include <sys/lock.h>
   49 #include <sys/mutex.h>
   50 #include <sys/malloc.h>
   51 #include <sys/file.h>   /* Must come after sys/malloc.h */
   52 #include <sys/mount.h>
   53 #include <sys/namei.h>
   54 #include <sys/proc.h>
   55 #include <sys/stat.h>
   56 #include <sys/vnode.h>
   57 
   58 #include <fs/fdescfs/fdesc.h>
   59 
   60 #define NFDCACHE 4
   61 #define FD_NHASH(ix) \
   62         (&fdhashtbl[(ix) & fdhash])
   63 static LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
   64 static u_long fdhash;
   65 
   66 struct mtx fdesc_hashmtx;
   67 
   68 static vop_getattr_t    fdesc_getattr;
   69 static vop_lookup_t     fdesc_lookup;
   70 static vop_open_t       fdesc_open;
   71 static vop_readdir_t    fdesc_readdir;
   72 static vop_reclaim_t    fdesc_reclaim;
   73 static vop_setattr_t    fdesc_setattr;
   74 
   75 static struct vop_vector fdesc_vnodeops = {
   76         .vop_default =          &default_vnodeops,
   77 
   78         .vop_access =           VOP_NULL,
   79         .vop_getattr =          fdesc_getattr,
   80         .vop_lookup =           fdesc_lookup,
   81         .vop_open =             fdesc_open,
   82         .vop_pathconf =         vop_stdpathconf,
   83         .vop_readdir =          fdesc_readdir,
   84         .vop_reclaim =          fdesc_reclaim,
   85         .vop_setattr =          fdesc_setattr,
   86 };
   87 
   88 static void fdesc_insmntque_dtr(struct vnode *, void *);
   89 static void fdesc_remove_entry(struct fdescnode *);
   90 
   91 /*
   92  * Initialise cache headers
   93  */
   94 int
   95 fdesc_init(struct vfsconf *vfsp)
   96 {
   97 
   98         mtx_init(&fdesc_hashmtx, "fdescfs_hash", NULL, MTX_DEF);
   99         fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
  100         return (0);
  101 }
  102 
  103 /*
  104  * Uninit ready for unload.
  105  */
  106 int
  107 fdesc_uninit(struct vfsconf *vfsp)
  108 {
  109 
  110         hashdestroy(fdhashtbl, M_CACHE, fdhash);
  111         mtx_destroy(&fdesc_hashmtx);
  112         return (0);
  113 }
  114 
  115 /*
  116  * If allocating vnode fails, call this.
  117  */
  118 static void
  119 fdesc_insmntque_dtr(struct vnode *vp, void *arg)
  120 {
  121 
  122         vgone(vp);
  123         vput(vp);
  124 }
  125 
  126 /*
  127  * Remove an entry from the hash if it exists.
  128  */
  129 static void
  130 fdesc_remove_entry(struct fdescnode *fd)
  131 {
  132         struct fdhashhead *fc;
  133         struct fdescnode *fd2;
  134 
  135         fc = FD_NHASH(fd->fd_ix);
  136         mtx_lock(&fdesc_hashmtx);
  137         LIST_FOREACH(fd2, fc, fd_hash) {
  138                 if (fd == fd2) {
  139                         LIST_REMOVE(fd, fd_hash);
  140                         break;
  141                 }
  142         }
  143         mtx_unlock(&fdesc_hashmtx);
  144 }
  145 
  146 int
  147 fdesc_allocvp(fdntype ftype, unsigned fd_fd, int ix, struct mount *mp,
  148     struct vnode **vpp)
  149 {
  150         struct fdescmount *fmp;
  151         struct fdhashhead *fc;
  152         struct fdescnode *fd, *fd2;
  153         struct vnode *vp, *vp2;
  154         struct thread *td;
  155         int error = 0;
  156 
  157         td = curthread;
  158         fc = FD_NHASH(ix);
  159 loop:
  160         mtx_lock(&fdesc_hashmtx);
  161         /*
  162          * If a forced unmount is progressing, we need to drop it. The flags are
  163          * protected by the hashmtx.
  164          */
  165         fmp = (struct fdescmount *)mp->mnt_data;
  166         if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) {
  167                 mtx_unlock(&fdesc_hashmtx);
  168                 return (-1);
  169         }
  170 
  171         LIST_FOREACH(fd, fc, fd_hash) {
  172                 if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
  173                         /* Get reference to vnode in case it's being free'd */
  174                         vp = fd->fd_vnode;
  175                         VI_LOCK(vp);
  176                         mtx_unlock(&fdesc_hashmtx);
  177                         if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td))
  178                                 goto loop;
  179                         *vpp = vp;
  180                         return (0);
  181                 }
  182         }
  183         mtx_unlock(&fdesc_hashmtx);
  184 
  185         fd = malloc(sizeof(struct fdescnode), M_TEMP, M_WAITOK);
  186 
  187         error = getnewvnode("fdescfs", mp, &fdesc_vnodeops, &vp);
  188         if (error) {
  189                 free(fd, M_TEMP);
  190                 return (error);
  191         }
  192         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
  193         vp->v_data = fd;
  194         fd->fd_vnode = vp;
  195         fd->fd_type = ftype;
  196         fd->fd_fd = fd_fd;
  197         fd->fd_ix = ix;
  198         error = insmntque1(vp, mp, fdesc_insmntque_dtr, NULL);
  199         if (error != 0) {
  200                 *vpp = NULLVP;
  201                 return (error);
  202         }
  203 
  204         /* Make sure that someone didn't beat us when inserting the vnode. */
  205         mtx_lock(&fdesc_hashmtx);
  206         /*
  207          * If a forced unmount is progressing, we need to drop it. The flags are
  208          * protected by the hashmtx.
  209          */
  210         fmp = (struct fdescmount *)mp->mnt_data;
  211         if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) {
  212                 mtx_unlock(&fdesc_hashmtx);
  213                 vgone(vp);
  214                 vput(vp);
  215                 *vpp = NULLVP;
  216                 return (-1);
  217         }
  218 
  219         LIST_FOREACH(fd2, fc, fd_hash) {
  220                 if (fd2->fd_ix == ix && fd2->fd_vnode->v_mount == mp) {
  221                         /* Get reference to vnode in case it's being free'd */
  222                         vp2 = fd2->fd_vnode;
  223                         VI_LOCK(vp2);
  224                         mtx_unlock(&fdesc_hashmtx);
  225                         error = vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK, td);
  226                         /* Someone beat us, dec use count and wait for reclaim */
  227                         vgone(vp);
  228                         vput(vp);
  229                         /* If we didn't get it, return no vnode. */
  230                         if (error)
  231                                 vp2 = NULLVP;
  232                         *vpp = vp2;
  233                         return (error);
  234                 }
  235         }
  236 
  237         /* If we came here, we can insert it safely. */
  238         LIST_INSERT_HEAD(fc, fd, fd_hash);
  239         mtx_unlock(&fdesc_hashmtx);
  240         *vpp = vp;
  241         return (0);
  242 }
  243 
  244 struct fdesc_get_ino_args {
  245         fdntype ftype;
  246         unsigned fd_fd;
  247         int ix;
  248         struct file *fp;
  249         struct thread *td;
  250 };
  251 
  252 static int
  253 fdesc_get_ino_alloc(struct mount *mp, void *arg, int lkflags,
  254     struct vnode **rvp)
  255 {
  256         struct fdesc_get_ino_args *a;
  257         int error;
  258 
  259         a = arg;
  260         error = fdesc_allocvp(a->ftype, a->fd_fd, a->ix, mp, rvp);
  261         fdrop(a->fp, a->td);
  262         return (error);
  263 }
  264 
  265 
  266 /*
  267  * vp is the current namei directory
  268  * ndp is the name to locate in that directory...
  269  */
  270 static int
  271 fdesc_lookup(struct vop_lookup_args *ap)
  272 {
  273         struct vnode **vpp = ap->a_vpp;
  274         struct vnode *dvp = ap->a_dvp;
  275         struct componentname *cnp = ap->a_cnp;
  276         char *pname = cnp->cn_nameptr;
  277         struct thread *td = cnp->cn_thread;
  278         struct file *fp;
  279         struct fdesc_get_ino_args arg;
  280         cap_rights_t rights;
  281         int nlen = cnp->cn_namelen;
  282         u_int fd, fd1;
  283         int error;
  284         struct vnode *fvp;
  285 
  286         if ((cnp->cn_flags & ISLASTCN) &&
  287             (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
  288                 error = EROFS;
  289                 goto bad;
  290         }
  291 
  292         if (cnp->cn_namelen == 1 && *pname == '.') {
  293                 *vpp = dvp;
  294                 VREF(dvp);
  295                 return (0);
  296         }
  297 
  298         if (VTOFDESC(dvp)->fd_type != Froot) {
  299                 error = ENOTDIR;
  300                 goto bad;
  301         }
  302 
  303         fd = 0;
  304         /* the only time a leading 0 is acceptable is if it's "" */
  305         if (*pname == '' && nlen != 1) {
  306                 error = ENOENT;
  307                 goto bad;
  308         }
  309         while (nlen--) {
  310                 if (*pname < '' || *pname > '9') {
  311                         error = ENOENT;
  312                         goto bad;
  313                 }
  314                 fd1 = 10 * fd + *pname++ - '';
  315                 if (fd1 < fd) {
  316                         error = ENOENT;
  317                         goto bad;
  318                 }
  319                 fd = fd1;
  320         }
  321 
  322         /*
  323          * No rights to check since 'fp' isn't actually used.
  324          */
  325         if ((error = fget(td, fd, cap_rights_init(&rights), &fp)) != 0)
  326                 goto bad;
  327 
  328         /* Check if we're looking up ourselves. */
  329         if (VTOFDESC(dvp)->fd_ix == FD_DESC + fd) {
  330                 /*
  331                  * In case we're holding the last reference to the file, the dvp
  332                  * will be re-acquired.
  333                  */
  334                 vhold(dvp);
  335                 VOP_UNLOCK(dvp, 0);
  336                 fdrop(fp, td);
  337 
  338                 /* Re-aquire the lock afterwards. */
  339                 vn_lock(dvp, LK_RETRY | LK_EXCLUSIVE);
  340                 vdrop(dvp);
  341                 fvp = dvp;
  342                 if ((dvp->v_iflag & VI_DOOMED) != 0)
  343                         error = ENOENT;
  344         } else {
  345                 /*
  346                  * Unlock our root node (dvp) when doing this, since we might
  347                  * deadlock since the vnode might be locked by another thread
  348                  * and the root vnode lock will be obtained afterwards (in case
  349                  * we're looking up the fd of the root vnode), which will be the
  350                  * opposite lock order. Vhold the root vnode first so we don't
  351                  * lose it.
  352                  */
  353                 arg.ftype = Fdesc;
  354                 arg.fd_fd = fd;
  355                 arg.ix = FD_DESC + fd;
  356                 arg.fp = fp;
  357                 arg.td = td;
  358                 error = vn_vget_ino_gen(dvp, fdesc_get_ino_alloc, &arg,
  359                     LK_EXCLUSIVE, &fvp);
  360         }
  361         
  362         if (error)
  363                 goto bad;
  364         *vpp = fvp;
  365         return (0);
  366 
  367 bad:
  368         *vpp = NULL;
  369         return (error);
  370 }
  371 
  372 static int
  373 fdesc_open(struct vop_open_args *ap)
  374 {
  375         struct vnode *vp = ap->a_vp;
  376 
  377         if (VTOFDESC(vp)->fd_type == Froot)
  378                 return (0);
  379 
  380         /*
  381          * XXX Kludge: set td->td_proc->p_dupfd to contain the value of the file
  382          * descriptor being sought for duplication. The error return ensures
  383          * that the vnode for this device will be released by vn_open. Open
  384          * will detect this special error and take the actions in dupfdopen.
  385          * Other callers of vn_open or VOP_OPEN will simply report the
  386          * error.
  387          */
  388         ap->a_td->td_dupfd = VTOFDESC(vp)->fd_fd;       /* XXX */
  389         return (ENODEV);
  390 }
  391 
  392 static int
  393 fdesc_getattr(struct vop_getattr_args *ap)
  394 {
  395         struct vnode *vp = ap->a_vp;
  396         struct vattr *vap = ap->a_vap;
  397 
  398         vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
  399         vap->va_fileid = VTOFDESC(vp)->fd_ix;
  400         vap->va_uid = 0;
  401         vap->va_gid = 0;
  402         vap->va_blocksize = DEV_BSIZE;
  403         vap->va_atime.tv_sec = boottime.tv_sec;
  404         vap->va_atime.tv_nsec = 0;
  405         vap->va_mtime = vap->va_atime;
  406         vap->va_ctime = vap->va_mtime;
  407         vap->va_gen = 0;
  408         vap->va_flags = 0;
  409         vap->va_bytes = 0;
  410         vap->va_filerev = 0;
  411 
  412         switch (VTOFDESC(vp)->fd_type) {
  413         case Froot:
  414                 vap->va_type = VDIR;
  415                 vap->va_nlink = 2;
  416                 vap->va_size = DEV_BSIZE;
  417                 vap->va_rdev = NODEV;
  418                 break;
  419 
  420         case Fdesc:
  421                 vap->va_type = VCHR;
  422                 vap->va_nlink = 1;
  423                 vap->va_size = 0;
  424                 vap->va_rdev = makedev(0, vap->va_fileid);
  425                 break;
  426 
  427         default:
  428                 panic("fdesc_getattr");
  429                 break;
  430         }
  431 
  432         vp->v_type = vap->va_type;
  433         return (0);
  434 }
  435 
  436 static int
  437 fdesc_setattr(struct vop_setattr_args *ap)
  438 {
  439         struct vattr *vap = ap->a_vap;
  440         struct vnode *vp;
  441         struct mount *mp;
  442         struct file *fp;
  443         struct thread *td = curthread;
  444         cap_rights_t rights;
  445         unsigned fd;
  446         int error;
  447 
  448         /*
  449          * Can't mess with the root vnode
  450          */
  451         if (VTOFDESC(ap->a_vp)->fd_type == Froot)
  452                 return (EACCES);
  453 
  454         fd = VTOFDESC(ap->a_vp)->fd_fd;
  455 
  456         /*
  457          * Allow setattr where there is an underlying vnode.
  458          */
  459         error = getvnode(td, fd,
  460             cap_rights_init(&rights, CAP_EXTATTR_SET), &fp);
  461         if (error) {
  462                 /*
  463                  * getvnode() returns EINVAL if the file descriptor is not
  464                  * backed by a vnode.  Silently drop all changes except
  465                  * chflags(2) in this case.
  466                  */
  467                 if (error == EINVAL) {
  468                         if (vap->va_flags != VNOVAL)
  469                                 error = EOPNOTSUPP;
  470                         else
  471                                 error = 0;
  472                 }
  473                 return (error);
  474         }
  475         vp = fp->f_vnode;
  476         if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) == 0) {
  477                 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
  478                 error = VOP_SETATTR(vp, ap->a_vap, ap->a_cred);
  479                 VOP_UNLOCK(vp, 0);
  480                 vn_finished_write(mp);
  481         }
  482         fdrop(fp, td);
  483         return (error);
  484 }
  485 
  486 #define UIO_MX 16
  487 
  488 static int
  489 fdesc_readdir(struct vop_readdir_args *ap)
  490 {
  491         struct uio *uio = ap->a_uio;
  492         struct filedesc *fdp;
  493         struct dirent d;
  494         struct dirent *dp = &d;
  495         int error, i, off, fcnt;
  496 
  497         if (VTOFDESC(ap->a_vp)->fd_type != Froot)
  498                 panic("fdesc_readdir: not dir");
  499 
  500         if (ap->a_ncookies != NULL)
  501                 *ap->a_ncookies = 0;
  502 
  503         off = (int)uio->uio_offset;
  504         if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 ||
  505             uio->uio_resid < UIO_MX)
  506                 return (EINVAL);
  507         i = (u_int)off / UIO_MX;
  508         fdp = uio->uio_td->td_proc->p_fd;
  509         error = 0;
  510 
  511         fcnt = i - 2;           /* The first two nodes are `.' and `..' */
  512 
  513         FILEDESC_SLOCK(fdp);
  514         while (i < fdp->fd_nfiles + 2 && uio->uio_resid >= UIO_MX) {
  515                 bzero((caddr_t)dp, UIO_MX);
  516                 switch (i) {
  517                 case 0: /* `.' */
  518                 case 1: /* `..' */
  519                         dp->d_fileno = i + FD_ROOT;
  520                         dp->d_namlen = i + 1;
  521                         dp->d_reclen = UIO_MX;
  522                         bcopy("..", dp->d_name, dp->d_namlen);
  523                         dp->d_name[i + 1] = '\0';
  524                         dp->d_type = DT_DIR;
  525                         break;
  526                 default:
  527                         if (fdp->fd_ofiles[fcnt].fde_file == NULL)
  528                                 break;
  529                         dp->d_namlen = sprintf(dp->d_name, "%d", fcnt);
  530                         dp->d_reclen = UIO_MX;
  531                         dp->d_type = DT_CHR;
  532                         dp->d_fileno = i + FD_DESC;
  533                         break;
  534                 }
  535                 if (dp->d_namlen != 0) {
  536                         /*
  537                          * And ship to userland
  538                          */
  539                         FILEDESC_SUNLOCK(fdp);
  540                         error = uiomove(dp, UIO_MX, uio);
  541                         if (error)
  542                                 goto done;
  543                         FILEDESC_SLOCK(fdp);
  544                 }
  545                 i++;
  546                 fcnt++;
  547         }
  548         FILEDESC_SUNLOCK(fdp);
  549 
  550 done:
  551         uio->uio_offset = i * UIO_MX;
  552         return (error);
  553 }
  554 
  555 static int
  556 fdesc_reclaim(struct vop_reclaim_args *ap)
  557 {
  558         struct vnode *vp;
  559         struct fdescnode *fd;
  560 
  561         vp = ap->a_vp;
  562         fd = VTOFDESC(vp);
  563         fdesc_remove_entry(fd);
  564         free(vp->v_data, M_TEMP);
  565         vp->v_data = NULL;
  566         return (0);
  567 }

Cache object: e0f4dccd53c0afc5540f3c6bad6cdd34


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