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_subr.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_subr.c,v 1.35 2007/07/09 21:10:50 ad Exp $       */
    2 
    3 /*-
    4  * Copyright (c) 2005 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  * Efficient memory file system supporting functions.
   35  */
   36 #include <sys/cdefs.h>
   37 __FBSDID("$FreeBSD$");
   38 
   39 #include <sys/param.h>
   40 #include <sys/namei.h>
   41 #include <sys/priv.h>
   42 #include <sys/proc.h>
   43 #include <sys/stat.h>
   44 #include <sys/systm.h>
   45 #include <sys/sysctl.h>
   46 #include <sys/vnode.h>
   47 #include <sys/vmmeter.h>
   48 
   49 #include <vm/vm.h>
   50 #include <vm/vm_param.h>
   51 #include <vm/vm_object.h>
   52 #include <vm/vm_page.h>
   53 #include <vm/vm_pageout.h>
   54 #include <vm/vm_pager.h>
   55 #include <vm/vm_extern.h>
   56 
   57 #include <fs/tmpfs/tmpfs.h>
   58 #include <fs/tmpfs/tmpfs_fifoops.h>
   59 #include <fs/tmpfs/tmpfs_vnops.h>
   60 
   61 SYSCTL_NODE(_vfs, OID_AUTO, tmpfs, CTLFLAG_RW, 0, "tmpfs file system");
   62 
   63 static long tmpfs_pages_reserved = TMPFS_PAGES_MINRESERVED;
   64 
   65 static int
   66 sysctl_mem_reserved(SYSCTL_HANDLER_ARGS)
   67 {
   68         int error;
   69         long pages, bytes;
   70 
   71         pages = *(long *)arg1;
   72         bytes = pages * PAGE_SIZE;
   73 
   74         error = sysctl_handle_long(oidp, &bytes, 0, req);
   75         if (error || !req->newptr)
   76                 return (error);
   77 
   78         pages = bytes / PAGE_SIZE;
   79         if (pages < TMPFS_PAGES_MINRESERVED)
   80                 return (EINVAL);
   81 
   82         *(long *)arg1 = pages;
   83         return (0);
   84 }
   85 
   86 SYSCTL_PROC(_vfs_tmpfs, OID_AUTO, memory_reserved, CTLTYPE_LONG|CTLFLAG_RW,
   87     &tmpfs_pages_reserved, 0, sysctl_mem_reserved, "L",
   88     "Amount of available memory and swap below which tmpfs growth stops");
   89 
   90 size_t
   91 tmpfs_mem_avail(void)
   92 {
   93         vm_ooffset_t avail;
   94 
   95         avail = swap_pager_avail + cnt.v_free_count + cnt.v_cache_count -
   96             tmpfs_pages_reserved;
   97         if (__predict_false(avail < 0))
   98                 avail = 0;
   99         return (avail);
  100 }
  101 
  102 size_t
  103 tmpfs_pages_used(struct tmpfs_mount *tmp)
  104 {
  105         const size_t node_size = sizeof(struct tmpfs_node) +
  106             sizeof(struct tmpfs_dirent);
  107         size_t meta_pages;
  108 
  109         meta_pages = howmany((uintmax_t)tmp->tm_nodes_inuse * node_size,
  110             PAGE_SIZE);
  111         return (meta_pages + tmp->tm_pages_used);
  112 }
  113 
  114 static size_t
  115 tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages)
  116 {
  117         if (tmpfs_mem_avail() < req_pages)
  118                 return (0);
  119 
  120         if (tmp->tm_pages_max != SIZE_MAX &&
  121             tmp->tm_pages_max < req_pages + tmpfs_pages_used(tmp))
  122                         return (0);
  123 
  124         return (1);
  125 }
  126 
  127 /* --------------------------------------------------------------------- */
  128 
  129 /*
  130  * Allocates a new node of type 'type' inside the 'tmp' mount point, with
  131  * its owner set to 'uid', its group to 'gid' and its mode set to 'mode',
  132  * using the credentials of the process 'p'.
  133  *
  134  * If the node type is set to 'VDIR', then the parent parameter must point
  135  * to the parent directory of the node being created.  It may only be NULL
  136  * while allocating the root node.
  137  *
  138  * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter
  139  * specifies the device the node represents.
  140  *
  141  * If the node type is set to 'VLNK', then the parameter target specifies
  142  * the file name of the target file for the symbolic link that is being
  143  * created.
  144  *
  145  * Note that new nodes are retrieved from the available list if it has
  146  * items or, if it is empty, from the node pool as long as there is enough
  147  * space to create them.
  148  *
  149  * Returns zero on success or an appropriate error code on failure.
  150  */
  151 int
  152 tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
  153     uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent,
  154     char *target, dev_t rdev, struct tmpfs_node **node)
  155 {
  156         struct tmpfs_node *nnode;
  157 
  158         /* If the root directory of the 'tmp' file system is not yet
  159          * allocated, this must be the request to do it. */
  160         MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR));
  161 
  162         MPASS(IFF(type == VLNK, target != NULL));
  163         MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL));
  164 
  165         if (tmp->tm_nodes_inuse >= tmp->tm_nodes_max)
  166                 return (ENOSPC);
  167         if (tmpfs_pages_check_avail(tmp, 1) == 0)
  168                 return (ENOSPC);
  169 
  170         nnode = (struct tmpfs_node *)uma_zalloc_arg(
  171                                 tmp->tm_node_pool, tmp, M_WAITOK);
  172 
  173         /* Generic initialization. */
  174         nnode->tn_type = type;
  175         vfs_timestamp(&nnode->tn_atime);
  176         nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime =
  177             nnode->tn_atime;
  178         nnode->tn_uid = uid;
  179         nnode->tn_gid = gid;
  180         nnode->tn_mode = mode;
  181         nnode->tn_id = alloc_unr(tmp->tm_ino_unr);
  182 
  183         /* Type-specific initialization. */
  184         switch (nnode->tn_type) {
  185         case VBLK:
  186         case VCHR:
  187                 nnode->tn_rdev = rdev;
  188                 break;
  189 
  190         case VDIR:
  191                 TAILQ_INIT(&nnode->tn_dir.tn_dirhead);
  192                 MPASS(parent != nnode);
  193                 MPASS(IMPLIES(parent == NULL, tmp->tm_root == NULL));
  194                 nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent;
  195                 nnode->tn_dir.tn_readdir_lastn = 0;
  196                 nnode->tn_dir.tn_readdir_lastp = NULL;
  197                 nnode->tn_links++;
  198                 TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent);
  199                 nnode->tn_dir.tn_parent->tn_links++;
  200                 TMPFS_NODE_UNLOCK(nnode->tn_dir.tn_parent);
  201                 break;
  202 
  203         case VFIFO:
  204                 /* FALLTHROUGH */
  205         case VSOCK:
  206                 break;
  207 
  208         case VLNK:
  209                 MPASS(strlen(target) < MAXPATHLEN);
  210                 nnode->tn_size = strlen(target);
  211                 nnode->tn_link = malloc(nnode->tn_size, M_TMPFSNAME,
  212                     M_WAITOK);
  213                 memcpy(nnode->tn_link, target, nnode->tn_size);
  214                 break;
  215 
  216         case VREG:
  217                 nnode->tn_reg.tn_aobj =
  218                     vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0,
  219                         NULL /* XXXKIB - tmpfs needs swap reservation */);
  220                 break;
  221 
  222         default:
  223                 panic("tmpfs_alloc_node: type %p %d", nnode, (int)nnode->tn_type);
  224         }
  225 
  226         TMPFS_LOCK(tmp);
  227         LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries);
  228         tmp->tm_nodes_inuse++;
  229         TMPFS_UNLOCK(tmp);
  230 
  231         *node = nnode;
  232         return 0;
  233 }
  234 
  235 /* --------------------------------------------------------------------- */
  236 
  237 /*
  238  * Destroys the node pointed to by node from the file system 'tmp'.
  239  * If the node does not belong to the given mount point, the results are
  240  * unpredicted.
  241  *
  242  * If the node references a directory; no entries are allowed because
  243  * their removal could need a recursive algorithm, something forbidden in
  244  * kernel space.  Furthermore, there is not need to provide such
  245  * functionality (recursive removal) because the only primitives offered
  246  * to the user are the removal of empty directories and the deletion of
  247  * individual files.
  248  *
  249  * Note that nodes are not really deleted; in fact, when a node has been
  250  * allocated, it cannot be deleted during the whole life of the file
  251  * system.  Instead, they are moved to the available list and remain there
  252  * until reused.
  253  */
  254 void
  255 tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
  256 {
  257         vm_object_t uobj;
  258 
  259 #ifdef INVARIANTS
  260         TMPFS_NODE_LOCK(node);
  261         MPASS(node->tn_vnode == NULL);
  262         MPASS((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0);
  263         TMPFS_NODE_UNLOCK(node);
  264 #endif
  265 
  266         TMPFS_LOCK(tmp);
  267         LIST_REMOVE(node, tn_entries);
  268         tmp->tm_nodes_inuse--;
  269         TMPFS_UNLOCK(tmp);
  270 
  271         switch (node->tn_type) {
  272         case VNON:
  273                 /* Do not do anything.  VNON is provided to let the
  274                  * allocation routine clean itself easily by avoiding
  275                  * duplicating code in it. */
  276                 /* FALLTHROUGH */
  277         case VBLK:
  278                 /* FALLTHROUGH */
  279         case VCHR:
  280                 /* FALLTHROUGH */
  281         case VDIR:
  282                 /* FALLTHROUGH */
  283         case VFIFO:
  284                 /* FALLTHROUGH */
  285         case VSOCK:
  286                 break;
  287 
  288         case VLNK:
  289                 free(node->tn_link, M_TMPFSNAME);
  290                 break;
  291 
  292         case VREG:
  293                 uobj = node->tn_reg.tn_aobj;
  294                 if (uobj != NULL) {
  295                         TMPFS_LOCK(tmp);
  296                         tmp->tm_pages_used -= uobj->size;
  297                         TMPFS_UNLOCK(tmp);
  298                         vm_object_deallocate(uobj);
  299                 }
  300                 break;
  301 
  302         default:
  303                 panic("tmpfs_free_node: type %p %d", node, (int)node->tn_type);
  304         }
  305 
  306         free_unr(tmp->tm_ino_unr, node->tn_id);
  307         uma_zfree(tmp->tm_node_pool, node);
  308 }
  309 
  310 /* --------------------------------------------------------------------- */
  311 
  312 /*
  313  * Allocates a new directory entry for the node node with a name of name.
  314  * The new directory entry is returned in *de.
  315  *
  316  * The link count of node is increased by one to reflect the new object
  317  * referencing it.
  318  *
  319  * Returns zero on success or an appropriate error code on failure.
  320  */
  321 int
  322 tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
  323     const char *name, uint16_t len, struct tmpfs_dirent **de)
  324 {
  325         struct tmpfs_dirent *nde;
  326 
  327         nde = (struct tmpfs_dirent *)uma_zalloc(
  328                                         tmp->tm_dirent_pool, M_WAITOK);
  329         nde->td_name = malloc(len, M_TMPFSNAME, M_WAITOK);
  330         nde->td_namelen = len;
  331         memcpy(nde->td_name, name, len);
  332 
  333         nde->td_node = node;
  334         if (node != NULL)
  335                 node->tn_links++;
  336 
  337         *de = nde;
  338 
  339         return 0;
  340 }
  341 
  342 /* --------------------------------------------------------------------- */
  343 
  344 /*
  345  * Frees a directory entry.  It is the caller's responsibility to destroy
  346  * the node referenced by it if needed.
  347  *
  348  * The link count of node is decreased by one to reflect the removal of an
  349  * object that referenced it.  This only happens if 'node_exists' is true;
  350  * otherwise the function will not access the node referred to by the
  351  * directory entry, as it may already have been released from the outside.
  352  */
  353 void
  354 tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de,
  355     boolean_t node_exists)
  356 {
  357         if (node_exists) {
  358                 struct tmpfs_node *node;
  359 
  360                 node = de->td_node;
  361                 if (node != NULL) {
  362                         MPASS(node->tn_links > 0);
  363                         node->tn_links--;
  364                 }
  365         }
  366 
  367         free(de->td_name, M_TMPFSNAME);
  368         uma_zfree(tmp->tm_dirent_pool, de);
  369 }
  370 
  371 /* --------------------------------------------------------------------- */
  372 
  373 /*
  374  * Allocates a new vnode for the node node or returns a new reference to
  375  * an existing one if the node had already a vnode referencing it.  The
  376  * resulting locked vnode is returned in *vpp.
  377  *
  378  * Returns zero on success or an appropriate error code on failure.
  379  */
  380 int
  381 tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, int lkflag,
  382     struct vnode **vpp)
  383 {
  384         int error = 0;
  385         struct vnode *vp;
  386 
  387 loop:
  388         TMPFS_NODE_LOCK(node);
  389 loop1:
  390         if ((vp = node->tn_vnode) != NULL) {
  391                 MPASS((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0);
  392                 VI_LOCK(vp);
  393                 if ((node->tn_type == VDIR && node->tn_dir.tn_parent == NULL) ||
  394                     ((vp->v_iflag & VI_DOOMED) != 0 &&
  395                     (lkflag & LK_NOWAIT) != 0)) {
  396                         VI_UNLOCK(vp);
  397                         TMPFS_NODE_UNLOCK(node);
  398                         error = ENOENT;
  399                         vp = NULL;
  400                         goto out;
  401                 }
  402                 if ((vp->v_iflag & VI_DOOMED) != 0) {
  403                         VI_UNLOCK(vp);
  404                         node->tn_vpstate |= TMPFS_VNODE_WRECLAIM;
  405                         while ((node->tn_vpstate & TMPFS_VNODE_WRECLAIM) != 0) {
  406                                 msleep(&node->tn_vnode, TMPFS_NODE_MTX(node),
  407                                     0, "tmpfsE", 0);
  408                         }
  409                         goto loop1;
  410                 }
  411                 TMPFS_NODE_UNLOCK(node);
  412                 error = vget(vp, lkflag | LK_INTERLOCK, curthread);
  413                 if (error == ENOENT)
  414                         goto loop;
  415                 if (error != 0) {
  416                         vp = NULL;
  417                         goto out;
  418                 }
  419 
  420                 /*
  421                  * Make sure the vnode is still there after
  422                  * getting the interlock to avoid racing a free.
  423                  */
  424                 if (node->tn_vnode == NULL || node->tn_vnode != vp) {
  425                         vput(vp);
  426                         goto loop;
  427                 }
  428 
  429                 goto out;
  430         }
  431 
  432         if ((node->tn_vpstate & TMPFS_VNODE_DOOMED) ||
  433             (node->tn_type == VDIR && node->tn_dir.tn_parent == NULL)) {
  434                 TMPFS_NODE_UNLOCK(node);
  435                 error = ENOENT;
  436                 vp = NULL;
  437                 goto out;
  438         }
  439 
  440         /*
  441          * otherwise lock the vp list while we call getnewvnode
  442          * since that can block.
  443          */
  444         if (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) {
  445                 node->tn_vpstate |= TMPFS_VNODE_WANT;
  446                 error = msleep((caddr_t) &node->tn_vpstate,
  447                     TMPFS_NODE_MTX(node), PDROP | PCATCH,
  448                     "tmpfs_alloc_vp", 0);
  449                 if (error)
  450                         return error;
  451 
  452                 goto loop;
  453         } else
  454                 node->tn_vpstate |= TMPFS_VNODE_ALLOCATING;
  455         
  456         TMPFS_NODE_UNLOCK(node);
  457 
  458         /* Get a new vnode and associate it with our node. */
  459         error = getnewvnode("tmpfs", mp, &tmpfs_vnodeop_entries, &vp);
  460         if (error != 0)
  461                 goto unlock;
  462         MPASS(vp != NULL);
  463 
  464         (void) vn_lock(vp, lkflag | LK_RETRY);
  465 
  466         vp->v_data = node;
  467         vp->v_type = node->tn_type;
  468 
  469         /* Type-specific initialization. */
  470         switch (node->tn_type) {
  471         case VBLK:
  472                 /* FALLTHROUGH */
  473         case VCHR:
  474                 /* FALLTHROUGH */
  475         case VLNK:
  476                 /* FALLTHROUGH */
  477         case VREG:
  478                 /* FALLTHROUGH */
  479         case VSOCK:
  480                 break;
  481         case VFIFO:
  482                 vp->v_op = &tmpfs_fifoop_entries;
  483                 break;
  484         case VDIR:
  485                 MPASS(node->tn_dir.tn_parent != NULL);
  486                 if (node->tn_dir.tn_parent == node)
  487                         vp->v_vflag |= VV_ROOT;
  488                 break;
  489 
  490         default:
  491                 panic("tmpfs_alloc_vp: type %p %d", node, (int)node->tn_type);
  492         }
  493 
  494         vnode_pager_setsize(vp, node->tn_size);
  495         error = insmntque(vp, mp);
  496         if (error)
  497                 vp = NULL;
  498 
  499 unlock:
  500         TMPFS_NODE_LOCK(node);
  501 
  502         MPASS(node->tn_vpstate & TMPFS_VNODE_ALLOCATING);
  503         node->tn_vpstate &= ~TMPFS_VNODE_ALLOCATING;
  504         node->tn_vnode = vp;
  505 
  506         if (node->tn_vpstate & TMPFS_VNODE_WANT) {
  507                 node->tn_vpstate &= ~TMPFS_VNODE_WANT;
  508                 TMPFS_NODE_UNLOCK(node);
  509                 wakeup((caddr_t) &node->tn_vpstate);
  510         } else
  511                 TMPFS_NODE_UNLOCK(node);
  512 
  513 out:
  514         *vpp = vp;
  515 
  516 #ifdef INVARIANTS
  517         if (error == 0) {
  518                 MPASS(*vpp != NULL && VOP_ISLOCKED(*vpp));
  519                 TMPFS_NODE_LOCK(node);
  520                 MPASS(*vpp == node->tn_vnode);
  521                 TMPFS_NODE_UNLOCK(node);
  522         }
  523 #endif
  524 
  525         return error;
  526 }
  527 
  528 /* --------------------------------------------------------------------- */
  529 
  530 /*
  531  * Destroys the association between the vnode vp and the node it
  532  * references.
  533  */
  534 void
  535 tmpfs_free_vp(struct vnode *vp)
  536 {
  537         struct tmpfs_node *node;
  538 
  539         node = VP_TO_TMPFS_NODE(vp);
  540 
  541         mtx_assert(TMPFS_NODE_MTX(node), MA_OWNED);
  542         node->tn_vnode = NULL;
  543         if ((node->tn_vpstate & TMPFS_VNODE_WRECLAIM) != 0)
  544                 wakeup(&node->tn_vnode);
  545         node->tn_vpstate &= ~TMPFS_VNODE_WRECLAIM;
  546         vp->v_data = NULL;
  547 }
  548 
  549 /* --------------------------------------------------------------------- */
  550 
  551 /*
  552  * Allocates a new file of type 'type' and adds it to the parent directory
  553  * 'dvp'; this addition is done using the component name given in 'cnp'.
  554  * The ownership of the new file is automatically assigned based on the
  555  * credentials of the caller (through 'cnp'), the group is set based on
  556  * the parent directory and the mode is determined from the 'vap' argument.
  557  * If successful, *vpp holds a vnode to the newly created file and zero
  558  * is returned.  Otherwise *vpp is NULL and the function returns an
  559  * appropriate error code.
  560  */
  561 int
  562 tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
  563     struct componentname *cnp, char *target)
  564 {
  565         int error;
  566         struct tmpfs_dirent *de;
  567         struct tmpfs_mount *tmp;
  568         struct tmpfs_node *dnode;
  569         struct tmpfs_node *node;
  570         struct tmpfs_node *parent;
  571 
  572         MPASS(VOP_ISLOCKED(dvp));
  573         MPASS(cnp->cn_flags & HASBUF);
  574 
  575         tmp = VFS_TO_TMPFS(dvp->v_mount);
  576         dnode = VP_TO_TMPFS_DIR(dvp);
  577         *vpp = NULL;
  578 
  579         /* If the entry we are creating is a directory, we cannot overflow
  580          * the number of links of its parent, because it will get a new
  581          * link. */
  582         if (vap->va_type == VDIR) {
  583                 /* Ensure that we do not overflow the maximum number of links
  584                  * imposed by the system. */
  585                 MPASS(dnode->tn_links <= LINK_MAX);
  586                 if (dnode->tn_links == LINK_MAX) {
  587                         error = EMLINK;
  588                         goto out;
  589                 }
  590 
  591                 parent = dnode;
  592                 MPASS(parent != NULL);
  593         } else
  594                 parent = NULL;
  595 
  596         /* Allocate a node that represents the new file. */
  597         error = tmpfs_alloc_node(tmp, vap->va_type, cnp->cn_cred->cr_uid,
  598             dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node);
  599         if (error != 0)
  600                 goto out;
  601 
  602         /* Allocate a directory entry that points to the new file. */
  603         error = tmpfs_alloc_dirent(tmp, node, cnp->cn_nameptr, cnp->cn_namelen,
  604             &de);
  605         if (error != 0) {
  606                 tmpfs_free_node(tmp, node);
  607                 goto out;
  608         }
  609 
  610         /* Allocate a vnode for the new file. */
  611         error = tmpfs_alloc_vp(dvp->v_mount, node, LK_EXCLUSIVE, vpp);
  612         if (error != 0) {
  613                 tmpfs_free_dirent(tmp, de, TRUE);
  614                 tmpfs_free_node(tmp, node);
  615                 goto out;
  616         }
  617 
  618         /* Now that all required items are allocated, we can proceed to
  619          * insert the new node into the directory, an operation that
  620          * cannot fail. */
  621         if (cnp->cn_flags & ISWHITEOUT)
  622                 tmpfs_dir_whiteout_remove(dvp, cnp);
  623         tmpfs_dir_attach(dvp, de);
  624 
  625 out:
  626 
  627         return error;
  628 }
  629 
  630 /* --------------------------------------------------------------------- */
  631 
  632 /*
  633  * Attaches the directory entry de to the directory represented by vp.
  634  * Note that this does not change the link count of the node pointed by
  635  * the directory entry, as this is done by tmpfs_alloc_dirent.
  636  */
  637 void
  638 tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de)
  639 {
  640         struct tmpfs_node *dnode;
  641 
  642         ASSERT_VOP_ELOCKED(vp, __func__);
  643         dnode = VP_TO_TMPFS_DIR(vp);
  644         TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_dirhead, de, td_entries);
  645         dnode->tn_size += sizeof(struct tmpfs_dirent);
  646         dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
  647             TMPFS_NODE_MODIFIED;
  648 }
  649 
  650 /* --------------------------------------------------------------------- */
  651 
  652 /*
  653  * Detaches the directory entry de from the directory represented by vp.
  654  * Note that this does not change the link count of the node pointed by
  655  * the directory entry, as this is done by tmpfs_free_dirent.
  656  */
  657 void
  658 tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de)
  659 {
  660         struct tmpfs_node *dnode;
  661 
  662         ASSERT_VOP_ELOCKED(vp, __func__);
  663         dnode = VP_TO_TMPFS_DIR(vp);
  664 
  665         if (dnode->tn_dir.tn_readdir_lastp == de) {
  666                 dnode->tn_dir.tn_readdir_lastn = 0;
  667                 dnode->tn_dir.tn_readdir_lastp = NULL;
  668         }
  669 
  670         TAILQ_REMOVE(&dnode->tn_dir.tn_dirhead, de, td_entries);
  671         dnode->tn_size -= sizeof(struct tmpfs_dirent);
  672         dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
  673             TMPFS_NODE_MODIFIED;
  674 }
  675 
  676 /* --------------------------------------------------------------------- */
  677 
  678 /*
  679  * Looks for a directory entry in the directory represented by node.
  680  * 'cnp' describes the name of the entry to look for.  Note that the .
  681  * and .. components are not allowed as they do not physically exist
  682  * within directories.
  683  *
  684  * Returns a pointer to the entry when found, otherwise NULL.
  685  */
  686 struct tmpfs_dirent *
  687 tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f,
  688     struct componentname *cnp)
  689 {
  690         boolean_t found;
  691         struct tmpfs_dirent *de;
  692 
  693         MPASS(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
  694         MPASS(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
  695             cnp->cn_nameptr[1] == '.')));
  696         TMPFS_VALIDATE_DIR(node);
  697 
  698         found = 0;
  699         TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) {
  700                 if (f != NULL && de->td_node != f)
  701                     continue;
  702                 MPASS(cnp->cn_namelen < 0xffff);
  703                 if (de->td_namelen == (uint16_t)cnp->cn_namelen &&
  704                     bcmp(de->td_name, cnp->cn_nameptr, de->td_namelen) == 0) {
  705                         found = 1;
  706                         break;
  707                 }
  708         }
  709         node->tn_status |= TMPFS_NODE_ACCESSED;
  710 
  711         return found ? de : NULL;
  712 }
  713 
  714 /* --------------------------------------------------------------------- */
  715 
  716 /*
  717  * Helper function for tmpfs_readdir.  Creates a '.' entry for the given
  718  * directory and returns it in the uio space.  The function returns 0
  719  * on success, -1 if there was not enough space in the uio structure to
  720  * hold the directory entry or an appropriate error code if another
  721  * error happens.
  722  */
  723 int
  724 tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
  725 {
  726         int error;
  727         struct dirent dent;
  728 
  729         TMPFS_VALIDATE_DIR(node);
  730         MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);
  731 
  732         dent.d_fileno = node->tn_id;
  733         dent.d_type = DT_DIR;
  734         dent.d_namlen = 1;
  735         dent.d_name[0] = '.';
  736         dent.d_name[1] = '\0';
  737         dent.d_reclen = GENERIC_DIRSIZ(&dent);
  738 
  739         if (dent.d_reclen > uio->uio_resid)
  740                 error = -1;
  741         else {
  742                 error = uiomove(&dent, dent.d_reclen, uio);
  743                 if (error == 0)
  744                         uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;
  745         }
  746 
  747         node->tn_status |= TMPFS_NODE_ACCESSED;
  748 
  749         return error;
  750 }
  751 
  752 /* --------------------------------------------------------------------- */
  753 
  754 /*
  755  * Helper function for tmpfs_readdir.  Creates a '..' entry for the given
  756  * directory and returns it in the uio space.  The function returns 0
  757  * on success, -1 if there was not enough space in the uio structure to
  758  * hold the directory entry or an appropriate error code if another
  759  * error happens.
  760  */
  761 int
  762 tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
  763 {
  764         int error;
  765         struct dirent dent;
  766 
  767         TMPFS_VALIDATE_DIR(node);
  768         MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
  769 
  770         /*
  771          * Return ENOENT if the current node is already removed.
  772          */
  773         TMPFS_ASSERT_LOCKED(node);
  774         if (node->tn_dir.tn_parent == NULL) {
  775                 return (ENOENT);
  776         }
  777 
  778         TMPFS_NODE_LOCK(node->tn_dir.tn_parent);
  779         dent.d_fileno = node->tn_dir.tn_parent->tn_id;
  780         TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent);
  781 
  782         dent.d_type = DT_DIR;
  783         dent.d_namlen = 2;
  784         dent.d_name[0] = '.';
  785         dent.d_name[1] = '.';
  786         dent.d_name[2] = '\0';
  787         dent.d_reclen = GENERIC_DIRSIZ(&dent);
  788 
  789         if (dent.d_reclen > uio->uio_resid)
  790                 error = -1;
  791         else {
  792                 error = uiomove(&dent, dent.d_reclen, uio);
  793                 if (error == 0) {
  794                         struct tmpfs_dirent *de;
  795 
  796                         de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
  797                         if (de == NULL)
  798                                 uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
  799                         else
  800                                 uio->uio_offset = tmpfs_dircookie(de);
  801                 }
  802         }
  803 
  804         node->tn_status |= TMPFS_NODE_ACCESSED;
  805 
  806         return error;
  807 }
  808 
  809 /* --------------------------------------------------------------------- */
  810 
  811 /*
  812  * Lookup a directory entry by its associated cookie.
  813  */
  814 struct tmpfs_dirent *
  815 tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie)
  816 {
  817         struct tmpfs_dirent *de;
  818 
  819         if (cookie == node->tn_dir.tn_readdir_lastn &&
  820             node->tn_dir.tn_readdir_lastp != NULL) {
  821                 return node->tn_dir.tn_readdir_lastp;
  822         }
  823 
  824         TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) {
  825                 if (tmpfs_dircookie(de) == cookie) {
  826                         break;
  827                 }
  828         }
  829 
  830         return de;
  831 }
  832 
  833 /* --------------------------------------------------------------------- */
  834 
  835 /*
  836  * Helper function for tmpfs_readdir.  Returns as much directory entries
  837  * as can fit in the uio space.  The read starts at uio->uio_offset.
  838  * The function returns 0 on success, -1 if there was not enough space
  839  * in the uio structure to hold the directory entry or an appropriate
  840  * error code if another error happens.
  841  */
  842 int
  843 tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
  844 {
  845         int error;
  846         off_t startcookie;
  847         struct tmpfs_dirent *de;
  848 
  849         TMPFS_VALIDATE_DIR(node);
  850 
  851         /* Locate the first directory entry we have to return.  We have cached
  852          * the last readdir in the node, so use those values if appropriate.
  853          * Otherwise do a linear scan to find the requested entry. */
  854         startcookie = uio->uio_offset;
  855         MPASS(startcookie != TMPFS_DIRCOOKIE_DOT);
  856         MPASS(startcookie != TMPFS_DIRCOOKIE_DOTDOT);
  857         if (startcookie == TMPFS_DIRCOOKIE_EOF) {
  858                 return 0;
  859         } else {
  860                 de = tmpfs_dir_lookupbycookie(node, startcookie);
  861         }
  862         if (de == NULL) {
  863                 return EINVAL;
  864         }
  865 
  866         /* Read as much entries as possible; i.e., until we reach the end of
  867          * the directory or we exhaust uio space. */
  868         do {
  869                 struct dirent d;
  870 
  871                 /* Create a dirent structure representing the current
  872                  * tmpfs_node and fill it. */
  873                 if (de->td_node == NULL) {
  874                         d.d_fileno = 1;
  875                         d.d_type = DT_WHT;
  876                 } else {
  877                         d.d_fileno = de->td_node->tn_id;
  878                         switch (de->td_node->tn_type) {
  879                         case VBLK:
  880                                 d.d_type = DT_BLK;
  881                                 break;
  882 
  883                         case VCHR:
  884                                 d.d_type = DT_CHR;
  885                                 break;
  886 
  887                         case VDIR:
  888                                 d.d_type = DT_DIR;
  889                                 break;
  890 
  891                         case VFIFO:
  892                                 d.d_type = DT_FIFO;
  893                                 break;
  894 
  895                         case VLNK:
  896                                 d.d_type = DT_LNK;
  897                                 break;
  898 
  899                         case VREG:
  900                                 d.d_type = DT_REG;
  901                                 break;
  902 
  903                         case VSOCK:
  904                                 d.d_type = DT_SOCK;
  905                                 break;
  906 
  907                         default:
  908                                 panic("tmpfs_dir_getdents: type %p %d",
  909                                     de->td_node, (int)de->td_node->tn_type);
  910                         }
  911                 }
  912                 d.d_namlen = de->td_namelen;
  913                 MPASS(de->td_namelen < sizeof(d.d_name));
  914                 (void)memcpy(d.d_name, de->td_name, de->td_namelen);
  915                 d.d_name[de->td_namelen] = '\0';
  916                 d.d_reclen = GENERIC_DIRSIZ(&d);
  917 
  918                 /* Stop reading if the directory entry we are treating is
  919                  * bigger than the amount of data that can be returned. */
  920                 if (d.d_reclen > uio->uio_resid) {
  921                         error = -1;
  922                         break;
  923                 }
  924 
  925                 /* Copy the new dirent structure into the output buffer and
  926                  * advance pointers. */
  927                 error = uiomove(&d, d.d_reclen, uio);
  928                 if (error == 0) {
  929                         (*cntp)++;
  930                         de = TAILQ_NEXT(de, td_entries);
  931                 }
  932         } while (error == 0 && uio->uio_resid > 0 && de != NULL);
  933 
  934         /* Update the offset and cache. */
  935         if (de == NULL) {
  936                 uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
  937                 node->tn_dir.tn_readdir_lastn = 0;
  938                 node->tn_dir.tn_readdir_lastp = NULL;
  939         } else {
  940                 node->tn_dir.tn_readdir_lastn = uio->uio_offset = tmpfs_dircookie(de);
  941                 node->tn_dir.tn_readdir_lastp = de;
  942         }
  943 
  944         node->tn_status |= TMPFS_NODE_ACCESSED;
  945         return error;
  946 }
  947 
  948 int
  949 tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp)
  950 {
  951         struct tmpfs_dirent *de;
  952         int error;
  953 
  954         error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL,
  955             cnp->cn_nameptr, cnp->cn_namelen, &de);
  956         if (error != 0)
  957                 return (error);
  958         tmpfs_dir_attach(dvp, de);
  959         return (0);
  960 }
  961 
  962 void
  963 tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp)
  964 {
  965         struct tmpfs_dirent *de;
  966 
  967         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
  968         MPASS(de != NULL && de->td_node == NULL);
  969         tmpfs_dir_detach(dvp, de);
  970         tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de, TRUE);
  971 }
  972 
  973 /* --------------------------------------------------------------------- */
  974 
  975 /*
  976  * Resizes the aobj associated with the regular file pointed to by 'vp' to the
  977  * size 'newsize'.  'vp' must point to a vnode that represents a regular file.
  978  * 'newsize' must be positive.
  979  *
  980  * Returns zero on success or an appropriate error code on failure.
  981  */
  982 int
  983 tmpfs_reg_resize(struct vnode *vp, off_t newsize, boolean_t ignerr)
  984 {
  985         struct tmpfs_mount *tmp;
  986         struct tmpfs_node *node;
  987         vm_object_t uobj;
  988         vm_page_t m, ma[1];
  989         vm_pindex_t idx, newpages, oldpages;
  990         off_t oldsize;
  991         int base, rv;
  992 
  993         MPASS(vp->v_type == VREG);
  994         MPASS(newsize >= 0);
  995 
  996         node = VP_TO_TMPFS_NODE(vp);
  997         uobj = node->tn_reg.tn_aobj;
  998         tmp = VFS_TO_TMPFS(vp->v_mount);
  999 
 1000         /*
 1001          * Convert the old and new sizes to the number of pages needed to
 1002          * store them.  It may happen that we do not need to do anything
 1003          * because the last allocated page can accommodate the change on
 1004          * its own.
 1005          */
 1006         oldsize = node->tn_size;
 1007         oldpages = OFF_TO_IDX(oldsize + PAGE_MASK);
 1008         MPASS(oldpages == uobj->size);
 1009         newpages = OFF_TO_IDX(newsize + PAGE_MASK);
 1010         if (newpages > oldpages &&
 1011             tmpfs_pages_check_avail(tmp, newpages - oldpages) == 0)
 1012                 return (ENOSPC);
 1013 
 1014         VM_OBJECT_LOCK(uobj);
 1015         if (newsize < oldsize) {
 1016                 /*
 1017                  * Zero the truncated part of the last page.
 1018                  */
 1019                 base = newsize & PAGE_MASK;
 1020                 if (base != 0) {
 1021                         idx = OFF_TO_IDX(newsize);
 1022 retry:
 1023                         m = vm_page_lookup(uobj, idx);
 1024                         if (m != NULL) {
 1025                                 if ((m->oflags & VPO_BUSY) != 0 ||
 1026                                     m->busy != 0) {
 1027                                         vm_page_sleep(m, "tmfssz");
 1028                                         goto retry;
 1029                                 }
 1030                                 MPASS(m->valid == VM_PAGE_BITS_ALL);
 1031                         } else if (vm_pager_has_page(uobj, idx, NULL, NULL)) {
 1032                                 m = vm_page_alloc(uobj, idx, VM_ALLOC_NORMAL);
 1033                                 if (m == NULL) {
 1034                                         VM_OBJECT_UNLOCK(uobj);
 1035                                         VM_WAIT;
 1036                                         VM_OBJECT_LOCK(uobj);
 1037                                         goto retry;
 1038                                 } else if (m->valid != VM_PAGE_BITS_ALL) {
 1039                                         ma[0] = m;
 1040                                         rv = vm_pager_get_pages(uobj, ma, 1, 0);
 1041                                         m = vm_page_lookup(uobj, idx);
 1042                                 } else
 1043                                         /* A cached page was reactivated. */
 1044                                         rv = VM_PAGER_OK;
 1045                                 vm_page_lock(m);
 1046                                 if (rv == VM_PAGER_OK) {
 1047                                         vm_page_deactivate(m);
 1048                                         vm_page_unlock(m);
 1049                                         vm_page_wakeup(m);
 1050                                 } else {
 1051                                         vm_page_free(m);
 1052                                         vm_page_unlock(m);
 1053                                         if (ignerr)
 1054                                                 m = NULL;
 1055                                         else {
 1056                                                 VM_OBJECT_UNLOCK(uobj);
 1057                                                 return (EIO);
 1058                                         }
 1059                                 }
 1060                         }
 1061                         if (m != NULL) {
 1062                                 pmap_zero_page_area(m, base, PAGE_SIZE - base);
 1063                                 vm_page_dirty(m);
 1064                                 vm_pager_page_unswapped(m);
 1065                         }
 1066                 }
 1067 
 1068                 /*
 1069                  * Release any swap space and free any whole pages.
 1070                  */
 1071                 if (newpages < oldpages) {
 1072                         swap_pager_freespace(uobj, newpages, oldpages -
 1073                             newpages);
 1074                         vm_object_page_remove(uobj, newpages, 0, 0);
 1075                 }
 1076         }
 1077         uobj->size = newpages;
 1078         VM_OBJECT_UNLOCK(uobj);
 1079 
 1080         TMPFS_LOCK(tmp);
 1081         tmp->tm_pages_used += (newpages - oldpages);
 1082         TMPFS_UNLOCK(tmp);
 1083 
 1084         node->tn_size = newsize;
 1085         vnode_pager_setsize(vp, newsize);
 1086         return (0);
 1087 }
 1088 
 1089 /* --------------------------------------------------------------------- */
 1090 
 1091 /*
 1092  * Change flags of the given vnode.
 1093  * Caller should execute tmpfs_update on vp after a successful execution.
 1094  * The vnode must be locked on entry and remain locked on exit.
 1095  */
 1096 int
 1097 tmpfs_chflags(struct vnode *vp, int flags, struct ucred *cred, struct thread *p)
 1098 {
 1099         int error;
 1100         struct tmpfs_node *node;
 1101 
 1102         MPASS(VOP_ISLOCKED(vp));
 1103 
 1104         node = VP_TO_TMPFS_NODE(vp);
 1105 
 1106         /* Disallow this operation if the file system is mounted read-only. */
 1107         if (vp->v_mount->mnt_flag & MNT_RDONLY)
 1108                 return EROFS;
 1109 
 1110         /*
 1111          * Callers may only modify the file flags on objects they
 1112          * have VADMIN rights for.
 1113          */
 1114         if ((error = VOP_ACCESS(vp, VADMIN, cred, p)))
 1115                 return (error);
 1116         /*
 1117          * Unprivileged processes are not permitted to unset system
 1118          * flags, or modify flags if any system flags are set.
 1119          */
 1120         if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
 1121                 if (node->tn_flags
 1122                   & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
 1123                         error = securelevel_gt(cred, 0);
 1124                         if (error)
 1125                                 return (error);
 1126                 }
 1127                 /* Snapshot flag cannot be set or cleared */
 1128                 if (((flags & SF_SNAPSHOT) != 0 &&
 1129                   (node->tn_flags & SF_SNAPSHOT) == 0) ||
 1130                   ((flags & SF_SNAPSHOT) == 0 &&
 1131                   (node->tn_flags & SF_SNAPSHOT) != 0))
 1132                         return (EPERM);
 1133                 node->tn_flags = flags;
 1134         } else {
 1135                 if (node->tn_flags
 1136                   & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
 1137                   (flags & UF_SETTABLE) != flags)
 1138                         return (EPERM);
 1139                 node->tn_flags &= SF_SETTABLE;
 1140                 node->tn_flags |= (flags & UF_SETTABLE);
 1141         }
 1142         node->tn_status |= TMPFS_NODE_CHANGED;
 1143 
 1144         MPASS(VOP_ISLOCKED(vp));
 1145 
 1146         return 0;
 1147 }
 1148 
 1149 /* --------------------------------------------------------------------- */
 1150 
 1151 /*
 1152  * Change access mode on the given vnode.
 1153  * Caller should execute tmpfs_update on vp after a successful execution.
 1154  * The vnode must be locked on entry and remain locked on exit.
 1155  */
 1156 int
 1157 tmpfs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct thread *p)
 1158 {
 1159         int error;
 1160         struct tmpfs_node *node;
 1161 
 1162         MPASS(VOP_ISLOCKED(vp));
 1163 
 1164         node = VP_TO_TMPFS_NODE(vp);
 1165 
 1166         /* Disallow this operation if the file system is mounted read-only. */
 1167         if (vp->v_mount->mnt_flag & MNT_RDONLY)
 1168                 return EROFS;
 1169 
 1170         /* Immutable or append-only files cannot be modified, either. */
 1171         if (node->tn_flags & (IMMUTABLE | APPEND))
 1172                 return EPERM;
 1173 
 1174         /*
 1175          * To modify the permissions on a file, must possess VADMIN
 1176          * for that file.
 1177          */
 1178         if ((error = VOP_ACCESS(vp, VADMIN, cred, p)))
 1179                 return (error);
 1180 
 1181         /*
 1182          * Privileged processes may set the sticky bit on non-directories,
 1183          * as well as set the setgid bit on a file with a group that the
 1184          * process is not a member of.
 1185          */
 1186         if (vp->v_type != VDIR && (mode & S_ISTXT)) {
 1187                 if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0))
 1188                         return (EFTYPE);
 1189         }
 1190         if (!groupmember(node->tn_gid, cred) && (mode & S_ISGID)) {
 1191                 error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
 1192                 if (error)
 1193                         return (error);
 1194         }
 1195 
 1196 
 1197         node->tn_mode &= ~ALLPERMS;
 1198         node->tn_mode |= mode & ALLPERMS;
 1199 
 1200         node->tn_status |= TMPFS_NODE_CHANGED;
 1201 
 1202         MPASS(VOP_ISLOCKED(vp));
 1203 
 1204         return 0;
 1205 }
 1206 
 1207 /* --------------------------------------------------------------------- */
 1208 
 1209 /*
 1210  * Change ownership of the given vnode.  At least one of uid or gid must
 1211  * be different than VNOVAL.  If one is set to that value, the attribute
 1212  * is unchanged.
 1213  * Caller should execute tmpfs_update on vp after a successful execution.
 1214  * The vnode must be locked on entry and remain locked on exit.
 1215  */
 1216 int
 1217 tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
 1218     struct thread *p)
 1219 {
 1220         int error;
 1221         struct tmpfs_node *node;
 1222         uid_t ouid;
 1223         gid_t ogid;
 1224 
 1225         MPASS(VOP_ISLOCKED(vp));
 1226 
 1227         node = VP_TO_TMPFS_NODE(vp);
 1228 
 1229         /* Assign default values if they are unknown. */
 1230         MPASS(uid != VNOVAL || gid != VNOVAL);
 1231         if (uid == VNOVAL)
 1232                 uid = node->tn_uid;
 1233         if (gid == VNOVAL)
 1234                 gid = node->tn_gid;
 1235         MPASS(uid != VNOVAL && gid != VNOVAL);
 1236 
 1237         /* Disallow this operation if the file system is mounted read-only. */
 1238         if (vp->v_mount->mnt_flag & MNT_RDONLY)
 1239                 return EROFS;
 1240 
 1241         /* Immutable or append-only files cannot be modified, either. */
 1242         if (node->tn_flags & (IMMUTABLE | APPEND))
 1243                 return EPERM;
 1244 
 1245         /*
 1246          * To modify the ownership of a file, must possess VADMIN for that
 1247          * file.
 1248          */
 1249         if ((error = VOP_ACCESS(vp, VADMIN, cred, p)))
 1250                 return (error);
 1251 
 1252         /*
 1253          * To change the owner of a file, or change the group of a file to a
 1254          * group of which we are not a member, the caller must have
 1255          * privilege.
 1256          */
 1257         if ((uid != node->tn_uid ||
 1258             (gid != node->tn_gid && !groupmember(gid, cred))) &&
 1259             (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0)))
 1260                 return (error);
 1261 
 1262         ogid = node->tn_gid;
 1263         ouid = node->tn_uid;
 1264 
 1265         node->tn_uid = uid;
 1266         node->tn_gid = gid;
 1267 
 1268         node->tn_status |= TMPFS_NODE_CHANGED;
 1269 
 1270         if ((node->tn_mode & (S_ISUID | S_ISGID)) && (ouid != uid || ogid != gid)) {
 1271                 if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0))
 1272                         node->tn_mode &= ~(S_ISUID | S_ISGID);
 1273         }
 1274 
 1275         MPASS(VOP_ISLOCKED(vp));
 1276 
 1277         return 0;
 1278 }
 1279 
 1280 /* --------------------------------------------------------------------- */
 1281 
 1282 /*
 1283  * Change size of the given vnode.
 1284  * Caller should execute tmpfs_update on vp after a successful execution.
 1285  * The vnode must be locked on entry and remain locked on exit.
 1286  */
 1287 int
 1288 tmpfs_chsize(struct vnode *vp, u_quad_t size, struct ucred *cred,
 1289     struct thread *p)
 1290 {
 1291         int error;
 1292         struct tmpfs_node *node;
 1293 
 1294         MPASS(VOP_ISLOCKED(vp));
 1295 
 1296         node = VP_TO_TMPFS_NODE(vp);
 1297 
 1298         /* Decide whether this is a valid operation based on the file type. */
 1299         error = 0;
 1300         switch (vp->v_type) {
 1301         case VDIR:
 1302                 return EISDIR;
 1303 
 1304         case VREG:
 1305                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
 1306                         return EROFS;
 1307                 break;
 1308 
 1309         case VBLK:
 1310                 /* FALLTHROUGH */
 1311         case VCHR:
 1312                 /* FALLTHROUGH */
 1313         case VFIFO:
 1314                 /* Allow modifications of special files even if in the file
 1315                  * system is mounted read-only (we are not modifying the
 1316                  * files themselves, but the objects they represent). */
 1317                 return 0;
 1318 
 1319         default:
 1320                 /* Anything else is unsupported. */
 1321                 return EOPNOTSUPP;
 1322         }
 1323 
 1324         /* Immutable or append-only files cannot be modified, either. */
 1325         if (node->tn_flags & (IMMUTABLE | APPEND))
 1326                 return EPERM;
 1327 
 1328         error = tmpfs_truncate(vp, size);
 1329         /* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
 1330          * for us, as will update tn_status; no need to do that here. */
 1331 
 1332         MPASS(VOP_ISLOCKED(vp));
 1333 
 1334         return error;
 1335 }
 1336 
 1337 /* --------------------------------------------------------------------- */
 1338 
 1339 /*
 1340  * Change access and modification times of the given vnode.
 1341  * Caller should execute tmpfs_update on vp after a successful execution.
 1342  * The vnode must be locked on entry and remain locked on exit.
 1343  */
 1344 int
 1345 tmpfs_chtimes(struct vnode *vp, struct timespec *atime, struct timespec *mtime,
 1346         struct timespec *birthtime, int vaflags, struct ucred *cred, struct thread *l)
 1347 {
 1348         int error;
 1349         struct tmpfs_node *node;
 1350 
 1351         MPASS(VOP_ISLOCKED(vp));
 1352 
 1353         node = VP_TO_TMPFS_NODE(vp);
 1354 
 1355         /* Disallow this operation if the file system is mounted read-only. */
 1356         if (vp->v_mount->mnt_flag & MNT_RDONLY)
 1357                 return EROFS;
 1358 
 1359         /* Immutable or append-only files cannot be modified, either. */
 1360         if (node->tn_flags & (IMMUTABLE | APPEND))
 1361                 return EPERM;
 1362 
 1363         /* Determine if the user have proper privilege to update time. */
 1364         if (vaflags & VA_UTIMES_NULL) {
 1365                 error = VOP_ACCESS(vp, VADMIN, cred, l);
 1366                 if (error)
 1367                         error = VOP_ACCESS(vp, VWRITE, cred, l);
 1368         } else
 1369                 error = VOP_ACCESS(vp, VADMIN, cred, l);
 1370         if (error)
 1371                 return (error);
 1372 
 1373         if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)
 1374                 node->tn_status |= TMPFS_NODE_ACCESSED;
 1375 
 1376         if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL)
 1377                 node->tn_status |= TMPFS_NODE_MODIFIED;
 1378 
 1379         if (birthtime->tv_nsec != VNOVAL && birthtime->tv_nsec != VNOVAL)
 1380                 node->tn_status |= TMPFS_NODE_MODIFIED;
 1381 
 1382         tmpfs_itimes(vp, atime, mtime);
 1383 
 1384         if (birthtime->tv_nsec != VNOVAL && birthtime->tv_nsec != VNOVAL)
 1385                 node->tn_birthtime = *birthtime;
 1386         MPASS(VOP_ISLOCKED(vp));
 1387 
 1388         return 0;
 1389 }
 1390 
 1391 /* --------------------------------------------------------------------- */
 1392 /* Sync timestamps */
 1393 void
 1394 tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
 1395     const struct timespec *mod)
 1396 {
 1397         struct tmpfs_node *node;
 1398         struct timespec now;
 1399 
 1400         node = VP_TO_TMPFS_NODE(vp);
 1401 
 1402         if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
 1403             TMPFS_NODE_CHANGED)) == 0)
 1404                 return;
 1405 
 1406         vfs_timestamp(&now);
 1407         if (node->tn_status & TMPFS_NODE_ACCESSED) {
 1408                 if (acc == NULL)
 1409                          acc = &now;
 1410                 node->tn_atime = *acc;
 1411         }
 1412         if (node->tn_status & TMPFS_NODE_MODIFIED) {
 1413                 if (mod == NULL)
 1414                         mod = &now;
 1415                 node->tn_mtime = *mod;
 1416         }
 1417         if (node->tn_status & TMPFS_NODE_CHANGED) {
 1418                 node->tn_ctime = now;
 1419         }
 1420         node->tn_status &=
 1421             ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
 1422 }
 1423 
 1424 /* --------------------------------------------------------------------- */
 1425 
 1426 void
 1427 tmpfs_update(struct vnode *vp)
 1428 {
 1429 
 1430         tmpfs_itimes(vp, NULL, NULL);
 1431 }
 1432 
 1433 /* --------------------------------------------------------------------- */
 1434 
 1435 int
 1436 tmpfs_truncate(struct vnode *vp, off_t length)
 1437 {
 1438         int error;
 1439         struct tmpfs_node *node;
 1440 
 1441         node = VP_TO_TMPFS_NODE(vp);
 1442 
 1443         if (length < 0) {
 1444                 error = EINVAL;
 1445                 goto out;
 1446         }
 1447 
 1448         if (node->tn_size == length) {
 1449                 error = 0;
 1450                 goto out;
 1451         }
 1452 
 1453         if (length > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
 1454                 return (EFBIG);
 1455 
 1456         error = tmpfs_reg_resize(vp, length, FALSE);
 1457         if (error == 0) {
 1458                 node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
 1459         }
 1460 
 1461 out:
 1462         tmpfs_update(vp);
 1463 
 1464         return error;
 1465 }

Cache object: 7d9fa846918781e90d90cd627adacc72


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