summaryrefslogblamecommitdiffstats
path: root/cpukit/libfs/src/rfs/rtems-rfs-rtems.c
blob: 7c92a09a3c9aba1e3afa12968b1f0c1c175e3aeb (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                                           



                   
                     

                   







                                 


































                                                                                 
          
                                                                  
                                                                     







                                                                         
                                  



                                                              
                                                                                    
                                
 







                                                                
 










                                                                    
 
      
                                                      













                                                                       
 






                                                               
                                                         






                 
                                                   







                                                                               
                                                      



                                     

                                           
                 
       










                                                                             

                                                                            



                                                                    
                                                                           










                                                                               


                                                                             






                                                                          
                                                                             














                                                                              
       
                                                                  
                                                                                         







                                                                   
     
   
 



                                                                
 



                                                              
                                                                 














                                                                              
          














                                                                         
                                                                                    
 










                                               
 






                                                                     
 










                                                                   
 









                                                                         
 
      
                                                      


                      
 





















                                                                               
                                                      



                                     

                                           
                 
       

            
 



















                                                                             
                                                                       











                                                                           
 


                                                 


                                                                             






                                                                              
                                                                                  














                                                                             
                                                                                 







                                                                       
     














                                                                         
 

















                                                                       
 









                                                                  
                                                                              



                                     
 











                                                                          
          









                                                                               
                                                                                 
                            
 
                            
 
                                                                       






                                                       
 










                                                      
          









                                                                               
 
                                                           
                                                                                            
                              
 
                                                                             




                                                              
 
                              
 










                                                                               
                                    
















                                                                                
 























                                                                               
 
                              
 
























                                                                         
 
                                                          
                                                                          
                               
 
                            
 





                                                              
 
























                                                              
 



           








                                                                        
          









                                                                         
 















                                                              
 
                              
 












                                                                              
          


















                                                                               
 







                                                            
 
                              
 











                                                                         
              







                                                                          
 
                                                             
                                                                     
 
                            
 











                                                                
   





                                                                          



                                


                                                           

                                                                                    
 
                            
 






                                                               
                                            
 






                                                              


                                       
                                                                 
   





                                                                              
 





                                                               
 
                              
 


           
   

                                                                 
 


                                                                         

                                
                            

                                                         
                                                                 
 
                            
 







                                                             
 

                                                           
                  

                                                                          
   
 
                                             
                                                 
                                                


                                                       




                                                                            
                                                                        














                                                                     



                                                               

                               
                                                               
        
                                                           
   
 
                                                 
 





                                                             
 













                                                   
          





















                                                                             
 














                                                               
 










                                                     
      











                                                              
 








                                                     
              











                                                                                   
                                                                                       


                               
 
                                                                             














                                                                           
              




                                              
 







                                                                            





                                                        
              
   
          










                                                                        
 






                                                                
                                                                                                        













                                                                               
 

















                                                                              





                                    
          



                                                                    

                                
 
                                               
 




                                                     


                                                    

                                          
                                             
 







                                                                      





                                                
                                       

                                                    


                                                    






                                      

                                                                               


                                                                              
                                    









                                                       
                                                      
                                                   
                                               
                                                     

                                             
                                                                                                            

                                             
                                           







                                           

                                                                           


                                 


                                                                            

                              



























                                                                            
 




                                                                    
 













                                                                               
 
                                                                              




                                                          
 






                                                                   
 













                                                                         
 
                              
 

                                           
 

                                                       
/*
 *  COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org>
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  $Id$
 */
/**
 * @file
 *
 * @ingroup rtems-rfs
 *
 * RTEMS File System Interface for RTEMS.
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <inttypes.h>
#include <stdlib.h>

#if SIZEOF_MODE_T == 8
#define PRIomode_t PRIo64
#elif SIZEOF_MODE_T == 4
#define PRIomode_t PRIo32
#else
#error "unsupport size of mode_t"
#endif

#include <rtems/rfs/rtems-rfs-file.h>
#include <rtems/rfs/rtems-rfs-dir.h>
#include <rtems/rfs/rtems-rfs-link.h>
#include "rtems-rfs-rtems.h"

/**
 * The libio permissions for read/execute.
 */
#define RTEMS_LIBIO_PERMS_RX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_READ)
/**
 * The libio permissions for write/execute.
 */
#define RTEMS_LIBIO_PERMS_WX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_WRITE)

/**
 * Evaluate the path to a node that wishes to be accessed. The pathloc is
 * returned with the ino to the node to be accessed.
 *
 * The routine starts from the root stripping away any leading path separators
 * breaking the path up into the node names and checking an inode exists for
 * that node name. Permissions are checked to insure access to the node is
 * allowed. A path to a node must be accessable all the way even if the end
 * result is directly accessable. As a user on Linux try "ls /root/../tmp" and
 * you will see if fails.
 *
 * The whole process is complicated by crossmount paths where we head down into
 * this file system only to return to the top and out to a another mounted file
 * system. For example we are mounted on '/e' and the user enters "ls
 * /e/a/b/../../dev". We need to head down then back up.
 *
 * @param path
 * @param pathlen
 * @param flags
 * @param pathloc
 */
static int
rtems_rfs_rtems_eval_path (const char*                       path,
                           size_t                            pathlen,
                           int                               flags,
                           rtems_filesystem_location_info_t* pathloc)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_inode_handle inode;
  rtems_rfs_ino          ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  uint32_t               doff = 0;
  const char*            node;
  size_t                 node_len;
  int                    stripped;
  int                    rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH))
    printf ("rtems-rfs-rtems: eval-path: in: path:%s pathlen:%zi ino:%" PRId32 "\n",
            path, pathlen, ino);

  /*
   * Eat any separators at the start of the path.
   */
  stripped = rtems_filesystem_prefix_separators (path, pathlen);
  path += stripped;
  pathlen -= stripped;

  rtems_rfs_rtems_lock (fs);

  while (true)
  {
    /*
     * Open and load the inode.
     */
    rc = rtems_rfs_inode_open (fs, ino, &inode, true);
    if (rc > 0)
    {
      rtems_rfs_rtems_unlock (fs);
      return rtems_rfs_rtems_error ("eval_path: opening inode", rc);
    }

    /*
     * Is this the end of the pathname we were given ?
     */
    if ((*path == '\0') || (pathlen == 0))
      break;

    /*
     * If a directory the execute bit must be set for us to enter.
     */
    if (RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)) &&
        !rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_SEARCH))
    {
      rtems_rfs_inode_close (fs, &inode);
      rtems_rfs_rtems_unlock (fs);
      return rtems_rfs_rtems_error ("eval_path: eval perms", EACCES);
    }

    /*
     * Extract the node name we will look for this time around.
     */
    node = path;
    node_len = 0;
    while (!rtems_filesystem_is_separator (*path) &&
           (*path != '\0') && pathlen &&
           ((node_len + 1) < rtems_rfs_fs_max_name (fs)))
    {
      path++;
      pathlen--;
      node_len++;
    }

    /*
     * Eat any separators at the start of the path.
     */
    stripped = rtems_filesystem_prefix_separators (path, pathlen);
    path += stripped;
    pathlen -= stripped;
    node_len += stripped;

    /*
     * If the node is the current directory and there is more path to come move
     * on to it otherwise we are at the inode we want.
     */
    if (rtems_rfs_current_dir (node))
    {
      if (*path)
      {
        rtems_rfs_inode_close (fs, &inode);
        continue;
      }
      break;
    }

    /*
     * If the node is a parent we must move up one directory. If the location
     * is on another file system we have a crossmount so we call that file
     * system to handle the remainder of the path.
     */
    if (rtems_rfs_parent_dir (node))
    {
      /*
       * If we are at the root inode of the file system we have a crossmount
       * path.
       */
      if (ino == RTEMS_RFS_ROOT_INO)
      {
        if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH))
          printf("rtems-rfs-rtems: eval-path: crossmount: path:%s (%zd)\n",
                 path - node_len, pathlen + node_len);
        rtems_rfs_inode_close (fs, &inode);
        rtems_rfs_rtems_unlock (fs);
        *pathloc = pathloc->mt_entry->mt_point_node;
        return (*pathloc->ops->evalpath_h)(path - node_len, pathlen + node_len,
                                           flags, pathloc);
      }

      /*
       * We need to find the parent of this node.
       */
      rc = rtems_rfs_dir_lookup_ino (fs, &inode,
                                     RTEMS_RFS_PARENT_DIR_STR,
                                     RTEMS_RFS_PARENT_DIR_SIZE, &ino, &doff);
      if (rc > 0)
      {
        rtems_rfs_inode_close (fs, &inode);
        rtems_rfs_rtems_unlock (fs);
        return rtems_rfs_rtems_error ("eval_path: read parent inode", rc);
      }
      if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH))
        printf("rtems-rfs-rtems: eval-path: parent: ino:%" PRId32 "\n", ino);
    }
    else
    {
      /*
       * Look up the node name in this directory. If found drop through, close
       * the current inode and let the loop open the inode so the mode can be
       * read and handlers set.
       */
      rc = rtems_rfs_dir_lookup_ino (fs, &inode,
                                     node, node_len - stripped, &ino, &doff);
      if (rc > 0)
      {
        rtems_rfs_inode_close (fs, &inode);
        rtems_rfs_rtems_unlock (fs);
        return ((errno = rc) == 0) ? 0 : -1;
      }
      if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH))
        printf("rtems-rfs-rtems: eval-path: down: path:%s ino:%" PRId32 "\n", node, ino);
    }

    rc = rtems_rfs_inode_close (fs, &inode);
    if (rc > 0)
    {
      rtems_rfs_inode_close (fs, &inode);
      rtems_rfs_rtems_unlock (fs);
      return rtems_rfs_rtems_error ("eval_path: closing node", rc);
    }
  }

  rtems_rfs_rtems_set_pathloc_ino (pathloc, ino);
  rtems_rfs_rtems_set_pathloc_doff (pathloc, doff);

  rc = rtems_rfs_rtems_set_handlers (pathloc, &inode) ? 0 : EIO;

  rtems_rfs_inode_close (fs, &inode);
  rtems_rfs_rtems_unlock (fs);

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH))
    printf("rtems-rfs-rtems: eval-path: ino:%" PRId32 "\n", ino);

  return rc;
}

