summaryrefslogblamecommitdiffstats
path: root/cpukit/libfs/src/rfs/rtems-rfs-file.c
blob: 4d722b6d84a79ae027d757b93f81ab8c2f2bf69f (plain) (tree)
1
2
3
4
5
6
7
8
9


        
                                          
                     
  


                                




                                                           
                                         

   



                   
                     
                   
 







                                                 
                                                   






                                                  
                                                            
 





















                                                                             
 








                                                                           
                                                                     















                                                                              
 
































                                                                            
                                                                     



                                                       
                                                                            

   
                         
                          
 
                 
 










                                                    
 
                                                   
                                                              






                                       

                                                              
 










                                                                 



                                                                  
     
 



                                                              
                                                                                    



                                                              
 



                                                            
                                                                                      



                                                              
 
                                                            





                                                           
 






                                                                              
 








                                                          
 
                                                
                                                                          
                                                                           
 




                                                           
 
                        
 













                                                                
 




                                                     
 



















                                                                                
 
                                                  
                                                                               
                                                  
 





                                                                         
 


                                                                       


                                                                         
 


                                                           
                                                               
                              
 













                                                     
                                                       
                                           
 







                                                                          


                                                                             
 






















                                                                               
 








                                                                     
 


                                                           
 
                                                
                                                                                

                                                                      
 














                                                                    
 


















                                                                          
                                                            
 










                                                                               
                                                                        
                                                            
   
                                          




































                                                                               









                                                          
                          

                                                
                                                                      
 

                                      
    


                                                                 
     
                       
   
      
                                                      
       






                                                                          
     
                          
       
          
                               
           






                                                                      
 
                     
         



                                       
            


                                                                               
             


























                                                                                
 





                                                                               

                      
 

                                                          
 
                                                                       
 



                                                                                
 

                                      
       






                                  
 



                                                                       
 

                                                                          
 








                                                                       
 
                                                          
 




                                                                          
     
 

                                                                        
 



                                             


















                                                       
/**
 * @file
 *
 * @brief RTEMS File Systems File Routines
 * @ingroup rtems_rfs
 *
 * These functions manage files.
 */

/*
 *  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.org/license/LICENSE.
 */

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

#include <inttypes.h>
#include <string.h>

#include <rtems/rfs/rtems-rfs-block-pos.h>
#include <rtems/rfs/rtems-rfs-file.h>
#include <rtems/rfs/rtems-rfs-file-system.h>
#include <rtems/rfs/rtems-rfs-trace.h>

