diff options
Diffstat (limited to 'cpukit/libfs/src/rfs/rtems-rfs-file.c')
-rw-r--r-- | cpukit/libfs/src/rfs/rtems-rfs-file.c | 596 |
1 files changed, 596 insertions, 0 deletions
diff --git a/cpukit/libfs/src/rfs/rtems-rfs-file.c b/cpukit/libfs/src/rfs/rtems-rfs-file.c new file mode 100644 index 0000000000..777726a23a --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-file.c @@ -0,0 +1,596 @@ +/* + * 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 Systems File Routines. + * + * These functions manage files. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.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, + uint32_t flags, + 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 (&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 = flags; + 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 (&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); + + *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); + + /* + * 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 + { + size = rtems_rfs_file_size (handle); + + /* + * If the file is same size do nothing else grow or shrink it ? + */ + if (size != new_size) + { + 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; +} |