/**
 * The following routine evaluates a path for a new node to be created. The
 * pathloc is returned with a pointer to the parent of the new node. The name
 * is returned with a pointer to the first character in the new node name. The
 * parent node is verified to be a directory.
 *
 * @param path
 * @param pathloc
 * @param name
 * @return int
 */
static int
rtems_rfs_rtems_eval_for_make (const char*                       path,
                               rtems_filesystem_location_info_t* pathloc,
                               const char**                      name)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_inode_handle inode;
  rtems_rfs_ino          ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  rtems_rfs_ino          node_ino;
  uint32_t               doff = 0;
  const char*            node;
  int                    node_len;
  int                    stripped;
  int                    rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE))
    printf ("rtems-rfs-rtems: eval-for-make: path:%s ino:%" PRId32 "\n", path, ino);

  *name = path + strlen (path);

  while (*name != path)
  {
    (*name)--;
    if (rtems_filesystem_is_separator (**name))
    {
      (*name)++;
      break;
    }
  }

  /*
   * Eat any separators at start of the path.
   */
  stripped = rtems_filesystem_prefix_separators (path, strlen(path));
  path += stripped;

  rtems_rfs_rtems_lock (fs);

  while (true)
  {
    /*
     * Open and load the inode.
     */
    rc = rtems_rfs_inode_open (fs, ino, &inode, true);
    if (rc > 0)
    {
      rtems_rfs_rtems_unlock (fs);
      return rtems_rfs_rtems_error ("eval_for_make: read ino", rc);
    }

    /*
     * If a directory the execute bit must be set for us to enter.
     */
    if (RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)) &&
        !rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_SEARCH))
    {
      rtems_rfs_inode_close (fs, &inode);
      rtems_rfs_rtems_unlock (fs);
      return rtems_rfs_rtems_error ("eval_for_make: eval perms", EACCES);
    }

    /*
     * Is this the end of the pathname we were given ?
     */
    if (path == *name)
      break;

    /*
     * Extract the node name we will look for this time around.
     */
    node = path;
    node_len = 0;
    while (!rtems_filesystem_is_separator(*path) &&
           (*path != '\0') &&
           (node_len < (rtems_rfs_fs_max_name (fs) - 1)))
    {
      node_len++;
      path++;
    }

    /*
     * Eat any separators at start of the new path.
     */
    stripped = rtems_filesystem_prefix_separators (path, strlen (path));
    path += stripped;
    node_len += stripped;

    /*
     * If the node is the current directory and there is more path to come move
     * on to it otherwise we are at the inode we want.
     */
    if (rtems_rfs_current_dir (node))
    {
      if (*path)
      {
        rtems_rfs_inode_close (fs, &inode);
        continue;
      }
      break;
    }

    /*
     * If the node is a parent we must move up one directory. If the location
     * is on another file system we have a crossmount so we call that file
     * system to handle the remainder of the path.
     */
    if (rtems_rfs_parent_dir (path))
    {
      /*
       * If we are at the root inode of the file system we have a crossmount
       * path.
       */
      if (ino == RTEMS_RFS_ROOT_INO)
      {
        if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE))
          printf("rtems-rfs-rtems: eval-for-make: crossmount: path:%s\n",
                 path - node_len);

        rtems_rfs_inode_close (fs, &inode);
        rtems_rfs_rtems_unlock (fs);
        *pathloc = pathloc->mt_entry->mt_point_node;
        return (*pathloc->ops->evalformake_h)(path + 2, pathloc, name);
      }

      /*
       * If not a directory give and up return. We cannot change dir from a
       * regular file or device node.
       */
      if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)))
      {
        rtems_rfs_inode_close (fs, &inode);
        rtems_rfs_rtems_unlock (fs);
        return rtems_rfs_rtems_error ("eval_for_make: not dir", ENOTSUP);
      }

      /*
       * We need to find the parent of this node.
       */
      rc = rtems_rfs_dir_lookup_ino (fs, &inode,
                                     RTEMS_RFS_PARENT_DIR_STR,
                                     RTEMS_RFS_PARENT_DIR_SIZE, &ino, &doff);
      if (rc > 0)
      {
        rtems_rfs_inode_close (fs, &inode);
        rtems_rfs_rtems_unlock (fs);
        return rtems_rfs_rtems_error ("eval_for_make: read parent inode", rc);
      }
      if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE))
        printf ("rtems-rfs-rtems: eval-for-make: parent: ino:%" PRId32 "\n", ino);
    }
    else
    {
      /*
       * Read the inode so we know it exists and what type it is.
       */
      rc = rtems_rfs_dir_lookup_ino (fs, &inode,
                                     node, node_len - stripped, &ino, &doff);
      if (rc > 0)
      {
        rtems_rfs_inode_close (fs, &inode);
        rtems_rfs_rtems_unlock (fs);
        return rtems_rfs_rtems_error ("eval_for_make: reading inode", rc);
      }
      if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE))
        printf("rtems-rfs-rtems: eval-for-make: down: path:%s ino:%" PRId32 "\n",
               node, ino);
    }

    rc = rtems_rfs_inode_close (fs, &inode);
    if (rc > 0)
    {
      rtems_rfs_rtems_unlock (fs);
      return rtems_rfs_rtems_error ("eval_for_make: closing node", rc);
    }
  }

  if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)))
  {
    rtems_rfs_inode_close (fs, &inode);
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("eval_for_make: not dir", ENOTDIR);
  }

  if (!rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_WX))
  {
    rtems_rfs_inode_close (fs, &inode);
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("eval_for_make: cannot write", EACCES);
  }

  /*
   * Make sure the name does not already exists in the directory.
   */
  rc = rtems_rfs_dir_lookup_ino (fs, &inode, *name, strlen (*name),
                                 &node_ino, &doff);
  if (rc == 0)
  {
    rtems_rfs_inode_close (fs, &inode);
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("eval_for_make: found name", EEXIST);
  }

  if (rc != ENOENT)
  {
    rtems_rfs_inode_close (fs, &inode);
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("eval_for_make: look up", rc);
  }

  /*
   * Set the parent ino in the path location.
   */

  rtems_rfs_rtems_set_pathloc_ino (pathloc, ino);
  rtems_rfs_rtems_set_pathloc_doff (pathloc, doff);

  rc = rtems_rfs_rtems_set_handlers (pathloc, &inode) ? 0 : EIO;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE))
    printf("rtems-rfs-rtems: eval-for-make: parent ino:%" PRId32 " name:%s\n",
           ino, *name);

  rtems_rfs_inode_close (fs, &inode);
  rtems_rfs_rtems_unlock (fs);

  return rc;
}