int
rtems_rfs_file_open (rtems_rfs_file_system*  fs,
                     rtems_rfs_ino           ino,
                     int                     oflag,
                     rtems_rfs_file_handle** file)
{
  rtems_rfs_file_handle* handle;
  rtems_rfs_file_shared* shared;
  int                    rc;

  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
    printf ("rtems-rfs: file-open: ino=%" PRId32 "\n", ino);

  *file = NULL;

  /*
   * Allocate a new handle and initialise it. Do this before we deal with the
   * shared node data so we do not have to be concerned with reference
   * counting.
   */
  handle = malloc (sizeof (rtems_rfs_file_handle));
  if (!handle)
    return ENOMEM;

  memset (handle, 0, sizeof (rtems_rfs_file_handle));

  rc = rtems_rfs_buffer_handle_open (fs, &handle->buffer);
  if (rc > 0)
  {
    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
      printf ("rtems-rfs: file-open: buffer handle open failed: %d: %s\n",
              rc, strerror (rc));
    free (handle);
    return rc;
  }

  /*
   * Scan the file system data list of open files for this ino. If found up
   * the reference count and return the pointer to the data.
   */
  shared = rtems_rfs_file_get_shared (fs, ino);
  if (shared)
  {
    shared->references++;
    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
      printf ("rtems-rfs: file-open: ino=%" PRId32 " shared\n", ino);
  }
  else
  {
    /*
     * None exists so create. Copy in the shared parts of the inode we hold in
     * memory.
     */
    shared = malloc (sizeof (rtems_rfs_file_shared));
    if (!shared)
    {
      rtems_rfs_buffer_handle_close (fs, &handle->buffer);
      free (handle);
      return ENOMEM;
    }

    memset (shared, 0, sizeof (rtems_rfs_file_shared));

    rc = rtems_rfs_inode_open (fs, ino, &shared->inode, true);
    if (rc > 0)
    {
      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
        printf ("rtems-rfs: file-open: inode open failed: %d: %s\n",
                rc, strerror (rc));
      free (shared);
      rtems_rfs_buffer_handle_close (fs, &handle->buffer);
      free (handle);
      return rc;
    }

    rc = rtems_rfs_block_map_open (fs, &shared->inode, &shared->map);
    if (rc > 0)
    {
      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
        printf ("rtems-rfs: file-open: block map open failed: %d: %s\n",
                rc, strerror (rc));
      rtems_rfs_inode_close (fs, &shared->inode);
      free (shared);
      rtems_rfs_buffer_handle_close (fs, &handle->buffer);
      free (handle);
      return rc;
    }

    shared->references = 1;
    shared->size.count = rtems_rfs_inode_get_block_count (&shared->inode);
    shared->size.offset = rtems_rfs_inode_get_block_offset (&shared->inode);
    shared->atime = rtems_rfs_inode_get_atime (&shared->inode);
    shared->mtime = rtems_rfs_inode_get_mtime (&shared->inode);
    shared->ctime = rtems_rfs_inode_get_ctime (&shared->inode);
    shared->fs = fs;

    rtems_chain_append_unprotected (&fs->file_shares, &shared->link);

    rtems_rfs_inode_unload (fs, &shared->inode, false);

    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
      printf ("rtems-rfs: file-open: ino=%" PRId32 " share created\n", ino);
  }

  handle->flags  = oflag;
  handle->shared = shared;

  *file = handle;

  return 0;
}

int
rtems_rfs_file_close (rtems_rfs_file_system* fs,
                      rtems_rfs_file_handle* handle)
{
  int rrc;
  int rc;

  rrc = 0;

  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
    printf ("rtems-rfs: file-close: entry: ino=%" PRId32 "\n",
            handle->shared->inode.ino);

  if (handle->shared->references > 0)
    handle->shared->references--;

  if (handle->shared->references == 0)
  {
    if (!rtems_rfs_inode_is_loaded (&handle->shared->inode))
      rrc = rtems_rfs_inode_load (fs, &handle->shared->inode);

    if (rrc == 0)
    {
      /*
       * @todo This could be clever and only update if different.
       */
      rtems_rfs_inode_set_atime (&handle->shared->inode,
                                 handle->shared->atime);
      rtems_rfs_inode_set_mtime (&handle->shared->inode,
                                 handle->shared->mtime);
      rtems_rfs_inode_set_ctime (&handle->shared->inode,
                                 handle->shared->ctime);
      if (!rtems_rfs_block_size_equal (&handle->shared->size,
                                       &handle->shared->map.size))
        rtems_rfs_block_map_set_size (&handle->shared->map,
                                      &handle->shared->size);
    }

    rc = rtems_rfs_block_map_close (fs, &handle->shared->map);
    if (rc > 0)
    {
      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
        printf ("rtems-rfs: file-close: map close error: ino=%" PRId32 ": %d: %s\n",
                handle->shared->inode.ino, rc, strerror (rc));
      if (rrc == 0)
        rrc = rc;
    }

    rc = rtems_rfs_inode_close (fs, &handle->shared->inode);
    if (rc > 0)
    {
      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
        printf ("rtems-rfs: file-close: inode close error: ino=%" PRId32 ": %d: %s\n",
                handle->shared->inode.ino, rc, strerror (rc));
      if (rrc == 0)
        rrc = rc;
    }

    rtems_chain_extract_unprotected (&handle->shared->link);
    free (handle->shared);
  }

  rc = rtems_rfs_buffer_handle_close (fs, &handle->buffer);
  if ((rrc == 0) && (rc > 0))
    rrc = rc;

  if (rrc > 0)
  {
    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
      printf ("rtems-rfs: file-close: result: %d: %s\n", rrc, strerror (rrc));
  }

  free (handle);

  return rrc;
}

