forked from Imagelibrary/rtems
663 lines
15 KiB
C
663 lines
15 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/*
|
|
* Copyright (C) 2012, 2020 embedded brains GmbH & Co. KG
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include <rtems/imfs.h>
|
|
#include <rtems/malloc.h>
|
|
#include <rtems/libcsupport.h>
|
|
|
|
#include <tmacros.h>
|
|
|
|
const char rtems_test_name[] = "FSIMFSGENERIC 1";
|
|
|
|
typedef enum {
|
|
TEST_NEW,
|
|
TEST_INITIALIZED,
|
|
TEST_FSTAT_OPEN_0,
|
|
TEST_FSTAT_OPEN_1,
|
|
TEST_OPEN,
|
|
TEST_READ,
|
|
TEST_WRITE,
|
|
TEST_IOCTL,
|
|
TEST_LSEEK,
|
|
TEST_FTRUNCATE,
|
|
TEST_FSYNC,
|
|
TEST_FDATASYNC,
|
|
TEST_FCNTL,
|
|
TEST_READV,
|
|
TEST_WRITEV,
|
|
TEST_CLOSED,
|
|
TEST_FSTAT_UNLINK,
|
|
TEST_REMOVED,
|
|
TEST_DESTROYED
|
|
} test_state;
|
|
|
|
static int handler_open(
|
|
rtems_libio_t *iop,
|
|
const char *path,
|
|
int oflag,
|
|
mode_t mode
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_FSTAT_OPEN_1);
|
|
*state = TEST_OPEN;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_close(
|
|
rtems_libio_t *iop
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_WRITEV);
|
|
*state = TEST_CLOSED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_read(
|
|
rtems_libio_t *iop,
|
|
void *buffer,
|
|
size_t count
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_OPEN);
|
|
*state = TEST_READ;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_write(
|
|
rtems_libio_t *iop,
|
|
const void *buffer,
|
|
size_t count
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_READ);
|
|
*state = TEST_WRITE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_ioctl(
|
|
rtems_libio_t *iop,
|
|
ioctl_command_t request,
|
|
void *buffer
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_WRITE);
|
|
*state = TEST_IOCTL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static off_t handler_lseek(
|
|
rtems_libio_t *iop,
|
|
off_t length,
|
|
int whence
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_IOCTL);
|
|
*state = TEST_LSEEK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_fstat(
|
|
const rtems_filesystem_location_info_t *loc,
|
|
struct stat *buf
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_location(loc);
|
|
|
|
switch (*state) {
|
|
case TEST_INITIALIZED:
|
|
*state = TEST_FSTAT_OPEN_0;
|
|
break;
|
|
case TEST_FSTAT_OPEN_0:
|
|
*state = TEST_FSTAT_OPEN_1;
|
|
break;
|
|
case TEST_CLOSED:
|
|
*state = TEST_FSTAT_UNLINK;
|
|
break;
|
|
default:
|
|
rtems_test_assert(0);
|
|
break;
|
|
}
|
|
|
|
buf->st_mode = S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_ftruncate(
|
|
rtems_libio_t *iop,
|
|
off_t length
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_LSEEK);
|
|
*state = TEST_FTRUNCATE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_fsync(
|
|
rtems_libio_t *iop
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_FTRUNCATE);
|
|
*state = TEST_FSYNC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_fdatasync(
|
|
rtems_libio_t *iop
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_FSYNC);
|
|
*state = TEST_FDATASYNC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_fcntl(
|
|
rtems_libio_t *iop,
|
|
int cmd
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_FDATASYNC);
|
|
*state = TEST_FCNTL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_readv(
|
|
rtems_libio_t *iop,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
ssize_t total
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_FCNTL);
|
|
*state = TEST_READV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_writev(
|
|
rtems_libio_t *iop,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
ssize_t total
|
|
)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_iop(iop);
|
|
|
|
rtems_test_assert(*state == TEST_READV);
|
|
*state = TEST_WRITEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const rtems_filesystem_file_handlers_r node_handlers = {
|
|
.open_h = handler_open,
|
|
.close_h = handler_close,
|
|
.read_h = handler_read,
|
|
.write_h = handler_write,
|
|
.ioctl_h = handler_ioctl,
|
|
.lseek_h = handler_lseek,
|
|
.fstat_h = handler_fstat,
|
|
.ftruncate_h = handler_ftruncate,
|
|
.fsync_h = handler_fsync,
|
|
.fdatasync_h = handler_fdatasync,
|
|
.fcntl_h = handler_fcntl,
|
|
.readv_h = handler_readv,
|
|
.writev_h = handler_writev
|
|
};
|
|
|
|
static IMFS_jnode_t *node_initialize(
|
|
IMFS_jnode_t *node,
|
|
void *arg
|
|
)
|
|
{
|
|
test_state *state = NULL;
|
|
|
|
node = IMFS_node_initialize_generic(node, arg);
|
|
state = IMFS_generic_get_context_by_node(node);
|
|
|
|
rtems_test_assert(*state == TEST_NEW);
|
|
*state = TEST_INITIALIZED;
|
|
|
|
return node;
|
|
}
|
|
|
|
static IMFS_jnode_t *node_remove(IMFS_jnode_t *node)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_node(node);
|
|
|
|
rtems_test_assert(*state == TEST_FSTAT_UNLINK);
|
|
*state = TEST_REMOVED;
|
|
|
|
return node;
|
|
}
|
|
|
|
static void node_destroy(IMFS_jnode_t *node)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_node(node);
|
|
|
|
rtems_test_assert(*state == TEST_REMOVED);
|
|
*state = TEST_DESTROYED;
|
|
|
|
IMFS_node_destroy_default(node);
|
|
}
|
|
|
|
static const IMFS_node_control node_control = {
|
|
.handlers = &node_handlers,
|
|
.node_initialize = node_initialize,
|
|
.node_remove = node_remove,
|
|
.node_destroy = node_destroy
|
|
};
|
|
|
|
static void test_node_operations(const char *path)
|
|
{
|
|
int rv;
|
|
int fd;
|
|
char buf[1];
|
|
ssize_t n;
|
|
off_t off;
|
|
struct iovec iov = {
|
|
.iov_base = &buf[0],
|
|
.iov_len = (int) sizeof(buf)
|
|
};
|
|
|
|
fd = open(path, O_RDWR);
|
|
rtems_test_assert(fd >= 0);
|
|
|
|
n = read(fd, buf, sizeof(buf));
|
|
rtems_test_assert(n == 0);
|
|
|
|
n = write(fd, buf, sizeof(buf));
|
|
rtems_test_assert(n == 0);
|
|
|
|
rv = ioctl(fd, 0);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
off = lseek(fd, 0, SEEK_SET);
|
|
rtems_test_assert(off == 0);
|
|
|
|
rv = ftruncate(fd, 0);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = fsync(fd);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = fdatasync(fd);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = fcntl(fd, F_GETFD);
|
|
rtems_test_assert(rv >= 0);
|
|
|
|
rv = readv(fd, &iov, 1);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = writev(fd, &iov, 1);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = close(fd);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = unlink(path);
|
|
rtems_test_assert(rv == 0);
|
|
}
|
|
|
|
static void test_imfs_make_generic_node(void)
|
|
{
|
|
static const char path[] = "generic";
|
|
test_state state;
|
|
int rv;
|
|
|
|
state = TEST_NEW;
|
|
rv = IMFS_make_generic_node(
|
|
path,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&node_control,
|
|
&state
|
|
);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
test_node_operations(path);
|
|
rtems_test_assert(state == TEST_DESTROYED);
|
|
}
|
|
|
|
static IMFS_jnode_t *node_initialize_error(
|
|
IMFS_jnode_t *node,
|
|
void *arg
|
|
)
|
|
{
|
|
errno = EIO;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static IMFS_jnode_t *node_remove_inhibited(IMFS_jnode_t *node)
|
|
{
|
|
rtems_test_assert(false);
|
|
|
|
return node;
|
|
}
|
|
|
|
static void node_destroy_inhibited(IMFS_jnode_t *node)
|
|
{
|
|
rtems_test_assert(false);
|
|
}
|
|
|
|
static const IMFS_node_control node_initialization_error_control = {
|
|
.handlers = &node_handlers,
|
|
.node_initialize = node_initialize_error,
|
|
.node_remove = node_remove_inhibited,
|
|
.node_destroy = node_destroy_inhibited
|
|
};
|
|
|
|
static const rtems_filesystem_operations_table *imfs_ops;
|
|
|
|
static int other_clone(rtems_filesystem_location_info_t *loc)
|
|
{
|
|
return (*imfs_ops->clonenod_h)(loc);
|
|
}
|
|
|
|
static rtems_filesystem_mount_table_entry_t *get_imfs_mt_entry(void)
|
|
{
|
|
return (rtems_filesystem_mount_table_entry_t *)
|
|
rtems_chain_first(&rtems_filesystem_mount_table);
|
|
}
|
|
|
|
static void test_imfs_make_generic_node_errors(void)
|
|
{
|
|
static const char path[] = "generic";
|
|
rtems_filesystem_mount_table_entry_t *mt_entry;
|
|
rtems_filesystem_operations_table other_ops;
|
|
rtems_resource_snapshot before;
|
|
void *opaque;
|
|
int rv;
|
|
|
|
rtems_resource_snapshot_take(&before);
|
|
|
|
errno = 0;
|
|
rv = IMFS_make_generic_node(
|
|
path,
|
|
S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&node_control,
|
|
NULL
|
|
);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EINVAL);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
mt_entry = get_imfs_mt_entry();
|
|
imfs_ops = mt_entry->ops;
|
|
other_ops = *imfs_ops;
|
|
other_ops.clonenod_h = other_clone;
|
|
mt_entry->ops = &other_ops;
|
|
rv = IMFS_make_generic_node(
|
|
path,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&node_control,
|
|
NULL
|
|
);
|
|
mt_entry->ops = imfs_ops;
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == ENOTSUP);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
opaque = rtems_heap_greedy_allocate(NULL, 0);
|
|
rv = IMFS_make_generic_node(
|
|
path,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&node_control,
|
|
NULL
|
|
);
|
|
rtems_heap_greedy_free(opaque);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == ENOMEM);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
rv = IMFS_make_generic_node(
|
|
path,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&node_initialization_error_control,
|
|
NULL
|
|
);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EIO);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
rv = IMFS_make_generic_node(
|
|
"/nil/nada",
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&node_control,
|
|
NULL
|
|
);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == ENOENT);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
}
|
|
|
|
static void user_node_destroy(IMFS_jnode_t *node)
|
|
{
|
|
test_state *state = IMFS_generic_get_context_by_node(node);
|
|
|
|
rtems_test_assert(*state == TEST_REMOVED);
|
|
*state = TEST_DESTROYED;
|
|
}
|
|
|
|
static const IMFS_node_control user_node_control = {
|
|
.handlers = &node_handlers,
|
|
.node_initialize = node_initialize,
|
|
.node_remove = node_remove,
|
|
.node_destroy = user_node_destroy
|
|
};
|
|
|
|
static void test_imfs_add_node(void)
|
|
{
|
|
static const char path[] = "/";
|
|
static const char name[] = "node";
|
|
size_t namelen = sizeof(name) - 1;
|
|
void *opaque;
|
|
rtems_resource_snapshot before;
|
|
IMFS_generic_t node = IMFS_GENERIC_NODE_INITIALIZER(
|
|
&user_node_control,
|
|
name,
|
|
namelen,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO
|
|
);
|
|
test_state state;
|
|
int rv;
|
|
|
|
/* Ensure that sure no dynamic memory is used */
|
|
opaque = rtems_heap_greedy_allocate(NULL, 0);
|
|
|
|
rtems_resource_snapshot_take(&before);
|
|
|
|
state = TEST_NEW;
|
|
rv = IMFS_add_node(path, &node.Node, &state);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
test_node_operations(name);
|
|
rtems_test_assert(state == TEST_DESTROYED);
|
|
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
rtems_heap_greedy_free(opaque);
|
|
}
|
|
|
|
static void test_imfs_add_node_errors(void)
|
|
{
|
|
static const char path[] = "/";
|
|
static const char name[] = "node";
|
|
size_t namelen = sizeof(name) - 1;
|
|
const char invalid_name[] = "/node";
|
|
size_t invalid_namelen = sizeof(invalid_name) - 1;
|
|
IMFS_jnode_t node = IMFS_NODE_INITIALIZER(
|
|
&user_node_control,
|
|
name,
|
|
namelen,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO
|
|
);
|
|
IMFS_jnode_t invalid_mode_node = IMFS_NODE_INITIALIZER(
|
|
&user_node_control,
|
|
name,
|
|
namelen,
|
|
S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO
|
|
);
|
|
IMFS_jnode_t init_error_node = IMFS_NODE_INITIALIZER(
|
|
&node_initialization_error_control,
|
|
name,
|
|
namelen,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO
|
|
);
|
|
IMFS_jnode_t invalid_name_node = IMFS_NODE_INITIALIZER(
|
|
&user_node_control,
|
|
invalid_name,
|
|
invalid_namelen,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO
|
|
);
|
|
rtems_filesystem_mount_table_entry_t *mt_entry;
|
|
rtems_filesystem_operations_table other_ops;
|
|
void *opaque;
|
|
rtems_resource_snapshot before;
|
|
int rv;
|
|
|
|
/* Ensure that sure no dynamic memory is used */
|
|
opaque = rtems_heap_greedy_allocate(NULL, 0);
|
|
|
|
rtems_resource_snapshot_take(&before);
|
|
|
|
errno = 0;
|
|
rv = IMFS_add_node(path, &invalid_mode_node, NULL);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EINVAL);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
mt_entry = get_imfs_mt_entry();
|
|
imfs_ops = mt_entry->ops;
|
|
other_ops = *imfs_ops;
|
|
other_ops.clonenod_h = other_clone;
|
|
mt_entry->ops = &other_ops;
|
|
rv = IMFS_add_node(path, &node, NULL);
|
|
mt_entry->ops = imfs_ops;
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == ENOTSUP);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
rv = IMFS_add_node(path, &init_error_node, NULL);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EIO);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
rv = IMFS_add_node("/nil/nada", &node, NULL);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == ENOENT);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
errno = 0;
|
|
rv = IMFS_add_node(path, &invalid_name_node, NULL);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EINVAL);
|
|
rtems_test_assert(rtems_resource_snapshot_check(&before));
|
|
|
|
rtems_heap_greedy_free(opaque);
|
|
}
|
|
|
|
static void Init(rtems_task_argument arg)
|
|
{
|
|
TEST_BEGIN();
|
|
|
|
test_imfs_make_generic_node();
|
|
test_imfs_make_generic_node_errors();
|
|
test_imfs_add_node();
|
|
test_imfs_add_node_errors();
|
|
|
|
TEST_END();
|
|
rtems_test_exit(0);
|
|
}
|
|
|
|
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
|
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
|
|
|
|
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 4
|
|
|
|
#define CONFIGURE_MAXIMUM_TASKS 1
|
|
|
|
#define CONFIGURE_UNIFIED_WORK_AREAS
|
|
|
|
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
|
|
|
|
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
|
|
|
#define CONFIGURE_INIT
|
|
|
|
#include <rtems/confdefs.h>
|