/**
 * The following rouine creates a new link node under parent with the name
 * given in name. The link node is set to point to the node at to_loc.
 *
 * @param to_loc
 * @param parent_loc
 * @param name
 * @return int
 */
static int
rtems_rfs_rtems_link (rtems_filesystem_location_info_t* to_loc,
                      rtems_filesystem_location_info_t* parent_loc,
                      const char*                       name)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (to_loc);
  rtems_rfs_ino          target = rtems_rfs_rtems_get_pathloc_ino (to_loc);
  rtems_rfs_ino          parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc);
  int                    rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_LINK))
    printf ("rtems-rfs-rtems: link: in: parent:%" PRId32 " target:%" PRId32 "\n",
            parent, target);

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_link (fs, name, strlen (name), parent, target, false);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("link: linking", rc);
	}

  rtems_rfs_rtems_unlock (fs);

	return 0;
}

/**
 * Routine to remove a link node from the file system.
 *
 * @param parent_loc
 * @param loc
 * @return int
 */

static int
rtems_rfs_rtems_unlink (rtems_filesystem_location_info_t* parent_loc,
                        rtems_filesystem_location_info_t* loc)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (parent_loc);
  rtems_rfs_ino          parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc);
  rtems_rfs_ino          ino = rtems_rfs_rtems_get_pathloc_ino (loc);
  uint32_t               doff = rtems_rfs_rtems_get_pathloc_doff (loc);
  int                    rc;

  rtems_rfs_rtems_lock (fs);

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_UNLINK))
    printf("rtems-rfs-rtems: unlink: parent:%" PRId32 " doff:%" PRIu32 " ino:%" PRId32 "\n",
           parent, doff, ino);

  rc = rtems_rfs_unlink (fs, parent, ino, doff, rtems_rfs_unlink_dir_denied);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("unlink: unlink inode", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return 0;
}

