Merge branch 'sizeofsizeof' master
authorCharles Manning <cdhmanning@gmail.com>
Fri, 15 May 2020 02:36:57 +0000 (14:36 +1200)
committerCharles Manning <cdhmanning@gmail.com>
Fri, 15 May 2020 02:36:57 +0000 (14:36 +1200)
Fix two bugs:

sizeof(sizeof(hdr)) used instaed of sizeof(hr)

remounting a read-only fs in rw did not clean up defered operations
properly.

25 files changed:
direct/optional_sort/qsort.c
direct/test-framework/basic-tests/dtest.c
direct/yaffsfs.c
direct/yportenv.h
rtems/Makefile.rtems [new file with mode: 0644]
rtems/RTEMS_NOTES [new file with mode: 0644]
rtems/rtems-y-test/basic-test/Makefile [new file with mode: 0644]
rtems/rtems-y-test/basic-test/README [new file with mode: 0644]
rtems/rtems-y-test/basic-test/yaffs-rtems-basic-test.c [new file with mode: 0644]
rtems/rtems-y-test/common/yaffs-rtems-flashsim.c [new file with mode: 0644]
rtems/rtems-y-test/common/yaffs-rtems-flashsim.h [new file with mode: 0644]
rtems/rtems-y-test/common/yaffs-rtems-test-wrapper.c [new file with mode: 0644]
rtems/rtems-y-test/fsx/Makefile [new file with mode: 0644]
rtems/rtems-y-test/fsx/README [new file with mode: 0644]
rtems/rtems-y-test/fsx/rtems-fsx.c [new file with mode: 0644]
rtems/rtems_yaffs.c [new file with mode: 0644]
rtems/rtems_yaffs.h [new file with mode: 0644]
rtems/rtems_yaffs_os_context.c [new file with mode: 0644]
rtems/rtems_yaffs_os_glue.c [new file with mode: 0644]
yaffs_checkptrw.c
yaffs_guts.c
yaffs_guts.h
yaffs_tagsmarshall.c
yaffs_vfs_multi.c
yaffs_vfs_single.c

index 9c77899..b66f930 100644 (file)
@@ -151,7 +151,7 @@ loop:       SWAPINIT(a, es);
        vecswap(pb, pn - r, r);
        r = pb - pa;
        if (r > es)
-               yaffs_qsort(a, r / es, es, cmp);
+               qsort(a, r / es, es, cmp);
        r = pd - pc;
        if (r > es) {
                /* Iterate rather than recurse to save stack space */
@@ -159,5 +159,5 @@ loop:       SWAPINIT(a, es);
                n = r / es;
                goto loop;
        }
-/*             yaffs_qsort(pn - r, r / es, es, cmp);*/
+/*             qsort(pn - r, r / es, es, cmp);*/
 }
index 8d1b991..1a98bcb 100644 (file)
@@ -519,7 +519,7 @@ void dumpDirFollow(const char *dname)
        yaffs_DIR *d;
        struct yaffs_dirent *de;
        struct yaffs_stat s;
-       char str[100];
+       char str[300];
 
        d = yaffs_opendir(dname);
 
@@ -970,7 +970,7 @@ int huge_directory_test_on_path(char *path)
        int total = 0;
        int lastTotal = 0;
 
-       char str[100];
+       char str[300];
 
 
        yaffs_start_up();
