forked from Imagelibrary/rtems
Perform a context-dependent deferred location release to avoid a deadlock on the file system instance locks, for example during a chdir(). Update #2936.
331 lines
7.7 KiB
C
331 lines
7.7 KiB
C
/**
|
|
* @file
|
|
*
|
|
* @brief RTEMS File Sysyem Path Eval Support
|
|
* @ingroup LibIOInternal
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2012 embedded brains GmbH. All rights reserved.
|
|
*
|
|
* embedded brains GmbH
|
|
* Obere Lagerstr. 30
|
|
* 82178 Puchheim
|
|
* Germany
|
|
* <rtems@embedded-brains.de>
|
|
*
|
|
* 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 <rtems/libio_.h>
|
|
|
|
#include <string.h>
|
|
|
|
static size_t get_parentpathlen(const char *path, size_t pathlen)
|
|
{
|
|
while (pathlen > 0) {
|
|
size_t i = pathlen - 1;
|
|
|
|
if (rtems_filesystem_is_delimiter(path [i])) {
|
|
return pathlen;
|
|
}
|
|
|
|
pathlen = i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void set_startloc(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
rtems_filesystem_global_location_t *const *global_root_ptr,
|
|
rtems_filesystem_global_location_t *const *global_current_ptr
|
|
)
|
|
{
|
|
if (ctx->pathlen > 0) {
|
|
char c = ctx->path [0];
|
|
|
|
ctx->rootloc = rtems_filesystem_global_location_obtain(global_root_ptr);
|
|
|
|
if (rtems_filesystem_is_delimiter(c)) {
|
|
++ctx->path;
|
|
--ctx->pathlen;
|
|
ctx->startloc = rtems_filesystem_global_location_obtain(
|
|
&ctx->rootloc
|
|
);
|
|
} else {
|
|
ctx->startloc = rtems_filesystem_global_location_obtain(
|
|
global_current_ptr
|
|
);
|
|
}
|
|
} else {
|
|
ctx->rootloc = rtems_filesystem_global_location_obtain_null();
|
|
ctx->startloc = rtems_filesystem_global_location_obtain_null();
|
|
errno = ENOENT;
|
|
}
|
|
}
|
|
|
|
static void check_access(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
int eval_flags
|
|
)
|
|
{
|
|
const rtems_filesystem_location_info_t *currentloc = &ctx->currentloc;
|
|
const rtems_filesystem_mount_table_entry_t *mt_entry = currentloc->mt_entry;
|
|
|
|
if ((eval_flags & RTEMS_FS_PERMS_WRITE) == 0 || mt_entry->writeable) {
|
|
struct stat st;
|
|
int rv;
|
|
|
|
st.st_mode = 0;
|
|
st.st_uid = 0;
|
|
st.st_gid = 0;
|
|
rv = (*currentloc->handlers->fstat_h)(currentloc, &st);
|
|
if (rv == 0) {
|
|
bool access_ok = rtems_filesystem_check_access(
|
|
eval_flags,
|
|
st.st_mode,
|
|
st.st_uid,
|
|
st.st_gid
|
|
);
|
|
|
|
if (!access_ok) {
|
|
rtems_filesystem_eval_path_error(ctx, EACCES);
|
|
}
|
|
} else {
|
|
rtems_filesystem_eval_path_error(ctx, 0);
|
|
}
|
|
} else {
|
|
rtems_filesystem_eval_path_error(ctx, EROFS);
|
|
}
|
|
}
|
|
|
|
void rtems_filesystem_eval_path_continue(
|
|
rtems_filesystem_eval_path_context_t *ctx
|
|
)
|
|
{
|
|
int eval_flags;
|
|
|
|
while (ctx->pathlen > 0) {
|
|
(*ctx->currentloc.mt_entry->ops->eval_path_h)(ctx);
|
|
}
|
|
|
|
eval_flags = rtems_filesystem_eval_path_get_flags(ctx);
|
|
if (rtems_filesystem_eval_path_has_token(ctx)) {
|
|
bool make = (eval_flags & RTEMS_FS_MAKE) != 0;
|
|
|
|
if (make) {
|
|
check_access(ctx, RTEMS_FS_PERMS_WRITE);
|
|
} else {
|
|
rtems_filesystem_eval_path_error(ctx, ENOENT);
|
|
}
|
|
} else {
|
|
bool exclusive = (eval_flags & RTEMS_FS_EXCLUSIVE) != 0;
|
|
|
|
if (!exclusive) {
|
|
check_access(ctx, ctx->flags);
|
|
} else {
|
|
rtems_filesystem_eval_path_error(ctx, EEXIST);
|
|
}
|
|
}
|
|
}
|
|
|
|
rtems_filesystem_location_info_t *
|
|
rtems_filesystem_eval_path_start_with_root_and_current(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
const char *path,
|
|
size_t pathlen,
|
|
int eval_flags,
|
|
rtems_filesystem_global_location_t *const *global_root_ptr,
|
|
rtems_filesystem_global_location_t *const *global_current_ptr
|
|
)
|
|
{
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
|
|
ctx->path = path;
|
|
ctx->pathlen = pathlen;
|
|
ctx->flags = eval_flags;
|
|
|
|
set_startloc(ctx, global_root_ptr, global_current_ptr);
|
|
|
|
rtems_filesystem_instance_lock(&ctx->startloc->location);
|
|
|
|
rtems_filesystem_location_clone(
|
|
&ctx->currentloc,
|
|
&ctx->startloc->location
|
|
);
|
|
|
|
rtems_filesystem_eval_path_continue(ctx);
|
|
|
|
return &ctx->currentloc;
|
|
}
|
|
|
|
rtems_filesystem_location_info_t *
|
|
rtems_filesystem_eval_path_start(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
const char *path,
|
|
int eval_flags
|
|
)
|
|
{
|
|
return rtems_filesystem_eval_path_start_with_root_and_current(
|
|
ctx,
|
|
path,
|
|
strlen(path),
|
|
eval_flags,
|
|
&rtems_filesystem_root,
|
|
&rtems_filesystem_current
|
|
);
|
|
}
|
|
|
|
rtems_filesystem_location_info_t *
|
|
rtems_filesystem_eval_path_start_with_parent(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
const char *path,
|
|
int eval_flags,
|
|
rtems_filesystem_location_info_t *parentloc,
|
|
int parent_eval_flags
|
|
)
|
|
{
|
|
size_t pathlen = strlen(path);
|
|
const char *parentpath = path;
|
|
size_t parentpathlen = get_parentpathlen(path, pathlen);
|
|
const char *name = NULL;
|
|
size_t namelen = 0;
|
|
const rtems_filesystem_location_info_t *currentloc = NULL;
|
|
|
|
if (pathlen > 0) {
|
|
if (parentpathlen == 0) {
|
|
parentpath = ".";
|
|
parentpathlen = 1;
|
|
name = path;
|
|
namelen = pathlen;
|
|
} else {
|
|
name = path + parentpathlen;
|
|
namelen = pathlen - parentpathlen;
|
|
}
|
|
}
|
|
|
|
currentloc = rtems_filesystem_eval_path_start_with_root_and_current(
|
|
ctx,
|
|
parentpath,
|
|
parentpathlen,
|
|
parent_eval_flags,
|
|
&rtems_filesystem_root,
|
|
&rtems_filesystem_current
|
|
);
|
|
|
|
rtems_filesystem_location_clone(parentloc, currentloc);
|
|
|
|
ctx->path = name;
|
|
ctx->pathlen = namelen;
|
|
ctx->flags = eval_flags;
|
|
|
|
rtems_filesystem_eval_path_continue(ctx);
|
|
|
|
return &ctx->currentloc;
|
|
}
|
|
|
|
void rtems_filesystem_eval_path_recursive(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
const char *path,
|
|
size_t pathlen
|
|
)
|
|
{
|
|
if (pathlen > 0) {
|
|
if (ctx->recursionlevel < RTEMS_FILESYSTEM_SYMLOOP_MAX) {
|
|
const char *saved_path = ctx->path;
|
|
size_t saved_pathlen = ctx->pathlen;
|
|
|
|
if (rtems_filesystem_is_delimiter(path [0])) {
|
|
rtems_filesystem_eval_path_restart(ctx, &ctx->rootloc);
|
|
}
|
|
|
|
ctx->path = path;
|
|
ctx->pathlen = pathlen;
|
|
|
|
++ctx->recursionlevel;
|
|
while (ctx->pathlen > 0) {
|
|
(*ctx->currentloc.mt_entry->ops->eval_path_h)(ctx);
|
|
}
|
|
--ctx->recursionlevel;
|
|
|
|
ctx->path = saved_path;
|
|
ctx->pathlen = saved_pathlen;
|
|
} else {
|
|
rtems_filesystem_eval_path_error(ctx, ELOOP);
|
|
}
|
|
} else {
|
|
rtems_filesystem_eval_path_error(ctx, ENOENT);
|
|
}
|
|
}
|
|
|
|
void rtems_filesystem_eval_path_error(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
int eno
|
|
)
|
|
{
|
|
ctx->path = NULL;
|
|
ctx->pathlen = 0;
|
|
ctx->token = NULL;
|
|
ctx->tokenlen = 0;
|
|
|
|
if (!rtems_filesystem_location_is_null(&ctx->currentloc)) {
|
|
if (eno != 0) {
|
|
errno = eno;
|
|
}
|
|
|
|
rtems_filesystem_location_detach(&ctx->currentloc);
|
|
}
|
|
}
|
|
|
|
static void free_location(rtems_filesystem_location_info_t *loc)
|
|
{
|
|
rtems_filesystem_mt_entry_declare_lock_context(lock_context);
|
|
|
|
(*loc->mt_entry->ops->freenod_h)(loc);
|
|
|
|
rtems_filesystem_mt_entry_lock(lock_context);
|
|
rtems_chain_extract_unprotected(&loc->mt_entry_node);
|
|
rtems_filesystem_mt_entry_unlock(lock_context);
|
|
}
|
|
|
|
void rtems_filesystem_eval_path_cleanup(
|
|
rtems_filesystem_eval_path_context_t *ctx
|
|
)
|
|
{
|
|
free_location(&ctx->currentloc);
|
|
rtems_filesystem_instance_unlock(&ctx->startloc->location);
|
|
rtems_filesystem_global_location_release(ctx->startloc, false);
|
|
rtems_filesystem_global_location_release(ctx->rootloc, false);
|
|
}
|
|
|
|
void rtems_filesystem_eval_path_cleanup_with_parent(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
rtems_filesystem_location_info_t *parentloc
|
|
)
|
|
{
|
|
free_location(parentloc);
|
|
rtems_filesystem_eval_path_cleanup(ctx);
|
|
}
|
|
|
|
void rtems_filesystem_eval_path_restart(
|
|
rtems_filesystem_eval_path_context_t *ctx,
|
|
rtems_filesystem_global_location_t **newstartloc_ptr
|
|
)
|
|
{
|
|
free_location(&ctx->currentloc);
|
|
rtems_filesystem_instance_unlock(&ctx->startloc->location);
|
|
rtems_filesystem_global_location_assign(
|
|
&ctx->startloc,
|
|
rtems_filesystem_global_location_obtain(newstartloc_ptr)
|
|
);
|
|
rtems_filesystem_instance_lock(&ctx->startloc->location);
|
|
rtems_filesystem_location_clone(&ctx->currentloc, &ctx->startloc->location);
|
|
}
|