/**
 * The following verifies that and returns the type of node that the loc refers
 * to.
 *
 * @param pathloc
 * @return rtems_filesystem_node_types_t
 */

static rtems_filesystem_node_types_t
rtems_rfs_rtems_node_type (rtems_filesystem_location_info_t* pathloc)
{
  rtems_rfs_file_system*        fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino                 ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  rtems_filesystem_node_types_t type;
  rtems_rfs_inode_handle        inode;
  uint16_t                      mode;
  int                           rc;

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_inode_open (fs, ino, &inode, true);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("node_type: opening inode", rc);
  }

  /*
   * Do not return RTEMS_FILESYSTEM_HARD_LINK because this would result in an
   * eval link which does not make sense in the case of the RFS file
   * system. All directory entries are links to an inode. A link such as a HARD
   * link is actually the normal path to a regular file, directory, device
   * etc's inode. Links to inodes can be considered "the real" one, yet they
   * are all links.
   */
  mode = rtems_rfs_inode_get_mode (&inode);
  if (RTEMS_RFS_S_ISDIR (mode))
    type = RTEMS_FILESYSTEM_DIRECTORY;
  else if (RTEMS_RFS_S_ISLNK (mode))
    type = RTEMS_FILESYSTEM_SYM_LINK;
  else if (RTEMS_RFS_S_ISBLK (mode) || RTEMS_RFS_S_ISCHR (mode))
    type = RTEMS_FILESYSTEM_DEVICE;
  else
    type = RTEMS_FILESYSTEM_MEMORY_FILE;

  rc = rtems_rfs_inode_close (fs, &inode);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("node_type: closing inode", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return type;
}

