commit 0221229b80c286e95200ab6d7472fe2a1771540d Author: Peter Andersson Date: Thu Jul 4 21:33:59 2013 +0200 inital commit diff --git a/build/linux_spiffs_test b/build/linux_spiffs_test new file mode 100755 index 0000000..335739f Binary files /dev/null and b/build/linux_spiffs_test differ diff --git a/build/main.d b/build/main.d new file mode 100644 index 0000000..6be1ada --- /dev/null +++ b/build/main.d @@ -0,0 +1,36 @@ + build/main.o build/main.d : src/main.c /usr/include/stdio.h /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/bits/predefs.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/sigset.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h src/stypes.h src/spiffs.h \ + src/default/spiffs_config.h src/stypes.h src/spiffs_nucleus.h \ + src/testrunner.h /usr/include/x86_64-linux-gnu/sys/stat.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h /usr/include/dirent.h \ + /usr/include/x86_64-linux-gnu/bits/dirent.h \ + /usr/include/x86_64-linux-gnu/bits/posix1_lim.h \ + /usr/include/x86_64-linux-gnu/bits/local_lim.h \ + /usr/include/linux/limits.h /usr/include/unistd.h \ + /usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + /usr/include/x86_64-linux-gnu/bits/environments.h \ + /usr/include/x86_64-linux-gnu/bits/confname.h /usr/include/getopt.h diff --git a/build/main.o b/build/main.o new file mode 100644 index 0000000..adba66d Binary files /dev/null and b/build/main.o differ diff --git a/build/spiffs_cache.d b/build/spiffs_cache.d new file mode 100644 index 0000000..d3a682c --- /dev/null +++ b/build/spiffs_cache.d @@ -0,0 +1,26 @@ + build/spiffs_cache.o build/spiffs_cache.d : src/spiffs_cache.c src/spiffs.h \ + src/default/spiffs_config.h src/stypes.h /usr/include/stdio.h \ + /usr/include/features.h /usr/include/x86_64-linux-gnu/bits/predefs.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/sigset.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h src/spiffs_nucleus.h diff --git a/build/spiffs_cache.o b/build/spiffs_cache.o new file mode 100644 index 0000000..1ac65f1 Binary files /dev/null and b/build/spiffs_cache.o differ diff --git a/build/spiffs_hydrogen.d b/build/spiffs_hydrogen.d new file mode 100644 index 0000000..816b12f --- /dev/null +++ b/build/spiffs_hydrogen.d @@ -0,0 +1,26 @@ + build/spiffs_hydrogen.o build/spiffs_hydrogen.d : src/spiffs_hydrogen.c src/spiffs.h \ + src/default/spiffs_config.h src/stypes.h /usr/include/stdio.h \ + /usr/include/features.h /usr/include/x86_64-linux-gnu/bits/predefs.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/sigset.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h src/spiffs_nucleus.h diff --git a/build/spiffs_hydrogen.o b/build/spiffs_hydrogen.o new file mode 100644 index 0000000..5f56d68 Binary files /dev/null and b/build/spiffs_hydrogen.o differ diff --git a/build/spiffs_nucleus.d b/build/spiffs_nucleus.d new file mode 100644 index 0000000..7f5c122 --- /dev/null +++ b/build/spiffs_nucleus.d @@ -0,0 +1,26 @@ + build/spiffs_nucleus.o build/spiffs_nucleus.d : src/spiffs_nucleus.c src/spiffs.h \ + src/default/spiffs_config.h src/stypes.h /usr/include/stdio.h \ + /usr/include/features.h /usr/include/x86_64-linux-gnu/bits/predefs.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/sigset.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h src/spiffs_nucleus.h diff --git a/build/spiffs_nucleus.o b/build/spiffs_nucleus.o new file mode 100644 index 0000000..038b4a4 Binary files /dev/null and b/build/spiffs_nucleus.o differ diff --git a/build/test_hydrogen.d b/build/test_hydrogen.d new file mode 100644 index 0000000..4bdcf9c --- /dev/null +++ b/build/test_hydrogen.d @@ -0,0 +1,37 @@ + build/test_hydrogen.o build/test_hydrogen.d : src/test_hydrogen.c src/testrunner.h src/test_spiffs.h \ + src/spiffs.h src/default/spiffs_config.h src/stypes.h \ + /usr/include/stdio.h /usr/include/features.h \ + /usr/include/x86_64-linux-gnu/bits/predefs.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/sigset.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h src/spiffs_nucleus.h \ + /usr/include/x86_64-linux-gnu/sys/stat.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h /usr/include/dirent.h \ + /usr/include/x86_64-linux-gnu/bits/dirent.h \ + /usr/include/x86_64-linux-gnu/bits/posix1_lim.h \ + /usr/include/x86_64-linux-gnu/bits/local_lim.h \ + /usr/include/linux/limits.h /usr/include/unistd.h \ + /usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + /usr/include/x86_64-linux-gnu/bits/environments.h \ + /usr/include/x86_64-linux-gnu/bits/confname.h /usr/include/getopt.h diff --git a/build/test_hydrogen.o b/build/test_hydrogen.o new file mode 100644 index 0000000..508d9fd Binary files /dev/null and b/build/test_hydrogen.o differ diff --git a/build/test_spiffs.d b/build/test_spiffs.d new file mode 100644 index 0000000..d29fe3e --- /dev/null +++ b/build/test_spiffs.d @@ -0,0 +1,37 @@ + build/test_spiffs.o build/test_spiffs.d : src/test_spiffs.c /usr/include/stdio.h \ + /usr/include/features.h /usr/include/x86_64-linux-gnu/bits/predefs.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/sigset.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h src/stypes.h src/spiffs.h \ + src/default/spiffs_config.h src/stypes.h src/spiffs_nucleus.h \ + src/testrunner.h src/test_spiffs.h \ + /usr/include/x86_64-linux-gnu/sys/stat.h \ + /usr/include/x86_64-linux-gnu/bits/stat.h /usr/include/fcntl.h \ + /usr/include/x86_64-linux-gnu/bits/fcntl.h /usr/include/dirent.h \ + /usr/include/x86_64-linux-gnu/bits/dirent.h \ + /usr/include/x86_64-linux-gnu/bits/posix1_lim.h \ + /usr/include/x86_64-linux-gnu/bits/local_lim.h \ + /usr/include/linux/limits.h /usr/include/unistd.h \ + /usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + /usr/include/x86_64-linux-gnu/bits/environments.h \ + /usr/include/x86_64-linux-gnu/bits/confname.h /usr/include/getopt.h diff --git a/build/test_spiffs.o b/build/test_spiffs.o new file mode 100644 index 0000000..af535e6 Binary files /dev/null and b/build/test_spiffs.o differ diff --git a/build/testrunner.d b/build/testrunner.d new file mode 100644 index 0000000..5ef6745 --- /dev/null +++ b/build/testrunner.d @@ -0,0 +1,25 @@ + build/testrunner.o build/testrunner.d : src/testrunner.c /usr/include/stdio.h \ + /usr/include/features.h /usr/include/x86_64-linux-gnu/bits/predefs.h \ + /usr/include/x86_64-linux-gnu/sys/cdefs.h \ + /usr/include/x86_64-linux-gnu/bits/wordsize.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs.h \ + /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ + /usr/include/x86_64-linux-gnu/bits/types.h \ + /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ + /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + /usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/x86_64-linux-gnu/bits/waitflags.h \ + /usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/x86_64-linux-gnu/bits/endian.h \ + /usr/include/x86_64-linux-gnu/bits/byteswap.h \ + /usr/include/x86_64-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/x86_64-linux-gnu/sys/select.h \ + /usr/include/x86_64-linux-gnu/bits/select.h \ + /usr/include/x86_64-linux-gnu/bits/sigset.h \ + /usr/include/x86_64-linux-gnu/bits/time.h \ + /usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h src/testrunner.h diff --git a/build/testrunner.o b/build/testrunner.o new file mode 100644 index 0000000..6d415a6 Binary files /dev/null and b/build/testrunner.o differ diff --git a/build/testsuites.d b/build/testsuites.d new file mode 100644 index 0000000..ae9197f --- /dev/null +++ b/build/testsuites.d @@ -0,0 +1 @@ + build/testsuites.o build/testsuites.d : src/testsuites.c src/testrunner.h diff --git a/build/testsuites.o b/build/testsuites.o new file mode 100644 index 0000000..bcd50e0 Binary files /dev/null and b/build/testsuites.o differ diff --git a/files.mk b/files.mk new file mode 100644 index 0000000..c7241c0 --- /dev/null +++ b/files.mk @@ -0,0 +1,4 @@ +FILES += spiffs_nucleus.c \ + spiffs_hydrogen.c \ + spiffs_cache.c + diff --git a/makefile b/makefile new file mode 100644 index 0000000..99c09a9 --- /dev/null +++ b/makefile @@ -0,0 +1,91 @@ +BINARY = linux_spiffs_test + +############ +# +# Paths +# +############ + +sourcedir = src +builddir = build + + +############# +# +# Build tools +# +############# + +CC = gcc $(COMPILEROPTIONS) +AS = gcc $(ASSEMBLEROPTIONS) +LD = ld +GDB = gdb +OBJCOPY = objcopy +OBJDUMP = objdump +MKDIR = mkdir -p + +############### +# +# Files and libs +# +############### + +FILES = main.c \ + test_spiffs.c \ + test_hydrogen.c \ + testsuites.c \ + testrunner.c +include files.mk +INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default +COMPILEROPTIONS = $(INCLUDE_DIRECTIVES) + +############ +# +# Tasks +# +############ + +vpath %.c ${sourcedir} ${sourcedir}/default + +OBJFILES = $(FILES:%.c=${builddir}/%.o) + +DEPFILES = $(FILES:%.c=${builddir}/%.d) + +ALLOBJFILES += $(OBJFILES) + +DEPENDENCIES = $(DEPFILES) + +# link object files, create binary for flashing +$(BINARY): $(ALLOBJFILES) + @echo "... linking" + @${CC} $(LINKEROPTIONS) -o ${builddir}/$(BINARY) $(ALLOBJFILES) $(LIBS) + +-include $(DEPENDENCIES) + +# compile c files +$(OBJFILES) : ${builddir}/%.o:%.c + @echo "... compile $@" + @${CC} -g -c -o $@ $< + +# make dependencies +$(DEPFILES) : ${builddir}/%.d:%.c + @echo "... depend $@"; \ + rm -f $@; \ + ${CC} $(COMPILEROPTIONS) -M $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*, ${builddir}/\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +all: mkdirs $(BINARY) + +mkdirs: + -@${MKDIR} ${builddir} + +clean: + @echo ... removing build files in ${builddir} + @rm -f ${builddir}/*.o + @rm -f ${builddir}/*.d + @rm -f ${builddir}/*.out + @rm -f ${builddir}/*.hex + @rm -f ${builddir}/*.elf + @rm -f ${builddir}/*.map + @rm -f ${builddir}/*_disasm.s diff --git a/src/default/spiffs_config.h b/src/default/spiffs_config.h new file mode 100644 index 0000000..d25606f --- /dev/null +++ b/src/default/spiffs_config.h @@ -0,0 +1,94 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + + +#include "stypes.h" +#include +#include +#include +#include + +// compile time switches + +#define SPIFFS_DBG(...) +#define SPIFFS_GC_DBG(...) +#define SPIFFS_CACHE_DBG(...) + +// define maximum number of gc runs to perform to reach desired free pages +#define SPIFFS_GC_MAX_RUNS 3 +// enable/disable statistics on gc +#define SPIFFS_GC_STATS 1 + +// enables/disable memory read caching of nucleus file system operations +// if enabled, memory area must be provided for cache in SPIFFS_init +#define SPIFFS_CACHE 1 +#if SPIFFS_CACHE +// enables memory write caching for file descriptors in hydrogen +#define SPIFFS_CACHE_WR 1 +// enable/disable statistics on caching +#define SPIFFS_CACHE_STATS 1 +#endif + +// checks header of each accessed page to validate state +#define SPIFFS_PAGE_CHECK 1 + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// garbage collecting heuristics - weight used for deleted pages +#define SPIFFS_GC_HEUR_W_DELET (10) +// garbage collecting heuristics - weight used for used pages +#define SPIFFS_GC_HEUR_W_USED (-1) + +// object name length +#define SPIFFS_OBJ_NAME_LEN (32 - sizeof(spiffs_obj_type)) + +// size of buffer on stack used when copying data +#define SPIFFS_COPY_BUFFER_STACK (64) + + +// types and constants + +// spiffs file descriptor index type +typedef s16_t spiffs_file; +// spiffs file descriptor attributes +typedef u32_t spiffs_attr; +// spiffs file descriptor mode +typedef u32_t spiffs_mode; + +// block index type +typedef u16_t spiffs_block_ix; // (address-phys_addr) / block_size +// page index type +typedef u16_t spiffs_page_ix; // (address-phys_addr) / page_size +// object id type - most significant bit is reserved for index flag +typedef u16_t spiffs_obj_id; +// object span index type +typedef u16_t spiffs_span_ix; +// object type +typedef u8_t spiffs_obj_type; + +// enable if only one spiffs instance with constant configuration will exist +// on the system +//#define SPIFFS_SINGLETON + +#ifdef SPIFFS_SINGLETON +// instead of giving parameters in config struct, singleton build must +// give parameters in defines below +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*1) +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536) +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536) +#endif + +#endif /* SPIFFS_CONFIG_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d4ff4e5 --- /dev/null +++ b/src/main.c @@ -0,0 +1,31 @@ +/* + ============================================================================ + Name : spiffs_test_.c + Author : + Version : + Copyright : Your copyright notice + Description : Hello World in C, Ansi-style + ============================================================================ + */ + +#include +#include +#include + +#include "stypes.h" +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#include "testrunner.h" + +#include +#include +#include +#include +#include + +int main(void) { + run_tests(); + return EXIT_SUCCESS; +} + diff --git a/src/spiffs.h b/src/spiffs.h new file mode 100644 index 0000000..369f4b4 --- /dev/null +++ b/src/spiffs.h @@ -0,0 +1,206 @@ +/* + * spiffs.h + * + * Created on: May 26, 2013 + * Author: petera + */ + + + +#ifndef SPIFFS_H_ +#define SPIFFS_H_ + +#include "spiffs_config.h" + +#define SPIFFS_OK 0 +#define SPIFFS_ERR_FULL -10000 +#define SPIFFS_ERR_NOT_FOUND -10001 +#define SPIFFS_ERR_END_OF_OBJECT -10002 +#define SPIFFS_ERR_DELETED -10003 +#define SPIFFS_ERR_NOT_FINALIZED -10004 +#define SPIFFS_ERR_NOT_INDEX -10005 +#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10006 +#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 +#define SPIFFS_ERR_FILE_CLOSED -10008 +#define SPIFFS_ERR_FILE_DELETED -10009 +#define SPIFFS_ERR_BAD_DESCRIPTOR -10010 +#define SPIFFS_ERR_IS_INDEX -10011 +#define SPIFFS_ERR_INDEX_WRONG_ID -10012 +#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10013 +#define SPIFFS_ERR_DATA_WRONG_ID -10014 + +#define SPIFFS_COUNTINUE -11000 + +// spi read call type +typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); +// spi write call type +typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); +// spi erase call type +typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); + +// size of buffer on stack used when copying data +#define SPIFFS_COPY_BUFFER_STACK (64) + +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) \ + print(__VA_ARGS__) +#endif +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) +#endif + +#define SPIFFS_APPEND (1<<0) +#define SPIFFS_TRUNC (1<<1) +#define SPIFFS_CREAT (1<<2) +#define SPIFFS_RDONLY (1<<3) +#define SPIFFS_WRONLY (1<<4) +#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) +#define SPIFFS_DIRECT (1<<5) + +#define SPIFFS_SEEK_SET (0) +#define SPIFFS_SEEK_CUR (1) +#define SPIFFS_SEEK_END (2) + +#define SPIFFS_TYPE_FILE (1) +#define SPIFFS_TYPE_DIR (2) +#define SPIFFS_TYPE_HARD_LINK (3) +#define SPIFFS_TYPE_SOFT_LINK (4) + +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif + +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + +// phys structs + +// spiffs spi configuration struct +typedef struct { + // physical read function + spiffs_read hal_read_f; + // physical write function + spiffs_write hal_write_f; + // physical erase function + spiffs_erase hal_erase_f; +#ifndef SPIFFS_SINGLETON + // physical size of the spi flash + u32_t phys_size; + // physical offset in spi flash used for spiffs, + // must be on block boundary + u32_t phys_addr; + // physical size when erasing a block + u32_t phys_erase_block; + + // logical size of a block, must be on physical + // block size boundary and must never be less than + // a physical block + u32_t log_block_size; + // logical size of a page, must be at least + // log_block_size / 8 + u32_t log_page_size; +#endif +} spiffs_config; + +typedef struct { + // file system configuration + spiffs_config cfg; + // number of logical blocks + u32_t block_count; + + // cursor for free blocks, block index + spiffs_block_ix free_cursor_block_ix; + // cursor for free blocks, entry index + int free_cursor_obj_lu_entry; + // cursor when searching, block index + spiffs_block_ix cursor_block_ix; + // cursor when searching, entry index + int cursor_obj_lu_entry; + + // primary work buffer, size of a logical page + u8_t *lu_work; + // secondary work buffer, size of a logical page + u8_t *work; + // file descriptor memory area + u8_t *fd_space; + // available file descriptors + u32_t fd_count; + + s32_t errno; + + u32_t free_blocks; + + u32_t stats_p_allocated; + u32_t stats_p_deleted; +#if SPIFFS_GC_STATS + u32_t stats_gc_runs; +#endif + +#if SPIFFS_CACHE + void *cache; + u32_t cache_size; +#if SPIFFS_CACHE_STATS + u32_t cache_hits; + u32_t cache_misses; +#endif +#endif +} spiffs; + +typedef struct { + spiffs_obj_id obj_id; + u32_t size; + spiffs_obj_type type; + u8_t name[SPIFFS_OBJ_NAME_LEN]; +} spiffs_stat; + +struct spiffs_dirent { + spiffs_obj_id obj_id; + u8_t name[SPIFFS_OBJ_NAME_LEN]; + spiffs_obj_type type; + u32_t size; +}; + +typedef struct { + spiffs *fs; + spiffs_block_ix block; + int entry; +} spiffs_DIR; + +// functions + +/** + * Initializes the file system dynamic parameters + * @param fs the file system struct + * @param config the physical and logical configuration of the file system + * @param work a memory work buffer comprising 2*config->log_page_size + * bytes used throughout all file system operations + * @param fd_space memory for file descriptors + * @param fd_space_size memory size of file descriptors + * @param cache memory for cache, may be null + * @param cache_size memory size of cache + */ +s32_t SPIFFS_init(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size); + +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_attr attr); +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_attr attr, spiffs_mode mode); +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); +s32_t SPIFFS_remove(spiffs *fs, const char *path); +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); +void SPIFFS_close(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_errno(spiffs *fs); + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); +s32_t SPIFFS_closedir(spiffs_DIR *d); +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); + +#endif /* SPIFFS_H_ */ diff --git a/src/spiffs_cache.c b/src/spiffs_cache.c new file mode 100644 index 0000000..54c2fcf --- /dev/null +++ b/src/spiffs_cache.c @@ -0,0 +1,289 @@ +/* + * spiffs_cache.c + * + * Created on: Jun 23, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if SPIFFS_CACHE + +static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache *cache = get_cache(fs); + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + cp->pix == pix ) { + SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix); + cp->last_access = cache->last_access; + return cp; + } + } + //SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix); + return 0; +} + +static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = get_cache(fs); + spiffs_cache_page *cp = get_cache_page_hdr(fs, cache, ix); + if (cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { + u8_t *mem = get_cache_page(fs, cache, ix); + res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } + + cp->flags = 0; + cache->cpage_use_map &= ~(1 << ix); + + if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id); + } else { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix); + } + } + + return res; +} + +static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { + // at least one free cpage + return SPIFFS_OK; + } + + // all busy, scan thru all to find the cpage which has oldest access + int i; + int cand_ix = -1; + u32_t oldest_val = 0; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = get_cache_page_hdr(fs, cache, i); + if ((cache->last_access - cp->last_access) > oldest_val && + (cp->flags & flag_mask) == flags) { + oldest_val = cache->last_access - cp->last_access; + cand_ix = i; + } + } + + if (cand_ix >= 0) { + res = spiffs_cache_page_free(fs, cand_ix, 1); + } + + return res; +} + +static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { + spiffs_cache *cache = get_cache(fs); + if (cache->cpage_use_map == 0xffffffff) { + // out of cache memory + return 0; + } + int i; + for (i = 0; i < cache->cpage_count; i++) { + if ((cache->cpage_use_map & (1<cpage_use_map |= (1<last_access = cache->last_access; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i); + return cp; + } + } + // out of cache entries + return 0; +} + +// ------------------------------ + +void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + if (cp) { + spiffs_cache_page_free(fs, cp->ix, 0); + } +} + +s32_t spiffs_phys_rd( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); + cache->last_access++; + if (cp) { +#if SPIFFS_CACHE_STATS + fs->cache_hits++; +#endif + cp->last_access = cache->last_access; + } else { + if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { + // for second layer lookup functions, we do not cache in order to prevent shredding + return fs->cfg.hal_read_f( + addr , + len, + dst); + } +#if SPIFFS_CACHE_STATS + fs->cache_misses++; +#endif + res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + cp = spiffs_cache_page_allocate(fs); + if (cp) { + cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; + cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + } + + s32_t res2 = fs->cfg.hal_read_f( + addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + get_cache_page(fs, cache, cp->ix)); + if (res2 != SPIFFS_OK) { + res = res2; + } + } + u8_t *mem = get_cache_page(fs, cache, cp->ix); + memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + return res; +} + +s32_t spiffs_phys_wr( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *src) { + spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + spiffs_cache *cache = get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + + if (cp) { + // have a cache page + // copy in data to cache page + + if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && + (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { + // page is being deleted, wipe from cache - unless it is a lookup page + spiffs_cache_page_free(fs, cp->ix, 0); + return fs->cfg.hal_write_f(addr, len, src); + } + + u8_t *mem = get_cache_page(fs, cache, cp->ix); + memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + + cache->last_access++; + cp->last_access = cache->last_access; + + if (cp->flags && SPIFFS_CACHE_FLAG_WRTHRU) { + // page is being updated, no write-cache, just pass thru + return fs->cfg.hal_write_f(addr, len, src); + } else { + return SPIFFS_OK; + } + } else { + // no cache page, no write cache - just write thru + return fs->cfg.hal_write_f(addr, len, src); + } +} + +#if SPIFFS_CACHE_WR +spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) { + spiffs_cache *cache = get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { + // all cpages free, no cpage cannot be assigned to obj_id + return 0; + } + + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && + cp->obj_id == fd->obj_id) { + return cp; + } + } + + return 0; +} + +spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { + // before this function is called, it is ensured that there is no already existing + // cache page with same object id + spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); + if (cp == 0) { + // could not get cache page + return 0; + } + + cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; + cp->obj_id = fd->obj_id; + fd->cache_page = cp; + return cp; +} + +void spiffs_cache_fh_release(spiffs *fs, spiffs_cache_page *cp) { + if (cp == 0) return; + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) { + cur_fd->cache_page = 0; + } + } + spiffs_cache_page_free(fs, cp->ix, 0); + + cp->obj_id = 0; +} + +#endif + +void spiffs_cache_init(spiffs *fs) { + if (fs->cache == 0) return; + u32_t sz = fs->cache_size; + u32_t cache_mask = 0; + int i; + int cache_entries = + (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); + if (cache_entries <= 0) return; + + for (i = 0; i < cache_entries; i++) { + cache_mask <<= 1; + cache_mask |= 1; + } + + spiffs_cache cache; + memset(&cache, 0, sizeof(spiffs_cache)); + cache.cpage_count = cache_entries; + cache.cpages = (u8_t *)(fs->cache + sizeof(spiffs_cache)); + + cache.cpage_use_map = 0xffffffff; + cache.cpage_use_mask = cache_mask; + memcpy(fs->cache, &cache, sizeof(spiffs_cache)); + + spiffs_cache *c = get_cache(fs); + + memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); + + c->cpage_use_map &= ~(c->cpage_use_mask); + for (i = 0; i < cache.cpage_count; i++) { + get_cache_page_hdr(fs, c, i)->ix = i; + } +} + +#endif // SPIFFS_CACHE diff --git a/src/spiffs_hydrogen.c b/src/spiffs_hydrogen.c new file mode 100644 index 0000000..23d8f7b --- /dev/null +++ b/src/spiffs_hydrogen.c @@ -0,0 +1,484 @@ +/* + * spiffs_hydrogen.c + * + * Created on: Jun 16, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); + +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_attr attr) { + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (u8_t*)path, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +} + +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_attr attr, spiffs_mode mode) { + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + + s32_t res = spiffs_fd_find_new(fs, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix); + if ((mode & SPIFFS_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((mode & SPIFFS_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { + spiffs_obj_id obj_id; + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (u8_t*)path, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + mode &= ~SPIFFS_TRUNC; + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, attr, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + if (mode & SPIFFS_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return fd->file_nbr; +} + +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + } else { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return len; +} + +static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN(fd->size - offset, len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + buf += m_len; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + +} + +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + u32_t offset; + + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + offset = fd->fdoffset; + +#if SPIFFS_CACHE_WR + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } +#endif + if (fd->mode & SPIFFS_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; + } else { + offset = fd->size; + } +#if SPIFFS_CACHE_WR + if (fd->cache_page) { + offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); + } +#endif + } + + SPIFFS_DBG("SPIFFS_write %i %04x offs:%i len %i\n", fh, fd->obj_id, offset, len); + +#if SPIFFS_CACHE_WR + if ((fd->mode & SPIFFS_DIRECT) == 0) { + if (len < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->offset || // writing before cache + offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache + offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page + { + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:&04x, boundary viol, offs:%i size:%i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + get_cache_page(fs, get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fh_release(fs, fd->cache_page); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->offset = offset; + fd->cache_page->size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %i for fd %i:%04x\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %i for fd %i:%04x, offs %i:%i len %i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = get_cache(fs); + u8_t *cpage_data = get_cache_page(fs, cache, fd->cache_page->ix); + memcpy(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, big write, offs:%i size:%i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + get_cache_page(fs, get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fh_release(fs, fd->cache_page); + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES(fs, res); + } + } + } +#endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset+offs; + break; + case SPIFFS_SEEK_END: + offs = (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size) + offs; + break; + } + + if (offs > fd->size) { + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_index( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +s32_t SPIFFS_remove(spiffs *fs, const char *path) { + SPIFFS_LOCK(fs); + + spiffs_fd fd; + spiffs_page_ix pix; + s32_t res; + fd.file_nbr = 0; + fd.fs = fs; + res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_open_by_page(fs, pix, &fd, 0,0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_truncate(&fd, 0, 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; +} + +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_cache_fh_release(fs, fd->cache_page); +#endif + + res = spiffs_object_truncate(fd, 0, 1); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return 0; +} + +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = spiffs_phys_rd(fs, +#if SPIFFS_CACHE + SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, +#endif + SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + s->obj_id = objix_hdr.p_hdr.obj_id; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); + + SPIFFS_UNLOCK(fs); + + return res; +} + +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + s32_t res = SPIFFS_OK; +#if SPIFFS_CACHE_WR + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + + if ((fd->mode & SPIFFS_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, flush, offs:%i size:%i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + get_cache_page(fs, get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fh_release(fs, fd->cache_page); + } + } +#endif + + return res; +} + +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + s32_t res = SPIFFS_OK; +#if SPIFFS_CACHE_WR + SPIFFS_LOCK(fs); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs,res); + SPIFFS_UNLOCK(fs); +#endif + + return res; +} + +void SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_LOCK(fs); + +#if SPIFFS_CACHE + spiffs_fflush_cache(fs, fh); +#endif + spiffs_fd_return(fs, fh); + + SPIFFS_UNLOCK(fs); +} + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; +} + +static s32_t spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_ERASED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((objix_hdr.p_hdr.obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL)) == SPIFFS_PH_FLAG_DELET) { + struct spiffs_dirent *e = (struct spiffs_dirent *)user_p; + e->obj_id = objix_hdr.p_hdr.obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + return SPIFFS_OK; + } + + return SPIFFS_COUNTINUE; +} + +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + SPIFFS_LOCK(fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + ret = e; + } else { + d->fs->errno = res; + } + SPIFFS_UNLOCK(fs); + return ret; +} + +s32_t SPIFFS_closedir(spiffs_DIR *d) { + return SPIFFS_OK; +} + diff --git a/src/spiffs_nucleus.c b/src/spiffs_nucleus.c new file mode 100644 index 0000000..7a5496a --- /dev/null +++ b/src/spiffs_nucleus.c @@ -0,0 +1,2063 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;\ + if (((ph).obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != ((objid) & ~SPIFFS_OBJ_ID_IX_FLAG)) return SPIFFS_ERR_INDEX_WRONG_ID; + +#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; \ + if (((ph).obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != ((objid) & ~SPIFFS_OBJ_ID_IX_FLAG)) return SPIFFS_ERR_DATA_WRONG_ID; + +static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id, spix); +#endif + return res; +} + +static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); +#endif + return res; +} + +s32_t SPIFFS_init(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size) { + SPIFFS_LOCK(fs); + memset(fs, 0, sizeof(spiffs)); + memcpy(&fs->cfg, config, sizeof(spiffs_config)); + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary, below is safe + u8_t ptr_size = sizeof(void*); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-to-int-cast" + u8_t addr_lsb = ((u8_t)fd_space) & (ptr_size-1); +#pragma GCC diagnostic pop + if (addr_lsb) { + fd_space += (ptr_size-addr_lsb); + fd_space_size -= (ptr_size-addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary, below is safe +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-to-int-cast" + addr_lsb = ((u8_t)cache) & (ptr_size-1); +#pragma GCC diagnostic pop + if (addr_lsb) { + cache += (ptr_size-addr_lsb); + cache_size -= (ptr_size-addr_lsb); + } + if (cache_size & (ptr_size-1)) { + cache_size -= (cache_size & (ptr_size-1)); + } +#if SPIFFS_CACHE + fs->cache = cache; + fs->cache_size = cache_size; + spiffs_cache_init(fs); +#endif + + s32_t res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count); + SPIFFS_DBG("free blocks: %i\n", fs->free_blocks); + + SPIFFS_UNLOCK(fs); + + return 0; +} + +s32_t SPIFFS_errno(spiffs *fs) { + return fs->errno; +} + +#if !SPIFFS_CACHE + +s32_t spiffs_phys_rd( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *dst) { + return fs->cfg.hal_read_f(addr, len, dst); +} + +s32_t spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *src) { + return fs->cfg.hal_write_f(addr, len, src); +} + +#endif + +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len) { + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; +} + +// Find object lookup entry containing given id with visitor. +// Iterate over object lookup pages in each block until a given object id entry is found. +// When found, the visitor function is called with block index, entry index and user_data. +// If visitor returns SPIFFS_CONTINUE, the search goes on. Otherwise, the search will be +// ended and visitor's return code is returned to caller. +// If no visitor is given (0) the search returns on first entry with matching object id, or +// when whole area is searched without finding object id. +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_index_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + u32_t user_data, + void *user_p, + spiffs_block_ix *block_ix, + int *index_entry) { + s32_t res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = SPIFFS_BLOCK_TO_PADDR(fs, starting_block); + + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = starting_index_entry; + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial + if (cur_entry >= SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = SPIFFS_BLOCK_TO_PADDR(fs, cur_block); + if (cur_block >= fs->block_count) { + // block wrap + cur_block = 0; + cur_block_addr = SPIFFS_CFG_PHYS_ADDR(fs); + } + } + + // check each block + while (res == SPIFFS_OK && entry_count > 0) { + int obj_lookup_page = cur_entry / entries_per_page; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages + cur_entry < SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page + { + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) { + if (block_ix) *block_ix = cur_block; + if (index_entry) *index_entry = cur_entry; + if (v) { + res = v(fs, obj_lu_buf[cur_entry-entry_offset], cur_block, cur_entry, user_data, user_p); + if (res == SPIFFS_COUNTINUE) { + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } else { + return res; + } + } + return SPIFFS_OK; + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_ERR_NOT_FOUND; + } else { + // block wrap + cur_block = 0; + cur_block_addr = SPIFFS_CFG_PHYS_ADDR(fs); + } + } + } // per block + + SPIFFS_CHECK_RES(res); + + return SPIFFS_ERR_NOT_FOUND; +} + + +static s32_t spiffs_obj_lu_scan_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + if (obj_id == SPIFFS_OBJ_ID_FREE) { + if (ix_entry == 0) { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } else if (obj_id == SPIFFS_OBJ_ID_ERASED) { + fs->stats_p_deleted++; + } else { + fs->stats_p_allocated++; + } + + return SPIFFS_COUNTINUE; +} + +// Scans thru all obj lu and counts free, deleted and used pages +s32_t spiffs_obj_lu_scan( + spiffs *fs) { + s32_t res; + spiffs_block_ix bix; + int entry; + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + SPIFFS_CHECK_RES(res); + + return res; +} + +// Find free object lookup entry +// Iterate over object lookup pages in each block until a free object id entry is found +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_index_entry, + spiffs_block_ix *block_ix, + int *index_entry) { + s32_t res; + res = spiffs_obj_lu_find_id(fs, starting_block, starting_index_entry, + SPIFFS_OBJ_ID_FREE, block_ix, index_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = *index_entry; + if (*index_entry == 0) { + fs->free_blocks--; + } + } + if (res == SPIFFS_ERR_NOT_FOUND) { + SPIFFS_DBG("fs full\n"); + } + return res == SPIFFS_ERR_NOT_FOUND ? SPIFFS_ERR_FULL : res; +} + +// Find object lookup entry containing given id +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_index_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *index_entry) { + s32_t res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_index_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, index_entry); + return res; +} + + +static s32_t spiffs_obj_lu_find_id_and_index_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.span_ix == (spiffs_span_ix)user_data && ((ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) == SPIFFS_PH_FLAG_DELET)) { + return SPIFFS_OK; + } else { + return SPIFFS_COUNTINUE; + } +} + +// Find object lookup entry containing given id and span index +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id_and_index( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_index_v, + (u32_t)spix, + 0, + &bix, + &entry); + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Allocates a free defined page with given obj_id +// Occupies object lookup entry and page +// data may be NULL; where only page header is stored, len and page_offs is ignored +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&ph->obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); + SPIFFS_CHECK_RES(res); + + // write page data + if (data) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0,SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } + + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} + +// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. +// If page data is null, provided header is used for metainfo and page data is physically copied. +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix) { + s32_t res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + if (dst_pix) *dst_pix = free_pix; + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data); + } else { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&p_hdr->obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + if (was_final) { + // mark finalized in destination page + p_hdr->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; +} + +// Deletes a page and removes it from object lookup. +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix) { + s32_t res; + spiffs_page_header hdr; + hdr.flags = 0xff & ~SPIFFS_PH_FLAG_DELET; + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_ERASED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_deleted++; + fs->stats_p_allocated--; + + // mark deleted in source page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&hdr.flags); + return res; +} + +// Create an object index header page with empty index and undefined length +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; + + res = spiffs_gc_check(fs, 0); + SPIFFS_CHECK_RES(res); + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("found free page @ %04x bix:%i entry:%i\n", SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char *)&oix_hdr.name, (char *)name, SPIFFS_OBJ_NAME_LEN); + + + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + + if (objix_hdr_pix) { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} + +// update object index header with any combination of name/size/index +// new_objix_hdr_data may be null, if so the object index header page is loaded +// name may be null, if so name is not changed +// size may be null, if so size is not changed +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + u8_t name[SPIFFS_OBJ_NAME_LEN], + u32_t size, + spiffs_page_ix *new_pix) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; + + if (new_objix_hdr_data) { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } else { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; + } + + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, objix_hdr->p_hdr.obj_id, 0); + + // change name + if (name) { + strncpy((char *)objix_hdr->name, (char *)name, SPIFFS_OBJ_NAME_LEN); + } + if (size) { + objix_hdr->size = size; + } + + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, 0, objix_hdr_pix, &new_objix_hdr_pix); + + if (res == SPIFFS_OK) { + if (new_pix) { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, objix_hdr->p_hdr.obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + + return res; +} + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_fd *fd, + int ev, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size) { + // update index caches in all file descriptors + obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + if (spix == 0) { + if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { + SPIFFS_DBG(" callback: setting fd %i:%04x objix_hdr_pix to %04x, size:%i\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) { + cur_fd->size = new_size; + } + } else if (ev == SPIFFS_EV_IX_DEL) { + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_ERASED; + } + } + if (cur_fd->cursor_objix_spix == spix) { + if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { + SPIFFS_DBG(" callback: setting fd %i:%04x span:%04x objix_pix to %04x\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } else { + cur_fd->cursor_objix_pix = 0; + } + } + } +} + +// Open object by id +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_attr attr, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; + + res = spiffs_obj_lu_find_id_and_index(fs, obj_id, 0, &pix); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_open_by_page(fs, pix, fd, attr, mode); + + return res; +} + +// Open object by page index +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_attr attr, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + SPIFFS_CHECK_RES(res); + + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = oix_hdr.p_hdr.obj_id; + fd->mode = mode; + + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); + + SPIFFS_DBG("open: fd %i is obj id %04x\n", fd->file_nbr, fd->obj_id); + + return res; +} + +// Append to object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len); + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index page, unless first pass + SPIFFS_DBG("append: %04x store objix %04x:%04x, written %i\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) { + // this is an update to object index header page + objix_hdr->size = offset+written; + if (offset == 0) { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } else { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: %04x store new objix_hdr, %04x:%04x, written %i\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } else { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, objix->p_hdr.obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: %04x store new size %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id, + offset+written, new_objix_hdr_page, 0, written); + } + fd->size = offset+written; + fd->offset = offset+written; + } + + // create or load new object index page + if (cur_objix_spix == 0) { + // load object index header page, must always exist + SPIFFS_DBG("append: %04x load objixhdr page %04x:%04x\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + // quick "load" of new object index page + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); + SPIFFS_DBG("append: %04x create objix page, %04x:%04x, written %i\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } else { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: %04x find objix span_ix:%04x\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_index(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: %04x found object index at page %04x\n", fd->obj_id, pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + fd->size = offset+written; + } + prev_objix_spix = cur_objix_spix; + } + + // write data + u32_t to_spiffs_wr = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, &p_hdr, &data[written], to_spiffs_wr, page_offs, 1, &data_page); + SPIFFS_DBG("append: %04x store new data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id, + data_page, data_spix, page_offs, to_spiffs_wr, written); + } else { + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) { + // get data page from object index header page + data_page = ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_page = ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_spiffs_wr, &data[written]); + SPIFFS_DBG("append: %04x store to existing data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id + , data_page, data_spix, page_offs, to_spiffs_wr, written); + } + + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: %04x wrote page %04x to objix_hdr entry %02x in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset+written; + } else { + // update object index page + ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: %04x wrote page %04x to objix entry %02x in mem\n", fd->obj_id + , data_page, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_spiffs_wr; + } // while all data + + fd->size = offset+written; + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: %04x store objix page, %04x:%04x, written %i\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, objix->p_hdr.obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + SPIFFS_DBG("append: %04x store new size %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id + , offset+written, new_objix_hdr_page, 0, written); + res2 = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res2); + } else { + // wrote within object index header page + if (offset == 0) { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset+written; + SPIFFS_DBG("append: %04x store fresh objix_hdr page, %04x:%04x, written %i\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, objix_hdr->p_hdr.obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } else { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: %04x store modified objix_hdr page, %04x:%04x, written %i\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } + } + + return res; +} + +// Modify object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len); + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } else { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, %04x:%04x, written %i\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, objix->p_hdr.obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page %04x:%04x\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:%04x\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_index(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page %04x\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + prev_objix_spix = cur_objix_spix; + } + + // write partial data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + orig_data_pix = ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, %04x:%04x offset:%i, len %i, written %i\n", data_pix, data_spix, page_offs, to_write, written); + } else { + // write to existing page, allocate new and copy unmodified data + + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_allocate_data(fs, &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) break; + + // copy unmodified data + if (page_offs > 0) { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) break; + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) break; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + SPIFFS_DBG("modify: store to existing data page, src:%04x, dst:%04x:%04x offset:%i, len %i, written %i\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); + if (res != SPIFFS_OK) break; + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page %04x to objix_hdr entry %02x in mem\n", data_pix, data_spix); + } else { + // update object index page + ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page %04x to objix entry %02x in mem\n", data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, %04x:%04x, written %i\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, objix->p_hdr.obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } else { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; +} + +static s32_t spiffs_object_find_object_index_header_by_name_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_ERASED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if ((objix_hdr.p_hdr.obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL)) == SPIFFS_PH_FLAG_DELET) { + if (strcmp((char *)user_p, (char *)objix_hdr.name) == 0) { + return SPIFFS_OK; + } + } + + return SPIFFS_COUNTINUE; +} + +// Finds object index header page by name +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + 0, + name, + &bix, + &entry); + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Truncates object to new size. If new size is null, object may be removed totally +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + + res = spiffs_gc_check(fs, 0); + SPIFFS_CHECK_RES(res); + + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // delete from end of object until desired len is reached + while (cur_size > new_size) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) { + if (prev_objix_spix != (spiffs_span_ix)-1) { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page %04x:%04x\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, objix->p_hdr.obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) { + // update object index header page + SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %i\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_index(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, &objix_pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("truncate: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } + + if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } else { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page %04x for data spix:%04x, cur_size:%i\n", data_pix, data_spix, cur_size); + } else { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete %i bytes from data page %04x for data spix:%04x, cur_size:%i\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) break; + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) break; + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } else { + // update object index page + ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices + if (cur_objix_spix == 0) { + // update object index header page + if (cur_size == 0) { + if (remove) { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page %04x\n", objix_pix); + + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } else { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page %04x\n", objix_pix); + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, objix_pix, fs->work, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update object index header page + SPIFFS_DBG("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, objix_pix, fs->work, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, objix->p_hdr.obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, %04x:%04x\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + + return res; +} + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + while (cur_offset < offset + len) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_index(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, &objix_pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("read: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size); + SPIFFS_DBG("read: offset:%i rd:%i data spix:%04x is data_pix:%04x addr:%08x\n", cur_offset, len_to_read, data_spix, data_pix, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + if (len_to_read <= 0) { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; +} + +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len) { + s32_t res; + u32_t free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count + - fs->stats_p_allocated - fs->stats_p_deleted; + int tries = 0; + + if (fs->free_blocks > 2 && + len < free_pages * SPIFFS_DATA_PAGE_SIZE(fs)) { + return SPIFFS_OK; + } + + do { + SPIFFS_GC_DBG("\ngc_check #i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n", + tries, + fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), + len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs)); + + spiffs_block_ix *cands; + int count; + spiffs_block_ix cand; + res = spiffs_gc_find_candidate(fs, &cands, &count); + SPIFFS_CHECK_RES(res); + if (count == 0) { + SPIFFS_GC_DBG("gc_check: no candidates, return\n"); + return res; + } +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + cand = cands[0]; + res = spiffs_gc_clean(fs, cand); + SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res); + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_page_stats(fs, cand); + SPIFFS_CHECK_RES(res); + + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, cand); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + // here we ignore res, just try erasing the block + SPIFFS_GC_DBG("gc_check: erase block %i\n", cand); + while (size > 0) { + SPIFFS_DBG("gc_check: erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + res = fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count + - fs->stats_p_allocated - fs->stats_p_deleted; +#if SPIFFS_CACHE + { + int i; + for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { + spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, cand) + i); + } + } +#endif + } while (tries++ < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || + len > free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); + SPIFFS_GC_DBG("gc_check: finished\n"); + + return res; +} + +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + int obj_lookup_page = 0; + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + u32_t dele = 0; + u32_t allo = 0; + + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + } else if (obj_id == SPIFFS_OBJ_ID_ERASED) { + dele++; + } else { + allo++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele); + fs->stats_p_allocated -= allo; + fs->stats_p_deleted -= dele; + return res; +} + +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidates, + int *candidate_count) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = SPIFFS_CFG_PHYS_ADDR(fs); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + + // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(int)score + int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(int))); + *candidate_count = 0; + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + // divide up work area into block indices and scores + // todo alignment? + spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; + int *cand_scores = (int *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + + *block_candidates = cand_blocks; + + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t used_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + // when a free entry is encountered, scan logic ensures that all following entries are free also + break; + } else if (obj_id == SPIFFS_OBJ_ID_ERASED) { + deleted_pages_in_block++; + } else { + used_pages_in_block++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + + // calculate score and insert into candidate table + // stoneage sort, but probably not so many blocks + if (res == SPIFFS_OK && deleted_pages_in_block > 0) { + int score = deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + used_pages_in_block * SPIFFS_GC_HEUR_W_USED; + int cand_ix = 0; + SPIFFS_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + while (cand_ix < max_candidates) { + if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } else if (cand_scores[cand_ix] < score) { + int reorder_cand_ix = max_candidates - 2; + while (reorder_cand_ix >= cand_ix) { + cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; + cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; + reorder_cand_ix--; + } + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } + cand_ix++; + } + (*candidate_count)++; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + return res; +} + +typedef enum { + MOVE_OBJ_IX, + FIND_OBJ_DATA, + MOVE_OBJ_DATA, + FINISHED +} spiffs_gc_clean_state; + +typedef struct { + spiffs_gc_clean_state state; + spiffs_obj_id cur_obj_id; + spiffs_span_ix cur_objix_spix; + spiffs_page_ix cur_objix_pix; + int stored_scan_entry_index; + u8_t obj_id_found; +} spiffs_gc; + +// Empties given block by moving all data into free pages of another block +// Strategy: +// scan object lookup for object index pages, move to new page in other block +// loop: +// scan object lookup for object data pages +// for first found id, check spix and load corresponding object index page to memory +// push object scan lookup entry index +// rescan object lookup, find data pages with same id and referenced by same object index +// move data page, update object index in memory +// when reached end of lookup, store updated object index +// pop object scan lookup entry index +// repeat loop until end of object lookup +// +s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_gc gc; + spiffs_page_ix cur_pix = 0; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + memset(&gc, 0, sizeof(spiffs_gc)); + gc.state = MOVE_OBJ_IX; + + if (fs->free_cursor_block_ix == bix) { + // move free cursor to next block + fs->free_cursor_block_ix = (bix+1)%fs->block_count; + fs->free_cursor_obj_lu_entry = 0; + SPIFFS_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix); + } + + while (res == SPIFFS_OK && gc.state != FINISHED) { + SPIFFS_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry); + gc.obj_id_found = 0; + + // scan through lookup pages + int obj_lookup_page = cur_entry / entries_per_page; + u8_t scan = 1; + // check each object lookup page + while (scan && res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (scan && res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); + + // act upon object id depending on gc state + switch (gc.state) { + case MOVE_OBJ_IX: + if (obj_id != SPIFFS_OBJ_ID_ERASED && obj_id != SPIFFS_OBJ_ID_FREE && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + // found an index object id + spiffs_page_header p_hdr; + spiffs_page_ix new_pix; + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, &p_hdr, cur_pix, &new_pix); + SPIFFS_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it + SPIFFS_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + if (res == SPIFFS_OK) { + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); + } + } + SPIFFS_CHECK_RES(res); + } + break; + case FIND_OBJ_DATA: + if (obj_id != SPIFFS_OBJ_ID_ERASED && obj_id != SPIFFS_OBJ_ID_FREE) { + SPIFFS_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id); + gc.obj_id_found = 1; + gc.cur_obj_id = obj_id; + scan = 0; + } + break; + case MOVE_OBJ_DATA: + if (obj_id == gc.cur_obj_id) { + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { + SPIFFS_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + } else { + spiffs_page_ix new_data_pix; + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, &p_hdr, cur_pix, &new_data_pix); + SPIFFS_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); + SPIFFS_CHECK_RES(res); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it + SPIFFS_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + new_data_pix = SPIFFS_OBJ_ID_FREE; + } + // update memory representation of object index page with new data page + if (gc.cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; + SPIFFS_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } else { + // update object index page + ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; + SPIFFS_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } + } + } + break; + default: + scan = 0; + break; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + + if (res != SPIFFS_OK) break; + + // state finalization and switch + switch (gc.state) { + case MOVE_OBJ_IX: + gc.state = FIND_OBJ_DATA; + cur_entry = 0; // restart entry scan index + break; + case FIND_OBJ_DATA: + if (gc.obj_id_found) { + // find out corresponding obj ix page and load it to memory + spiffs_page_header p_hdr; + spiffs_page_ix objix_pix; + gc.stored_scan_entry_index = cur_entry; + cur_entry = 0; + gc.state = MOVE_OBJ_DATA; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); + SPIFFS_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix); + res = spiffs_obj_lu_find_id_and_index(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, &objix_pix); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id, gc.cur_objix_spix); + gc.cur_objix_pix = objix_pix; + } else { + gc.state = FINISHED; + } + break; + case MOVE_OBJ_DATA: { + // store modified objix (hdr) page + spiffs_page_ix new_objix_pix; + gc.state = FIND_OBJ_DATA; + cur_entry = gc.stored_scan_entry_index; + if (gc.cur_objix_spix == 0) { + // store object index header page + res = spiffs_object_update_index_hdr(fs, 0, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix); + SPIFFS_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0); + SPIFFS_CHECK_RES(res); + } else { + // store object index page + spiffs_page_ix new_objix_pix; + res = spiffs_page_move(fs, 0, fs->work, 0, gc.cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + break; + default: + cur_entry = 0; + break; + } + SPIFFS_DBG("gc_clean: state-> %i\n", gc.state); + } // while state != FINISHED + + + return res; +} + +typedef struct { + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; +} spiffs_free_obj_id_state; + +static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + u32_t user_data, void *user_p) { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_ERASED) { + spiffs_obj_id min_obj_id = user_data; + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + int bit_ix = (id-min_obj_id) & 7; + int byte_ix = (id-min_obj_id) >> 3; + if (byte_ix >= 0 && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + fs->work[byte_ix] |= (1<= state->min_obj_id && id <= state->max_obj_id) { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix %i for id %04x min:%04x max%04x comp:%i\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } + } + } + return SPIFFS_COUNTINUE; +} + +s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id) { + s32_t res = SPIFFS_OK; + u32_t max_objects = (SPIFFS_CFG_PHYS_SZ(fs) / (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG; + } + state.compaction = 0; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { + if (state.max_obj_id - state.min_obj_id <= SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { + // possible to represent in bitmap + int i, j; + SPIFFS_DBG("free_obj_id: BITM min:%04x max:%04x\n", state.min_obj_id, state.max_obj_id); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id, 0, 0, 0); + if (res == SPIFFS_ERR_NOT_FOUND) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { + u8_t mask = fs->work[i]; + if (mask == 0xff) { + continue; + } + for (j = 0; j < 8; j++) { + if ((mask & (1<work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) { + if (map[i] < min_count) { + min_count = map[i]; + min_i = i; + if (min_count == 0) { + break; + } + } + } + + if (min_count == state.compaction) { + // there are no free objids! + SPIFFS_DBG("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:%i min_count:%i min:%04x max:%04x compact:%i\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } else { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:%04x min:%04x -> %04x\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:%04x max:%04x compact:%i\n", state.min_obj_id, state.max_obj_id, state.compaction); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, 0, &state, 0, 0); + if (res == SPIFFS_ERR_NOT_FOUND) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + } + } + + return res; +} + +s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd) { + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + cur_fd->file_nbr = i+1; + *fd = cur_fd; + return SPIFFS_OK; + } + } + return SPIFFS_ERR_OUT_OF_FILE_DESCS; +} + +s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { + if (f <= 0 || f > fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f-1]; + if (fd->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; + return SPIFFS_OK; +} + +s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { + if (f <= 0 || f > fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f-1]; + if ((*fd)->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; +} diff --git a/src/spiffs_nucleus.h b/src/spiffs_nucleus.h new file mode 100644 index 0000000..bd5b9c9 --- /dev/null +++ b/src/spiffs_nucleus.h @@ -0,0 +1,584 @@ +/* + * spiffs_nucleus.h + * + * Created on: Jun 15, 2013 + * Author: petera + */ + +/* SPIFFS layout + * + * spiffs is designed for following spi flash characteristics: + * - only big areas of data (blocks) can be erased + * - erasing resets all bits in a block to ones + * - writing pulls ones to zeroes + * - zeroes cannot be pulled to ones, without erase + * - wear leveling + * + * spiffs is also meant to be run on embedded, memory constraint devices. + * + * Entire area is divided in blocks. Entire area is also divided in pages. + * Each block contains same number of pages. A page cannot be erased, but a + * block can be erased. + * + * Entire area must be block_size * x + * page_size must be block_size / (2^y) where y > 2 + * + * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes + * + * BLOCK 0 PAGE 0 object lookup 1 + * PAGE 1 object lookup 2 + * ... + * PAGE n-1 object lookup n + * PAGE n object data 1 + * PAGE n+1 object data 2 + * ... + * PAGE n+m-1 object data m + * + * BLOCK 1 PAGE n+m object lookup 1 + * PAGE n+m+1 object lookup 2 + * ... + * PAGE 2n+m-1 object lookup n + * PAGE 2n+m object data 1 + * PAGE 2n+m object data 2 + * ... + * PAGE 2n+2m-1 object data m + * ... + * + * n is number of object lookup pages, which is number of pages needed to index all pages + * in a block by object id + * : block_size / page_size * sizeof(obj_id) / page_size + * m is number data pages, which is number of pages in block minus number of lookup pages + * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size + * thus, n+m is total number of pages in a block + * : block_size / page_size + * + * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 + * + * Object lookup pages contain object id entries. Each entry represent the corresponding + * data page. + * Assuming a 16 bit object id, an object id being 0xffff represents a free page. + * An object id being 0x0000 represents a deleted page. + * + * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. + * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. + * page 2 : data : data for object id 0008 + * page 3 : data : data for object id 0001 + * page 4 : data : data for object id 0aaa + * ... + * + * + * Object data pages can be either object index pages or object content. + * All object data pages contains a data page header, containing object id and span index. + * The span index denotes the object page ordering amongst data pages with same object id. + * This applies to both object index pages (when index spans more than one page of entries), + * and object data pages. + * An object index page contains page entries pointing to object content page. The entry index + * in a object index page correlates to the span index in the actual object data page. + * The first object index page (span index 0) is called object index header page, and also + * contains object flags (directory/file), size, object name etc. + * + * ex: + * BLOCK 1 + * PAGE 256: objectl lookup page 1 + * [*123] [ 123] [ 123] [ 123] + * [ 123] [*123] [ 123] [ 123] + * [free] [free] [free] [free] ... + * PAGE 257: objectl lookup page 2 + * [free] [free] [free] [free] ... + * PAGE 258: object index page (header) + * obj.id:0123 span.ix:0000 flags:INDEX + * size:1600 name:ex.txt type:file + * [259] [260] [261] [262] + * PAGE 259: object data page + * obj.id:0123 span.ix:0000 flags:DATA + * PAGE 260: object data page + * obj.id:0123 span.ix:0001 flags:DATA + * PAGE 261: object data page + * obj.id:0123 span.ix:0002 flags:DATA + * PAGE 262: object data page + * obj.id:0123 span.ix:0003 flags:DATA + * PAGE 263: object index page + * obj.id:0123 span.ix:0001 flags:INDEX + * [264] [265] [fre] [fre] + * [fre] [fre] [fre] [fre] + * PAGE 264: object data page + * obj.id:0123 span.ix:0004 flags:DATA + * PAGE 265: object data page + * obj.id:0123 span.ix:0005 flags:DATA + * + */ +#ifndef SPIFFS_NUCLEUS_H_ +#define SPIFFS_NUCLEUS_H_ + +#define SPIFFS_EV_IX_UPD 0 +#define SPIFFS_EV_IX_NEW 1 +#define SPIFFS_EV_IX_DEL 2 + +#define SPIFFS_OBJ_ID_IX_FLAG (1<<(8*sizeof(spiffs_obj_id)-1)) + +#define SPIFFS_UNDEFINED_LEN (-1) + +#define SPIFFS_OBJ_ID_ERASED ((spiffs_obj_id)0) +#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) + +#ifndef SPIFFS_SINGLETON +#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ + ((fs)->cfg.log_page_size) +#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ + ((fs)->cfg.log_block_size) +#define SPIFFS_CFG_PHYS_SZ(fs) \ + ((fs)->cfg.phys_size) +#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ + ((fs)->cfg.phys_erase_block) +#define SPIFFS_CFG_PHYS_ADDR(fs) \ + ((fs)->cfg.phys_addr) +#endif + +// total number of pages per block, including object lookup pages +#define SPIFFS_PAGES_PER_BLOCK(fs) \ + ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// number of object lookup pages per block +#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ + (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) +// checks if page index belongs to object lookup +#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ + (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// number of object lookup entries in all object lookup pages +#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ + (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// converts a block to physical address +#define SPIFFS_BLOCK_TO_PADDR(fs, block) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) +// converts a object lookup entry to page index +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ + ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) +// converts a object lookup entry to physical address of corresponding page +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ + (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a page to physical address +#define SPIFFS_PAGE_TO_PADDR(fs, page) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a physical address to page +#define SPIFFS_PADDR_TO_PAGE(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// gives index in page for a physical address +#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// returns containing block for given page +#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ + ((page) / SPIFFS_PAGES_PER_BLOCK(fs)) +// returns starting page for block +#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ + ((block) * SPIFFS_PAGES_PER_BLOCK(fs)) +// converts page to entry in object lookup page +#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ + ((page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// returns data size in a data page +#define SPIFFS_DATA_PAGE_SIZE(fs) \ + ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) + +// define helpers object + +// entries in an object header page index +#define SPIFFS_OBJ_HDR_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) +// entries in an object page index +#define SPIFFS_OBJ_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) +// object index entry for given data span index +#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) +// object index span index number for given data span index or entry +#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) + + +#define SPIFFS_OP_T_OBJ_LU (0<<0) +#define SPIFFS_OP_T_OBJ_LU2 (1<<0) +#define SPIFFS_OP_T_OBJ_IX (2<<0) +#define SPIFFS_OP_T_OBJ_DA (3<<0) +#define SPIFFS_OP_C_DELE (0<<2) +#define SPIFFS_OP_C_UPDT (1<<2) +#define SPIFFS_OP_C_MOVS (2<<2) +#define SPIFFS_OP_C_MOVD (3<<2) +#define SPIFFS_OP_C_FLSH (4<<2) +#define SPIFFS_OP_C_READ (5<<2) + +#define SPIFFS_OP_TYPE_MASK (3<<0) +#define SPIFFS_OP_COM_MASK (7<<2) + + +// if 0, writing is finalized, else under modification +#define SPIFFS_PH_FLAG_FINAL (1<<0) +// if 0, page is deleted, else valid +#define SPIFFS_PH_FLAG_DELET (1<<1) +// if 0, page is corrupt, else ok +#define SPIFFS_PH_FLAG_CORRU (1<<2) +// if 0, this is an index page, else a data page +#define SPIFFS_PH_FLAG_INDEX (1<<3) + + +#define SPIFFS_CHECK_RES(res) \ + do { \ + if ((res) < SPIFFS_OK) return (res); \ + } while (0); + +#define SPIFFS_API_CHECK_RES(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->errno = (res); \ + return -1; \ + } + +#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->errno = (res); \ + SPIFFS_UNLOCK(fs); \ + return -1; \ + } + +#define SPIFFS_VIS_CHECK_ID (1<<0) +#define SPIFFS_VIS_NO_WRAP (1<<1) + +#if SPIFFS_CACHE + +#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) +#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) +#define SPIFFS_CACHE_FLAG_OBJLU (1<<2) +#define SPIFFS_CACHE_FLAG_OBJIX (1<<3) +#define SPIFFS_CACHE_FLAG_DATA (1<<4) +#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) + +#define SPIFFS_CACHE_PAGE_SIZE(fs) \ + (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) + +#define get_cache(fs) \ + ((spiffs_cache *)((fs)->cache)) + +#define get_cache_page_hdr(fs, c, ix) \ + ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) + +#define get_cache_page(fs, c, ix) \ + ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) + +typedef struct { + u8_t flags; + u8_t ix; + u32_t last_access; + union { + struct { + // type read cache + spiffs_page_ix pix; + }; +#if SPIFFS_CACHE_WR + struct { + // type write cache + spiffs_obj_id obj_id; + u8_t refs; + u32_t offset; + u16_t size; + }; +#endif + }; +} spiffs_cache_page; + +typedef struct { + u8_t cpage_count; + u32_t last_access; + u32_t cpage_use_map; + u32_t cpage_use_mask; + u8_t *cpages; +} spiffs_cache; + +#endif + + +// spiffs nucleus file descriptor +typedef struct { + // the filesystem of this descriptor + spiffs *fs; + // number of file descriptor - if 0, the file descriptor is closed + spiffs_file file_nbr; + // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted + spiffs_obj_id obj_id; + // size of the file + u32_t size; + // cached object index header page index + spiffs_page_ix objix_hdr_pix; + // cached offset object index page index + spiffs_page_ix cursor_objix_pix; + // cached offset object index span index + spiffs_span_ix cursor_objix_spix; + // current absolute offset + u32_t offset; + // current file descriptor offset + u32_t fdoffset; + // mode + spiffs_mode mode; +#if SPIFFS_CACHE_WR + spiffs_cache_page *cache_page; +#endif +} spiffs_fd; + + +// object structs + +// page header, part of each page except object lookup pages +typedef struct __attribute(( packed )) { + // object id + spiffs_obj_id obj_id; + // object span index + spiffs_span_ix span_ix; + // flags + u8_t flags; +} spiffs_page_header; + +// object index header page header +typedef struct __attribute(( packed )) { + // common page header + spiffs_page_header p_hdr; + // alignment + u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + // size of object + u32_t size; + // type of object + spiffs_obj_type type; + // name of object + u8_t name[SPIFFS_OBJ_NAME_LEN]; +} spiffs_page_object_ix_header; + +// object index page header +typedef struct __attribute(( packed )) { + spiffs_page_header p_hdr; + u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; +} spiffs_page_object_ix; + +// callback func for object lookup visitor + +typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + u32_t user_data, void *user_p); + + +#if SPIFFS_CACHE +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) +#else +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (addr), (len), (src)) +#endif +// --------------- + +s32_t spiffs_phys_rd( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *dst); + +s32_t spiffs_phys_wr( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *src); + +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len); + +s32_t spiffs_phys_count_free_blocks( + spiffs *fs); + +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_index_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + u32_t user_data, + void *user_p, + spiffs_block_ix *block_ix, + int *index_entry); + +// --------------- + +s32_t spiffs_obj_lu_scan( + spiffs *fs); + +s32_t spiffs_obj_lu_find_free_obj_id( + spiffs *fs, + spiffs_obj_id *obj_id); + +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_index_entry, + spiffs_block_ix *block_ix, + int *index_entry); + +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_index_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *index_entry); + +s32_t spiffs_obj_lu_find_id_and_index( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix); + +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix); + +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix); + +// --------------- + +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix); + +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + u8_t name[SPIFFS_OBJ_NAME_LEN], + u32_t size, + spiffs_page_ix *new_pix); + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_fd *fd, + int ev, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size); + +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *f, + spiffs_attr attr, + spiffs_mode mode); + +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *f, + spiffs_attr attr, + spiffs_mode mode); + +s32_t spiffs_object_append( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_modify( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst); + +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_len, + u8_t remove_object); + +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len); + +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidate, + int *candidate_count); + +s32_t spiffs_gc_clean( + spiffs *fs, + spiffs_block_ix bix); + +// --------------- + +s32_t spiffs_fd_find_new( + spiffs *fs, + spiffs_fd **fd); + +s32_t spiffs_fd_return( + spiffs *fs, + spiffs_file f); + +s32_t spiffs_fd_get( + spiffs *fs, + spiffs_file f, + spiffs_fd **fd); + +#if SPIFFS_CACHE +void spiffs_cache_init(spiffs *fs); +void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix); +#if SPIFFS_CACHE_WR +spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd); +void spiffs_cache_fh_release(spiffs *fs, spiffs_cache_page *cp); +spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd); +#endif +#endif + +void spiffs_test_list_objects(spiffs *fs); + +#endif /* SPIFFS_NUCLEUS_H_ */ diff --git a/src/stypes.h b/src/stypes.h new file mode 100644 index 0000000..f3656d8 --- /dev/null +++ b/src/stypes.h @@ -0,0 +1,28 @@ +/* + * stypes.h + * + * Created on: May 26, 2013 + * Author: petera + */ + +#ifndef STYPES_H_ +#define STYPES_H_ + +#define print(...) printf(__VA_ARGS__) + +#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__); + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +void real_assert(int c, const char *n, const char *file, int l); + +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + + +#endif /* STYPES_H_ */ diff --git a/src/test_hydrogen.c b/src/test_hydrogen.c new file mode 100644 index 0000000..44eb5df --- /dev/null +++ b/src/test_hydrogen.c @@ -0,0 +1,1514 @@ +/* + * test_suites.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + +//#define REMOVE_LONG_TESTS + +static void test_on_stop(test *t) { + printf(" spiffs errno:%i\n", SPIFFS_errno(&__fs)); +} + +#define CHECK(r) if (!(r)) return -1; +#define CHECK_RES(r) if (r < 0) return -1; +#define FS_PURE_DATA_PAGES(fs) \ + ((fs)->cfg.phys_size / (fs)->cfg.log_page_size - (fs)->block_count * SPIFFS_OBJ_LOOKUP_PAGES(fs)) +#define FS_PURE_DATA_SIZE(fs) \ + FS_PURE_DATA_PAGES(fs) * SPIFFS_DATA_PAGE_SIZE(fs) + +void memrand(u8_t *b, int len) { + int i; + for (i = 0; i < len; i++) { + b[i] = rand(); + } +} + +int test_create_file(char *name) { + spiffs_stat s; + spiffs_file fd; + int res = SPIFFS_creat(FS, name, 0); + CHECK_RES(res); + fd = SPIFFS_open(FS, name, 0, SPIFFS_RDONLY); + CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + CHECK_RES(res); + CHECK(strcmp((char*)s.name, name) == 0); + CHECK(s.size == 0); + SPIFFS_close(FS, fd); + return 0; +} + +int test_create_and_write_file(char *name, int size, int chunk_size) { + int res; + spiffs_file fd; + printf(" create and write %s", name); + res = test_create_file(name); + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, 0, SPIFFS_APPEND | SPIFFS_RDWR); + CHECK(fd >= 0); + int pfd = open(make_test_fname(name), O_APPEND | O_TRUNC | O_CREAT | O_RDWR); + int offset = 0; + int mark = 0; + while (offset < size) { + int len = MIN(size-offset, chunk_size); + if (offset > mark) { + mark += size/16; + printf("."); + fflush(stdout); + } + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + write(pfd, buf, len); + free(buf); + if (res < 0) { + printf("\n error @ offset %i, res %i\n", offset, res); + } + offset += len; + CHECK(res >= 0); + } + printf("\n"); + close(pfd); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + CHECK(res >= 0); + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + return 0; +} + +#if SPIFFS_CACHE +#if SPIFFS_CACHE_STATS +static u32_t chits_tot = 0; +static u32_t cmiss_tot = 0; +#endif +#endif + +void _setup() { + fs_reset(); + test_init(test_on_stop); +} + +void _teardown() { + clear_test_path(); + printf(" free blocks : %i of %i\n", (FS)->free_blocks, (FS)->block_count); + printf(" pages allocated : %i\n", (FS)->stats_p_allocated); + printf(" pages deleted : %i\n", (FS)->stats_p_deleted); +#if SPIFFS_GC_STATS + printf(" gc runs : %i\n", (FS)->stats_gc_runs); +#endif +#if SPIFFS_CACHE +#if SPIFFS_CACHE_STATS + chits_tot += (FS)->cache_hits; + cmiss_tot += (FS)->cache_misses; + printf(" cache hits : %i (sum %i)\n", (FS)->cache_hits, chits_tot); + printf(" cache misses : %i (sum %i)\n", (FS)->cache_misses, cmiss_tot); + printf(" cache utiliz : %f\n", ((float)chits_tot/(float)(chits_tot + cmiss_tot))); +#endif +#endif + + +} + +typedef enum { + EMPTY, + SMALL, + MEDIUM, + LARGE, +} tfile_size; + +typedef enum { + UNTAMPERED, + APPENDED, + MODIFIED, + REWRITTEN, +} tfile_type; + +typedef enum { + SHORT = 4, + NORMAL = 20, + LONG = 100, +} tfile_life; + +typedef struct { + tfile_size tsize; + tfile_type ttype; + tfile_life tlife; +} tfile_conf; + +typedef struct { + int state; + spiffs_file fd; + tfile_conf cfg; + char name[32]; +} tfile; + +u32_t tfile_get_size(tfile_size s) { + switch (s) { + case EMPTY: + return 0; + case SMALL: + return SPIFFS_DATA_PAGE_SIZE(FS)/2; + case MEDIUM: + return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS)); + case LARGE: + return (FS)->cfg.phys_size/3; + } + return 0; +} + +int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files) { + int res; + tfile *tfiles = malloc(sizeof(tfile) * max_concurrent_files); + memset(tfiles, 0, sizeof(tfile) * max_concurrent_files); + int run = 0; + int cur_config_ix = 0; + char name[32]; + while (run < max_runs) { + printf(" run %i/%i\n", run, max_runs); + int i; + for (i = 0; i < max_concurrent_files; i++) { + sprintf(name, "file%i_%i", (1+run), i); + tfile *tf = &tfiles[i]; + if (tf->state == 0 && cur_config_ix < cfg_count) { +// create a new file + strcpy(tf->name, name); + printf(" create new %s with cfg %i/%i\n", name, (1+cur_config_ix), cfg_count); + tf->state = 1; + tf->cfg = cfgs[cur_config_ix]; + if (tf->cfg.tsize == EMPTY) { + res = SPIFFS_creat(FS, name, 0); + CHECK_RES(res); + int pfd = open(make_test_fname(name), O_TRUNC | O_CREAT | O_RDWR); + close(pfd); + int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0; + spiffs_file fd = SPIFFS_open(FS, name, 0, extra_flags | SPIFFS_RDWR); + CHECK(fd > 0); + tf->fd = fd; + } else { + int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0; + spiffs_file fd = SPIFFS_open(FS, name, 0, extra_flags | SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR); + CHECK(fd > 0); + extra_flags = tf->cfg.ttype == APPENDED ? O_APPEND : 0; + int pfd = open(make_test_fname(name), extra_flags | O_TRUNC | O_CREAT | O_RDWR); + tf->fd = fd; + int size = tfile_get_size(tf->cfg.tsize); + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, fd, buf, size); + CHECK_RES(res); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(name); + CHECK_RES(res); + } + + cur_config_ix++; + } else if (tf->state > 0) { +// hande file lifecycle + switch (tf->cfg.ttype) { + case UNTAMPERED: { + break; + } + case APPENDED: { + printf(" appending %s\n", tf->name); + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, tf->fd, buf, size); + CHECK_RES(res); + int pfd = open(make_test_fname(tf->name), O_APPEND | O_RDWR); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + case MODIFIED: { + printf(" modify %s\n", tf->name); + spiffs_stat stat; + res = SPIFFS_fstat(FS, tf->fd, &stat); + CHECK_RES(res); + int size = stat.size / tf->cfg.tlife + SPIFFS_DATA_PAGE_SIZE(FS)/3; + int offs = (stat.size / tf->cfg.tlife) * tf->state; + res = SPIFFS_lseek(FS, tf->fd, offs, SPIFFS_SEEK_SET); + CHECK_RES(res); + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, tf->fd, buf, size); + CHECK_RES(res); + int pfd = open(make_test_fname(tf->name), O_RDWR); + lseek(pfd, offs, SEEK_SET); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + case REWRITTEN: { + if (tf->fd > 0) { + SPIFFS_close(FS, tf->fd); + } + printf(" rewriting %s\n", tf->name); + spiffs_file fd = SPIFFS_open(FS, tf->name, 0, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR); + CHECK(fd > 0); + int pfd = open(make_test_fname(tf->name), O_TRUNC | O_CREAT | O_RDWR); + tf->fd = fd; + int size = tfile_get_size(tf->cfg.tsize); + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, fd, buf, size); + CHECK_RES(res); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + } + tf->state++; + if (tf->state > tf->cfg.tlife) { +// file outlived its time, kill it + if (tf->fd > 0) { + SPIFFS_close(FS, tf->fd); + } + printf(" removing %s\n", tf->name); + res = read_and_verify(tf->name); + CHECK_RES(res); + res = SPIFFS_remove(FS, tf->name); + CHECK_RES(res); + remove(make_test_fname(tf->name)); + memset(tf, 0, sizeof(tf)); + } + + } + } + + run++; + } + free(tfiles); + return 0; +} + + + + +SUITE(hydrogen_tests) +void setup() { + _setup(); +} +void teardown() { + _teardown(); +} + + +TEST(missing_file) +{ + spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", 0, 0); + TEST_CHECK(fd < 0); + return TEST_RES_OK; +} +TEST_END(missing_file) + + +TEST(bad_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", 0, 0); + TEST_CHECK(fd < 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + return TEST_RES_OK; +} +TEST_END(bad_fd) + + +TEST(closed_fd) +{ + int res; + spiffs_stat s; + res = test_create_file("file"); + spiffs_file fd = SPIFFS_open(FS, "file", 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + SPIFFS_close(FS, fd); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + return TEST_RES_OK; +} +TEST_END(closed_fd) + + +TEST(deleted_same_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd; + res = test_create_file("remove"); + fd = SPIFFS_open(FS, "remove", 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + fd = SPIFFS_open(FS, "remove", 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + + return TEST_RES_OK; +} +TEST_END(deleted_same_fd) + + +TEST(deleted_other_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd, fd_orig; + res = test_create_file("remove"); + fd_orig = SPIFFS_open(FS, "remove", 0, SPIFFS_RDONLY); + TEST_CHECK(fd_orig >= 0); + fd = SPIFFS_open(FS, "remove", 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd_orig); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd_orig); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + + return TEST_RES_OK; +} +TEST_END(deleted_other_fd) + + +TEST(file_by_open) +{ + int res; + spiffs_stat s; + spiffs_file fd = SPIFFS_open(FS, "filebopen", 0, SPIFFS_CREAT); + TEST_CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0); + TEST_CHECK(s.size == 0); + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, "filebopen", 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0); + TEST_CHECK(s.size == 0); + SPIFFS_close(FS, fd); + return TEST_RES_OK; +} +TEST_END(file_by_open) + + +TEST(file_by_creat) +{ + int res; + res = test_create_file("filebcreat"); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(file_by_creat) + + + + +TEST(list_dir) +{ + int res; + res = test_create_file("file1"); + TEST_CHECK(res >= 0); + res = test_create_file("file2"); + TEST_CHECK(res >= 0); + res = test_create_file("file3"); + TEST_CHECK(res >= 0); + res = test_create_file("file4"); + TEST_CHECK(res >= 0); + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + printf(" %s\n", pe->name); + // TODO verify + } + SPIFFS_closedir(&d); + + return TEST_RES_OK; +} +TEST_END(list_dir) + + + +TEST(remove_single_by_path) +{ + int res; + spiffs_file fd; + res = test_create_file("remove"); + TEST_CHECK(res >= 0); + res = SPIFFS_remove(FS, "remove"); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, "remove", 0, SPIFFS_RDONLY); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END(remove_single_by_path) + + +TEST(remove_single_by_fd) +{ + int res; + spiffs_file fd; + res = test_create_file("remove"); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, "remove", 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + fd = SPIFFS_open(FS, "remove", 0, SPIFFS_RDONLY); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END(remove_single_by_fd) + + +TEST(write_big_file_chunks_page) +{ + int size = ((50*(FS)->cfg.phys_size)/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS)); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_page) + + +TEST(write_big_files_chunks_page) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS)); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_page) + + +TEST(write_big_file_chunks_index) +{ + int size = ((50*(FS)->cfg.phys_size)/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS)); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_index) + + +TEST(write_big_files_chunks_index) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS)); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_index) + + +TEST(write_big_file_chunks_huge) +{ + int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_huge) + + +TEST(write_big_files_chunks_huge) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, size); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_huge) + + +TEST(truncate_big_file) +{ + int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + spiffs_file fd = SPIFFS_open(FS, "bigfile", 0, SPIFFS_RDWR); + TEST_CHECK(fd > 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, "bigfile", 0, SPIFFS_RDWR); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END(truncate_big_file) + + + +TEST(file_uniqueness) +{ + int res; + spiffs_file fd; + char fname[32]; + int files = (FS_PURE_DATA_PAGES(FS) / 2) - SPIFFS_PAGES_PER_BLOCK(FS)*3; + int i; + printf(" creating %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "%i", i); + res = test_create_file(fname); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, fname, 0, SPIFFS_APPEND | SPIFFS_RDWR); + TEST_CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, content, strlen(content)+1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + } + printf(" checking %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + char ref_content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "%i", i); + fd = SPIFFS_open(FS, fname, 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp(ref_content, content) == 0); + SPIFFS_close(FS, fd); + } + printf(" removing %i files\n", files/2); + for (i = 0; i < files; i += 2) { + sprintf(fname, "file%i", i); + res = SPIFFS_remove(FS, fname); + TEST_CHECK(res >= 0); + } + printf(" creating %i files\n", files/2); + for (i = 0; i < files; i += 2) { + char content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "new%i", i); + res = test_create_file(fname); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, fname, 0, SPIFFS_APPEND | SPIFFS_RDWR); + TEST_CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, content, strlen(content)+1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + } + printf(" checking %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + char ref_content[20]; + sprintf(fname, "file%i", i); + if ((i & 1) == 0) { + sprintf(content, "new%i", i); + } else { + sprintf(content, "%i", i); + } + fd = SPIFFS_open(FS, fname, 0, SPIFFS_RDONLY); + TEST_CHECK(fd >= 0); + res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp(ref_content, content) == 0); + SPIFFS_close(FS, fd); + } + + return TEST_RES_OK; +} +TEST_END(file_uniqueness) + +int create_and_read_back(int size, int chunk) { + char *name = "file"; + spiffs_file fd; + s32_t res; + + u8_t *buf = malloc(size); + memrand(buf, size); + + res = test_create_file(name); + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, 0, SPIFFS_APPEND | SPIFFS_RDWR); + CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, buf, size); + CHECK(res >= 0); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + CHECK(res >= 0); + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, name, 0, SPIFFS_RDONLY); + CHECK(fd >= 0); + + u8_t *rbuf = malloc(size); + int offs = 0; + while (offs < size) { + int len = MIN(size - offs, chunk); + res = SPIFFS_read(FS, fd, &rbuf[offs], len); + CHECK(res >= 0); + CHECK(memcmp(&rbuf[offs], &buf[offs], len) == 0); + + offs += chunk; + } + + CHECK(memcmp(&rbuf[0], &buf[0], size) == 0); + + SPIFFS_close(FS, fd); + + free(rbuf); + free(buf); + + return 0; +} + +TEST(read_chunk_1) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*8, 1) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_1) + + +TEST(read_chunk_page) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*2, + SPIFFS_DATA_PAGE_SIZE(FS)) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_page) + + +TEST(read_chunk_index) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*4, + SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_index) + + +TEST(read_chunk_huge) +{ + int sz = (2*(FS)->cfg.phys_size)/3; + TEST_CHECK(create_and_read_back(sz, sz) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_huge) + + +TEST(lseek_simple_modification) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int i; + int len = 4096; + fd = SPIFFS_open(FS, fname, 0, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + lseek(pfd, len/2, SEEK_SET); + len = len/4; + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END(lseek_simple_modification) + + +TEST(lseek_modification_append) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int i; + int len = 4096; + fd = SPIFFS_open(FS, fname, 0, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + lseek(pfd, len/2, SEEK_SET); + + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END(lseek_modification_append) + + +TEST(lseek_modification_append_multi) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = 1024; + int runs = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS) / (len/2); + + fd = SPIFFS_open(FS, fname, 0, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + while (runs--) { + res = SPIFFS_lseek(FS, fd, -len/2, SPIFFS_SEEK_END); + TEST_CHECK(res >= 0); + lseek(pfd, -len/2, SEEK_END); + + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + } + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END(lseek_modification_append_multi) + + +TEST(lseek_read) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + int runs = 100000; + + fd = SPIFFS_open(FS, fname, 0, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR); + TEST_CHECK(fd > 0); + u8_t *refbuf = malloc(len); + memrand(refbuf, len); + res = SPIFFS_write(FS, fd, refbuf, len); + TEST_CHECK(res >= 0); + + int offs = 0; + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + + while (runs--) { + int i; + u8_t buf[64]; + if (offs + 41 + sizeof(buf) >= len) { + offs = (offs + 41 + sizeof(buf)) % len; + res = SPIFFS_lseek(FS, fd, offs, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + } + res = SPIFFS_lseek(FS, fd, 41, SPIFFS_SEEK_CUR); + TEST_CHECK(res >= 0); + offs += 41; + res = SPIFFS_read(FS, fd, buf, sizeof(buf)); + TEST_CHECK(res >= 0); + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != refbuf[offs+i]) { + printf(" mismatch at offs %i\n", offs); + } + TEST_CHECK(buf[i] == refbuf[offs+i]); + } + offs += sizeof(buf); + + res = SPIFFS_lseek(FS, fd, -(sizeof(buf)+11), SPIFFS_SEEK_CUR); + TEST_CHECK(res >= 0); + offs -= (sizeof(buf)+11); + res = SPIFFS_read(FS, fd, buf, sizeof(buf)); + TEST_CHECK(res >= 0); + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != refbuf[offs+i]) { + printf(" mismatch at offs %i\n", offs); + } + TEST_CHECK(buf[i] == refbuf[offs+i]); + } + offs += sizeof(buf); + } + + free(refbuf); + SPIFFS_close(FS, fd); + + return TEST_RES_OK; +} +TEST_END(lseek_read) + + +#ifndef REMOVE_LONG_TESTS +TEST(write_small_file_chunks_1) +{ + int res = test_create_and_write_file("smallfile", 256, 1); + TEST_CHECK(res >= 0); + res = read_and_verify("smallfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_small_file_chunks_1) + + +TEST(write_small_files_chunks_1) +{ + char name[32]; + int f; + int size = 512; + int files = ((20*(FS)->cfg.phys_size)/100)/size; + int res; + for (f = 0; f < files; f++) { + sprintf(name, "smallfile%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "smallfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_small_files_chunks_1) + +TEST(write_big_file_chunks_1) +{ + int size = ((50*(FS)->cfg.phys_size)/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, 1); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_1) + +TEST(write_big_files_chunks_1) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_1) +#endif // REMOVE_LONG_TESTS + +TEST(long_run_config_many_small_one_long) +{ + tfile_conf cfgs[] = { + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 206, 5); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(long_run_config_many_small_one_long) + +TEST(long_run_config_many_medium) +{ + tfile_conf cfgs[] = { + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 305, 5); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(long_run_config_many_medium) + + +TEST(long_run_config_many_small) +{ + tfile_conf cfgs[] = { + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 115, 6); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(long_run_config_many_small) + +SUITE_END(hydrogen_tests) + +SUITE(dev_tests) +void setup() { + _setup(); +} +void teardown() { + _teardown(); +} +#if 0 +TEST(write_small_file_chunks_1) +{ + int res = test_create_and_write_file("smallfile", 600, 1); + TEST_CHECK(res >= 0); + res = read_and_verify("smallfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_small_file_chunks_1) + +TEST(write_big_file_chunks_page) +{ + int size = ((50*(FS)->cfg.phys_size)/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS)); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_page) +#else + + +TEST(long_run_config_many_small) +{ + tfile_conf cfgs[] = { + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 115, 6); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(long_run_config_many_small) +#endif +SUITE_END(dev_tests) + diff --git a/src/test_spiffs.c b/src/test_spiffs.c new file mode 100644 index 0000000..979a25b --- /dev/null +++ b/src/test_spiffs.c @@ -0,0 +1,270 @@ +/* + * test_spiffs.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + + +#include +#include +#include + +#include "stypes.h" +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#include "testrunner.h" + +#include "test_spiffs.h" + +#include +#include +#include +#include +#include + +#define SECTOR_SIZE 65536 //(0x10000) +#define PAGE_SIZE 256 //256 +#define LOG_BLOCK 65536 +#define LOG_PAGE PAGE_SIZE + +static unsigned char area[1024*1024*1]; + +static int erases[sizeof(area)/SECTOR_SIZE]; +static char _path[256]; + +spiffs __fs; +static u8_t _work[LOG_PAGE*2]; +static u8_t _fds[256+128]; +static u8_t _cache[256*5]; + +#define TEST_PATH "/home/petera/proj/spiffs_test_/test_data/" + +char *make_test_fname(const char *name) { + sprintf(_path, "%s%s", TEST_PATH, name); + return _path; +} + +void clear_test_path() { + DIR *dp; + struct dirent *ep; + dp = opendir(TEST_PATH); + + if (dp != NULL) { + while ((ep = readdir(dp))) { + if (ep->d_name[0] != '.') { + sprintf(_path, "%s%s", TEST_PATH, ep->d_name); + remove(_path); + } + } + closedir(dp); + } +} + +static s32_t _read(u32_t addr, u32_t size, u8_t *dst) { + memcpy(dst, &area[addr], size); + return 0; +} + +static s32_t _write(u32_t addr, u32_t size, u8_t *src) { + int i; + //printf("wr %08x %i\n", addr, size); + for (i = 0; i < size; i++) { + if (((addr + i) & (PAGE_SIZE-1)) != offsetof(spiffs_page_header, flags)) { + if (((area[addr + i] ^ src[i]) & src[i])) { + printf("trying to write %02x to %02x at addr %08x\n", src[i], area[addr + i], addr+i); + spiffs_page_ix pix = (addr + i) / PAGE_SIZE; + dump_page(&__fs, pix); + return -1; + } + } + area[addr + i] &= src[i]; + } + return 0; +} + +static s32_t _erase(u32_t addr, u32_t size) { + if (addr & (SECTOR_SIZE-1)) { + return -1; + } + if (size & (SECTOR_SIZE-1)) { + return -1; + } + erases[addr/SECTOR_SIZE]++; + memset(&area[addr], 0xff, size); + return 0; +} + +void hexdump_mem(u8_t *b, u32_t len) { + while (len--) { + if ((((u32_t)b)&0x1f) == 0) { + printf("\n"); + } + printf("%02x", *b++); + } + printf("\n"); +} + +void hexdump(u32_t addr, u32_t len) { + int remainder = (addr % 32) == 0 ? 0 : 32 - (addr % 32); + u32_t a; + for (a = addr - remainder; a < addr+len; a++) { + if ((a & 0x1f) == 0) { + if (a != addr) { + printf(" "); + int j; + for (j = 0; j < 32; j++) { + if (a-32+j < addr) + printf(" "); + else { + printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]); + } + } + } + printf("%s %08x: ", a<=addr ? "":"\n", a); + } + if (a < addr) { + printf(" "); + } else { + printf("%02x", area[a]); + } + } + int j; + printf(" "); + for (j = 0; j < 32; j++) { + if (a-32+j < addr) + printf(" "); + else { + printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]); + } + } + printf("\n"); +} + +void dump_page(spiffs *fs, spiffs_page_ix p) { + printf("page %04x ", p); + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, p); + if (p % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // obj lu page + printf("OBJ_LU"); + } else { + // data page + spiffs_page_header *ph = (spiffs_page_header *)&area[addr]; + printf("DATA %04x:%04x ", ph->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, ph->span_ix); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_FINAL) == 0) ? "F" : "f"); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_DELET) == 0) ? "D" : "d"); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_INDEX) == 0) ? "I" : "i"); + printf("%s ", ((ph->flags & SPIFFS_PH_FLAG_CORRU) == 0) ? "C" : "c"); + if (ph->obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + // object index + printf("OBJ_IX"); + if (ph->span_ix == 0) { + printf("_HDR "); + spiffs_page_object_ix_header *oix_hdr = (spiffs_page_object_ix_header *)&area[addr]; + printf("'%s' %i bytes type:%02x", oix_hdr->name, oix_hdr->size, oix_hdr->type); + } + } else { + // data page + printf("CONTENT"); + } + } + printf("\n"); + u32_t len = fs->cfg.log_page_size; + hexdump(addr, len); +} + +int read_and_verify(char *name) { + s32_t res; + int fd = SPIFFS_open(&__fs, name, 0, 0); + if (fd < 0) { + printf(" read_and_verify: could not open file %s\n", name); + return fd; + } + int pfd = open(make_test_fname(name), O_RDONLY); + spiffs_stat s; + res = SPIFFS_fstat(&__fs, fd, &s); + if (res < 0) { + printf(" read_and_verify: could not stat file %s\n", name); + return res; + } + if (s.size == 0) { + SPIFFS_close(&__fs, fd); + close(pfd); + return 0; + } + + //printf("verifying %s, len %i\n", name, s.size); + int offs = 0; + u8_t buf_d[256]; + u8_t buf_v[256]; + while (offs < s.size) { + int read_len = MIN(s.size - offs, sizeof(buf_d)); + res = SPIFFS_read(&__fs, fd, buf_d, read_len); + if (res < 0) { + printf(" read_and_verify: could not read file %s offs:%i len:%i filelen:%i\n", name, offs, read_len, s.size); + return res; + } + int pres = read(pfd, buf_v, read_len); + (void)pres; + //printf("reading offs:%i len:%i spiffs_res:%i posix_res:%i\n", offs, read_len, res, pres); + int i; + int veri_ok = 1; + for (i = 0; veri_ok && i < read_len; i++) { + if (buf_d[i] != buf_v[i]) { + printf("file verification mismatch @ %i, %02x %c != %02x %c\n", offs+i, buf_d[i], buf_d[i], buf_v[i], buf_v[i]); + int j = MAX(0, i-16); + int k = MIN(sizeof(buf_d), i+16); + k = MIN(s.size-offs, k); + int l; + for (l = j; l < k; l++) { + printf("%c", buf_d[l] > 31 ? buf_d[l] : '.'); + } + printf("\n"); + for (l = j; l < k; l++) { + printf("%c", buf_v[l] > 31 ? buf_v[l] : '.'); + } + printf("\n"); + veri_ok = 0; + } + } + if (!veri_ok) { + SPIFFS_close(&__fs, fd); + close(pfd); + printf("data mismatch\n"); + return -1; + } + + offs += read_len; + } + + SPIFFS_close(&__fs, fd); + close(pfd); + + return 0; +} + +void fs_reset() { + memset(area, 0xff, sizeof(area)); + spiffs_config c; + c.hal_erase_f = _erase; + c.hal_read_f = _read; + c.hal_write_f = _write; + c.log_block_size = LOG_BLOCK; + c.log_page_size = LOG_PAGE; + c.phys_addr = 0; + c.phys_erase_block = SECTOR_SIZE; + c.phys_size = sizeof(area); + memset(erases,0,sizeof(erases)); + memset(_cache,0,sizeof(_cache)); + SPIFFS_init(&__fs, &c, _work, _fds, sizeof(_fds), _cache, sizeof(_cache)); +} + +void real_assert(int c, const char *n, const char *file, int l) { + if (c == 0) { + printf("ASSERT: %s %s @ %i\n", (n ? n : ""), file, l); + printf("fs errno:%i\n", __fs.errno); + exit(0); + } +} + diff --git a/src/test_spiffs.h b/src/test_spiffs.h new file mode 100644 index 0000000..cddb293 --- /dev/null +++ b/src/test_spiffs.h @@ -0,0 +1,25 @@ +/* + * test_spiffs.h + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +#ifndef TEST_SPIFFS_H_ +#define TEST_SPIFFS_H_ + +#include "spiffs.h" + +#define FS &__fs + +extern spiffs __fs; + +void fs_reset(); +int read_and_verify(char *name); +void dump_page(spiffs *fs, spiffs_page_ix p); +void hexdump(u32_t addr, u32_t len); +char *make_test_fname(const char *name); +void clear_test_path(); + + +#endif /* TEST_SPIFFS_H_ */ diff --git a/src/testrunner.c b/src/testrunner.c new file mode 100644 index 0000000..633105b --- /dev/null +++ b/src/testrunner.c @@ -0,0 +1,120 @@ +/* + * testrunner.c + * + * Created on: Jun 18, 2013 + * Author: petera + */ + + +#include +#include +#include +#include "testrunner.h" + +static struct { + test *tests; + test *_last_test; + int test_count; + void (*on_stop)(test *t); + test_res *failed; + test_res *failed_last; + test_res *stopped; + test_res *stopped_last; +} test_main; + +void test_init(void (*on_stop)(test *t)) { + test_main.on_stop = on_stop; +} + +void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)) { + if (f == 0) return; + DBGT("adding test %s\n", name); + test *t = malloc(sizeof(test)); + memset(t, 0, sizeof(test)); + t->f = f; + strcpy(t->name, name); + t->setup = setup; + t->teardown = teardown; + if (test_main.tests == 0) { + test_main.tests = t; + } else { + test_main._last_test->_next = t; + } + test_main._last_test = t; + test_main.test_count++; +} + +static void add_res(test *t, test_res **head, test_res **last) { + test_res *tr = malloc(sizeof(test_res)); + memset(tr,0,sizeof(test_res)); + strcpy(tr->name, t->name); + if (*head == 0) { + *head = tr; + } else { + (*last)->_next = tr; + } + *last = tr; +} + +static void dump_res(test_res **head) { + test_res *tr = (*head); + while (tr) { + test_res *next_tr = tr->_next; + printf(" %s\n", tr->name); + free(tr); + tr = next_tr; + } +} + +void run_tests() { + memset(&test_main, 0, sizeof(test_main)); + DBGT("adding suites...\n"); + add_suites(); + DBGT("%i tests added\n", test_main.test_count); + DBGT("running tests...\n"); + int ok = 0; + int failed = 0; + int stopped = 0; + test *cur_t = test_main.tests; + int i = 1; + while (cur_t) { + cur_t->setup(cur_t); + test *next_test = cur_t->_next; + DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name); + i++; + int res = cur_t->f(cur_t); + cur_t->teardown(cur_t); + switch (res) { + case TEST_RES_OK: + ok++; + printf(" .. ok\n"); + break; + case TEST_RES_FAIL: + failed++; + printf(" .. FAILED\n"); + if (test_main.on_stop) test_main.on_stop(cur_t); + add_res(cur_t, &test_main.failed, &test_main.failed_last); + break; + case TEST_RES_ASSERT: + stopped++; + printf(" .. ABORTED\n"); + if (test_main.on_stop) test_main.on_stop(cur_t); + add_res(cur_t, &test_main.stopped, &test_main.stopped_last); + break; + } + free(cur_t); + cur_t = next_test; + } + DBGT("ran %i tests\n", test_main.test_count); + printf("Test report, %i tests\n", test_main.test_count); + printf("%i succeeded\n", ok); + printf("%i failed\n", failed); + dump_res(&test_main.failed); + printf("%i stopped\n", stopped); + dump_res(&test_main.stopped); + if (ok < test_main.test_count) { + printf("\nFAILED\n"); + } else { + printf("\nALL TESTS OK\n"); + } +} diff --git a/src/testrunner.h b/src/testrunner.h new file mode 100644 index 0000000..72d0350 --- /dev/null +++ b/src/testrunner.h @@ -0,0 +1,110 @@ +/* + * testrunner.h + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +/* + +SUITE(mysuite) + +void setup(test *t) {} + +void teardown(test *t) {} + +TEST(mytest) { + printf("mytest runs now..\n"); + return 0; +} TEST_END(mytest) + +SUITE_END(mysuite) + + + +SUITE(mysuite2) + +void setup(test *t) {} + +void teardown(test *t) {} + +TEST(mytest2a) { + printf("mytest2a runs now..\n"); + return 0; +} TEST_END(mytest2a) + +TEST(mytest2b) { + printf("mytest2b runs now..\n"); + return 0; +} TEST_END(mytest2b) + +SUITE_END(mysuite2) + + + +void add_suites() { + ADD_SUITE(mysuite); + ADD_SUITE(mysuite2); +} + */ + +#ifndef TESTS_H_ +#define TESTS_H_ + +#define TEST_RES_OK 0 +#define TEST_RES_FAIL -1 +#define TEST_RES_ASSERT -2 + +struct test_s; + +typedef int (*test_f)(struct test_s *t); + +typedef struct test_s { + test_f f; + char name[256]; + void *data; + void (*setup)(struct test_s *t); + void (*teardown)(struct test_s *t); + struct test_s *_next; +} test; + +typedef struct test_res_s { + char name[256]; + struct test_res_s *_next; +} test_res; + +#define TEST_CHECK(x) if (!(x)) { \ + printf(" TEST FAIL %s:%i\n", __FILE__, __LINE__); \ + goto __fail_stop; \ +} +#define TEST_ASSERT(x) if (!(x)) { \ + printf(" TEST ASSERT %s:%i\n", __FILE__, __LINE__); \ + goto __fail_assert; \ +} + +#define DBGT(...) printf(__VA_ARGS__) + +#define str(s) #s + +#define SUITE(sui) \ + extern void __suite_##sui() { +#define SUITE_END(sui) \ + } +#define ADD_SUITE(sui) \ + __suite_##sui(); +#define TEST(tf) \ + int tf(struct test_s *t) { do +#define TEST_END(tf) \ + while(0); \ + __fail_stop: return TEST_RES_FAIL; \ + __fail_assert: return TEST_RES_ASSERT; \ + } \ + add_test(tf, str(tf), setup, teardown); + + +void add_suites(); +void test_init(void (*on_stop)(test *t)); +void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)); +void run_tests(); + +#endif /* TESTS_H_ */ diff --git a/src/testsuites.c b/src/testsuites.c new file mode 100644 index 0000000..a7c51e7 --- /dev/null +++ b/src/testsuites.c @@ -0,0 +1,13 @@ +/* + * testsuites.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +#include "testrunner.h" + +void add_suites() { + ADD_SUITE(hydrogen_tests) + //ADD_SUITE(dev_tests); +}