int
rtems_rfs_file_io_start (rtems_rfs_file_handle* handle,
                         size_t*                available,
                         bool                   read)
{
  size_t size;

  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
    printf ("rtems-rfs: file-io: start: %s pos=%" PRIu32 ":%" PRIu32 "\n",
            read ? "read" : "write",  handle->bpos.bno, handle->bpos.boff);

  if (!rtems_rfs_buffer_handle_has_block (&handle->buffer))
  {
    rtems_rfs_buffer_block block;
    bool                   request_read;
    int                    rc;

    request_read = read;

    rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle),
                                   rtems_rfs_file_map (handle),
                                   rtems_rfs_file_bpos (handle),
                                   &block);
    if (rc > 0)
    {
      /*
       * Has the read reached the EOF ?
       */
      if (read && (rc == ENXIO))
      {
        *available = 0;
        return 0;
      }

      if (rc != ENXIO)
        return rc;

      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
        printf ("rtems-rfs: file-io: start: grow\n");

      rc = rtems_rfs_block_map_grow (rtems_rfs_file_fs (handle),
                                     rtems_rfs_file_map (handle),
                                     1, &block);
      if (rc > 0)
        return rc;

      request_read = false;
    }
    else
    {
      /*
       * If this is a write check if the write starts within a block or the
       * amount of data is less than a block size. If it is read the block
       * rather than getting a block to fill.
       */
      if (!read &&
          (rtems_rfs_file_block_offset (handle) ||
           (*available < rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)))))
        request_read = true;
    }

    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
      printf ("rtems-rfs: file-io: start: block=%" PRIu32 " request-read=%s\n",
              block, request_read ? "yes" : "no");

    rc = rtems_rfs_buffer_handle_request (rtems_rfs_file_fs (handle),
                                          rtems_rfs_file_buffer (handle),
                                          block, request_read);
    if (rc > 0)
      return rc;
  }

  if (read
      && rtems_rfs_block_map_last (rtems_rfs_file_map (handle))
      && rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle)))
    size = rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle));
  else
    size = rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));

  *available = size - rtems_rfs_file_block_offset (handle);

  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
    printf ("rtems-rfs: file-io: start: available=%zu (%zu)\n",
            *available, size);

  return 0;
}

