/**
* @file
*
* @brief RTEMS File Systems Inode Routines
* @ingroup rtems_rfs
*
* These functions manage inodes in the RFS file system. An inode is part of a
* block that reside after the bitmaps in the group.
*/
/*
* 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.h>
#include <rtems/rfs/rtems-rfs-file-system.h>
#include <rtems/rfs/rtems-rfs-inode.h>
#include <rtems/rfs/rtems-rfs-dir.h>
int
rtems_rfs_inode_alloc (rtems_rfs_file_system* fs,
rtems_rfs_bitmap_bit goal,
rtems_rfs_ino* ino)
{
rtems_rfs_bitmap_bit bit;
int rc;
rc = rtems_rfs_group_bitmap_alloc (fs, goal, true, &bit);
*ino = bit;
return rc;
}
int
rtems_rfs_inode_free (rtems_rfs_file_system* fs,
rtems_rfs_ino ino)
{
rtems_rfs_bitmap_bit bit;
bit = ino;
return rtems_rfs_group_bitmap_free (fs, true, bit);
}
int
rtems_rfs_inode_open (rtems_rfs_file_system* fs,
rtems_rfs_ino ino,
rtems_rfs_inode_handle* handle,
bool load)
{
int group;
int gino;
int index;
int rc;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_OPEN))
printf ("rtems-rfs: inode-open: ino: %" PRIu32 "\n", ino);
if (ino == RTEMS_RFS_EMPTY_INO)
return EINVAL;
if ((ino - RTEMS_RFS_ROOT_INO) > rtems_rfs_fs_inodes (fs))
return EINVAL;
handle->ino = ino;
handle->node = NULL;
handle->loads = 0;
gino = ino - RTEMS_RFS_ROOT_INO;
group = gino / fs->group_inodes;
gino = gino % fs->group_inodes;
index = (gino / fs->inodes_per_block) + RTEMS_RFS_GROUP_INODE_BLOCK;
handle->offset = gino % fs->inodes_per_block;
handle->block = rtems_rfs_group_block (&fs->groups[group], index);
rc = rtems_rfs_buffer_handle_open (fs, &handle->buffer);
if ((rc == 0) && load)
rc = rtems_rfs_inode_load (fs, handle);
return rc;
}
int
rtems_rfs_inode_close (rtems_rfs_file_system* fs,
rtems_rfs_inode_handle* handle)
{
int rc;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_CLOSE))
printf ("rtems-rfs: inode-close: ino: %" PRIu32 "\n", handle->ino);
rc = rtems_rfs_inode_unload (fs, handle, true);
if ((rc == 0) && (handle->loads > 0))
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_CLOSE))
printf ("rtems-rfs: inode-close: bad loads number: %d\n",
handle->loads);
rc = EIO;
}
handle->ino = 0;
return rc;
}
int
rtems_rfs_inode_load (rtems_rfs_file_system* fs,
rtems_rfs_inode_handle* handle)
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_LOAD))
printf ("rtems-rfs: inode-load: ino=%" PRIu32 " loads=%i loaded=%s\n",
handle->ino, handle->loads,
rtems_rfs_inode_is_loaded (handle) ? "yes" : "no");
/*
* An inode does not move so once loaded no need to do again.
*/
if (!rtems_rfs_inode_is_loaded (handle))
{
int rc;
rc = rtems_rfs_buffer_handle_request (fs,&handle->buffer,
handle->block, true);
if (rc > 0)
return rc;
handle->node = rtems_rfs_buffer_data (&handle->buffer);
handle->node += handle->offset;
}
handle->loads++;
return 0;
}
int
rtems_rfs_inode_unload (rtems_rfs_file_system* fs,
rtems_rfs_inode_handle* handle,
bool update_ctime)
{
int rc = 0;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_UNLOAD))
printf ("rtems-rfs: inode-unload: ino=%" PRIu32 " loads=%i loaded=%s\n",
handle->ino, handle->loads,
rtems_rfs_inode_is_loaded (handle) ? "yes" : "no");
if (rtems_rfs_inode_is_loaded (handle))
{
if (handle->loads == 0)
return EIO;
handle->loads--;
if (handle->loads == 0)
{
/*
* If the buffer is dirty it will be release. Also set the ctime.
*/
if (rtems_rfs_buffer_dirty (&handle->buffer) && update_ctime)
rtems_rfs_inode_set_ctime (handle, time (NULL));
rc = rtems_rfs_buffer_handle_release (fs, &handle->buffer);
handle->node = NULL;
}
}
return rc;
}
int
rtems_rfs_inode_create (rtems_rfs_file_system* fs,
rtems_rfs_ino parent,
const char* name,
size_t length,
uint16_t mode,
uint16_t links,
uid_t uid,
gid_t gid,
rtems_rfs_ino* ino)
{
rtems_rfs_inode_handle parent_inode;
rtems_rfs_inode_handle inode;
int rc;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_CREATE))
{
const char* type = "unknown";
int c;
if (RTEMS_RFS_S_ISDIR (mode))
type = "dir";
else if (RTEMS_RFS_S_ISCHR (mode))
type = "char";
else if (RTEMS_RFS_S_ISBLK (mode))
type = "block";
else if (RTEMS_RFS_S_ISREG (mode))
type = "file";
else if (RTEMS_RFS_S_ISLNK (mode))
type = "link";
printf("rtems-rfs: inode-create: parent:%" PRIu32 " name:", parent);
for (c = 0; c < length; c++)
printf ("%c", name[c]);
printf (" type:%s mode:%04x (%03o)\n", type, mode, mode & ((1 << 10) - 1));
}
/*
* The file type is field within the mode. Check we have a sane mode set.
*/
switch (mode & RTEMS_RFS_S_IFMT)
{
case RTEMS_RFS_S_IFDIR:
case RTEMS_RFS_S_IFCHR:
case RTEMS_RFS_S_IFBLK:
case RTEMS_RFS_S_IFREG:
case RTEMS_RFS_S_IFLNK:
break;
default:
return EINVAL;
}
rc = rtems_rfs_inode_alloc (fs, parent, ino);
if (rc > 0)
return rc;
rc = rtems_rfs_inode_open (fs, *ino, &inode, true);
if (rc > 0)
{
rtems_rfs_inode_free (fs, *ino);
return rc;
}
rc = rtems_rfs_inode_initialise (&inode, links, mode, uid, gid);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &inode);
rtems_rfs_inode_free (fs, *ino);
return rc;
}
/*
* Only handle the specifics of a directory. Let caller handle the others.
*
* The inode delete will free the inode.
*/
if (RTEMS_RFS_S_ISDIR (mode))
{
rc = rtems_rfs_dir_add_entry (fs, &inode, ".", 1, *ino);
if (rc == 0)
rc = rtems_rfs_dir_add_entry (fs, &inode, "..", 2, parent);
if (rc > 0)
{
rtems_rfs_inode_delete (fs, &inode);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
}
rc = rtems_rfs_inode_open (fs, parent, &parent_inode, true);
if (rc > 0)
{
rtems_rfs_inode_delete (fs, &inode);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_dir_add_entry (fs, &parent_inode, name, length, *ino);
if (rc > 0)
{
rtems_rfs_inode_delete (fs, &inode);
rtems_rfs_inode_close (fs, &inode);
rtems_rfs_inode_close (fs, &parent_inode);
return rc;
}
/*
* If the node is a directory update the parent link count as the
* new directory has the '..' link that points to the parent.
*/
if (RTEMS_RFS_S_ISDIR (mode))
rtems_rfs_inode_set_links (&parent_inode,
rtems_rfs_inode_get_links (&parent_inode) + 1);
/*
* Update the parent's mtime.
*/
rc = rtems_rfs_inode_time_stamp_now (&parent_inode, true, true);
if (rc > 0)
{
rtems_rfs_inode_delete (fs, &inode);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_inode_close (fs, &parent_inode);
if (rc > 0)
{
rtems_rfs_inode_delete (fs, &inode);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_inode_close (fs, &inode);
if (rc > 0)
{
rtems_rfs_inode_free (fs, *ino);
return rc;
}
return 0;
}
int
rtems_rfs_inode_delete (rtems_rfs_file_system* fs,
rtems_rfs_inode_handle* handle)
{
int rc = 0;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_INODE_DELETE))
printf("rtems-rfs: inode-delete: ino:%" PRIu32 " loaded:%s\n",
rtems_rfs_inode_ino (handle),
rtems_rfs_inode_is_loaded (handle) ? "yes" : "no");
if (rtems_rfs_inode_is_loaded (handle))
{
rtems_rfs_block_map map;
/*
* Free the ino number.
*/
rc = rtems_rfs_inode_free (fs, handle->ino);
if (rc > 0)
return rc;
/*
* Free the blocks the inode may have attached.
*/
rc = rtems_rfs_block_map_open (fs, handle, &map);
if (rc == 0)
{
int rrc;
rrc = rtems_rfs_block_map_free_all (fs, &map);
rc = rtems_rfs_block_map_close (fs, &map);
if (rc > 0)
rrc = rc;
memset (handle->node, 0xff, RTEMS_RFS_INODE_SIZE);
rtems_rfs_buffer_mark_dirty (&handle->buffer);
/*
* Do the release here to avoid the ctime field being set on a
* close. Also if the loads is greater then one then other loads
* active. Forcing the loads count to 0.
*/
rc = rtems_rfs_buffer_handle_release (fs, &handle->buffer);
handle->loads = 0;
handle->node = NULL;
/*
* Return the first error and drop any that followed.
*/
if (rrc > 0)
rc = rrc;
}
}
return rc;
}
int
rtems_rfs_inode_initialise (rtems_rfs_inode_handle* handle,
uint16_t links,
uint16_t mode,
uid_t uid,
gid_t gid)
{
int b;
rtems_rfs_inode_set_links (handle, links);
rtems_rfs_inode_set_flags (handle, 0);
rtems_rfs_inode_set_mode (handle, mode);
rtems_rfs_inode_set_uid_gid (handle, uid, gid);
rtems_rfs_inode_set_block_offset (handle, 0);
rtems_rfs_inode_set_block_count (handle, 0);
for (b = 0; b < RTEMS_RFS_INODE_BLOCKS; b++)
rtems_rfs_inode_set_block (handle, b, 0);
rtems_rfs_inode_set_last_map_block (handle, 0);
rtems_rfs_inode_set_last_data_block (handle, 0);
return rtems_rfs_inode_time_stamp_now (handle, true, true);
}
int
rtems_rfs_inode_time_stamp_now (rtems_rfs_inode_handle* handle,
bool atime,
bool mtime)
{
time_t now;
if (!rtems_rfs_inode_is_loaded (handle))
return ENXIO;
now = time (NULL);
if (atime)
rtems_rfs_inode_set_atime (handle, now);
if (mtime)
rtems_rfs_inode_set_mtime (handle, now);
return 0;
}
rtems_rfs_pos
rtems_rfs_inode_get_size (rtems_rfs_file_system* fs,
rtems_rfs_inode_handle* handle)
{
rtems_rfs_block_size size;
size.count = rtems_rfs_inode_get_block_count (handle);
size.offset = rtems_rfs_inode_get_block_offset (handle);
return rtems_rfs_block_get_size (fs, &size);
}