diff options
Diffstat (limited to 'cpukit/libcsupport/src/privateenv.c')
-rw-r--r-- | cpukit/libcsupport/src/privateenv.c | 287 |
1 files changed, 125 insertions, 162 deletions
diff --git a/cpukit/libcsupport/src/privateenv.c b/cpukit/libcsupport/src/privateenv.c index 89137200b7..28f866272b 100644 --- a/cpukit/libcsupport/src/privateenv.c +++ b/cpukit/libcsupport/src/privateenv.c @@ -1,3 +1,9 @@ +/** + * @file + * + * @ingroup LibIOEnv + */ + /* * Instantiate a private user environment for the calling thread. * @@ -14,191 +20,148 @@ */ #if HAVE_CONFIG_H -#include "config.h" + #include "config.h" #endif -#include <stdlib.h> /* free */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ + +#include <stdlib.h> -#include <rtems.h> -#include <rtems/chain.h> #include <rtems/libio_.h> +#include <rtems/score/thread.h> -/* cleanup a user environment - * NOTE: this must be called with - * thread dispatching disabled! - */ -static void -free_user_env(void *venv) +static void free_user_env(void *arg) { - rtems_user_env_t *env = (rtems_user_env_t*) venv ; - - if (env != &rtems_global_user_env - #ifdef HAVE_USERENV_REFCNT - && --env->refcnt <= 0 - #endif - ) { - rtems_filesystem_freenode( &env->current_directory); - rtems_filesystem_freenode( &env->root_directory); - free(env); + rtems_user_env_t *env = arg; + bool uses_global_env = env == &rtems_global_user_env; + + if (!uses_global_env) { + if (env->reference_count == 1) { + rtems_filesystem_global_location_release(env->current_directory); + rtems_filesystem_global_location_release(env->root_directory); + free(env); + } else { + --env->reference_count; + } } } +static void free_user_env_protected(rtems_user_env_t *env) +{ + _Thread_Disable_dispatch(); + free_user_env(env); + _Thread_Enable_dispatch(); +} + rtems_status_code rtems_libio_set_private_env(void) { rtems_status_code sc = RTEMS_SUCCESSFUL; - rtems_id task_id = rtems_task_self(); - rtems_filesystem_location_info_t root_loc; - rtems_filesystem_location_info_t current_loc; - rtems_user_env_t *new_env = NULL; - int rv = 0; - - rv = rtems_filesystem_evaluate_path("/", 1, 0, &root_loc, 0); - if (rv != 0) - goto error_0; - - rv = rtems_filesystem_evaluate_path("/", 1, 0, ¤t_loc, 0); - if (rv != 0) - goto error_1; - - /* - * Malloc is necessary whenever the current task does not - * have its own environment in place. This could be: - * a) it never had one - * OR - * b) it shared another task's environment - */ - - /* - * Bharath: I'm not sure if the check can be reduced to - * if( rtems_current_user_env->task_id != task_id ) { - */ - - if ( - rtems_current_user_env == &rtems_global_user_env - || rtems_current_user_env->task_id != task_id - ) { - new_env = malloc(sizeof(rtems_user_env_t)); - if (new_env == NULL) - goto error_2; - - #ifdef HAVE_USERENV_REFCNT - new_env->refcnt = 1; - #endif - - sc = rtems_task_variable_add( - RTEMS_SELF, - (void*)&rtems_current_user_env, - (void(*)(void *))free_user_env - ); - if (sc != RTEMS_SUCCESSFUL) - goto error_3; - - rtems_current_user_env = new_env; + rtems_id self_task_id = rtems_task_self(); + rtems_user_env_t *old_env = rtems_current_user_env; + bool uses_global_env = old_env == &rtems_global_user_env; + bool uses_shared_env = old_env->task_id != self_task_id; + + if (uses_global_env || uses_shared_env) { + rtems_user_env_t *new_env = calloc(1, sizeof(*new_env)); + + if (new_env != NULL) { + *new_env = *old_env; + new_env->reference_count = 1; + new_env->task_id = self_task_id; + new_env->root_directory = + rtems_filesystem_global_location_obtain(&old_env->root_directory); + new_env->current_directory = + rtems_filesystem_global_location_obtain(&old_env->current_directory); + + if ( + !rtems_filesystem_global_location_is_null(new_env->root_directory) + && !rtems_filesystem_global_location_is_null(new_env->current_directory) + ) { + sc = rtems_task_variable_add( + RTEMS_SELF, + (void **) &rtems_current_user_env, + free_user_env + ); + if (sc == RTEMS_SUCCESSFUL) { + free_user_env_protected(old_env); + rtems_current_user_env = new_env; + } else { + sc = RTEMS_TOO_MANY; + } + } else { + sc = RTEMS_UNSATISFIED; + } + + if (sc != RTEMS_SUCCESSFUL) { + free_user_env(new_env); + } + } else { + sc = RTEMS_NO_MEMORY; + } } - /* Inherit the global values */ - *rtems_current_user_env = rtems_global_user_env; - - rtems_current_user_env->task_id = task_id; - - /* - * Clone the pathlocs. In contrast to most other code we must _not_ free the - * original locs because what we are trying to do here is forking off clones. - * The reason is a pathloc can be allocated by the file system and needs to - * be freed when deleting the environment. - */ - rtems_filesystem_root = root_loc; - rtems_filesystem_current = current_loc; - - return RTEMS_SUCCESSFUL; - -error_3: - free(new_env); - -error_2: - rtems_filesystem_freenode(¤t_loc); - -error_1: - rtems_filesystem_freenode(&root_loc); - -error_0: - return RTEMS_NO_MEMORY; + return sc; } -/* - * Share the same private environment between two tasks: - * Task_id (remote) and RTEMS_SELF(current). - */ - -/* NOTE: - * - * THIS CODE HAS NO PROTECTION IMPLEMENTED - * - * Tasks who wish to share their environments must - * - * a) assert that no participants are concurrently - * executing - * libio_share_private_env() and/or libio_set_private_env() - * - * b) mutex access to rtems_filesystem_current, rtems_filesytem_root - * while changing any of those (chdir(), chroot()). - */ - rtems_status_code rtems_libio_share_private_env(rtems_id task_id) { - rtems_status_code sc; - rtems_user_env_t * shared_user_env; - rtems_id current_task_id; - - /* - * get current task id - */ - current_task_id = rtems_task_self(); - - /* - * If this was an attempt to share the task with self, - * if somebody wanted to do it... Lets tell them, its shared - */ - - if( task_id == current_task_id ) - return RTEMS_SUCCESSFUL; - /* - * Try to get the requested user environment - */ - sc = rtems_task_variable_get( - task_id, - (void*)&rtems_current_user_env, - (void*)&shared_user_env ); - - /* - * If it was not successful, return the error code - */ - if (sc != RTEMS_SUCCESSFUL) - return sc; - - /* - * If we are here, we have the required environment to be - * shared with the current task - */ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_id self_task_id = rtems_task_self(); + + if (task_id != RTEMS_SELF && self_task_id != task_id) { + rtems_user_env_t *env; /* - * If we have a current environment in place, we need to - * free it, since we will be sharing the variable with the - * shared_user_env + * We have to disable the thread dispatching to prevent deletion of the + * environment in the meantime. */ - - if (rtems_current_user_env->task_id==current_task_id) { - rtems_user_env_t *tmp = rtems_current_user_env; - free_user_env( tmp ); + _Thread_Disable_dispatch(); + sc = rtems_task_variable_get( + task_id, + (void *) &rtems_current_user_env, + (void *) &env + ); + if (sc == RTEMS_SUCCESSFUL) { + ++env->reference_count; + } else { + sc = RTEMS_UNSATISFIED; + } + _Thread_Enable_dispatch(); + + if (sc == RTEMS_SUCCESSFUL) { + sc = rtems_task_variable_add( + RTEMS_SELF, + (void **) &rtems_current_user_env, + free_user_env + ); + if (sc == RTEMS_SUCCESSFUL) { + free_user_env_protected(rtems_current_user_env); + rtems_current_user_env = env; + } else { + free_user_env_protected(env); + sc = RTEMS_TOO_MANY; + } + } } - /* the current_user_env is the same pointer that remote env */ - rtems_current_user_env = shared_user_env; + return sc; +} - /* increase the reference count */ -#ifdef HAVE_USERENV_REFCNT - rtems_current_user_env->refcnt++; -#endif +void rtems_libio_use_global_env(void) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_user_env_t *env = rtems_current_user_env; + bool uses_private_env = env != &rtems_global_user_env; - return RTEMS_SUCCESSFUL; + if (uses_private_env) { + sc = rtems_task_variable_delete( + RTEMS_SELF, + (void **) &rtems_current_user_env + ); + if (sc != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(0xdeadbeef); + } + + rtems_current_user_env = &rtems_global_user_env; + } } |