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; }
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; }