summaryrefslogtreecommitdiffstats
path: root/cpukit/libcsupport/src/privateenv.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libcsupport/src/privateenv.c')
-rw-r--r--cpukit/libcsupport/src/privateenv.c287
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, &current_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(&current_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;
+ }
}