/**
* @file
*
* @brief RTEMS File Systems Link Routines
* @ingroup rtems_rfs
*
* These functions manage links. A link is the addition of a directory entry
* in a parent directory and incrementing the links count in the inode.
*/
/*
* 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-buffer.h>
#include <rtems/rfs/rtems-rfs-file-system.h>
#include <rtems/rfs/rtems-rfs-trace.h>
#include <rtems/rfs/rtems-rfs-dir.h>
#include <rtems/rfs/rtems-rfs-dir-hash.h>
#include <rtems/rfs/rtems-rfs-link.h>
int
rtems_rfs_link (rtems_rfs_file_system* fs,
const char* name,
int length,
rtems_rfs_ino parent,
rtems_rfs_ino target,
bool link_dir)
{
rtems_rfs_inode_handle parent_inode;
rtems_rfs_inode_handle target_inode;
uint16_t links;
int rc;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_LINK))
{
int c;
printf ("rtems-rfs: link: parent(%" PRIu32 ") -> ", parent);
for (c = 0; c < length; c++)
printf ("%c", name[c]);
printf ("(%" PRIu32 ")\n", target);
}
rc = rtems_rfs_inode_open (fs, target, &target_inode, true);
if (rc)
return rc;
/*
* If the target inode is a directory and we cannot link directories
* return a not supported error code.
*/
if (!link_dir && S_ISDIR (rtems_rfs_inode_get_mode (&target_inode)))
{
rtems_rfs_inode_close (fs, &target_inode);
return ENOTSUP;
}
rc = rtems_rfs_inode_open (fs, parent, &parent_inode, true);
if (rc)
{
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
rc = rtems_rfs_dir_add_entry (fs, &parent_inode, name, length, target);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &parent_inode);
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
links = rtems_rfs_inode_get_links (&target_inode) + 1;
rtems_rfs_inode_set_links (&target_inode, links);
rc = rtems_rfs_inode_time_stamp_now (&parent_inode, true, true);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &parent_inode);
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
rc = rtems_rfs_inode_close (fs, &parent_inode);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
rc = rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
int
rtems_rfs_unlink (rtems_rfs_file_system* fs,
rtems_rfs_ino parent,
rtems_rfs_ino target,
uint32_t doff,
rtems_rfs_unlink_dir dir_mode)
{
rtems_rfs_inode_handle parent_inode;
rtems_rfs_inode_handle target_inode;
uint16_t links;
bool dir;
int rc;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: unlink: parent(%" PRIu32 ") -X-> (%" PRIu32 ")\n", parent, target);
rc = rtems_rfs_inode_open (fs, target, &target_inode, true);
if (rc)
return rc;
/*
* If a directory process the unlink mode.
*/
dir = RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&target_inode));
if (dir)
{
switch (dir_mode)
{
case rtems_rfs_unlink_dir_denied:
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: link is a directory\n");
rtems_rfs_inode_close (fs, &target_inode);
return EISDIR;
case rtems_rfs_unlink_dir_if_empty:
rc = rtems_rfs_dir_empty (fs, &target_inode);
if (rc > 0)
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: dir-empty: %d: %s\n", rc, strerror (rc));
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
break;
default:
break;
}
}
rc = rtems_rfs_inode_open (fs, parent, &parent_inode, true);
if (rc)
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: link: inode-open failed: %d: %s\n",
rc, strerror (rc));
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
rc = rtems_rfs_dir_del_entry (fs, &parent_inode, target, doff);
if (rc > 0)
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: unlink: dir-del failed: %d: %s\n",
rc, strerror (rc));
rtems_rfs_inode_close (fs, &parent_inode);
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
links = rtems_rfs_inode_get_links (&target_inode);
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: unlink: target:%" PRIu32 " links:%u\n", target, links);
if (links > 1)
{
links--;
rtems_rfs_inode_set_links (&target_inode, links);
}
else
{
/*
* Erasing the inode releases all blocks attached to it.
*/
rc = rtems_rfs_inode_delete (fs, &target_inode);
if (rc > 0)
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: unlink: inode-del failed: %d: %s\n",
rc, strerror (rc));
rtems_rfs_inode_close (fs, &parent_inode);
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
if (dir)
{
links = rtems_rfs_inode_get_links (&parent_inode);
if (links > 1)
links--;
rtems_rfs_inode_set_links (&parent_inode, links);
}
}
rc = rtems_rfs_inode_time_stamp_now (&parent_inode, true, true);
if (rc > 0)
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: link: inode-time-stamp failed: %d: %s\n",
rc, strerror (rc));
rtems_rfs_inode_close (fs, &parent_inode);
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
rc = rtems_rfs_inode_close (fs, &parent_inode);
if (rc > 0)
{
if (rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: link: parent inode-close failed: %d: %s\n",
rc, strerror (rc));
rtems_rfs_inode_close (fs, &target_inode);
return rc;
}
rc = rtems_rfs_inode_close (fs, &target_inode);
if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_UNLINK))
printf ("rtems-rfs: link: target inode-close failed: %d: %s\n",
rc, strerror (rc));
return rc;
}
int
rtems_rfs_symlink (rtems_rfs_file_system* fs,
const char* name,
int length,
const char* link,
int link_length,
uid_t uid,
gid_t gid,
rtems_rfs_ino parent)
{
rtems_rfs_inode_handle inode;
rtems_rfs_ino ino;
int rc;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_SYMLINK))
{
int c;
printf ("rtems-rfs: symlink: parent:%" PRIu32 " name:", parent);
for (c = 0; c < length; c++)
printf ("%c", name[c]);
printf (" link:");
for (c = 0; c < link_length; c++)
printf ("%c", link[c]);
}
if (link_length >= rtems_rfs_fs_block_size (fs))
return ENAMETOOLONG;
rc = rtems_rfs_inode_create (fs, parent, name, strlen (name),
RTEMS_RFS_S_SYMLINK,
1, uid, gid, &ino);
if (rc > 0)
return rc;
rc = rtems_rfs_inode_open (fs, ino, &inode, true);
if (rc > 0)
return rc;
/*
* If the link length is less than the length of data union in the inode
* place the link into the data area else allocate a block and write the link
* to that.
*/
if (link_length < RTEMS_RFS_INODE_DATA_NAME_SIZE)
{
memset (inode.node->data.name, 0, RTEMS_RFS_INODE_DATA_NAME_SIZE);
memcpy (inode.node->data.name, link, link_length);
rtems_rfs_inode_set_block_count (&inode, 0);
}
else
{
rtems_rfs_block_map map;
rtems_rfs_block_no block;
rtems_rfs_buffer_handle buffer;
uint8_t* data;
rc = rtems_rfs_block_map_open (fs, &inode, &map);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_block_map_grow (fs, &map, 1, &block);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_buffer_handle_open (fs, &buffer);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, false);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
data = rtems_rfs_buffer_data (&buffer);
memset (data, 0xff, rtems_rfs_fs_block_size (fs));
memcpy (data, link, link_length);
rc = rtems_rfs_buffer_handle_close (fs, &buffer);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_block_map_close (fs, &map);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &inode);
return rc;
}
}
rtems_rfs_inode_set_block_offset (&inode, link_length);
rc = rtems_rfs_inode_close (fs, &inode);
return rc;
}
int
rtems_rfs_symlink_read (rtems_rfs_file_system* fs,
rtems_rfs_ino link,
char* path,
size_t size,
size_t* length)
{
rtems_rfs_inode_handle inode;
int rc;
if (rtems_rfs_trace (RTEMS_RFS_TRACE_SYMLINK_READ))
printf ("rtems-rfs: symlink-read: link:%" PRIu32 "\n", link);
rc = rtems_rfs_inode_open (fs, link, &inode, true);
if (rc)
return rc;
if (!RTEMS_RFS_S_ISLNK (rtems_rfs_inode_get_mode (&inode)))
{
rtems_rfs_inode_close (fs, &inode);
return EINVAL;
}
*length = rtems_rfs_inode_get_block_offset (&inode);
if (size < *length)
{
*length = size;
}
if (rtems_rfs_inode_get_block_count (&inode) == 0)
{
memcpy (path, inode.node->data.name, *length);
}
else
{
rtems_rfs_block_map map;
rtems_rfs_block_no block;
rtems_rfs_buffer_handle buffer;
char* data;
rc = rtems_rfs_block_map_open (fs, &inode, &map);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_block_map_seek (fs, &map, 0, &block);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_buffer_handle_open (fs, &buffer);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_buffer_handle_request (fs, &buffer, block, false);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
data = rtems_rfs_buffer_data (&buffer);
memcpy (path, data, *length);
rc = rtems_rfs_buffer_handle_close (fs, &buffer);
if (rc > 0)
{
rtems_rfs_block_map_close (fs, &map);
rtems_rfs_inode_close (fs, &inode);
return rc;
}
rc = rtems_rfs_block_map_close (fs, &map);
if (rc > 0)
{
rtems_rfs_inode_close (fs, &inode);
return rc;
}
}
rc = rtems_rfs_inode_close (fs, &inode);
return rc;
}