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

Cache object: f5260a1560c07bc30634df3f5d67fb3c


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