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;
}
示例#2
0
int
rtems_rfs_block_map_shrink (rtems_rfs_file_system* fs,
                            rtems_rfs_block_map*   map,
                            size_t                 blocks)
{
  if (rtems_rfs_trace (RTEMS_RFS_TRACE_BLOCK_MAP_SHRINK))
    printf ("rtems-rfs: block-map-shrink: entry: blocks=%zd count=%" PRIu32 "\n",
            blocks, map->size.count);

  if (map->size.count == 0)
    return 0;

  if (blocks > map->size.count)
    blocks = map->size.count;

  while (blocks)
  {
    rtems_rfs_block_no block;
    rtems_rfs_block_no block_to_free;
    int                rc;

    block = map->size.count - 1;

    if (block < RTEMS_RFS_INODE_BLOCKS)
    {
      /*
       * We have less than RTEMS_RFS_INODE_BLOCKS so they are held in the
       * inode.
       */
      block_to_free = map->blocks[block];
      map->blocks[block] = 0;
    }
    else
    {
      /*
       * Single indirect access is occuring. It could still be doubly indirect.
       *
       * The 'direct' variable is the offset in to the indirect table of
       * blocks, and 'singly' is the inode block index of the singly indirect
       * table of block numbers.
       */
      rtems_rfs_block_no direct;
      rtems_rfs_block_no singly;

      direct = block % fs->blocks_per_block;
      singly = block / fs->blocks_per_block;

      if (block < fs->block_map_singly_blocks)
      {
        /*
         * Request the indirect block and then obtain the block number from the
         * indirect block.
         */
        rc = rtems_rfs_buffer_handle_request (fs, &map->singly_buffer,
                                              map->blocks[singly], true);
        if (rc > 0)
          return rc;

        block_to_free = rtems_rfs_block_get_number (&map->singly_buffer,
                                                    direct);

        rc = rtems_rfs_block_map_indirect_shrink (fs, map, &map->singly_buffer,
                                                  singly, direct);
        if (rc)
          return rc;
      }
      else if (block < fs->block_map_doubly_blocks)
      {
        /*
         * Doubly indirect tables are being used. The 'doubly' variable is the
         * index in to the inode's block table and points to a singly indirect
         * table of block numbers. The 'doubly_singly' variable is the index
         * into the doubly indirect table pointing to the singly indirect table
         * of block numbers that form the map. This is used later to determine
         * if the current doubly indirect table needs to be freed. The 'direct'
         * value is still valid for doubly indirect tables.
         */
        rtems_rfs_block_no doubly;
        rtems_rfs_block_no doubly_singly;

        doubly        = singly / fs->blocks_per_block;
        doubly_singly = singly % fs->blocks_per_block;

        rc = rtems_rfs_buffer_handle_request (fs, &map->doubly_buffer,
                                              map->blocks[doubly], true);
        if (rc > 0)
          return rc;

        singly = rtems_rfs_block_get_number (&map->doubly_buffer,
                                             doubly_singly);

        /*
         * Read the singly indirect table and get the block number.
         */
        rc = rtems_rfs_buffer_handle_request (fs, &map->singly_buffer,
                                              singly, true);
        if (rc > 0)
          return rc;

        block_to_free = rtems_rfs_block_get_number (&map->singly_buffer,
                                                    direct);

        if (direct == 0)
        {
          rc = rtems_rfs_group_bitmap_free (fs, false, singly);
          if (rc > 0)
            return rc;

          map->last_map_block = singly;

          rc = rtems_rfs_block_map_indirect_shrink (fs, map, &map->doubly_buffer,
                                                    doubly, doubly_singly);
          if (rc)
            return rc;
        }
      }
      else
      {
        rc = EIO;
        break;
      }
    }
    rc = rtems_rfs_group_bitmap_free (fs, false, block_to_free);
    if (rc > 0)
      return rc;
    map->size.count--;
    map->size.offset = 0;
    map->last_data_block = block_to_free;
    map->dirty = true;
    blocks--;
  }

  if (map->size.count == 0)
  {
    map->last_map_block = 0;
    map->last_data_block = 0;
  }

  /*
   * Keep the position inside the map.
   */
  if (rtems_rfs_block_pos_past_end (&map->bpos, &map->size))
    rtems_rfs_block_size_get_bpos (&map->size, &map->bpos);

  return 0;
}