diff options
Diffstat (limited to 'cpukit/libfs/src/rfs/rtems-rfs-rtems.c')
-rw-r--r-- | cpukit/libfs/src/rfs/rtems-rfs-rtems.c | 1286 |
1 files changed, 1286 insertions, 0 deletions
diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c new file mode 100644 index 0000000000..bf688f0f67 --- /dev/null +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c @@ -0,0 +1,1286 @@ +/* + * 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 System Interface for RTEMS. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> +#include <stdlib.h> + +#if SIZEOF_MODE_T == 8 +#define PRIomode_t PRIo64 +#elif SIZEOF_MODE_T == 4 +#define PRIomode_t PRIo32 +#else +#error "unsupport size of mode_t" +#endif + +#include <rtems/rfs/rtems-rfs-file.h> +#include <rtems/rfs/rtems-rfs-dir.h> +#include <rtems/rfs/rtems-rfs-link.h> +#include "rtems-rfs-rtems.h" + +/** + * The libio permissions for read/execute. + */ +#define RTEMS_LIBIO_PERMS_RX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_READ) +/** + * The libio permissions for write/execute. + */ +#define RTEMS_LIBIO_PERMS_WX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_WRITE) + +/** + * Evaluate the path to a node that wishes to be accessed. The pathloc is + * returned with the ino to the node to be accessed. + * + * The routine starts from the root stripping away any leading path separators + * breaking the path up into the node names and checking an inode exists for + * that node name. Permissions are checked to insure access to the node is + * allowed. A path to a node must be accessable all the way even if the end + * result is directly accessable. As a user on Linux try "ls /root/../tmp" and + * you will see if fails. + * + * The whole process is complicated by crossmount paths where we head down into + * this file system only to return to the top and out to a another mounted file + * system. For example we are mounted on '/e' and the user enters "ls + * /e/a/b/../../dev". We need to head down then back up. + * + * @param path + * @param pathlen + * @param flags + * @param pathloc + */ +static int +rtems_rfs_rtems_eval_path (const char* path, + size_t pathlen, + int flags, + rtems_filesystem_location_info_t* pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_inode_handle inode; + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + uint32_t doff = 0; + const char* node; + size_t node_len; + int stripped; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf ("rtems-rfs-rtems: eval-path: in: path:%s pathlen:%zi ino:%" PRId32 "\n", + path, pathlen, ino); + + /* + * Eat any separators at the start of the path. + */ + stripped = rtems_filesystem_prefix_separators (path, pathlen); + path += stripped; + pathlen -= stripped; + + rtems_rfs_rtems_lock (fs); + + while (true) + { + /* + * Open and load the inode. + */ + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: opening inode", rc); + } + + /* + * Is this the end of the pathname we where given ? + */ + if ((*path == '\0') || (pathlen == 0)) + break; + + /* + * If a directory the execute bit must be set for us to enter. + */ + if (RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)) && + !rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_SEARCH)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: eval perms", EACCES); + } + + /* + * Extract the node name we will look for this time around. + */ + node = path; + node_len = 0; + while (!rtems_filesystem_is_separator (*path) && + (*path != '\0') && pathlen && + ((node_len + 1) < rtems_rfs_fs_max_name (fs))) + { + path++; + pathlen--; + node_len++; + } + + /* + * Eat any separators at start of the path. + */ + stripped = rtems_filesystem_prefix_separators (path, pathlen); + path += stripped; + pathlen -= stripped; + node_len += stripped; + + /* + * If the node is the current directory and there is more path to come move + * on it else we are at the inode we want. + */ + if (rtems_rfs_current_dir (node)) + { + if (*path) + { + rtems_rfs_inode_close (fs, &inode); + continue; + } + break; + } + + /* + * If the node is a parent we must move up one directory. If the location + * is on another file system we have a crossmount so we call that file + * system to handle the remainder of the path. + */ + if (rtems_rfs_parent_dir (node)) + { + /* + * If we are at root inode of the file system we have a crossmount path. + */ + if (ino == RTEMS_RFS_ROOT_INO) + { + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: crossmount: path:%s (%zd)\n", + path - node_len, pathlen + node_len); + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + *pathloc = pathloc->mt_entry->mt_point_node; + return (*pathloc->ops->evalpath_h)(path - node_len, pathlen + node_len, + flags, pathloc); + } + + /* + * We need to find the parent of this node. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, "..", 2, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: read parent inode", rc); + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: parent: ino:%" PRId32 "\n", ino); + } + else + { + /* + * Look up the node name in this directory. If found drop through, close + * the current inode and let the loop open the inode so the mode can be + * read and handlers set. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, + node, node_len - stripped, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return ((errno = rc) == 0) ? 0 : -1; + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: down: path:%s ino:%" PRId32 "\n", node, ino); + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_path: closing node", rc); + } + } + + rtems_rfs_rtems_set_pathloc_ino (pathloc, ino); + rtems_rfs_rtems_set_pathloc_doff (pathloc, doff); + + rc = rtems_rfs_rtems_set_handlers (pathloc, &inode) ? 0 : EIO; + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_PATH)) + printf("rtems-rfs-rtems: eval-path: ino:%" PRId32 "\n", ino); + + return rc; +} + +/** + * The following routine evaluates a path for a new node to be created. The + * pathloc is returned with a pointer to the parent of the new node. The name + * is returned with a pointer to the first character in the new node name. The + * parent node is verified to be a directory. + * + * @param path + * @param pathloc + * @param name + * @return int + */ +static int +rtems_rfs_rtems_eval_for_make (const char* path, + rtems_filesystem_location_info_t* pathloc, + const char** name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_inode_handle inode; + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_ino node_ino; + uint32_t doff = 0; + const char* node; + int node_len; + int stripped; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf ("rtems-rfs-rtems: eval-for-make: path:%s ino:%" PRId32 "\n", path, ino); + + *name = path + strlen (path); + + while (*name != path) + { + (*name)--; + if (rtems_filesystem_is_separator (**name)) + { + (*name)++; + break; + } + } + + /* + * Eat any separators at start of the path. + */ + stripped = rtems_filesystem_prefix_separators (path, strlen(path)); + path += stripped; + + rtems_rfs_rtems_lock (fs); + + while (true) + { + /* + * Open and load the inode. + */ + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: read ino", rc); + } + + /* + * If a directory the execute bit must be set for us to enter. + */ + if (RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode)) && + !rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_SEARCH)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: eval perms", EACCES); + } + + /* + * Is this the end of the pathname we where given ? + */ + if (path == *name) + break; + + /* + * Extract the node name we will look for this time around. + */ + node = path; + node_len = 0; + while (!rtems_filesystem_is_separator(*path) && + (*path != '\0') && + (node_len < (rtems_rfs_fs_max_name (fs) - 1))) + { + node_len++; + path++; + } + + /* + * Eat any separators at start of the new path. + */ + stripped = rtems_filesystem_prefix_separators (path, strlen (path)); + path += stripped; + node_len += stripped; + + /* + * If the node is the current directory and there is more path to come move + * on it else we are at the inode we want. + */ + if (rtems_rfs_current_dir (node)) + { + if (*path) + { + rtems_rfs_inode_close (fs, &inode); + continue; + } + break; + } + + /* + * If the node is a parent we must move up one directory. If the location + * is on another file system we have a crossmount so we call that file + * system to handle the remainder of the path. + */ + if (rtems_rfs_parent_dir (path)) + { + /* + * If we are at the root inode of the file system we have a crossmount + * path. + */ + if (ino == RTEMS_RFS_ROOT_INO) + { + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf("rtems-rfs-rtems: eval-for-make: crossmount: path:%s\n", + path - node_len); + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + *pathloc = pathloc->mt_entry->mt_point_node; + return (*pathloc->ops->evalformake_h)(path - node_len, pathloc, name); + } + + /* + * If not a directory give and up return. We cannot change dir from a + * regular file or device node. + */ + if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode))) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: not dir", ENOTSUP); + } + + /* + * We need to find the parent of this node. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, "..", 2, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: read parent inode", rc); + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf ("rtems-rfs-rtems: eval-for-make: parent: ino:%" PRId32 "\n", ino); + } + else + { + /* + * Read the inode so we know it exists and what type it is. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, + node, node_len - stripped, &ino, &doff); + if (rc > 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: reading inode", rc); + } + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf("rtems-rfs-rtems: eval-for-make: down: path:%s ino:%" PRId32 "\n", + node, ino); + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: closing node", rc); + } + } + + if (!RTEMS_RFS_S_ISDIR (rtems_rfs_inode_get_mode (&inode))) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: not dir", ENOTDIR); + } + + if (!rtems_rfs_rtems_eval_perms (&inode, RTEMS_LIBIO_PERMS_WX)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: cannot write", EACCES); + } + + /* + * Make sure the name does not already exists in the directory. + */ + rc = rtems_rfs_dir_lookup_ino (fs, &inode, *name, strlen (*name), + &node_ino, &doff); + if (rc == 0) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: found name", EEXIST); + } + + if (rc != ENOENT) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("eval_for_make: look up", rc); + } + + /* + * Set the parent ino in the path location. + */ + + rtems_rfs_rtems_set_pathloc_ino (pathloc, ino); + rtems_rfs_rtems_set_pathloc_doff (pathloc, doff); + + rc = rtems_rfs_rtems_set_handlers (pathloc, &inode) ? 0 : EIO; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_EVAL_FOR_MAKE)) + printf("rtems-rfs-rtems: eval-for-make: parent ino:%" PRId32 " name:%s\n", + ino, *name); + + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + + return rc; +} + +/** + * The following rouine creates a new link node under parent with the name + * given in name. The link node is set to point to the node at to_loc. + * + * @param to_loc + * @param parent_loc + * @param name + * @return int + */ +static int +rtems_rfs_rtems_link (rtems_filesystem_location_info_t* to_loc, + rtems_filesystem_location_info_t* parent_loc, + const char* name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (to_loc); + rtems_rfs_ino target = rtems_rfs_rtems_get_pathloc_ino (to_loc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc); + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_LINK)) + printf ("rtems-rfs-rtems: link: in: parent:%" PRId32 " target:%" PRId32 "\n", + parent, target); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_link (fs, name, strlen (name), parent, target, false); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("link: linking", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * Routine to remove a link node from the file system. + * + * @param parent_loc + * @param loc + * @return int + */ + +static int +rtems_rfs_rtems_unlink (rtems_filesystem_location_info_t* parent_loc, + rtems_filesystem_location_info_t* loc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (parent_loc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (loc); + uint32_t doff = rtems_rfs_rtems_get_pathloc_doff (loc); + int rc; + + rtems_rfs_rtems_lock (fs); + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_UNLINK)) + printf("rtems-rfs-rtems: unlink: parent:%" PRId32 " doff:%" PRIu32 " ino:%" PRId32 "\n", + parent, doff, ino); + + rc = rtems_rfs_unlink (fs, parent, ino, doff, rtems_rfs_unlink_dir_denied); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("unlink: unlink inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * The following verifies that and returns the type of node that the loc refers + * to. + * + * @param pathloc + * @return rtems_filesystem_node_types_t + */ + +static rtems_filesystem_node_types_t +rtems_rfs_rtems_node_type (rtems_filesystem_location_info_t* pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_filesystem_node_types_t type; + rtems_rfs_inode_handle inode; + uint16_t mode; + int rc; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("node_type: opening inode", rc); + } + + /* + * Do not return RTEMS_FILESYSTEM_HARD_LINK because this would result in an + * eval link which does not make sense in the case of the RFS file + * system. All directory entries are links to an inode. A link such as a HARD + * link is actually the normal path to a regular file, directory, device + * etc's inode. Links to inodes can be considered "the real" one, yet they + * are all links. + */ + mode = rtems_rfs_inode_get_mode (&inode); + if (RTEMS_RFS_S_ISDIR (mode)) + type = RTEMS_FILESYSTEM_DIRECTORY; + else if (RTEMS_RFS_S_ISLNK (mode)) + type = RTEMS_FILESYSTEM_SYM_LINK; + else if (RTEMS_RFS_S_ISBLK (mode) || RTEMS_RFS_S_ISCHR (mode)) + type = RTEMS_FILESYSTEM_DEVICE; + else + type = RTEMS_FILESYSTEM_MEMORY_FILE; + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("node_type: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return type; +} + +/** + * This routine is the implementation of the chown() system call for the + * RFS. + * + * @param pathloc + * @param owner + * @param group + * return int + */ + +static int +rtems_rfs_rtems_chown (rtems_filesystem_location_info_t *pathloc, + uid_t owner, + gid_t group) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; +#if defined (RTEMS_POSIX_API) + uid_t uid; +#endif + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_CHOWN)) + printf ("rtems-rfs-rtems: chown: in: ino:%" PRId32 " uid:%d gid:%d\n", + ino, owner, group); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("chown: opening inode", rc); + } + + /* + * Verify I am the owner of the node or the super user. + */ + +#if defined (RTEMS_POSIX_API) + uid = geteuid(); + + if ((uid != rtems_rfs_inode_get_uid (&inode)) && (uid != 0)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("chown: not able", EPERM); + } +#endif + + rtems_rfs_inode_set_uid_gid (&inode, owner, group); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("chown: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * This routine is the implementation of the utime() system call for the + * RFS. + * + * @param pathloc + * @param atime + * @param mtime + * return int + */ + +static int +rtems_rfs_rtems_utime(rtems_filesystem_location_info_t* pathloc, + time_t atime, + time_t mtime) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; + int rc; + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("utime: read inode", rc); + } + + rtems_rfs_inode_set_atime (&inode, atime); + rtems_rfs_inode_set_mtime (&inode, mtime); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("utime: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * The following rouine creates a new symbolic link node under parent with the + * name given in name. The node is set to point to the node at to_loc. + * + * @param parent_loc + * @param link_name + * @param node_name + * return int + */ + +static int +rtems_rfs_rtems_symlink (rtems_filesystem_location_info_t* parent_loc, + const char* link_name, + const char* node_name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (parent_loc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_loc); + uid_t uid; + gid_t gid; + int rc; + +#if defined(RTEMS_POSIX_API) + uid = geteuid (); + gid = getegid (); +#else + uid = 0; + gid = 0; +#endif + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_symlink (fs, node_name, strlen (node_name), + link_name, strlen (link_name), + uid, gid, parent); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("symlink: linking", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * The following rouine puts the symblic links destination name into buf. + * + * @param loc + * @param buf + * @param bufsize + * @return int + */ + +static ssize_t +rtems_rfs_rtems_readlink (rtems_filesystem_location_info_t* pathloc, + char* buf, + size_t bufsize) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + size_t length; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_READLINK)) + printf ("rtems-rfs-rtems: readlink: in: ino:%" PRId32 "\n", ino); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_symlink_read (fs, ino, buf, bufsize, &length); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("readlink: reading link", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return (int) length; +} + +int +rtems_rfs_rtems_fchmod (rtems_filesystem_location_info_t* pathloc, + mode_t mode) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; + uint16_t imode; +#if defined (RTEMS_POSIX_API) + uid_t uid; +#endif + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_FCHMOD)) + printf ("rtems-rfs-rtems: fchmod: in: ino:%" PRId32 " mode:%06" PRIomode_t "\n", + ino, mode); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("fchmod: opening inode", rc); + } + + imode = rtems_rfs_inode_get_mode (&inode); + + /* + * Verify I am the owner of the node or the super user. + */ +#if defined (RTEMS_POSIX_API) + uid = geteuid(); + + if ((uid != rtems_rfs_inode_get_uid (&inode)) && (uid != 0)) + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("fchmod: checking uid", EPERM); + } +#endif + + imode &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); + imode |= mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); + + rtems_rfs_inode_set_mode (&inode, imode); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("fchmod: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +int +rtems_rfs_rtems_fstat (rtems_filesystem_location_info_t* pathloc, + struct stat* buf) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_inode_handle inode; + rtems_rfs_file_shared* shared; + uint16_t mode; + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_STAT)) + printf ("rtems-rfs-rtems: stat: in: ino:%" PRId32 "\n", ino); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("stat: opening inode", rc); + } + + mode = rtems_rfs_inode_get_mode (&inode); + + if (RTEMS_RFS_S_ISCHR (mode) || RTEMS_RFS_S_ISBLK (mode)) + { + buf->st_rdev = + rtems_filesystem_make_dev_t (rtems_rfs_inode_get_block (&inode, 0), + rtems_rfs_inode_get_block (&inode, 1)); + } + + buf->st_dev = rtems_rfs_fs_device (fs); + buf->st_ino = rtems_rfs_inode_ino (&inode); + buf->st_mode = rtems_rfs_rtems_mode (mode); + buf->st_nlink = rtems_rfs_inode_get_links (&inode); + buf->st_uid = rtems_rfs_inode_get_uid (&inode); + buf->st_gid = rtems_rfs_inode_get_gid (&inode); + + /* + * Need to check is the ino is an open file. If so we take the values from + * the open file rather than the inode. + */ + shared = rtems_rfs_file_get_shared (fs, rtems_rfs_inode_ino (&inode)); + + if (shared) + { + buf->st_atime = rtems_rfs_file_shared_get_atime (shared); + buf->st_mtime = rtems_rfs_file_shared_get_mtime (shared); + buf->st_ctime = rtems_rfs_file_shared_get_ctime (shared); + buf->st_blocks = rtems_rfs_file_shared_get_block_count (shared); + + if (S_ISLNK (buf->st_mode)) + buf->st_size = rtems_rfs_file_shared_get_block_offset (shared); + else + buf->st_size = rtems_rfs_file_shared_get_size (fs, shared); + } + else + { + buf->st_atime = rtems_rfs_inode_get_atime (&inode); + buf->st_mtime = rtems_rfs_inode_get_mtime (&inode); + buf->st_ctime = rtems_rfs_inode_get_ctime (&inode); + buf->st_blocks = rtems_rfs_inode_get_block_count (&inode); + + if (S_ISLNK (buf->st_mode)) + buf->st_size = rtems_rfs_inode_get_block_offset (&inode); + else + buf->st_size = rtems_rfs_inode_get_size (fs, &inode); + } + + buf->st_blksize = rtems_rfs_fs_block_size (fs); + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("stat: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * Routine to create a node in the RFS file system. + * + * @param name + * @param mode + * @param dev + * @param pathloc + * @return int + */ + +static int +rtems_rfs_rtems_mknod (const char *name, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (pathloc); + rtems_rfs_ino ino; + rtems_rfs_inode_handle inode; + uid_t uid; + gid_t gid; + int rc; + +#if defined(RTEMS_POSIX_API) + uid = geteuid (); + gid = getegid (); +#else + uid = 0; + gid = 0; +#endif + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_inode_create (fs, parent, name, strlen (name), + rtems_rfs_rtems_imode (mode), + 1, uid, gid, &ino); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: inode create", rc); + } + + rc = rtems_rfs_inode_open (fs, ino, &inode, true); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: inode open", rc); + } + + if (S_ISDIR(mode) || S_ISREG(mode)) + { + } + else if (S_ISCHR (mode) || S_ISBLK (mode)) + { + int major; + int minor; + rtems_filesystem_split_dev_t (dev, major, minor); + rtems_rfs_inode_set_block (&inode, 0, major); + rtems_rfs_inode_set_block (&inode, 1, minor); + } + else + { + rtems_rfs_inode_close (fs, &inode); + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: bad mode", EINVAL); + } + + rc = rtems_rfs_inode_close (fs, &inode); + if (rc > 0) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("mknod: closing inode", rc); + } + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * Routine to remove a node from the RFS file system. + * + * @param parent_pathloc + * @param pathloc + * @return int + */ +int +rtems_rfs_rtems_rmnod (rtems_filesystem_location_info_t* parent_pathloc, + rtems_filesystem_location_info_t* pathloc) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + rtems_rfs_ino parent = rtems_rfs_rtems_get_pathloc_ino (parent_pathloc); + rtems_rfs_ino ino = rtems_rfs_rtems_get_pathloc_ino (pathloc); + uint32_t doff = rtems_rfs_rtems_get_pathloc_doff (pathloc); + int rc; + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_RMNOD)) + printf ("rtems-rfs: rmnod: parent:%" PRId32 " doff:%" PRIu32 ", ino:%" PRId32 "\n", + parent, doff, ino); + + rtems_rfs_rtems_lock (fs); + + rc = rtems_rfs_unlink (fs, parent, ino, doff, rtems_rfs_unlink_dir_denied); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("rmnod: unlinking", rc); + } + + rtems_rfs_rtems_unlock (fs); + return 0; +} + +/** + * The following routine does a sync on an inode node. Currently it flushes + * everything related to this device. + * + * @param iop + * @return int + */ +int +rtems_rfs_rtems_fdatasync (rtems_libio_t* iop) +{ + int rc; + + rc = rtems_rfs_buffer_sync (rtems_rfs_rtems_pathloc_dev (&iop->pathinfo)); + if (rc) + return rtems_rfs_rtems_error ("fdatasync: sync", rc); + + return 0; +} + +/** + * Rename the node. + * + * @param old_parent_loc The old name's parent location. + * @param old_loc The old name's location. + * @param new_parent_loc The new name's parent location. + * @param new_name The new name. + * @return int + */ +static int +rtems_rfs_rtems_rename(rtems_filesystem_location_info_t* old_parent_loc, + rtems_filesystem_location_info_t* old_loc, + rtems_filesystem_location_info_t* new_parent_loc, + const char* new_name) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (old_loc); + rtems_rfs_ino old_parent; + rtems_rfs_ino new_parent; + rtems_rfs_ino ino; + uint32_t doff; + int rc; + + old_parent = rtems_rfs_rtems_get_pathloc_ino (old_parent_loc); + new_parent = rtems_rfs_rtems_get_pathloc_ino (new_parent_loc); + + ino = rtems_rfs_rtems_get_pathloc_ino (old_loc); + doff = rtems_rfs_rtems_get_pathloc_doff (old_loc); + + if (rtems_rfs_rtems_trace (RTEMS_RFS_RTEMS_DEBUG_RENAME)) + printf ("rtems-rfs: rename: ino:%" PRId32 " doff:%" PRIu32 ", new parent:%" PRId32 " new name:%s\n", + ino, doff, new_parent, new_name); + + rtems_rfs_rtems_lock (fs); + + /* + * Link to the inode before unlinking so the inode is not erased when + * unlinked. + */ + rc = rtems_rfs_link (fs, new_name, strlen (new_name), new_parent, ino, true); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("rename: linking", rc); + } + + /* + * Unlink all inodes even directories with the dir option as false because a + * directory may not be empty. + */ + rc = rtems_rfs_unlink (fs, old_parent, ino, doff, + rtems_rfs_unlink_dir_allowed); + if (rc) + { + rtems_rfs_rtems_unlock (fs); + return rtems_rfs_rtems_error ("rename: unlinking", rc); + } + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * Return the file system stat data. + * + * @param pathloc + * @param sb + * @return int + */ +static int +rtems_rfs_rtems_statvfs (rtems_filesystem_location_info_t* pathloc, + struct statvfs* sb) +{ + rtems_rfs_file_system* fs = rtems_rfs_rtems_pathloc_dev (pathloc); + size_t blocks; + size_t inodes; + + rtems_rfs_group_usage (fs, &blocks, &inodes); + + sb->f_bsize = rtems_rfs_fs_block_size (fs); + sb->f_frsize = rtems_rfs_fs_media_block_size (fs); + sb->f_blocks = rtems_rfs_fs_media_blocks (fs); + sb->f_bfree = rtems_rfs_fs_blocks (fs) - blocks; + sb->f_bavail = sb->f_bfree; + sb->f_files = rtems_rfs_fs_inodes (fs); + sb->f_ffree = rtems_rfs_fs_inodes (fs) - inodes; + sb->f_favail = sb->f_ffree; + sb->f_fsid = RTEMS_RFS_SB_MAGIC; + sb->f_flag = rtems_rfs_fs_flags (fs); + sb->f_namemax = rtems_rfs_fs_max_name (fs); + + return 0; +} + +/** + * Handler table for RFS link nodes + */ +const rtems_filesystem_file_handlers_r rtems_rfs_rtems_link_handlers = +{ + .open_h = rtems_filesystem_default_open, + .close_h = rtems_filesystem_default_close, + .read_h = rtems_filesystem_default_read, + .write_h = rtems_filesystem_default_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_rfs_rtems_fstat, + .fchmod_h = rtems_filesystem_default_fchmod, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_filesystem_default_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_rfs_rtems_rmnod +}; + +/** + * Forward decl for the ops table. + */ + +int rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data); +int rtems_rfs_rtems_shutdown (rtems_filesystem_mount_table_entry_t *mt_entry); + +/** + * RFS file system operations table. + */ +const rtems_filesystem_operations_table rtems_rfs_ops = +{ + .evalpath_h = rtems_rfs_rtems_eval_path, + .evalformake_h = rtems_rfs_rtems_eval_for_make, + .link_h = rtems_rfs_rtems_link, + .unlink_h = rtems_rfs_rtems_unlink, + .node_type_h = rtems_rfs_rtems_node_type, + .mknod_h = rtems_rfs_rtems_mknod, + .chown_h = rtems_rfs_rtems_chown, + .freenod_h = rtems_filesystem_default_freenode, + .mount_h = rtems_filesystem_default_mount, + .fsmount_me_h = rtems_rfs_rtems_initialise, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_rfs_rtems_shutdown, + .utime_h = rtems_rfs_rtems_utime, + .eval_link_h = rtems_filesystem_default_evaluate_link, /* never called cause we lie in the node type */ + .symlink_h = rtems_rfs_rtems_symlink, + .readlink_h = rtems_rfs_rtems_readlink, + .rename_h = rtems_rfs_rtems_rename, + .statvfs_h = rtems_rfs_rtems_statvfs +}; + +/** + * Open the file system. + */ + +int +rtems_rfs_rtems_initialise (rtems_filesystem_mount_table_entry_t* mt_entry, + const void* data) +{ + rtems_rfs_rtems_private* rtems; + rtems_rfs_file_system* fs; + int rc; + + rtems = malloc (sizeof (rtems_rfs_rtems_private)); + if (!rtems) + return rtems_rfs_rtems_error ("initialise: local data", ENOMEM); + + memset (rtems, 0, sizeof (rtems_rfs_rtems_private)); + + rc = rtems_rfs_mutex_create (&rtems->access); + if (rc > 0) + { + free (rtems); + return rtems_rfs_rtems_error ("initialise: cannot create mutex", rc); + } + + rc = rtems_rfs_mutex_lock (&rtems->access); + if (rc > 0) + { + rtems_rfs_mutex_destroy (&rtems->access); + free (rtems); + return rtems_rfs_rtems_error ("initialise: cannot lock access mutex", rc); + } + + rc = rtems_rfs_fs_open (mt_entry->dev, rtems, 0, &fs); + if (rc) + { + free (rtems); + return rtems_rfs_rtems_error ("initialise: open", rc); + } + + mt_entry->fs_info = fs; + + mt_entry->mt_fs_root.node_access = (void*) RTEMS_RFS_ROOT_INO; + mt_entry->mt_fs_root.handlers = &rtems_rfs_rtems_dir_handlers; + mt_entry->mt_fs_root.ops = &rtems_rfs_ops; + + rtems_rfs_rtems_unlock (fs); + + return 0; +} + +/** + * Shutdown the file system. + */ +int +rtems_rfs_rtems_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry) +{ + rtems_rfs_file_system* fs = mt_entry->fs_info; + rtems_rfs_rtems_private* rtems; + int rc; + + rtems = rtems_rfs_fs_user (fs); + + rc = rtems_rfs_fs_close(fs); + + rtems_rfs_mutex_destroy (&rtems->access); + free (rtems); + + return rtems_rfs_rtems_error ("shutdown: close", rc); +} |