[BACK]Return to tmpfs_subr.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / fs / tmpfs

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/sys/fs/tmpfs/tmpfs_subr.c between version 1.77 and 1.77.2.2

version 1.77, 2011/08/27 15:32:28 version 1.77.2.2, 2014/05/22 11:41:02
Line 1 
Line 1 
 /*      $NetBSD$        */  /*      $NetBSD$        */
   
 /*  /*
  * Copyright (c) 2005-2011 The NetBSD Foundation, Inc.   * Copyright (c) 2005-2013 The NetBSD Foundation, Inc.
  * All rights reserved.   * All rights reserved.
  *   *
  * This code is derived from software contributed to The NetBSD Foundation   * This code is derived from software contributed to The NetBSD Foundation
Line 59 
Line 59 
  *      reference counting and link counting.  That is, an inode can only be   *      reference counting and link counting.  That is, an inode can only be
  *      destroyed if its associated vnode is inactive.  The destruction is   *      destroyed if its associated vnode is inactive.  The destruction is
  *      done on vnode reclamation i.e. tmpfs_reclaim().  It should be noted   *      done on vnode reclamation i.e. tmpfs_reclaim().  It should be noted
  *      that tmpfs_node_t::tn_links being 0 is a destruction criterion.   *      that tmpfs_node_t::tn_links being 0 is a destruction criterion.
  *   *
  *      If an inode has references within the file system (tn_links > 0) and   *      If an inode has references within the file system (tn_links > 0) and
  *      its inactive vnode gets reclaimed/recycled - then the association is   *      its inactive vnode gets reclaimed/recycled - then the association is
Line 77 
Line 77 
 __KERNEL_RCSID(0, "$NetBSD$");  __KERNEL_RCSID(0, "$NetBSD$");
   
 #include <sys/param.h>  #include <sys/param.h>
   #include <sys/cprng.h>
 #include <sys/dirent.h>  #include <sys/dirent.h>
 #include <sys/event.h>  #include <sys/event.h>
 #include <sys/kmem.h>  #include <sys/kmem.h>
Line 98  __KERNEL_RCSID(0, "$NetBSD$");
Line 99  __KERNEL_RCSID(0, "$NetBSD$");
 #include <fs/tmpfs/tmpfs_specops.h>  #include <fs/tmpfs/tmpfs_specops.h>
 #include <fs/tmpfs/tmpfs_vnops.h>  #include <fs/tmpfs/tmpfs_vnops.h>
   
   static void     tmpfs_dir_putseq(tmpfs_node_t *, tmpfs_dirent_t *);
   
 /*  /*
  * tmpfs_alloc_node: allocate a new inode of a specified type and   * tmpfs_alloc_node: allocate a new inode of a specified type and
  * insert it into the list of specified mount point.   * insert it into the list of specified mount point.
Line 124  tmpfs_alloc_node(tmpfs_mount_t *tmp, enu
Line 127  tmpfs_alloc_node(tmpfs_mount_t *tmp, enu
          * for applications that do not understand 64-bit ino_t.           * for applications that do not understand 64-bit ino_t.
          */           */
         nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode));          nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode));
         nnode->tn_gen = TMPFS_NODE_GEN_MASK & arc4random();          /*
            * Make sure the generation number is not zero.
            * tmpfs_inactive() uses generation zero to mark dead nodes.
            */
           do {
                   nnode->tn_gen = TMPFS_NODE_GEN_MASK & cprng_fast32();
           } while (nnode->tn_gen == 0);
   
         /* Generic initialization. */          /* Generic initialization. */
         nnode->tn_type = type;          nnode->tn_type = type;
         nnode->tn_size = 0;          nnode->tn_size = 0;
         nnode->tn_status = 0;  
         nnode->tn_flags = 0;          nnode->tn_flags = 0;
         nnode->tn_lockf = NULL;          nnode->tn_lockf = NULL;
   
Line 155  tmpfs_alloc_node(tmpfs_mount_t *tmp, enu
Line 163  tmpfs_alloc_node(tmpfs_mount_t *tmp, enu
                 /* Directory. */                  /* Directory. */
                 TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir);                  TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir);
                 nnode->tn_spec.tn_dir.tn_parent = NULL;                  nnode->tn_spec.tn_dir.tn_parent = NULL;
                 nnode->tn_spec.tn_dir.tn_readdir_lastn = 0;                  nnode->tn_spec.tn_dir.tn_seq_arena = NULL;
                   nnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START;
                 nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;                  nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
   
                 /* Extra link count for the virtual '.' entry. */                  /* Extra link count for the virtual '.' entry. */
