summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2012-03-09 14:46:40 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2012-03-14 11:14:38 +0100
commit23ed785839045d39e4757a039e5ab3045808527b (patch)
treea9e43dc6f3e13483dfaffe43f3368e247e595138
parentPR2040: libblock: Flash disk starvations statistic (diff)
downloadrtems-23ed785839045d39e4757a039e5ab3045808527b.tar.bz2
PR2040: libtests/flashdisk01: New test
-rw-r--r--testsuites/libtests/Makefile.am1
-rw-r--r--testsuites/libtests/configure.ac1
-rw-r--r--testsuites/libtests/flashdisk01/Makefile.am19
-rw-r--r--testsuites/libtests/flashdisk01/flashdisk01.doc11
-rw-r--r--testsuites/libtests/flashdisk01/flashdisk01.scn265
-rw-r--r--testsuites/libtests/flashdisk01/init.c353
-rw-r--r--testsuites/libtests/flashdisk01/test-file-system.c832
-rw-r--r--testsuites/libtests/flashdisk01/test-file-system.h52
8 files changed, 1534 insertions, 0 deletions
diff --git a/testsuites/libtests/Makefile.am b/testsuites/libtests/Makefile.am
index 0e30a51032..3bcd8c2e70 100644
--- a/testsuites/libtests/Makefile.am
+++ b/testsuites/libtests/Makefile.am
@@ -5,6 +5,7 @@
ACLOCAL_AMFLAGS = -I ../aclocal
SUBDIRS = POSIX
+SUBDIRS += flashdisk01
SUBDIRS += bspcmdline01 cpuuse devfs01 devfs02 devfs03 devfs04 \
deviceio01 devnullfatal01 dumpbuf01 gxx01 \
diff --git a/testsuites/libtests/configure.ac b/testsuites/libtests/configure.ac
index b634ff8246..ec022bcf10 100644
--- a/testsuites/libtests/configure.ac
+++ b/testsuites/libtests/configure.ac
@@ -43,6 +43,7 @@ AM_CONDITIONAL(NETTESTS,test "$rtems_cv_RTEMS_NETWORKING" = "yes")
# Explicitly list all Makefiles here
AC_CONFIG_FILES([Makefile
+flashdisk01/Makefile
block01/Makefile
block02/Makefile
block03/Makefile
diff --git a/testsuites/libtests/flashdisk01/Makefile.am b/testsuites/libtests/flashdisk01/Makefile.am
new file mode 100644
index 0000000000..fd34fe6d8c
--- /dev/null
+++ b/testsuites/libtests/flashdisk01/Makefile.am
@@ -0,0 +1,19 @@
+rtems_tests_PROGRAMS = flashdisk01
+flashdisk01_SOURCES = init.c test-file-system.c
+
+dist_rtems_tests_DATA = flashdisk01.scn flashdisk01.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 = $(flashdisk01_OBJECTS)
+LINK_LIBS = $(flashdisk01_LDLIBS)
+
+flashdisk01$(EXEEXT): $(flashdisk01_OBJECTS) $(flashdisk01_DEPENDENCIES)
+ @rm -f flashdisk01$(EXEEXT)
+ $(make-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/libtests/flashdisk01/flashdisk01.doc b/testsuites/libtests/flashdisk01/flashdisk01.doc
new file mode 100644
index 0000000000..6bb1205f13
--- /dev/null
+++ b/testsuites/libtests/flashdisk01/flashdisk01.doc
@@ -0,0 +1,11 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: flashdisk01
+
+directives:
+
+ TBD
+
+concepts:
+
+ TBD
diff --git a/testsuites/libtests/flashdisk01/flashdisk01.scn b/testsuites/libtests/flashdisk01/flashdisk01.scn
new file mode 100644
index 0000000000..5dcc4b66e8
--- /dev/null
+++ b/testsuites/libtests/flashdisk01/flashdisk01.scn
@@ -0,0 +1,265 @@
+*** TEST FLASHDISK 1 ***
+[00]: start
+[00]: mount: /dev/fdda -> /mnt
+[00]: init root: /mnt
+[00]: create dir: 1804928587
+[00]: create dir: 959030623
+[00]: create dir: 1903590565
+[00]: open file: file
+[00]: sleep: 700 ms
+[00]: close file
+[00]: sleep: 200 ms
+[00]: open dir: 959030623
+[00]: create dir: 838545539
+[00]: create file: 1594243340
+[00]: create file: 162216788
+[00]: create file: 1841205112
+[00]: sleep: 700 ms
+[00]: remove dir: 838545539
+[00]: create dir: 1143741253
+[00]: remove file: 1594243340
+[00]: sleep: 400 ms
+[00]: close dir
+[00]: sleep: 400 ms
+[00]: create file: 832100416
+[00]: open dir: 1903590565
+[00]: create file: 1401208270
+[00]: create dir: 2032315143
+[00]: create dir: 2026989069
+[00]: close dir
+[00]: open dir: 1804928587
+[00]: close dir
+[00]: open dir: 1804928587
+[00]: sleep: 100 ms
+[00]: create dir: 650320721
+[00]: sleep: 500 ms
+[00]: create dir: 1857409239
+[00]: create file: 2060801678
+[00]: create dir: 490998763
+[00]: create dir: 644928527
+[00]: create file: 1758287264
+[00]: close dir
+[00]: sleep: 500 ms
+[00]: open file: file
+[00]: read from file
+[00]: read from file
+[00]: read from file
+[00]: sleep: 700 ms
+[00]: read from file
+[00]: sleep: 200 ms
+[00]: sleep: 300 ms
+[00]: read from file
+[00]: append to file
+[00]: read from file
+[00]: close file
+[00]: remove dir: 1804928587
+[00]: create file: 2073894790
+[00]: sleep: 900 ms
+[00]: create dir: 472737403
+[00]: remove dir: 1903590565
+[00]: create dir: 689486081
+[00]: create dir: 373636079
+[00]: remove file: file
+[00]: create dir: 1506727461
+[00]: open dir: 959030623
+[00]: create dir: 1029668001
+[00]: remove file: 162216788
+[00]: close dir
+[00]: sleep: 400 ms
+[00]: open dir: 959030623
+[00]: remove file: 1841205112
+[00]: create file: 378843792
+[00]: sleep: 1000 ms
+[00]: create file: 1871951748
+[00]: open dir: 1143741253
+[00]: create file: 855915646
+[00]: sleep: 700 ms
+[00]: create file: 2008730550
+[00]: close dir
+[00]: create file: 1327563176
+[00]: remove dir: 1143741253
+[00]: create file: 1384249492
+[00]: close dir
+[00]: remove dir: 959030623
+[00]: open file: 832100416
+[00]: read from file
+[00]: sleep: 400 ms
+[00]: close file
+[00]: open file: 832100416
+[00]: close file
+[00]: create dir: 2025552021
+[00]: remove dir: 959030623
+[00]: create dir: 1178339711
+[00]: remove file: 832100416
+[00]: remove file: 2073894790
+[00]: open dir: 1506727461
+[00]: sleep: 700 ms
+[00]: close dir
+[00]: sleep: 300 ms
+[00]: open dir: 1506727461
+[00]: create dir: 1625748679
+[00]: close dir
+[00]: remove dir: 689486081
+[00]: remove dir: 959030623
+[00]: open dir: 959030623
+[00]: sleep: 1000 ms
+[00]: close dir
+[00]: sleep: 100 ms
+[00]: open dir: 959030623
+[00]: create dir: 1128727833
+[00]: create file: 1203746106
+[00]: open dir: 1128727833
+[00]: create dir: 58221889
+[00]: create file: 611814452
+[00]: create dir: 420219909
+[00]: close dir
+[00]: remove file: 1384249492
+[00]: close dir
+[00]: open dir: 959030623
+[00]: close dir
+[00]: sleep: 800 ms
+[00]: remove dir: 1506727461
+[00]: create file: 550820384
+[00]: create file: 833699694
+[00]: remove dir: 1506727461
+[00]: create dir: 861614531
+[00]: remove dir: 1506727461
+[00]: create dir: 846638047
+[00]: remove dir: 1506727461
+[00]: create dir: 1766470371
+[00]: remove dir: 959030623
+[00]: create file: 1969337196
+[00]: remove dir: 1506727461
+[00]: create file: 1937189238
+[00]: remove dir: 959030623
+[00]: create dir: 1615521955
+[00]: remove dir: 959030623
+[00]: sleep: 500 ms
+[00]: open dir: 1506727461
+[00]: create file: 926653184
+[00]: remove dir: 1625748679
+[00]: close dir
+[00]: create dir: 975747319
+[00]: open dir: 959030623
+[00]: open file: 1871951748
+[00]: sleep: 600 ms
+[00]: close file
+[00]: sleep: 800 ms
+[00]: remove file: 378843792
+[00]: open file: 1203746106
+[00]: read from file
+[00]: append to file
+[00]: sleep: 600 ms
+[00]: close file
+[00]: open file: 1871951748
+[00]: append to file
+[00]: close file
+[00]: remove file: 1871951748
+[00]: sleep: 400 ms
+[00]: create file: 1835979858
+[00]: open dir: 1128727833
+[00]: create dir: 862728993
+[00]: sleep: 700 ms
+[00]: remove dir: 862728993
+[00]: close dir
+[00]: close dir
+[00]: open dir: 975747319
+[00]: sleep: 400 ms
+[00]: create dir: 1840098529
+[00]: create file: 231405798
+[00]: remove dir: 1840098529
+[00]: close dir
+[00]: remove file: 550820384
+[00]: create file: 1020303340
+[00]: create dir: 895068705
+[00]: open dir: 975747319
+[00]: close dir
+[00]: open file: 1020303340
+[00]: read from file
+[00]: close file
+[00]: create dir: 1993404773
+[00]: remove file: 1020303340
+[00]: create dir: 2112395285
+[00]: create dir: 81202983
+[00]: remove dir: 975747319
+[00]: remove dir: 959030623
+[00]: create dir: 467069459
+[00]: open dir: 1506727461
+[00]: create file: 612609068
+[00]: create dir: 1793174441
+[00]: create file: 710808000
+[00]: sleep: 300 ms
+[00]: create file: 897370450
+[00]: create file: 1901152134
+[00]: sleep: 200 ms
+[00]: close dir
+[00]: open dir: 1506727461
+[00]: close dir
+[00]: sleep: 800 ms
+[00]: create file: 906254788
+[00]: remove dir: 467069459
+[00]: remove dir: 1506727461
+[00]: open dir: 895068705
+[00]: close dir
+[00]: create file: 1216543556
+[00]: create file: 37176518
+[00]: open dir: 2112395285
+[00]: close dir
+[00]: create dir: 659416143
+[00]: remove file: 37176518
+[00]: sleep: 500 ms
+[00]: remove dir: 895068705
+[00]: create dir: 1507289677
+[00]: open dir: 959030623
+[00]: create file: 1766335976
+[00]: close dir
+[00]: sleep: 400 ms
+[00]: open dir: 2112395285
+[00]: create dir: 1476817327
+[00]: close dir
+[00]: create dir: 1841667551
+[00]: remove dir: 2112395285
+[00]: open dir: 1507289677
+[00]: close dir
+[00]: create file: 1610238546
+[00]: create file: 220151308
+[00]: remove dir: 1507289677
+[00]: open file: 1216543556
+[00]: append to file
+[00]: append to file
+[00]: close file
+[00]: remove file: 1216543556
+[00]: open dir: 959030623
+[00]: sleep: 500 ms
+[00]: create file: 1508553554
+[00]: create file: 1742399670
+[00]: sleep: 100 ms
+[00]: close dir
+[00]: create dir: 2113094209
+[00]: remove dir: 959030623
+[00]: remove file: 1610238546
+[00]: create dir: 2146380029
+[00]: sleep: 700 ms
+[00]: create file: 1557834172
+[00]: remove dir: 2146380029
+[00]: finish
+fdisk:Flash Disk Driver Status : 2.0
+fdisk:Block count 124
+fdisk:Unavail blocks 32
+fdisk:Starvations 76
+fdisk:Available queue 2 (2)
+fdisk:Used queue 2 (2)
+fdisk:Erase queue 0 (0)
+fdisk:Failed queue 0 (0)
+fdisk:Queue total 4 of 4, ok
+fdisk:Device count 1
+fdisk: Device 0
+fdisk: Segment count 4
+fdisk: 0 A--- p: 31 a: 30/ 30 u: 0/ 0 e: 1/ 1 br:30
+fdisk: 1 A--- p: 31 a: 0/ 0 u: 0/ 0 e: 31/ 31 br:0
+fdisk: 2 -U-- p: 31 a: 31/ 31 u: 0/ 0 e: 0/ 0 br:31
+fdisk: 3 -U-- p: 31 a: 31/ 31 u: 0/ 0 e: 0/ 0 br:31
+fdisk:Used List:
+fdisk: 0 00:002 u: 0
+fdisk: 1 00:003 u: 0
+*** END OF TEST FLASHDISK 1 ***
diff --git a/testsuites/libtests/flashdisk01/init.c b/testsuites/libtests/flashdisk01/init.c
new file mode 100644
index 0000000000..d9f26f38c2
--- /dev/null
+++ b/testsuites/libtests/flashdisk01/init.c
@@ -0,0 +1,353 @@
+/*
+ * 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.com/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "tmacros.h"
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <rtems/flashdisk.h>
+#include <rtems/libio.h>
+#include <rtems/blkdev.h>
+#include <rtems/rtems-rfs-format.h>
+
+#include "test-file-system.h"
+
+#define FLASHDISK_CONFIG_COUNT 1
+
+#define FLASHDISK_DEVICE_COUNT 1
+
+#define FLASHDISK_SEGMENT_COUNT 4
+
+#define FLASHDISK_SEGMENT_SIZE (16 * 1024)
+
+#define FLASHDISK_BLOCK_SIZE 512
+
+#define FLASHDISK_BLOCKS_PER_SEGMENT \
+ (FLASHDISK_SEGMENT_SIZE / FLASHDISK_BLOCK_SIZE)
+
+#define FLASHDISK_SIZE \
+ (FLASHDISK_SEGMENT_COUNT * FLASHDISK_SEGMENT_SIZE)
+
+static const rtems_rfs_format_config rfs_config;
+
+static const char device [] = "/dev/fdda";
+
+static const char mnt [] = "/mnt";
+
+static const char file [] = "/mnt/file";
+
+static uint8_t flashdisk_data [FLASHDISK_SIZE];
+
+static void flashdisk_print_status(const char *disk_path)
+{
+ int rv;
+ int fd = open(disk_path, O_RDWR);
+ rtems_test_assert(fd >= 0);
+
+ rv = ioctl(fd, RTEMS_FDISK_IOCTL_PRINT_STATUS);
+ rtems_test_assert(rv == 0);
+
+ rv = close(fd);
+ rtems_test_assert(rv == 0);
+}
+
+static int test_rfs_mount_handler(
+ const char *disk_path,
+ const char *mount_path,
+ void *arg
+)
+{
+ return mount_and_make_target_path(
+ disk_path,
+ mount_path,
+ RTEMS_FILESYSTEM_TYPE_RFS,
+ RTEMS_FILESYSTEM_READ_WRITE,
+ NULL
+ );
+}
+
+static int test_rfs_format_handler(
+ const char *disk_path,
+ void *arg
+)
+{
+ flashdisk_print_status(disk_path);
+
+ rtems_test_assert(0);
+
+ errno = EIO;
+
+ return -1;
+}
+
+static const test_file_system_handler test_rfs_handler = {
+ .mount = test_rfs_mount_handler,
+ .format = test_rfs_format_handler
+};
+
+static void test(void)
+{
+ int rv;
+ const void *data = NULL;
+
+ rv = mkdir(mnt, S_IRWXU | S_IRWXG | S_IRWXO);
+ rtems_test_assert(rv == 0);
+
+ rv = rtems_rfs_format(device, &rfs_config);
+ rtems_test_assert(rv == 0);
+
+ rv = mount(
+ device,
+ mnt,
+ RTEMS_FILESYSTEM_TYPE_RFS,
+ RTEMS_FILESYSTEM_READ_WRITE,
+ data
+ );
+ rtems_test_assert(rv == 0);
+
+ rv = mknod(file, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO, 0);
+ rtems_test_assert(rv == 0);
+
+ rv = unmount(mnt);
+ rtems_test_assert(rv == 0);
+
+ test_file_system_with_handler(
+ 0,
+ device,
+ mnt,
+ &test_rfs_handler,
+ NULL
+ );
+
+ flashdisk_print_status(device);
+}
+
+static void Init(rtems_task_argument arg)
+{
+ puts("\n\n*** TEST FLASHDISK 1 ***");
+
+ test();
+
+ puts("*** END OF TEST FLASHDISK 1 ***");
+
+ rtems_test_exit(0);
+}
+
+static void erase_device(void)
+{
+ memset(&flashdisk_data [0], 0xff, FLASHDISK_SIZE);
+}
+
+static rtems_device_driver flashdisk_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+)
+{
+ erase_device();
+
+ return rtems_fdisk_initialize(major, minor, arg);
+}
+
+static uint8_t *get_data_pointer(
+ const rtems_fdisk_segment_desc *sd,
+ uint32_t segment,
+ uint32_t offset
+)
+{
+ offset += sd->offset + (segment - sd->segment) * sd->size;
+
+ return &flashdisk_data [offset];
+}
+
+static int flashdisk_read(
+ const rtems_fdisk_segment_desc *sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ void *buffer,
+ uint32_t size
+)
+{
+ int eno = 0;
+ const uint8_t *data = get_data_pointer(sd, segment, offset);
+
+ memcpy(buffer, data, size);
+
+ return eno;
+}
+
+static int flashdisk_write(
+ const rtems_fdisk_segment_desc *sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void *buffer,
+ uint32_t size
+)
+{
+ int eno = 0;
+ uint8_t *data = get_data_pointer(sd, segment, offset);
+
+ memcpy(data, buffer, size);
+
+ return eno;
+}
+
+static int flashdisk_blank(
+ const rtems_fdisk_segment_desc *sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ uint32_t size
+)
+{
+ int eno = 0;
+ const uint8_t *current = get_data_pointer(sd, segment, offset);
+ const uint8_t *end = current + size;;
+
+ while (eno == 0 && current != end) {
+ if (*current != 0xff) {
+ eno = EIO;
+ }
+ ++current;
+ }
+
+ return eno;
+}
+
+static int flashdisk_verify(
+ const rtems_fdisk_segment_desc *sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void *buffer,
+ uint32_t size
+)
+{
+ int eno = 0;
+ uint8_t *data = get_data_pointer(sd, segment, offset);
+
+ if (memcmp(data, buffer, size) != 0) {
+ eno = EIO;
+ }
+
+ return eno;
+}
+
+static int flashdisk_erase(
+ const rtems_fdisk_segment_desc *sd,
+ uint32_t device,
+ uint32_t segment
+)
+{
+ int eno = 0;
+ uint8_t *data = get_data_pointer(sd, segment, 0);
+
+ memset(data, 0xff, sd->size);
+
+ return eno;
+}
+
+static int flashdisk_erase_device(
+ const rtems_fdisk_device_desc *sd,
+ uint32_t device
+)
+{
+ int eno = 0;
+
+ erase_device();
+
+ return eno;
+}
+
+static const rtems_fdisk_segment_desc flashdisk_segment_desc = {
+ .count = FLASHDISK_SEGMENT_COUNT,
+ .segment = 0,
+ .offset = 0,
+ .size = FLASHDISK_SEGMENT_SIZE
+};
+
+static const rtems_fdisk_driver_handlers flashdisk_ops = {
+ .read = flashdisk_read,
+ .write = flashdisk_write,
+ .blank = flashdisk_blank,
+ .verify = flashdisk_verify,
+ .erase = flashdisk_erase,
+ .erase_device = flashdisk_erase_device
+};
+
+static const rtems_fdisk_device_desc flashdisk_device = {
+ .segment_count = 1,
+ .segments = &flashdisk_segment_desc,
+ .flash_ops = &flashdisk_ops
+};
+
+const rtems_flashdisk_config
+rtems_flashdisk_configuration [FLASHDISK_CONFIG_COUNT] = {
+ {
+ .block_size = FLASHDISK_BLOCK_SIZE,
+ .device_count = FLASHDISK_DEVICE_COUNT,
+ .devices = &flashdisk_device,
+ .flags = RTEMS_FDISK_CHECK_PAGES
+ | RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE,
+ .unavail_blocks = FLASHDISK_BLOCKS_PER_SEGMENT,
+ .compact_segs = 2,
+ .avail_compact_segs = 1,
+ .info_level = 0
+ }
+};
+
+uint32_t rtems_flashdisk_configuration_size = FLASHDISK_CONFIG_COUNT;
+
+#define FLASHDISK_DRIVER { \
+ .initialization_entry = flashdisk_initialize, \
+ .open_entry = rtems_blkdev_generic_open, \
+ .close_entry = rtems_blkdev_generic_close, \
+ .read_entry = rtems_blkdev_generic_read, \
+ .write_entry = rtems_blkdev_generic_write, \
+ .control_entry = rtems_blkdev_generic_ioctl \
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+#define CONFIGURE_APPLICATION_EXTRA_DRIVERS FLASHDISK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
+
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 6
+
+#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
+#define CONFIGURE_FILESYSTEM_RFS
+
+#define CONFIGURE_MAXIMUM_TASKS 2
+#define CONFIGURE_MAXIMUM_SEMAPHORES 1
+
+#define CONFIGURE_MINIMUM_TASK_STACK_SIZE (32 * 1024)
+
+#define CONFIGURE_EXTRA_TASK_STACKS (8 * 1024)
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/libtests/flashdisk01/test-file-system.c b/testsuites/libtests/flashdisk01/test-file-system.c
new file mode 100644
index 0000000000..d3515d0743
--- /dev/null
+++ b/testsuites/libtests/flashdisk01/test-file-system.c
@@ -0,0 +1,832 @@
+/*
+ * Copyright (c) 2010-2012 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * 82178 Puchheim
+ * Germany
+ * <info@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.com/license/LICENSE.
+ */
+
+#include "test-file-system.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/endian.h>
+
+#define ASSERT_SC(sc) assert((sc) == RTEMS_SUCCESSFUL)
+
+#define BUFFER_SIZE (32 * 1024)
+
+#define HEADER_SIZE 8
+
+/**
+ * @brief Test states.
+ *
+ * digraph {
+ * INIT -> MOUNT;
+ * MOUNT -> INIT_ROOT;
+ * MOUNT -> FORMAT;
+ * INIT_ROOT -> CHOOSE_DIR_ACTION
+ * INIT_ROOT -> ERROR;
+ * CHOOSE_DIR_ACTION -> DIR_OPEN;
+ * CHOOSE_DIR_ACTION -> DIR_CLOSE;
+ * CHOOSE_DIR_ACTION -> DIR_CREATE;
+ * CHOOSE_DIR_ACTION -> DIR_DELETE;
+ * CHOOSE_DIR_ACTION -> DIR_SLEEP;
+ * DIR_OPEN -> CHOOSE_DIR_ACTION;
+ * DIR_OPEN -> CHOOSE_FILE_ACTION;
+ * DIR_OPEN -> ERROR;
+ * DIR_CLOSE -> CHOOSE_DIR_ACTION;
+ * DIR_CLOSE -> ERROR;
+ * DIR_CREATE -> CHOOSE_DIR_ACTION;
+ * DIR_CREATE -> DIR_DELETE;
+ * DIR_CREATE -> ERROR;
+ * DIR_DELETE -> CHOOSE_DIR_ACTION;
+ * DIR_DELETE -> ERROR;
+ * DIR_SLEEP -> CHOOSE_DIR_ACTION;
+ * CHOOSE_FILE_ACTION -> FILE_CLOSE;
+ * CHOOSE_FILE_ACTION -> FILE_APPEND;
+ * CHOOSE_FILE_ACTION -> FILE_READ;
+ * CHOOSE_FILE_ACTION -> FILE_SLEEP;
+ * FILE_CLOSE -> CHOOSE_DIR_ACTION;
+ * FILE_CLOSE -> ERROR;
+ * FILE_APPEND -> CHOOSE_FILE_ACTION;
+ * FILE_APPEND -> FILE_CLOSE_AND_UNLINK;
+ * FILE_APPEND -> ERROR;
+ * FILE_READ -> CHOOSE_FILE_ACTION;
+ * FILE_READ -> ERROR;
+ * FILE_SLEEP -> CHOOSE_FILE_ACTION;
+ * FILE_CLOSE_AND_UNLINK -> CHOOSE_DIR_ACTION;
+ * FILE_CLOSE_AND_UNLINK -> ERROR;
+ * ERROR -> FORMAT;
+ * FORMAT -> MOUNT;
+ * FORMAT -> FINISH;
+ * }
+ */
+typedef enum {
+ CHOOSE_DIR_ACTION,
+ CHOOSE_FILE_ACTION,
+ DIR_CLOSE,
+ DIR_CREATE,
+ DIR_DELETE,
+ DIR_OPEN,
+ DIR_SLEEP,
+ ERROR,
+ FILE_APPEND,
+ FILE_READ,
+ FILE_CLOSE,
+ FILE_SLEEP,
+ FILE_CLOSE_AND_UNLINK,
+ FINISH,
+ FORMAT,
+ INIT,
+ INIT_ROOT,
+ MOUNT
+} test_state;
+
+typedef struct {
+ DIR *dir;
+ unsigned level;
+ unsigned content_count;
+ int fd;
+ int eno;
+ uint8_t buf [BUFFER_SIZE];
+ char file_path [MAXPATHLEN];
+} fs_state;
+
+static test_state do_format(
+ unsigned index,
+ fs_state *fs,
+ const char *disk_path,
+ const test_file_system_handler *handler,
+ void *handler_arg
+)
+{
+ int rv = 0;
+
+ printf("[%02u]: format: %s\n", index, disk_path);
+
+ rv = (*handler->format)(disk_path, handler_arg);
+ if (rv == 0) {
+ return MOUNT;
+ } else {
+ fs->eno = errno;
+
+ return FINISH;
+ }
+}
+
+static uint32_t simple_random(uint32_t v)
+{
+ v *= 1664525;
+ v += 1013904223;
+
+ return v;
+}
+
+static unsigned get_bucket_with_random(unsigned count, long random)
+{
+ long unsigned unit = (1U << 31) / count;
+ long unsigned bucket = (long unsigned) random / unit;
+
+ if (bucket != count) {
+ return bucket;
+ } else {
+ return bucket - 1;
+ }
+}
+
+static unsigned get_bucket(unsigned count)
+{
+ return get_bucket_with_random(count, lrand48());
+}
+
+static test_state do_choose_dir_action(void)
+{
+ switch (get_bucket(8)) {
+ case 0:
+ case 1:
+ return DIR_CLOSE;
+ case 2:
+ case 3:
+ return DIR_CREATE;
+ case 4:
+ case 5:
+ return DIR_OPEN;
+ case 6:
+ return DIR_DELETE;
+ case 7:
+ return DIR_SLEEP;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+static test_state do_choose_file_action(void)
+{
+ switch (get_bucket(4)) {
+ case 0:
+ return FILE_CLOSE;
+ case 1:
+ return FILE_SLEEP;
+ case 2:
+ return FILE_APPEND;
+ case 3:
+ return FILE_READ;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+static bool is_normal_entry(const char *entry_name)
+{
+ static const char *black_list [] = {
+ ".",
+ "..",
+ "lost+found"
+ };
+ size_t n = sizeof(black_list) / sizeof(black_list [0]);
+ size_t i = 0;
+ bool ok = true;
+
+ while (ok && i < n) {
+ ok = ok && strcmp(entry_name, black_list [i]) != 0;
+ ++i;
+ }
+
+ return ok;
+}
+
+static bool open_dir(fs_state *fs, const char *dir_path)
+{
+ int rv = 0;
+ bool change_dir = true;
+
+ if (dir_path == NULL) {
+ if (fs->level > 1) {
+ rv = chdir("..");
+ if (rv != 0) {
+ fs->eno = errno;
+
+ return false;
+ }
+
+ --fs->level;
+ } else {
+ return true;
+ }
+ dir_path = ".";
+ change_dir = false;
+ }
+
+ if (fs->dir != NULL) {
+ rv = closedir(fs->dir);
+ if (rv != 0) {
+ fs->eno = errno;
+
+ return false;
+ }
+ }
+
+ fs->content_count = 0;
+ fs->dir = opendir(dir_path);
+
+ if (fs->dir != NULL) {
+ struct dirent *de = NULL;
+
+ rewinddir(fs->dir);
+ while ((de = readdir(fs->dir)) != NULL) {
+ if (is_normal_entry(de->d_name)) {
+ ++fs->content_count;
+ }
+ }
+ } else {
+ fs->eno = errno;
+
+ return false;
+ }
+
+ if (change_dir) {
+ rv = chdir(dir_path);
+ if (rv != 0) {
+ fs->eno = errno;
+
+ return false;
+ }
+
+ ++fs->level;
+ }
+
+ return true;
+}
+
+static char *get_dir_entry(fs_state *fs, bool *is_dir)
+{
+ int rv = 0;
+ char *entry_name = NULL;
+
+ if (fs->content_count > 0) {
+ struct dirent *de = NULL;
+ unsigned bucket = get_bucket(fs->content_count);
+ unsigned i = 0;
+
+ rewinddir(fs->dir);
+ while ((de = readdir(fs->dir)) != NULL) {
+ if (is_normal_entry(de->d_name)) {
+ if (i != bucket) {
+ ++i;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (de != NULL) {
+ struct stat st;
+
+ rv = stat(de->d_name, &st);
+ if (rv == 0) {
+ *is_dir = S_ISDIR(st.st_mode);
+
+ entry_name = strdup(de->d_name);
+ }
+ }
+ }
+
+ return entry_name;
+}
+
+
+static test_state do_init_root(unsigned index, fs_state *fs, const char *mount_path)
+{
+ printf("[%02u]: init root: %s\n", index, mount_path);
+
+ if (open_dir(fs, mount_path)) {
+ return CHOOSE_DIR_ACTION;
+ } else {
+ return ERROR;
+ }
+}
+
+static test_state do_dir_close(unsigned index, fs_state *fs)
+{
+ if (fs->level > 1) {
+ printf("[%02u]: close dir\n", index);
+
+ if (open_dir(fs, NULL)) {
+ return CHOOSE_DIR_ACTION;
+ } else {
+ return ERROR;
+ }
+ } else {
+ return CHOOSE_DIR_ACTION;
+ }
+}
+
+static int open_file(fs_state *fs, const char *path, int flags, mode_t mode)
+{
+ size_t n = strlcpy(fs->file_path, path, sizeof(fs->file_path));
+ assert(n < sizeof(fs->file_path));
+
+ return open(fs->file_path, flags, mode);
+}
+
+static test_state do_dir_create(unsigned index, fs_state *fs)
+{
+ int rv = 0;
+ test_state state = ERROR;
+ long number = lrand48();
+ char name [64];
+
+ snprintf(name, sizeof(name), "%li", number);
+
+ if ((number % 2) == 0) {
+ printf("[%02u]: create file: %s\n", index, name);
+
+ rv = open_file(fs, name, O_RDONLY | O_CREAT, 0777);
+
+ if (rv >= 0) {
+ rv = close(rv);
+
+ if (rv == 0) {
+ state = CHOOSE_DIR_ACTION;
+ }
+ } else if (errno == ENOSPC) {
+ state = DIR_DELETE;
+ } else {
+ fs->eno = errno;
+ }
+ } else {
+ printf("[%02u]: create dir: %s\n", index, name);
+
+ rv = mkdir(name, 0777);
+
+ if (rv == 0) {
+ ++fs->content_count;
+
+ state = CHOOSE_DIR_ACTION;
+ } else if (errno == EEXIST) {
+ state = CHOOSE_DIR_ACTION;
+ } else if (errno == ENOSPC) {
+ state = DIR_DELETE;
+ } else {
+ fs->eno = errno;
+ }
+ }
+
+ return state;
+}
+
+static test_state remove_node(unsigned index, fs_state *fs, const char *path, bool is_dir)
+{
+ test_state state = ERROR;
+ int rv = 0;
+
+ if (is_dir) {
+ printf("[%02u]: remove dir: %s\n", index, path);
+
+ rv = rmdir(path);
+ } else {
+ printf("[%02u]: remove file: %s\n", index, path);
+
+ rv = unlink(path);
+ }
+
+ if (rv == 0) {
+ --fs->content_count;
+
+ state = CHOOSE_DIR_ACTION;
+ } else if (errno == ENOTEMPTY) {
+ state = CHOOSE_DIR_ACTION;
+ } else {
+ fs->eno = errno;
+ }
+
+ return state;
+}
+
+static test_state do_dir_delete(unsigned index, fs_state *fs)
+{
+ test_state state = ERROR;
+
+ if (fs->content_count > 0) {
+ bool is_dir = false;
+ char *dir_entry_path = get_dir_entry(fs, &is_dir);
+
+ if (dir_entry_path != NULL) {
+ state = remove_node(index, fs, dir_entry_path, is_dir);
+ free(dir_entry_path);
+ }
+ } else {
+ state = CHOOSE_DIR_ACTION;
+ }
+
+ return state;
+}
+
+static test_state do_dir_open(unsigned index, fs_state *fs)
+{
+ test_state state = ERROR;
+
+ if (fs->content_count > 0) {
+ bool is_dir = false;
+ char *dir_entry_path = get_dir_entry(fs, &is_dir);
+
+ if (dir_entry_path != NULL) {
+ if (is_dir) {
+ printf("[%02u]: open dir: %s\n", index, dir_entry_path);
+
+ if (open_dir(fs, dir_entry_path)) {
+ state = CHOOSE_DIR_ACTION;
+ }
+ } else {
+ printf("[%02u]: open file: %s\n", index, dir_entry_path);
+
+ fs->fd = open_file(fs, dir_entry_path, O_RDWR, 0);
+
+ if (fs->fd >= 0) {
+ state = CHOOSE_FILE_ACTION;
+ } else {
+ fs->eno = errno;
+ }
+ }
+
+ free(dir_entry_path);
+ }
+ } else {
+ state = CHOOSE_DIR_ACTION;
+ }
+
+ return state;
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ #define VALUE_SHIFT_INIT 0
+ #define VALUE_SHIFT_INC 8
+#else
+ #define VALUE_SHIFT_INIT 24
+ #define VALUE_SHIFT_INC -8
+#endif
+
+static test_state do_file_read(unsigned index, fs_state *fs)
+{
+ test_state state = ERROR;
+ int fd = fs->fd;
+ off_t pos = lseek(fd, 0, SEEK_SET);
+ struct stat st;
+ int rv = fstat(fd, &st);
+ off_t file_size = st.st_size;
+
+ printf("[%02u]: read from file\n", index);
+
+ if (pos == 0 && rv == 0) {
+ if (file_size >= HEADER_SIZE) {
+ size_t remaining = (size_t) file_size;
+ size_t header = HEADER_SIZE;
+ size_t data = 0;
+ uint32_t header_buf [2];
+ uint8_t *header_pos = (uint8_t *) header_buf;
+ uint32_t value = 0;
+ unsigned value_shift = VALUE_SHIFT_INIT;
+
+ while (remaining > 0) {
+ size_t buf_size = sizeof(fs->buf);
+ size_t in = remaining < buf_size ? remaining : buf_size;
+ ssize_t in_actual = 0;
+ uint8_t *buf = fs->buf;
+
+ in_actual = read(fd, buf, in);
+ if (in_actual > 0) {
+ while (in_actual > 0) {
+ if (header > 0) {
+ size_t copy = header <= (size_t) in_actual ?
+ header : (size_t) in_actual;
+
+ memcpy(header_pos, buf, copy);
+
+ in_actual -= (ssize_t) copy;
+ remaining -= copy;
+ buf += copy;
+ header -= copy;
+ header_pos += copy;
+
+ if (header == 0) {
+ data = header_buf [0];
+ value = header_buf [1];
+ value_shift = VALUE_SHIFT_INIT;
+ value = simple_random(value);
+ }
+ }
+
+ if (data > 0) {
+ size_t compare = data <= (size_t) in_actual ?
+ data : (size_t) in_actual;
+ size_t c = 0;
+
+ for (c = 0; c < compare; ++c) {
+ assert(buf [c] == (uint8_t) (value >> value_shift));
+ value_shift = (value_shift + VALUE_SHIFT_INC) % 32;
+ if (value_shift == VALUE_SHIFT_INIT) {
+ value = simple_random(value);
+ }
+ }
+
+ in_actual -= (ssize_t) compare;
+ remaining -= compare;
+ buf += compare;
+ data -= compare;
+
+ if (data == 0) {
+ header = HEADER_SIZE;
+ header_pos = (uint8_t *) header_buf;
+ }
+ }
+ }
+ } else if (in_actual == 0) {
+ /* This is either a severe bug or a damaged file system */
+ printf("[%02u]: error: invalid file size\n", index);
+ remaining = 0;
+ } else {
+ fs->eno = errno;
+ remaining = 0;
+ }
+ }
+
+ if (remaining == 0) {
+ state = CHOOSE_FILE_ACTION;
+ }
+ } else if (file_size == 0) {
+ state = CHOOSE_FILE_ACTION;
+ } else {
+ printf("[%02u]: error: unexpected file size\n", index);
+ }
+ } else {
+ fs->eno = errno;
+ }
+
+ return state;
+}
+
+static test_state do_file_append(unsigned index, fs_state *fs)
+{
+ test_state state = ERROR;
+ int fd = fs->fd;
+ off_t pos = lseek(fd, 0, SEEK_END);
+
+ printf("[%02u]: append to file\n", index);
+
+ if (pos != (off_t) -1) {
+ size_t buf_size = sizeof(fs->buf);
+ long random = lrand48();
+ size_t out = get_bucket_with_random(buf_size, random) + 1;
+ ssize_t out_actual = 0;
+ uint8_t *buf = fs->buf;
+ uint32_t value = (uint32_t) random;
+ uint32_t *words = (uint32_t *) &buf [0];
+ size_t word_count = 0;
+ size_t w = 0;
+
+ /* Must be big enough for the header */
+ out = out >= HEADER_SIZE ? out : HEADER_SIZE;
+
+ /*
+ * In case out is not an integral multiple of four we will write a bit to
+ * much. This does not hurt since the buffer is big enough.
+ */
+ word_count = (out + 3) / 4;
+
+ /* The first word will contain the byte count with random data */
+ words [0] = out - HEADER_SIZE;
+
+ for (w = 1; w < word_count; ++w) {
+ words [w] = value;
+ value = simple_random(value);
+ }
+
+ out_actual = write(fd, buf, out);
+
+ if (out_actual == (ssize_t) out) {
+ state = CHOOSE_FILE_ACTION;
+ } else if (out_actual >= 0 || errno == ENOSPC) {
+ state = FILE_CLOSE_AND_UNLINK;
+ } else {
+ fs->eno = errno;
+ }
+ } else {
+ fs->eno = errno;
+ }
+
+ return state;
+}
+
+static test_state do_file_close(unsigned index, fs_state *fs)
+{
+ int rv = 0;
+ test_state state = ERROR;
+
+ printf("[%02u]: close file\n", index);
+
+ rv = close(fs->fd);
+ fs->fd = -1;
+
+ if (rv == 0) {
+ state = CHOOSE_DIR_ACTION;
+ } else {
+ fs->eno = errno;
+ }
+
+ return state;
+}
+
+static test_state do_file_close_and_unlink(unsigned index, fs_state *fs)
+{
+ test_state state = do_file_close(index, fs);
+
+ if (state != ERROR) {
+ state = remove_node(index, fs, fs->file_path, false);
+ }
+
+ return state;
+}
+
+static test_state do_sleep(unsigned index, test_state state)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_interval ms = ((rtems_interval) get_bucket(10) + 1) * 100;
+ rtems_interval interval = ms / rtems_configuration_get_milliseconds_per_tick();
+
+ printf("[%02u]: sleep: %" PRIu32 " ms\n", index, ms);
+
+ sc = rtems_task_wake_after(interval);
+ ASSERT_SC(sc);
+
+ return state;
+}
+
+static test_state do_mount(
+ unsigned index,
+ fs_state *fs,
+ const char *disk_path,
+ const char *mount_path,
+ const test_file_system_handler *handler,
+ void *handler_arg
+)
+{
+ int rv = 0;
+
+ printf("[%02u]: mount: %s -> %s\n", index, disk_path, mount_path);
+
+ rv = (*handler->mount)(disk_path, mount_path, handler_arg);
+ if (rv == 0) {
+ return INIT_ROOT;
+ } else {
+ fs->eno = errno;
+
+ return FORMAT;
+ }
+}
+
+static test_state do_error(unsigned index, fs_state *fs, const char *mount_path)
+{
+ int rv = 0;
+
+ if (fs->eno > 0) {
+ printf("[%02u]: error: %s\n", index, strerror(fs->eno));
+ } else {
+ printf("[%02u]: error\n", index);
+ }
+ fs->eno = 0;
+
+ if (fs->fd >= 0) {
+ close(fs->fd);
+ fs->fd = -1;
+ }
+
+ if (fs->dir != NULL) {
+ closedir(fs->dir);
+ fs->dir = NULL;
+ }
+
+ chdir("/");
+ fs->level = 0;
+
+ rv = unmount(mount_path);
+ if (rv == 0) {
+ return FORMAT;
+ } else {
+ return FINISH;
+ }
+}
+
+void test_file_system_with_handler(
+ unsigned index,
+ const char *disk_path,
+ const char *mount_path,
+ const test_file_system_handler *handler,
+ void *handler_arg
+)
+{
+ int counter = 0;
+ test_state state = INIT;
+ fs_state *fs = calloc(1, sizeof(*fs));
+
+ if (fs == NULL) {
+ printf("[%02u]: not enough memory\n", index);
+ return;
+ }
+
+ fs->fd = -1;
+
+ printf("[%02u]: start\n", index);
+
+ while (state != FINISH && counter < 600) {
+ switch (state) {
+ case CHOOSE_DIR_ACTION:
+ state = do_choose_dir_action();
+ break;
+ case CHOOSE_FILE_ACTION:
+ state = do_choose_file_action();
+ break;
+ case DIR_CLOSE:
+ state = do_dir_close(index, fs);
+ break;
+ case DIR_CREATE:
+ state = do_dir_create(index, fs);
+ break;
+ case DIR_DELETE:
+ state = do_dir_delete(index, fs);
+ break;
+ case DIR_OPEN:
+ state = do_dir_open(index, fs);
+ break;
+ case DIR_SLEEP:
+ state = do_sleep(index, CHOOSE_DIR_ACTION);
+ break;
+ case ERROR:
+ state = do_error(index, fs, mount_path);
+ break;
+ case FILE_APPEND:
+ state = do_file_append(index, fs);
+ break;
+ case FILE_READ:
+ state = do_file_read(index, fs);
+ break;
+ case FILE_CLOSE:
+ state = do_file_close(index, fs);
+ break;
+ case FILE_CLOSE_AND_UNLINK:
+ state = do_file_close_and_unlink(index, fs);
+ break;
+ case FILE_SLEEP:
+ state = do_sleep(index, CHOOSE_FILE_ACTION);
+ break;
+ case FORMAT:
+ state = do_format(index, fs, disk_path, handler, handler_arg);
+ break;
+ case INIT:
+ state = MOUNT;
+ break;
+ case INIT_ROOT:
+ state = do_init_root(index, fs, mount_path);
+ break;
+ case MOUNT:
+ state = do_mount(
+ index,
+ fs,
+ disk_path,
+ mount_path,
+ handler,
+ handler_arg
+ );
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ ++counter;
+ }
+
+ printf("[%02u]: finish\n", index);
+
+ free(fs);
+}
diff --git a/testsuites/libtests/flashdisk01/test-file-system.h b/testsuites/libtests/flashdisk01/test-file-system.h
new file mode 100644
index 0000000000..f0894eb8c9
--- /dev/null
+++ b/testsuites/libtests/flashdisk01/test-file-system.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010-2012 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * 82178 Puchheim
+ * Germany
+ * <info@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.com/license/LICENSE.
+ */
+
+#ifndef TEST_FILE_SYSTEM_H
+#define TEST_FILE_SYSTEM_H
+
+#include <rtems.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef int (*test_file_system_mount_handler)(
+ const char *disk_path,
+ const char *mount_path,
+ void *arg
+);
+
+typedef int (*test_file_system_format_handler)(
+ const char *disk_path,
+ void *arg
+);
+
+typedef struct {
+ test_file_system_mount_handler mount;
+ test_file_system_format_handler format;
+} test_file_system_handler;
+
+void test_file_system_with_handler(
+ unsigned index,
+ const char *disk_path,
+ const char *mount_path,
+ const test_file_system_handler *handler,
+ void *handler_arg
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* TEST_FILE_SYSTEM_H */