/** * @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 * * 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 #include #include #include #include #include #include #include #include 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; }