Line 166  tmpfs_alloc_node(tmpfs_mount_t *tmp, enu
Line 175  tmpfs_alloc_node(tmpfs_mount_t *tmp, enu
                 break;                  break;
         case VLNK:          case VLNK:
                 /* Symbolic link.  Target specifies the file name. */                  /* Symbolic link.  Target specifies the file name. */
                 KASSERT(target && strlen(target) < MAXPATHLEN);                  KASSERT(target != NULL);
   
                 nnode->tn_size = strlen(target);                  nnode->tn_size = strlen(target);
   
                 if (nnode->tn_size == 0) {                  if (nnode->tn_size == 0) {
                           /* Zero-length targets are supported. */
                         nnode->tn_spec.tn_lnk.tn_link = NULL;                          nnode->tn_spec.tn_lnk.tn_link = NULL;
                         break;                          break;
                 }                  }
   
                   KASSERT(nnode->tn_size < MAXPATHLEN);
                   nnode->tn_size++; /* include the NUL terminator */
   
                 nnode->tn_spec.tn_lnk.tn_link =                  nnode->tn_spec.tn_lnk.tn_link =
                     tmpfs_strname_alloc(tmp, nnode->tn_size);                      tmpfs_strname_alloc(tmp, nnode->tn_size);
                 if (nnode->tn_spec.tn_lnk.tn_link == NULL) {                  if (nnode->tn_spec.tn_lnk.tn_link == NULL) {
Line 235  tmpfs_free_node(tmpfs_mount_t *tmp, tmpf
Line 249  tmpfs_free_node(tmpfs_mount_t *tmp, tmpf
                 }                  }
                 break;                  break;
         case VDIR:          case VDIR:
                 /*                  KASSERT(node->tn_size == 0);
                  * KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir));                  KASSERT(node->tn_spec.tn_dir.tn_seq_arena == NULL);
                  * KASSERT(node->tn_spec.tn_dir.tn_parent == NULL ||                  KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir));
                  *     node == tmp->tm_root);                  KASSERT(node->tn_spec.tn_dir.tn_parent == NULL ||
                  */                      node == tmp->tm_root);
                 break;                  break;
         default:          default:
                 break;                  break;
         }          }
           KASSERT(node->tn_vnode == NULL);
           KASSERT(node->tn_links == 0);
   
         mutex_destroy(&node->tn_vlock);          mutex_destroy(&node->tn_vlock);
         tmpfs_node_put(tmp, node);          tmpfs_node_put(tmp, node);
Line 264  tmpfs_vnode_get(struct mount *mp, tmpfs_
Line 280  tmpfs_vnode_get(struct mount *mp, tmpfs_
 again:  again:
         /* If there is already a vnode, try to reclaim it. */          /* If there is already a vnode, try to reclaim it. */
         if ((vp = node->tn_vnode) != NULL) {          if ((vp = node->tn_vnode) != NULL) {
                 atomic_or_ulong(&node->tn_gen, TMPFS_RECLAIMING_BIT);                  atomic_or_32(&node->tn_gen, TMPFS_RECLAIMING_BIT);
                 mutex_enter(vp->v_interlock);                  mutex_enter(vp->v_interlock);
                 mutex_exit(&node->tn_vlock);                  mutex_exit(&node->tn_vlock);
                 error = vget(vp, LK_EXCLUSIVE);                  error = vget(vp, LK_EXCLUSIVE);
Line 272  again:
Line 288  again:
                         mutex_enter(&node->tn_vlock);                          mutex_enter(&node->tn_vlock);
                         goto again;                          goto again;
                 }                  }
                 atomic_and_ulong(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);                  atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);
                 *vpp = vp;                  *vpp = vp;
                 return error;                  return error;
         }          }
         if (TMPFS_NODE_RECLAIMING(node)) {          if (TMPFS_NODE_RECLAIMING(node)) {
                 atomic_and_ulong(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);                  atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);
         }          }
   
         /*          /*
Line 332  again:
Line 348  again:
 }  }
   
 /*  /*
  * tmpfs_alloc_file: allocate a new file of specified type and adds it   * tmpfs_construct_node: allocate a new file of specified type and adds it
  * into the parent directory.   * into the parent directory.
  *   *
  * => Credentials of the caller are used.   * => Credentials of the caller are used.
  */   */
 int  int
 tmpfs_alloc_file(vnode_t *dvp, vnode_t **vpp, struct vattr *vap,  tmpfs_construct_node(vnode_t *dvp, vnode_t **vpp, struct vattr *vap,
     struct componentname *cnp, char *target)      struct componentname *cnp, char *target)
 {  {
         tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);          tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
Line 349  tmpfs_alloc_file(vnode_t *dvp, vnode_t *
Line 365  tmpfs_alloc_file(vnode_t *dvp, vnode_t *
         KASSERT(VOP_ISLOCKED(dvp));          KASSERT(VOP_ISLOCKED(dvp));
         *vpp = NULL;          *vpp = NULL;
   
           /*
            * If directory was removed, prevent from node creation.  The vnode
            * might still be referenced, but it is about to be reclaimed.
            */
           if (dnode->tn_links == 0) {
                   error = ENOENT;
                   goto out;
           }
   
         /* Check for the maximum number of links limit. */          /* Check for the maximum number of links limit. */
         if (vap->va_type == VDIR) {          if (vap->va_type == VDIR) {
                 /* Check for maximum links limit. */                  /* Check for maximum links limit. */
Line 385  tmpfs_alloc_file(vnode_t *dvp, vnode_t *
Line 410  tmpfs_alloc_file(vnode_t *dvp, vnode_t *
         if (cnp->cn_flags & ISWHITEOUT) {          if (cnp->cn_flags & ISWHITEOUT) {
                 wde = tmpfs_dir_lookup(dnode, cnp);                  wde = tmpfs_dir_lookup(dnode, cnp);
                 KASSERT(wde != NULL && wde->td_node == TMPFS_NODE_WHITEOUT);                  KASSERT(wde != NULL && wde->td_node == TMPFS_NODE_WHITEOUT);
                 tmpfs_dir_detach(dvp, wde);                  tmpfs_dir_detach(dnode, wde);
                 tmpfs_free_dirent(tmp, wde);                  tmpfs_free_dirent(tmp, wde);
         }          }
   
         /* Associate inode and attach the entry into the directory. */          /* Associate inode and attach the entry into the directory. */
         tmpfs_dir_attach(dvp, de, node);          tmpfs_dir_attach(dnode, de, node);
   
         /* Make node opaque if requested. */          /* Make node opaque if requested. */
         if (cnp->cn_flags & ISWHITEOUT)          if (cnp->cn_flags & ISWHITEOUT)
                 node->tn_flags |= UF_OPAQUE;                  node->tn_flags |= UF_OPAQUE;
   
           /* Update the parent's timestamps. */
           tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 out:  out:
         vput(dvp);          if (error == 0)
                   VOP_UNLOCK(*vpp);
   
         return error;          return error;
 }  }
   
Line 421  tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c
Line 451  tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c
         }          }
         nde->td_namelen = len;          nde->td_namelen = len;
         memcpy(nde->td_name, name, len);          memcpy(nde->td_name, name, len);
           nde->td_seq = TMPFS_DIRSEQ_NONE;
   
         *de = nde;          *de = nde;
         return 0;          return 0;
Line 432  tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c
Line 463  tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c
 void  void
 tmpfs_free_dirent(tmpfs_mount_t *tmp, tmpfs_dirent_t *de)  tmpfs_free_dirent(tmpfs_mount_t *tmp, tmpfs_dirent_t *de)
 {  {
           KASSERT(de->td_node == NULL);
         /* KASSERT(de->td_node == NULL); */          KASSERT(de->td_seq == TMPFS_DIRSEQ_NONE);
         tmpfs_strname_free(tmp, de->td_name, de->td_namelen);          tmpfs_strname_free(tmp, de->td_name, de->td_namelen);
         tmpfs_dirent_put(tmp, de);          tmpfs_dirent_put(tmp, de);
 }  }
Line 443  tmpfs_free_dirent(tmpfs_mount_t *tmp, tm
Line 474  tmpfs_free_dirent(tmpfs_mount_t *tmp, tm
  * and attach the entry into the directory, specified by vnode.   * and attach the entry into the directory, specified by vnode.
  *   *
  * => Increases link count on the associated node.   * => Increases link count on the associated node.
  * => Increases link count on directory node, if our node is VDIR.   * => Increases link count on directory node if our node is VDIR.
  *    It is caller's responsibility to check for the LINK_MAX limit.   * => It is caller's responsibility to check for the LINK_MAX limit.
  * => Triggers kqueue events here.   * => Triggers kqueue events here.
  */   */
 void  void
 tmpfs_dir_attach(vnode_t *dvp, tmpfs_dirent_t *de, tmpfs_node_t *node)  tmpfs_dir_attach(tmpfs_node_t *dnode, tmpfs_dirent_t *de, tmpfs_node_t *node)
 {  {
         tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);          vnode_t *dvp = dnode->tn_vnode;
         int events = NOTE_WRITE;          int events = NOTE_WRITE;
   
           KASSERT(dvp != NULL);
         KASSERT(VOP_ISLOCKED(dvp));          KASSERT(VOP_ISLOCKED(dvp));
   
           /* Get a new sequence number. */
           KASSERT(de->td_seq == TMPFS_DIRSEQ_NONE);
           de->td_seq = tmpfs_dir_getseq(dnode, de);
   
         /* Associate directory entry and the inode. */          /* Associate directory entry and the inode. */
         de->td_node = node;          de->td_node = node;
         if (node != TMPFS_NODE_WHITEOUT) {          if (node != TMPFS_NODE_WHITEOUT) {
Line 463  tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir
Line 499  tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir
   
                 /* Save the hint (might overwrite). */                  /* Save the hint (might overwrite). */
                 node->tn_dirent_hint = de;                  node->tn_dirent_hint = de;
           } else if ((dnode->tn_gen & TMPFS_WHITEOUT_BIT) == 0) {
                   /* Flag that there are whiteout entries. */
                   atomic_or_32(&dnode->tn_gen, TMPFS_WHITEOUT_BIT);
         }          }
   
         /* Insert the entry to the directory (parent of inode). */          /* Insert the entry to the directory (parent of inode). */
         TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);          TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
         dnode->tn_size += sizeof(tmpfs_dirent_t);          dnode->tn_size += sizeof(tmpfs_dirent_t);
         dnode->tn_status |= TMPFS_NODE_STATUSALL;  
         uvm_vnp_setsize(dvp, dnode->tn_size);          uvm_vnp_setsize(dvp, dnode->tn_size);
   
         if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) {          if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) {
Line 493  tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir
Line 531  tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir
  * => Decreases link count on the associated node.   * => Decreases link count on the associated node.
  * => Decreases the link count on directory node, if our node is VDIR.   * => Decreases the link count on directory node, if our node is VDIR.
  * => Triggers kqueue events here.   * => Triggers kqueue events here.
    *
    * => Note: dvp and vp may be NULL only if called by tmpfs_unmount().
  */   */
 void  void
 tmpfs_dir_detach(vnode_t *dvp, tmpfs_dirent_t *de)  tmpfs_dir_detach(tmpfs_node_t *dnode, tmpfs_dirent_t *de)
 {  {
         tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);  
         tmpfs_node_t *node = de->td_node;          tmpfs_node_t *node = de->td_node;
           vnode_t *vp, *dvp = dnode->tn_vnode;
         int events = NOTE_WRITE;          int events = NOTE_WRITE;
   
         KASSERT(VOP_ISLOCKED(dvp));          KASSERT(dvp == NULL || VOP_ISLOCKED(dvp));
   
         if (node != TMPFS_NODE_WHITEOUT) {  
                 vnode_t *vp = node->tn_vnode;  
   
                 KASSERT(VOP_ISLOCKED(vp));  
   
           if (__predict_true(node != TMPFS_NODE_WHITEOUT)) {
                 /* Deassociate the inode and entry. */                  /* Deassociate the inode and entry. */
                 de->td_node = NULL;  
                 node->tn_dirent_hint = NULL;                  node->tn_dirent_hint = NULL;
   
                 KASSERT(node->tn_links > 0);                  KASSERT(node->tn_links > 0);
                 node->tn_links--;                  node->tn_links--;
                 if (vp) {  
                         VN_KNOTE(vp, node->tn_links ?                  if ((vp = node->tn_vnode) != NULL) {
                             NOTE_LINK : NOTE_DELETE);                          KASSERT(VOP_ISLOCKED(vp));
                           VN_KNOTE(vp, node->tn_links ? NOTE_LINK : NOTE_DELETE);
                 }                  }
   
                 /* If directory - decrease the link count of parent. */                  /* If directory - decrease the link count of parent. */
Line 529  tmpfs_dir_detach(vnode_t *dvp, tmpfs_dir
Line 565  tmpfs_dir_detach(vnode_t *dvp, tmpfs_dir
                         events |= NOTE_LINK;                          events |= NOTE_LINK;
                 }                  }
         }          }
           de->td_node = NULL;
   
         /* Remove the entry from the directory. */          /* Remove the entry from the directory. */
         if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) {          if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) {
                 dnode->tn_spec.tn_dir.tn_readdir_lastn = 0;  
                 dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;                  dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
         }          }
         TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);          TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
   
         dnode->tn_size -= sizeof(tmpfs_dirent_t);          dnode->tn_size -= sizeof(tmpfs_dirent_t);
         dnode->tn_status |= TMPFS_NODE_STATUSALL;          tmpfs_dir_putseq(dnode, de);
         uvm_vnp_setsize(dvp, dnode->tn_size);  
         VN_KNOTE(dvp, events);          if (dvp) {
                   uvm_vnp_setsize(dvp, dnode->tn_size);
                   VN_KNOTE(dvp, events);
           }
 }  }
   
 /*  /*
Line 568  tmpfs_dir_lookup(tmpfs_node_t *node, str
Line 606  tmpfs_dir_lookup(tmpfs_node_t *node, str
                         continue;                          continue;
                 break;                  break;
         }          }
         node->tn_status |= TMPFS_NODE_ACCESSED;  
         return de;          return de;
 }  }
   
 /*  /*
  * tmpfs_dir_cached: get a cached directory entry if it is valid.  Used to   * tmpfs_dir_cached: get a cached directory entry if it is valid.  Used to
  * avoid unnecessary tmpds_dir_lookup().   * avoid unnecessary tmpfs_dir_lookup().
  *   *
  * => The vnode must be locked.   * => The vnode must be locked.
  */   */
Line 598  tmpfs_dir_cached(tmpfs_node_t *node)
Line 635  tmpfs_dir_cached(tmpfs_node_t *node)
 }  }
   
 /*  /*
  * tmpfs_dir_getdotdent: helper function for tmpfs_readdir.  Creates a   * tmpfs_dir_getseq: get a per-directory sequence number for the entry.
  * '.' entry for the given directory and returns it in the uio space.   *
    * => Shall not be larger than 2^31 for linux32 compatibility.
  */   */
 int  uint32_t
 tmpfs_dir_getdotdent(tmpfs_node_t *node, struct uio *uio)  tmpfs_dir_getseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de)
 {  {
         struct dirent *dentp;          uint32_t seq = de->td_seq;
         int error;          vmem_t *seq_arena;
           vmem_addr_t off;
           int error __diagused;
   
         TMPFS_VALIDATE_DIR(node);          TMPFS_VALIDATE_DIR(dnode);
         KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);  
   
         dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP);          if (__predict_true(seq != TMPFS_DIRSEQ_NONE)) {
         dentp->d_fileno = node->tn_id;                  /* Already set. */
         dentp->d_type = DT_DIR;                  KASSERT(seq >= TMPFS_DIRSEQ_START);
         dentp->d_namlen = 1;                  return seq;
         dentp->d_name[0] = '.';          }
         dentp->d_name[1] = '\0';  
         dentp->d_reclen = _DIRENT_SIZE(dentp);          /*
            * The "." and ".." and the end-of-directory have reserved numbers.
         if (dentp->d_reclen > uio->uio_resid)           * The other sequence numbers are allocated as following:
                 error = -1;           *
         else {           * - The first half of the 2^31 is assigned incrementally.
                 error = uiomove(dentp, dentp->d_reclen, uio);           *
                 if (error == 0)           * - If that range is exceeded, then the second half of 2^31
                         uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;           * is used, but managed by vmem(9).
            */
   
           seq = dnode->tn_spec.tn_dir.tn_next_seq;
           KASSERT(seq >= TMPFS_DIRSEQ_START);
   
           if (__predict_true(seq < TMPFS_DIRSEQ_END)) {
                   /* First half: just increment and return. */
                   dnode->tn_spec.tn_dir.tn_next_seq++;
                   return seq;
           }
   
           /*
            * First half exceeded, use the second half.  May need to create
            * vmem(9) arena for the directory first.
            */
           if ((seq_arena = dnode->tn_spec.tn_dir.tn_seq_arena) == NULL) {
                   seq_arena = vmem_create("tmpfscoo", 0,
                       TMPFS_DIRSEQ_END - 1, 1, NULL, NULL, NULL, 0,
                       VM_SLEEP, IPL_NONE);
                   dnode->tn_spec.tn_dir.tn_seq_arena = seq_arena;
                   KASSERT(seq_arena != NULL);
           }
           error = vmem_alloc(seq_arena, 1, VM_SLEEP | VM_BESTFIT, &off);
           KASSERT(error == 0);
   
           KASSERT(off < TMPFS_DIRSEQ_END);
           seq = off | TMPFS_DIRSEQ_END;
           return seq;
   }
   
   static void
   tmpfs_dir_putseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de)
   {
           vmem_t *seq_arena = dnode->tn_spec.tn_dir.tn_seq_arena;
           uint32_t seq = de->td_seq;
   
           TMPFS_VALIDATE_DIR(dnode);
   
           if (seq == TMPFS_DIRSEQ_NONE || seq < TMPFS_DIRSEQ_END) {
                   /* First half (or no sequence number set yet). */
                   KASSERT(de->td_seq >= TMPFS_DIRSEQ_START);
           } else {
                   /* Second half. */
                   KASSERT(seq_arena != NULL);
                   KASSERT(seq >= TMPFS_DIRSEQ_END);
                   seq &= ~TMPFS_DIRSEQ_END;
                   vmem_free(seq_arena, seq, 1);
           }
           de->td_seq = TMPFS_DIRSEQ_NONE;
   
           /* Empty?  We can reset. */
           if (seq_arena && dnode->tn_size == 0) {
                   dnode->tn_spec.tn_dir.tn_seq_arena = NULL;
                   dnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START;
                   vmem_destroy(seq_arena);
         }          }
         node->tn_status |= TMPFS_NODE_ACCESSED;  
         kmem_free(dentp, sizeof(struct dirent));  
         return error;  
 }  }
   
 /*  /*
  * tmpfs_dir_getdotdotdent: helper function for tmpfs_readdir.  Creates a   * tmpfs_dir_lookupbyseq: lookup a directory entry by the sequence number.
  * '..' entry for the given directory and returns it in the uio space.  
  */   */
 int  tmpfs_dirent_t *
 tmpfs_dir_getdotdotdent(tmpfs_node_t *node, struct uio *uio)  tmpfs_dir_lookupbyseq(tmpfs_node_t *node, off_t seq)
 {  {
         struct dirent *dentp;          tmpfs_dirent_t *de = node->tn_spec.tn_dir.tn_readdir_lastp;
         int error;  
   
         TMPFS_VALIDATE_DIR(node);          TMPFS_VALIDATE_DIR(node);
         KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);  
   
         dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP);          /*
         dentp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id;           * First, check the cache.  If does not match - perform a lookup.
         dentp->d_type = DT_DIR;           */
         dentp->d_namlen = 2;          if (de && de->td_seq == seq) {
         dentp->d_name[0] = '.';                  KASSERT(de->td_seq >= TMPFS_DIRSEQ_START);
         dentp->d_name[1] = '.';                  KASSERT(de->td_seq != TMPFS_DIRSEQ_NONE);
         dentp->d_name[2] = '\0';                  return de;
         dentp->d_reclen = _DIRENT_SIZE(dentp);  
   
         if (dentp->d_reclen > uio->uio_resid)  
                 error = -1;  
         else {  
                 error = uiomove(dentp, dentp->d_reclen, uio);  
                 if (error == 0) {  
                         tmpfs_dirent_t *de;  
   
                         de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);  
                         if (de == NULL)  
                                 uio->uio_offset = TMPFS_DIRCOOKIE_EOF;  
                         else  
                                 uio->uio_offset = tmpfs_dircookie(de);  
                 }  
         }          }
         node->tn_status |= TMPFS_NODE_ACCESSED;          TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
         kmem_free(dentp, sizeof(struct dirent));                  KASSERT(de->td_seq >= TMPFS_DIRSEQ_START);
         return error;                  KASSERT(de->td_seq != TMPFS_DIRSEQ_NONE);
                   if (de->td_seq == seq)
                           return de;
           }
           return NULL;
 }  }
   
 /*  /*
  * tmpfs_dir_lookupbycookie: lookup a directory entry by associated cookie.   * tmpfs_dir_getdotents: helper function for tmpfs_readdir() to get the
    * dot meta entries, that is, "." or "..".  Copy it to the UIO space.
  */   */
 tmpfs_dirent_t *  static int
 tmpfs_dir_lookupbycookie(tmpfs_node_t *node, off_t cookie)  tmpfs_dir_getdotents(tmpfs_node_t *node, struct dirent *dp, struct uio *uio)
 {  {
         tmpfs_dirent_t *de;          tmpfs_dirent_t *de;
           off_t next = 0;
           int error;
   
         KASSERT(VOP_ISLOCKED(node->tn_vnode));          switch (uio->uio_offset) {
           case TMPFS_DIRSEQ_DOT:
                   dp->d_fileno = node->tn_id;
                   strlcpy(dp->d_name, ".", sizeof(dp->d_name));
                   next = TMPFS_DIRSEQ_DOTDOT;
                   break;
           case TMPFS_DIRSEQ_DOTDOT:
                   dp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id;
                   strlcpy(dp->d_name, "..", sizeof(dp->d_name));
                   de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
                   next = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF;
                   break;
           default:
                   KASSERT(false);
           }
           dp->d_type = DT_DIR;
           dp->d_namlen = strlen(dp->d_name);
           dp->d_reclen = _DIRENT_SIZE(dp);
   
         if (cookie == node->tn_spec.tn_dir.tn_readdir_lastn &&          if (dp->d_reclen > uio->uio_resid) {
             node->tn_spec.tn_dir.tn_readdir_lastp != NULL) {                  return EJUSTRETURN;
                 return node->tn_spec.tn_dir.tn_readdir_lastp;  
         }          }
         TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {          if ((error = uiomove(dp, dp->d_reclen, uio)) != 0) {
                 if (tmpfs_dircookie(de) == cookie) {                  return error;
                         break;  
                 }  
         }          }
         return de;  
           uio->uio_offset = next;
           return error;
 }  }
   
 /*  /*
  * tmpfs_dir_getdents: relper function for tmpfs_readdir.   * tmpfs_dir_getdents: helper function for tmpfs_readdir.
  *   *
  * => Returns as much directory entries as can fit in the uio space.   * => Returns as much directory entries as can fit in the uio space.
  * => The read starts at uio->uio_offset.   * => The read starts at uio->uio_offset.
Line 703  int
Line 799  int
 tmpfs_dir_getdents(tmpfs_node_t *node, struct uio *uio, off_t *cntp)  tmpfs_dir_getdents(tmpfs_node_t *node, struct uio *uio, off_t *cntp)
 {  {
         tmpfs_dirent_t *de;          tmpfs_dirent_t *de;
         struct dirent *dentp;          struct dirent dent;
         off_t startcookie;          int error = 0;
         int error;  
   
         KASSERT(VOP_ISLOCKED(node->tn_vnode));          KASSERT(VOP_ISLOCKED(node->tn_vnode));
         TMPFS_VALIDATE_DIR(node);          TMPFS_VALIDATE_DIR(node);
   
         /*          /*
          * Locate the first directory entry we have to return.  We have cached           * First check for the "." and ".." cases.
          * the last readdir in the node, so use those values if appropriate.           * Note: tmpfs_dir_getdotents() will "seek" for us.
          * Otherwise do a linear scan to find the requested entry.  
          */           */
         startcookie = uio->uio_offset;          memset(&dent, 0, sizeof(dent));
         KASSERT(startcookie != TMPFS_DIRCOOKIE_DOT);  
         KASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT);          if (uio->uio_offset == TMPFS_DIRSEQ_DOT) {
         if (startcookie == TMPFS_DIRCOOKIE_EOF) {                  if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) {
                 return 0;                          goto done;
         } else {                  }
                 de = tmpfs_dir_lookupbycookie(node, startcookie);                  (*cntp)++;
         }          }
           if (uio->uio_offset == TMPFS_DIRSEQ_DOTDOT) {
                   if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) {
                           goto done;
                   }
                   (*cntp)++;
           }
   
           /* Done if we reached the end. */
           if (uio->uio_offset == TMPFS_DIRSEQ_EOF) {
                   goto done;
           }
   
           /* Locate the directory entry given by the given sequence number. */
           de = tmpfs_dir_lookupbyseq(node, uio->uio_offset);
         if (de == NULL) {          if (de == NULL) {
                 return EINVAL;                  error = EINVAL;
                   goto done;
         }          }
   
         /*          /*
          * Read as much entries as possible; i.e., until we reach the end           * Read as many entries as possible; i.e., until we reach the end
          * of the directory or we exhaust uio space.           * of the directory or we exhaust UIO space.
          */           */
         dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP);  
         do {          do {
                 /*  
                  * Create a dirent structure representing the current  
                  * inode and fill it.  
                  */  
                 if (de->td_node == TMPFS_NODE_WHITEOUT) {                  if (de->td_node == TMPFS_NODE_WHITEOUT) {
                         dentp->d_fileno = 1;                          dent.d_fileno = 1;
                         dentp->d_type = DT_WHT;                          dent.d_type = DT_WHT;
                 } else {                  } else {
                         dentp->d_fileno = de->td_node->tn_id;                          dent.d_fileno = de->td_node->tn_id;
                         switch (de->td_node->tn_type) {                          dent.d_type = vtype2dt(de->td_node->tn_type);
                         case VBLK:  
                                 dentp->d_type = DT_BLK;  
                                 break;  
                         case VCHR:  
                                 dentp->d_type = DT_CHR;  
                                 break;  
                         case VDIR:  
                                 dentp->d_type = DT_DIR;  
                                 break;  
                         case VFIFO:  
                                 dentp->d_type = DT_FIFO;  
                                 break;  
                         case VLNK:  
                                 dentp->d_type = DT_LNK;  
                                 break;  
                         case VREG:  
                                 dentp->d_type = DT_REG;  
                                 break;  
                         case VSOCK:  
                                 dentp->d_type = DT_SOCK;  
                                 break;  
                         default:  
                                 KASSERT(false);  
                         }  
                 }                  }
                 dentp->d_namlen = de->td_namelen;                  dent.d_namlen = de->td_namelen;
                 KASSERT(de->td_namelen < sizeof(dentp->d_name));                  KASSERT(de->td_namelen < sizeof(dent.d_name));
                 memcpy(dentp->d_name, de->td_name, de->td_namelen);                  memcpy(dent.d_name, de->td_name, de->td_namelen);
                 dentp->d_name[de->td_namelen] = '\0';                  dent.d_name[de->td_namelen] = '\0';
                 dentp->d_reclen = _DIRENT_SIZE(dentp);                  dent.d_reclen = _DIRENT_SIZE(&dent);
   
                 /* Stop reading if the directory entry we are treating is                  if (dent.d_reclen > uio->uio_resid) {
                  * bigger than the amount of data that can be returned. */                          /* Exhausted UIO space. */
                 if (dentp->d_reclen > uio->uio_resid) {                          error = EJUSTRETURN;
                         error = -1;  
                         break;                          break;
                 }                  }
   
                 /*                  /* Copy out the directory entry and continue. */
                  * Copy the new dirent structure into the output buffer and                  error = uiomove(&dent, dent.d_reclen, uio);
                  * advance pointers.                  if (error) {
                  */                          break;
                 error = uiomove(dentp, dentp->d_reclen, uio);                  }
   
                 (*cntp)++;                  (*cntp)++;
                 de = TAILQ_NEXT(de, td_entries);                  de = TAILQ_NEXT(de, td_entries);
         } while (error == 0 && uio->uio_resid > 0 && de != NULL);  
   
         /* Update the offset and cache. */          } while (uio->uio_resid > 0 && de);
         if (de == NULL) {  
                 uio->uio_offset = TMPFS_DIRCOOKIE_EOF;          /* Cache the last entry or clear and mark EOF. */
                 node->tn_spec.tn_dir.tn_readdir_lastn = 0;          uio->uio_offset = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF;
                 node->tn_spec.tn_dir.tn_readdir_lastp = NULL;          node->tn_spec.tn_dir.tn_readdir_lastp = de;
         } else {  done:
                 node->tn_spec.tn_dir.tn_readdir_lastn = uio->uio_offset =          tmpfs_update(node->tn_vnode, TMPFS_UPDATE_ATIME);
                     tmpfs_dircookie(de);  
                 node->tn_spec.tn_dir.tn_readdir_lastp = de;          if (error == EJUSTRETURN) {
                   /* Exhausted UIO space - just return. */
                   error = 0;
         }          }
         node->tn_status |= TMPFS_NODE_ACCESSED;          KASSERT(error >= 0);
         kmem_free(dentp, sizeof(struct dirent));  
         return error;          return error;
 }  }
   