int
rtems_rfs_file_io_end (rtems_rfs_file_handle* handle,
                       size_t                 size,
                       bool                   read)
{
  bool atime;
  bool mtime;
  bool length;
  int  rc = 0;

  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
    printf ("rtems-rfs: file-io:   end: %s size=%zu\n",
            read ? "read" : "write", size);

  if (rtems_rfs_buffer_handle_has_block (&handle->buffer))
  {
    if (!read)
      rtems_rfs_buffer_mark_dirty (rtems_rfs_file_buffer (handle));
    rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
                                          rtems_rfs_file_buffer (handle));
    if (rc > 0)
    {
      printf (
        "rtems-rfs: file-io:   end: error on release: %s size=%zu: %d: %s\n",
        read ? "read" : "write", size, rc, strerror (rc));

      return rc;
    }
  }

  /*
   * Update the handle's position. Only a block size can be handled at a time
   * so no special maths is needed. If the offset is bigger than the block size
   * increase the block number and adjust the offset.
   *
   * If we are the last block and the position is past the current size update
   * the size with the new length. The map holds the block count.
   */
  handle->bpos.boff += size;

  if (handle->bpos.boff >=
      rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)))
  {
    handle->bpos.bno++;
    handle->bpos.boff -= rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));
  }

  length = false;
  mtime = false;

  if (!read &&
      rtems_rfs_block_map_past_end (rtems_rfs_file_map (handle),
                                    rtems_rfs_file_bpos (handle)))
  {
    rtems_rfs_block_map_set_size_offset (rtems_rfs_file_map (handle),
                                         handle->bpos.boff);
    length = true;
    mtime = true;
  }

  atime  = rtems_rfs_file_update_atime (handle);
  mtime  = rtems_rfs_file_update_mtime (handle) && mtime;
  length = rtems_rfs_file_update_length (handle) && length;

  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
    printf ("rtems-rfs: file-io:   end: pos=%" PRIu32 ":%" PRIu32 " %c %c %c\n",
            handle->bpos.bno, handle->bpos.boff,
            atime ? 'A' : '-', mtime ? 'M' : '-', length ? 'L' : '-');

  if (atime || mtime)
  {
    time_t now = time (NULL);
    if (read && atime)
      handle->shared->atime = now;
    if (!read && mtime)
      handle->shared->mtime = now;
  }
  if (length)
  {
    handle->shared->size.count =
      rtems_rfs_block_map_count (rtems_rfs_file_map (handle));
    handle->shared->size.offset =
      rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle));
  }

  return rc;
}

int
rtems_rfs_file_io_release (rtems_rfs_file_handle* handle)
{
  int rc = 0;
  if (rtems_rfs_buffer_handle_has_block (&handle->buffer))
    rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
                                          rtems_rfs_file_buffer (handle));
  return rc;
}

int
rtems_rfs_file_seek (rtems_rfs_file_handle* handle,
                     rtems_rfs_pos          pos,
                     rtems_rfs_pos*         new_pos)
{
  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
    printf ("rtems-rfs: file-seek: new=%" PRIu64 "\n", pos);

  /*
   * This call only sets the position if it is in a valid part of the file. The
   * user can request past the end of the file then write to extend the
   * file. The lseek entry states:
   *
   *    "Although lseek() may position the file offset beyond the end of the
   *     file, this function does not itself extend the size of the file."
   *
   * This means the file needs to set the file size to the pos only when a
   * write occurs.
   */
  if (pos <= rtems_rfs_file_shared_get_size (rtems_rfs_file_fs (handle),
                                            handle->shared))
  {
    rtems_rfs_file_set_bpos (handle, pos);
    
    /*
     * If the file has a block check if it maps to the current position and it
     * does not release it. That will force us to get the block at the new
     * position when the I/O starts.
     */
    if (rtems_rfs_buffer_handle_has_block (&handle->buffer))
    {
      rtems_rfs_buffer_block block;
      int                    rc;
      
      rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle),
                                     rtems_rfs_file_map (handle),
                                     rtems_rfs_file_bpos (handle),
                                     &block);
      if (rc > 0)
        return rc;
      if (rtems_rfs_buffer_bnum (&handle->buffer) != block)
      {        
        rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
                                              rtems_rfs_file_buffer (handle));
        if (rc > 0)
          return rc;
      }
    }
  }
  else
  {
    /*
     * The seek is outside the current file so release any buffer. A write will
     * extend the file.
     */
    int rc = rtems_rfs_file_io_release (handle);
    if (rc > 0)
      return rc;
  }
  
  *new_pos = pos;
  return 0;
}

