/*
* Evaluation IMFS Node Support Routines
*
* COPYRIGHT (c) 1989-1998.
* On-Line Applications Research Corporation (OAR).
* Copyright assigned to U.S. Government, 1994.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.OARcorp.com/rtems/license.html.
*
* $Id$
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include "imfs.h"
#include "libio_.h"
#define RTEMS_LIBIO_PERMS_RX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_READ)
#define RTEMS_LIBIO_PERMS_WX (RTEMS_LIBIO_PERMS_SEARCH | RTEMS_LIBIO_PERMS_WRITE)
#define MAXSYMLINK 5
int IMFS_Set_handlers(
rtems_filesystem_location_info_t *loc
)
{
IMFS_jnode_t *node = loc->node_access;
IMFS_fs_info_t *fs_info;
fs_info = loc->mt_entry->fs_info;
switch( node->type ) {
case IMFS_DIRECTORY:
loc->handlers = fs_info->directory_handlers;
break;
case IMFS_DEVICE:
loc->handlers = &IMFS_device_handlers;
break;
case IMFS_SYM_LINK:
case IMFS_HARD_LINK:
loc->handlers = &rtems_filesystem_null_handlers;
break;
case IMFS_MEMORY_FILE:
loc->handlers = fs_info->memfile_handlers;
break;
}
return 0;
}
/*
* IMFS_evaluate_permission
*
* The following routine evaluates that we have permission
* to do flags on the node.
*/
int IMFS_evaluate_permission(
rtems_filesystem_location_info_t *node,
int flags
)
{
uid_t st_uid;
gid_t st_gid;
IMFS_jnode_t *jnode;
int flags_to_test;
if ( !rtems_libio_is_valid_perms( flags ) ) {
assert( 0 );
set_errno_and_return_minus_one( EIO );
}
jnode = node->node_access;
#if defined(RTEMS_POSIX_API)
st_uid = geteuid();
st_gid = getegid();
#else
st_uid = jnode->st_uid;
st_gid = jnode->st_gid;
#endif
/*
* Check if I am owner or a group member or someone else.
*/
flags_to_test = flags;
if ( st_uid == jnode->st_uid )
flags_to_test <<= 6;
else if ( st_gid == jnode->st_gid )
flags_to_test <<= 3;
else
/* must be other - do nothing */;
/*
* If all of the flags are set we have permission
* to do this.
*/
if ( ( flags_to_test & jnode->st_mode) == flags_to_test )
return 1;
return 0;
}
/*
* IMFS_evaluate_hard_link
*
* The following routine evaluates a hardlink to the actual node.
*/
int IMFS_evaluate_hard_link(
rtems_filesystem_location_info_t *node, /* IN/OUT */
int flags /* IN */
)
{
IMFS_jnode_t *jnode = node->node_access;
int result = 0;
/*
* Check for things that should never happen.
*/
if ( jnode->type != IMFS_HARD_LINK )
rtems_fatal_error_occurred (0xABCD0000);
/*
* Set the hard link value and the handlers.
*/
node->node_access = jnode->info.hard_link.link_node;
IMFS_Set_handlers( node );
/*
* Verify we have the correct permissions for this node.
*/
if ( !IMFS_evaluate_permission( node, flags ) )
set_errno_and_return_minus_one( EACCES );
return result;
}
/*
* IMFS_evaluate_sym_link
*
* The following routine evaluates a symbolic link to the actual node.
*/
int IMFS_evaluate_sym_link(
rtems_filesystem_location_info_t *node, /* IN/OUT */
int flags /* IN */
)
{
IMFS_jnode_t *jnode = node->node_access;
int result = 0;
int i;
/*
* Check for things that should never happen.
*/
if ( jnode->type != IMFS_SYM_LINK )
rtems_fatal_error_occurred (0xABCD0000);
if ( !jnode->Parent )
rtems_fatal_error_occurred( 0xBAD00000 );
/*
* Move the node_access to either the symbolic links parent or
* root depending on the symbolic links path.
*/
node->node_access = jnode->Parent;
rtems_filesystem_get_sym_start_loc(
jnode->info.sym_link.name,
&i,
node
);
/*
* Use eval path to evaluate the path of the symbolic link.
*/
result = IMFS_eval_path(
&jnode->info.sym_link.name[i],
flags,
node
);
IMFS_Set_handlers( node );
/*
* Verify we have the correct permissions for this node.
*/
if ( !IMFS_evaluate_permission( node, flags ) )
set_errno_and_return_minus_one( EACCES );
return result;
}
/*
* IMFS_evaluate_link
*
* The following routine returns the real node pointed to by a link.
*/
int IMFS_evaluate_link(
rtems_filesystem_location_info_t *node, /* IN/OUT */
int flags /* IN */
)
{
IMFS_jnode_t *jnode;
int result = 0;
do {
jnode = node->node_access;
/*
* Increment and check the link counter.
*/
rtems_filesystem_link_counts ++;
if ( rtems_filesystem_link_counts > MAXSYMLINK ) {
rtems_filesystem_link_counts = 0;
set_errno_and_return_minus_one( ELOOP );
}
/*
* Follow the Link node.
*/
if ( jnode->type == IMFS_HARD_LINK )
result = IMFS_evaluate_hard_link( node, flags );
else if (jnode->type == IMFS_SYM_LINK )
result = IMFS_evaluate_sym_link( node, flags );
} while ( ( result == 0 ) && ( ( jnode->type == IMFS_SYM_LINK ) ||
( jnode->type == IMFS_HARD_LINK ) ) );
/*
* Clear link counter.
*/
rtems_filesystem_link_counts = 0;
return result;
}
/*
* IMFS_evaluate_for_make
*
* The following routine evaluate path for a new node to be created.
* pathloc is returned with a pointer to the parent of the new node.
* name is returned with a pointer to the first character in the
* new node name. The parent node is verified to be a directory.
*/
int IMFS_evaluate_for_make(
const char *path, /* IN */
rtems_filesystem_location_info_t *pathloc, /* IN/OUT */
const char **name /* OUT */
)
{
int i = 0;
int len;
IMFS_token_types type;
char token[ IMFS_NAME_MAX + 1 ];
rtems_filesystem_location_info_t newloc;
IMFS_jnode_t *node;
int done = 0;
int result;
/*
* This was filled in by the caller and is valid in the
* mount table.
*/
node = pathloc->node_access;
/*
* Evaluate all tokens until we are done or an error occurs.
*/
while( !done ) {
type = IMFS_get_token( &path[i], token, &len );
i += len;
if ( !pathloc->node_access )
set_errno_and_return_minus_one( ENOENT );
/*
* I cannot move out of this directory without execute permission.
*/
if ( type != IMFS_NO_MORE_PATH )
if ( node->type == IMFS_DIRECTORY )
if ( !IMFS_evaluate_permission( pathloc, RTEMS_LIBIO_PERMS_SEARCH ) )
set_errno_and_return_minus_one( EACCES );
node = pathloc->node_access;
switch( type ) {
case IMFS_UP_DIR:
/*
* Am I at the root of this mounted filesystem?
*/
if (pathloc->node_access == pathloc->mt_entry->mt_fs_root.node_access){
/*
* Am I at the root of all filesystems?
*/
if ( pathloc->node_access == rtems_filesystem_root.node_access ) {
break;
} else {
newloc = pathloc->mt_entry->mt_point_node;
*pathloc = newloc;
return (*pathloc->ops->evalformake)( &path[i-len], pathloc, name );
}
} else {
if ( !node->Parent )
set_errno_and_return_minus_one( ENOENT );
node = node->Parent;
}
pathloc->node_access = node;
break;
case IMFS_NAME:
if ( node->type == IMFS_HARD_LINK ) {
result = IMFS_evaluate_link( pathloc, 0 );
if ( result == -1 )
return -1;
} else if ( node->type == IMFS_SYM_LINK ) {
result = IMFS_evaluate_link( pathloc, 0 );
if ( result == -1 )
return -1;
}
node = pathloc->node_access;
if ( !node )
set_errno_and_return_minus_one( ENOTDIR );
/*
* Only a directory can be decended into.
*/
if ( node->type != IMFS_DIRECTORY )
set_errno_and_return_minus_one( ENOTDIR );
/*
* If we are at a node that is a mount point. Set loc to the
* new fs root node and let them finish evaluating the path.
*/
if ( node->info.directory.mt_fs != NULL ) {
newloc = node->info.directory.mt_fs->mt_fs_root;
*pathloc = newloc;
return (*pathloc->ops->evalformake)( &path[i-len], pathloc, name );
}
/*
* Otherwise find the token name in the present location.
*/
node = IMFS_find_match_in_dir( node, token );
/*
* If there is no node we have found the name of the node we
* wish to create.
*/
if ( ! node )
done = TRUE;
else
pathloc->node_access = node;
break;
case IMFS_NO_MORE_PATH:
set_errno_and_return_minus_one( EEXIST );
break;
case IMFS_INVALID_TOKEN:
set_errno_and_return_minus_one( ENAMETOOLONG );
break;
case IMFS_CURRENT_DIR:
break;
}
}
*name = &path[ i - len ];
/*
* We have evaluated the path as far as we can.
* Verify there is not any invalid stuff at the end of the name.
*/
for( ; path[i] != '\0'; i++) {
if ( !IMFS_is_separator( path[ i ] ) )
set_errno_and_return_minus_one( ENOENT );
}
/*
* Verify we can execute and write to this directory.
*/
result = IMFS_Set_handlers( pathloc );
/*
* The returned node must be a directory
*/
node = pathloc->node_access;
if ( node->type != IMFS_DIRECTORY )
set_errno_and_return_minus_one( ENOTDIR );
/*
* We must have Write and execute permission on the returned node.
*/
if ( !IMFS_evaluate_permission( pathloc, RTEMS_LIBIO_PERMS_WX ) )
set_errno_and_return_minus_one( EACCES );
return result;
}
/*
* IMFS_eval_path
*
* The following routine evaluate path for a node that wishes to be
* accessed with mode. pathloc is returned with a pointer to the
* node to be accessed.
*/
int IMFS_eval_path(
const char *pathname, /* IN */
int flags, /* IN */
rtems_filesystem_location_info_t *pathloc /* IN/OUT */
)
{
int i = 0;
int len;
IMFS_token_types type = IMFS_CURRENT_DIR;
char token[ IMFS_NAME_MAX + 1 ];
rtems_filesystem_location_info_t newloc;
IMFS_jnode_t *node;
int result;
if ( !rtems_libio_is_valid_perms( flags ) ) {
assert( 0 );
set_errno_and_return_minus_one( EIO );
}
/*
* This was filled in by the caller and is valid in the
* mount table.
*/
node = pathloc->node_access;
/*
* Evaluate all tokens until we are done or an error occurs.
*/
while( (type != IMFS_NO_MORE_PATH) && (type != IMFS_INVALID_TOKEN) ) {
type = IMFS_get_token( &pathname[i], token, &len );
i += len;
if ( !pathloc->node_access )
set_errno_and_return_minus_one( ENOENT );
/*
* I cannot move out of this directory without execute permission.
*/
if ( type != IMFS_NO_MORE_PATH )
if ( node->type == IMFS_DIRECTORY )
if ( !IMFS_evaluate_permission( pathloc, RTEMS_LIBIO_PERMS_SEARCH ) )
set_errno_and_return_minus_one( EACCES );
node = pathloc->node_access;
switch( type ) {
case IMFS_UP_DIR:
/*
* Am I at the root of this mounted filesystem?
*/
if (pathloc->node_access ==
pathloc->mt_entry->mt_fs_root.node_access) {
/*
* Am I at the root of all filesystems?
*/
if ( pathloc->node_access == rtems_filesystem_root.node_access ) {
break; /* Throw out the .. in this case */
} else {
newloc = pathloc->mt_entry->mt_point_node;
*pathloc = newloc;
return (*pathloc->ops->evalpath)(&(pathname[i-len]),flags,pathloc);
}
} else {
if ( !node->Parent )
set_errno_and_return_minus_one( ENOENT );
node = node->Parent;
pathloc->node_access = node;
}
pathloc->node_access = node;
break;
case IMFS_NAME:
/*
* If we are at a link follow it.
*/
if ( node->type == IMFS_HARD_LINK ) {
IMFS_evaluate_hard_link( pathloc, 0 );
node = pathloc->node_access;
if ( !node )
set_errno_and_return_minus_one( ENOTDIR );
} else if ( node->type == IMFS_SYM_LINK ) {
result = IMFS_evaluate_sym_link( pathloc, 0 );
node = pathloc->node_access;
if ( result == -1 )
return -1;
}
/*
* Only a directory can be decended into.
*/
if ( node->type != IMFS_DIRECTORY )
set_errno_and_return_minus_one( ENOTDIR );
/*
* If we are at a node that is a mount point. Set loc to the
* new fs root node and let them finish evaluating the path.
*/
if ( node->info.directory.mt_fs != NULL ) {
newloc = node->info.directory.mt_fs->mt_fs_root;
*pathloc = newloc;
return (*pathloc->ops->evalpath)( &pathname[i-len], flags, pathloc );
}
/*
* Otherwise find the token name in the present location.
*/
node = IMFS_find_match_in_dir( node, token );
if ( !node )
set_errno_and_return_minus_one( ENOENT );
/*
* Set the node access to the point we have found.
*/
pathloc->node_access = node;
break;
case IMFS_NO_MORE_PATH:
case IMFS_CURRENT_DIR:
break;
case IMFS_INVALID_TOKEN:
set_errno_and_return_minus_one( ENAMETOOLONG );
break;
}
}
/*
* Only return root node if this is the base file system.
*/
if ((pathloc->node_access == pathloc->mt_entry->mt_fs_root.node_access) &&
(pathloc->node_access != rtems_filesystem_root.node_access) ) {
newloc = pathloc->mt_entry->mt_point_node;
*pathloc = newloc;
}
result = IMFS_Set_handlers( pathloc );
/*
* Verify we have the correct permissions for this node.
*/
if ( !IMFS_evaluate_permission( pathloc, flags ) )
set_errno_and_return_minus_one( EACCES );
return result;
}