Line 833  tmpfs_reg_resize(struct vnode *vp, off_t
Line 911  tmpfs_reg_resize(struct vnode *vp, off_t
                         return ENOSPC;                          return ENOSPC;
                 }                  }
         } else if (newsize < oldsize) {          } else if (newsize < oldsize) {
                 int zerolen = MIN(round_page(newsize), node->tn_size) - newsize;                  size_t zerolen;
   
                   zerolen = MIN(round_page(newsize), node->tn_size) - newsize;
                 ubc_zerorange(uobj, newsize, zerolen, UBC_UNMAP_FLAG(vp));                  ubc_zerorange(uobj, newsize, zerolen, UBC_UNMAP_FLAG(vp));
         }          }
   
Line 863  tmpfs_reg_resize(struct vnode *vp, off_t
Line 942  tmpfs_reg_resize(struct vnode *vp, off_t
   
 /*  /*
  * tmpfs_chflags: change flags of the given vnode.   * tmpfs_chflags: change flags of the given vnode.
  *  
  * => Caller should perform tmpfs_update().  
  */   */
 int  int
 tmpfs_chflags(vnode_t *vp, int flags, kauth_cred_t cred, lwp_t *l)  tmpfs_chflags(vnode_t *vp, int flags, kauth_cred_t cred, lwp_t *l)
 {  {
         tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);          tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
         kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;          kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;
         int error, fs_decision = 0;          int error;
           bool changing_sysflags = false;
   
         KASSERT(VOP_ISLOCKED(vp));          KASSERT(VOP_ISLOCKED(vp));
   
Line 879  tmpfs_chflags(vnode_t *vp, int flags, ka
Line 957  tmpfs_chflags(vnode_t *vp, int flags, ka
         if (vp->v_mount->mnt_flag & MNT_RDONLY)          if (vp->v_mount->mnt_flag & MNT_RDONLY)
                 return EROFS;                  return EROFS;
   
         if (kauth_cred_geteuid(cred) != node->tn_uid) {  
                 fs_decision = EACCES;  
         }  
   
         /*          /*
          * If the new flags have non-user flags that are different than           * If the new flags have non-user flags that are different than
          * those on the node, we need special permission to change them.           * those on the node, we need special permission to change them.
          */           */
         if ((flags & SF_SETTABLE) != (node->tn_flags & SF_SETTABLE)) {          if ((flags & SF_SETTABLE) != (node->tn_flags & SF_SETTABLE)) {
                 action |= KAUTH_VNODE_WRITE_SYSFLAGS;                  action |= KAUTH_VNODE_WRITE_SYSFLAGS;
                 if (!fs_decision) {                  changing_sysflags = true;
                         fs_decision = EPERM;  
                 }  
         }          }
   
         /*          /*
Line 902  tmpfs_chflags(vnode_t *vp, int flags, ka
Line 974  tmpfs_chflags(vnode_t *vp, int flags, ka
                 action |= KAUTH_VNODE_HAS_SYSFLAGS;                  action |= KAUTH_VNODE_HAS_SYSFLAGS;
         }          }
   
         error = kauth_authorize_vnode(cred, action, vp, NULL, fs_decision);          error = kauth_authorize_vnode(cred, action, vp, NULL,
               genfs_can_chflags(cred, vp->v_type, node->tn_uid,
               changing_sysflags));
         if (error)          if (error)
                 return error;                  return error;
   
Line 915  tmpfs_chflags(vnode_t *vp, int flags, ka
Line 989  tmpfs_chflags(vnode_t *vp, int flags, ka
          *      proper permissions, and if we're here it means it's okay to           *      proper permissions, and if we're here it means it's okay to
          *      change them...           *      change them...
          */           */
         if ((action & KAUTH_VNODE_WRITE_SYSFLAGS) == 0) {          if (!changing_sysflags) {
                 /* Clear all user-settable flags and re-set them. */                  /* Clear all user-settable flags and re-set them. */
                 node->tn_flags &= SF_SETTABLE;                  node->tn_flags &= SF_SETTABLE;
                 node->tn_flags |= (flags & UF_SETTABLE);                  node->tn_flags |= (flags & UF_SETTABLE);
         } else {          } else {
                 node->tn_flags = flags;                  node->tn_flags = flags;
         }          }
         node->tn_status |= TMPFS_NODE_CHANGED;          tmpfs_update(vp, TMPFS_UPDATE_CTIME);
         VN_KNOTE(vp, NOTE_ATTRIB);          VN_KNOTE(vp, NOTE_ATTRIB);
         return 0;          return 0;
 }  }
   
 /*  /*
  * tmpfs_chmod: change access mode on the given vnode.   * tmpfs_chmod: change access mode on the given vnode.
  *  
  * => Caller should perform tmpfs_update().  
  */   */
 int  int
 tmpfs_chmod(vnode_t *vp, mode_t mode, kauth_cred_t cred, lwp_t *l)  tmpfs_chmod(vnode_t *vp, mode_t mode, kauth_cred_t cred, lwp_t *l)
Line 949  tmpfs_chmod(vnode_t *vp, mode_t mode, ka
Line 1021  tmpfs_chmod(vnode_t *vp, mode_t mode, ka
                 return EPERM;                  return EPERM;
   
         error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,          error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,
             NULL, genfs_can_chmod(vp, cred, node->tn_uid, node->tn_gid, mode));              NULL, genfs_can_chmod(vp->v_type, cred, node->tn_uid, node->tn_gid, mode));
         if (error) {          if (error) {
                 return error;                  return error;
         }          }
         node->tn_mode = (mode & ALLPERMS);          node->tn_mode = (mode & ALLPERMS);
         node->tn_status |= TMPFS_NODE_CHANGED;          tmpfs_update(vp, TMPFS_UPDATE_CTIME);
         VN_KNOTE(vp, NOTE_ATTRIB);          VN_KNOTE(vp, NOTE_ATTRIB);
         return 0;          return 0;
 }  }
Line 964  tmpfs_chmod(vnode_t *vp, mode_t mode, ka
Line 1036  tmpfs_chmod(vnode_t *vp, mode_t mode, ka
  *   *
  * => At least one of uid or gid must be different than VNOVAL.   * => At least one of uid or gid must be different than VNOVAL.
  * => Attribute is unchanged for VNOVAL case.   * => Attribute is unchanged for VNOVAL case.
  * => Caller should perform tmpfs_update().  
  */   */
 int  int
 tmpfs_chown(vnode_t *vp, uid_t uid, gid_t gid, kauth_cred_t cred, lwp_t *l)  tmpfs_chown(vnode_t *vp, uid_t uid, gid_t gid, kauth_cred_t cred, lwp_t *l)
Line 992  tmpfs_chown(vnode_t *vp, uid_t uid, gid_
Line 1063  tmpfs_chown(vnode_t *vp, uid_t uid, gid_
                 return EPERM;                  return EPERM;
   
         error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp,          error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp,
             NULL, genfs_can_chown(vp, cred, node->tn_uid, node->tn_gid, uid,              NULL, genfs_can_chown(cred, node->tn_uid, node->tn_gid, uid,
             gid));              gid));
         if (error) {          if (error) {
                 return error;                  return error;
         }          }
         node->tn_uid = uid;          node->tn_uid = uid;
         node->tn_gid = gid;          node->tn_gid = gid;
         node->tn_status |= TMPFS_NODE_CHANGED;          tmpfs_update(vp, TMPFS_UPDATE_CTIME);
         VN_KNOTE(vp, NOTE_ATTRIB);          VN_KNOTE(vp, NOTE_ATTRIB);
         return 0;          return 0;
 }  }
Line 1011  int
Line 1082  int
 tmpfs_chsize(vnode_t *vp, u_quad_t size, kauth_cred_t cred, lwp_t *l)  tmpfs_chsize(vnode_t *vp, u_quad_t size, kauth_cred_t cred, lwp_t *l)
 {  {
         tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);          tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
           const off_t length = size;
           int error;
   
         KASSERT(VOP_ISLOCKED(vp));          KASSERT(VOP_ISLOCKED(vp));
   
Line 1041  tmpfs_chsize(vnode_t *vp, u_quad_t size,
Line 1114  tmpfs_chsize(vnode_t *vp, u_quad_t size,
                 return EPERM;                  return EPERM;
         }          }
   
         /* Note: tmpfs_truncate() will raise NOTE_EXTEND and NOTE_ATTRIB. */          if (length < 0) {
         return tmpfs_truncate(vp, size);                  return EINVAL;
           }
           if (node->tn_size == length) {
                   return 0;
           }
   
           /* Note: tmpfs_reg_resize() will raise NOTE_EXTEND and NOTE_ATTRIB. */
           if ((error = tmpfs_reg_resize(vp, length)) != 0) {
                   return error;
           }
           tmpfs_update(vp, TMPFS_UPDATE_CTIME | TMPFS_UPDATE_MTIME);
           return 0;
 }  }
   
 /*  /*
Line 1071  tmpfs_chtimes(vnode_t *vp, const struct 
Line 1155  tmpfs_chtimes(vnode_t *vp, const struct 
         if (error)          if (error)
                 return error;                  return error;
   
         if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)          if (atime->tv_sec != VNOVAL) {
                 node->tn_status |= TMPFS_NODE_ACCESSED;                  node->tn_atime = *atime;
           }
         if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL)          if (mtime->tv_sec != VNOVAL) {
                 node->tn_status |= TMPFS_NODE_MODIFIED;                  node->tn_mtime = *mtime;
           }
         if (btime->tv_sec == VNOVAL && btime->tv_nsec == VNOVAL)          if (btime->tv_sec != VNOVAL) {
                 btime = NULL;                  node->tn_birthtime = *btime;
           }
         tmpfs_update(vp, atime, mtime, btime, 0);  
         VN_KNOTE(vp, NOTE_ATTRIB);          VN_KNOTE(vp, NOTE_ATTRIB);
         return 0;          return 0;
 }  }
   
 /*  /*
  * tmpfs_update: update timestamps, et al.   * tmpfs_update: update the timestamps as indicated by the flags.
  */   */
 void  void
 tmpfs_update(vnode_t *vp, const struct timespec *acc,  tmpfs_update(vnode_t *vp, unsigned tflags)
     const struct timespec *mod, const struct timespec *birth, int flags)  
 {  {
         tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);          tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
         struct timespec nowtm;          struct timespec nowtm;
   
         /* KASSERT(VOP_ISLOCKED(vp)); */          if (tflags == 0) {
   
         if (flags & UPDATE_CLOSE) {  
                 /* XXX Need to do anything special? */  
         }  
         if ((node->tn_status & TMPFS_NODE_STATUSALL) == 0) {  
                 return;                  return;
         }          }
         if (birth != NULL) {  
                 node->tn_birthtime = *birth;  
         }  
         vfs_timestamp(&nowtm);          vfs_timestamp(&nowtm);
   
         if (node->tn_status & TMPFS_NODE_ACCESSED) {          if (tflags & TMPFS_UPDATE_ATIME) {
                 node->tn_atime = acc ? *acc : nowtm;                  node->tn_atime = nowtm;
         }          }
         if (node->tn_status & TMPFS_NODE_MODIFIED) {          if (tflags & TMPFS_UPDATE_MTIME) {
                 node->tn_mtime = mod ? *mod : nowtm;                  node->tn_mtime = nowtm;
         }          }
         if (node->tn_status & TMPFS_NODE_CHANGED) {          if (tflags & TMPFS_UPDATE_CTIME) {
                 node->tn_ctime = nowtm;                  node->tn_ctime = nowtm;
         }          }
   
         node->tn_status &= ~TMPFS_NODE_STATUSALL;  
 }  
   
 int  
 tmpfs_truncate(vnode_t *vp, off_t length)  
 {  
         tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);  
         int error;  
   
         if (length < 0) {  
                 error = EINVAL;  
                 goto out;  
         }  
         if (node->tn_size == length) {  
                 error = 0;  
                 goto out;  
         }  
         error = tmpfs_reg_resize(vp, length);  
         if (error == 0) {  
                 node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;  
         }  
 out:  
         tmpfs_update(vp, NULL, NULL, NULL, 0);  
         return error;  
 }  }

Legend:
Removed from v.1.77  
changed lines
  Added in v.1.77.2.2

CVSweb <webmaster@jp.NetBSD.org>