/**
 * This routine is the implementation of the chown() system call for the
 * RFS.
 *
 * @param pathloc
 * @param owner
 * @param group
 * return int
 */

static int
rtems_rfs_rtems_chown (rtems_filesystem_location_info_t *pathloc,
                       uid_t                             owner,
                       gid_t                             group)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino          ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  rtems_rfs_inode_handle inode;
#if defined (RTEMS_POSIX_API)
  uid_t                  uid;
#endif
  int                    rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_CHOWN))
    printf ("rtems-rfs-rtems: chown: in: ino:%" PRId32 " uid:%d gid:%d\n",
            ino, owner, group);

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_inode_open (fs, ino, &inode, true);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("chown: opening inode", rc);
  }

  /*
   *  Verify I am the owner of the node or the super user.
   */

#if defined (RTEMS_POSIX_API)
  uid = geteuid();

  if ((uid != rtems_rfs_inode_get_uid (&inode)) && (uid != 0))
  {
    rtems_rfs_inode_close (fs, &inode);
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("chown: not able", EPERM);
  }
#endif

  rtems_rfs_inode_set_uid_gid (&inode, owner, group);

  rc = rtems_rfs_inode_close (fs, &inode);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("chown: closing inode", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return 0;
}

/**
 * This routine is the implementation of the utime() system call for the
 * RFS.
 *
 * @param pathloc
 * @param atime
 * @param mtime
 * return int
 */

static int
rtems_rfs_rtems_utime(rtems_filesystem_location_info_t* pathloc,
                      time_t                            atime,
                      time_t                            mtime)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino          ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  rtems_rfs_inode_handle inode;
  int                    rc;

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_inode_open (fs, ino, &inode, true);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("utime: read inode", rc);
  }

  rtems_rfs_inode_set_atime (&inode, atime);
  rtems_rfs_inode_set_mtime (&inode, mtime);

  rc = rtems_rfs_inode_close (fs, &inode);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("utime: closing inode", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return 0;
}

/**
 * The following rouine creates a new symbolic link node under parent with the
 * name given in name. The node is set to point to the node at to_loc.
 *
 * @param parent_loc
 * @param link_name
 * @param node_name
 * return int
 */

static int
rtems_rfs_rtems_symlink (rtems_filesystem_location_info_t* parent_loc,
                         const char*                       link_name,
                         const char*                       node_name)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (parent_loc);
  rtems_rfs_ino          parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc);
  uid_t                  uid;
  gid_t                  gid;
  int                    rc;

#if defined(RTEMS_POSIX_API)
  uid = geteuid ();
  gid = getegid ();
#else
  uid = 0;
  gid = 0;
#endif

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_symlink (fs, node_name, strlen (node_name),
                          link_name, strlen (link_name),
                          uid, gid, parent);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("symlink: linking", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return 0;
}

/**
 * The following rouine puts the symblic links destination name into buf.
 *
 * @param loc
 * @param buf
 * @param bufsize
 * @return int
 */

static ssize_t
rtems_rfs_rtems_readlink (rtems_filesystem_location_info_t* pathloc,
                          char*                             buf,
                          size_t                            bufsize)
{
  rtems_rfs_file_system*  fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino           ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  size_t                  length;
  int                     rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_READLINK))
    printf ("rtems-rfs-rtems: readlink: in: ino:%" PRId32 "\n", ino);

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_symlink_read (fs, ino, buf, bufsize, &length);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("readlink: reading link", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return (int) length;
}

int
rtems_rfs_rtems_fchmod (rtems_filesystem_location_info_t* pathloc,
                        mode_t                            mode)
{
  rtems_rfs_file_system*  fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino           ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  rtems_rfs_inode_handle  inode;
  uint16_t                imode;
#if defined (RTEMS_POSIX_API)
  uid_t                   uid;
#endif
  int                     rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FCHMOD))
    printf ("rtems-rfs-rtems: fchmod: in: ino:%" PRId32 " mode:%06" PRIomode_t "\n",
            ino, mode);

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_inode_open (fs, ino, &inode, true);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("fchmod: opening inode", rc);
  }

  imode = rtems_rfs_inode_get_mode (&inode);

  /*
   *  Verify I am the owner of the node or the super user.
   */
#if defined (RTEMS_POSIX_API)
  uid = geteuid();

  if ((uid != rtems_rfs_inode_get_uid (&inode)) && (uid != 0))
  {
    rtems_rfs_inode_close (fs, &inode);
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("fchmod: checking uid", EPERM);
  }
#endif

  imode &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
  imode |= mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);

  rtems_rfs_inode_set_mode (&inode, imode);

  rc = rtems_rfs_inode_close (fs, &inode);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("fchmod: closing inode", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return 0;
}