index af8c82b..d1e4e4e 100644 (file)
@@ -3050,6 +3050,7 @@ int yaffs_remount_common(struct yaffs_dev *dev, const YCHAR *path,
                       int force, int read_only)
 {
        int retVal = -1;
+       int was_read_only;
 
        if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
                yaffsfs_SetError(-EFAULT);
@@ -3072,7 +3073,11 @@ int yaffs_remount_common(struct yaffs_dev *dev, const YCHAR *path,
                        if (force || !yaffsfs_IsDevBusy(dev)) {
                                if (read_only)
                                        yaffs_checkpoint_save(dev);
+                               was_read_only = dev->read_only;
                                dev->read_only = read_only ? 1 : 0;
+                               if (was_read_only && !read_only) {
+                                       yaffs_guts_cleanup(dev);
+                               }
                                retVal = 0;
                        } else
                                yaffsfs_SetError(-EBUSY);
index d931f81..ee27f73 100644 (file)
 #define __YPORTENV_H__
 
 
+#ifdef __rtems__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define CONFIG_YAFFS_DIRECT 1
+#define CONFIG_YAFFS_SHORT_NAMES_IN_RAM 1
+#define CONFIG_YAFFS_YAFFS2 1
+#define CONFIG_YAFFS_PROVIDE_DEFS 1
+#define CONFIG_YAFFSFS_PROVIDE_VALUES 1
+#define CONFIG_YAFFS_DEFINES_TYPES 1
+#define NO_Y_INLINE 1
+#define loff_t off_t
+
+#endif /* __rtems__ */
+
 /* Definition of types */
 #ifdef CONFIG_YAFFS_DEFINES_TYPES
 typedef unsigned char u8;
@@ -26,7 +44,6 @@ typedef unsigned long long u64;
 typedef signed int s32;
 #endif
 
-
 #ifdef CONFIG_YAFFS_PROVIDE_DEFS
 /* File types */
 
diff --git a/rtems/Makefile.rtems b/rtems/Makefile.rtems
new file mode 100644 (file)
index 0000000..4803a9f
--- /dev/null
@@ -0,0 +1,151 @@
+#
+# This file was originally written to work from the yaffs2 base directory
+# which required deleting some of the Linux files.
+#
+# This is now modified to run from the yaffs2/rtems directory and copies in
+# all files as symbolic links.
+#
+
+include $(RTEMS_MAKEFILE_PATH)/Makefile.inc
+include $(RTEMS_MAKEFILE_PATH)/make/target.cfg
+
+INSTALL_BASE = $(RTEMS_MAKEFILE_PATH)/lib
+
+BUILDDIR = build-$(RTEMS_BSP)
+
+CPPFLAGS += -I. 
+
+DEPFLAGS = -MT $@ -MD -MP -MF $(basename $@).d
+
+GCCFLAGS = -g -I . -B $(INSTALL_BASE) -specs bsp_specs -qrtems
+
+CFLAGS += $(DEPFLAGS) $(GCCFLAGS)
+
+# Files to be made into local symlinks
+YCORE_SYMLINKS = \
+       yaffs_ecc.c \
+       yaffs_endian.c \
+       yaffs_guts.c \
+       yaffs_packedtags1.c \
+       yaffs_tagscompat.c \
+       yaffs_packedtags2.c \
+       yaffs_nand.c \
+       yaffs_checkptrw.c \
+       yaffs_nameval.c \
+       yaffs_allocator.c \
+       yaffs_bitmap.c \
+       yaffs_yaffs1.c \
+       yaffs_yaffs2.c \
+       yaffs_verify.c \
+       yaffs_summary.c \
+       yaffs_tagsmarshall.c\
+       yaffs_ecc.h \
+       yaffs_guts.h \
+       yaffs_packedtags1.h \
+       yaffs_tagscompat.h \
+       yaffs_packedtags2.h \
+       yaffs_nand.h \
+       yaffs_checkptrw.h \
+       yaffs_nameval.h \
+       yaffs_attribs.h \
+       yaffs_allocator.h \
+       yaffs_bitmap.h \
+       yaffs_yaffs1.h \
+       yaffs_yaffs2.h \
+       yaffs_verify.h \
+       yaffs_summary.h \
+       yaffs_trace.h \
+       yaffs_endian.h \
+       yaffs_getblockinfo.h \
+       yaffs_tagsmarshall.h
+
+
+DIRECT_SYMLINKS = \
+       yaffs_attribs.c \
+       yaffs_hweight.c \
+       yaffs_hweight.h \
+       yportenv.h \
+       ydirectenv.h \
+       yaffscfg.h \
+       yaffs_osglue.h \
+       yaffs_list.h \
+       yaffsfs.h
+
+DIRECT_QSORT_SYMLINKS = \
+       qsort.c
+
+ALL_SYMLINKS = $(YCORE_SYMLINKS) $(DIRECT_SYMLINKS) $(DIRECT_QSORT_SYMLINKS)
+
+
+INCLUDES = rtems_yaffs.h \
+       yportenv.h \
+       ydirectenv.h \
+       yaffs_osglue.h \
+       yaffs_hweight.h \
+       yaffscfg.h \
+       yaffs_list.h \
+       yaffsfs.h \
+       yaffs_guts.h \
+       yaffs_packedtags2.h \
+       yaffs_ecc.h
+
+LIB = $(BUILDDIR)/libyaffs2.a
+LIB_PIECES = yaffs_ecc \
+       yaffs_endian \
+       yaffs_guts \
+       yaffs_packedtags1 \
+       yaffs_tagscompat \
+       yaffs_tagsmarshall\
+       yaffs_packedtags2 \
+       yaffs_nand \
+       yaffs_checkptrw \
+       qsort \
+       yaffs_nameval \
+       yaffs_attribs \
+       yaffs_allocator \
+       yaffs_bitmap \
+       yaffs_yaffs1 \
+       yaffs_yaffs2 \
+       yaffs_verify \
+       yaffs_summary \
+       yaffs_hweight \
+       rtems_yaffs \
+       rtems_yaffs_os_context \
+       rtems_yaffs_os_glue
+LIB_OBJS = $(LIB_PIECES:%=$(BUILDDIR)/%.o)
+LIB_DEPS = $(LIB_PIECES:%=$(BUILDDIR)/%.d)
+
+all: $(BUILDDIR) $(ALL_SYMLINKS) $(LIB)
+
+symlinks:$(ALL_SYMLINKS)
+
+$(YCORE_SYMLINKS): 
+       ln -s ../$@ $@
+
+$(DIRECT_SYMLINKS): 
+       ln -s ../direct/$@ $@
+
+$(DIRECT_QSORT_SYMLINKS): 
+       ln -s ../direct/optional_sort/$@ $@
+
+$(BUILDDIR):
+       mkdir $(BUILDDIR)
+
+$(LIB): $(LIB_OBJS)
+       $(AR) rcu $@ $^
+       $(RANLIB) $@
+
+$(BUILDDIR)/%.o: %.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+
+clean:
+       rm -rf $(BUILDDIR) $(ALL_SYMLINKS)
+
+install:  all
+       mkdir -p $(INSTALL_BASE)/include/yaffs
+       install -m 644 $(LIB) $(INSTALL_BASE)
+       install -m 644 $(INCLUDES) $(INSTALL_BASE)/include/yaffs
+
+.PHONY: clean install
+
+-include $(LIB_DEPS)
diff --git a/rtems/RTEMS_NOTES b/rtems/RTEMS_NOTES
new file mode 100644 (file)
index 0000000..0366b05
--- /dev/null
@@ -0,0 +1,136 @@
+
+
+Building with Makeifle
+
+Assuming the BSP is the sparc erc32 BSP in:
+/home/charles/quick-start/rtems/5/sparc-rtems5/erc32/
+
+
+
+Set up environment variuables
+
+export RTEMS_MAKEFILE_PATH=/home/charles/quick-start/rtems/5/sparc-rtems5/erc32/
+export PATH=/home/charles/quick-start/rtems/5/bin/:$PATH
+
+Now we can build and install the Yaffs library:
+
+$cd /opt/y/git/yaffs2/rtems
+
+$make -f Makefile.rtems clean
+$make -f Makefile.rtems all
+   output is build-erc32/libyaffs2.a
+$make -f Makefile.rtems install
+   mkdir -p /home/charles/quick-start/rtems/5/sparc-rtems5/erc32/lib/include/yaffs
+   install -m 644 build-erc32/libyaffs2.a /home/charles/quick-start/rtems/5/sparc-rtems5/erc32/lib
+   install -m 644 rtems_yaffs.h yportenv.h ydirectenv.h yaffs_osglue.h yaffs_hweight.h yaffscfg.h yaffs_list.h yaffsfs.h yaffs_guts.h yaffs_packedtags2.h yaffs_ecc.h /home/charles/quick-start/rtems/5/sparc-rtems5/erc32/lib/include/yaffs
+
+
+Building test application
+
+
+$ cd rtems-y-test/
+
+
+
+Running 
+
+$  sparc-rtems5-sis hello_world_c/o-optimize/hello.exe
+
+ SIS - SPARC/RISCV instruction simulator 2.20,  copyright Jiri Gaisler 2019
+ Bug-reports to jiri@gaisler.se
+
+ ERC32 emulation enabled
+
+ Loaded hello_world_c/o-optimize/hello.exe, entry 0x02000000
+sis> go
+resuming at 0x02000000
+
+
+*** HELLO WORLD TEST ***
+Hello World 123
+*** END OF HELLO WORLD TEST ***
+
+*** FATAL ***
+fatal source: 5 (RTEMS_FATAL_SOURCE_EXIT)
+fatal code: 0 (0x00000000)
+RTEMS version: 5.0.0.e22554535796fc29a7ed7c5e2338128e324a621d-modified
+RTEMS tools: 7.5.0 20191114 (RTEMS 5, RSB 5 (599c4d7c87fa), Newlib d14714c69)
+executing thread ID: 0x08a010001
+executing thread name: UI1 
+cpu 0 in error mode (tt = 0x101)
+   125619  0200c0e0:  91d02000   ta  0x0
+sis> 
+
+
+Running with GDB
+
+Open a second terminal to run sis in gdb mode
+
+$ sparc-rtems5-sis -gdb
+
+ SIS - SPARC/RISCV instruction simulator 2.20,  copyright Jiri Gaisler 2019
+ Bug-reports to jiri@gaisler.se
+
+ ERC32 emulation enabled
+
+gdb: listening on port 1234 connected
+X2000000,0:#40
+
+
+*** HELLO WORLD TEST ***
+Hello World 123
+*** END OF HELLO WORLD TEST ***
+
+*** FATAL ***
+fatal source: 5 (RTEMS_FATAL_SOURCE_EXIT)
+fatal code: 0 (0x00000000)
+RTEMS version: 5.0.0.e22554535796fc29a7ed7c5e2338128e324a621d-modified
+RTEMS tools: 7.5.0 20191114 (RTEMS 5, RSB 5 (599c4d7c87fa), Newlib d14714c69)
+executing thread ID: 0x08a010001
+executing thread name: UI1 
+
+
+
+
+
+
+From other window use gdb
+
+
+
+$ sparc-rtems5-gdb hello.exe
+GNU gdb (GDB) 8.3
+Copyright (C) 2019 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+Type "show copying" and "show warranty" for details.
+This GDB was configured as "--host=x86_64-linux-gnu --target=sparc-rtems5".
+Type "show configuration" for configuration details.
+For bug reporting instructions, please see:
+<http://www.gnu.org/software/gdb/bugs/>.
+Find the GDB manual and other documentation resources online at:
+    <http://www.gnu.org/software/gdb/documentation/>.
+
+For help, type "help".
+Type "apropos word" to search for commands related to "word"...
+Reading symbols from hello.exe...
+(gdb) target remote :1234
+:1234: Connection timed out.
+(gdb) target remote :1234
+Remote debugging using :1234
+0x00000000 in ?? ()
+(gdb) load
+Loading section .text, size 0x12880 lma 0x2000000
+Loading section .rtemsroset, size 0x40 lma 0x2012880
+Loading section .data, size 0x530 lma 0x20138c0
+Start address 0x2000000, load size 77296
+Transfer rate: 2434 KB/sec, 270 bytes/write.
+(gdb) c
+Continuing.
+
+Program received signal SIGTERM, Terminated.
+syscall () at /home/charles/quick-start/src/rtems/c/src/../../cpukit/score/cpu/sparc/syscall.S:44
+44             ta      0                       ! syscall 1, halt with %g1,%g2,%g3 info
+(gdb) 
+
diff --git a/rtems/rtems-y-test/basic-test/Makefile b/rtems/rtems-y-test/basic-test/Makefile
new file mode 100644 (file)
index 0000000..9576d69
--- /dev/null
@@ -0,0 +1,58 @@
+#
+#  Makefile for yaffs-rtems-test.exe
+#
+
+#
+#  RTEMS_MAKEFILE_PATH is typically set in an environment variable
+#
+
+EXEC=yaffs-rtems-test.exe
+PGM=${ARCH}/$(EXEC)
+
+# optional managers required
+MANAGERS=all
+
+# C source names
+CSRCS = yaffs-rtems-basic-test.c
+CSRCS += yaffs-rtems-test-wrapper.c yaffs-rtems-flashsim.c
+COBJS_ = $(CSRCS:.c=.o)
+COBJS = $(COBJS_:%=${ARCH}/%)
+
+# C++ source names
+CXXSRCS =
+CXXOBJS_ = $(CXXSRCS:.cc=.o)
+CXXOBJS = $(CXXOBJS_:%=${ARCH}/%)
+
+# AS source names
+ASSRCS =
+ASOBJS_ = $(ASSRCS:.s=.o)
+ASOBJS = $(ASOBJS_:%=${ARCH}/%)
+
+# Libraries
+#LIBS = -lrtemsall -lc  -lyaffs2
+LINK_LIBS = -lyaffs2
+
+include $(RTEMS_MAKEFILE_PATH)/Makefile.inc
+
+include $(RTEMS_CUSTOM)
+include $(PROJECT_ROOT)/make/leaf.cfg
+
+OBJS= $(COBJS) $(CXXOBJS) $(ASOBJS)
+
+all:    ${ARCH} $(PGM)
+
+#Create symlinks
+yaffs-rtems-test-wrapper.c: ../common/yaffs-rtems-test-wrapper.c
+       ln -sf $^ $@
+
+yaffs-rtems-flashsim.c: ../common/yaffs-rtems-flashsim.c
+       ln -sf $^ $@
+
+yaffs-rtems-flashsim.h: ../common/yaffs-rtems-flashsim.h
+       ln -sf $^ $@
+
+$(OBJS): yaffs-rtems-flashsim.h
+
+$(PGM): $(OBJS)
+       $(make-exe)
+
diff --git a/rtems/rtems-y-test/basic-test/README b/rtems/rtems-y-test/basic-test/README
new file mode 100644 (file)
index 0000000..83403b6
--- /dev/null
@@ -0,0 +1,4 @@
+The basic tests test common fs operations such as open, close, read, write etc.
+
+Multiple files and directories are opened, created, read, deleted, truncated etc.
+
diff --git a/rtems/rtems-y-test/basic-test/yaffs-rtems-basic-test.c b/rtems/rtems-y-test/basic-test/yaffs-rtems-basic-test.c
new file mode 100644 (file)
index 0000000..c526739
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ *  Simple test program -- demonstrating use of IMFS
+ */
+
+#include <bsp.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dirent.h>
+
+#include <rtems/libio.h>
+#include <yaffs/rtems_yaffs.h>
+
+void set_uint8_t_buffer(uint8_t *buf, uint32_t n, uint8_t start, uint8_t inc)
+{
+       while (n) {
+               *buf = start;
+               buf++;
+               start += inc;
+               n--;
+       }
+}
+
+void make_test_file_name(char *out, int out_size, char *root_path, char *dir, char *file, int index)
+{
+       if (index >= 0)
+               snprintf(out, out_size, "%s/%s/%s-%d",
+                                       root_path, dir, file, index);
+       else
+               snprintf(out, out_size, "%s/%s/%s",
+                                       root_path, dir, file);
+}
+
+void make_test_dir_name(char *out, int out_size, char *root_path, char *dir)
+{
+       snprintf(out, out_size, "%s/%s", root_path, dir);
+}
+
+void dump_directory_tree_worker(const char *dname,int recursive)
+{
+       DIR *d;
+       struct dirent *de;
+       struct stat s;
+       char str[1000];
+
+       d = opendir(dname);
+
+       if(!d) {
+               printf("opendir failed\n");
+       } else {
+               while((de = readdir(d)) != NULL) {
+                       sprintf(str,"%s/%s",dname,de->d_name);
+
+                       lstat(str,&s);
+
+                       printf("%s inode %d length %d mode 0%o ",
+                               str, (int)s.st_ino, (int)s.st_size, s.st_mode);
+                       switch(s.st_mode & S_IFMT) {
+                               case S_IFREG: printf("data file"); break;
+                               case S_IFDIR: printf("directory"); break;
+                               case S_IFLNK: printf("symlink -->");
+                                                         if(readlink(str,str,100) < 0)
+                                                               printf("no alias");
+                                                         else
+                                                               printf("\"%s\"",str);
+                                                         break;
+                               default: printf("unknown mode"); break;
+                       }
+
+                       printf("\n");
+
+                       if((s.st_mode & S_IFMT) == S_IFDIR && recursive)
+                               dump_directory_tree_worker(str,1);
+               }
+               closedir(d);
+       }
+}
+
+static void dump_directory_tree(const char *dname)
+{
+       printf("Directory tree of %s\n", dname);
+       dump_directory_tree_worker(dname,1);
+}
+
+
+void recursively_delete(char *objname)
+{
+       struct stat s;
+       DIR *d;
+       struct dirent *de;
+       char str[500];
+
+
+       //printf("deleting %s\n", objname);
+       lstat(objname, &s);
+
+       switch(s.st_mode & S_IFMT) {
+               case S_IFREG:
+                       printf("delete data file %s returns %d\n",
+                               objname, unlink(objname));
+               break;
+               case S_IFLNK:
+                       printf("delete symlink %s returns %d\n",
+                               objname, unlink(objname));
+               break;
+               case S_IFDIR:
+                       d = opendir(objname);
+                       if(!d) {
+                               printf("opendir failed\n");
+                       } else {
+                               while((de = readdir(d)) != NULL) {
+                                       snprintf(str, sizeof(str), "%s/%s",
+                                               objname, de->d_name);
+                                       recursively_delete(str);
+                               }
+                               closedir(d);
+                       }
+                       printf("delete directory %s returns %d\n",
+                               objname, rmdir(objname));
+               break;
+       }
+}
+
+
+
+void dumpDir(const char *dname)
+{
+       dump_directory_tree_worker(dname,0);
+}
+
+int basic_file_test(char *root_path, char *test_path)
+{
+       char fname[100];
+       char dname[100];
+       int fd;
+       int ret;
+       uint8_t buf[100];
+       uint8_t buf2[100];
+
+       make_test_dir_name(dname, sizeof(dname), root_path, test_path);
+       make_test_file_name(fname, sizeof(fname), root_path, test_path, "file", -1);
+
+       ret = mkdir(dname, 0777);
+
+       if (ret < 0) {
+               perror("mkdir");
+               return ret;
+       }
+
+       fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0777);
+       printf("open %s  = %d\n", fname, fd);
+       if (fd < 0) {
+               perror("opening test file");
+               return fd;
+       }
+
+       set_uint8_t_buffer(buf, sizeof(buf), 0xAA, 1);
+
+       ret = write(fd, buf, sizeof(buf));
+
+       printf("write returned %d\n", ret);
+
+       if (ret < 0) {
+               perror("writing file");
+               return ret;
+       }
+
+       ret = fdatasync(fd);
+
+       if (ret < 0) {
+               perror("fdatasync problem");
+               return ret;
+       }
+
+       ret = lseek(fd, 0, SEEK_END);
+
+       printf("lseek end ret = %d\n", ret);
+
+       ret = lseek(fd, 0, SEEK_SET);
+       printf("lseek start ret = %d\n", ret);
+
+       ret = read(fd, buf2, sizeof(buf2));
+
+       printf("reading file ret = %d\n", ret);
+
+       if (ret < 0) {
+               perror("reading file");
+               return ret;
+       }
+
+       dump_directory_tree(root_path);
+
+       if (memcmp(buf, buf2, sizeof(buf)) == 0) {
+               printf("buffers match\n");
+               return 0;
+       } else {
+               printf("buffers do not match\n");
+               return -1;
+       }
+
+       return ret;
+}
+
+
+int create_delete_files_pass(char *root_path, char *test_path, int n_files, int del_when_done)
+{
+       char fname[100];
+       char lname[100];
+       char dname[100];
+       int *fds = NULL;
+       int ret;
+       int i;
+       uint8_t buf[100];
+       uint8_t buf2[100];
+
+       fds = malloc(n_files * sizeof (int));
+
+       if (!fds) {
+               printf("Failed to malloc\n");
+               ret = -1;
+               goto err;
+       }
+
+       make_test_dir_name(dname, sizeof(dname), root_path, test_path);
+
+       recursively_delete(dname);
+
+       ret = access(dname, F_OK);
+       printf("access of non-existing expects -1 returned %d\n", ret);
+
+       if (ret != -1) {
+               printf("access should have been -1, was %d\n", ret);
+               ret = -1;
+               goto err;
+       }
+
+       ret = mkdir(dname, 0777);
+
+       if (ret < 0) {
+               perror("mkdir");
+               goto err;
+       }
+
+       ret = access(dname, F_OK);
+       printf("access of existing returned %d\n", ret);
+
+       if (ret < 0) {
+               perror("access of existing directory");
+               goto err;
+       }
+
+       for (i = 0; i < n_files; i++) {
+               int link_fd;
+
+               make_test_file_name(fname, sizeof(fname), root_path, test_path, "file-", i);
+               make_test_file_name(lname, sizeof(lname), root_path, test_path, "link-", i);
+
+               ret = symlink(fname, lname);
+
+               if (ret < 0) {
+                       perror("creating symlink");
+                       goto err;
+               }
+
+               fds[i] = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0777);
+               printf("open %s  = %d\n", fname, fds[i]);
+
+               if (fds[i] < 0) {
+                       perror("opening test file");
+                       ret = fds[i];
+                       goto err;
+               }
+
+
+               link_fd = open(lname, O_RDWR, 0777);
+               printf("opening link %s  = %d\n", lname, link_fd);
+
+               if (link_fd < 0) {
+                       perror("opening symlink file");
+                       ret = link_fd;
+                       goto err;
+               }
+               close(link_fd);
+
+       }
+
+       set_uint8_t_buffer(buf, sizeof(buf), 0xAA, 1);
+
+       for(i = 0; i < n_files; i++) {
+               ret = write(fds[i], buf, sizeof(buf));
+               printf("write returned %d\n", ret);
+               if (ret < 0) {
+                       perror("writing file");
+                       goto err;
+               }
+       }
+
+       for(i = 0; i < n_files; i++) {
+               int trunc_size = sizeof(buf2)/2;
+
+               ret = lseek(fds[i], 0, SEEK_END);
+
+               printf("lseek end ret = %d\n", ret);
+
+               ret = lseek(fds[i], 0, SEEK_SET);
+               printf("lseek start ret = %d\n", ret);
+
+               ret = read(fds[i], buf2, sizeof(buf2));
+
+               printf("reading file ret = %d\n", ret);
+               if (ret < 0) {
+                       perror("reading file");
+                       goto err;
+               }
+               ret = ftruncate(fds[i], trunc_size);
+
+               if (ret < 0) {
+                       perror("ftruncate");
+                       goto err;
+               }
+
+               ret = lseek(fds[i], 0, SEEK_END);
+               if (ret != trunc_size) {
+                       printf("truncated size is %d but lseek returned %d\n",
+                               trunc_size, ret);
+                       ret = -1;
+                       goto err;
+               }
+
+
+       }
+
+       for(i = 0; i < n_files; i++) {
+               ret = close(fds[i]);
+               if (ret < 0) {
+                       perror("closing file");
+                       goto err;
+               }
+       }
+
+       dump_directory_tree(root_path);
+
+       if (memcmp(buf, buf2, sizeof(buf)) == 0) {
+               printf("buffers match\n");
+               ret = 0;
+       } else {
+               printf("buffers do not match\n");
+               ret = -1;
+       }
+
+       if (del_when_done)
+               recursively_delete(dname);
+err:
+       free(fds);
+
+       return ret;
+}
+
+int create_delete_files(char *root_path, char *test_path, int n_files, int n_passes)
+{
+       int i;
+       int ret;
+       for (i = 0; i < n_passes; i++) {
+               printf("\nCreate and Delete Files Pass %d\n", i);
+               ret = create_delete_files_pass(root_path, test_path, n_files, 1);
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+#define YPATH "/yaffs_mount_pt"
+#define FNAME YPATH"/test"
+#define DIRNAME YPATH"/dirtest"
+
+void check_fail(int ret)
+{
+       if (ret < 0)
+               printf("Test failed\n");
+}
+
+void run_the_test(void)
+{
+       check_fail(basic_file_test(YPATH, "basic-test-dir"));
+       check_fail(create_delete_files(YPATH, "create-del-test-dir", 15, 50));
+
+       printf("\n\n\nAll Yaffs Tests passed Ok\n\n\n");
+}
diff --git a/rtems/rtems-y-test/common/yaffs-rtems-flashsim.c b/rtems/rtems-y-test/common/yaffs-rtems-flashsim.c
new file mode 100644 (file)
index 0000000..aafa5c0
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Modified verion of yramsim.c.
+ */
+#include "yaffs-rtems-flashsim.h"
+
+#include <string.h>
+#include "../../yaffs_guts.h"
+
+
+
+#define N_RAM_SIM_DEVS 1
+
+#define DATA_SIZE      2048
+#define SPARE_SIZE     64
+#define PAGE_SIZE      (DATA_SIZE + SPARE_SIZE)
+#define PAGES_PER_BLOCK        64
+
+
+#if 0
+#define dout printf
+#else
+#define dout(...) do { } while(0)
+#endif
+
+
+
+uint32_t stats_reads;
+uint32_t stats_writes;
+uint32_t stats_erases;
+
+typedef struct {
+       unsigned char page[PAGES_PER_BLOCK][PAGE_SIZE];
+       unsigned blockOk;
+} Block;
+
+typedef struct {
+       Block **blockList;
+       int nBlocks;
+} SimData;
+
+
+SimData *simDevs[N_RAM_SIM_DEVS];
+
+static SimData *DevToSim(struct yaffs_dev *dev)
+{
+       return (SimData*)(dev->driver_context);
+}
+
+
+static void CheckInitialised(void)
+{
+
+}
+
+static int yramsim_erase_internal(SimData *sim, unsigned blockId,int force)
+{
+       if(blockId < 0 || blockId >= sim->nBlocks){
+               return 0;
+       }
+
+       if(!sim->blockList[blockId]){
+               return 0;
+       }
+
+       if(!force && !sim->blockList[blockId]->blockOk){
+               return 0;
+       }
+
+       memset(sim->blockList[blockId],0xff,sizeof(Block));
+       sim->blockList[blockId]->blockOk = 1;
+
+       return 1;
+}
+
+
+
+
+static int yramsim_initialise(struct yaffs_dev *dev)
+{
+       SimData *sim = DevToSim(dev);
+       Block **blockList = sim->blockList;
+       return blockList != NULL;
+}
+
+
+static int yramsim_deinitialise(struct yaffs_dev *dev)
+{
+       return 1;
+}
+
+static int yramsim_rd_chunk (struct yaffs_dev *dev, int pageId,
+                                         u8 *data, int dataLength,
+                                         u8 *spare, int spareLength,
+                                         enum yaffs_ecc_result *ecc_result)
+{
+       SimData *sim = DevToSim(dev);
+       Block **blockList = sim->blockList;
+
+       unsigned blockId = pageId / PAGES_PER_BLOCK;
+       unsigned pageOffset = pageId % PAGES_PER_BLOCK;
+       unsigned char * d;
+       unsigned char *s;
+
+       stats_reads++;
+
+       if(blockId >= sim->nBlocks ||
+          pageOffset >= PAGES_PER_BLOCK ||
+          dataLength >DATA_SIZE ||
+          spareLength > SPARE_SIZE ||
+          !blockList[blockId]->blockOk){
+                  return YAFFS_FAIL;
+       }
+
+       d = blockList[blockId]->page[pageOffset];
+       s = d + DATA_SIZE;
+
+       if(data)
+               memcpy(data,d,dataLength);
+
+       if(spare)
+               memcpy(spare,s,spareLength);
+
+       if (ecc_result)
+               *ecc_result  = YAFFS_ECC_RESULT_NO_ERROR;
+
+       return YAFFS_OK;
+}
+
+static int yramsim_wr_chunk (struct yaffs_dev *dev, int pageId,
+                                          const u8 *data, int dataLength,
+                                          const u8 *spare, int spareLength)
+{
+       SimData *sim = DevToSim(dev);
+       Block **blockList = sim->blockList;
+
+       unsigned blockId = pageId / PAGES_PER_BLOCK;
+       unsigned pageOffset = pageId % PAGES_PER_BLOCK;
+       unsigned char * d;
+       unsigned char *s;
+
+       dout("wr_chunk\n");
+
+       stats_writes++;
+
+       if(blockId >= sim->nBlocks ||
+          pageOffset >= PAGES_PER_BLOCK ||
+          dataLength >DATA_SIZE ||
+          spareLength > SPARE_SIZE ||
+          !blockList[blockId]->blockOk){
+                  return YAFFS_FAIL;
+       }
+
+       d = blockList[blockId]->page[pageOffset];
+       s = d + DATA_SIZE;
+
+       if(data)
+               memcpy(d,data,dataLength);
+
+       if(spare)
+               memcpy(s,spare,spareLength);
+
+       return YAFFS_OK;
+}
+
+
+static int yramsim_erase(struct yaffs_dev *dev, int blockId)
+{
+       SimData *sim = DevToSim(dev);
+
+       stats_erases++;
+
+       CheckInitialised();
+       return yramsim_erase_internal(sim,blockId,0);
+}
+
+static int yramsim_check_block_bad(struct yaffs_dev *dev, int blockId)
+{
+       SimData *sim = DevToSim(dev);
+       Block **blockList = sim->blockList;
+       if(blockId >= sim->nBlocks){
+               return YAFFS_FAIL;
+       }
+
+       return blockList[blockId]->blockOk ? YAFFS_OK : YAFFS_FAIL;
+}
+
+static int yramsim_mark_block_bad(struct yaffs_dev *dev, int blockId)
+{
+       SimData *sim = DevToSim(dev);
+       Block **blockList = sim->blockList;
+       if(blockId >= sim->nBlocks){
+               return YAFFS_FAIL;
+       }
+
+       blockList[blockId]->blockOk = 0;
+
+       return YAFFS_OK;
+}
+
+
+static SimData *yramsim_alloc_sim_data(u32 devId, u32 nBlocks)
+{
+       int ok = 1;
+
+       Block **blockList;
+       SimData *sim;
+       Block *b;
+       u32 i;
+
+       if(devId >= N_RAM_SIM_DEVS)
+               return NULL;
+
+       sim = simDevs[devId];
+
+       if(sim)
+               return sim;
+
+       sim = malloc(sizeof (SimData));
+       if(!sim)
+               return NULL;
+
+       simDevs[devId] = sim;
+
+       blockList = malloc(nBlocks * sizeof(Block *));
+
+       sim->blockList = blockList;
+       sim->nBlocks = nBlocks;
+       if(!blockList){
+               free(sim);
+               return NULL;
+       }
+
+       for(i = 0; i < nBlocks; i++)
+               blockList[i] = NULL;
+
+       for(i = 0; i < nBlocks && ok; i++){
+               b=  malloc(sizeof(Block));
+               if(b){
+                       blockList[i] = b;
+                       yramsim_erase_internal(sim,i,1);
+               }
+               else
+                       ok = 0;
+       }
+
+       if(!ok){
+               for(i = 0; i < nBlocks; i++)
+                       if(blockList[i]){
+                               free(blockList[i]);
+                               blockList[i] = NULL;
+                       }
+               free(blockList);
+               blockList = NULL;
+               free(sim);
+               sim = NULL;
+       }
+
+       return sim;
+}
+
+
+struct yaffs_dev *yramsim_CreateRamSim(const YCHAR *name,
+                               u32 devId, u32 nBlocks,
+                               u32 start_block, u32 end_block)
+{
+       SimData *sim;
+       struct yaffs_dev *dev;
+       struct yaffs_param *p;
+       struct yaffs_driver *d;
+
+       sim = yramsim_alloc_sim_data(devId, nBlocks);
+
+       dev = malloc(sizeof(*dev));
+
+       if(!sim || !dev){
+               free(sim);
+               free(dev);
+               printf("Flash Sim creation failed. sim = %p, dev = %p\n",
+                               sim, dev);
+               return NULL;
+       }
+
+       memset(dev, 0, sizeof(*dev));
+
+       if(start_block >= sim->nBlocks)
+               start_block = 0;
+       if(end_block == 0 || end_block >= sim->nBlocks)
+               end_block = sim->nBlocks - 1;
+
+       p = &dev->param;
+       p->name = strdup(name);
+       p->start_block = start_block;
+       p->end_block = end_block;
+       p->total_bytes_per_chunk = DATA_SIZE;
+       p->spare_bytes_per_chunk= SPARE_SIZE;
+       p->chunks_per_block = PAGES_PER_BLOCK;
+       p->n_reserved_blocks = 2;
+       p->use_nand_ecc = 1;
+       p->inband_tags = 0;
+       p->is_yaffs2 = 1;
+
+       d= &dev->drv;
+       d->drv_initialise_fn = yramsim_initialise;
+       d->drv_deinitialise_fn = yramsim_deinitialise;
+       d->drv_read_chunk_fn = yramsim_rd_chunk;
+       d->drv_write_chunk_fn = yramsim_wr_chunk;
+       d->drv_erase_fn = yramsim_erase;
+       d->drv_check_bad_fn = yramsim_check_block_bad;
+       d->drv_mark_bad_fn = yramsim_mark_block_bad;
+
+       dev->driver_context= (void *)sim;
+
+       printf("Created simulated flash device %p\n", dev);
+       return dev;
+}
+
+struct yaffs_dev *yaffs_rtems_flashsim_setup(void)
+{
+       return yramsim_CreateRamSim("ramsim",
+                               0, 100,
+                               0, 99);
+}
+
+
+void yaffs_rtems_flashsim_dump_status(void)
+{
+       printf("\nFlashsim stats\n");
+       printf("reads.....%d\n", stats_reads);
+       printf("writes....%d\n", stats_writes);
+       printf("erases....%d\n", stats_erases);
+}
diff --git a/rtems/rtems-y-test/common/yaffs-rtems-flashsim.h b/rtems/rtems-y-test/common/yaffs-rtems-flashsim.h
new file mode 100644 (file)
index 0000000..50f259b
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __YAFFS_FLASH_SIM_H__
+#define __YAFFS_FLASH_SIM_H__
+
+struct yaffs_dev;
+
+struct yaffs_dev *yaffs_rtems_flashsim_setup(void);
+
+void yaffs_rtems_flashsim_dump_status(void);
+
+#endif
+
diff --git a/rtems/rtems-y-test/common/yaffs-rtems-test-wrapper.c b/rtems/rtems-y-test/common/yaffs-rtems-test-wrapper.c
new file mode 100644 (file)
index 0000000..4d23917
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ *  Simple test program -- demonstrating use of IMFS
+ */
+
+#include <bsp.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rtems/libio.h>
+#include <yaffs/rtems_yaffs.h>
+
+#include "yaffs-rtems-flashsim.h"
+
+#define YPATH "/yaffs_mount_pt"
+//#define YPATH ""
+
+void yaffs_bug_fn(const char *file_name, int line_no)
+{
+       printf("Yaffs bug at %s:%d\n", file_name, line_no);
+}
+
+int filesystem_init(const char *mount_target)
+{
+       struct yaffs_dev *device;
+       rtems_yaffs_default_os_context *os_context;
+       rtems_yaffs_mount_data mount_args;
+
+
+       rtems_filesystem_register(RTEMS_FILESYSTEM_TYPE_YAFFS,
+                                 rtems_yaffs_mount_handler);
+
+       // We mount the filesystem by passing an appropriate
+       // rtems_yaffs_mount_data as the last argument to mount(). mount_data is
+       // used to pass a yaffs_dev pointer by-value.
+
+
+       device = yaffs_rtems_flashsim_setup();
+
+       // Initialize callback storage for RTEMS's VFS inside the yaffs_dev.
+       os_context = malloc(sizeof(rtems_yaffs_default_os_context));
+       rtems_yaffs_initialize_default_os_context(os_context);
+
+       device->os_context = os_context;
+
+       mount_args.dev = device;
+
+       if (mount_and_make_target_path(NULL,
+                              mount_target,
+                              RTEMS_FILESYSTEM_TYPE_YAFFS,
+                              RTEMS_FILESYSTEM_READ_WRITE,
+                              &mount_args) < 0) {
+               perror("mount_and_make");
+               return errno;
+       } else {
+               chmod(mount_target, 0777); /* Make partition rw/modifiable */
+               return 0;
+       }
+}
+
+extern int run_the_test(void);
+
+rtems_task Init(
+  rtems_task_argument ignored
+)
+{
+       int err;
+
+        printf("Starting\n");
+
+       err = filesystem_init(YPATH);
+
+       printf("filesystem_init(\"%s\") returned %d\n", YPATH, err);
+
+       run_the_test();
+
+       yaffs_rtems_flashsim_dump_status();
+
+   exit(0);
+}
+
+
+
+
+#if 0
+So basically, we are registering our NAND-specific callbacks with YAFFS
+and registering the RTEMS-YAFFS filesystem callbacks with RTEMS.
+The rtems_filesystem_register() associates the mount() system call with
+a callback function to handle that system call, in this case
+rtems_yaffs_mount_handler().  rtems_yaffs_mount_handler() and
+RTEMS_FILESYSTEM_TYPE_YAFFS (just a string) are provided
+by the rtems-yaffs fork.
+
+mount_and_make_target_path() is provided by RTEMS: it combines a
+mkdir -p` with mount(), passing the mount_args to the
+previously-registered handler.
+#endif
+
+
+/* configuration information */
+
+/* NOTICE: the clock driver is explicitly disabled */
+#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+
+#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
+#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 32
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+#define CONFIGURE_MAXIMUM_TASKS 1
+
+#define CONFIGURE_MAXIMUM_SEMAPHORES        20
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
+/* end of file */
diff --git a/rtems/rtems-y-test/fsx/Makefile b/rtems/rtems-y-test/fsx/Makefile
new file mode 100644 (file)
index 0000000..0b35cf0
--- /dev/null
@@ -0,0 +1,58 @@
+#
+#  Makefile for rtems-fsx.exe
+#
+
+#
+#  RTEMS_MAKEFILE_PATH is typically set in an environment variable
+#
+
+EXEC=rtems-fsx.exe
+PGM=${ARCH}/$(EXEC)
+
+# optional managers required
+MANAGERS=all
+
+# C source names
+CSRCS = rtems-fsx.c
+CSRCS += yaffs-rtems-test-wrapper.c yaffs-rtems-flashsim.c
+COBJS_ = $(CSRCS:.c=.o)
+COBJS = $(COBJS_:%=${ARCH}/%)
+
+# C++ source names
+CXXSRCS =
+CXXOBJS_ = $(CXXSRCS:.cc=.o)
+CXXOBJS = $(CXXOBJS_:%=${ARCH}/%)
+
+# AS source names
+ASSRCS =
+ASOBJS_ = $(ASSRCS:.s=.o)
+ASOBJS = $(ASOBJS_:%=${ARCH}/%)
+
+# Libraries
+#LIBS = -lrtemsall -lc  -lyaffs2
+LINK_LIBS = -lyaffs2
+
+include $(RTEMS_MAKEFILE_PATH)/Makefile.inc
+
+include $(RTEMS_CUSTOM)
+include $(PROJECT_ROOT)/make/leaf.cfg
+
+OBJS= $(COBJS) $(CXXOBJS) $(ASOBJS)
+
+all:    ${ARCH} $(PGM)
+
+#Create symlinks
+yaffs-rtems-test-wrapper.c: ../common/yaffs-rtems-test-wrapper.c
+       ln -sf $^ $@
+
+yaffs-rtems-flashsim.c: ../common/yaffs-rtems-flashsim.c
+       ln -sf $^ $@
+
+yaffs-rtems-flashsim.h: ../common/yaffs-rtems-flashsim.h
+       ln -sf $^ $@
+
+$(OBJS): yaffs-rtems-flashsim.h
+
+$(PGM): $(OBJS)
+       $(make-exe)
+
diff --git a/rtems/rtems-y-test/fsx/README b/rtems/rtems-y-test/fsx/README
new file mode 100644 (file)
index 0000000..d0d8b42
--- /dev/null
@@ -0,0 +1,38 @@
+FSX is a file system exerciser originally written at Apple for 
+stress testing file system operations, particularly those related to 
+seeking, truncating etc.
+
+A good output looks like:
+
+Starting
+Created simulated flash device 0x2d24348
+yaffs: 0 blocks to be sorted...
+filesystem_init("/yaffs_mount_pt") returned 0
+mkdir returned 0
+fsx_init done
+truncating to largest ever: 0x1cbf7
+truncating to largest ever: 0x27453
+truncating to largest ever: 0x2d9bd
+truncating to largest ever: 0x36c22
+truncating to largest ever: 0x3e9f5
+truncating to largest ever: 0x3eff0
+truncating to largest ever: 0x3fd22
+truncating to largest ever: 0x3fe0b
+truncating to largest ever: 0x3fe29
+truncating to largest ever: 0x3ff4b
+All operations completed A-OK!
+fsx wanted to exit with 0
+
+Flashsim stats
+reads.....44373
+writes....46000
+erases....644
+
+
+
+To run for longer or shorter periods modify this line:
+      return fsx_main(FSX_TEST_DIR, 10000);
+
+
+This program has been run overnight (using a very large value) and no 
+problems were observed.
\ No newline at end of file
diff --git a/rtems/rtems-y-test/fsx/rtems-fsx.c b/rtems/rtems-y-test/fsx/rtems-fsx.c
new file mode 100644 (file)
index 0000000..54b0cee
--- /dev/null
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.2 (the
+ * "License").  You may not use this file except in compliance with the
+ * License.  Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ *     WARNING--WARNING--WARNING
+ *     This is not the original fsx.c. It has been modified to run with
+ *      yaffs direct. Seek out the original fsx.c if you want to do anything
+ *     else.
+ *
+ *
+ *
+ *     File:   fsx.c
+ *     Author: Avadis Tevanian, Jr.
+ *
+ *     File system exerciser.
+ *
+ *     Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com
+ *
+ *     Various features from Joe Sokol, Pat Dirks, and Clark Warner.
+ *
+ *     Small changes to work under Linux -- davej@suse.de
+ *
+ *     Sundry porting patches from Guy Harris 12/2001
+ *
+ *     Checks for mmap last-page zero fill.
+ *
+ *     Modified heavily by Charles Manning to exercise via the
+ *     yaffs direct interface.
+ *
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _UWIN
+# include <sys/param.h>
+# include <limits.h>
+# include <time.h>
+# include <strings.h>
+#endif
+#include <fcntl.h>
+#include <sys/mman.h>
+#ifndef MAP_FILE
+# define MAP_FILE 0
+#endif
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+
+
+#define NUMPRINTCOLUMNS 32     /* # columns of data to print on each line */
+
+/*
+ *     A log entry is an operation and a bunch of arguments.
+ */
+
+struct log_entry {
+       int     operation;
+       int     args[3];
+};
+
+#define        LOGSIZE 1000
+
+struct log_entry       oplog[LOGSIZE]; /* the log */
+int                    logptr = 0;     /* current position in log */
+int                    logcount = 0;   /* total ops */
+
+/*
+ *     Define operations
+ */
+
+#define        OP_READ         1
+#define OP_WRITE       2
+#define OP_TRUNCATE    3
+#define OP_CLOSEOPEN   4
+#define OP_MAPREAD     5
+#define OP_MAPWRITE    6
+#define OP_SKIPPED     7
+
+int page_size;
+int page_mask;
+
+char   *original_buf;                  /* a pointer to the original data */
+char   *good_buf;                      /* a pointer to the correct data */
+char   *temp_buf;                      /* a pointer to the current data */
+
+char   fname[200];                             /* name of our test file */
+char   mount_name[200];
+
+int    fd;                             /* fd for our test file */
+
+off_t          file_size = 0;
+off_t          biggest = 0;
+char           state[256];
+unsigned long  testcalls = 0;          /* calls to function "test" */
+
+unsigned long  simulatedopcount = 0;   /* -b flag */
+unsigned       closeprob = 0;          /* -c flag */
+int    debug = 0;                      /* -d flag */
+unsigned long  debugstart = 0;         /* -D flag */
+unsigned long  maxfilelen = 256 * 1024;        /* -l flag */
+int    sizechecks = 1;                 /* -n flag disables them */
+int    maxoplen = 64 * 1024;           /* -o flag */
+int    quiet = 0;                      /* -q flag */
+unsigned long progressinterval = 0;    /* -p flag */
+int    readbdy = 1;                    /* -r flag */
+int    style = 0;                      /* -s flag */
+int    truncbdy = 1;                   /* -t flag */
+int    writebdy = 1;                   /* -w flag */
+long   monitorstart = -1;              /* -m flag */
+long   monitorend = -1;                /* -m flag */
+int    lite = 0;                       /* -L flag */
+int    randomoplen = 1;                /* -O flag disables it */
+int    seed = 1;                       /* -S flag */
+
+int     mapped_writes = 0;           /* yaffs direct does not support mmapped files */
+int    mapped_reads = 0;
+
+int    fsxgoodfd = 0;
+FILE * fsxlogf = NULL;
+int badoff = -1;
+int closeopen = 0;
+
+
+
+void EXIT(int x)
+{
+       printf("fsx wanted to exit with %d\n",x);
+       while(x){}
+}
+
+char goodfile[1024];
+char logfile[1024];
+
+
+void
+vwarnc(code, fmt, ap)
+       int code;
+       const char *fmt;
+       va_list ap;
+{
+       fprintf(stderr, "fsx: ");
+       if (fmt != NULL) {
+               vfprintf(stderr, fmt, ap);
+               fprintf(stderr, ": ");
+       }
+       fprintf(stderr, "%s\n", strerror(code));
+}
+
+
+void
+warn(const char * fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       vwarnc(errno, fmt, ap);
+       va_end(ap);
+}
+
+
+void
+prt(char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vfprintf(stdout, fmt, args);
+       if (fsxlogf)
+               vfprintf(fsxlogf, fmt, args);
+       va_end(args);
+}
+
+void
+prterr(char *prefix)
+{
+       prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
+}
+
+
+void
+log4(int operation, int arg0, int arg1, int arg2)
+{
+       struct log_entry *le;
+
+       le = &oplog[logptr];
+       le->operation = operation;
+       if (closeopen)
+               le->operation = ~ le->operation;
+       le->args[0] = arg0;
+       le->args[1] = arg1;
+       le->args[2] = arg2;
+       logptr++;
+       logcount++;
+       if (logptr >= LOGSIZE)
+               logptr = 0;
+}
+
+
+void
+logdump(void)
+{
+       int     i, count, down;
+       struct log_entry        *lp;
+
+       prt("LOG DUMP (%d total operations):\n", logcount);
+       if (logcount < LOGSIZE) {
+               i = 0;
+               count = logcount;
+       } else {
+               i = logptr;
+               count = LOGSIZE;
+       }
+       for ( ; count > 0; count--) {
+               int opnum;
+
+               opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
+               prt("%d(%d mod 256): ", opnum, opnum%256);
+               lp = &oplog[i];
+               if ((closeopen = lp->operation < 0))
+                       lp->operation = ~ lp->operation;
+
+               switch (lp->operation) {
+               case OP_MAPREAD:
+                       prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)",
+                           lp->args[0], lp->args[0] + lp->args[1] - 1,
+                           lp->args[1]);
+                       if (badoff >= lp->args[0] && badoff <
+                                                    lp->args[0] + lp->args[1])
+                               prt("\t***RRRR***");
+                       break;
+               case OP_MAPWRITE:
+                       prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
+                           lp->args[0], lp->args[0] + lp->args[1] - 1,
+                           lp->args[1]);
+                       if (badoff >= lp->args[0] && badoff <
+                                                    lp->args[0] + lp->args[1])
+                               prt("\t******WWWW");
+                       break;
+               case OP_READ:
+                       prt("READ\t0x%x thru 0x%x\t(0x%x bytes)",
+                           lp->args[0], lp->args[0] + lp->args[1] - 1,
+                           lp->args[1]);
+                       if (badoff >= lp->args[0] &&
+                           badoff < lp->args[0] + lp->args[1])
+                               prt("\t***RRRR***");
+                       break;
+               case OP_WRITE:
+                       prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)",
+                           lp->args[0], lp->args[0] + lp->args[1] - 1,
+                           lp->args[1]);
+                       if (lp->args[0] > lp->args[2])
+                               prt(" HOLE");
+                       else if (lp->args[0] + lp->args[1] > lp->args[2])
+                               prt(" EXTEND");
+                       if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
+                           badoff < lp->args[0] + lp->args[1])
+                               prt("\t***WWWW");
+                       break;
+               case OP_TRUNCATE:
+                       down = lp->args[0] < lp->args[1];
+                       prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
+                           down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
+                       if (badoff >= lp->args[!down] &&
+                           badoff < lp->args[!!down])
+                               prt("\t******WWWW");
+                       break;
+               case OP_SKIPPED:
+                       prt("SKIPPED (no operation)");
+                       break;
+               default:
+                       prt("BOGUS LOG ENTRY (operation code = %d)!",
+                           lp->operation);
+               }
+               if (closeopen)
+                       prt("\n\t\tCLOSE/OPEN");
+               prt("\n");
+               i++;
+               if (i == LOGSIZE)
+                       i = 0;
+       }
+}
+
+
+void
+save_buffer(char *buffer, off_t bufferlength, int fd)
+{
+       off_t ret;
+       ssize_t byteswritten;
+
+       if (fd <= 0 || bufferlength == 0)
+               return;
+
+       if (bufferlength > SSIZE_MAX) {
+               prt("fsx flaw: overflow in save_buffer\n");
+               EXIT(67);
+       }
+       if (lite) {
+               off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
+               if (size_by_seek == (off_t)-1)
+                       prterr("save_buffer: lseek eof");
+               else if (bufferlength > size_by_seek) {
+                       warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek,
+                            (unsigned long long)bufferlength);
+                       bufferlength = size_by_seek;
+               }
+       }
+
+       ret = lseek(fd, (off_t)0, SEEK_SET);
+       if (ret == (off_t)-1)
+               prterr("save_buffer: lseek 0");
+
+       byteswritten = write(fd, buffer, (size_t)bufferlength);
+       if (byteswritten != bufferlength) {
+               if (byteswritten == -1)
+                       prterr("save_buffer write");
+               else
+                       warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n",
+                            (unsigned)byteswritten,
+                            (unsigned long long)bufferlength);
+       }
+}
+
+
+void
+report_failure(int status)
+{
+       logdump();
+
+       if (fsxgoodfd) {
+               if (good_buf) {
+                       save_buffer(good_buf, file_size, fsxgoodfd);
+                       prt("Correct content saved for comparison\n");
+                       prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n",
+                           fname, fname);
+               }
+               close(fsxgoodfd);
+       }
+       prt("Exiting with %d\n",status);
+       EXIT(status);
+}
+
+
+#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
+                                       *(((unsigned char *)(cp)) + 1)))
+
+void
+check_buffers(unsigned offset, unsigned size)
+{
+       unsigned char c, t;
+       unsigned i = 0;
+       unsigned n = 0;
+       unsigned op = 0;
+       unsigned bad = 0;
+
+       if (memcmp(good_buf + offset, temp_buf, size) != 0) {
+               prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
+                   offset, size);
+               prt("OFFSET\tGOOD\tBAD\tRANGE\n");
+               while (size > 0) {
+                       c = good_buf[offset];
+                       t = temp_buf[i];
+                       if (c != t) {
+                               if (n == 0) {
+                                       bad = short_at(&temp_buf[i]);
+                                       prt("0x%5x\t0x%04x\t0x%04x", offset,
+                                           short_at(&good_buf[offset]), bad);
+                                       op = temp_buf[offset & 1 ? i+1 : i];
+                               }
+                               n++;
+                               badoff = offset;
+                       }
+                       offset++;
+                       i++;
+                       size--;
+               }
+               if (n) {
+                       prt("\t0x%5x\n", n);
+                       if (bad)
+                               prt("operation# (mod 256) for the bad data may be %u\n", ((unsigned)op & 0xff));
+                       else
+                               prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n");
+               } else
+                       prt("????????????????\n");
+               report_failure(110);
+       }
+}
+
+
+void
+check_size(void)
+{
+       struct stat     statbuf;
+       off_t   size_by_seek;
+
+       if (fstat(fd, &statbuf)) {
+               prterr("check_size: fstat");
+               statbuf.st_size = -1;
+       }
+       size_by_seek = lseek(fd, (off_t)0, SEEK_END);
+       if (file_size != statbuf.st_size ||
+          file_size != size_by_seek) {
+               prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
+                   (unsigned long long)file_size,
+                   (unsigned long long)statbuf.st_size,
+                   (unsigned long long)size_by_seek);
+               report_failure(120);
+       }
+}
+
+
+void
+check_trunc_hack(void)
+{
+       struct stat statbuf;
+
+       ftruncate(fd, (off_t)0);
+       ftruncate(fd, (off_t)100000);
+       fstat(fd, &statbuf);
+       if (statbuf.st_size != (off_t)100000) {
+               prt("no extend on truncate! not posix!\n");
+               EXIT(130);
+       }
+       ftruncate(fd, (off_t)0);
+}
+
+
+void
+doread(unsigned offset, unsigned size)
+{
+       off_t ret;
+       unsigned iret;
+
+       offset -= offset % readbdy;
+       if (size == 0) {
+               if (!quiet && testcalls > simulatedopcount)
+                       prt("skipping zero size read\n");
+               log4(OP_SKIPPED, OP_READ, offset, size);
+               return;
+       }
+       if ((off_t)(size + offset) > file_size) {
+               if (!quiet && testcalls > simulatedopcount)
+                       prt("skipping seek/read past end of file\n");
+               log4(OP_SKIPPED, OP_READ, offset, size);
+               return;
+       }
+
+       log4(OP_READ, offset, size, 0);
+
+       if (testcalls <= simulatedopcount)
+               return;
+
+       if (!quiet && ((progressinterval &&
+                       testcalls % progressinterval == 0) ||
+                      (debug &&
+                       (monitorstart == -1 ||
+                        (offset + size > monitorstart &&
+                         (monitorend == -1 || offset <= monitorend))))))
+               prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
+                   offset, offset + size - 1, size);
+       ret = lseek(fd, (off_t)offset, SEEK_SET);
+       if (ret == (off_t)-1) {
+               prterr("doread: lseek");
+               report_failure(140);
+       }
+       iret = read(fd, temp_buf, size);
+       if (iret != size) {
+               if (iret == (unsigned)(-1))
+                       prterr("doread: read");
+               else
+                       prt("short read: 0x%x bytes instead of 0x%x\n",
+                           iret, size);
+               report_failure(141);
+       }
+       check_buffers(offset, size);
+}
+
+
+
+
+
+void
+gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
+{
+       while (size--) {
+               good_buf[offset] = testcalls % 256;
+               if (offset % 2)
+                       good_buf[offset] += original_buf[offset];
+               offset++;
+       }
+}
+
+
+void
+dowrite(unsigned offset, unsigned size)
+{
+       off_t ret;
+       unsigned iret;
+
+       offset -= offset % writebdy;
+       if (size == 0) {
+               if (!quiet && testcalls > simulatedopcount)
+                       prt("skipping zero size write\n");
+               log4(OP_SKIPPED, OP_WRITE, offset, size);
+               return;
+       }
+
+       log4(OP_WRITE, offset, size, file_size);
+
+       gendata(original_buf, good_buf, offset, size);
+       if (file_size < offset + size) {
+               if (file_size < offset)
+                       memset(good_buf + file_size, '\0', offset - file_size);
+               file_size = offset + size;
+               if (lite) {
+                       warn("Lite file size bug in fsx!");
+                       report_failure(149);
+               }
+       }
+
+       if (testcalls <= simulatedopcount)
+               return;
+
+       if (!quiet && ((progressinterval &&
+                       testcalls % progressinterval == 0) ||
+                      (debug &&
+                       (monitorstart == -1 ||
+                        (offset + size > monitorstart &&
+                         (monitorend == -1 || offset <= monitorend))))))
+               prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
+                   offset, offset + size - 1, size);
+       ret = lseek(fd, (off_t)offset, SEEK_SET);
+       if (ret == (off_t)-1) {
+               prterr("dowrite: lseek");
+               report_failure(150);
+       }
+       iret = write(fd, good_buf + offset, size);
+       if (iret != size) {
+               if (iret == (unsigned)(-1))
+                       prterr("dowrite: write");
+               else
+                       prt("short write: 0x%x bytes instead of 0x%x\n",
+                           iret, size);
+               report_failure(151);
+       }
+}
+
+
+
+void
+dotruncate(unsigned size)
+{
+       int oldsize = file_size;
+
+       size -= size % truncbdy;
+       if (size > biggest) {
+               biggest = size;
+               if (!quiet && testcalls > simulatedopcount)
+                       prt("truncating to largest ever: 0x%x\n", size);
+       }
+
+       log4(OP_TRUNCATE, size, (unsigned)file_size, 0);
+
+       if (size > file_size)
+               memset(good_buf + file_size, '\0', size - file_size);
+       file_size = size;
+
+       if (testcalls <= simulatedopcount)
+               return;
+
+       if ((progressinterval && testcalls % progressinterval == 0) ||
+           (debug && (monitorstart == -1 || monitorend == -1 ||
+                      size <= monitorend)))
+               prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
+       if (ftruncate(fd, (off_t)size) == -1) {
+               prt("ftruncate1: %x\n", size);
+               prterr("dotruncate: ftruncate");
+               report_failure(160);
+       }
+}
+
+
+void
+writefileimage()
+{
+       ssize_t iret;
+
+       if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
+               prterr("writefileimage: lseek");
+               report_failure(171);
+       }
+       iret = write(fd, good_buf, file_size);
+       if ((off_t)iret != file_size) {
+               if (iret == -1)
+                       prterr("writefileimage: write");
+               else
+                       prt("short write: 0x%x bytes instead of 0x%llx\n",
+                           iret, (unsigned long long)file_size);
+               report_failure(172);
+       }
+       if (lite ? 0 : ftruncate(fd, file_size) == -1) {
+               prt("ftruncate2: %llx\n", (unsigned long long)file_size);
+               prterr("writefileimage: ftruncate");
+               report_failure(173);
+       }
+}
+
+
+void
+docloseopen(void)
+{
+       if (testcalls <= simulatedopcount)
+               return;
+
+       if (debug)
+               prt("%lu close/open\n", testcalls);
+       if (close(fd)) {
+               prterr("docloseopen: close");
+               report_failure(180);
+       }
+       fd = open(fname, O_RDWR, 0);
+       if (fd < 0) {
+               prterr("docloseopen: open");
+               report_failure(181);
+       }
+}
+
+
+void
+fsx_do_op(void)
+{
+       unsigned long   offset;
+       unsigned long   size = maxoplen;
+       unsigned long   rv = random();
+       unsigned long   op = rv % (3 + !lite + mapped_writes);
+
+       /* turn off the map read if necessary */
+
+       if (op == 2 && !mapped_reads)
+           op = 0;
+
+       if (simulatedopcount > 0 && testcalls == simulatedopcount)
+               writefileimage();
+
+       testcalls++;
+
+       if (closeprob)
+               closeopen = (rv >> 3) < (1U << 28) / closeprob;
+
+       if (debugstart > 0 && testcalls >= debugstart)
+               debug = 1;
+
+       if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
+               prt("%lu...\n", testcalls);
+
+       /*
+        * READ:        op = 0
+        * WRITE:       op = 1
+        * MAPREAD:     op = 2
+        * TRUNCATE:    op = 3
+        * MAPWRITE:    op = 3 or 4
+        */
+       if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */
+               dotruncate(random() % maxfilelen);
+       else {
+               if (randomoplen)
+                       size = random() % (maxoplen+1);
+               if (lite ? 0 : op == 3)
+                       dotruncate(size);
+               else {
+                       offset = random();
+                       if (op == 1 || op == (lite ? 3 : 4)) {
+                               offset %= maxfilelen;
+                               if (offset + size > maxfilelen)
+                                       size = maxfilelen - offset;
+                               dowrite(offset, size);
+                       } else {
+                               if (file_size)
+                                       offset %= file_size;
+                               else
+                                       offset = 0;
+                               if ((ssize_t)(offset + size) > file_size)
+                                       size = file_size - offset;
+                               doread(offset, size);
+                       }
+               }
+       }
+       if (sizechecks && testcalls > simulatedopcount)
+               check_size();
+       if (closeopen)
+               docloseopen();
+}
+
+
+void
+cleanup(sig)
+       int     sig;
+{
+       if (sig)
+               prt("signal %d\n", sig);
+       prt("testcalls = %lu\n", testcalls);
+       EXIT(sig);
+}
+
+
+void
+usage(void)
+{
+       fprintf(stdout, "usage: %s",
+               "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
+       -b opnum: beginning operation number (default 1)\n\
+       -c P: 1 in P chance of file close+open at each op (default infinity)\n\
+       -d: debug output for all operations\n\
+       -l flen: the upper bound on file size (default 262144)\n\
+       -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
+       -n: no verifications of file size\n\
+       -o oplen: the upper bound on operation size (default 65536)\n\
+       -p progressinterval: debug output at specified operation interval\n\
+       -q: quieter operation\n\
+       -r readbdy: 4096 would make reads page aligned (default 1)\n\
+       -s style: 1 gives smaller truncates (default 0)\n\
+       -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
+       -w writebdy: 4096 would make writes page aligned (default 1)\n\
+       -D startingop: debug output starting at specified operation\n\
+       -L: fsxLite - no file creations & no file size changes\n\
+       -N numops: total # operations to do (default infinity)\n\
+       -O: use oplen (see -o flag) for every op (default random)\n\
+       -P dirpath: save .fsxlog and .fsxgood files in dirpath (default ./)\n\
+       -S seed: for random # generator (default 1) 0 gets timestamp\n\
+       fname: this filename is REQUIRED (no default)\n");
+       EXIT(90);
+}
+
+
+int
+getnum(char *s, char **e)
+{
+       int ret = -1;
+
+       *e = (char *) 0;
+       ret = strtol(s, e, 0);
+       if (*e)
+               switch (**e) {
+               case 'b':
+               case 'B':
+                       ret *= 512;
+                       *e = *e + 1;
+                       break;
+               case 'k':
+               case 'K':
+                       ret *= 1024;
+                       *e = *e + 1;
+                       break;
+               case 'm':
+               case 'M':
+                       ret *= 1024*1024;
+                       *e = *e + 1;
+                       break;
+               case 'w':
+               case 'W':
+                       ret *= 4;
+                       *e = *e + 1;
+                       break;
+               }
+       return (ret);
+}
+
+
+
+extern int random_seed;
+extern int simulate_power_failure;
+
+
+int mounted_by_fsx = 0;
+
+
+
+int
+fsx_init(const char *mount_pt)
+{
+       unsigned        i;
+
+       goodfile[0] = 0;
+       logfile[0] = 0;
+
+       page_size = getpagesize();
+       page_mask = page_size - 1;
+
+       setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
+
+       strcpy(mount_name,mount_pt);
+       strcpy(fname,mount_name);
+       strcat(fname,"/fsxdata");
+
+#if 0
+       signal(SIGHUP,  cleanup);
+       signal(SIGINT,  cleanup);
+       signal(SIGPIPE, cleanup);
+       signal(SIGALRM, cleanup);
+       signal(SIGTERM, cleanup);
+       signal(SIGXCPU, cleanup);
+       signal(SIGXFSZ, cleanup);
+       signal(SIGVTALRM, cleanup);
+       signal(SIGUSR1, cleanup);
+       signal(SIGUSR2, cleanup);
+#endif
+
+#if 0
+       initstate(seed, state, 256);
+       setstate(state);
+#endif
+
+       fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666);
+       if (fd < 0) {
+               prterr(fname);
+               EXIT(91);
+       }
+       strncat(goodfile, fname, 256);
+       strcat (goodfile, ".fsxgood");
+       fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
+       if (fsxgoodfd < 0) {
+               prterr(goodfile);
+               EXIT(92);
+       }
+       strncat(logfile, "fsx", 256);
+       strcat (logfile, ".fsxlog");
+       fsxlogf = fopen(logfile, "w");
+       if (fsxlogf == NULL) {
+               prterr(logfile);
+               EXIT(93);
+       }
+       if (lite) {
+               off_t ret;
+               file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
+               if (file_size == (off_t)-1) {
+                       prterr(fname);
+                       warn("main: lseek eof");
+                       EXIT(94);
+               }
+               ret = lseek(fd, (off_t)0, SEEK_SET);
+               if (ret == (off_t)-1) {
+                       prterr(fname);
+                       warn("main: lseek 0");
+                       EXIT(95);
+               }
+       }
+       original_buf = (char *) malloc(maxfilelen);
+       for (i = 0; i < maxfilelen; i++)
+               original_buf[i] = random() % 256;
+       good_buf = (char *) malloc(maxfilelen);
+       memset(good_buf, '\0', maxfilelen);
+       temp_buf = (char *) malloc(maxoplen);
+       memset(temp_buf, '\0', maxoplen);
+       if (lite) {     /* zero entire existing file */
+               ssize_t written;
+
+               written = write(fd, good_buf, (size_t)maxfilelen);
+               if (written != (ssize_t)maxfilelen) {
+                       if (written == -1) {
+                               prterr(fname);
+                               warn("main: error on write");
+                       } else
+                               warn("main: short write, 0x%x bytes instead of 0x%x\n",
+                                    (unsigned)written, maxfilelen);
+                       EXIT(98);
+               }
+       } else
+               check_trunc_hack();
+
+       printf("fsx_init done\n");
+
+       return 0;
+}
+
+
+int fsx_complete(void)
+{
+       if (close(fd)) {
+               prterr("close");
+               report_failure(99);
+       }
+
+       close(fsxgoodfd);
+
+       prt("All operations completed A-OK!\n");
+
+       EXIT(0);
+       return 0;
+}
+
+int fsx_main(const char *mount_pt, int numops)
+{
+       fsx_init(mount_pt);
+       while (numops == -1 || numops--)
+               fsx_do_op();
+       fsx_complete();
+
+       return 0;
+}
+
+
+#define YPATH "/yaffs_mount_pt"
+
+#define FSX_TEST_DIR YPATH"/fsx_mount"
+
+int run_the_test(void)
+{
+       int ret;
+       ret = mkdir(FSX_TEST_DIR, 0777);
+       printf("mkdir returned %d\n", ret);
+
+       if (ret < 0)
+               perror("mkdir");
+
+       return fsx_main(FSX_TEST_DIR, 10000);
+}
diff --git a/rtems/rtems_yaffs.c b/rtems/rtems_yaffs.c
new file mode 100644 (file)
index 0000000..4611320
--- /dev/null
@@ -0,0 +1,803 @@
+/*
+ * YAFFS port to RTEMS
+ *
+ * Copyright (C) 2010, 2011 Sebastien Bourdeauducq
+ * Copyright (C) 2011 Stephan Hoffmann <sho@reLinux.de>
+ * Copyright (C) 2011-2012 embedded brains GmbH <rtems@embedded-brains.de>
+ * Copyright (C) 2019 Space Sciences and Engineering, LLC
+ *     <jbrandmeyer@planetiq.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * As a special exception, linking other files with the object code from
+ * this one to produce an executable application does not by itself cause
+ * the resulting executable application to be covered by the GNU General
+ * Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU Public License. In particular,
+ * the other YAFFS files are not covered by this exception, and using them
+ * in a proprietary application requires a paid license from Aleph One.
+ */
+
+#include <rtems.h>
+#include <rtems/libio_.h>
+#include <rtems/seterr.h>
+#include <rtems/userenv.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <dirent.h>
+
+#include "yportenv.h"
+
+#include "yaffs_guts.h"
+#include "yaffs_trace.h"
+#include "yaffs_packedtags2.h"
+
+#include "rtems_yaffs.h"
+
+/* RTEMS interface */
+
+static const rtems_filesystem_file_handlers_r yaffs_directory_handlers;
+static const rtems_filesystem_file_handlers_r yaffs_file_handlers;
+static const rtems_filesystem_file_handlers_r yaffs_link_handlers;
+static const rtems_filesystem_operations_table yaffs_ops;
+
+/* locking */
+
+static void ylock(struct yaffs_dev *dev)
+{
+       rtems_yaffs_os_context *os_context = dev->os_context;
+       (*os_context->lock)(dev, os_context);
+}
+
+static void yunlock(struct yaffs_dev *dev)
+{
+       rtems_yaffs_os_context *os_context = dev->os_context;
+       (*os_context->unlock)(dev, os_context);
+}
+
+static void rtems_yaffs_os_unmount(struct yaffs_dev *dev)
+{
+       rtems_yaffs_os_context *os_context = dev->os_context;
+       (*os_context->unmount)(dev, os_context);
+}
+
+static struct yaffs_obj *ryfs_get_object_by_location(
+       const rtems_filesystem_location_info_t *loc
+)
+{
+       return loc->node_access;
+}
+
+static struct yaffs_obj *ryfs_get_object_by_iop(
+       const rtems_libio_t *iop
+)
+{
+       return iop->pathinfo.node_access;
+}
+
+static struct yaffs_dev *ryfs_get_device_by_mt_entry(
+       const rtems_filesystem_mount_table_entry_t *mt_entry
+)
+{
+       return mt_entry->fs_info;
+}
+
+static void ryfs_set_location(rtems_filesystem_location_info_t *loc, struct yaffs_obj *obj)
+{
+       loc->node_access = obj;
+
+       switch (obj->variant_type) {
+               case YAFFS_OBJECT_TYPE_FILE:
+                       loc->handlers = &yaffs_file_handlers;
+                       break;
+               case YAFFS_OBJECT_TYPE_DIRECTORY:
+                       loc->handlers = &yaffs_directory_handlers;
+                       break;
+               case YAFFS_OBJECT_TYPE_SYMLINK:
+                       loc->handlers = &yaffs_link_handlers;
+                       break;
+               default:
+                       loc->handlers = &rtems_filesystem_handlers_default;
+                       break;
+       };
+}
+
+static bool ryfs_eval_is_directory(
+       rtems_filesystem_eval_path_context_t *ctx,
+       void *arg
+)
+{
+       rtems_filesystem_location_info_t *currentloc =
+               rtems_filesystem_eval_path_get_currentloc(ctx);
+       struct yaffs_obj *obj = ryfs_get_object_by_location(currentloc);
+
+       obj = yaffs_get_equivalent_obj(obj);
+
+       return obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY;
+}
+
+static const char *ryfs_make_string(char *buf, const char *src, size_t len)
+{
+       buf [len] = '\0';
+
+       return memcpy(buf, src, len);
+}
+
+static struct yaffs_obj *ryfs_search_in_directory(
+       struct yaffs_obj *dir,
+       const char *token,
+       size_t tokenlen
+)
+{
+       if (rtems_filesystem_is_parent_directory(token, tokenlen)) {
+               dir = dir->parent;
+       } else if (!rtems_filesystem_is_current_directory(token, tokenlen)) {
+               if (tokenlen < YAFFS_MAX_NAME_LENGTH) {
+                       char buf [YAFFS_MAX_NAME_LENGTH + 1];
+
+                       dir = yaffs_find_by_name(
+                               dir,
+                               ryfs_make_string(buf, token, tokenlen)
+                       );
+               } else {
+                       dir = NULL;
+               }
+       }
+
+       return dir;
+}
+
+static rtems_filesystem_eval_path_generic_status ryfs_eval_token(
+       rtems_filesystem_eval_path_context_t *ctx,
+       void *arg,
+       const char *token,
+       size_t tokenlen
+)
+{
+       rtems_filesystem_eval_path_generic_status status =
+               RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
+       rtems_filesystem_location_info_t *currentloc =
+               rtems_filesystem_eval_path_get_currentloc(ctx);
+       struct yaffs_obj *dir = ryfs_get_object_by_location(currentloc);
+       bool access_ok = rtems_filesystem_eval_path_check_access(
+               ctx,
+               RTEMS_FS_PERMS_EXEC,
+               dir->yst_mode,
+               (uid_t) dir->yst_uid,
+               (gid_t) dir->yst_gid
+       );
+
+       if (access_ok) {
+               struct yaffs_obj *entry = ryfs_search_in_directory(dir, token, tokenlen);
+
+               if (entry != NULL) {
+                       bool terminal = !rtems_filesystem_eval_path_has_path(ctx);
+                       int eval_flags = rtems_filesystem_eval_path_get_flags(ctx);
+                       bool follow_hard_link = (eval_flags & RTEMS_FS_FOLLOW_HARD_LINK) != 0;
+                       bool follow_sym_link = (eval_flags & RTEMS_FS_FOLLOW_SYM_LINK) != 0;
+                       enum yaffs_obj_type type = entry->variant_type;
+
+                       rtems_filesystem_eval_path_clear_token(ctx);
+
+                       if (type == YAFFS_OBJECT_TYPE_HARDLINK && (follow_hard_link || !terminal)) {
+                               entry = yaffs_get_equivalent_obj(entry);
+                       }
+
+                       if (type == YAFFS_OBJECT_TYPE_SYMLINK && (follow_sym_link || !terminal)) {
+                               const char *target = entry->variant.symlink_variant.alias;
+
+                               rtems_filesystem_eval_path_recursive(ctx, target, strlen(target));
+                       } else {
+                               ryfs_set_location(currentloc, entry);
+
+                               if (!terminal) {
+                                       status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
+                               }
+                       }
+               } else {
+                       status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY;
+               }
+       }
+
+       return status;
+}
+
+static const rtems_filesystem_eval_path_generic_config ryfs_eval_config = {
+       .is_directory = ryfs_eval_is_directory,
+       .eval_token = ryfs_eval_token
+};
+
+static void ryfs_eval_path(rtems_filesystem_eval_path_context_t *ctx)
+{
+       rtems_filesystem_eval_path_generic(ctx, NULL, &ryfs_eval_config);
+}
+
+/* Helper functions */
+
+static int ryfs_mknod(
+       const rtems_filesystem_location_info_t *parentloc,
+       const char *name,
+       size_t namelen,
+       mode_t mode,
+       dev_t dev
+)
+{
+       int rv = 0;
+       struct yaffs_obj *parent = ryfs_get_object_by_location(parentloc);
+       struct yaffs_obj *(*create)(
+               struct yaffs_obj *parent,
+               const YCHAR *name,
+               u32 mode,
+               u32 uid,
+               u32 gid
+       );
+
+       switch (mode & S_IFMT) {
+               case S_IFREG:
+                       create = yaffs_create_file;
+                       break;
+               case S_IFDIR:
+                       create = yaffs_create_dir;
+                       break;
+               default:
+                       errno = EINVAL;
+                       rv = -1;
+                       break;
+       }
+
+       if (rv == 0) {
+               char buf [YAFFS_MAX_NAME_LENGTH + 1];
+               struct yaffs_obj *entry = (*create)(
+                       parent,
+                       ryfs_make_string(buf, name, namelen),
+                       mode,
+                       geteuid(),
+                       getegid()
+               );
+
+               if (entry == NULL) {
+                       errno = ENOSPC;
+                       rv = -1;
+               }
+       }
+
+       return rv;
+}
+
+static int ryfs_utime(
+       const rtems_filesystem_location_info_t *loc,
+       time_t actime,
+       time_t modtime
+)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
+
+       obj = yaffs_get_equivalent_obj(obj);
+       if (obj != NULL) {
+               obj->dirty = 1;
+               obj->yst_atime = (u32) actime;
+               obj->yst_mtime = (u32) modtime;
+               obj->yst_ctime = (u32) time(NULL);
+       } else {
+               errno = EIO;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+static int ryfs_rename(
+       const rtems_filesystem_location_info_t *old_parent_loc,
+       const rtems_filesystem_location_info_t *old_loc,
+       const rtems_filesystem_location_info_t *new_parent_loc,
+       const char *name,
+       size_t namelen
+)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_location(old_loc);
+       char old_name_buf [YAFFS_MAX_NAME_LENGTH + 1];
+       char new_name_buf [YAFFS_MAX_NAME_LENGTH + 1];
+       int yc;
+
+       yaffs_get_obj_name(obj, old_name_buf, sizeof(old_name_buf));
+       yc = yaffs_rename_obj(
+               obj->parent,
+               old_name_buf,
+               ryfs_get_object_by_location(new_parent_loc),
+               ryfs_make_string(new_name_buf, name, namelen)
+       );
+       if (yc != YAFFS_OK) {
+               errno = EIO;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+static ssize_t ryfs_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
+{
+       struct yaffs_obj *obj;
+       struct yaffs_dev *dev;
+       struct dirent *de = (struct dirent *)buffer;
+       size_t i;
+       size_t maxcount;
+       struct list_head *next;
+       ssize_t readlen;
+
+       obj = (struct yaffs_obj *)iop->pathinfo.node_access;
+       dev = obj->my_dev;
+       maxcount = count / sizeof(struct dirent);
+
+       ylock(dev);
+
+       if(iop->offset == 0) {
+               if(list_empty(&obj->variant.dir_variant.children))
+                       iop->data1 = NULL;
+               else
+                       iop->data1 = list_entry(obj->variant.dir_variant.children.next, struct yaffs_obj, siblings);
+       }
+
+       i = 0;
+       while((i < maxcount) && (iop->data1 != NULL)) {
+               de[i].d_ino = (long)yaffs_get_equivalent_obj((struct yaffs_obj *)iop->data1)->obj_id;
+               de[i].d_off = 0;
+               yaffs_get_obj_name((struct yaffs_obj *)iop->data1, de[i].d_name, NAME_MAX);
+               de[i].d_reclen = sizeof(struct dirent);
+               de[i].d_namlen = (unsigned short)strnlen(de[i].d_name, NAME_MAX);
+
+               i++;
+               next = ((struct yaffs_obj *)iop->data1)->siblings.next;
+               if(next == &obj->variant.dir_variant.children)
+                       iop->data1 = NULL; /* end of list */
+               else
+                       iop->data1 = list_entry(next, struct yaffs_obj, siblings);
+       }
+
+       readlen = (ssize_t)(i * sizeof(struct dirent));
+       iop->offset = iop->offset + readlen;
+
+       yunlock(dev);
+
+       return readlen;
+}
+
+static int ryfs_fstat(const rtems_filesystem_location_info_t *loc, struct stat *buf)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
+       struct yaffs_dev *dev = obj->my_dev;
+       rtems_yaffs_os_context *os_context = dev->os_context;
+
+       ylock(dev);
+
+       obj = yaffs_get_equivalent_obj(obj);
+       if (obj != NULL) {
+               buf->st_dev = os_context->dev;
+               buf->st_ino = obj->obj_id;
+               buf->st_mode = obj->yst_mode;
+               buf->st_nlink = (nlink_t) yaffs_get_obj_link_count(obj);
+               buf->st_rdev = obj->yst_rdev;
+               buf->st_size = yaffs_get_obj_length(obj);
+               buf->st_blksize = obj->my_dev->data_bytes_per_chunk;
+               buf->st_blocks = (blkcnt_t)
+                       ((buf->st_size + buf->st_blksize - 1) / buf->st_blksize);
+               buf->st_uid = (uid_t) obj->yst_uid;
+               buf->st_gid = (gid_t) obj->yst_gid;
+               buf->st_atime = (time_t) obj->yst_atime;
+               buf->st_ctime = (time_t) obj->yst_ctime;
+               buf->st_mtime = (time_t) obj->yst_mtime;
+       } else {
+               errno = EIO;
+               rv = -1;
+       }
+
+       yunlock(dev);
+
+       return rv;
+}
+
+static int ryfs_fchmod(const rtems_filesystem_location_info_t *loc, mode_t mode)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
+       int yc;
+
+       obj = yaffs_get_equivalent_obj(obj);
+       if (obj != NULL) {
+               obj->yst_mode = mode;
+               obj->dirty = 1;
+               yc = yaffs_flush_file(obj, 0, 0, 0);
+       } else {
+               yc = YAFFS_FAIL;
+       }
+
+       if (yc != YAFFS_OK) {
+               errno = EIO;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+static int ryfs_chown(
+       const rtems_filesystem_location_info_t *loc,
+       uid_t owner,
+       gid_t group
+)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
+       int yc;
+
+       obj = yaffs_get_equivalent_obj(obj);
+       if (obj != NULL) {
+               obj->yst_uid = owner;
+               obj->yst_gid = group;
+               obj->dirty = 1;
+               yc = yaffs_flush_file(obj, 0, 0, 0);
+       } else {
+               yc = YAFFS_FAIL;
+       }
+
+       if (yc != YAFFS_OK) {
+               errno = EIO;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+static int ryfs_fsync_or_fdatasync(rtems_libio_t *iop)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
+       struct yaffs_dev *dev = obj->my_dev;
+       int yc;
+
+       ylock(dev);
+       yc = yaffs_flush_file(obj, 0, 1, 0);
+       if (rtems_filesystem_location_is_instance_root(&iop->pathinfo)) {
+               yaffs_flush_whole_cache(dev, 0);
+       }
+       yunlock(dev);
+
+       if (yc != YAFFS_OK) {
+               errno = EIO;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+static int ryfs_rmnod(
+       const rtems_filesystem_location_info_t *parentloc,
+       const rtems_filesystem_location_info_t *loc
+)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
+       int yc = yaffs_del_obj(obj);
+
+       if (yc != YAFFS_OK) {
+               errno = ENOTEMPTY;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+static int ryfs_file_open(rtems_libio_t *iop, const char *pathname, int oflag, mode_t mode)
+{
+       struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
+       struct yaffs_dev *dev = obj->my_dev;
+       int length = 0;
+
+       ylock(dev);
+       length = yaffs_get_obj_length(obj);
+       if ((iop->flags & LIBIO_FLAGS_APPEND) != 0) {
+               iop->offset = length;
+       }
+       yunlock(dev);
+
+       return 0;
+}
+
+static int ryfs_file_close(rtems_libio_t *iop)
+{
+       struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
+       struct yaffs_dev *dev = obj->my_dev;
+
+       ylock(dev);
+       yaffs_flush_file(obj, 1, 0, 1);
+       yunlock(dev);
+
+       return 0;
+}
+
+static ssize_t ryfs_file_read(rtems_libio_t *iop, void *buffer, size_t count)
+{
+       struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
+       struct yaffs_dev *dev = obj->my_dev;
+       ssize_t nr;
+       int ol;
+       size_t maxread;
+
+       ylock(dev);
+
+       ol = yaffs_get_obj_length(obj);
+       if(iop->offset >= ol)
+               maxread = 0;
+       else
+               maxread = (size_t)(ol - (int)iop->offset);
+       if(count > maxread)
+               count = maxread;
+
+       nr = yaffs_file_rd(obj, buffer, iop->offset, (int)count);
+       if (nr >= 0) {
+               iop->offset += nr;
+       } else {
+               errno = EIO;
+               nr = -1;
+       }
+
+       yunlock(dev);
+
+       return nr;
+}
+
+static ssize_t ryfs_file_write(rtems_libio_t *iop, const void *buffer, size_t count)
+{
+       struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
+       struct yaffs_dev *dev = obj->my_dev;
+       ssize_t rv = -1;
+       int max_size = INT_MAX;
+       off_t offset;
+
+       if (count == 0) {
+               return 0;
+       }
+
+       ylock(dev);
+       offset = iop->offset;
+       if (offset < max_size) {
+               size_t max_count = max_size - (size_t) offset;
+
+               if (count > max_count) {
+                       count = max_count;
+               }
+
+               rv = yaffs_wr_file(obj, buffer, offset, (int) count, 0);
+               if (rv > 0) {
+                       iop->offset += rv;
+               } else {
+                       errno = ENOSPC;
+                       rv = -1;
+               }
+       } else {
+               errno = EFBIG;
+       }
+       yunlock(dev);
+
+       return rv;
+}
+
+static int ryfs_file_ftruncate(rtems_libio_t *iop, off_t length)
+{
+       int rv = 0;
+       struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
+       struct yaffs_dev *dev = obj->my_dev;
+       int yc;
+
+       ylock(dev);
+       yc = yaffs_resize_file(obj, length);
+       yunlock(dev);
+
+       if (yc != YAFFS_OK) {
+               errno = EIO;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+int rtems_yaffs_mount_handler(rtems_filesystem_mount_table_entry_t *mt_entry, const void *data)
+{
+       const rtems_yaffs_mount_data *mount_data = data;
+       struct yaffs_dev *dev = mount_data->dev;
+
+       if (dev->read_only && mt_entry->writeable) {
+               errno = EACCES;
+               return -1;
+       }
+
+       ylock(dev);
+       if (yaffs_guts_initialise(dev) == YAFFS_FAIL) {
+               yunlock(dev);
+               errno = ENOMEM;
+               return -1;
+       }
+
+       mt_entry->fs_info = dev;
+       mt_entry->ops = &yaffs_ops;
+       mt_entry->mt_fs_root->location.node_access = dev->root_dir;
+       mt_entry->mt_fs_root->location.handlers = &yaffs_directory_handlers;
+
+       yaffs_flush_whole_cache(dev, 0);
+       yunlock(dev);
+
+       return 0;
+}
+
+static void ryfs_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry)
+{
+       struct yaffs_dev *dev = ryfs_get_device_by_mt_entry(mt_entry);
+
+       ylock(dev);
+       yaffs_flush_whole_cache(dev, 1);
+       yaffs_checkpoint_save(dev);
+       yaffs_deinitialise(dev);
+       yunlock(dev);
+       rtems_yaffs_os_unmount(dev);
+}
+
+static void ryfs_lock(const rtems_filesystem_mount_table_entry_t *mt_entry)
+{
+       struct yaffs_dev *dev = ryfs_get_device_by_mt_entry(mt_entry);
+
+       ylock(dev);
+}
+
+static void ryfs_unlock(const rtems_filesystem_mount_table_entry_t *mt_entry)
+{
+       struct yaffs_dev *dev = ryfs_get_device_by_mt_entry(mt_entry);
+
+       yunlock(dev);
+}
+
+/**
+ * Construct a link from parent/name to target.
+ */
+static int ryfs_symlink(const rtems_filesystem_location_info_t *parent_loc,
+               const char *name,
+               size_t namelen,
+               const char *target)
+{
+       struct yaffs_obj *parent_dir = ryfs_get_object_by_location(parent_loc);
+       struct yaffs_dev *dev = parent_dir->my_dev;
+       uint32_t mode;
+       struct yaffs_obj *created_link;
+       int ret;
+
+       ylock(dev);
+
+       mode = S_IFLNK |
+               ((S_IRWXU | S_IRWXG | S_IRWXO) & ~rtems_filesystem_umask);
+
+       created_link = yaffs_create_symlink(parent_dir, name, mode, 
+                                               geteuid(), getegid(), target);
+
+       if (created_link != NULL) {
+               ret = 0;
+       } else {
+               errno = EINVAL;
+               ret = -1;
+       }
+
+       yunlock(dev);
+       return ret;
+}
+
+/**
+ * Read the target name of a symbolic link.  Interpretation of the path name is
+ * up to the caller.
+ *
+ * @param loc The location of the symlink
+ * @param dst_buf A non-NULL pointer to the caller's buffer for the characters.
+ * @param dst_buf_size The size of the caller's buffer in characters.
+ *
+ * @retval -1 An error occurred, the error may be found via errno.
+ * @retval non-negative size of the actual contents in characters, including the
+ * terminating NULL.
+ */
+static ssize_t ryfs_readlink(const rtems_filesystem_location_info_t *loc,
+               char *dst_buf, size_t dst_buf_size)
+{
+       struct yaffs_obj *link = ryfs_get_object_by_location(loc);
+       struct yaffs_dev *dev = link->my_dev;
+
+       ylock(dev);
+       ssize_t chars_copied = -1;
+
+       link = yaffs_get_equivalent_obj(link);
+       if (!link) {
+               errno = EBADF;
+               goto error_locked;
+       }
+
+       if (link->variant_type != YAFFS_OBJECT_TYPE_SYMLINK) {
+               errno = EINVAL;
+               goto error_locked;
+       }
+
+       // Source string length including the terminating NULL.
+       size_t src_buf_size = strlen(link->variant.symlink_variant.alias) + 1;
+       if (src_buf_size > dst_buf_size)
+               src_buf_size = dst_buf_size;
+       memcpy(dst_buf, link->variant.symlink_variant.alias, src_buf_size);
+       chars_copied = src_buf_size;
+
+error_locked:
+       yunlock(dev);
+       return chars_copied;
+}
+
+static const rtems_filesystem_file_handlers_r yaffs_directory_handlers = {
+       .open_h = rtems_filesystem_default_open,
+       .close_h = rtems_filesystem_default_close,
+       .read_h = ryfs_dir_read,
+       .write_h = rtems_filesystem_default_write,
+       .ioctl_h = rtems_filesystem_default_ioctl,
+       .lseek_h = rtems_filesystem_default_lseek_directory,
+       .fstat_h = ryfs_fstat,
+       .ftruncate_h = rtems_filesystem_default_ftruncate_directory,
+       .fsync_h = ryfs_fsync_or_fdatasync,
+       .fdatasync_h = ryfs_fsync_or_fdatasync,
+       .fcntl_h = rtems_filesystem_default_fcntl
+};
+
+static const rtems_filesystem_file_handlers_r yaffs_file_handlers = {
+       .open_h = ryfs_file_open,
+       .close_h = ryfs_file_close,
+       .read_h = ryfs_file_read,
+       .write_h = ryfs_file_write,
+       .ioctl_h = rtems_filesystem_default_ioctl,
+       .lseek_h = rtems_filesystem_default_lseek_file,
+       .fstat_h = ryfs_fstat,
+       .ftruncate_h = ryfs_file_ftruncate,
+       .fsync_h = ryfs_fsync_or_fdatasync,
+       .fdatasync_h = ryfs_fsync_or_fdatasync,
+       .fcntl_h = rtems_filesystem_default_fcntl
+};
+
+static const rtems_filesystem_file_handlers_r yaffs_link_handlers = {
+       .open_h = rtems_filesystem_default_open,
+       .close_h = rtems_filesystem_default_close,
+       .read_h = rtems_filesystem_default_read,
+       .write_h = rtems_filesystem_default_write,
+       .ioctl_h = rtems_filesystem_default_ioctl,
+       .lseek_h = rtems_filesystem_default_lseek_file,
+       .fstat_h = ryfs_fstat,
+       .ftruncate_h = rtems_filesystem_default_ftruncate,
+       .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
+       .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
+       .fcntl_h = rtems_filesystem_default_fcntl,
+};
+
+static const rtems_filesystem_operations_table yaffs_ops = {
+       .lock_h = ryfs_lock,
+       .unlock_h = ryfs_unlock,
+       .eval_path_h = ryfs_eval_path,
+       .link_h = rtems_filesystem_default_link,
+       .are_nodes_equal_h = rtems_filesystem_default_are_nodes_equal,
+       .mknod_h = ryfs_mknod,
+       .rmnod_h = ryfs_rmnod,
+       .fchmod_h = ryfs_fchmod,
+       .chown_h = ryfs_chown,
+       .clonenod_h = rtems_filesystem_default_clonenode,
+       .freenod_h = rtems_filesystem_default_freenode,
+       .mount_h = rtems_filesystem_default_mount,
+       .unmount_h = rtems_filesystem_default_unmount,
+       .fsunmount_me_h = ryfs_fsunmount,
+       .utime_h = ryfs_utime,
+       .symlink_h = ryfs_symlink,
+       .readlink_h = ryfs_readlink,
+       .rename_h = ryfs_rename,
+       .statvfs_h = rtems_filesystem_default_statvfs
+};
diff --git a/rtems/rtems_yaffs.h b/rtems/rtems_yaffs.h
new file mode 100644 (file)
index 0000000..e94fba7
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * YAFFS port to RTEMS
+ *
+ * Copyright (C) 2010 Sebastien Bourdeauducq
+ * Copyright (C) 2011 Stephan Hoffmann <sho@reLinux.de>
+ * Copyright (C) 2011 embedded brains GmbH <rtems@embedded-brains.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ * 
+ * As a special exception, including this header in a file does not by
+ * itself cause the resulting executable application to be covered by the
+ * GNU General Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU Public License. In particular,
+ * the other YAFFS files are not covered by this exception, and using them
+ * in a proprietary application requires a paid license from Aleph One.
+ */
+
+#ifndef __RTEMS_YAFFS_H
+#define __RTEMS_YAFFS_H
+
+#include <rtems.h>
+#include <rtems/fs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Must be inside the extern "C" */
+#include "yportenv.h"
+#include "yaffs_guts.h"
+
+/**
+ * @defgroup rtems_yaffs YAFFS Support for RTEMS
+ *
+ *
+ * @{
+ */
+
+#define RTEMS_FILESYSTEM_TYPE_YAFFS "yaffs"
+
+typedef void (*rtems_yaffs_os_handler)(
+  struct yaffs_dev *dev,
+  void *os_context
+);
+
+/**
+ * @brief Per YAFFS file system instance context.
+ */
+typedef struct {
+  rtems_yaffs_os_handler lock;
+  rtems_yaffs_os_handler unlock;
+  rtems_yaffs_os_handler unmount;
+
+  /**
+   * @brief The device containing the file system instance.
+   *
+   * This will be used for the st_dev field in stat().
+   */
+  dev_t dev;
+} rtems_yaffs_os_context;
+
+/**
+ * @brief Default per YAFFS file system instance context.
+ */
+typedef struct {
+  rtems_yaffs_os_context os_context;
+  rtems_id semaphore_id;
+} rtems_yaffs_default_os_context;
+
+/**
+ * @brief Data for YAFFS mount handler.
+ *
+ * @see rtems_yaffs_mount_handler()
+ */
+typedef struct {
+  /**
+   * @brief YAFFS device of the file system instance.
+   *
+   * The @a param field has to be completely set up.  The
+   * @a driver_context can point to arbitrary driver specific
+   * information.  The @a os_context must point to an initialized
+   * structure that begins with a rtems_yaffs_os_context structure.
+   */
+  struct yaffs_dev *dev;
+} rtems_yaffs_mount_data;
+
+/**
+ * @brief YAFFS mount handler.
+ *
+ * The @a data pointer must point to a completely initialized
+ * rtems_yaffs_mount_data structure.  The ownership of the YAFFS device
+ * structure changes.  This structure is now owned by the file system layer.
+ *
+ * @retval 0 Successful operation.
+ * @retval -1 An error occurred.  The @c errno indicates the error.
+ */
+int rtems_yaffs_mount_handler(
+  rtems_filesystem_mount_table_entry_t *mt_entry,
+  const void *data
+);
+
+/**
+ * @brief Initializes the default per file system context @a os_context.
+ *
+ * A binary semaphore with priority inheritance will be used to ensure mutual
+ * exclusion.
+ *
+ * The umount handler will release all resources of the default context.
+ *
+ * @retval 0 Successful operation.
+ * @retval -1 An error occurred.  The @c errno indicates the error.
+ */
+int rtems_yaffs_initialize_default_os_context(
+  rtems_yaffs_default_os_context *os_context
+);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __RTEMS_YAFFS_H */
diff --git a/rtems/rtems_yaffs_os_context.c b/rtems/rtems_yaffs_os_context.c
new file mode 100644 (file)
index 0000000..1885b9b
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * YAFFS port to RTEMS
+ *
+ * Copyright (c) 2011 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Obere Lagerstr. 30
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems@embedded-brains.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * As a special exception, linking other files with the object code from
+ * this one to produce an executable application does not by itself cause
+ * the resulting executable application to be covered by the GNU General
+ * Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU Public License. In particular,
+ * the other YAFFS files are not covered by this exception, and using them
+ * in a proprietary application requires a paid license from Aleph One.
+ */
+
+#include "rtems_yaffs.h"
+
+#include <assert.h>
+#include <errno.h>
+
+static void rtems_yaffs_default_lock(struct yaffs_dev *dev, void *arg)
+{
+       rtems_status_code sc = RTEMS_SUCCESSFUL;
+       rtems_yaffs_default_os_context *os_context = arg;
+
+        sc = rtems_semaphore_obtain(
+               os_context->semaphore_id,
+               RTEMS_WAIT,
+               RTEMS_NO_TIMEOUT
+       );
+       assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void rtems_yaffs_default_unlock(struct yaffs_dev *dev, void *arg)
+{
+       rtems_status_code sc = RTEMS_SUCCESSFUL;
+       rtems_yaffs_default_os_context *os_context = arg;
+
+        sc = rtems_semaphore_release(os_context->semaphore_id);
+       assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void rtems_yaffs_default_unmount(struct yaffs_dev *dev, void *arg)
+{
+       rtems_status_code sc = RTEMS_SUCCESSFUL;
+       rtems_yaffs_default_os_context *os_context = arg;
+
+        sc = rtems_semaphore_delete(os_context->semaphore_id);
+       assert(sc == RTEMS_SUCCESSFUL);
+}
+
+int rtems_yaffs_initialize_default_os_context(
+       rtems_yaffs_default_os_context *os_context
+)
+{
+       rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+       os_context->os_context.lock = rtems_yaffs_default_lock;
+       os_context->os_context.unlock = rtems_yaffs_default_unlock;
+       os_context->os_context.unmount = rtems_yaffs_default_unmount;
+
+       sc = rtems_semaphore_create(
+               rtems_build_name('Y', 'A', 'F', 'S'),
+               1,
+               RTEMS_LOCAL
+                       | RTEMS_BINARY_SEMAPHORE
+                       | RTEMS_INHERIT_PRIORITY
+                       | RTEMS_PRIORITY,
+               0,
+               &os_context->semaphore_id
+       );
+       if (sc == RTEMS_SUCCESSFUL) {
+               return 0;
+       } else {
+               errno = ENOMEM;
+
+               return -1;
+       }
+}
diff --git a/rtems/rtems_yaffs_os_glue.c b/rtems/rtems_yaffs_os_glue.c
new file mode 100644 (file)
index 0000000..467d8b1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * YAFFS port to RTEMS
+ *
+ * Copyright (C) 2010, 2011 Sebastien Bourdeauducq
+ * Copyright (C) 2011 Stephan Hoffmann <sho@reLinux.de>
+ * Copyright (C) 2011 embedded brains GmbH <rtems@embedded-brains.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * As a special exception, linking other files with the object code from
+ * this one to produce an executable application does not by itself cause
+ * the resulting executable application to be covered by the GNU General
+ * Public License.
+ * This exception does not however invalidate any other reasons why the
+ * executable file might be covered by the GNU Public License. In particular,
+ * the other YAFFS files are not covered by this exception, and using them
+ * in a proprietary application requires a paid license from Aleph One.
+ */
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "yaffs_trace.h"
+#include "yaffs_osglue.h"
+
+unsigned int yaffs_trace_mask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS;
+
+unsigned int yaffs_wr_attempts;
+
+void *yaffsfs_malloc(size_t size)
+{
+       return malloc(size);
+}
+
+void yaffsfs_free(void *ptr)
+{
+       free(ptr);
+}
+
+u32 yaffsfs_CurrentTime(void)
+{
+       return time(NULL);
+}
index 53688b5..f5c3be8 100644 (file)
@@ -438,7 +438,7 @@ int yaffs_checkpt_close(struct yaffs_dev *dev)
 
        if (dev->checkpt_open_write) {
                if (dev->checkpt_byte_offs !=
-                       sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))
+                       sizeof(struct yaffs_checkpt_chunk_hdr))
                        yaffs2_checkpt_flush_buffer(dev);
        } else if (dev->checkpt_block_list) {
                for (i = 0;
index 40a5b46..c52ff84 100644 (file)
@@ -4799,6 +4799,18 @@ int yaffs_guts_format_dev(struct yaffs_dev *dev)
        return YAFFS_OK;
 }
 
+/*
+ * If the dev is mounted r/w then the cleanup will happen during
+ * yaffs_guts_initialise. However if the dev is mounted ro then
+ * the cleanup will be dfered until yaffs is remounted r/w.
+ */
+void yaffs_guts_cleanup(struct yaffs_dev *dev)
+{
+       yaffs_strip_deleted_objs(dev);
+       yaffs_fix_hanging_objs(dev);
+       if (dev->param.empty_lost_n_found)
+                       yaffs_empty_l_n_f(dev);
+}
 
 int yaffs_guts_initialise(struct yaffs_dev *dev)
 {
@@ -5012,10 +5024,7 @@ int yaffs_guts_initialise(struct yaffs_dev *dev)
                        init_failed = 1;
                }
 
-               yaffs_strip_deleted_objs(dev);
-               yaffs_fix_hanging_objs(dev);
-               if (dev->param.empty_lost_n_found)
-                       yaffs_empty_l_n_f(dev);
+               yaffs_guts_cleanup(dev);
        }
 
        if (init_failed) {
index 5ebc378..124e4c9 100644 (file)
@@ -885,6 +885,7 @@ struct yaffs_xattr_mod {
 
 int yaffs_guts_initialise(struct yaffs_dev *dev);
 void yaffs_deinitialise(struct yaffs_dev *dev);
+void yaffs_guts_cleanup(struct yaffs_dev *dev);
 
 int yaffs_get_n_free_chunks(struct yaffs_dev *dev);
 
index cded165..bcd6f24 100644 (file)
@@ -14,7 +14,7 @@
  * tags storage.
  */
 
-#include "yaffs_guts.h"
+#include "yaffs_tagsmarshall.h"
 #include "yaffs_trace.h"
 #include "yaffs_packedtags2.h"
 
index a36d9bf..3044db7 100644 (file)
@@ -2718,6 +2718,7 @@ static int yaffs_sync_fs(struct super_block *sb)
 static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
 {
        int read_only = 0;
+       int was_read_only = 0;
        struct mtd_info *mtd;
        struct yaffs_dev *dev = 0;
 
@@ -2747,8 +2748,18 @@ static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
        }
 
        dev = sb->s_fs_info;
+       was_read_only = dev->read_only;
        dev->read_only = read_only;
 
+       if (was_read_only && !read_only) {
+               yaffs_gross_lock(dev);
+               yaffs_guts_cleanup(dev);
+               yaffs_gross_unlock(dev);
+               yaffs_bg_start(dev);
+       } else if (!was_read_only && read_only) {
+               yaffs_bg_stop(dev);
+       }
+
        return 0;
 }
 
@@ -3687,6 +3698,7 @@ static struct file_operations procfs_ops = {
        .open  = yaffs_proc_open,
        .read  = seq_read,
        .write = yaffs_proc_write,
+       .release = single_release,
 };
 
 static int yaffs_procfs_init(void)
index 0ac24bc..1abbfd8 100644 (file)
@@ -1489,15 +1489,34 @@ static void yaffs_flush_inodes(struct super_block *sb)
 {
        struct inode *iptr;
        struct yaffs_obj *obj;
+       struct yaffs_dev *dev = yaffs_super_to_dev(sb);
 
+       spin_lock(&sb->s_inode_list_lock);
        list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) {
+               spin_lock(&inode->i_lock);
+               if (iptr->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) {
+                       spin_unlock(&inode->i_lock);
+                       continue;
+               }
+
+               __iget(iptr);
+               spin_unlock(&inode->i_lock);
+               spin_unlock(&sb->s_inode_list_lock);
+
                obj = yaffs_inode_to_obj(iptr);
                if (obj) {
                        yaffs_trace(YAFFS_TRACE_OS,
                                "flushing obj %d", obj->obj_id);
                        yaffs_flush_file(obj, 1, 0, 0);
                }
+
+               yaffs_gross_unlock(dev);
+               iput(iptr);
+               yaffs_gross_lock(dev);
+
+               spin_lock(&sb->s_inode_list_lock);
        }
+       spin_unlock(&sb->s_inode_list_lock);
 }
 
 static void yaffs_flush_super(struct super_block *sb, int do_checkpoint)