text: /****************************************************************************
   * fs/tmpfs/fs_tmpfs.c
   *
   *   Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved.
   *   Author: Gregory Nutt <gnutt@nuttx.org>
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   * 3. Neither the name NuttX nor the names of its contributors may be
   *    used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <nuttx/config.h>
  
  #include <sys/stat.h>
  #include <sys/statfs.h>
  #include <stdint.h>
  #include <string.h>
  #include <fcntl.h>
  #include <dirent.h>
  #include <semaphore.h>
  #include <errno.h>
  #include <assert.h>
  #include <debug.h>
  
  #include <nuttx/kmalloc.h>
  #include <nuttx/fs/fs.h>
  #include <nuttx/fs/dirent.h>
  #include <nuttx/fs/ioctl.h>
  
  #include "fs_tmpfs.h"
  
  #ifndef CONFIG_DISABLE_MOUNTPOINT
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  #if CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD <= CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD
  #  warning CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD needs to be > ALLOCGUARD
  #endif
  
  #if CONFIG_FS_TMPFS_FILE_FREEGUARD <= CONFIG_FS_TMPFS_FILE_ALLOCGUARD
  #  warning CONFIG_FS_TMPFS_FILE_FREEGUARD needs to be > ALLOCGUARD
  #endif
  
  #define tmpfs_lock_file(tfo) \
             (tmpfs_lock_object((FAR struct tmpfs_object_s *)tfo))
  #define tmpfs_lock_directory(tdo) \
             (tmpfs_lock_object((FAR struct tmpfs_object_s *)tdo))
  #define tmpfs_unlock_file(tfo) \
             (tmpfs_unlock_object((FAR struct tmpfs_object_s *)tfo))
  #define tmpfs_unlock_directory(tdo) \
             (tmpfs_unlock_object((FAR struct tmpfs_object_s *)tdo))
  
  /****************************************************************************
   * Private Function Prototypes
   ****************************************************************************/
  /* TMPFS helpers */
  
  static void tmpfs_lock_reentrant(FAR struct tmpfs_sem_s *sem);
  static void tmpfs_lock(FAR struct tmpfs_s *fs);
  static void tmpfs_unlock_reentrant(FAR struct tmpfs_sem_s *sem);
  static void tmpfs_unlock(FAR struct tmpfs_s *fs);
  static void tmpfs_lock_object(FAR struct tmpfs_object_s *to);
  static void tmpfs_unlock_object(FAR struct tmpfs_object_s *to);
  static int  tmpfs_realloc_directory(FAR struct tmpfs_directory_s **tdo,
                unsigned int nentries);
  static int  tmpfs_realloc_file(FAR struct tmpfs_file_s **tfo,
                size_t newsize);
  static void tmpfs_release_lockedobject(FAR struct tmpfs_object_s *to);
  static void tmpfs_release_lockedfile(FAR struct tmpfs_file_s *tfo);
  static int  tmpfs_find_dirent(FAR struct tmpfs_directory_s *tdo,
                FAR const char *name);
  static int  tmpfs_remove_dirent(FAR struct tmpfs_directory_s *tdo,
                FAR const char *name);
  static int  tmpfs_add_dirent(FAR struct tmpfs_directory_s **tdo,
                FAR struct tmpfs_object_s *to, FAR const char *name);
  static FAR struct tmpfs_file_s *tmpfs_alloc_file(void);
  static int  tmpfs_create_file(FAR struct tmpfs_s *fs,
                FAR const char *relpath, FAR struct tmpfs_file_s **tfo);
  static FAR struct tmpfs_directory_s *tmpfs_alloc_directory(void);
  static int  tmpfs_create_directory(FAR struct tmpfs_s *fs,
                FAR const char *relpath, FAR struct tmpfs_directory_s **tdo);
  static int  tmpfs_find_object(FAR struct tmpfs_s *fs,
                FAR const char *relpath, FAR struct tmpfs_object_s **object,
                FAR struct tmpfs_directory_s **parent);
  static int  tmpfs_find_file(FAR struct tmpfs_s *fs,
                FAR const char *relpath,
                FAR struct tmpfs_file_s **tfo,
                FAR struct tmpfs_directory_s **parent);
  static int  tmpfs_find_directory(FAR struct tmpfs_s *fs,
                FAR const char *relpath,
                FAR struct tmpfs_directory_s **tdo,
                FAR struct tmpfs_directory_s **parent);
  static int  tmpfs_statfs_callout(FAR struct tmpfs_directory_s *tdo,
                unsigned int index, FAR void *arg);
  static int  tmpfs_free_callout(FAR struct tmpfs_directory_s *tdo,
                unsigned int index, FAR void *arg);
  static int  tmpfs_foreach(FAR struct tmpfs_directory_s *tdo,
                tmpfs_foreach_t callout, FAR void *arg);
  
  /* File system operations */
  
  static int  tmpfs_open(FAR struct file *filep, FAR const char *relpath,
                int oflags, mode_t mode);
  static int  tmpfs_close(FAR struct file *filep);
  static ssize_t tmpfs_read(FAR struct file *filep, FAR char *buffer,
                size_t buflen);
  static ssize_t tmpfs_write(FAR struct file *filep, FAR const char *buffer,
                size_t buflen);
  static off_t tmpfs_seek(FAR struct file *filep, off_t offset, int whence);
  static int  tmpfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
  static int  tmpfs_dup(FAR const struct file *oldp, FAR struct file *newp);
  static int  tmpfs_fstat(FAR const struct file *filep, FAR struct stat *buf);
  
  static int  tmpfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
                FAR struct fs_dirent_s *dir);
  static int  tmpfs_closedir(FAR struct inode *mountpt,
                FAR struct fs_dirent_s *dir);
  static int  tmpfs_readdir(FAR struct inode *mountpt,
                FAR struct fs_dirent_s *dir);
  static int  tmpfs_rewinddir(FAR struct inode *mountpt,
                FAR struct fs_dirent_s *dir);
  static int  tmpfs_bind(FAR struct inode *blkdriver, FAR const void *data,
                FAR void **handle);
  static int  tmpfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
                unsigned int flags);
  static int  tmpfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf);
  static int  tmpfs_unlink(FAR struct inode *mountpt, FAR const char *relpath);
  static int  tmpfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
                mode_t mode);
  static int  tmpfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath);
  static int  tmpfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
                FAR const char *newrelpath);
  static void tmpfs_stat_common(FAR struct tmpfs_object_s *to,
                                FAR struct stat *buf);
  static int  tmpfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
                FAR struct stat *buf);
  
  /****************************************************************************
   * Public Data
   ****************************************************************************/
  
  const struct mountpt_operations tmpfs_operations =
  {
    tmpfs_open,       /* open */
    tmpfs_close,      /* close */
    tmpfs_read,       /* read */
    tmpfs_write,      /* write */
    tmpfs_seek,       /* seek */
    tmpfs_ioctl,      /* ioctl */
    NULL,             /* sync */
    tmpfs_dup,        /* dup */
    tmpfs_fstat,      /* fstat */
    tmpfs_opendir,    /* opendir */
    tmpfs_closedir,   /* closedir */
    tmpfs_readdir,    /* readdir */
    tmpfs_rewinddir,  /* rewinddir */
    tmpfs_bind,       /* bind */
    tmpfs_unbind,     /* unbind */
    tmpfs_statfs,     /* statfs */
    tmpfs_unlink,     /* unlink */
    tmpfs_mkdir,      /* mkdir */
    tmpfs_rmdir,      /* rmdir */
    tmpfs_rename,     /* rename */
    tmpfs_stat,       /* stat */
  };
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: tmpfs_lock_reentrant
   ****************************************************************************/
  
  static void tmpfs_lock_reentrant(FAR struct tmpfs_sem_s *sem)
  {
    pid_t me;
  
    /* Do we already hold the semaphore? */
  
    me = getpid();
    if (me == sem->ts_holder)
      {
        /* Yes... just increment the count */
  
        sem->ts_count++;
        DEBUGASSERT(sem->ts_count > 0);
      }
  
    /* Take the semaphore (perhaps waiting) */
  
    else
      {
        while (sem_wait(&sem->ts_sem) != 0)
          {
            /* The only case that an error should occr here is if
             * the wait was awakened by a signal.
             */
  
            DEBUGASSERT(get_errno() == EINTR);
          }
  
        /* No we hold the semaphore */
  
        sem->ts_holder = me;
        sem->ts_count  = 1;
      }
  }
  
  /****************************************************************************
   * Name: tmpfs_lock
   ****************************************************************************/
  
  static void tmpfs_lock(FAR struct tmpfs_s *fs)
  {
    tmpfs_lock_reentrant(&fs->tfs_exclsem);
  }
  
  /****************************************************************************
   * Name: tmpfs_lock_object
   ****************************************************************************/
  
  static void tmpfs_lock_object(FAR struct tmpfs_object_s *to)
  {
    tmpfs_lock_reentrant(&to->to_exclsem);
  }
  
  /****************************************************************************
   * Name: tmpfs_unlock_reentrant
   ****************************************************************************/
  
  static void tmpfs_unlock_reentrant(FAR struct tmpfs_sem_s *sem)
  {
    DEBUGASSERT(sem->ts_holder == getpid());
  
    /* Is this our last count on the semaphore? */
  
    if (sem->ts_count > 1)
      {
        /* No.. just decrement the count */
  
        sem->ts_count--;
      }
  
    /* Yes.. then we can really release the semaphore */
  
    else
      {
        sem->ts_holder = TMPFS_NO_HOLDER;
        sem->ts_count  = 0;
        sem_post(&sem->ts_sem);
      }
  }
  
  /****************************************************************************
   * Name: tmpfs_unlock
   ****************************************************************************/
  
  static void tmpfs_unlock(FAR struct tmpfs_s *fs)
  {
    tmpfs_unlock_reentrant(&fs->tfs_exclsem);
  }
  
  /****************************************************************************
   * Name: tmpfs_unlock_object
   ****************************************************************************/
  
  static void tmpfs_unlock_object(FAR struct tmpfs_object_s *to)
  {
    tmpfs_unlock_reentrant(&to->to_exclsem);
  }
  
  /****************************************************************************
   * Name: tmpfs_realloc_directory
   ****************************************************************************/
  
  static int tmpfs_realloc_directory(FAR struct tmpfs_directory_s **tdo,
                                     unsigned int nentries)
  {
    FAR struct tmpfs_directory_s *oldtdo = *tdo;
    FAR struct tmpfs_directory_s *newtdo;
    size_t objsize;
    int ret = oldtdo->tdo_nentries;
  
    /* Get the new object size */
  
    objsize = SIZEOF_TMPFS_DIRECTORY(nentries);
    if (objsize <= oldtdo->tdo_alloc)
      {
        /* Already big enough.
         * REVISIT: Missing logic to shrink directory objects.
         */
  
        oldtdo->tdo_nentries = nentries;
        return ret;
      }
  
    /* Added some additional amount to the new size to account frequent
     * reallocations.
     */
  
    objsize += CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD;
  
    /* Realloc the directory object */
  
    newtdo = (FAR struct tmpfs_directory_s *)kmm_realloc(oldtdo, objsize);
    if (newtdo == NULL)
      {
        return -ENOMEM;
      }
  
    /* Adjust the reference in the parent directory entry */
  
    DEBUGASSERT(newtdo->tdo_dirent);
    newtdo->tdo_dirent->tde_object = (FAR struct tmpfs_object_s *)newtdo;
  
    /* Return the new address of the reallocated directory object */
  
    newtdo->tdo_alloc    = objsize;
    newtdo->tdo_nentries = nentries;
    *tdo                 = newtdo;
  
    /* Adjust the reference in the parent directory entry */
  
    DEBUGASSERT(newtdo->tdo_dirent);
    newtdo->tdo_dirent->tde_object = (FAR struct tmpfs_object_s *)newtdo;
  
    /* Return the index to the first, newly allocated directory entry */
  
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_realloc_file
   ****************************************************************************/
  
  static int tmpfs_realloc_file(FAR struct tmpfs_file_s **tfo,
                                size_t newsize)
  {
    FAR struct tmpfs_file_s *oldtfo = *tfo;
    FAR struct tmpfs_file_s *newtfo;
    size_t objsize;
    size_t allocsize;
    size_t delta;
  
    /* Check if the current allocation is sufficent */
  
    objsize = SIZEOF_TMPFS_FILE(newsize);
  
    /* Are we growing or shrinking the object? */
  
    if (objsize <= oldtfo->tfo_alloc)
      {
        /* Shrinking ... Shrink unconditionally if the size is shrinking to
         * zero.
         */
  
        if (newsize > 0)
          {
            /* Otherwise, don't realloc unless the object has shrunk by a
             * lot.
             */
  
            delta = oldtfo->tfo_alloc - objsize;
            if (delta <= CONFIG_FS_TMPFS_FILE_FREEGUARD)
              {
                /* Hasn't shrunk enough.. Return doing nothing for now */
  
                oldtfo->tfo_size = newsize;
                return OK;
              }
          }
      }
  
    /* Added some additional amount to the new size to account frequent
     * reallocations.
     */
  
    allocsize = objsize + CONFIG_FS_TMPFS_FILE_ALLOCGUARD;
  
    /* Realloc the file object */
  
    newtfo = (FAR struct tmpfs_file_s *)kmm_realloc(oldtfo, allocsize);
    if (newtfo == NULL)
      {
        return -ENOMEM;
      }
  
    /* Adjust the reference in the parent directory entry */
  
    DEBUGASSERT(newtfo->tfo_dirent);
    newtfo->tfo_dirent->tde_object = (FAR struct tmpfs_object_s *)newtfo;
  
    /* Return the new address of the reallocated file object */
  
    newtfo->tfo_alloc = allocsize;
    newtfo->tfo_size  = newsize;
    *tfo              = newtfo;
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_release_lockedobject
   ****************************************************************************/
  
  static void tmpfs_release_lockedobject(FAR struct tmpfs_object_s *to)
  {
    DEBUGASSERT(to && to->to_refs > 0);
  
    /* Is this a file object? */
  
    if (to->to_type == TMPFS_REGULAR)
      {
        tmpfs_release_lockedfile((FAR struct tmpfs_file_s *)to);
      }
    else
      {
        to->to_refs--;
        tmpfs_unlock_object(to);
      }
  }
  
  /****************************************************************************
   * Name: tmpfs_release_lockedfile
   ****************************************************************************/
  
  static void tmpfs_release_lockedfile(FAR struct tmpfs_file_s *tfo)
  {
    DEBUGASSERT(tfo && tfo->tfo_refs > 0);
  
    /* If there are no longer any references to the file and the file has been
     * unlinked from its parent directory, then free the file object now.
     */
  
    if (tfo->tfo_refs == 1 && (tfo->tfo_flags & TFO_FLAG_UNLINKED) != 0)
      {
        sem_destroy(&tfo->tfo_exclsem.ts_sem);
        kmm_free(tfo);
      }
  
    /* Otherwise, just decrement the reference count on the file object */
  
    else
      {
        tfo->tfo_refs--;
        tmpfs_unlock_file(tfo);
      }
  }
  
  /****************************************************************************
   * Name: tmpfs_find_dirent
   ****************************************************************************/
  
  static int tmpfs_find_dirent(FAR struct tmpfs_directory_s *tdo,
                               FAR const char *name)
  {
    int i;
  
    /* Search the list of directory entries for a match */
  
    for (i = 0;
         i < tdo->tdo_nentries &&
         strcmp(tdo->tdo_entry[i].tde_name, name) != 0;
         i++);
  
    /* Return what we found, if anything */
  
    return i < tdo->tdo_nentries ? i : -ENOENT;
  }
  
  /****************************************************************************
   * Name: tmpfs_remove_dirent
   ****************************************************************************/
  
  static int tmpfs_remove_dirent(FAR struct tmpfs_directory_s *tdo,
                                 FAR const char *name)
  {
    int index;
    int last;
  
    /* Search the list of directory entries for a match */
  
    index = tmpfs_find_dirent(tdo, name);
    if (index < 0)
      {
        return index;
      }
  
    /* Free the object name */
  
    if (tdo->tdo_entry[index].tde_name != NULL)
      {
        kmm_free(tdo->tdo_entry[index].tde_name);
      }
  
    /* Remove by replacing this entry with the final directory entry */
  
    last = tdo->tdo_nentries - 1;
    if (index != last)
      {
        FAR struct tmpfs_dirent_s *newtde;
        FAR struct tmpfs_dirent_s *oldtde;
        FAR struct tmpfs_object_s *to;
  
        /* Move the directory entry */
  
        newtde             = &tdo->tdo_entry[index];
        oldtde             = &tdo->tdo_entry[last];
        to                 = oldtde->tde_object;
  
        newtde->tde_object = to;
        newtde->tde_name   = oldtde->tde_name;
  
        /* Reset the backward link to the directory entry */
  
        to->to_dirent      = newtde;
      }
  
    /* And decrement the count of directory entries */
  
    tdo->tdo_nentries = last;
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_add_dirent
   ****************************************************************************/
  
  static int tmpfs_add_dirent(FAR struct tmpfs_directory_s **tdo,
                              FAR struct tmpfs_object_s *to,
                              FAR const char *name)
  {
    FAR struct tmpfs_directory_s *oldtdo;
    FAR struct tmpfs_directory_s *newtdo;
    FAR struct tmpfs_dirent_s *tde;
    FAR char *newname;
    unsigned int nentries;
    int index;
  
    /* Copy the name string so that it will persist as long as the
     * directory entry.
     */
  
    newname = strdup(name);
    if (newname == NULL)
      {
        return -ENOMEM;
      }
  
    /* Get the new number of entries */
  
    oldtdo = *tdo;
    nentries = oldtdo->tdo_nentries + 1;
  
    /* Reallocate the directory object (if necessary) */
  
    index = tmpfs_realloc_directory(tdo, nentries);
    if (index < 0)
      {
        kmm_free(newname);
        return index;
      }
  
    /* Save the new object info in the new directory entry */
  
    newtdo          = *tdo;
    tde             = &newtdo->tdo_entry[index];
    tde->tde_object = to;
    tde->tde_name   = newname;
  
    /* Add backward link to the directory entry to the object */
  
    to->to_dirent  = tde;
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_alloc_file
   ****************************************************************************/
  
  static FAR struct tmpfs_file_s *tmpfs_alloc_file(void)
  {
    FAR struct tmpfs_file_s *tfo;
    size_t allocsize;
  
    /* Create a new zero length file object */
  
    allocsize = SIZEOF_TMPFS_FILE(CONFIG_FS_TMPFS_FILE_ALLOCGUARD);
    tfo = (FAR struct tmpfs_file_s *)kmm_malloc(allocsize);
    if (tfo == NULL)
      {
        return NULL;
      }
  
    /* Initialize the new file object.  NOTE that the initial state is
     * locked with one reference count.
     */
  
    tfo->tfo_alloc = allocsize;
    tfo->tfo_type  = TMPFS_REGULAR;
    tfo->tfo_refs  = 1;
    tfo->tfo_flags = 0;
    tfo->tfo_size  = 0;
  
    tfo->tfo_exclsem.ts_holder = getpid();
    tfo->tfo_exclsem.ts_count  = 1;
    sem_init(&tfo->tfo_exclsem.ts_sem, 0, 0);
  
    return tfo;
  }
  
  /****************************************************************************
   * Name: tmpfs_create_file
   ****************************************************************************/
  
  static int tmpfs_create_file(FAR struct tmpfs_s *fs,
                               FAR const char *relpath,
                               FAR struct tmpfs_file_s **tfo)
  {
    FAR struct tmpfs_directory_s *parent;
    FAR struct tmpfs_file_s *newtfo;
    FAR char *copy;
    FAR char *name;
    int ret;
  
    /* Duplicate the path variable so that we can modify it */
  
    copy = strdup(relpath);
    if (copy == NULL)
      {
        return -ENOMEM;
      }
  
    /* Separate the path into the file name and the path to the parent
     * directory.
     */
  
    name = strrchr(copy, '/');
    if (name == NULL)
      {
        /* No subdirectories... use the root directory */
  
        name   = copy;
        parent = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
  
        /* Lock the root directory to emulate the behavior of tmpfs_find_directory() */
  
        tmpfs_lock_directory(parent);
        parent->tdo_refs++;
      }
    else
      {
        /* Terminate the parent directory path */
  
        *name++ = '\0';
  
        /* Locate the parent directory that should contain this name.
         * On success, tmpfs_find_directory() will lock the parent
         * directory and increment the reference count.
         */
  
        ret = tmpfs_find_directory(fs, copy, &parent, NULL);
        if (ret < 0)
          {
            goto errout_with_copy;
          }
      }
  
    /* Verify that no object of this name already exists in the directory */
  
    ret = tmpfs_find_dirent(parent, name);
    if (ret != -ENOENT)
      {
        /* Something with this name already exists in the directory.
         * OR perhaps some fatal error occurred.
         */
  
        if (ret >= 0)
          {
            ret = -EEXIST;
          }
  
        goto errout_with_parent;
      }
  
    /* Allocate an empty file.  The initial state of the file is locked with one
     * reference count.
     */
  
    newtfo = tmpfs_alloc_file();
    if (newtfo == NULL)
      {
        ret = -ENOMEM;
        goto errout_with_parent;
      }
  
    /* Then add the new, empty file to the directory */
  
    ret = tmpfs_add_dirent(&parent, (FAR struct tmpfs_object_s *)newtfo, name);
    if (ret < 0)
      {
        goto errout_with_file;
      }
  
    /* Release the reference and lock on the parent directory */
  
    parent->tdo_refs--;
    tmpfs_unlock_directory(parent);
  
    /* Free the copy of the relpath and return success */
  
    kmm_free(copy);
    *tfo = newtfo;
    return OK;
  
  /* Error exits */
  
  errout_with_file:
    sem_destroy(&newtfo->tfo_exclsem.ts_sem);
    kmm_free(newtfo);
  
  errout_with_parent:
    parent->tdo_refs--;
    tmpfs_unlock_directory(parent);
  
  errout_with_copy:
    kmm_free(copy);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_alloc_directory
   ****************************************************************************/
  
  static FAR struct tmpfs_directory_s *tmpfs_alloc_directory(void)
  {
    FAR struct tmpfs_directory_s *tdo;
    size_t allocsize;
    unsigned int nentries;
  
    /* Convert the pre-allocated memory to a number of directory entries */
  
    nentries = (CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD +
                sizeof(struct tmpfs_dirent_s) - 1) /
               sizeof(struct tmpfs_dirent_s);
  
    /* Create a new zero length directory object */
  
    allocsize = SIZEOF_TMPFS_DIRECTORY(nentries);
    tdo = (FAR struct tmpfs_directory_s *)kmm_malloc(allocsize);
    if (tdo == NULL)
      {
        return NULL;
      }
  
    /* Initialize the new directory object */
  
    tdo->tdo_alloc    = allocsize;
    tdo->tdo_type     = TMPFS_DIRECTORY;
    tdo->tdo_refs     = 0;
    tdo->tdo_nentries = 0;
  
    tdo->tdo_exclsem.ts_holder = TMPFS_NO_HOLDER;
    tdo->tdo_exclsem.ts_count  = 0;
    sem_init(&tdo->tdo_exclsem.ts_sem, 0, 1);
  
    return tdo;
  }
  
  /****************************************************************************
   * Name: tmpfs_create_directory
   ****************************************************************************/
  
  static int tmpfs_create_directory(FAR struct tmpfs_s *fs,
                                    FAR const char *relpath,
                                    FAR struct tmpfs_directory_s **tdo)
  {
    FAR struct tmpfs_directory_s *parent;
    FAR struct tmpfs_directory_s *newtdo;
    FAR char *copy;
    FAR char *name;
    int ret;
  
    /* Duplicate the path variable so that we can modify it */
  
    copy = strdup(relpath);
    if (copy == NULL)
      {
        return -ENOMEM;
      }
  
    /* Separate the path into the file name and the path to the parent
     * directory.
     */
  
    name = strrchr(copy, '/');
    if (name == NULL)
      {
        /* No subdirectories... use the root directory */
  
        name   = copy;
        parent = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
  
        tmpfs_lock_directory(parent);
        parent->tdo_refs++;
      }
    else
      {
        /* Terminate the parent directory path */
  
        *name++ = '\0';
  
        /* Locate the parent directory that should contain this name.
         * On success, tmpfs_find_directory() will lockthe parent
         * directory and increment the reference count.
         */
  
        ret = tmpfs_find_directory(fs, copy, &parent, NULL);
        if (ret < 0)
          {
            goto errout_with_copy;
          }
      }
  
    /* Verify that no object of this name already exists in the directory */
  
    ret = tmpfs_find_dirent(parent, name);
    if (ret != -ENOENT)
      {
        /* Something with this name already exists in the directory.
         * OR perhaps some fatal error occurred.
         */
  
        if (ret >= 0)
          {
            ret = -EEXIST;
          }
  
        goto errout_with_parent;
      }
  
    /* Allocate an empty directory object.  NOTE that there is no reference on
     * the new directory and the object is not locked.
     */
  
    newtdo = tmpfs_alloc_directory();
    if (newtdo == NULL)
      {
        ret = -ENOMEM;
        goto errout_with_parent;
      }
  
    /* Then add the new, empty file to the directory */
  
    ret = tmpfs_add_dirent(&parent, (FAR struct tmpfs_object_s *)newtdo, name);
    if (ret < 0)
      {
        goto errout_with_directory;
      }
  
    /* Free the copy of the relpath, release our reference to the parent directory,
     * and return success
     */
  
    parent->tdo_refs--;
    tmpfs_unlock_directory(parent);
    kmm_free(copy);
  
    /* Return the (unlocked, unreferenced) directory object to the caller */
  
    if (tdo != NULL)
      {
        *tdo = newtdo;
      }
  
    return OK;
  
  /* Error exits */
  
  errout_with_directory:
    sem_destroy(&newtdo->tdo_exclsem.ts_sem);
    kmm_free(newtdo);
  
  errout_with_parent:
    parent->tdo_refs--;
    tmpfs_unlock_directory(parent);
  
  errout_with_copy:
    kmm_free(copy);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_find_object
   ****************************************************************************/
  
  static int tmpfs_find_object(FAR struct tmpfs_s *fs,
                               FAR const char *relpath,
                               FAR struct tmpfs_object_s **object,
                               FAR struct tmpfs_directory_s **parent)
  {
    FAR struct tmpfs_object_s *to = NULL;
    FAR struct tmpfs_directory_s *tdo = NULL;
    FAR struct tmpfs_directory_s *next_tdo;
    FAR char *segment;
    FAR char *next_segment;
    FAR char *tkptr;
    FAR char *copy;
    int index;
  
    /* Make a copy of the path (so that we can modify it via strtok) */
  
    copy = strdup(relpath);
    if (copy == NULL)
      {
        return -ENOMEM;
      }
  
    /* Traverse the file system for any object with the matching name */
  
    to       = fs->tfs_root.tde_object;
    next_tdo = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
  
    for (segment =  strtok_r(copy, "/", &tkptr);
         segment != NULL;
         segment = next_segment)
      {
        /* Get the next segment after the one we are currently working on.
         * This will be NULL is we are working on the final segment of the
         * relpath.
         */
  
        next_segment = strtok_r(NULL, "/", &tkptr);
  
        /* Search the next directory. */
  
        tdo = next_tdo;
  
        /* Find the TMPFS object with the next segment name in the current
         * directory.
         */
  
        index = tmpfs_find_dirent(tdo, segment);
        if (index < 0)
          {
            /* No object with this name exists in the directory. */
  
            kmm_free(copy);
            return index;
          }
  
        to = tdo->tdo_entry[index].tde_object;
  
        /* Is this object another directory? */
  
        if (to->to_type != TMPFS_DIRECTORY)
          {
            /* No.  Was this the final segment in the path? */
  
            if (next_segment == NULL)
              {
                /* Then we can break out of the loop now */
  
                 break;
              }
  
            /* No, this was not the final segement of the relpath.
             * We cannot continue the search if any of the intermediate
             * segments do no correspond to directories.
             */
  
            kmm_free(copy);
            return -ENOTDIR;
          }
  
        /* Search this directory for the next segement.  If we
         * exit the loop, tdo will still refer to the parent
         * directory of to.
         */
  
        next_tdo = (FAR struct tmpfs_directory_s *)to;
      }
  
    /* When we exit this loop (successfully), to will point to the TMPFS
     * object associated with the terminal segment of the relpath.
     * Increment the reference count on the located object.
     */
  
    /* Free the dup'ed string */
  
    kmm_free(copy);
  
    /* Return what we found */
  
    if (parent)
      {
        if (tdo != NULL)
          {
            /* Get exclusive access to the parent and increment the reference
             * count on the object.
             */
  
            tmpfs_lock_directory(tdo);
            tdo->tdo_refs++;
          }
  
        *parent = tdo;
      }
  
    if (object)
      {
        if (to != NULL)
          {
            /* Get exclusive access to the object and increment the reference
             * count on the object.
             */
  
            tmpfs_lock_object(to);
            to->to_refs++;
          }
  
        *object = to;
      }
  
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_find_file
   ****************************************************************************/
  
  static int tmpfs_find_file(FAR struct tmpfs_s *fs,
                             FAR const char *relpath,
                             FAR struct tmpfs_file_s **tfo,
                             FAR struct tmpfs_directory_s **parent)
  {
    FAR struct tmpfs_object_s *to;
    int ret;
  
    /* Find the object at this path.  If successful, tmpfs_find_object() will
     * lock both the object and the parent directory and will increment the
     * reference count on both.
     */
  
    ret = tmpfs_find_object(fs, relpath, &to, parent);
    if (ret >= 0)
      {
        /* We found it... but is it a regular file? */
  
        if (to->to_type != TMPFS_REGULAR)
          {
            /* No... unlock the object and its parent and return an error */
  
            tmpfs_release_lockedobject(to);
  
            if (parent)
              {
                FAR struct tmpfs_directory_s *tdo = *parent;
  
                tdo->tdo_refs--;
                tmpfs_unlock_directory(tdo);
              }
  
            ret = -EISDIR;
          }
  
        /* Return the verified file object */
  
        *tfo = (FAR struct tmpfs_file_s *)to;
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_find_directory
   ****************************************************************************/
  
  static int tmpfs_find_directory(FAR struct tmpfs_s *fs,
                             FAR const char *relpath,
                             FAR struct tmpfs_directory_s **tdo,
                             FAR struct tmpfs_directory_s **parent)
  {
    FAR struct tmpfs_object_s *to;
    int ret;
  
    /* Find the object at this path */
  
    ret = tmpfs_find_object(fs, relpath, &to, parent);
    if (ret >= 0)
      {
        /* We found it... but is it a regular file? */
  
        if (to->to_type != TMPFS_DIRECTORY)
          {
            /* No... unlock the object and its parent and return an error */
  
            tmpfs_release_lockedobject(to);
  
            if (parent)
              {
                FAR struct tmpfs_directory_s *tmptdo = *parent;
  
                tmptdo->tdo_refs--;
                tmpfs_unlock_directory(tmptdo);
              }
  
            ret = -ENOTDIR;
          }
  
        /* Return the verified file object */
  
        *tdo = (FAR struct tmpfs_directory_s *)to;
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_statfs_callout
   ****************************************************************************/
  
  static int tmpfs_statfs_callout(FAR struct tmpfs_directory_s *tdo,
                                  unsigned int index, FAR void *arg)
  {
    FAR struct tmpfs_object_s *to;
    FAR struct tmpfs_statfs_s *tmpbuf;
  
    DEBUGASSERT(tdo != NULL && arg != NULL && index < tdo->tdo_nentries);
  
    to     = tdo->tdo_entry[index].tde_object;
    tmpbuf = (FAR struct tmpfs_statfs_s *)arg;
  
    DEBUGASSERT(to != NULL);
  
    /* Accumulate statistics.  Save the total memory allocted for this object. */
  
    tmpbuf->tsf_alloc += to->to_alloc;
  
    /* Is this directory entry a file object? */
  
    if (to->to_type == TMPFS_REGULAR)
      {
        FAR struct tmpfs_file_s *tmptfo;
  
        /* It is a file object.  Increment the number of files and update the
         * amount of memory in use.
         */
  
        tmptfo             = (FAR struct tmpfs_file_s *)to;
        tmpbuf->tsf_inuse += tmptfo->tfo_size;
        tmpbuf->tsf_files++;
      }
    else /* if (to->to_type == TMPFS_DIRECTORY) */
      {
        FAR struct tmpfs_directory_s *tmptdo;
        size_t inuse;
        size_t avail;
  
        /* It is a directory object.  Update the amount of memory in use
         * for the directory and estimate the number of free directory nodes.
         */
  
        tmptdo = (FAR struct tmpfs_directory_s *)to;
        inuse  = SIZEOF_TMPFS_DIRECTORY(tmptdo->tdo_nentries);
        avail  = tmptdo->tdo_alloc - inuse;
  
        tmpbuf->tsf_inuse += inuse;
        tmpbuf->tsf_ffree += avail / sizeof(struct tmpfs_dirent_s);
      }
  
    return TMPFS_CONTINUE;
  }
  
  /****************************************************************************
   * Name: tmpfs_free_callout
   ****************************************************************************/
  
  static int tmpfs_free_callout(FAR struct tmpfs_directory_s *tdo,
                                unsigned int index, FAR void *arg)
  {
    FAR struct tmpfs_dirent_s *tde;
    FAR struct tmpfs_object_s *to;
    FAR struct tmpfs_file_s *tfo;
    unsigned int last;
  
    /* Free the object name */
  
    if (tdo->tdo_entry[index].tde_name != NULL)
      {
        kmm_free(tdo->tdo_entry[index].tde_name);
      }
  
    /* Remove by replacing this entry with the final directory entry */
  
    tde  = &tdo->tdo_entry[index];
    to   = tde->tde_object;
    last = tdo->tdo_nentries - 1;
  
    if (index != last)
      {
        FAR struct tmpfs_dirent_s *oldtde;
        FAR struct tmpfs_object_s *oldto;
  
        /* Move the directory entry */
  
        oldtde           = &tdo->tdo_entry[last];
        oldto            = oldtde->tde_object;
  
        tde->tde_object  = oldto;
        tde->tde_name    = oldtde->tde_name;
  
        /* Reset the backward link to the directory entry */
  
        oldto->to_dirent = tde;
      }
  
    /* And decrement the count of directory entries */
  
    tdo->tdo_nentries = last;
  
    /* Is this directory entry a file object? */
  
    if (to->to_type == TMPFS_REGULAR)
      {
        tfo = (FAR struct tmpfs_file_s *)to;
  
        /* Are there references to the file? */
  
        if (tfo->tfo_refs > 0)
          {
            /* Yes.. We cannot delete the file now.  Just mark it as unlinked. */
  
            tfo->tfo_flags |= TFO_FLAG_UNLINKED;
            return TMPFS_UNLINKED;
          }
      }
  
    /* Free the object now */
  
    sem_destroy(&to->to_exclsem.ts_sem);
    kmm_free(to);
    return TMPFS_DELETED;
  }
  
  /****************************************************************************
   * Name: tmpfs_foreach
   ****************************************************************************/
  
  static int tmpfs_foreach(FAR struct tmpfs_directory_s *tdo,
                           tmpfs_foreach_t callout, FAR void *arg)
  {
    FAR struct tmpfs_object_s *to;
    unsigned int index;
    int ret;
  
    /* Visit each directory entry */
  
    for (index = 0; index < tdo->tdo_nentries; )
      {
        /* Lock the object and take a reference */
  
        to = tdo->tdo_entry[index].tde_object;
        tmpfs_lock_object(to);
        to->to_refs++;
  
        /* Is the next entry a directory? */
  
        if (to->to_type == TMPFS_DIRECTORY)
          {
            FAR struct tmpfs_directory_s *next =
              (FAR struct tmpfs_directory_s *)to;
  
            /* Yes.. traverse its children first in the case the final
             * action will be to delete the directory.
             */
  
            ret = tmpfs_foreach(next, tmpfs_free_callout, NULL);
            if (ret < 0)
              {
                return -ECANCELED;
              }
          }
  
        /* Perform the callout */
  
        ret = callout(tdo, index, arg);
        switch (ret)
          {
           case TMPFS_CONTINUE:    /* Continue enumeration */
             /* Release the object and index to the next entry */
  
             tmpfs_release_lockedobject(to);
             index++;
             break;
  
           case TMPFS_HALT:        /* Stop enumeration */
             /* Release the object and cancel the traversal */
  
             tmpfs_release_lockedobject(to);
             return -ECANCELED;
  
           case TMPFS_UNLINKED:    /* Only the directory entry was deleted */
             /* Release the object and continue with the same index */
  
             tmpfs_release_lockedobject(to);
  
           case TMPFS_DELETED:     /* Object and directory entry deleted */
             break;                /* Continue with the same index */
          }
      }
  
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_open
   ****************************************************************************/
  
  static int tmpfs_open(FAR struct file *filep, FAR const char *relpath,
                        int oflags, mode_t mode)
  {
    FAR struct inode *inode;
    FAR struct tmpfs_s *fs;
    FAR struct tmpfs_file_s *tfo;
    off_t offset;
    int ret;
  
    finfo("filep: %p\n", filep);
    DEBUGASSERT(filep->f_priv == NULL && filep->f_inode != NULL);
  
    /* Get the mountpoint inode reference from the file structure and the
     * mountpoint private data from the inode structure
     */
  
    inode = filep->f_inode;
    fs    = inode->i_private;
  
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Skip over any leading directory separators (shouldn't be any) */
  
    for (; *relpath == '/'; relpath++);
  
    /* Find the file object associated with this relative path.
     * If successful, this action will lock both the parent directory and
     * the file object, adding one to the reference count of both.
     * In the event that -ENOENT, there will still be a reference and
     * lock on the returned directory.
     */
  
    ret = tmpfs_find_file(fs, relpath, &tfo, NULL);
    if (ret >= 0)
      {
        /* The file exists.  We hold the lock and one reference count
         * on the file object.
         *
         * It would be an error if we are asked to create it exclusively
         */
  
        if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
          {
            /* Already exists -- can't create it exclusively */
  
            ret = -EEXIST;
            goto errout_with_filelock;
          }
  
        /* Check if the caller has sufficient privileges to open the file */
        /* REVISIT: No file protection implemented */
  
        /* If O_TRUNC is specified and the file is opened for writing,
         * then truncate the file.  This operation requires that the file is
         * writeable, but we have already checked that. O_TRUNC without write
         * access is ignored.
         */
  
        if ((oflags & (O_TRUNC | O_WRONLY)) == (O_TRUNC | O_WRONLY))
          {
            /* Truncate the file to zero length (if it is not already
             * zero length)
             */
  
            if (tfo->tfo_size > 0)
              {
                ret = tmpfs_realloc_file(&tfo, 0);
                if (ret < 0)
                  {
                    goto errout_with_filelock;
                  }
              }
          }
      }
  
    /* ENOENT would be returned by tmpfs_find_file() if the full directory
     * path was found, but the file was not found in the final directory.
     */
  
    else if (ret == -ENOENT)
      {
        /* The file does not exist.  Were we asked to create it? */
  
        if ((oflags & O_CREAT) == 0)
          {
             /* No.. then we fail with -ENOENT */
  
             ret = -ENOENT;
             goto errout_with_fslock;
         }
  
        /* Yes.. create the file object.  There will be a reference and a lock
         * on the new file object.
         */
  
        ret = tmpfs_create_file(fs, relpath, &tfo);
        if (ret < 0)
          {
            goto errout_with_fslock;
          }
     }
  
    /* Some other error occurred */
  
    else
      {
        goto errout_with_fslock;
      }
  
    /* Save the struct tmpfs_file_s instance as the file private data */
  
    filep->f_priv = tfo;
  
    /* In write/append mode, we need to set the file pointer to the end of the
     * file.
     */
  
    offset = 0;
    if ((oflags & (O_APPEND | O_WRONLY)) == (O_APPEND | O_WRONLY))
      {
        offset = tfo->tfo_size;
      }
  
    filep->f_pos = offset;
  
    /* Unlock the file file object, but retain the reference count */
  
    tmpfs_unlock_file(tfo);
    tmpfs_unlock(fs);
    return OK;
  
    /* Error exits */
  
  errout_with_filelock:
    tmpfs_release_lockedfile(tfo);
  
  errout_with_fslock:
    tmpfs_unlock(fs);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_close
   ****************************************************************************/
  
  static int tmpfs_close(FAR struct file *filep)
  {
    FAR struct tmpfs_file_s *tfo;
  
    finfo("filep: %p\n", filep);
    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
  
    /* Recover our private data from the struct file instance */
  
    tfo = filep->f_priv;
  
    /* Get exclusive access to the file */
  
    tmpfs_lock_file(tfo);
  
    /* Decrement the reference count on the file */
  
    DEBUGASSERT(tfo->tfo_refs > 0);
    if (tfo->tfo_refs > 0)
      {
        tfo->tfo_refs--;
      }
  
    filep->f_priv = NULL;
  
    /* If the reference count decremented to zero and the file has been
     * unlinked, then free the file allocation now.
     */
  
    if (tfo->tfo_refs == 0 && (tfo->tfo_flags & TFO_FLAG_UNLINKED) != 0)
      {
        /* Free the file object while we hold the lock?  Weird but this
         * should be safe because the object is unlinked and could not
         * have any other references.
         */
  
        kmm_free(tfo);
        return OK;
      }
  
    /* Release the lock on the file */
  
    tmpfs_unlock_file(tfo);
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_read
   ****************************************************************************/
  
  static ssize_t tmpfs_read(FAR struct file *filep, FAR char *buffer,
                            size_t buflen)
  {
    FAR struct tmpfs_file_s *tfo;
    ssize_t nread;
    off_t startpos;
    off_t endpos;
  
    finfo("filep: %p buffer: %p buflen: %lu\n",
          filep, buffer, (unsigned long)buflen);
    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
  
    /* Recover our private data from the struct file instance */
  
    tfo = filep->f_priv;
  
    /* Get exclusive access to the file */
  
    tmpfs_lock_file(tfo);
  
    /* Handle attempts to read beyond the end of the file. */
  
    startpos = filep->f_pos;
    nread    = buflen;
    endpos   = startpos + buflen;
  
    if (endpos > tfo->tfo_size)
      {
        endpos = tfo->tfo_size;
        nread  = endpos - startpos;
      }
  
    /* Copy data from the memory object to the user buffer */
  
    memcpy(buffer, &tfo->tfo_data[startpos], nread);
    filep->f_pos += nread;
  
    /* Release the lock on the file */
  
    tmpfs_unlock_file(tfo);
    return nread;
  }
  
  /****************************************************************************
   * Name: tmpfs_write
   ****************************************************************************/
  
  static ssize_t tmpfs_write(FAR struct file *filep, FAR const char *buffer,
                             size_t buflen)
  {
    FAR struct tmpfs_file_s *tfo;
    ssize_t nwritten;
    off_t startpos;
    off_t endpos;
    int ret;
  
    finfo("filep: %p buffer: %p buflen: %lu\n",
          filep, buffer, (unsigned long)buflen);
    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
  
    /* Recover our private data from the struct file instance */
  
    tfo = filep->f_priv;
  
    /* Get exclusive access to the file */
  
    tmpfs_lock_file(tfo);
  
    /* Handle attempts to read beyond the end of the file */
  
    startpos = filep->f_pos;
    nwritten = buflen;
    endpos   = startpos + buflen;
  
    if (endpos > tfo->tfo_size)
      {
        /* Reallocate the file to handle the write past the end of the file. */
  
        ret = tmpfs_realloc_file(&tfo, (size_t)endpos);
        if (ret < 0)
          {
            goto errout_with_lock;
          }
  
        filep->f_priv = tfo;
      }
  
    /* Copy data from the memory object to the user buffer */
  
    memcpy(&tfo->tfo_data[startpos], buffer, nwritten);
    filep->f_pos += nwritten;
  
    /* Release the lock on the file */
  
    tmpfs_unlock_file(tfo);
    return nwritten;
  
  errout_with_lock:
    tmpfs_unlock_file(tfo);
    return (ssize_t)ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_seek
   ****************************************************************************/
  
  static off_t tmpfs_seek(FAR struct file *filep, off_t offset, int whence)
  {
    FAR struct tmpfs_file_s *tfo;
    off_t position;
  
    finfo("filep: %p\n", filep);
    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
  
    /* Recover our private data from the struct file instance */
  
    tfo = filep->f_priv;
  
    /* Map the offset according to the whence option */
  
    switch (whence)
      {
        case SEEK_SET: /* The offset is set to offset bytes. */
            position = offset;
            break;
  
        case SEEK_CUR: /* The offset is set to its current location plus
                        * offset bytes. */
            position = offset + filep->f_pos;
            break;
  
        case SEEK_END: /* The offset is set to the size of the file plus
                        * offset bytes. */
            position = offset + tfo->tfo_size;
            break;
  
        default:
            return -EINVAL;
      }
  
    /* Attempts to set the position beyound the end of file will
     * work if the file is open for write access.
     *
     * REVISIT: This simple implementation has no per-open storage that
     * would be needed to retain the open flags.
     */
  
  #if 0
    if (position > tfo->tfo_size && (tfo->tfo_oflags & O_WROK) == 0)
      {
        /* Otherwise, the position is limited to the file size */
  
        position = tfo->tfo_size;
      }
  #endif
  
    /* Save the new file position */
  
    filep->f_pos = position;
    return position;
  }
  
  /****************************************************************************
   * Name: tmpfs_ioctl
   ****************************************************************************/
  
  static int tmpfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
  {
    FAR struct tmpfs_file_s *tfo;
    FAR void **ppv = (FAR void**)arg;
  
    finfo("filep: %p cmd: %d arg: %08lx\n", filep, cmd, arg);
    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
  
    /* Recover our private data from the struct file instance */
  
    tfo = filep->f_inode->i_private;
  
    DEBUGASSERT(tfo != NULL);
  
    /* Only one ioctl command is supported */
  
    if (cmd == FIOC_MMAP && ppv != NULL)
      {
        /* Return the address on the media corresponding to the start of
         * the file.
         */
  
        *ppv = (FAR void *)tfo->tfo_data;
        return OK;
      }
  
    ferr("ERROR: Invalid cmd: %d\n", cmd);
    return -ENOTTY;
  }
  
  /****************************************************************************
   * Name: tmpfs_dup
   ****************************************************************************/
  
  static int tmpfs_dup(FAR const struct file *oldp, FAR struct file *newp)
  {
    FAR struct tmpfs_file_s *tfo;
  
    finfo("Dup %p->%p\n", oldp, newp);
    DEBUGASSERT(oldp->f_priv != NULL && oldp->f_inode != NULL &&
                newp->f_priv == NULL && newp->f_inode != NULL);
  
    /* Recover our private data from the struct file instance */
  
    tfo = oldp->f_priv;
    DEBUGASSERT(tfo != NULL);
  
    /* Increment the reference count (atomically)*/
  
    tmpfs_lock_file(tfo);
    tfo->tfo_refs++;
    tmpfs_unlock_file(tfo);
  
    /* Save a copy of the file object as the dup'ed file.  This
     * simple implementation does not many any per-open data
     * structures so there is not really much to the dup operation.
     */
  
    newp->f_priv = tfo;
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_fstat
   *
   * Description:
   *   Obtain information about an open file associated with the file
   *   descriptor 'fd', and will write it to the area pointed to by 'buf'.
   *
   ****************************************************************************/
  
  static int tmpfs_fstat(FAR const struct file *filep, FAR struct stat *buf)
  {
    FAR struct tmpfs_file_s *tfo;
  
    finfo("Fstat %p\n", buf);
    DEBUGASSERT(filep != NULL && buf != NULL);
  
    /* Recover our private data from the struct file instance */
  
    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
    tfo = filep->f_priv;
  
    /* Get exclusive access to the file */
  
    tmpfs_lock_file(tfo);
  
    /* Return information about the file in the stat buffer.*/
  
    tmpfs_stat_common((FAR struct tmpfs_object_s *)tfo, buf);
  
    /* Release the lock on the file and return success. */
  
    tmpfs_unlock_file(tfo);
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_opendir
   ****************************************************************************/
  
  static int tmpfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
                           FAR struct fs_dirent_s *dir)
  {
    FAR struct tmpfs_s *fs;
    FAR struct tmpfs_directory_s *tdo;
    int ret;
  
    finfo("mountpt: %p relpath: %s dir: %p\n",
          mountpt, relpath, dir);
    DEBUGASSERT(mountpt != NULL && relpath != NULL && dir != NULL);
  
    /* Get the mountpoint private data from the inode structure */
  
    fs = mountpt->i_private;
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Skip over any leading directory separators (shouldn't be any) */
  
    for (; *relpath == '/'; relpath++);
  
    /* Find the directory object associated with this relative path.
     * If successful, this action will lock both the parent directory and
     * the file object, adding one to the reference count of both.
     * In the event that -ENOENT, there will still be a reference and
     * lock on the returned directory.
     */
  
    ret = tmpfs_find_directory(fs, relpath, &tdo, NULL);
    if (ret >= 0)
      {
        dir->u.tmpfs.tf_tdo   = tdo;
        dir->u.tmpfs.tf_index = 0;
      }
  
    /* Release the lock on the file system and return the result */
  
    tmpfs_unlock(fs);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_closedir
   ****************************************************************************/
  
  static int tmpfs_closedir(FAR struct inode *mountpt,
                            FAR struct fs_dirent_s *dir)
  {
    FAR struct tmpfs_directory_s *tdo;
  
    finfo("mountpt: %p dir: %p\n",  mountpt, dir);
    DEBUGASSERT(mountpt != NULL && dir != NULL);
  
    /* Get the directory structure from the dir argument */
  
    tdo = dir->u.tmpfs.tf_tdo;
    DEBUGASSERT(tdo != NULL);
  
    /* Decrement the reference count on the directory object */
  
    tmpfs_lock_directory(tdo);
    tdo->tdo_refs--;
    tmpfs_unlock_directory(tdo);
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_readdir
   ****************************************************************************/
  
  static int tmpfs_readdir(FAR struct inode *mountpt,
                           FAR struct fs_dirent_s *dir)
  {
    FAR struct tmpfs_directory_s *tdo;
    unsigned int index;
    int ret;
  
    finfo("mountpt: %p dir: %p\n",  mountpt, dir);
    DEBUGASSERT(mountpt != NULL && dir != NULL);
  
    /* Get the directory structure from the dir argument and lock it */
  
    tdo = dir->u.tmpfs.tf_tdo;
    DEBUGASSERT(tdo != NULL);
  
    tmpfs_lock_directory(tdo);
  
    /* Have we reached the end of the directory? */
  
    index = dir->u.tmpfs.tf_index;
    if (index >= tdo->tdo_nentries)
      {
        /* We signal the end of the directory by returning the special error:
         * -ENOENT
         */
  
        finfo("End of directory\n");
        ret = -ENOENT;
      }
    else
      {
        FAR struct tmpfs_dirent_s *tde;
        FAR struct tmpfs_object_s *to;
  
        /* Does this entry refer to a file or a directory object? */
  
        tde = &tdo->tdo_entry[index];
        to  = tde->tde_object;
        DEBUGASSERT(to != NULL);
  
        if (to->to_type == TMPFS_DIRECTORY)
          {
            /* A directory */
  
             dir->fd_dir.d_type = DTYPE_DIRECTORY;
          }
        else /* to->to_type == TMPFS_REGULAR) */
          {
            /* A regular file */
  
             dir->fd_dir.d_type = DTYPE_FILE;
          }
  
        /* Copy the entry name */
  
        strncpy(dir->fd_dir.d_name, tde->tde_name, NAME_MAX + 1);
  
        /* Increment the index for next time */
  
        dir->u.tmpfs.tf_index = index + 1;
        ret = OK;
      }
  
    tmpfs_unlock_directory(tdo);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_rewinddir
   ****************************************************************************/
  
  static int tmpfs_rewinddir(FAR struct inode *mountpt,
                             FAR struct fs_dirent_s *dir)
  {
    finfo("mountpt: %p dir: %p\n",  mountpt, dir);
    DEBUGASSERT(mountpt != NULL && dir != NULL);
  
    /* Set the readdir index to zero */
  
    dir->u.tmpfs.tf_index = 0;
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_bind
   ****************************************************************************/
  
  static int tmpfs_bind(FAR struct inode *blkdriver, FAR const void *data,
                        FAR void **handle)
  {
    FAR struct tmpfs_directory_s *tdo;
    FAR struct tmpfs_s *fs;
  
    finfo("blkdriver: %p data: %p handle: %p\n", blkdriver, data, handle);
    DEBUGASSERT(blkdriver == NULL && handle != NULL);
  
    /* Create an instance of the tmpfs file system */
  
    fs = (FAR struct tmpfs_s *)kmm_zalloc(sizeof(struct tmpfs_s));
    if (fs == NULL)
      {
        return -ENOMEM;
      }
  
    /* Create a root file system.  This is like a single directory entry in
     * the file system structure.
     */
  
    tdo = tmpfs_alloc_directory();
    if (tdo == NULL)
      {
        kmm_free(fs);
        return -ENOMEM;
      }
  
    fs->tfs_root.tde_object = (FAR struct tmpfs_object_s *)tdo;
    fs->tfs_root.tde_name   = "";
  
    /* Set up the backward link (to support reallocation) */
  
    tdo->tdo_dirent         = &fs->tfs_root;
  
    /* Initialize the file system state */
  
    fs->tfs_exclsem.ts_holder = TMPFS_NO_HOLDER;
    fs->tfs_exclsem.ts_count  = 0;
    sem_init(&fs->tfs_exclsem.ts_sem, 0, 1);
  
    /* Return the new file system handle */
  
    *handle = (FAR void *)fs;
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_unbind
   ****************************************************************************/
  
  static int tmpfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
                          unsigned int flags)
  {
    FAR struct tmpfs_s *fs = (FAR struct tmpfs_s *)handle;
    FAR struct tmpfs_directory_s *tdo;
    int ret;
  
    finfo("handle: %p blkdriver: %p flags: %02x\n",
          handle, blkdriver, flags);
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Lock the file system */
  
    tmpfs_lock(fs);
  
    /* Traverse all directory entries (recursively), freeing all resources. */
  
    tdo = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
    ret = tmpfs_foreach(tdo, tmpfs_free_callout, NULL);
  
    /* Now we can destroy the root file system and the file system itself. */
  
    sem_destroy(&tdo->tdo_exclsem.ts_sem);
    kmm_free(tdo);
  
    sem_destroy(&fs->tfs_exclsem.ts_sem);
    kmm_free(fs);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_statfs
   ****************************************************************************/
  
  static int tmpfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
  {
    FAR struct tmpfs_s *fs;
    FAR struct tmpfs_directory_s *tdo;
    struct tmpfs_statfs_s tmpbuf;
    size_t inuse;
    size_t avail;
    off_t blkalloc;
    off_t blkused;
    int ret;
  
    finfo("mountpt: %p buf: %p\n", mountpt, buf);
    DEBUGASSERT(mountpt != NULL && buf != NULL);
  
    /* Get the file system structure from the inode reference. */
  
    fs = mountpt->i_private;
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Set up the memory use for the file system and root directory object */
  
    tdo              = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
    inuse            = sizeof(struct tmpfs_s) +
                       SIZEOF_TMPFS_DIRECTORY(tdo->tdo_nentries);
    avail            = sizeof(struct tmpfs_s) +
                       tdo->tdo_alloc - inuse;
  
    tmpbuf.tsf_alloc = tdo->tdo_alloc;
    tmpbuf.tsf_inuse = inuse;
    tmpbuf.tsf_files = 0;
    tmpbuf.tsf_ffree = avail / sizeof(struct tmpfs_dirent_s);
  
    /* Traverse the file system to accurmulate statistics */
  
    ret = tmpfs_foreach(tdo, tmpfs_statfs_callout, (FAR void *)&tmpbuf);
    if (ret < 0)
      {
        return -ECANCELED;
      }
  
    /* Return something for the file system description */
  
    blkalloc        = (tmpbuf.tsf_alloc + CONFIG_FS_TMPFS_BLOCKSIZE - 1) /
                       CONFIG_FS_TMPFS_BLOCKSIZE;
    blkused         = (tmpbuf.tsf_inuse + CONFIG_FS_TMPFS_BLOCKSIZE - 1) /
                       CONFIG_FS_TMPFS_BLOCKSIZE;
  
    buf->f_type     = TMPFS_MAGIC;
    buf->f_namelen  = NAME_MAX;
    buf->f_bsize    = CONFIG_FS_TMPFS_BLOCKSIZE;
    buf->f_blocks   = blkalloc;
    buf->f_bfree    = blkalloc - blkused;
    buf->f_bavail   = blkalloc - blkused;
    buf->f_files    = tmpbuf.tsf_files;
    buf->f_ffree    = tmpbuf.tsf_ffree;
  
    /* Release the lock on the file system */
  
    tmpfs_unlock(fs);
    return OK;
  }
  
  /****************************************************************************
   * Name: tmpfs_unlink
   ****************************************************************************/
  
  static int tmpfs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
  {
    FAR struct tmpfs_s *fs;
    FAR struct tmpfs_directory_s *tdo;
    FAR struct tmpfs_file_s *tfo = NULL;
    FAR const char *name;
    int ret;
  
    finfo("mountpt: %p relpath: %s\n", mountpt, relpath);
    DEBUGASSERT(mountpt != NULL && relpath != NULL);
  
    /* Get the file system structure from the inode reference. */
  
    fs = mountpt->i_private;
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Find the file object and parent directory associated with this relative
     * path.  If successful, tmpfs_find_file will lock both the file object
     * and the parent directory and take one reference count on each.
     */
  
    ret = tmpfs_find_file(fs, relpath, &tfo, &tdo);
    if (ret < 0)
      {
        goto errout_with_lock;
      }
  
    DEBUGASSERT(tfo != NULL);
  
    /* Get the file name from the relative path */
  
    name = strrchr(relpath, '/');
    if (name != NULL)
      {
        /* Skip over the file '/' character */
  
        name++;
      }
    else
      {
        /* The name must lie in the root directory */
  
        name = relpath;
      }
  
    /* Remove the file from parent directory */
  
    ret = tmpfs_remove_dirent(tdo, name);
    if (ret < 0)
      {
        goto errout_with_objects;
      }
  
    /* If the reference count is not one, then just mark the file as
     * unlinked
     */
  
    if (tfo->tfo_refs > 1)
      {
        /* Make the file object as unlinked */
  
        tfo->tfo_flags |= TFO_FLAG_UNLINKED;
  
        /* Release the reference count on the file object */
  
        tfo->tfo_refs--;
        tmpfs_unlock_file(tfo);
      }
  
    /* Otherwise we can free the object now */
  
    else
      {
        sem_destroy(&tfo->tfo_exclsem.ts_sem);
        kmm_free(tfo);
      }
  
    /* Release the reference and lock on the parent directory */
  
    tdo->tdo_refs--;
    tmpfs_unlock_directory(tdo);
    tmpfs_unlock(fs);
  
    return OK;
  
  errout_with_objects:
    tmpfs_release_lockedfile(tfo);
  
    tdo->tdo_refs--;
    tmpfs_unlock_directory(tdo);
  
  errout_with_lock:
    tmpfs_unlock(fs);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_mkdir
   ****************************************************************************/
  
  static int tmpfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
                         mode_t mode)
  {
    FAR struct tmpfs_s *fs;
    int ret;
  
    finfo("mountpt: %p relpath: %s mode: %04x\n", mountpt, relpath, mode);
    DEBUGASSERT(mountpt != NULL && relpath != NULL);
  
    /* Get the file system structure from the inode reference. */
  
    fs = mountpt->i_private;
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Create the directory. */
  
    ret = tmpfs_create_directory(fs, relpath, NULL);
    tmpfs_unlock(fs);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_rmdir
   ****************************************************************************/
  
  static int tmpfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
  {
    FAR struct tmpfs_s *fs;
    FAR struct tmpfs_directory_s *parent;
    FAR struct tmpfs_directory_s *tdo;
    FAR const char *name;
    int ret;
  
    finfo("mountpt: %p relpath: %s\n", mountpt, relpath);
    DEBUGASSERT(mountpt != NULL && relpath != NULL);
  
    /* Get the file system structure from the inode reference. */
  
    fs = mountpt->i_private;
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Find the directory object and parent directory associated with this
     * relative path.  If successful, tmpfs_find_file will lock both the
     * directory object and the parent directory and take one reference count
     * on each.
     */
  
    ret = tmpfs_find_directory(fs, relpath, &tdo, &parent);
    if (ret < 0)
      {
        goto errout_with_lock;
      }
  
    /* Is the directory empty?  We cannot remove directories that still
     * contain references to file system objects.  No can we remove the
     * directory if there are outstanding references on it (other than
     * our reference).
     */
  
    if (tdo->tdo_nentries > 0 || tdo->tdo_refs > 1)
      {
        ret = -EBUSY;
        goto errout_with_objects;
      }
  
    /* Get the directory name from the relative path */
  
    name = strrchr(relpath, '/');
    if (name != NULL)
      {
        /* Skip over the fidirectoryle '/' character */
  
        name++;
      }
    else
      {
        /* The name must lie in the root directory */
  
        name = relpath;
      }
  
    /* Remove the directory from parent directory */
  
    ret = tmpfs_remove_dirent(parent, name);
    if (ret < 0)
      {
        goto errout_with_objects;
      }
  
    /* Free the directory object */
  
    sem_destroy(&tdo->tdo_exclsem.ts_sem);
    kmm_free(tdo);
  
    /* Release the reference and lock on the parent directory */
  
    parent->tdo_refs--;
    tmpfs_unlock_directory(parent);
    tmpfs_unlock(fs);
  
    return OK;
  
  errout_with_objects:
    tdo->tdo_refs--;
    tmpfs_unlock_directory(tdo);
  
    parent->tdo_refs--;
    tmpfs_unlock_directory(parent);
  
  errout_with_lock:
    tmpfs_unlock(fs);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_rename
   ****************************************************************************/
  
  static int tmpfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
                          FAR const char *newrelpath)
  {
    FAR struct tmpfs_directory_s *oldparent;
    FAR struct tmpfs_directory_s *newparent;
    FAR struct tmpfs_object_s *to;
    FAR struct tmpfs_s *fs;
    FAR const char *oldname;
    FAR char *newname;
    FAR char *copy;
    int ret;
  
    finfo("mountpt: %p oldrelpath: %s newrelpath: %s\n",
          mountpt, oldrelpath, newrelpath);
    DEBUGASSERT(mountpt != NULL && oldrelpath != NULL && newrelpath != NULL);
  
    /* Get the file system structure from the inode reference. */
  
    fs = mountpt->i_private;
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Duplicate the newpath variable so that we can modify it */
  
    copy = strdup(newrelpath);
    if (copy == NULL)
      {
        return -ENOMEM;
      }
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Separate the new path into the new file name and the path to the new
     * parent directory.
     */
  
    newname = strrchr(copy, '/');
    if (newname == NULL)
      {
        /* No subdirectories... use the root directory */
  
        newname   = copy;
        newparent = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
  
        tmpfs_lock_directory(newparent);
        newparent->tdo_refs++;
      }
    else
      {
        /* Terminate the parent directory path */
  
        *newname++ = '\0';
  
        /* Locate the parent directory that should contain this name.
         * On success, tmpfs_find_directory() will lockthe parent
         * directory and increment the reference count.
         */
  
        ret = tmpfs_find_directory(fs, copy, &newparent, NULL);
        if (ret < 0)
          {
            goto errout_with_lock;
          }
      }
  
    /* Verify that no object of this name already exists in the destination
     * directory.
     */
  
    ret = tmpfs_find_dirent(newparent, newname);
    if (ret != -ENOENT)
      {
        /* Something with this name already exists in the directory.
         * OR perhaps some fatal error occurred.
         */
  
        if (ret >= 0)
          {
            ret = -EEXIST;
          }
  
        goto errout_with_newparent;
      }
  
    /* Find the old object at oldpath.  If successful, tmpfs_find_object()
     * will lock both the object and the parent directory and will increment
     * the reference count on both.
     */
  
    ret = tmpfs_find_object(fs, oldrelpath, &to, &oldparent);
    if (ret < 0)
      {
         goto errout_with_newparent;
      }
  
    /* Get the old file name from the relative path */
  
    oldname = strrchr(oldrelpath, '/');
    if (oldname != NULL)
      {
        /* Skip over the file '/' character */
  
        oldname++;
      }
    else
      {
        /* The name must lie in the root directory */
  
        oldname = oldrelpath;
      }
  
    /* Remove the entry from the parent directory */
  
    ret = tmpfs_remove_dirent(oldparent, oldname);
    if (ret < 0)
      {
        goto errout_with_oldparent;
      }
  
    /* Add an entry to the new parent directory. */
  
    ret = tmpfs_add_dirent(&newparent, to, newname);
  
  errout_with_oldparent:
    oldparent->tdo_refs--;
    tmpfs_unlock_directory(oldparent);
  
    tmpfs_release_lockedobject(to);
  
  errout_with_newparent:
    newparent->tdo_refs--;
    tmpfs_unlock_directory(newparent);
  
  errout_with_lock:
    tmpfs_unlock(fs);
    kmm_free(copy);
    return ret;
  }
  
  /****************************************************************************
   * Name: tmpfs_stat_common
   ****************************************************************************/
  
  static void tmpfs_stat_common(FAR struct tmpfs_object_s *to,
                                FAR struct stat *buf)
  {
    size_t objsize;
  
    /* Is the tmpfs object a regular file? */
  
    memset(buf, 0, sizeof(struct stat));
  
    if (to->to_type == TMPFS_REGULAR)
      {
        FAR struct tmpfs_file_s *tfo =
          (FAR struct tmpfs_file_s *)to;
  
        /* -rwxrwxrwx */
  
        buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFREG;
  
        /* Get the size of the object */
  
        objsize = tfo->tfo_size;
      }
    else /* if (to->to_type == TMPFS_DIRECTORY) */
      {
        FAR struct tmpfs_directory_s *tdo =
          (FAR struct tmpfs_directory_s *)to;
  
        /* drwxrwxrwx */
  
        buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFDIR;
  
        /* Get the size of the object */
  
        objsize = SIZEOF_TMPFS_DIRECTORY(tdo->tdo_nentries);
      }
  
    /* Fake the rest of the information */
  
    buf->st_size    = objsize;
    buf->st_blksize = CONFIG_FS_TMPFS_BLOCKSIZE;
    buf->st_blocks  = (objsize + CONFIG_FS_TMPFS_BLOCKSIZE - 1) /
                      CONFIG_FS_TMPFS_BLOCKSIZE;
  }
  
  /****************************************************************************
   * Name: tmpfs_stat
   ****************************************************************************/
  
  static int tmpfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
                        FAR struct stat *buf)
  {
    FAR struct tmpfs_s *fs;
    FAR struct tmpfs_object_s *to;
    int ret;
  
    finfo("mountpt=%p relpath=%s buf=%p\n", mountpt, relpath, buf);
    DEBUGASSERT(mountpt != NULL && relpath != NULL && buf != NULL);
  
    /* Get the file system structure from the inode reference. */
  
    fs = mountpt->i_private;
    DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
  
    /* Get exclusive access to the file system */
  
    tmpfs_lock(fs);
  
    /* Find the tmpfs object at the relpath.  If successful,
     * tmpfs_find_object() will lock the object and increment the
     * reference count on the object.
     */
  
    ret = tmpfs_find_object(fs, relpath, &to, NULL);
    if (ret < 0)
      {
        goto errout_with_fslock;
      }
  
    /* We found it... Return information about the file object in the stat
     * buffer.
     */
  
    DEBUGASSERT(to != NULL);
    tmpfs_stat_common(to, buf);
  
    /* Unlock the object and return success */
  
    tmpfs_release_lockedobject(to);
    ret = OK;
  
  errout_with_fslock:
    tmpfs_unlock(fs);
    return ret;
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  #endif /* CONFIG_DISABLE_MOUNTPOINT */
  