int
rtems_rfs_rtems_fstat (rtems_filesystem_location_info_t* pathloc,
                       struct stat*                      buf)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino          ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  rtems_rfs_inode_handle inode;
  rtems_rfs_file_shared* shared;
  uint16_t               mode;
  int                    rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_STAT))
    printf ("rtems-rfs-rtems: stat: in: ino:%" PRId32 "\n", ino);

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_inode_open (fs, ino, &inode, true);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("stat: opening inode", rc);
  }

  mode = rtems_rfs_inode_get_mode (&inode);

  if (RTEMS_RFS_S_ISCHR (mode) || RTEMS_RFS_S_ISBLK (mode))
  {
    buf->st_rdev =
      rtems_filesystem_make_dev_t (rtems_rfs_inode_get_block (&inode, 0),
                                   rtems_rfs_inode_get_block (&inode, 1));
  }

  buf->st_dev     = rtems_rfs_fs_device (fs);
  buf->st_ino     = rtems_rfs_inode_ino (&inode);
  buf->st_mode    = rtems_rfs_rtems_mode (mode);
  buf->st_nlink   = rtems_rfs_inode_get_links (&inode);
  buf->st_uid     = rtems_rfs_inode_get_uid (&inode);
  buf->st_gid     = rtems_rfs_inode_get_gid (&inode);

  /*
   * Need to check is the ino is an open file. If so we take the values from
   * the open file rather than the inode.
   */
  shared = rtems_rfs_file_get_shared (fs, rtems_rfs_inode_ino (&inode));

  if (shared)
  {
    buf->st_atime   = rtems_rfs_file_shared_get_atime (shared);
    buf->st_mtime   = rtems_rfs_file_shared_get_mtime (shared);
    buf->st_ctime   = rtems_rfs_file_shared_get_ctime (shared);
    buf->st_blocks  = rtems_rfs_file_shared_get_block_count (shared);

    if (S_ISLNK (buf->st_mode))
      buf->st_size = rtems_rfs_file_shared_get_block_offset (shared);
    else
      buf->st_size = rtems_rfs_file_shared_get_size (fs, shared);
  }
  else
  {
    buf->st_atime   = rtems_rfs_inode_get_atime (&inode);
    buf->st_mtime   = rtems_rfs_inode_get_mtime (&inode);
    buf->st_ctime   = rtems_rfs_inode_get_ctime (&inode);
    buf->st_blocks  = rtems_rfs_inode_get_block_count (&inode);

    if (S_ISLNK (buf->st_mode))
      buf->st_size = rtems_rfs_inode_get_block_offset (&inode);
    else
      buf->st_size = rtems_rfs_inode_get_size (fs, &inode);
  }

  buf->st_blksize = rtems_rfs_fs_block_size (fs);

  rc = rtems_rfs_inode_close (fs, &inode);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("stat: closing inode", rc);
  }

  rtems_rfs_rtems_unlock (fs);
  return 0;
}

/**
 * Routine to create a node in the RFS file system.
 *
 * @param name
 * @param mode
 * @param dev
 * @param pathloc
 * @return int
 */

static int
rtems_rfs_rtems_mknod (const char                       *name,
                       mode_t                            mode,
                       dev_t                             dev,
                       rtems_filesystem_location_info_t *pathloc)
{
  rtems_rfs_file_system*  fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino           parent = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  rtems_rfs_ino           ino;
  rtems_rfs_inode_handle  inode;
  uid_t                   uid;
  gid_t                   gid;
  int                     rc;

#if defined(RTEMS_POSIX_API)
  uid = geteuid ();
  gid = getegid ();
#else
  uid = 0;
  gid = 0;
#endif

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_inode_create (fs, parent, name, strlen (name),
                               rtems_rfs_rtems_imode (mode),
                               1, uid, gid, &ino);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("mknod: inode create", rc);
  }

  rc = rtems_rfs_inode_open (fs, ino, &inode, true);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("mknod: inode open", rc);
  }

  if (S_ISDIR(mode) || S_ISREG(mode))
  {
  }
  else if (S_ISCHR (mode) || S_ISBLK (mode))
  {
    int major;
    int minor;
    rtems_filesystem_split_dev_t (dev, major, minor);
    rtems_rfs_inode_set_block (&inode, 0, major);
    rtems_rfs_inode_set_block (&inode, 1, minor);
  }
  else
  {
    rtems_rfs_inode_close (fs, &inode);
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("mknod: bad mode", EINVAL);
  }

  rc = rtems_rfs_inode_close (fs, &inode);
  if (rc > 0)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("mknod: closing inode", rc);
  }

  rtems_rfs_rtems_unlock (fs);
  return 0;
}

/**
 * Routine to remove a node from the RFS file system.
 *
 * @param parent_pathloc
 * @param pathloc
 * @return int
 */
