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

Cache object: d3c7fc25a526a13d825fd838eb2f6b18


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