int
rtems_rfs_file_set_size (rtems_rfs_file_handle* handle,
                         rtems_rfs_pos          new_size)
{
  rtems_rfs_block_map* map  = rtems_rfs_file_map (handle);
  rtems_rfs_pos        size;
  int                  rc;

  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
    printf ("rtems-rfs: file-set-size: size=%" PRIu64 "\n", new_size);

  size = rtems_rfs_file_size (handle);
  
  /*
   * If the file is same size do nothing else grow or shrink it ?
   *
   * If the file does not change size do not update the times.
   */
  if (size != new_size)
  {
    /*
     * Short cut for the common truncate on open call.
     */
    if (new_size == 0)
    {
      rc = rtems_rfs_block_map_free_all (rtems_rfs_file_fs (handle), map);
      if (rc > 0)
        return rc;
    }
    else
    {
      if (size < new_size)
      {
        /*
         * Grow. Fill with 0's.
         */
        rtems_rfs_pos count;
        uint32_t      length;
        bool          read_block;

        count = new_size - size;
        length = rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));
        read_block = false;

        while (count)
        {
          rtems_rfs_buffer_block block;
          rtems_rfs_block_pos    bpos;
          uint8_t*               dst;

          /*
           * Get the block position for the current end of the file as seen by
           * the map. If not found and the EOF grow the map then fill the block
           * with 0.
           */
          rtems_rfs_block_size_get_bpos (rtems_rfs_block_map_size (map), &bpos);
          rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle),
                                         map, &bpos, &block);
          if (rc > 0)
          {
            /*
             * Have we reached the EOF ?
             */
            if (rc != ENXIO)
              return rc;

            rc = rtems_rfs_block_map_grow (rtems_rfs_file_fs (handle),
                                           map, 1, &block);
            if (rc > 0)
              return rc;
          }

          if (count < (length - bpos.boff))
          {
            length = count + bpos.boff;
            read_block = true;
            rtems_rfs_block_map_set_size_offset (map, length);
          }
          else
          {
            rtems_rfs_block_map_set_size_offset (map, 0);
          }

          /*
           * Only read the block if the length is not the block size.
           */
          rc = rtems_rfs_buffer_handle_request (rtems_rfs_file_fs (handle),
                                                rtems_rfs_file_buffer (handle),
                                                block, read_block);
          if (rc > 0)
            return rc;

          dst = rtems_rfs_buffer_data (&handle->buffer);
          memset (dst + bpos.boff, 0, length - bpos.boff);

          rtems_rfs_buffer_mark_dirty (rtems_rfs_file_buffer (handle));

          rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
                                                rtems_rfs_file_buffer (handle));
          if (rc > 0)
            return rc;

          count -= length - bpos.boff;
        }
      }
      else
      {
        /*
         * Shrink
         */
        rtems_rfs_block_no blocks;
        uint32_t           offset;

        blocks =
          rtems_rfs_block_map_count (map) -
          (((new_size - 1) /
            rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle))) + 1);

        offset =
          new_size % rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));

        if (blocks)
        {
          int rc;
          rc = rtems_rfs_block_map_shrink (rtems_rfs_file_fs (handle),
                                           rtems_rfs_file_map (handle),
                                           blocks);
          if (rc > 0)
            return rc;
        }

        rtems_rfs_block_map_set_size_offset (map, offset);

        if (rtems_rfs_block_pos_past_end (rtems_rfs_file_bpos (handle),
                                          rtems_rfs_block_map_size (map)))
          rtems_rfs_block_size_get_bpos (rtems_rfs_block_map_size (map),
                                         rtems_rfs_file_bpos (handle));
      }
    }

    handle->shared->size.count  = rtems_rfs_block_map_count (map);
    handle->shared->size.offset = rtems_rfs_block_map_size_offset (map);

    if (rtems_rfs_file_update_mtime (handle))
      handle->shared->mtime = time (NULL);
  }
  
  return 0;
}

rtems_rfs_file_shared*
rtems_rfs_file_get_shared (rtems_rfs_file_system* fs,
                           rtems_rfs_ino          ino)
{
  rtems_chain_node* node;
  node = rtems_chain_first (&fs->file_shares);
  while (!rtems_chain_is_tail (&fs->file_shares, node))
  {
    rtems_rfs_file_shared* shared;
    shared = (rtems_rfs_file_shared*) node;
    if (shared->inode.ino == ino)
      return shared;
    node = rtems_chain_next (node);
  }
  return NULL;
}