int
rtems_rfs_rtems_rmnod (rtems_filesystem_location_info_t* parent_pathloc,
                       rtems_filesystem_location_info_t* pathloc)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  rtems_rfs_ino          parent = rtems_rfs_rtems_get_pathloc_ino (parent_pathloc);
  rtems_rfs_ino          ino = rtems_rfs_rtems_get_pathloc_ino (pathloc);
  uint32_t               doff = rtems_rfs_rtems_get_pathloc_doff (pathloc);
  int                    rc;

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_RMNOD))
    printf ("rtems-rfs: rmnod: parent:%" PRId32 " doff:%" PRIu32 ", ino:%" PRId32 "\n",
            parent, doff, ino);

  rtems_rfs_rtems_lock (fs);

  rc = rtems_rfs_unlink (fs, parent, ino, doff, rtems_rfs_unlink_dir_denied);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("rmnod: unlinking", rc);
  }

  rtems_rfs_rtems_unlock (fs);
  return 0;
}

/**
 * The following routine does a sync on an inode node. Currently it flushes
 * everything related to this device.
 *
 * @param iop
 * @return int
 */
int
rtems_rfs_rtems_fdatasync (rtems_libio_t* iop)
{
  int rc;

  rc = rtems_rfs_buffer_sync (rtems_rfs_rtems_pathloc_dev (&iop->pathinfo));
  if (rc)
    return rtems_rfs_rtems_error ("fdatasync: sync", rc);

  return 0;
}

/**
 * Rename the node.
 *
 * @param old_parent_loc The old name's parent location.
 * @param old_loc The old name's location.
 * @param new_parent_loc The new name's parent location.
 * @param new_name The new name.
 * @return int
 */
static int
rtems_rfs_rtems_rename(rtems_filesystem_location_info_t* old_parent_loc,
                       rtems_filesystem_location_info_t* old_loc,
                       rtems_filesystem_location_info_t* new_parent_loc,
                       const char*                       new_name)
{
  rtems_rfs_file_system*  fs = rtems_rfs_rtems_pathloc_dev (old_loc);
  rtems_rfs_ino           old_parent;
  rtems_rfs_ino           new_parent;
  rtems_rfs_ino           ino;
  uint32_t                doff;
  int                     rc;

  old_parent = rtems_rfs_rtems_get_pathloc_ino (old_parent_loc);
  new_parent = rtems_rfs_rtems_get_pathloc_ino (new_parent_loc);

  ino  = rtems_rfs_rtems_get_pathloc_ino (old_loc);
  doff = rtems_rfs_rtems_get_pathloc_doff (old_loc);

  if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_RENAME))
    printf ("rtems-rfs: rename: ino:%" PRId32 " doff:%" PRIu32 ", new parent:%" PRId32 " new name:%s\n",
            ino, doff, new_parent, new_name);

  rtems_rfs_rtems_lock (fs);

  /*
   * Link to the inode before unlinking so the inode is not erased when
   * unlinked.
   */
  rc = rtems_rfs_link (fs, new_name, strlen (new_name), new_parent, ino, true);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("rename: linking", rc);
  }

  /*
   * Unlink all inodes even directories with the dir option as false because a
   * directory may not be empty.
   */
  rc = rtems_rfs_unlink (fs, old_parent, ino, doff,
                         rtems_rfs_unlink_dir_allowed);
  if (rc)
  {
    rtems_rfs_rtems_unlock (fs);
    return rtems_rfs_rtems_error ("rename: unlinking", rc);
  }

  rtems_rfs_rtems_unlock (fs);

  return 0;
}

/**
 * Return the file system stat data.
 *
 * @param pathloc
 * @param sb
 * @return int
 */
static int
rtems_rfs_rtems_statvfs (rtems_filesystem_location_info_t* pathloc,
                         struct statvfs*                   sb)
{
  rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc);
  size_t                 blocks;
  size_t                 inodes;

  rtems_rfs_group_usage (fs, &blocks, &inodes);

  sb->f_bsize   = rtems_rfs_fs_block_size (fs);
  sb->f_frsize  = rtems_rfs_fs_media_block_size (fs);
  sb->f_blocks  = rtems_rfs_fs_media_blocks (fs);
  sb->f_bfree   = rtems_rfs_fs_blocks (fs) - blocks;
  sb->f_bavail  = sb->f_bfree;
  sb->f_files   = rtems_rfs_fs_inodes (fs);
  sb->f_ffree   = rtems_rfs_fs_inodes (fs) - inodes;
  sb->f_favail  = sb->f_ffree;
  sb->f_fsid    = RTEMS_RFS_SB_MAGIC;
  sb->f_flag    = rtems_rfs_fs_flags (fs);
  sb->f_namemax = rtems_rfs_fs_max_name (fs);

  return 0;
}

/**
 *  Handler table for RFS link nodes
 */
const rtems_filesystem_file_handlers_r rtems_rfs_rtems_link_handlers =
{
  .open_h      = rtems_filesystem_default_open,
  .close_h     = rtems_filesystem_default_close,
  .read_h      = rtems_filesystem_default_read,
  .write_h     = rtems_filesystem_default_write,
  .ioctl_h     = rtems_filesystem_default_ioctl,
  .lseek_h     = rtems_filesystem_default_lseek,
  .fstat_h     = rtems_rfs_rtems_fstat,
  .fchmod_h    = rtems_filesystem_default_fchmod,
  .ftruncate_h = rtems_filesystem_default_ftruncate,
  .fsync_h     = rtems_filesystem_default_fsync,
  .fdatasync_h = rtems_filesystem_default_fdatasync,
  .fcntl_h     = rtems_filesystem_default_fcntl,
  .rmnod_h     = rtems_rfs_rtems_rmnod
};

