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/tmpfs/tmpfs_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 /*      $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $    */
    2 
    3 /*-
    4  * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
    9  * 2005 program.
   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 /*
   34  * tmpfs vnode interface.
   35  */
   36 #include <sys/cdefs.h>
   37 __FBSDID("$FreeBSD$");
   38 
   39 #include <sys/param.h>
   40 #include <sys/fcntl.h>
   41 #include <sys/lockf.h>
   42 #include <sys/namei.h>
   43 #include <sys/priv.h>
   44 #include <sys/proc.h>
   45 #include <sys/sched.h>
   46 #include <sys/sf_buf.h>
   47 #include <sys/stat.h>
   48 #include <sys/systm.h>
   49 #include <sys/sysctl.h>
   50 #include <sys/unistd.h>
   51 #include <sys/vnode.h>
   52 
   53 #include <vm/vm.h>
   54 #include <vm/vm_object.h>
   55 #include <vm/vm_page.h>
   56 #include <vm/vm_pager.h>
   57 
   58 #include <machine/_inttypes.h>
   59 
   60 #include <fs/fifofs/fifo.h>
   61 #include <fs/tmpfs/tmpfs_vnops.h>
   62 #include <fs/tmpfs/tmpfs.h>
   63 
   64 SYSCTL_DECL(_vfs_tmpfs);
   65 
   66 static volatile int tmpfs_rename_restarts;
   67 SYSCTL_INT(_vfs_tmpfs, OID_AUTO, rename_restarts, CTLFLAG_RD,
   68     __DEVOLATILE(int *, &tmpfs_rename_restarts), 0,
   69     "Times rename had to restart due to lock contention");
   70 
   71 /* --------------------------------------------------------------------- */
   72 
   73 static int
   74 tmpfs_lookup(struct vop_cachedlookup_args *v)
   75 {
   76         struct vnode *dvp = v->a_dvp;
   77         struct vnode **vpp = v->a_vpp;
   78         struct componentname *cnp = v->a_cnp;
   79 
   80         int error;
   81         struct tmpfs_dirent *de;
   82         struct tmpfs_node *dnode;
   83 
   84         dnode = VP_TO_TMPFS_DIR(dvp);
   85         *vpp = NULLVP;
   86 
   87         /* Check accessibility of requested node as a first step. */
   88         error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_thread);
   89         if (error != 0)
   90                 goto out;
   91 
   92         /* We cannot be requesting the parent directory of the root node. */
   93         MPASS(IMPLIES(dnode->tn_type == VDIR &&
   94             dnode->tn_dir.tn_parent == dnode,
   95             !(cnp->cn_flags & ISDOTDOT)));
   96 
   97         TMPFS_ASSERT_LOCKED(dnode);
   98         if (dnode->tn_dir.tn_parent == NULL) {
   99                 error = ENOENT;
  100                 goto out;
  101         }
  102         if (cnp->cn_flags & ISDOTDOT) {
  103                 int ltype = 0;
  104 
  105                 ltype = VOP_ISLOCKED(dvp);
  106                 vhold(dvp);
  107                 VOP_UNLOCK(dvp, 0);
  108                 /* Allocate a new vnode on the matching entry. */
  109                 error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_dir.tn_parent,
  110                     cnp->cn_lkflags, vpp);
  111 
  112                 vn_lock(dvp, ltype | LK_RETRY);
  113                 vdrop(dvp);
  114         } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
  115                 VREF(dvp);
  116                 *vpp = dvp;
  117                 error = 0;
  118         } else {
  119                 de = tmpfs_dir_lookup(dnode, NULL, cnp);
  120                 if (de != NULL && de->td_node == NULL)
  121                         cnp->cn_flags |= ISWHITEOUT;
  122                 if (de == NULL || de->td_node == NULL) {
  123                         /* The entry was not found in the directory.
  124                          * This is OK if we are creating or renaming an
  125                          * entry and are working on the last component of
  126                          * the path name. */
  127                         if ((cnp->cn_flags & ISLASTCN) &&
  128                             (cnp->cn_nameiop == CREATE || \
  129                             cnp->cn_nameiop == RENAME ||
  130                             (cnp->cn_nameiop == DELETE &&
  131                             cnp->cn_flags & DOWHITEOUT &&
  132                             cnp->cn_flags & ISWHITEOUT))) {
  133                                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
  134                                     cnp->cn_thread);
  135                                 if (error != 0)
  136                                         goto out;
  137 
  138                                 /* Keep the component name in the buffer for
  139                                  * future uses. */
  140                                 cnp->cn_flags |= SAVENAME;
  141 
  142                                 error = EJUSTRETURN;
  143                         } else
  144                                 error = ENOENT;
  145                 } else {
  146                         struct tmpfs_node *tnode;
  147 
  148                         /* The entry was found, so get its associated
  149                          * tmpfs_node. */
  150                         tnode = de->td_node;
  151 
  152                         /* If we are not at the last path component and
  153                          * found a non-directory or non-link entry (which
  154                          * may itself be pointing to a directory), raise
  155                          * an error. */
  156                         if ((tnode->tn_type != VDIR &&
  157                             tnode->tn_type != VLNK) &&
  158                             !(cnp->cn_flags & ISLASTCN)) {
  159                                 error = ENOTDIR;
  160                                 goto out;
  161                         }
  162 
  163                         /* If we are deleting or renaming the entry, keep
  164                          * track of its tmpfs_dirent so that it can be
  165                          * easily deleted later. */
  166                         if ((cnp->cn_flags & ISLASTCN) &&
  167                             (cnp->cn_nameiop == DELETE ||
  168                             cnp->cn_nameiop == RENAME)) {
  169                                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
  170                                     cnp->cn_thread);
  171                                 if (error != 0)
  172                                         goto out;
  173 
  174                                 /* Allocate a new vnode on the matching entry. */
  175                                 error = tmpfs_alloc_vp(dvp->v_mount, tnode,
  176                                                 cnp->cn_lkflags, vpp);
  177                                 if (error != 0)
  178                                         goto out;
  179 
  180                                 if ((dnode->tn_mode & S_ISTXT) &&
  181                                   VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, cnp->cn_thread) &&
  182                                   VOP_ACCESS(*vpp, VADMIN, cnp->cn_cred, cnp->cn_thread)) {
  183                                         error = EPERM;
  184                                         vput(*vpp);
  185                                         *vpp = NULL;
  186                                         goto out;
  187                                 }
  188                                 cnp->cn_flags |= SAVENAME;
  189                         } else {
  190                                 error = tmpfs_alloc_vp(dvp->v_mount, tnode,
  191                                                 cnp->cn_lkflags, vpp);
  192                         }
  193                 }
  194         }
  195 
  196         /* Store the result of this lookup in the cache.  Avoid this if the
  197          * request was for creation, as it does not improve timings on
  198          * emprical tests. */
  199         if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE)
  200                 cache_enter(dvp, *vpp, cnp);
  201 
  202 out:
  203         /* If there were no errors, *vpp cannot be null and it must be
  204          * locked. */
  205         MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp)));
  206 
  207         return error;
  208 }
  209 
  210 /* --------------------------------------------------------------------- */
  211 
  212 static int
  213 tmpfs_create(struct vop_create_args *v)
  214 {
  215         struct vnode *dvp = v->a_dvp;
  216         struct vnode **vpp = v->a_vpp;
  217         struct componentname *cnp = v->a_cnp;
  218         struct vattr *vap = v->a_vap;
  219 
  220         MPASS(vap->va_type == VREG || vap->va_type == VSOCK);
  221 
  222         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
  223 }
  224 /* --------------------------------------------------------------------- */
  225 
  226 static int
  227 tmpfs_mknod(struct vop_mknod_args *v)
  228 {
  229         struct vnode *dvp = v->a_dvp;
  230         struct vnode **vpp = v->a_vpp;
  231         struct componentname *cnp = v->a_cnp;
  232         struct vattr *vap = v->a_vap;
  233 
  234         if (vap->va_type != VBLK && vap->va_type != VCHR &&
  235             vap->va_type != VFIFO)
  236                 return EINVAL;
  237 
  238         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
  239 }
  240 
  241 /* --------------------------------------------------------------------- */
  242 
  243 static int
  244 tmpfs_open(struct vop_open_args *v)
  245 {
  246         struct vnode *vp = v->a_vp;
  247         int mode = v->a_mode;
  248 
  249         int error;
  250         struct tmpfs_node *node;
  251 
  252         MPASS(VOP_ISLOCKED(vp));
  253 
  254         node = VP_TO_TMPFS_NODE(vp);
  255 
  256         /* The file is still active but all its names have been removed
  257          * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
  258          * it is about to die. */
  259         if (node->tn_links < 1)
  260                 return (ENOENT);
  261 
  262         /* If the file is marked append-only, deny write requests. */
  263         if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
  264                 error = EPERM;
  265         else {
  266                 error = 0;
  267                 vnode_create_vobject(vp, node->tn_size, v->a_td);
  268         }
  269 
  270         MPASS(VOP_ISLOCKED(vp));
  271         return error;
  272 }
  273 
  274 /* --------------------------------------------------------------------- */
  275 
  276 static int
  277 tmpfs_close(struct vop_close_args *v)
  278 {
  279         struct vnode *vp = v->a_vp;
  280 
  281         MPASS(VOP_ISLOCKED(vp));
  282 
  283         /* Update node times. */
  284         tmpfs_update(vp);
  285 
  286         return (0);
  287 }
  288 
  289 /* --------------------------------------------------------------------- */
  290 
  291 int
  292 tmpfs_access(struct vop_access_args *v)
  293 {
  294         struct vnode *vp = v->a_vp;
  295         accmode_t accmode = v->a_accmode;
  296         struct ucred *cred = v->a_cred;
  297 
  298         int error;
  299         struct tmpfs_node *node;
  300 
  301         MPASS(VOP_ISLOCKED(vp));
  302 
  303         node = VP_TO_TMPFS_NODE(vp);
  304 
  305         switch (vp->v_type) {
  306         case VDIR:
  307                 /* FALLTHROUGH */
  308         case VLNK:
  309                 /* FALLTHROUGH */
  310         case VREG:
  311                 if (accmode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
  312                         error = EROFS;
  313                         goto out;
  314                 }
  315                 break;
  316 
  317         case VBLK:
  318                 /* FALLTHROUGH */
  319         case VCHR:
  320                 /* FALLTHROUGH */
  321         case VSOCK:
  322                 /* FALLTHROUGH */
  323         case VFIFO:
  324                 break;
  325 
  326         default:
  327                 error = EINVAL;
  328                 goto out;
  329         }
  330 
  331         if (accmode & VWRITE && node->tn_flags & IMMUTABLE) {
  332                 error = EPERM;
  333                 goto out;
  334         }
  335 
  336         error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
  337             node->tn_gid, accmode, cred, NULL);
  338 
  339 out:
  340         MPASS(VOP_ISLOCKED(vp));
  341 
  342         return error;
  343 }
  344 
  345 /* --------------------------------------------------------------------- */
  346 
  347 int
  348 tmpfs_getattr(struct vop_getattr_args *v)
  349 {
  350         struct vnode *vp = v->a_vp;
  351         struct vattr *vap = v->a_vap;
  352 
  353         struct tmpfs_node *node;
  354 
  355         node = VP_TO_TMPFS_NODE(vp);
  356 
  357         tmpfs_update(vp);
  358 
  359         vap->va_type = vp->v_type;
  360         vap->va_mode = node->tn_mode;
  361         vap->va_nlink = node->tn_links;
  362         vap->va_uid = node->tn_uid;
  363         vap->va_gid = node->tn_gid;
  364         vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
  365         vap->va_fileid = node->tn_id;
  366         vap->va_size = node->tn_size;
  367         vap->va_blocksize = PAGE_SIZE;
  368         vap->va_atime = node->tn_atime;
  369         vap->va_mtime = node->tn_mtime;
  370         vap->va_ctime = node->tn_ctime;
  371         vap->va_birthtime = node->tn_birthtime;
  372         vap->va_gen = node->tn_gen;
  373         vap->va_flags = node->tn_flags;
  374         vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
  375                 node->tn_rdev : NODEV;
  376         vap->va_bytes = round_page(node->tn_size);
  377         vap->va_filerev = 0;
  378 
  379         return 0;
  380 }
  381 
  382 /* --------------------------------------------------------------------- */
  383 
  384 /* XXX Should this operation be atomic?  I think it should, but code in
  385  * XXX other places (e.g., ufs) doesn't seem to be... */
  386 int
  387 tmpfs_setattr(struct vop_setattr_args *v)
  388 {
  389         struct vnode *vp = v->a_vp;
  390         struct vattr *vap = v->a_vap;
  391         struct ucred *cred = v->a_cred;
  392         struct thread *td = curthread;
  393 
  394         int error;
  395 
  396         MPASS(VOP_ISLOCKED(vp));
  397 
  398         error = 0;
  399 
  400         /* Abort if any unsettable attribute is given. */
  401         if (vap->va_type != VNON ||
  402             vap->va_nlink != VNOVAL ||
  403             vap->va_fsid != VNOVAL ||
  404             vap->va_fileid != VNOVAL ||
  405             vap->va_blocksize != VNOVAL ||
  406             vap->va_gen != VNOVAL ||
  407             vap->va_rdev != VNOVAL ||
  408             vap->va_bytes != VNOVAL)
  409                 error = EINVAL;
  410 
  411         if (error == 0 && (vap->va_flags != VNOVAL))
  412                 error = tmpfs_chflags(vp, vap->va_flags, cred, td);
  413 
  414         if (error == 0 && (vap->va_size != VNOVAL))
  415                 error = tmpfs_chsize(vp, vap->va_size, cred, td);
  416 
  417         if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
  418                 error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, td);
  419 
  420         if (error == 0 && (vap->va_mode != (mode_t)VNOVAL))
  421                 error = tmpfs_chmod(vp, vap->va_mode, cred, td);
  422 
  423         if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
  424             vap->va_atime.tv_nsec != VNOVAL) ||
  425             (vap->va_mtime.tv_sec != VNOVAL &&
  426             vap->va_mtime.tv_nsec != VNOVAL) ||
  427             (vap->va_birthtime.tv_sec != VNOVAL &&
  428             vap->va_birthtime.tv_nsec != VNOVAL)))
  429                 error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
  430                         &vap->va_birthtime, vap->va_vaflags, cred, td);
  431 
  432         /* Update the node times.  We give preference to the error codes
  433          * generated by this function rather than the ones that may arise
  434          * from tmpfs_update. */
  435         tmpfs_update(vp);
  436 
  437         MPASS(VOP_ISLOCKED(vp));
  438 
  439         return error;
  440 }
  441 
  442 /* --------------------------------------------------------------------- */
  443 static int
  444 tmpfs_nocacheread(vm_object_t tobj, vm_pindex_t idx,
  445     vm_offset_t offset, size_t tlen, struct uio *uio)
  446 {
  447         vm_page_t       m;
  448         int             error;
  449 
  450         VM_OBJECT_LOCK(tobj);
  451         vm_object_pip_add(tobj, 1);
  452         m = vm_page_grab(tobj, idx, VM_ALLOC_WIRED |
  453             VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
  454         if (m->valid != VM_PAGE_BITS_ALL) {
  455                 if (vm_pager_has_page(tobj, idx, NULL, NULL)) {
  456                         error = vm_pager_get_pages(tobj, &m, 1, 0);
  457                         if (error != 0) {
  458                                 printf("tmpfs get pages from pager error [read]\n");
  459                                 goto out;
  460                         }
  461                 } else
  462                         vm_page_zero_invalid(m, TRUE);
  463         }
  464         VM_OBJECT_UNLOCK(tobj);
  465         error = uiomove_fromphys(&m, offset, tlen, uio);
  466         VM_OBJECT_LOCK(tobj);
  467 out:
  468         vm_page_lock_queues();
  469         vm_page_unwire(m, TRUE);
  470         vm_page_unlock_queues();
  471         vm_page_wakeup(m);
  472         vm_object_pip_subtract(tobj, 1);
  473         VM_OBJECT_UNLOCK(tobj);
  474 
  475         return (error);
  476 }
  477 
  478 static __inline int
  479 tmpfs_nocacheread_buf(vm_object_t tobj, vm_pindex_t idx,
  480     vm_offset_t offset, size_t tlen, void *buf)
  481 {
  482         struct uio uio;
  483         struct iovec iov;
  484 
  485         uio.uio_iovcnt = 1;
  486         uio.uio_iov = &iov;
  487         iov.iov_base = buf;
  488         iov.iov_len = tlen;
  489 
  490         uio.uio_offset = 0;
  491         uio.uio_resid = tlen;
  492         uio.uio_rw = UIO_READ;
  493         uio.uio_segflg = UIO_SYSSPACE;
  494         uio.uio_td = curthread;
  495 
  496         return (tmpfs_nocacheread(tobj, idx, offset, tlen, &uio));
  497 }
  498 
  499 static int
  500 tmpfs_mappedread(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio)
  501 {
  502         struct sf_buf   *sf;
  503         vm_pindex_t     idx;
  504         vm_page_t       m;
  505         vm_offset_t     offset;
  506         off_t           addr;
  507         size_t          tlen;
  508         char            *ma;
  509         int             error;
  510 
  511         addr = uio->uio_offset;
  512         idx = OFF_TO_IDX(addr);
  513         offset = addr & PAGE_MASK;
  514         tlen = MIN(PAGE_SIZE - offset, len);
  515 
  516         if ((vobj == NULL) || (vobj->resident_page_count == 0))
  517                 goto nocache;
  518 
  519         VM_OBJECT_LOCK(vobj);
  520 lookupvpg:
  521         if (((m = vm_page_lookup(vobj, idx)) != NULL) &&
  522             vm_page_is_valid(m, offset, tlen)) {
  523                 if (vm_page_sleep_if_busy(m, FALSE, "tmfsmr"))
  524                         goto lookupvpg;
  525                 vm_page_busy(m);
  526                 VM_OBJECT_UNLOCK(vobj);
  527                 error = uiomove_fromphys(&m, offset, tlen, uio);
  528                 VM_OBJECT_LOCK(vobj);
  529                 vm_page_wakeup(m);
  530                 VM_OBJECT_UNLOCK(vobj);
  531                 return  (error);
  532         } else if (m != NULL && uio->uio_segflg == UIO_NOCOPY) {
  533                 KASSERT(offset == 0,
  534                     ("unexpected offset in tmpfs_mappedread for sendfile"));
  535                 if (vm_page_sleep_if_busy(m, FALSE, "tmfsmr"))
  536                         goto lookupvpg;
  537                 vm_page_busy(m);
  538                 VM_OBJECT_UNLOCK(vobj);
  539                 sched_pin();
  540                 sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
  541                 ma = (char *)sf_buf_kva(sf);
  542                 error = tmpfs_nocacheread_buf(tobj, idx, 0, tlen, ma);
  543                 if (error == 0) {
  544                         if (tlen != PAGE_SIZE)
  545                                 bzero(ma + tlen, PAGE_SIZE - tlen);
  546                         uio->uio_offset += tlen;
  547                         uio->uio_resid -= tlen;
  548                 }
  549                 sf_buf_free(sf);
  550                 sched_unpin();
  551                 VM_OBJECT_LOCK(vobj);
  552                 if (error == 0)
  553                         m->valid = VM_PAGE_BITS_ALL;
  554                 vm_page_wakeup(m);
  555                 VM_OBJECT_UNLOCK(vobj);
  556                 return  (error);
  557         }
  558         VM_OBJECT_UNLOCK(vobj);
  559 nocache:
  560         error = tmpfs_nocacheread(tobj, idx, offset, tlen, uio);
  561 
  562         return  (error);
  563 }
  564 
  565 static int
  566 tmpfs_read(struct vop_read_args *v)
  567 {
  568         struct vnode *vp = v->a_vp;
  569         struct uio *uio = v->a_uio;
  570 
  571         struct tmpfs_node *node;
  572         vm_object_t uobj;
  573         size_t len;
  574         int resid;
  575 
  576         int error = 0;
  577 
  578         node = VP_TO_TMPFS_NODE(vp);
  579 
  580         if (vp->v_type != VREG) {
  581                 error = EISDIR;
  582                 goto out;
  583         }
  584 
  585         if (uio->uio_offset < 0) {
  586                 error = EINVAL;
  587                 goto out;
  588         }
  589 
  590         node->tn_status |= TMPFS_NODE_ACCESSED;
  591 
  592         uobj = node->tn_reg.tn_aobj;
  593         while ((resid = uio->uio_resid) > 0) {
  594                 error = 0;
  595                 if (node->tn_size <= uio->uio_offset)
  596                         break;
  597                 len = MIN(node->tn_size - uio->uio_offset, resid);
  598                 if (len == 0)
  599                         break;
  600                 error = tmpfs_mappedread(vp->v_object, uobj, len, uio);
  601                 if ((error != 0) || (resid == uio->uio_resid))
  602                         break;
  603         }
  604 
  605 out:
  606 
  607         return error;
  608 }
  609 
  610 /* --------------------------------------------------------------------- */
  611 
  612 static int
  613 tmpfs_mappedwrite(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio)
  614 {
  615         vm_pindex_t     idx;
  616         vm_page_t       vpg, tpg;
  617         vm_offset_t     offset;
  618         off_t           addr;
  619         size_t          tlen;
  620         int             error;
  621 
  622         error = 0;
  623         
  624         addr = uio->uio_offset;
  625         idx = OFF_TO_IDX(addr);
  626         offset = addr & PAGE_MASK;
  627         tlen = MIN(PAGE_SIZE - offset, len);
  628 
  629         if ((vobj == NULL) || (vobj->resident_page_count == 0)) {
  630                 vpg = NULL;
  631                 goto nocache;
  632         }
  633 
  634         VM_OBJECT_LOCK(vobj);
  635 lookupvpg:
  636         if (((vpg = vm_page_lookup(vobj, idx)) != NULL) &&
  637             vm_page_is_valid(vpg, offset, tlen)) {
  638                 if (vm_page_sleep_if_busy(vpg, FALSE, "tmfsmw"))
  639                         goto lookupvpg;
  640                 vm_page_busy(vpg);
  641                 vm_page_lock_queues();
  642                 vm_page_undirty(vpg);
  643                 vm_page_unlock_queues();
  644                 VM_OBJECT_UNLOCK(vobj);
  645                 error = uiomove_fromphys(&vpg, offset, tlen, uio);
  646         } else {
  647                 VM_OBJECT_UNLOCK(vobj);
  648                 vpg = NULL;
  649         }
  650 nocache:
  651         VM_OBJECT_LOCK(tobj);
  652         vm_object_pip_add(tobj, 1);
  653         tpg = vm_page_grab(tobj, idx, VM_ALLOC_WIRED |
  654             VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
  655         if (tpg->valid != VM_PAGE_BITS_ALL) {
  656                 if (vm_pager_has_page(tobj, idx, NULL, NULL)) {
  657                         error = vm_pager_get_pages(tobj, &tpg, 1, 0);
  658                         if (error != 0) {
  659                                 printf("tmpfs get pages from pager error [write]\n");
  660                                 goto out;
  661                         }
  662                 } else
  663                         vm_page_zero_invalid(tpg, TRUE);
  664         }
  665         VM_OBJECT_UNLOCK(tobj);
  666         if (vpg == NULL)
  667                 error = uiomove_fromphys(&tpg, offset, tlen, uio);
  668         else {
  669                 KASSERT(vpg->valid == VM_PAGE_BITS_ALL, ("parts of vpg invalid"));
  670                 pmap_copy_page(vpg, tpg);
  671         }
  672         VM_OBJECT_LOCK(tobj);
  673 out:
  674         if (vobj != NULL)
  675                 VM_OBJECT_LOCK(vobj);
  676         vm_page_lock_queues();
  677         if (error == 0) {
  678                 KASSERT(tpg->valid == VM_PAGE_BITS_ALL,
  679                     ("parts of tpg invalid"));
  680                 vm_page_dirty(tpg);
  681         }
  682         vm_page_unwire(tpg, TRUE);
  683         vm_page_unlock_queues();
  684         vm_page_wakeup(tpg);
  685         if (vpg != NULL)
  686                 vm_page_wakeup(vpg);
  687         if (vobj != NULL)
  688                 VM_OBJECT_UNLOCK(vobj);
  689         vm_object_pip_subtract(tobj, 1);
  690         VM_OBJECT_UNLOCK(tobj);
  691 
  692         return  (error);
  693 }
  694 
  695 static int
  696 tmpfs_write(struct vop_write_args *v)
  697 {
  698         struct vnode *vp = v->a_vp;
  699         struct uio *uio = v->a_uio;
  700         int ioflag = v->a_ioflag;
  701         struct thread *td = uio->uio_td;
  702 
  703         boolean_t extended;
  704         int error = 0;
  705         off_t oldsize;
  706         struct tmpfs_node *node;
  707         vm_object_t uobj;
  708         size_t len;
  709         int resid;
  710 
  711         node = VP_TO_TMPFS_NODE(vp);
  712         oldsize = node->tn_size;
  713 
  714         if (uio->uio_offset < 0 || vp->v_type != VREG) {
  715                 error = EINVAL;
  716                 goto out;
  717         }
  718 
  719         if (uio->uio_resid == 0) {
  720                 error = 0;
  721                 goto out;
  722         }
  723 
  724         if (ioflag & IO_APPEND)
  725                 uio->uio_offset = node->tn_size;
  726 
  727         if (uio->uio_offset + uio->uio_resid >
  728           VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
  729                 return (EFBIG);
  730 
  731         if (vn_rlimit_fsize(vp, uio, td))
  732                 return (EFBIG);
  733 
  734         extended = uio->uio_offset + uio->uio_resid > node->tn_size;
  735         if (extended) {
  736                 error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
  737                 if (error != 0)
  738                         goto out;
  739         }
  740 
  741         uobj = node->tn_reg.tn_aobj;
  742         while ((resid = uio->uio_resid) > 0) {
  743                 if (node->tn_size <= uio->uio_offset)
  744                         break;
  745                 len = MIN(node->tn_size - uio->uio_offset, resid);
  746                 if (len == 0)
  747                         break;
  748                 error = tmpfs_mappedwrite(vp->v_object, uobj, len, uio);
  749                 if ((error != 0) || (resid == uio->uio_resid))
  750                         break;
  751         }
  752 
  753         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
  754             (extended ? TMPFS_NODE_CHANGED : 0);
  755 
  756         if (node->tn_mode & (S_ISUID | S_ISGID)) {
  757                 if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0))
  758                         node->tn_mode &= ~(S_ISUID | S_ISGID);
  759         }
  760 
  761         if (error != 0)
  762                 (void)tmpfs_reg_resize(vp, oldsize);
  763 
  764 out:
  765         MPASS(IMPLIES(error == 0, uio->uio_resid == 0));
  766         MPASS(IMPLIES(error != 0, oldsize == node->tn_size));
  767 
  768         return error;
  769 }
  770 
  771 /* --------------------------------------------------------------------- */
  772 
  773 static int
  774 tmpfs_fsync(struct vop_fsync_args *v)
  775 {
  776         struct vnode *vp = v->a_vp;
  777 
  778         MPASS(VOP_ISLOCKED(vp));
  779 
  780         tmpfs_update(vp);
  781 
  782         return 0;
  783 }
  784 
  785 /* --------------------------------------------------------------------- */
  786 
  787 static int
  788 tmpfs_remove(struct vop_remove_args *v)
  789 {
  790         struct vnode *dvp = v->a_dvp;
  791         struct vnode *vp = v->a_vp;
  792 
  793         int error;
  794         struct tmpfs_dirent *de;
  795         struct tmpfs_mount *tmp;
  796         struct tmpfs_node *dnode;
  797         struct tmpfs_node *node;
  798 
  799         MPASS(VOP_ISLOCKED(dvp));
  800         MPASS(VOP_ISLOCKED(vp));
  801 
  802         if (vp->v_type == VDIR) {
  803                 error = EISDIR;
  804                 goto out;
  805         }
  806 
  807         dnode = VP_TO_TMPFS_DIR(dvp);
  808         node = VP_TO_TMPFS_NODE(vp);
  809         tmp = VFS_TO_TMPFS(vp->v_mount);
  810         de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
  811         MPASS(de != NULL);
  812 
  813         /* Files marked as immutable or append-only cannot be deleted. */
  814         if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
  815             (dnode->tn_flags & APPEND)) {
  816                 error = EPERM;
  817                 goto out;
  818         }
  819 
  820         /* Remove the entry from the directory; as it is a file, we do not
  821          * have to change the number of hard links of the directory. */
  822         tmpfs_dir_detach(dvp, de);
  823         if (v->a_cnp->cn_flags & DOWHITEOUT)
  824                 tmpfs_dir_whiteout_add(dvp, v->a_cnp);
  825 
  826         /* Free the directory entry we just deleted.  Note that the node
  827          * referred by it will not be removed until the vnode is really
  828          * reclaimed. */
  829         tmpfs_free_dirent(tmp, de, TRUE);
  830 
  831         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED;
  832         error = 0;
  833 
  834 out:
  835 
  836         return error;
  837 }
  838 
  839 /* --------------------------------------------------------------------- */
  840 
  841 static int
  842 tmpfs_link(struct vop_link_args *v)
  843 {
  844         struct vnode *dvp = v->a_tdvp;
  845         struct vnode *vp = v->a_vp;
  846         struct componentname *cnp = v->a_cnp;
  847 
  848         int error;
  849         struct tmpfs_dirent *de;
  850         struct tmpfs_node *node;
  851 
  852         MPASS(VOP_ISLOCKED(dvp));
  853         MPASS(cnp->cn_flags & HASBUF);
  854         MPASS(dvp != vp); /* XXX When can this be false? */
  855 
  856         node = VP_TO_TMPFS_NODE(vp);
  857 
  858         /* XXX: Why aren't the following two tests done by the caller? */
  859 
  860         /* Hard links of directories are forbidden. */
  861         if (vp->v_type == VDIR) {
  862                 error = EPERM;
  863                 goto out;
  864         }
  865 
  866         /* Cannot create cross-device links. */
  867         if (dvp->v_mount != vp->v_mount) {
  868                 error = EXDEV;
  869                 goto out;
  870         }
  871 
  872         /* Ensure that we do not overflow the maximum number of links imposed
  873          * by the system. */
  874         MPASS(node->tn_links <= LINK_MAX);
  875         if (node->tn_links == LINK_MAX) {
  876                 error = EMLINK;
  877                 goto out;
  878         }
  879 
  880         /* We cannot create links of files marked immutable or append-only. */
  881         if (node->tn_flags & (IMMUTABLE | APPEND)) {
  882                 error = EPERM;
  883                 goto out;
  884         }
  885 
  886         /* Allocate a new directory entry to represent the node. */
  887         error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
  888             cnp->cn_nameptr, cnp->cn_namelen, &de);
  889         if (error != 0)
  890                 goto out;
  891 
  892         /* Insert the new directory entry into the appropriate directory. */
  893         if (cnp->cn_flags & ISWHITEOUT)
  894                 tmpfs_dir_whiteout_remove(dvp, cnp);
  895         tmpfs_dir_attach(dvp, de);
  896 
  897         /* vp link count has changed, so update node times. */
  898         node->tn_status |= TMPFS_NODE_CHANGED;
  899         tmpfs_update(vp);
  900 
  901         error = 0;
  902 
  903 out:
  904         return error;
  905 }
  906 
  907 /* --------------------------------------------------------------------- */
  908 
  909 /*
  910  * We acquire all but fdvp locks using non-blocking acquisitions.  If we
  911  * fail to acquire any lock in the path we will drop all held locks,
  912  * acquire the new lock in a blocking fashion, and then release it and
  913  * restart the rename.  This acquire/release step ensures that we do not
  914  * spin on a lock waiting for release.  On error release all vnode locks
  915  * and decrement references the way tmpfs_rename() would do.
  916  */
  917 static int
  918 tmpfs_rename_relock(struct vnode *fdvp, struct vnode **fvpp,
  919     struct vnode *tdvp, struct vnode **tvpp,
  920     struct componentname *fcnp, struct componentname *tcnp)
  921 {
  922         struct vnode *nvp;
  923         struct mount *mp;
  924         struct tmpfs_dirent *de;
  925         int error, restarts = 0;
  926 
  927         VOP_UNLOCK(tdvp, 0);
  928         if (*tvpp != NULL && *tvpp != tdvp)
  929                 VOP_UNLOCK(*tvpp, 0);
  930         mp = fdvp->v_mount;
  931 
  932 relock:
  933         restarts += 1;
  934         error = vn_lock(fdvp, LK_EXCLUSIVE);
  935         if (error)
  936                 goto releout;
  937         if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
  938                 VOP_UNLOCK(fdvp, 0);
  939                 error = vn_lock(tdvp, LK_EXCLUSIVE);
  940                 if (error)
  941                         goto releout;
  942                 VOP_UNLOCK(tdvp, 0);
  943                 goto relock;
  944         }
  945         /*
  946          * Re-resolve fvp to be certain it still exists and fetch the
  947          * correct vnode.
  948          */
  949         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(fdvp), NULL, fcnp);
  950         if (de == NULL) {
  951                 VOP_UNLOCK(fdvp, 0);
  952                 VOP_UNLOCK(tdvp, 0);
  953                 if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
  954                     (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
  955                         error = EINVAL;
  956                 else
  957                         error = ENOENT;
  958                 goto releout;
  959         }
  960         error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE | LK_NOWAIT, &nvp);
  961         if (error != 0) {
  962                 VOP_UNLOCK(fdvp, 0);
  963                 VOP_UNLOCK(tdvp, 0);
  964                 if (error != EBUSY)
  965                         goto releout;
  966                 error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE, &nvp);
  967                 if (error != 0)
  968                         goto releout;
  969                 VOP_UNLOCK(nvp, 0);
  970                 /*
  971                  * Concurrent rename race.
  972                  */
  973                 if (nvp == tdvp) {
  974                         vrele(nvp);
  975                         error = EINVAL;
  976                         goto releout;
  977                 }
  978                 vrele(*fvpp);
  979                 *fvpp = nvp;
  980                 goto relock;
  981         }
  982         vrele(*fvpp);
  983         *fvpp = nvp;
  984         VOP_UNLOCK(*fvpp, 0);
  985         /*
  986          * Re-resolve tvp and acquire the vnode lock if present.
  987          */
  988         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(tdvp), NULL, tcnp);
  989         /*
  990          * If tvp disappeared we just carry on.
  991          */
  992         if (de == NULL && *tvpp != NULL) {
  993                 vrele(*tvpp);
  994                 *tvpp = NULL;
  995         }
  996         /*
  997          * Get the tvp ino if the lookup succeeded.  We may have to restart
  998          * if the non-blocking acquire fails.
  999          */
 1000         if (de != NULL) {
 1001                 nvp = NULL;
 1002                 error = tmpfs_alloc_vp(mp, de->td_node,
 1003                     LK_EXCLUSIVE | LK_NOWAIT, &nvp);
 1004                 if (*tvpp != NULL)
 1005                         vrele(*tvpp);
 1006                 *tvpp = nvp;
 1007                 if (error != 0) {
 1008                         VOP_UNLOCK(fdvp, 0);
 1009                         VOP_UNLOCK(tdvp, 0);
 1010                         if (error != EBUSY)
 1011                                 goto releout;
 1012                         error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE,
 1013                             &nvp);
 1014                         if (error != 0)
 1015                                 goto releout;
 1016                         VOP_UNLOCK(nvp, 0);
 1017                         /*
 1018                          * fdvp contains fvp, thus tvp (=fdvp) is not empty.
 1019                          */
 1020                         if (nvp == fdvp) {
 1021                                 error = ENOTEMPTY;
 1022                                 goto releout;
 1023                         }
 1024                         goto relock;
 1025                 }
 1026         }
 1027         tmpfs_rename_restarts += restarts;
 1028 
 1029         return (0);
 1030 
 1031 releout:
 1032         vrele(fdvp);
 1033         vrele(*fvpp);
 1034         vrele(tdvp);
 1035         if (*tvpp != NULL)
 1036                 vrele(*tvpp);
 1037         tmpfs_rename_restarts += restarts;
 1038 
 1039         return (error);
 1040 }
 1041 
 1042 static int
 1043 tmpfs_rename(struct vop_rename_args *v)
 1044 {
 1045         struct vnode *fdvp = v->a_fdvp;
 1046         struct vnode *fvp = v->a_fvp;
 1047         struct componentname *fcnp = v->a_fcnp;
 1048         struct vnode *tdvp = v->a_tdvp;
 1049         struct vnode *tvp = v->a_tvp;
 1050         struct componentname *tcnp = v->a_tcnp;
 1051         struct mount *mp = NULL;
 1052 
 1053         char *newname;
 1054         int error;
 1055         struct tmpfs_dirent *de;
 1056         struct tmpfs_mount *tmp;
 1057         struct tmpfs_node *fdnode;
 1058         struct tmpfs_node *fnode;
 1059         struct tmpfs_node *tnode;
 1060         struct tmpfs_node *tdnode;
 1061 
 1062         MPASS(VOP_ISLOCKED(tdvp));
 1063         MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp)));
 1064         MPASS(fcnp->cn_flags & HASBUF);
 1065         MPASS(tcnp->cn_flags & HASBUF);
 1066 
 1067         /* Disallow cross-device renames.
 1068          * XXX Why isn't this done by the caller? */
 1069         if (fvp->v_mount != tdvp->v_mount ||
 1070             (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
 1071                 error = EXDEV;
 1072                 goto out;
 1073         }
 1074 
 1075         /* If source and target are the same file, there is nothing to do. */
 1076         if (fvp == tvp) {
 1077                 error = 0;
 1078                 goto out;
 1079         }
 1080 
 1081         /* If we need to move the directory between entries, lock the
 1082          * source so that we can safely operate on it. */
 1083         if (fdvp != tdvp && fdvp != tvp) {
 1084                 if (vn_lock(fdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
 1085                         mp = tdvp->v_mount;
 1086                         error = vfs_busy(mp, 0);
 1087                         if (error != 0) {
 1088                                 mp = NULL;
 1089                                 goto out;
 1090                         }
 1091                         error = tmpfs_rename_relock(fdvp, &fvp, tdvp, &tvp,
 1092                             fcnp, tcnp);
 1093                         if (error != 0) {
 1094                                 vfs_unbusy(mp);
 1095                                 return (error);
 1096                         }
 1097                         ASSERT_VOP_ELOCKED(fdvp,
 1098                             "tmpfs_rename: fdvp not locked");
 1099                         ASSERT_VOP_ELOCKED(tdvp,
 1100                             "tmpfs_rename: tdvp not locked");
 1101                         if (tvp != NULL)
 1102                                 ASSERT_VOP_ELOCKED(tvp,
 1103                                     "tmpfs_rename: tvp not locked");
 1104                         if (fvp == tvp) {
 1105                                 error = 0;
 1106                                 goto out_locked;
 1107                         }
 1108                 }
 1109         }
 1110 
 1111         tmp = VFS_TO_TMPFS(tdvp->v_mount);
 1112         tdnode = VP_TO_TMPFS_DIR(tdvp);
 1113         tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
 1114         fdnode = VP_TO_TMPFS_DIR(fdvp);
 1115         fnode = VP_TO_TMPFS_NODE(fvp);
 1116         de = tmpfs_dir_lookup(fdnode, fnode, fcnp);
 1117 
 1118         /* Entry can disappear before we lock fdvp,
 1119          * also avoid manipulating '.' and '..' entries. */
 1120         if (de == NULL) {
 1121                 if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
 1122                     (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
 1123                         error = EINVAL;
 1124                 else
 1125                         error = ENOENT;
 1126                 goto out_locked;
 1127         }
 1128         MPASS(de->td_node == fnode);
 1129 
 1130         /* If re-naming a directory to another preexisting directory
 1131          * ensure that the target directory is empty so that its
 1132          * removal causes no side effects.
 1133          * Kern_rename gurantees the destination to be a directory
 1134          * if the source is one. */
 1135         if (tvp != NULL) {
 1136                 MPASS(tnode != NULL);
 1137 
 1138                 if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
 1139                     (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
 1140                         error = EPERM;
 1141                         goto out_locked;
 1142                 }
 1143 
 1144                 if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
 1145                         if (tnode->tn_size > 0) {
 1146                                 error = ENOTEMPTY;
 1147                                 goto out_locked;
 1148                         }
 1149                 } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
 1150                         error = ENOTDIR;
 1151                         goto out_locked;
 1152                 } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
 1153                         error = EISDIR;
 1154                         goto out_locked;
 1155                 } else {
 1156                         MPASS(fnode->tn_type != VDIR &&
 1157                                 tnode->tn_type != VDIR);
 1158                 }
 1159         }
 1160 
 1161         if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))
 1162             || (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
 1163                 error = EPERM;
 1164                 goto out_locked;
 1165         }
 1166 
 1167         /* Ensure that we have enough memory to hold the new name, if it
 1168          * has to be changed. */
 1169         if (fcnp->cn_namelen != tcnp->cn_namelen ||
 1170             bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
 1171                 newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK);
 1172         } else
 1173                 newname = NULL;
 1174 
 1175         /* If the node is being moved to another directory, we have to do
 1176          * the move. */
 1177         if (fdnode != tdnode) {
 1178                 /* In case we are moving a directory, we have to adjust its
 1179                  * parent to point to the new parent. */
 1180                 if (de->td_node->tn_type == VDIR) {
 1181                         struct tmpfs_node *n;
 1182 
 1183                         /* Ensure the target directory is not a child of the
 1184                          * directory being moved.  Otherwise, we'd end up
 1185                          * with stale nodes. */
 1186                         n = tdnode;
 1187                         /* TMPFS_LOCK garanties that no nodes are freed while
 1188                          * traversing the list. Nodes can only be marked as
 1189                          * removed: tn_parent == NULL. */
 1190                         TMPFS_LOCK(tmp);
 1191                         TMPFS_NODE_LOCK(n);
 1192                         while (n != n->tn_dir.tn_parent) {
 1193                                 struct tmpfs_node *parent;
 1194 
 1195                                 if (n == fnode) {
 1196                                         TMPFS_NODE_UNLOCK(n);
 1197                                         TMPFS_UNLOCK(tmp);
 1198                                         error = EINVAL;
 1199                                         if (newname != NULL)
 1200                                                     free(newname, M_TMPFSNAME);
 1201                                         goto out_locked;
 1202                                 }
 1203                                 parent = n->tn_dir.tn_parent;
 1204                                 TMPFS_NODE_UNLOCK(n);
 1205                                 if (parent == NULL) {
 1206                                         n = NULL;
 1207                                         break;
 1208                                 }
 1209                                 TMPFS_NODE_LOCK(parent);
 1210                                 if (parent->tn_dir.tn_parent == NULL) {
 1211                                         TMPFS_NODE_UNLOCK(parent);
 1212                                         n = NULL;
 1213                                         break;
 1214                                 }
 1215                                 n = parent;
 1216                         }
 1217                         TMPFS_UNLOCK(tmp);
 1218                         if (n == NULL) {
 1219                                 error = EINVAL;
 1220                                 if (newname != NULL)
 1221                                             free(newname, M_TMPFSNAME);
 1222                                 goto out_locked;
 1223                         }
 1224                         TMPFS_NODE_UNLOCK(n);
 1225 
 1226                         /* Adjust the parent pointer. */
 1227                         TMPFS_VALIDATE_DIR(fnode);
 1228                         TMPFS_NODE_LOCK(de->td_node);
 1229                         de->td_node->tn_dir.tn_parent = tdnode;
 1230                         TMPFS_NODE_UNLOCK(de->td_node);
 1231 
 1232                         /* As a result of changing the target of the '..'
 1233                          * entry, the link count of the source and target
 1234                          * directories has to be adjusted. */
 1235                         TMPFS_NODE_LOCK(tdnode);
 1236                         TMPFS_ASSERT_LOCKED(tdnode);
 1237                         tdnode->tn_links++;
 1238                         TMPFS_NODE_UNLOCK(tdnode);
 1239 
 1240                         TMPFS_NODE_LOCK(fdnode);
 1241                         TMPFS_ASSERT_LOCKED(fdnode);
 1242                         fdnode->tn_links--;
 1243                         TMPFS_NODE_UNLOCK(fdnode);
 1244                 }
 1245 
 1246                 /* Do the move: just remove the entry from the source directory
 1247                  * and insert it into the target one. */
 1248                 tmpfs_dir_detach(fdvp, de);
 1249                 if (fcnp->cn_flags & DOWHITEOUT)
 1250                         tmpfs_dir_whiteout_add(fdvp, fcnp);
 1251                 if (tcnp->cn_flags & ISWHITEOUT)
 1252                         tmpfs_dir_whiteout_remove(tdvp, tcnp);
 1253                 tmpfs_dir_attach(tdvp, de);
 1254         }
 1255 
 1256         /* If the name has changed, we need to make it effective by changing
 1257          * it in the directory entry. */
 1258         if (newname != NULL) {
 1259                 MPASS(tcnp->cn_namelen <= MAXNAMLEN);
 1260 
 1261                 free(de->td_name, M_TMPFSNAME);
 1262                 de->td_namelen = (uint16_t)tcnp->cn_namelen;
 1263                 memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
 1264                 de->td_name = newname;
 1265 
 1266                 fnode->tn_status |= TMPFS_NODE_CHANGED;
 1267                 tdnode->tn_status |= TMPFS_NODE_MODIFIED;
 1268         }
 1269 
 1270         /* If we are overwriting an entry, we have to remove the old one
 1271          * from the target directory. */
 1272         if (tvp != NULL) {
 1273                 /* Remove the old entry from the target directory. */
 1274                 de = tmpfs_dir_lookup(tdnode, tnode, tcnp);
 1275                 tmpfs_dir_detach(tdvp, de);
 1276 
 1277                 /* Free the directory entry we just deleted.  Note that the
 1278                  * node referred by it will not be removed until the vnode is
 1279                  * really reclaimed. */
 1280                 tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), de, TRUE);
 1281         }
 1282         cache_purge(fvp);
 1283         if (tvp != NULL)
 1284                 cache_purge(tvp);
 1285         cache_purge_negative(tdvp);
 1286 
 1287         error = 0;
 1288 
 1289 out_locked:
 1290         if (fdvp != tdvp && fdvp != tvp)
 1291                 VOP_UNLOCK(fdvp, 0);
 1292 
 1293 out:
 1294         /* Release target nodes. */
 1295         /* XXX: I don't understand when tdvp can be the same as tvp, but
 1296          * other code takes care of this... */
 1297         if (tdvp == tvp)
 1298                 vrele(tdvp);
 1299         else
 1300                 vput(tdvp);
 1301         if (tvp != NULL)
 1302                 vput(tvp);
 1303 
 1304         /* Release source nodes. */
 1305         vrele(fdvp);
 1306         vrele(fvp);
 1307 
 1308         if (mp != NULL)
 1309                 vfs_unbusy(mp);
 1310 
 1311         return error;
 1312 }
 1313 
 1314 /* --------------------------------------------------------------------- */
 1315 
 1316 static int
 1317 tmpfs_mkdir(struct vop_mkdir_args *v)
 1318 {
 1319         struct vnode *dvp = v->a_dvp;
 1320         struct vnode **vpp = v->a_vpp;
 1321         struct componentname *cnp = v->a_cnp;
 1322         struct vattr *vap = v->a_vap;
 1323 
 1324         MPASS(vap->va_type == VDIR);
 1325 
 1326         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
 1327 }
 1328 
 1329 /* --------------------------------------------------------------------- */
 1330 
 1331 static int
 1332 tmpfs_rmdir(struct vop_rmdir_args *v)
 1333 {
 1334         struct vnode *dvp = v->a_dvp;
 1335         struct vnode *vp = v->a_vp;
 1336 
 1337         int error;
 1338         struct tmpfs_dirent *de;
 1339         struct tmpfs_mount *tmp;
 1340         struct tmpfs_node *dnode;
 1341         struct tmpfs_node *node;
 1342 
 1343         MPASS(VOP_ISLOCKED(dvp));
 1344         MPASS(VOP_ISLOCKED(vp));
 1345 
 1346         tmp = VFS_TO_TMPFS(dvp->v_mount);
 1347         dnode = VP_TO_TMPFS_DIR(dvp);
 1348         node = VP_TO_TMPFS_DIR(vp);
 1349 
 1350         /* Directories with more than two entries ('.' and '..') cannot be
 1351          * removed. */
 1352          if (node->tn_size > 0) {
 1353                  error = ENOTEMPTY;
 1354                  goto out;
 1355          }
 1356 
 1357         if ((dnode->tn_flags & APPEND)
 1358             || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
 1359                 error = EPERM;
 1360                 goto out;
 1361         }
 1362 
 1363         /* This invariant holds only if we are not trying to remove "..".
 1364           * We checked for that above so this is safe now. */
 1365         MPASS(node->tn_dir.tn_parent == dnode);
 1366 
 1367         /* Get the directory entry associated with node (vp).  This was
 1368          * filled by tmpfs_lookup while looking up the entry. */
 1369         de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
 1370         MPASS(TMPFS_DIRENT_MATCHES(de,
 1371             v->a_cnp->cn_nameptr,
 1372             v->a_cnp->cn_namelen));
 1373 
 1374         /* Check flags to see if we are allowed to remove the directory. */
 1375         if (dnode->tn_flags & APPEND
 1376                 || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
 1377                 error = EPERM;
 1378                 goto out;
 1379         }
 1380 
 1381 
 1382         /* Detach the directory entry from the directory (dnode). */
 1383         tmpfs_dir_detach(dvp, de);
 1384         if (v->a_cnp->cn_flags & DOWHITEOUT)
 1385                 tmpfs_dir_whiteout_add(dvp, v->a_cnp);
 1386 
 1387         /* No vnode should be allocated for this entry from this point */
 1388         TMPFS_NODE_LOCK(node);
 1389         TMPFS_ASSERT_ELOCKED(node);
 1390         node->tn_links--;
 1391         node->tn_dir.tn_parent = NULL;
 1392         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
 1393             TMPFS_NODE_MODIFIED;
 1394 
 1395         TMPFS_NODE_UNLOCK(node);
 1396 
 1397         TMPFS_NODE_LOCK(dnode);
 1398         TMPFS_ASSERT_ELOCKED(dnode);
 1399         dnode->tn_links--;
 1400         dnode->tn_status |= TMPFS_NODE_ACCESSED | \
 1401             TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
 1402         TMPFS_NODE_UNLOCK(dnode);
 1403 
 1404         cache_purge(dvp);
 1405         cache_purge(vp);
 1406 
 1407         /* Free the directory entry we just deleted.  Note that the node
 1408          * referred by it will not be removed until the vnode is really
 1409          * reclaimed. */
 1410         tmpfs_free_dirent(tmp, de, TRUE);
 1411 
 1412         /* Release the deleted vnode (will destroy the node, notify
 1413          * interested parties and clean it from the cache). */
 1414 
 1415         dnode->tn_status |= TMPFS_NODE_CHANGED;
 1416         tmpfs_update(dvp);
 1417 
 1418         error = 0;
 1419 
 1420 out:
 1421         return error;
 1422 }
 1423 
 1424 /* --------------------------------------------------------------------- */
 1425 
 1426 static int
 1427 tmpfs_symlink(struct vop_symlink_args *v)
 1428 {
 1429         struct vnode *dvp = v->a_dvp;
 1430         struct vnode **vpp = v->a_vpp;
 1431         struct componentname *cnp = v->a_cnp;
 1432         struct vattr *vap = v->a_vap;
 1433         char *target = v->a_target;
 1434 
 1435 #ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */
 1436         MPASS(vap->va_type == VLNK);
 1437 #else
 1438         vap->va_type = VLNK;
 1439 #endif
 1440 
 1441         return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
 1442 }
 1443 
 1444 /* --------------------------------------------------------------------- */
 1445 
 1446 static int
 1447 tmpfs_readdir(struct vop_readdir_args *v)
 1448 {
 1449         struct vnode *vp = v->a_vp;
 1450         struct uio *uio = v->a_uio;
 1451         int *eofflag = v->a_eofflag;
 1452         u_long **cookies = v->a_cookies;
 1453         int *ncookies = v->a_ncookies;
 1454 
 1455         int error;
 1456         off_t startoff;
 1457         off_t cnt = 0;
 1458         struct tmpfs_node *node;
 1459 
 1460         /* This operation only makes sense on directory nodes. */
 1461         if (vp->v_type != VDIR)
 1462                 return ENOTDIR;
 1463 
 1464         node = VP_TO_TMPFS_DIR(vp);
 1465 
 1466         startoff = uio->uio_offset;
 1467 
 1468         if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
 1469                 error = tmpfs_dir_getdotdent(node, uio);
 1470                 if (error != 0)
 1471                         goto outok;
 1472                 cnt++;
 1473         }
 1474 
 1475         if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
 1476                 error = tmpfs_dir_getdotdotdent(node, uio);
 1477                 if (error != 0)
 1478                         goto outok;
 1479                 cnt++;
 1480         }
 1481 
 1482         error = tmpfs_dir_getdents(node, uio, &cnt);
 1483 
 1484 outok:
 1485         MPASS(error >= -1);
 1486 
 1487         if (error == -1)
 1488                 error = (cnt != 0) ? 0 : EINVAL;
 1489 
 1490         if (eofflag != NULL)
 1491                 *eofflag =
 1492                     (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
 1493 
 1494         /* Update NFS-related variables. */
 1495         if (error == 0 && cookies != NULL && ncookies != NULL) {
 1496                 off_t i;
 1497                 off_t off = startoff;
 1498                 struct tmpfs_dirent *de = NULL;
 1499 
 1500                 *ncookies = cnt;
 1501                 *cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
 1502 
 1503                 for (i = 0; i < cnt; i++) {
 1504                         MPASS(off != TMPFS_DIRCOOKIE_EOF);
 1505                         if (off == TMPFS_DIRCOOKIE_DOT) {
 1506                                 off = TMPFS_DIRCOOKIE_DOTDOT;
 1507                         } else {
 1508                                 if (off == TMPFS_DIRCOOKIE_DOTDOT) {
 1509                                         de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
 1510                                 } else if (de != NULL) {
 1511                                         de = TAILQ_NEXT(de, td_entries);
 1512                                 } else {
 1513                                         de = tmpfs_dir_lookupbycookie(node,
 1514                                             off);
 1515                                         MPASS(de != NULL);
 1516                                         de = TAILQ_NEXT(de, td_entries);
 1517                                 }
 1518                                 if (de == NULL)
 1519                                         off = TMPFS_DIRCOOKIE_EOF;
 1520                                 else
 1521                                         off = tmpfs_dircookie(de);
 1522                         }
 1523 
 1524                         (*cookies)[i] = off;
 1525                 }
 1526                 MPASS(uio->uio_offset == off);
 1527         }
 1528 
 1529         return error;
 1530 }
 1531 
 1532 /* --------------------------------------------------------------------- */
 1533 
 1534 static int
 1535 tmpfs_readlink(struct vop_readlink_args *v)
 1536 {
 1537         struct vnode *vp = v->a_vp;
 1538         struct uio *uio = v->a_uio;
 1539 
 1540         int error;
 1541         struct tmpfs_node *node;
 1542 
 1543         MPASS(uio->uio_offset == 0);
 1544         MPASS(vp->v_type == VLNK);
 1545 
 1546         node = VP_TO_TMPFS_NODE(vp);
 1547 
 1548         error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid),
 1549             uio);
 1550         node->tn_status |= TMPFS_NODE_ACCESSED;
 1551 
 1552         return error;
 1553 }
 1554 
 1555 /* --------------------------------------------------------------------- */
 1556 
 1557 static int
 1558 tmpfs_inactive(struct vop_inactive_args *v)
 1559 {
 1560         struct vnode *vp = v->a_vp;
 1561         struct thread *l = v->a_td;
 1562 
 1563         struct tmpfs_node *node;
 1564 
 1565         MPASS(VOP_ISLOCKED(vp));
 1566 
 1567         node = VP_TO_TMPFS_NODE(vp);
 1568 
 1569         if (node->tn_links == 0)
 1570                 vrecycle(vp, l);
 1571 
 1572         return 0;
 1573 }
 1574 
 1575 /* --------------------------------------------------------------------- */
 1576 
 1577 int
 1578 tmpfs_reclaim(struct vop_reclaim_args *v)
 1579 {
 1580         struct vnode *vp = v->a_vp;
 1581 
 1582         struct tmpfs_mount *tmp;
 1583         struct tmpfs_node *node;
 1584 
 1585         node = VP_TO_TMPFS_NODE(vp);
 1586         tmp = VFS_TO_TMPFS(vp->v_mount);
 1587 
 1588         vnode_destroy_vobject(vp);
 1589         cache_purge(vp);
 1590 
 1591         TMPFS_NODE_LOCK(node);
 1592         TMPFS_ASSERT_ELOCKED(node);
 1593         tmpfs_free_vp(vp);
 1594 
 1595         /* If the node referenced by this vnode was deleted by the user,
 1596          * we must free its associated data structures (now that the vnode
 1597          * is being reclaimed). */
 1598         if (node->tn_links == 0 &&
 1599             (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) {
 1600                 node->tn_vpstate = TMPFS_VNODE_DOOMED;
 1601                 TMPFS_NODE_UNLOCK(node);
 1602                 tmpfs_free_node(tmp, node);
 1603         } else
 1604                 TMPFS_NODE_UNLOCK(node);
 1605 
 1606         MPASS(vp->v_data == NULL);
 1607         return 0;
 1608 }
 1609 
 1610 /* --------------------------------------------------------------------- */
 1611 
 1612 static int
 1613 tmpfs_print(struct vop_print_args *v)
 1614 {
 1615         struct vnode *vp = v->a_vp;
 1616 
 1617         struct tmpfs_node *node;
 1618 
 1619         node = VP_TO_TMPFS_NODE(vp);
 1620 
 1621         printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n",
 1622             node, node->tn_flags, node->tn_links);
 1623         printf("\tmode 0%o, owner %d, group %d, size %" PRIdMAX
 1624             ", status 0x%x\n",
 1625             node->tn_mode, node->tn_uid, node->tn_gid,
 1626             (uintmax_t)node->tn_size, node->tn_status);
 1627 
 1628         if (vp->v_type == VFIFO)
 1629                 fifo_printinfo(vp);
 1630 
 1631         printf("\n");
 1632 
 1633         return 0;
 1634 }
 1635 
 1636 /* --------------------------------------------------------------------- */
 1637 
 1638 static int
 1639 tmpfs_pathconf(struct vop_pathconf_args *v)
 1640 {
 1641         int name = v->a_name;
 1642         register_t *retval = v->a_retval;
 1643 
 1644         int error;
 1645 
 1646         error = 0;
 1647 
 1648         switch (name) {
 1649         case _PC_LINK_MAX:
 1650                 *retval = LINK_MAX;
 1651                 break;
 1652 
 1653         case _PC_NAME_MAX:
 1654                 *retval = NAME_MAX;
 1655                 break;
 1656 
 1657         case _PC_PATH_MAX:
 1658                 *retval = PATH_MAX;
 1659                 break;
 1660 
 1661         case _PC_PIPE_BUF:
 1662                 *retval = PIPE_BUF;
 1663                 break;
 1664 
 1665         case _PC_CHOWN_RESTRICTED:
 1666                 *retval = 1;
 1667                 break;
 1668 
 1669         case _PC_NO_TRUNC:
 1670                 *retval = 1;
 1671                 break;
 1672 
 1673         case _PC_SYNC_IO:
 1674                 *retval = 1;
 1675                 break;
 1676 
 1677         case _PC_FILESIZEBITS:
 1678                 *retval = 0; /* XXX Don't know which value should I return. */
 1679                 break;
 1680 
 1681         default:
 1682                 error = EINVAL;
 1683         }
 1684 
 1685         return error;
 1686 }
 1687 
 1688 static int
 1689 tmpfs_vptofh(struct vop_vptofh_args *ap)
 1690 {
 1691         struct tmpfs_fid *tfhp;
 1692         struct tmpfs_node *node;
 1693 
 1694         tfhp = (struct tmpfs_fid *)ap->a_fhp;
 1695         node = VP_TO_TMPFS_NODE(ap->a_vp);
 1696 
 1697         tfhp->tf_len = sizeof(struct tmpfs_fid);
 1698         tfhp->tf_id = node->tn_id;
 1699         tfhp->tf_gen = node->tn_gen;
 1700 
 1701         return (0);
 1702 }
 1703 
 1704 static int
 1705 tmpfs_whiteout(struct vop_whiteout_args *ap)
 1706 {
 1707         struct vnode *dvp = ap->a_dvp;
 1708         struct componentname *cnp = ap->a_cnp;
 1709         struct tmpfs_dirent *de;
 1710 
 1711         switch (ap->a_flags) {
 1712         case LOOKUP:
 1713                 return (0);
 1714         case CREATE:
 1715                 de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
 1716                 if (de != NULL)
 1717                         return (de->td_node == NULL ? 0 : EEXIST);
 1718                 return (tmpfs_dir_whiteout_add(dvp, cnp));
 1719         case DELETE:
 1720                 tmpfs_dir_whiteout_remove(dvp, cnp);
 1721                 return (0);
 1722         default:
 1723                 panic("tmpfs_whiteout: unknown op");
 1724         }
 1725 }
 1726 
 1727 /* --------------------------------------------------------------------- */
 1728 
 1729 /*
 1730  * vnode operations vector used for files stored in a tmpfs file system.
 1731  */
 1732 struct vop_vector tmpfs_vnodeop_entries = {
 1733         .vop_default =                  &default_vnodeops,
 1734         .vop_lookup =                   vfs_cache_lookup,
 1735         .vop_cachedlookup =             tmpfs_lookup,
 1736         .vop_create =                   tmpfs_create,
 1737         .vop_mknod =                    tmpfs_mknod,
 1738         .vop_open =                     tmpfs_open,
 1739         .vop_close =                    tmpfs_close,
 1740         .vop_access =                   tmpfs_access,
 1741         .vop_getattr =                  tmpfs_getattr,
 1742         .vop_setattr =                  tmpfs_setattr,
 1743         .vop_read =                     tmpfs_read,
 1744         .vop_write =                    tmpfs_write,
 1745         .vop_fsync =                    tmpfs_fsync,
 1746         .vop_remove =                   tmpfs_remove,
 1747         .vop_link =                     tmpfs_link,
 1748         .vop_rename =                   tmpfs_rename,
 1749         .vop_mkdir =                    tmpfs_mkdir,
 1750         .vop_rmdir =                    tmpfs_rmdir,
 1751         .vop_symlink =                  tmpfs_symlink,
 1752         .vop_readdir =                  tmpfs_readdir,
 1753         .vop_readlink =                 tmpfs_readlink,
 1754         .vop_inactive =                 tmpfs_inactive,
 1755         .vop_reclaim =                  tmpfs_reclaim,
 1756         .vop_print =                    tmpfs_print,
 1757         .vop_pathconf =                 tmpfs_pathconf,
 1758         .vop_vptofh =                   tmpfs_vptofh,
 1759         .vop_whiteout =                 tmpfs_whiteout,
 1760         .vop_bmap =                     VOP_EOPNOTSUPP,
 1761 };
 1762 

Cache object: 68d639d0aca6bcf8c83ab2d155050289


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