From baef823cd550449bfbcc36625b9571389d8ad1af Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 13 Sep 2017 09:22:19 +0200 Subject: libio: Add hold/drop iop reference Check iop reference count in close() and return -1 with errno set to EBUSY in case the file descriptor is still in use. Update #3132. --- testsuites/fstests/Makefile.am | 1 + testsuites/fstests/configure.ac | 1 + testsuites/fstests/fsclose01/Makefile.am | 19 ++ testsuites/fstests/fsclose01/fsclose01.doc | 26 ++ testsuites/fstests/fsclose01/fsclose01.scn | 2 + testsuites/fstests/fsclose01/init.c | 511 +++++++++++++++++++++++++++++ 6 files changed, 560 insertions(+) create mode 100644 testsuites/fstests/fsclose01/Makefile.am create mode 100644 testsuites/fstests/fsclose01/fsclose01.doc create mode 100644 testsuites/fstests/fsclose01/fsclose01.scn create mode 100644 testsuites/fstests/fsclose01/init.c (limited to 'testsuites/fstests') diff --git a/testsuites/fstests/Makefile.am b/testsuites/fstests/Makefile.am index 1302fe009a..158a797a2c 100644 --- a/testsuites/fstests/Makefile.am +++ b/testsuites/fstests/Makefile.am @@ -2,6 +2,7 @@ ACLOCAL_AMFLAGS = -I ../aclocal _SUBDIRS = _SUBDIRS += fsbdpart01 +_SUBDIRS += fsclose01 _SUBDIRS += fsdosfsformat01 _SUBDIRS += fsdosfsname01 _SUBDIRS += fsdosfsname02 diff --git a/testsuites/fstests/configure.ac b/testsuites/fstests/configure.ac index e5dc840ba8..37a92bbca5 100644 --- a/testsuites/fstests/configure.ac +++ b/testsuites/fstests/configure.ac @@ -78,6 +78,7 @@ AC_CHECK_SIZEOF([blkcnt_t]) # Explicitly list all Makefiles here AC_CONFIG_FILES([Makefile fsbdpart01/Makefile +fsclose01/Makefile fsdosfsformat01/Makefile fsdosfsname01/Makefile fsdosfsname02/Makefile diff --git a/testsuites/fstests/fsclose01/Makefile.am b/testsuites/fstests/fsclose01/Makefile.am new file mode 100644 index 0000000000..dc2da84b82 --- /dev/null +++ b/testsuites/fstests/fsclose01/Makefile.am @@ -0,0 +1,19 @@ +rtems_tests_PROGRAMS = fsclose01 +fsclose01_SOURCES = init.c + +dist_rtems_tests_DATA = fsclose01.scn fsclose01.doc + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../automake/compile.am +include $(top_srcdir)/../automake/leaf.am + +AM_CPPFLAGS += -I$(top_srcdir)/../support/include + +LINK_OBJS = $(fsclose01_OBJECTS) +LINK_LIBS = $(fsclose01_LDLIBS) + +fsclose01$(EXEEXT): $(fsclose01_OBJECTS) $(fsclose01_DEPENDENCIES) + @rm -f fsclose01$(EXEEXT) + $(make-exe) + +include $(top_srcdir)/../automake/local.am diff --git a/testsuites/fstests/fsclose01/fsclose01.doc b/testsuites/fstests/fsclose01/fsclose01.doc new file mode 100644 index 0000000000..29277a4a91 --- /dev/null +++ b/testsuites/fstests/fsclose01/fsclose01.doc @@ -0,0 +1,26 @@ +This file describes the directives and concepts tested by this test set. + +test set name: fsclose01 + +directives: + + - close() + - fcntl() + - fdatasync() + - fchdir() + - fchmod() + - fchown() + - fstat() + - fsync() + - ftruncate() + - ioctl() + - lseek() + - read() + - readv() + - write() + - writev() + +concepts: + + - Ensure the close() return -1 with errno set to EBUSY in case the file + descriptor is still in use. diff --git a/testsuites/fstests/fsclose01/fsclose01.scn b/testsuites/fstests/fsclose01/fsclose01.scn new file mode 100644 index 0000000000..3da936932f --- /dev/null +++ b/testsuites/fstests/fsclose01/fsclose01.scn @@ -0,0 +1,2 @@ +*** BEGIN OF TEST FSCLOSE 1 *** +*** END OF TEST FSCLOSE 1 *** diff --git a/testsuites/fstests/fsclose01/init.c b/testsuites/fstests/fsclose01/init.c new file mode 100644 index 0000000000..a9de652b51 --- /dev/null +++ b/testsuites/fstests/fsclose01/init.c @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2012, 2017 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +const char rtems_test_name[] = "FSCLOSE 1"; + +typedef enum { + ACTION_ClOSE, + ACTION_FCNTL, + ACTION_FDATASYNC, + ACTION_FCHDIR, + ACTION_FCHMOD, + ACTION_FCHOWN, + /* ACTION_FPATHCONF, not easy to test */ + ACTION_FSTAT, + ACTION_FSYNC, + ACTION_FTRUNCATE, + ACTION_IOCTL, + ACTION_LSEEK, + ACTION_READ, + ACTION_READV, + ACTION_WRITE, + ACTION_WRITEV +} test_action; + +typedef struct { + rtems_id worker_id; + int fd; + test_action action; + bool wait_in_close; + bool wait_in_fstat; + int close_count; + int fcntl_count; + int fdatasync_count; + int fstat_count; + int fsync_count; + int ftruncate_count; + int ioctl_count; + int lseek_count; + int open_count; + int read_count; + int readv_count; + int write_count; + int writev_count; +} test_context; + +static test_context test_instance; + +static void wait(void) +{ + rtems_status_code sc; + + sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); +} + +static void wakeup_worker(const test_context *ctx) +{ + rtems_status_code sc; + + sc = rtems_event_transient_send(ctx->worker_id); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); +} + +static int handler_open( + rtems_libio_t *iop, + const char *path, + int oflag, + mode_t mode +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->open_count; + + return 0; +} + +static int handler_close( + rtems_libio_t *iop +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->close_count; + + if (ctx->wait_in_close) { + ctx->wait_in_close = false; + wait(); + } + + return 0; +} + +static ssize_t handler_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->read_count; + + wait(); + return 0; +} + +static ssize_t handler_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->write_count; + + wait(); + return 0; +} + +static int handler_ioctl( + rtems_libio_t *iop, + ioctl_command_t request, + void *buffer +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->ioctl_count; + + wait(); + return 0; +} + +static off_t handler_lseek( + rtems_libio_t *iop, + off_t length, + int whence +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->lseek_count; + + wait(); + return 0; +} + +static int handler_fstat( + const rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + test_context *ctx; + + buf->st_mode = S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO; + ctx = IMFS_generic_get_context_by_location(loc); + ++ctx->fstat_count; + + if (ctx->wait_in_fstat) { + ctx->wait_in_fstat = false; + wait(); + } + + return 0; +} + +static int handler_ftruncate( + rtems_libio_t *iop, + off_t length +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->ftruncate_count; + + wait(); + return 0; +} + +static int handler_fsync( + rtems_libio_t *iop +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->fsync_count; + + wait(); + return 0; +} + +static int handler_fdatasync( + rtems_libio_t *iop +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->fdatasync_count; + + wait(); + return 0; +} + +static int handler_fcntl( + rtems_libio_t *iop, + int cmd +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->fcntl_count; + + wait(); + return 0; +} + +static ssize_t handler_readv( + rtems_libio_t *iop, + const struct iovec *iov, + int iovcnt, + ssize_t total +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->readv_count; + + wait(); + return 0; +} + +static ssize_t handler_writev( + rtems_libio_t *iop, + const struct iovec *iov, + int iovcnt, + ssize_t total +) +{ + test_context *ctx; + + ctx = IMFS_generic_get_context_by_iop(iop); + ++ctx->writev_count; + + wait(); + 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 const IMFS_node_control node_control = { + .handlers = &node_handlers, + .node_initialize = IMFS_node_initialize_generic, + .node_remove = IMFS_node_remove_default, + .node_destroy = IMFS_node_destroy_default +}; + +static void worker_task(rtems_task_argument arg) +{ + test_context *ctx; + int rv; + char buf[1]; + ssize_t n; + off_t off; + struct iovec iov = { + .iov_base = &buf[0], + .iov_len = sizeof(buf) + }; + struct stat st; + + ctx = (test_context *) arg; + + while (true) { + wait(); + + switch (ctx->action) { + case ACTION_ClOSE: + ctx->wait_in_close = true; + rv = close(ctx->fd); + rtems_test_assert(rv == 0); + break; + case ACTION_FCNTL: + rv = fcntl(ctx->fd, F_GETFD); + rtems_test_assert(rv >= 0); + break; + case ACTION_FDATASYNC: + rv = fdatasync(ctx->fd); + rtems_test_assert(rv == 0); + break; + case ACTION_FCHDIR: + ctx->wait_in_fstat = true; + rv = fchdir(ctx->fd); + rtems_test_assert(rv == -1); + rtems_test_assert(errno == ENOTDIR); + break; + case ACTION_FCHMOD: + rv = fstat(ctx->fd, &st); + rtems_test_assert(rv == 0); + ctx->wait_in_fstat = true; + rv = fchmod(ctx->fd, st.st_mode); + rtems_test_assert(rv == 0); + break; + case ACTION_FCHOWN: + rv = fstat(ctx->fd, &st); + rtems_test_assert(rv == 0); + ctx->wait_in_fstat = true; + rv = fchown(ctx->fd, st.st_uid, st.st_gid); + rtems_test_assert(rv == 0); + break; + case ACTION_FSTAT: + ctx->wait_in_fstat = true; + rv = fstat(ctx->fd, &st); + rtems_test_assert(rv == 0); + break; + case ACTION_FSYNC: + rv = fsync(ctx->fd); + rtems_test_assert(rv == 0); + break; + case ACTION_FTRUNCATE: + rv = ftruncate(ctx->fd, 0); + rtems_test_assert(rv == 0); + break; + case ACTION_IOCTL: + rv = ioctl(ctx->fd, 0); + rtems_test_assert(rv == 0); + break; + case ACTION_LSEEK: + off = lseek(ctx->fd, off, SEEK_SET); + rtems_test_assert(off == 0); + break; + case ACTION_READ: + n = read(ctx->fd, buf, sizeof(buf)); + rtems_test_assert(n == 0); + break; + case ACTION_READV: + n = readv(ctx->fd, &iov, 1); + rtems_test_assert(n == 0); + break; + case ACTION_WRITE: + n = write(ctx->fd, buf, sizeof(buf)); + rtems_test_assert(n == 0); + break; + case ACTION_WRITEV: + n = writev(ctx->fd, &iov, 1); + rtems_test_assert(n == 0); + break; + default: + rtems_test_assert(0); + break; + } + } +} + +static void test(test_context *ctx) +{ + const char *path = "generic"; + int rv; + rtems_status_code sc; + test_action ac; + + rv = IMFS_make_generic_node( + path, + S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, + &node_control, + ctx + ); + rtems_test_assert(rv == 0); + + sc = rtems_task_create( + rtems_build_name('W', 'O', 'R', 'K'), + 1, + RTEMS_MINIMUM_STACK_SIZE, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &ctx->worker_id + ); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start( + ctx->worker_id, + worker_task, + (rtems_task_argument) ctx + ); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + for (ac = ACTION_ClOSE; ac <= ACTION_WRITEV; ++ac) { + ctx->action = ac; + ctx->fd = open(path, O_RDWR); + rtems_test_assert(ctx->fd >= 0); + + wakeup_worker(ctx); + rv = close(ctx->fd); + rtems_test_assert(rv == -1); + + if (ac == ACTION_ClOSE) { + rtems_test_assert(errno == EBADF); + } else { + rtems_test_assert(errno == EBUSY); + } + + wakeup_worker(ctx); + rv = close(ctx->fd); + + if (ac == ACTION_ClOSE) { + rtems_test_assert(rv == -1); + rtems_test_assert(errno == EBADF); + } else { + rtems_test_assert(rv == 0); + } + } + + sc = rtems_task_delete(ctx->worker_id); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + rv = unlink(path); + rtems_test_assert(rv == 0); + + rtems_test_assert(ctx->close_count == 15); + rtems_test_assert(ctx->fcntl_count == 1); + rtems_test_assert(ctx->fdatasync_count == 1); + rtems_test_assert(ctx->fstat_count == 38); + rtems_test_assert(ctx->fsync_count == 1); + rtems_test_assert(ctx->ftruncate_count == 1); + rtems_test_assert(ctx->ioctl_count == 1); + rtems_test_assert(ctx->lseek_count == 1); + rtems_test_assert(ctx->open_count == 15); + rtems_test_assert(ctx->read_count == 1); + rtems_test_assert(ctx->readv_count == 1); + rtems_test_assert(ctx->write_count == 1); + rtems_test_assert(ctx->writev_count == 1); +} + +static void Init(rtems_task_argument arg) +{ + TEST_BEGIN(); + test(&test_instance); + TEST_END(); + rtems_test_exit(0); +} + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER + +#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 4 + +#define CONFIGURE_MAXIMUM_TASKS 2 + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INIT_TASK_PRIORITY 2 +#define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES + +#define CONFIGURE_INIT + +#include -- cgit v1.2.3