/**
 * Forward decl for the ops table.
 */

int rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t *mt_entry,
                                const void                           *data);
int rtems_rfs_rtems_shutdown (rtems_filesystem_mount_table_entry_t *mt_entry);

/**
 * RFS file system operations table.
 */
const rtems_filesystem_operations_table rtems_rfs_ops =
{
  .evalpath_h     = rtems_rfs_rtems_eval_path,
  .evalformake_h  = rtems_rfs_rtems_eval_for_make,
  .link_h         = rtems_rfs_rtems_link,
  .unlink_h       = rtems_rfs_rtems_unlink,
  .node_type_h    = rtems_rfs_rtems_node_type,
  .mknod_h        = rtems_rfs_rtems_mknod,
  .chown_h        = rtems_rfs_rtems_chown,
  .freenod_h      = rtems_filesystem_default_freenode,
  .mount_h        = rtems_filesystem_default_mount,
  .fsmount_me_h   = rtems_rfs_rtems_initialise,
  .unmount_h      = rtems_filesystem_default_unmount,
  .fsunmount_me_h = rtems_rfs_rtems_shutdown,
  .utime_h        = rtems_rfs_rtems_utime,
  .eval_link_h    = rtems_filesystem_default_evaluate_link, /* never called cause we lie in the node type */
  .symlink_h      = rtems_rfs_rtems_symlink,
  .readlink_h     = rtems_rfs_rtems_readlink,
  .rename_h       = rtems_rfs_rtems_rename,
  .statvfs_h      = rtems_rfs_rtems_statvfs
};

/**
 * Open the file system.
 */

int
rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t* mt_entry,
                            const void*                           data)
{
  rtems_rfs_rtems_private* rtems;
  rtems_rfs_file_system*   fs;
  uint32_t                 flags = 0;
  uint32_t                 max_held_buffers = RTEMS_RFS_FS_MAX_HELD_BUFFERS;
  const char*              options = data;
  int                      rc;

  /*
   * Parse the options the user specifiies.
   */
  while (options)
  {
    printf ("options=%s\n", options);
    if (strncmp (options, "hold-bitmaps",
                 sizeof ("hold-bitmaps") - 1) == 0)
      flags |= RTEMS_RFS_FS_BITMAPS_HOLD;
    else if (strncmp (options, "no-local-cache",
                      sizeof ("no-local-cache") - 1) == 0)
      flags |= RTEMS_RFS_FS_NO_LOCAL_CACHE;
    else if (strncmp (options, "max-held-bufs",
                      sizeof ("max-held-bufs") - 1) == 0)
    {
      max_held_buffers = strtoul (options + sizeof ("max-held-bufs"), 0, 0);
    }
    else
      return rtems_rfs_rtems_error ("initialise: invalid option", EINVAL);

    options = strchr (options, ',');
    if (options)
    {
      ++options;
      if (*options == '\0')
        options = NULL;
    }
  }

  rtems = malloc (sizeof (rtems_rfs_rtems_private));
  if (!rtems)
    return rtems_rfs_rtems_error ("initialise: local data", ENOMEM);

  memset (rtems, 0, sizeof (rtems_rfs_rtems_private));

  rc = rtems_rfs_mutex_create (&rtems->access);
  if (rc > 0)
  {
    free (rtems);
    return rtems_rfs_rtems_error ("initialise: cannot create mutex", rc);
  }

  rc = rtems_rfs_mutex_lock (&rtems->access);
  if (rc > 0)
  {
    rtems_rfs_mutex_destroy (&rtems->access);
    free (rtems);
    return rtems_rfs_rtems_error ("initialise: cannot lock access  mutex", rc);
  }

  rc = rtems_rfs_fs_open (mt_entry->dev, rtems, flags, max_held_buffers, &fs);
  if (rc)
  {
    free (rtems);
    return rtems_rfs_rtems_error ("initialise: open", rc);
  }

  mt_entry->fs_info = fs;

  mt_entry->mt_fs_root.node_access = (void*) RTEMS_RFS_ROOT_INO;
  mt_entry->mt_fs_root.handlers    = &rtems_rfs_rtems_dir_handlers;
  mt_entry->mt_fs_root.ops         = &rtems_rfs_ops;

  rtems_rfs_rtems_unlock (fs);

  return 0;
}

/**
 * Shutdown the file system.
 */
int
rtems_rfs_rtems_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry)
{
  rtems_rfs_file_system*   fs = mt_entry->fs_info;
  rtems_rfs_rtems_private* rtems;
  int                      rc;

  rtems = rtems_rfs_fs_user (fs);

  rc = rtems_rfs_fs_close(fs);

  rtems_rfs_mutex_destroy (&rtems->access);
  free (rtems);

  return rtems_rfs_rtems_error ("shutdown: close", rc);
}