aboutsummaryrefslogtreecommitdiff
path: root/libiot/vfs
diff options
context:
space:
mode:
Diffstat (limited to 'libiot/vfs')
-rw-r--r--libiot/vfs/fat.c453
-rw-r--r--libiot/vfs/include/esp_vfs.h424
-rw-r--r--libiot/vfs/include/sys/dirent.h56
-rw-r--r--libiot/vfs/lfs.c949
-rw-r--r--libiot/vfs/mkfile7
-rw-r--r--libiot/vfs/ramfs.c634
-rw-r--r--libiot/vfs/spiffs.c1423
-rw-r--r--libiot/vfs/vfs.h128
8 files changed, 4074 insertions, 0 deletions
diff --git a/libiot/vfs/fat.c b/libiot/vfs/fat.c
new file mode 100644
index 0000000..d612277
--- /dev/null
+++ b/libiot/vfs/fat.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L.
+ * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * * The WHITECAT logotype cannot be changed, you can remove it, but you
+ * cannot change it in any way. The WHITECAT logotype is:
+ *
+ * /\ /\
+ * / \_____/ \
+ * /_____________\
+ * W H I T E C A T
+ *
+ * * Redistributions in binary form must retain all copyright notices printed
+ * to any local or remote output device. This include any reference to
+ * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may
+ * appear in the future.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Lua RTOS fat vfs
+ *
+ */
+
+#include "sdkconfig.h"
+
+#if (CONFIG_SD_CARD_MMC || CONFIG_SD_CARD_SPI) && CONFIG_LUA_RTOS_USE_FAT
+
+#include <stdio.h>
+
+#include "esp_vfs.h"
+
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_vfs_fat.h"
+#include "driver/sdmmc_host.h"
+#include "driver/sdspi_host.h"
+#include "sdmmc_cmd.h"
+#include "driver/sdmmc_defs.h"
+
+#include <drivers/gpio.h>
+#include <drivers/spi.h>
+#include <drivers/power_bus.h>
+
+#include <sys/driver.h>
+#include <sys/mount.h>
+#include <sys/syslog.h>
+
+#include <sys/vfs/vfs.h>
+
+int vfs_fat_mount(const char *target) {
+#if CONFIG_SD_CARD_SPI
+ spi_bus_t *spi_bus = get_spi_info();
+
+ #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ // Lock resources
+ if (spi_lock_bus_resources(CONFIG_LUA_RTOS_SD_SPI, DRIVER_ALL_FLAGS)) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_SD_CS, DRIVER_ALL_FLAGS, "SD Card - CS")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+
+ sdmmc_host_t host = SDSPI_HOST_DEFAULT();
+ sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
+
+ #if (CONFIG_LUA_RTOS_SD_SPI == 2)
+ host.slot = HSPI_HOST;
+
+ slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI2_MISO;
+ slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI2_MOSI;
+ slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI2_CLK;
+ slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS;
+ #endif // (CONFIG_LUA_RTOS_SD_SPI == 2)
+
+ #if (CONFIG_LUA_RTOS_SD_SPI == 3)
+ host.slot = VSPI_HOST;
+
+ slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI3_MISO;
+ slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI3_MOSI;
+ slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI3_CLK;
+ slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS;
+ #endif // (CONFIG_LUA_RTOS_SD_SPI == 3)
+
+ spi_bus[spi_idx(CONFIG_LUA_RTOS_SD_SPI)].setup |= SPI_DMA_SETUP;
+#endif // CONFIG_SD_CARD_SPI
+
+#if CONFIG_SD_CARD_MMC
+ sdmmc_host_t host = SDMMC_HOST_DEFAULT();
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+
+ slot_config.gpio_cd = CONFIG_LUA_RTOS_MCC_CD;
+ slot_config.gpio_wp = CONFIG_LUA_RTOS_MCC_WP;
+
+ #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ // Lock resources
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 15, DRIVER_ALL_FLAGS, "SD Card - CMD")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14, DRIVER_ALL_FLAGS, "SD Card - CLK")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 2, DRIVER_ALL_FLAGS, "SD Card - DAT0")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+
+ gpio_pin_pullup(2);
+ gpio_pin_pullup(14);
+ gpio_pin_pullup(15);
+
+ #if CONFIG_LUA_RTOS_MCC_1_LINE
+ host.flags = SDMMC_HOST_FLAG_1BIT;
+ slot_config.width = 1;
+ #endif // CONFIG_LUA_RTOS_MCC_1_LINE
+
+ #if CONFIG_LUA_RTOS_MCC_4_LINE
+ host.flags = SDMMC_HOST_FLAG_4BIT;
+ slot_config.width = 4;
+
+ gpio_pin_pullup(4);
+ gpio_pin_pullup(12);
+ gpio_pin_pullup(13);
+
+ #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 4, DRIVER_ALL_FLAGS, "SD Card - DAT1")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 12, DRIVER_ALL_FLAGS, "SD Card - DAT2")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 13, DRIVER_ALL_FLAGS, "SD Card - DAT3")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ #endif // CONFIG_LUA_RTOS_MCC_4_LINE
+
+ #if CONFIG_LUA_RTOS_MCC_CD != -1
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_CD, DRIVER_ALL_FLAGS, "SD Card - CD")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_MCC_CD
+
+ #if CONFIG_LUA_RTOS_MCC_WP != -1
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_WP, DRIVER_ALL_FLAGS, "SD Card - WP")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_MCC_WP
+#endif // CONFIG_SD_CARD_MMC
+
+#if CONFIG_LUA_RTOS_SD_CONNECTED_TO_POWER_BUS
+ pwbus_on();
+#endif
+
+ esp_vfs_fat_sdmmc_mount_config_t mount_config = {
+ .format_if_mount_failed = false,
+ .max_files = 5
+ };
+
+ #if CONFIG_SD_CARD_SPI
+ syslog(LOG_INFO, "sd is at spi%d, cs=%s%d",
+ CONFIG_LUA_RTOS_SD_SPI,
+ gpio_portname(CONFIG_LUA_RTOS_SD_CS), gpio_name(CONFIG_LUA_RTOS_SD_CS)
+ );
+ #endif
+
+ #if CONFIG_SD_CARD_MMC
+ syslog(LOG_INFO, "sd is at mmc0");
+ #endif
+
+ sdmmc_card_t* card;
+ esp_err_t ret = esp_vfs_fat_sdmmc_mount("/fat", &host, &slot_config, &mount_config, &card);
+ if (ret != ESP_OK) {
+ syslog(LOG_INFO, "fat can't mounted (error %d)", ret);
+ vfs_fat_umount(target);
+ return -1;
+ }
+
+ syslog(LOG_INFO, "sd name %s", card->cid.name);
+ syslog(LOG_INFO, "sd type %s", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC");
+ syslog(LOG_INFO, "sd working at %s", (card->csd.tr_speed > 25000000)?"high speed":"default speed");
+ syslog(LOG_INFO, "sd size %.2f GB",
+ ((((double)card->csd.capacity) * ((double)card->csd.sector_size)) / 1073741824.0)
+ );
+
+ syslog(LOG_INFO, "sd CSD ver=%d, sector_size=%d, capacity=%d read_bl_len=%d",
+ card->csd.csd_ver,
+ card->csd.sector_size, card->csd.capacity, card->csd.read_block_len
+ );
+
+ syslog(LOG_INFO, "sd SCR sd_spec=%d, bus_width=%d", card->scr.sd_spec, card->scr.bus_width);
+
+ syslog(LOG_INFO, "fat mounted on %s", target);
+
+ return 0;
+}
+
+int vfs_fat_umount(const char *target) {
+ // Unmount
+ esp_vfs_fat_sdmmc_unmount();
+
+#if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ #if CONFIG_SD_CARD_SPI
+ spi_bus_t *spi_bus = get_spi_info();
+
+ // Unlock resources
+ spi_unlock_bus_resources(CONFIG_LUA_RTOS_SD_SPI);
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_SD_CS);
+ spi_bus[spi_idx(CONFIG_LUA_RTOS_SD_SPI)].setup &= ~SPI_DMA_SETUP;
+ #endif // CONFIG_SD_CARD_SPI
+
+ #if CONFIG_SD_CARD_MMC
+ // Unlock resources
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 15);
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14);
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 2);
+
+ #if CONFIG_LUA_RTOS_MCC_4_LINE
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14);
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 12);
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 13);
+ #endif
+
+ #if CONFIG_LUA_RTOS_MCC_CD != -1
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_CD);
+ #endif
+
+ #if CONFIG_LUA_RTOS_MCC_WP != -1
+ driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_WP);
+ #endif
+ #endif // CONFIG_SD_CARD_MMC
+#endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+
+#if CONFIG_LUA_RTOS_SD_CONNECTED_TO_POWER_BUS
+ pwbus_off();
+#endif
+
+ syslog(LOG_INFO, "fat unmounted");
+
+ return 0;
+}
+
+int vfs_fat_format(const char *target) {
+ vfs_fat_umount(target);
+
+#if CONFIG_SD_CARD_SPI
+ spi_bus_t *spi_bus = get_spi_info();
+
+ #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ // Lock resources
+ if (spi_lock_bus_resources(CONFIG_LUA_RTOS_SD_SPI, DRIVER_ALL_FLAGS)) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_SD_CS, DRIVER_ALL_FLAGS, "SD Card - CS")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+
+ sdmmc_host_t host = SDSPI_HOST_DEFAULT();
+ sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
+
+ #if (CONFIG_LUA_RTOS_SD_SPI == 2)
+ host.slot = HSPI_HOST;
+
+ slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI2_MISO;
+ slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI2_MOSI;
+ slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI2_CLK;
+ slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS;
+ #endif // (CONFIG_LUA_RTOS_SD_SPI == 2)
+
+ #if (CONFIG_LUA_RTOS_SD_SPI == 3)
+ host.slot = VSPI_HOST;
+
+ slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI3_MISO;
+ slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI3_MOSI;
+ slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI3_CLK;
+ slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS;
+ #endif // (CONFIG_LUA_RTOS_SD_SPI == 3)
+
+ spi_bus[spi_idx(CONFIG_LUA_RTOS_SD_SPI)].setup |= SPI_DMA_SETUP;
+#endif // CONFIG_SD_CARD_SPI
+
+#if CONFIG_SD_CARD_MMC
+ sdmmc_host_t host = SDMMC_HOST_DEFAULT();
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+
+ slot_config.gpio_cd = CONFIG_LUA_RTOS_MCC_CD;
+ slot_config.gpio_wp = CONFIG_LUA_RTOS_MCC_WP;
+
+ #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ // Lock resources
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 15, DRIVER_ALL_FLAGS, "SD Card - CMD")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14, DRIVER_ALL_FLAGS, "SD Card - CLK")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 2, DRIVER_ALL_FLAGS, "SD Card - DAT0")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+
+ gpio_pin_pullup(2);
+ gpio_pin_pullup(14);
+ gpio_pin_pullup(15);
+
+ #if CONFIG_LUA_RTOS_MCC_1_LINE
+ host.flags = SDMMC_HOST_FLAG_1BIT;
+ slot_config.width = 1;
+ #endif // CONFIG_LUA_RTOS_MCC_1_LINE
+
+ #if CONFIG_LUA_RTOS_MCC_4_LINE
+ host.flags = SDMMC_HOST_FLAG_4BIT;
+ slot_config.width = 4;
+
+ gpio_pin_pullup(4);
+ gpio_pin_pullup(12);
+ gpio_pin_pullup(13);
+
+ #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 4, DRIVER_ALL_FLAGS, "SD Card - DAT1")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 12, DRIVER_ALL_FLAGS, "SD Card - DAT2")) {
+ return -1;
+ }
+
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 13, DRIVER_ALL_FLAGS, "SD Card - DAT3")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS
+ #endif // CONFIG_LUA_RTOS_MCC_4_LINE
+
+ #if CONFIG_LUA_RTOS_MCC_CD != -1
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_CD, DRIVER_ALL_FLAGS, "SD Card - CD")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_MCC_CD
+
+ #if CONFIG_LUA_RTOS_MCC_WP != -1
+ if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_WP, DRIVER_ALL_FLAGS, "SD Card - WP")) {
+ return -1;
+ }
+ #endif // CONFIG_LUA_RTOS_MCC_WP
+#endif // CONFIG_SD_CARD_MMC
+
+ esp_vfs_fat_sdmmc_mount_config_t mount_config = {
+ .format_if_mount_failed = false,
+ .max_files = 5
+ };
+
+ #if CONFIG_SD_CARD_SPI
+ syslog(LOG_INFO, "sd is at spi%d, cs=%s%d",
+ CONFIG_LUA_RTOS_SD_SPI,
+ gpio_portname(CONFIG_LUA_RTOS_SD_CS), gpio_name(CONFIG_LUA_RTOS_SD_CS)
+ );
+ #endif
+
+ #if CONFIG_SD_CARD_MMC
+ syslog(LOG_INFO, "sd is at mmc0");
+ #endif
+
+ sdmmc_card_t* card;
+ esp_err_t ret = esp_vfs_fat_sdmmc_format("/fat", &host, &slot_config, &mount_config, &card);
+ if (ret != ESP_OK) {
+ syslog(LOG_INFO, "fat can't mounted (error %d)", ret);
+ vfs_fat_umount(target);
+ return -1;
+ }
+
+ syslog(LOG_INFO, "sd name %s", card->cid.name);
+ syslog(LOG_INFO, "sd type %s", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC");
+ syslog(LOG_INFO, "sd working at %s", (card->csd.tr_speed > 25000000)?"high speed":"default speed");
+ syslog(LOG_INFO, "sd size %.2f GB",
+ ((((double)card->csd.capacity) * ((double)card->csd.sector_size)) / 1073741824.0)
+ );
+
+ syslog(LOG_INFO, "sd CSD ver=%d, sector_size=%d, capacity=%d read_bl_len=%d",
+ card->csd.csd_ver,
+ card->csd.sector_size, card->csd.capacity, card->csd.read_block_len
+ );
+
+ syslog(LOG_INFO, "sd SCR sd_spec=%d, bus_width=%d", card->scr.sd_spec, card->scr.bus_width);
+
+ syslog(LOG_INFO, "fat mounted on %s", target);
+
+ vfs_fat_umount(target);
+ vfs_fat_mount(target);
+
+ return 0;
+}
+
+int vfs_fat_fsstat(const char *target, u32_t *total, u32_t *used) {
+ DWORD nclst = 0;
+ FATFS* fs = NULL;
+ FRESULT err = f_getfree (target, &nclst, &fs);
+
+ if (err != FR_OK) {
+ syslog(LOG_ERR, "fat get fs info of '%s' (%i)", target, err);
+ return -1;
+ }
+
+ //fs->free_clst and nclst both are equal and give the number of free clusters
+ //fs->csize = Cluster size [sectors]
+ //fs->ssize = Sector size (512, 1024, 2048 or 4096)
+ //fs->fsize = Size of an FAT [sectors]
+ //fs->n_fatent = nclst + 2
+
+ DWORD tclst = fs->n_fatent - 2;
+
+ if (total) {
+ *total = tclst * fs->csize * fs->ssize;
+ }
+
+ if (used) {
+ *used = (tclst - nclst) * fs->csize * fs->ssize;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/libiot/vfs/include/esp_vfs.h b/libiot/vfs/include/esp_vfs.h
new file mode 100644
index 0000000..a0762d3
--- /dev/null
+++ b/libiot/vfs/include/esp_vfs.h
@@ -0,0 +1,424 @@
+// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __ESP_VFS_H__
+#define __ESP_VFS_H__
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <utime.h>
+#include <FreeRTOS.h>
+#include <semphr.h>
+#include <esp_err.h>
+#include <sys/types.h>
+#include <sys/reent.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+//#include <sys/termios.h>
+//#include <../platform_include/sys/termios.h>
+//#include <sys/poll.h>
+#include <dirent.h>
+#include <string.h>
+//#include "sdkconfig.h"
+
+#include "lwip/sockets.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _SYS_TYPES_FD_SET
+#error "VFS should be used with FD_SETSIZE and FD_SET from sys/types.h"
+#endif
+
+/**
+ * Maximum number of (global) file descriptors.
+ */
+#define MAX_FDS FD_SETSIZE /* for compatibility with fd_set and select() */
+
+/**
+ * Maximum length of path prefix (not including zero terminator)
+ */
+#define ESP_VFS_PATH_MAX 15
+
+/**
+ * Default value of flags member in esp_vfs_t structure.
+ */
+#define ESP_VFS_FLAG_DEFAULT 0
+
+/**
+ * Flag which indicates that FS needs extra context pointer in syscalls.
+ */
+#define ESP_VFS_FLAG_CONTEXT_PTR 1
+
+/*
+ * @brief VFS identificator used for esp_vfs_register_with_id()
+ */
+typedef int esp_vfs_id_t;
+
+/**
+ * @brief VFS definition structure
+ *
+ * This structure should be filled with pointers to corresponding
+ * FS driver functions.
+ *
+ * VFS component will translate all FDs so that the filesystem implementation
+ * sees them starting at zero. The caller sees a global FD which is prefixed
+ * with an pre-filesystem-implementation.
+ *
+ * Some FS implementations expect some state (e.g. pointer to some structure)
+ * to be passed in as a first argument. For these implementations,
+ * populate the members of this structure which have _p suffix, set
+ * flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer
+ * to esp_vfs_register function.
+ * If the implementation doesn't use this extra argument, populate the
+ * members without _p suffix and set flags member to ESP_VFS_FLAG_DEFAULT.
+ *
+ * If the FS driver doesn't provide some of the functions, set corresponding
+ * members to NULL.
+ */
+typedef struct
+{
+ int flags; /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */
+ union {
+ ssize_t (*write_p)(void* p, int fd, const void * data, size_t size);
+ ssize_t (*write)(int fd, const void * data, size_t size);
+ };
+ union {
+ off_t (*lseek_p)(void* p, int fd, off_t size, int mode);
+ off_t (*lseek)(int fd, off_t size, int mode);
+ };
+ union {
+ ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size);
+ ssize_t (*read)(int fd, void * dst, size_t size);
+ };
+ union {
+ int (*open_p)(void* ctx, const char * path, int flags, int mode);
+ int (*open)(const char * path, int flags, int mode);
+ };
+ union {
+ int (*close_p)(void* ctx, int fd);
+ int (*close)(int fd);
+ };
+ union {
+ int (*fstat_p)(void* ctx, int fd, struct stat * st);
+ int (*fstat)(int fd, struct stat * st);
+ };
+ union {
+ int (*stat_p)(void* ctx, const char * path, struct stat * st);
+ int (*stat)(const char * path, struct stat * st);
+ };
+ union {
+ int (*link_p)(void* ctx, const char* n1, const char* n2);
+ int (*link)(const char* n1, const char* n2);
+ };
+ union {
+ int (*unlink_p)(void* ctx, const char *path);
+ int (*unlink)(const char *path);
+ };
+ union {
+ int (*rename_p)(void* ctx, const char *src, const char *dst);
+ int (*rename)(const char *src, const char *dst);
+ };
+ union {
+ DIR* (*opendir_p)(void* ctx, const char* name);
+ DIR* (*opendir)(const char* name);
+ };
+ union {
+ struct dirent* (*readdir_p)(void* ctx, DIR* pdir);
+ struct dirent* (*readdir)(DIR* pdir);
+ };
+ union {
+ int (*readdir_r_p)(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
+ int (*readdir_r)(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
+ };
+ union {
+ long (*telldir_p)(void* ctx, DIR* pdir);
+ long (*telldir)(DIR* pdir);
+ };
+ union {
+ void (*seekdir_p)(void* ctx, DIR* pdir, long offset);
+ void (*seekdir)(DIR* pdir, long offset);
+ };
+ union {
+ int (*closedir_p)(void* ctx, DIR* pdir);
+ int (*closedir)(DIR* pdir);
+ };
+ union {
+ int (*mkdir_p)(void* ctx, const char* name, mode_t mode);
+ int (*mkdir)(const char* name, mode_t mode);
+ };
+ union {
+ int (*rmdir_p)(void* ctx, const char* name);
+ int (*rmdir)(const char* name);
+ };
+ union {
+ int (*fcntl_p)(void* ctx, int fd, int cmd, va_list args);
+ int (*fcntl)(int fd, int cmd, va_list args);
+ };
+ union {
+ int (*ioctl_p)(void* ctx, int fd, int cmd, va_list args);
+ int (*ioctl)(int fd, int cmd, va_list args);
+ };
+ union {
+ int (*fsync_p)(void* ctx, int fd);
+ int (*fsync)(int fd);
+ };
+ union {
+ int (*access_p)(void* ctx, const char *path, int amode);
+ int (*access)(const char *path, int amode);
+ };
+ union {
+ int (*truncate_p)(void* ctx, const char *path, off_t length);
+ int (*truncate)(const char *path, off_t length);
+ };
+ union {
+ int (*utime_p)(void* ctx, const char *path, const struct utimbuf *times);
+ int (*utime)(const char *path, const struct utimbuf *times);
+ };
+ union {
+ int (*ftruncate_p)(void* ctx, int fd, off_t length);
+ int (*ftruncate)(int fd, off_t length);
+ };
+ union {
+ int (*writev_p)(void* ctx, int fd, const struct iovec *iov, int iovcnt);
+ int (*writev)(int fd, const struct iovec *iov, int iovcnt);
+ };
+ union {
+ int (*select_p)(void* ctx, int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
+ int (*select)(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
+ };
+#ifdef CONFIG_SUPPORT_TERMIOS
+ union {
+ int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p);
+ int (*tcsetattr)(int fd, int optional_actions, const struct termios *p);
+ };
+ union {
+ int (*tcgetattr_p)(void *ctx, int fd, struct termios *p);
+ int (*tcgetattr)(int fd, struct termios *p);
+ };
+ union {
+ int (*tcdrain_p)(void *ctx, int fd);
+ int (*tcdrain)(int fd);
+ };
+ union {
+ int (*tcflush_p)(void *ctx, int fd, int select);
+ int (*tcflush)(int fd, int select);
+ };
+ union {
+ int (*tcflow_p)(void *ctx, int fd, int action);
+ int (*tcflow)(int fd, int action);
+ };
+ union {
+ pid_t (*tcgetsid_p)(void *ctx, int fd);
+ pid_t (*tcgetsid)(int fd);
+ };
+ union {
+ int (*tcsendbreak_p)(void *ctx, int fd, int duration);
+ int (*tcsendbreak)(int fd, int duration);
+ };
+#endif // CONFIG_SUPPORT_TERMIOS
+
+ /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */
+ esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, SemaphoreHandle_t *signal_sem);
+ /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */
+ int (*socket_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
+ /** called by VFS to interrupt the socket_select call when select is activated from a non-socket VFS driver; set only for the socket driver */
+ void (*stop_socket_select)();
+ /** stop_socket_select which can be called from ISR; set only for the socket driver */
+ void (*stop_socket_select_isr)(BaseType_t *woken);
+ /** end_select is called to stop the I/O multiplexing and deinitialize the environment created by start_select for the given VFS */
+ void (*end_select)();
+} esp_vfs_t;
+
+
+/**
+ * Register a virtual filesystem for given path prefix.
+ *
+ * @param base_path file path prefix associated with the filesystem.
+ * Must be a zero-terminated C string, up to ESP_VFS_PATH_MAX
+ * characters long, and at least 2 characters long.
+ * Name must start with a "/" and must not end with "/".
+ * For example, "/data" or "/dev/spi" are valid.
+ * These VFSes would then be called to handle file paths such as
+ * "/data/myfile.txt" or "/dev/spi/0".
+ * @param vfs Pointer to esp_vfs_t, a structure which maps syscalls to
+ * the filesystem driver functions. VFS component doesn't
+ * assume ownership of this pointer.
+ * @param ctx If vfs->flags has ESP_VFS_FLAG_CONTEXT_PTR set, a pointer
+ * which should be passed to VFS functions. Otherwise, NULL.
+ *
+ * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
+ * registered.
+ */
+esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx);
+
+
+/**
+ * Special case function for registering a VFS that uses a method other than
+ * open() to open new file descriptors from the interval <min_fd; max_fd).
+ *
+ * This is a special-purpose function intended for registering LWIP sockets to VFS.
+ *
+ * @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register().
+ * @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register().
+ * @param min_fd The smallest file descriptor this VFS will use.
+ * @param max_fd Upper boundary for file descriptors this VFS will use (the biggest file descriptor plus one).
+ *
+ * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
+ * registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries
+ * are incorrect.
+ */
+esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd);
+
+/**
+ * Special case function for registering a VFS that uses a method other than
+ * open() to open new file descriptors. In comparison with
+ * esp_vfs_register_fd_range, this function doesn't pre-registers an interval
+ * of file descriptors. File descriptors can be registered later, by using
+ * esp_vfs_register_fd.
+ *
+ * @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register().
+ * @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register().
+ * @param vfs_id Here will be written the VFS ID which can be passed to
+ * esp_vfs_register_fd for registering file descriptors.
+ *
+ * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
+ * registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries
+ * are incorrect.
+ */
+esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t *vfs_id);
+
+/**
+ * Unregister a virtual filesystem for given path prefix
+ *
+ * @param base_path file prefix previously used in esp_vfs_register call
+ * @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix
+ * hasn't been registered
+ */
+esp_err_t esp_vfs_unregister(const char* base_path);
+
+/**
+ * Special function for registering another file descriptor for a VFS registered
+ * by esp_vfs_register_with_id.
+ *
+ * @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
+ * @param fd The registered file descriptor will be written to this address.
+ *
+ * @return ESP_OK if the registration is successful,
+ * ESP_ERR_NO_MEM if too many file descriptors are registered,
+ * ESP_ERR_INVALID_ARG if the arguments are incorrect.
+ */
+esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd);
+
+/**
+ * Special function for unregistering a file descriptor belonging to a VFS
+ * registered by esp_vfs_register_with_id.
+ *
+ * @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
+ * @param fd File descriptor which should be unregistered.
+ *
+ * @return ESP_OK if the registration is successful,
+ * ESP_ERR_INVALID_ARG if the arguments are incorrect.
+ */
+esp_err_t esp_vfs_unregister_fd(esp_vfs_id_t vfs_id, int fd);
+
+/**
+ * These functions are to be used in newlib syscall table. They will be called by
+ * newlib when it needs to use any of the syscalls.
+ */
+/**@{*/
+ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size);
+off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode);
+ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size);
+int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode);
+int esp_vfs_close(struct _reent *r, int fd);
+int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st);
+int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st);
+int esp_vfs_link(struct _reent *r, const char* n1, const char* n2);
+int esp_vfs_unlink(struct _reent *r, const char *path);
+int esp_vfs_rename(struct _reent *r, const char *src, const char *dst);
+int esp_vfs_utime(const char *path, const struct utimbuf *times);
+/**@}*/
+
+/**
+ * @brief Synchronous I/O multiplexing which implements the functionality of POSIX select() for VFS
+ * @param nfds Specifies the range of descriptors which should be checked.
+ * The first nfds descriptors will be checked in each set.
+ * @param readfds If not NULL, then points to a descriptor set that on input
+ * specifies which descriptors should be checked for being
+ * ready to read, and on output indicates which descriptors
+ * are ready to read.
+ * @param writefds If not NULL, then points to a descriptor set that on input
+ * specifies which descriptors should be checked for being
+ * ready to write, and on output indicates which descriptors
+ * are ready to write.
+ * @param errorfds If not NULL, then points to a descriptor set that on input
+ * specifies which descriptors should be checked for error
+ * conditions, and on output indicates which descriptors
+ * have error conditions.
+ * @param timeout If not NULL, then points to timeval structure which
+ * specifies the time period after which the functions should
+ * time-out and return. If it is NULL, then the function will
+ * not time-out.
+ *
+ * @return The number of descriptors set in the descriptor sets, or -1
+ * when an error (specified by errno) have occurred.
+ */
+int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
+
+/**
+ * @brief Notification from a VFS driver about a read/write/error condition
+ *
+ * This function is called when the VFS driver detects a read/write/error
+ * condition as it was requested by the previous call to start_select.
+ *
+ * @param signal_sem semaphore handle which was passed to the driver by the start_select call
+ */
+void esp_vfs_select_triggered(SemaphoreHandle_t *signal_sem);
+
+/**
+ * @brief Notification from a VFS driver about a read/write/error condition (ISR version)
+ *
+ * This function is called when the VFS driver detects a read/write/error
+ * condition as it was requested by the previous call to start_select.
+ *
+ * @param signal_sem semaphore handle which was passed to the driver by the start_select call
+ * @param woken is set to pdTRUE if the function wakes up a task with higher priority
+ */
+void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *woken);
+
+/**
+ * @brief Implements the VFS layer for synchronous I/O multiplexing by poll()
+ *
+ * The implementation is based on esp_vfs_select. The parameters and return values are compatible with POSIX poll().
+ *
+ * @param fds Pointer to the array containing file descriptors and events poll() should consider.
+ * @param nfds Number of items in the array fds.
+ * @param timeout Poll() should wait at least timeout milliseconds. If the value is 0 then it should return
+ * immediately. If the value is -1 then it should wait (block) until the event occurs.
+ *
+ * @return A positive return value indicates the number of file descriptors that have been selected. The 0
+ * return value indicates a timed-out poll. -1 is return on failure and errno is set accordingly.
+ *
+ */
+int esp_vfs_poll(struct pollfd *fds, nfds_t nfds, int timeout);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //__ESP_VFS_H__
diff --git a/libiot/vfs/include/sys/dirent.h b/libiot/vfs/include/sys/dirent.h
new file mode 100644
index 0000000..ffc764c
--- /dev/null
+++ b/libiot/vfs/include/sys/dirent.h
@@ -0,0 +1,56 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * This header file provides POSIX-compatible definitions of directory
+ * access functions and related data types.
+ * See http://pubs.opengroup.org/onlinepubs/7908799/xsh/dirent.h.html
+ * for reference.
+ */
+
+/**
+ * @brief Opaque directory structure
+ */
+typedef struct {
+ uint16_t dd_vfs_idx; /*!< VFS index, not to be used by applications */
+ uint16_t dd_rsv; /*!< field reserved for future extension */
+ /* remaining fields are defined by VFS implementation */
+} DIR;
+
+/**
+ * @brief Directory entry structure
+ */
+struct dirent {
+ int d_ino; /*!< file number */
+ uint8_t d_type; /*!< not defined in POSIX, but present in BSD and Linux */
+#define DT_UNKNOWN 0
+#define DT_REG 1
+#define DT_DIR 2
+ char d_name[256]; /*!< zero-terminated file name */
+ uint32_t d_fsize;
+};
+
+DIR* opendir(const char* name);
+struct dirent* readdir(DIR* pdir);
+long telldir(DIR* pdir);
+void seekdir(DIR* pdir, long loc);
+void rewinddir(DIR* pdir);
+int closedir(DIR* pdir);
+int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
+
diff --git a/libiot/vfs/lfs.c b/libiot/vfs/lfs.c
new file mode 100644
index 0000000..c008e5e
--- /dev/null
+++ b/libiot/vfs/lfs.c
@@ -0,0 +1,949 @@
+/*
+ * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L.
+ * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * * The WHITECAT logotype cannot be changed, you can remove it, but you
+ * cannot change it in any way. The WHITECAT logotype is:
+ *
+ * /\ /\
+ * / \_____/ \
+ * /_____________\
+ * W H I T E C A T
+ *
+ * * Redistributions in binary form must retain all copyright notices printed
+ * to any local or remote output device. This include any reference to
+ * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may
+ * appear in the future.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Lua RTOS lfs vfs
+ *
+ */
+
+#include "sdkconfig.h"
+
+#if CONFIG_LUA_RTOS_USE_LFS
+
+#include "rom/spi_flash.h"
+#include "esp_partition.h"
+
+#include <freertos/FreeRTOS.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+
+#include "esp_vfs.h"
+#include <errno.h>
+
+#include "lfs.h"
+
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/list.h>
+#include <sys/fcntl.h>
+#include <sys/vfs/vfs.h>
+#include <dirent.h>
+
+static int vfs_lfs_open(const char *path, int flags, int mode);
+static ssize_t vfs_lfs_write(int fd, const void *data, size_t size);
+static ssize_t vfs_lfs_read(int fd, void * dst, size_t size);
+static int vfs_lfs_fstat(int fd, struct stat * st);
+static int vfs_lfs_close(int fd);
+static off_t vfs_lfs_lseek(int fd, off_t size, int mode);
+static int vfs_lfs_access(const char *path, int amode);
+static long vfs_lfs_telldir(DIR *dirp);
+
+static struct list files;
+static lfs_t lfs;
+
+struct vfs_lfs_context {
+ uint32_t base_addr;
+ struct mtx lock;
+};
+
+/*
+ * This function translate error codes from lfs to errno error codes
+ *
+ */
+static int lfs_to_errno(int res) {
+ switch (res) {
+ case LFS_ERR_OK:
+ return 0;
+
+ case LFS_ERR_IO:
+ case LFS_ERR_CORRUPT:
+ return EIO;
+
+ case LFS_ERR_NOENT:
+ return ENOENT;
+
+ case LFS_ERR_EXIST:
+ return EEXIST;
+
+ case LFS_ERR_NOTDIR:
+ return ENOTDIR;
+
+ case LFS_ERR_ISDIR:
+ return EISDIR;
+
+ case LFS_ERR_NOTEMPTY:
+ return ENOTEMPTY;
+
+ case LFS_ERR_BADF:
+ return EBADF;
+
+ case LFS_ERR_NOMEM:
+ return ENOMEM;
+
+ case LFS_ERR_NOSPC:
+ return ENOSPC;
+
+ case LFS_ERR_INVAL:
+ return EINVAL;
+
+ default:
+ return res;
+ }
+}
+
+static int vfs_lfs_open(const char *path, int flags, int mode) {
+ int fd;
+ int result;
+
+ // Allocate new file
+ vfs_file_t *file = calloc(1, sizeof(vfs_file_t));
+ if (!file) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ file->fs_file = (void *)calloc(1, sizeof(lfs_file_t));
+ if (!file->fs_file) {
+ free(file);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ file->path = strdup(path);
+ if (!file->path) {
+ free(file->fs_file);
+ free(file);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // Add file to file list and get the file descriptor
+ int res = lstadd(&files, file, &fd);
+ if (res) {
+ free(file->fs_file);
+ free(file->path);
+ free(file);
+ errno = res;
+ return -1;
+ }
+
+ // Translate flags to lfs flags
+ int lfs_flags = 0;
+
+ if (flags == O_APPEND)
+ lfs_flags |= LFS_O_APPEND;
+
+ if (flags == O_RDONLY)
+ lfs_flags |= LFS_O_RDONLY;
+
+ if (flags & O_WRONLY)
+ lfs_flags |= LFS_O_WRONLY;
+
+ if (flags & O_RDWR)
+ lfs_flags |= LFS_O_RDWR;
+
+ if (flags & O_EXCL)
+ lfs_flags |= LFS_O_EXCL;
+
+ if (flags & O_CREAT)
+ lfs_flags |= LFS_O_CREAT;
+
+ if (flags & O_TRUNC)
+ lfs_flags |= LFS_O_TRUNC;
+
+ if (*path == '/') {
+ path++;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ if ((result = lfs_file_open(&lfs, file->fs_file, path, lfs_flags)) < 0) {
+ errno = lfs_to_errno(result);
+ lstremove(&files, fd, 0);
+
+ free(file->fs_file);
+ free(file->path);
+ free(file);
+
+ mtx_unlock(&ctx->lock);
+
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return fd;
+}
+
+static ssize_t vfs_lfs_write(int fd, const void *data, size_t size) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Write to file
+ result = lfs_file_write(&lfs, file->fs_file, (void *)data, size);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return result;
+}
+
+static ssize_t vfs_lfs_read(int fd, void *dst, size_t size) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Read from file
+ result = lfs_file_read(&lfs, file->fs_file, dst, size);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return result;
+}
+
+static int vfs_lfs_fstat(int fd, struct stat *st) {
+ vfs_file_t *file;
+ struct lfs_info info;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Init stats
+ memset(st, 0, sizeof(struct stat));
+
+ // Set block size for this file system
+ st->st_blksize = lfs.cfg->block_size;
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Get the file stats
+ result = lfs_stat(&lfs, file->path, &info);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ st->st_size = info.size;
+ st->st_mode = ((info.type==LFS_TYPE_REG)?S_IFREG:S_IFDIR);
+
+ return 0;
+}
+
+static int vfs_lfs_close(int fd) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Close file
+ result = lfs_file_close(&lfs, file->fs_file);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ // Remove file from file list
+ lstremove(&files, fd, 0);
+
+ free(file->fs_file);
+ free(file->path);
+ free(file);
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static off_t vfs_lfs_lseek(int fd, off_t size, int mode) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ int whence = LFS_SEEK_CUR;
+
+ switch (mode) {
+ case SEEK_SET:
+ whence = LFS_SEEK_SET;
+ break;
+ case SEEK_CUR:
+ whence = LFS_SEEK_CUR;
+ break;
+ case SEEK_END:
+ whence = LFS_SEEK_END;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ result = lfs_file_seek(&lfs, file->fs_file, size, whence);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return result;
+}
+
+static int vfs_lfs_stat(const char *path, struct stat *st) {
+ struct lfs_info info;
+ int result;
+
+ // Init stats
+ memset(st, 0, sizeof(struct stat));
+
+ // Set block size for this file system
+ st->st_blksize = lfs.cfg->block_size;
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Get the file stats
+ result = lfs_stat(&lfs, path, &info);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ st->st_size = info.size;
+ st->st_mode = ((info.type==LFS_TYPE_REG)?S_IFREG:S_IFDIR);
+
+ return 0;
+}
+
+static int vfs_lfs_access(const char *path, int amode) {
+#if 0
+ struct stat s;
+
+ if (vfs_lfs_stat(path, &s) < 0) {
+ return -1;
+ }
+
+ if (s.st_mode != S_IFREG) {
+ errno = EACCES;
+ return -1;
+ }
+
+ return 0;
+#endif
+ return 0;
+
+}
+
+static int vfs_lfs_unlink(const char *path) {
+ struct lfs_info info;
+ int result;
+
+ // Sanity checks
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Is not a directory
+ result = lfs_stat(&lfs, path, &info);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ if (info.type == LFS_TYPE_DIR) {
+ mtx_unlock(&ctx->lock);
+ errno = EPERM;
+ return -1;
+ }
+
+ // Unlink
+ if ((result = lfs_remove(&lfs, path)) < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static int vfs_lfs_rename(const char *src, const char *dst) {
+ int result;
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ if ((result = lfs_rename(&lfs, src, dst)) < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static DIR* vfs_lfs_opendir(const char *name) {
+ int result;
+
+ vfs_dir_t *dir;
+
+ dir = vfs_allocate_dir("lfs", name);
+ if (!dir) {
+ return NULL;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Open directory
+ if ((result = lfs_dir_open(&lfs, dir->fs_dir, name)) < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ vfs_free_dir(dir);
+
+ return NULL;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return (DIR *)dir;
+}
+
+static int vfs_lfs_rmdir(const char *name) {
+ struct lfs_info info;
+ int result;
+
+ // Sanity checks
+ if (strcmp(name,"/") == 0) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ // Is a directory
+ result = lfs_stat(&lfs, name, &info);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ if (info.type != LFS_TYPE_DIR) {
+ mtx_unlock(&ctx->lock);
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ // Unlink
+ if ((result = lfs_remove(&lfs, name)) < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static struct dirent *vfs_lfs_readdir(DIR *pdir) {
+ vfs_dir_t *dir = (vfs_dir_t *)pdir;
+ struct dirent *ent = &dir->ent;
+ int result;
+
+ // Clear current entry
+ memset(ent, 0, sizeof(struct dirent));
+
+ // If there are mount points to read, read them first
+ if (dir->mount) {
+ struct dirent *ment = mount_readdir((DIR *)dir);
+ if (ment) {
+ return ment;
+ }
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+again:
+ // Read next directory entry
+ if ((result = lfs_dir_read(&lfs, ((vfs_dir_t *)pdir)->fs_dir, (struct lfs_info *)dir->fs_info)) < 0) {
+ errno = lfs_to_errno(result);
+ } else if (result > 0) {
+ if ((strcmp(((struct lfs_info *)dir->fs_info)->name,".") == 0) || (strcmp(((struct lfs_info *)dir->fs_info)->name,"..") == 0)) {
+ goto again;
+ }
+
+ ent->d_type = (((struct lfs_info *)dir->fs_info)->type == LFS_TYPE_REG)?DT_REG:DT_DIR;
+ ent->d_fsize = ((struct lfs_info *)dir->fs_info)->size;
+ strlcpy(ent->d_name, ((struct lfs_info *)dir->fs_info)->name, MAXNAMLEN);
+
+ mtx_unlock(&ctx->lock);
+
+ return ent;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return NULL;
+}
+
+static long vfs_lfs_telldir(DIR *dirp) {
+ vfs_dir_t *dir = (vfs_dir_t *)dirp;
+
+ lfs_soff_t offset;
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ offset = lfs_dir_tell(&lfs, dir->fs_dir);
+ if (offset < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = EBADF;
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return (long)offset;
+}
+
+static int vfs_piffs_closedir(DIR *pdir) {
+ vfs_dir_t *dir = ((vfs_dir_t *)pdir);
+ int result;
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ if ((result = lfs_dir_close(&lfs, dir->fs_dir)) < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ vfs_free_dir(dir);
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static int vfs_lfs_mkdir(const char *path, mode_t mode) {
+ int result;
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ if ((result = lfs_mkdir(&lfs, path)) < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static int vfs_lfs_fsync(int fd) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ result = lfs_file_sync(&lfs, file->fs_file);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static int vfs_lfs_ftruncate(int fd, off_t length) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context;
+
+ mtx_lock(&ctx->lock);
+
+ result = lfs_file_truncate(&lfs, file->fs_file, length);
+ if (result < 0) {
+ mtx_unlock(&ctx->lock);
+ errno = lfs_to_errno(result);
+ return -1;
+ }
+
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static int lfs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) {
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)c->context;
+
+ if (spi_flash_read(ctx->base_addr + (block * c->block_size) + off, buffer, size) != 0) {
+ return LFS_ERR_IO;
+ }
+
+ return 0;
+}
+
+static int lfs_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)c->context;
+
+ if (spi_flash_write(ctx->base_addr + (block * c->block_size) + off, buffer, size) != 0) {
+ return LFS_ERR_IO;
+ }
+
+ return 0;
+}
+
+static int lfs_erase(const struct lfs_config *c, lfs_block_t block) {
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)c->context;
+
+ if (spi_flash_erase_sector((ctx->base_addr + (block * c->block_size)) >> 12) != 0) {
+ return LFS_ERR_IO;
+ }
+
+ return 0;
+}
+
+static int lfs_sync(const struct lfs_config *c) {
+ return 0;
+}
+
+static struct lfs_config *lfs_config() {
+ // Find a partition
+ uint32_t base_address = 0;
+ uint32_t fs_size = 0;
+
+ const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, LUA_RTOS_LFS_PART, NULL);
+
+ if (!partition) {
+ syslog(LOG_ERR, "lfs can't find a valid partition");
+ return NULL;
+ } else {
+ base_address = partition->address;
+ fs_size = partition->size;
+ }
+
+ // Allocate file system configuration data
+ struct lfs_config *cfg = calloc(1, sizeof(struct lfs_config));
+ if (!cfg) {
+ syslog(LOG_ERR, "lfs not enough memory");
+ return NULL;
+ }
+
+ struct vfs_lfs_context *ctx = calloc(1, sizeof(struct vfs_lfs_context));
+ if (!ctx) {
+ free(cfg);
+
+ syslog(LOG_ERR, "lfs not enough memory");
+ return NULL;
+ }
+
+ // Configure the file system
+ cfg->read = lfs_read;
+ cfg->prog = lfs_prog;
+ cfg->erase = lfs_erase;
+ cfg->sync = lfs_sync;
+
+ cfg->block_size = CONFIG_LUA_RTOS_LFS_BLOCK_SIZE;
+ cfg->read_size = CONFIG_LUA_RTOS_LFS_READ_SIZE;
+ cfg->prog_size = CONFIG_LUA_RTOS_LFS_PROG_SIZE;
+ cfg->block_count = fs_size / cfg->block_size;
+ cfg->lookahead = cfg->block_count;
+
+ cfg->context = ctx;
+ ctx->base_addr = base_address;
+
+ return cfg;
+}
+
+int vfs_lfs_mount(const char *target) {
+ esp_vfs_t vfs = {
+ .flags = ESP_VFS_FLAG_DEFAULT,
+ .write = &vfs_lfs_write,
+ .open = &vfs_lfs_open,
+ .fstat = &vfs_lfs_fstat,
+ .close = &vfs_lfs_close,
+ .read = &vfs_lfs_read,
+ .lseek = &vfs_lfs_lseek,
+ .stat = &vfs_lfs_stat,
+ .link = NULL,
+ .unlink = &vfs_lfs_unlink,
+ .rename = &vfs_lfs_rename,
+ .mkdir = &vfs_lfs_mkdir,
+ .opendir = &vfs_lfs_opendir,
+ .readdir = &vfs_lfs_readdir,
+ .closedir = &vfs_piffs_closedir,
+ .rmdir = &vfs_lfs_rmdir,
+ .fsync = &vfs_lfs_fsync,
+ .access = &vfs_lfs_access,
+ .ftruncate = &vfs_lfs_ftruncate,
+ .telldir = &vfs_lfs_telldir,
+ };
+
+ // Get configuration
+ struct lfs_config *cfg = lfs_config();
+ if (!cfg) {
+ return -1;
+ }
+
+ struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)(cfg->context);
+
+ mtx_init(&ctx->lock, NULL, NULL, 0);
+
+ syslog(LOG_INFO,
+ "lfs start address at 0x%x, size %d Kb",
+ ctx->base_addr, (cfg->block_count * cfg->block_size) / 1024);
+
+ syslog(LOG_INFO, "lfs %d blocks, %d bytes/block, %d bytes/read, %d bytes/write",cfg->block_count,cfg->block_size, cfg->read_size, cfg->prog_size);
+
+ // Mount file system
+ int err = lfs_mount(&lfs, cfg);
+ if (err < 0) {
+ syslog(LOG_INFO, "lfs formatting ...");
+
+ int block;
+ for(block = 0;block < cfg->block_count;block++) {
+ lfs_erase(cfg, block);
+ }
+
+ lfs_format(&lfs, cfg);
+ err = lfs_mount(&lfs, cfg);
+ }
+
+ if (err == LFS_ERR_OK) {
+ lstinit(&files, 0, LIST_DEFAULT);
+
+ // Register the file system
+ ESP_ERROR_CHECK(esp_vfs_register("/lfs", &vfs, NULL));
+
+ syslog(LOG_INFO, "lfs mounted on %s", target);
+
+ return 0;
+ } else {
+ free(cfg);
+ free(ctx);
+
+ syslog(LOG_INFO, "lfs mount error");
+ }
+
+ return -1;
+}
+
+int vfs_lfs_umount(const char *target) {
+ // Unmount
+ lfs_umount(&lfs);
+
+ // Free resources
+ if (lfs.cfg) {
+ if (lfs.cfg->context) {
+ mtx_destroy(&((struct vfs_lfs_context *)lfs.cfg->context)->lock);
+ free(lfs.cfg->context);
+ }
+
+ free((struct lfs_config *)lfs.cfg);
+ }
+
+ lstdestroy(&files, 1);
+
+ // Unregister vfs
+ esp_vfs_unregister("/lfs");
+
+ syslog(LOG_INFO, "lfs unmounted");
+
+ return 0;
+}
+
+int vfs_lfs_format(const char *target) {
+ // Unmount first
+ vfs_lfs_umount(target);
+
+ // Get configuration
+ struct lfs_config *cfg = lfs_config();
+ if (!cfg) {
+ return -1;
+ }
+
+ // Format
+ int block;
+ for(block = 0;block < cfg->block_count;block++) {
+ lfs_erase(cfg, block);
+ }
+
+ int err = lfs_format(&lfs, cfg);
+
+ // Free resources
+ if (lfs.cfg) {
+ if (lfs.cfg->context) {
+ free(lfs.cfg->context);
+ }
+
+ free((struct lfs_config *)lfs.cfg);
+ }
+
+ if (err == LFS_ERR_OK) {
+ // Mount again
+ vfs_lfs_mount(target);
+ }
+
+ return -1;
+}
+
+int vfs_lfs_fsstat(const char *target, u32_t *total, u32_t *used) {
+
+ int err = lfs_info(&lfs, total, used);
+ if (err != 0) {
+ syslog(LOG_ERR, "lfs get fs info of '%s' (%i)", target, err);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/libiot/vfs/mkfile b/libiot/vfs/mkfile
new file mode 100644
index 0000000..600ebc7
--- /dev/null
+++ b/libiot/vfs/mkfile
@@ -0,0 +1,7 @@
+OFILES=\
+ $OFILES\
+ vfs/spiffs.$O\
+
+CFLAGS=\
+ $CFLAGS\
+ -Ivfs/include\
diff --git a/libiot/vfs/ramfs.c b/libiot/vfs/ramfs.c
new file mode 100644
index 0000000..270209c
--- /dev/null
+++ b/libiot/vfs/ramfs.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L.
+ * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * * The WHITECAT logotype cannot be changed, you can remove it, but you
+ * cannot change it in any way. The WHITECAT logotype is:
+ *
+ * /\ /\
+ * / \_____/ \
+ * /_____________\
+ * W H I T E C A T
+ *
+ * * Redistributions in binary form must retain all copyright notices printed
+ * to any local or remote output device. This include any reference to
+ * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may
+ * appear in the future.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Lua RTOS, RAM file system
+ *
+ */
+
+#include "sdkconfig.h"
+
+#if CONFIG_LUA_RTOS_USE_RAM_FS
+
+#include "esp_partition.h"
+
+#include <freertos/FreeRTOS.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+
+#include "esp_vfs.h"
+
+#include <errno.h>
+
+#include "ramfs.h"
+
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/list.h>
+#include <sys/fcntl.h>
+#include <sys/vfs/vfs.h>
+#include <dirent.h>
+
+static int vfs_ramfs_open(const char *path, int flags, int mode);
+static ssize_t vfs_ramfs_write(int fd, const void *data, size_t size);
+static ssize_t vfs_ramfs_read(int fd, void * dst, size_t size);
+static int vfs_ramfs_fstat(int fd, struct stat * st);
+static int vfs_ramfs_close(int fd);
+static off_t vfs_ramfs_lseek(int fd, off_t size, int mode);
+static int vfs_ramfs_access(const char *path, int amode);
+
+static struct list files;
+static ramfs_t fs;
+
+/*
+ * This function translate file system errors to POSIX errors
+ *
+ */
+static int ramfs_to_errno(int res) {
+ switch (res) {
+ case RAMFS_ERR_OK:
+ return 0;
+ case RAMFS_ERR_NOMEM:
+ return ENOMEM;
+ case RAMFS_ERR_NOENT:
+ return ENOENT;
+ case RAMFS_ERR_EXIST:
+ return EEXIST;
+ case RAMFS_ERR_NOTDIR:
+ return ENOTDIR;
+ case RAMFS_ERR_BADF:
+ return EBADF;
+ case RAMFS_ERR_ACCESS:
+ return EACCES;
+ case RAMFS_ERR_NOSPC:
+ return ENOSPC;
+ case RAMFS_ERR_INVAL:
+ return EINVAL;
+ case RAMFS_ERR_ISDIR:
+ return EISDIR;
+ case RAMFS_ERR_NOTEMPTY:
+ return ENOTEMPTY;
+ case RAMFS_ERR_BUSY:
+ return EBUSY;
+ case RAMFS_ERR_PERM:
+ return EPERM;
+ case RAMFS_ERR_NAMETOOLONG:
+ return ENAMETOOLONG;
+ }
+
+ return ENOTSUP;
+}
+
+static int vfs_ramfs_open(const char *path, int flags, int mode) {
+ int fd;
+ int result;
+
+ // Allocate new file
+ vfs_file_t *file = calloc(1, sizeof(vfs_file_t));
+ if (!file) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ file->fs_file = (void *)calloc(1, sizeof(ramfs_file_t));
+ if (!file->fs_file) {
+ free(file);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // Add file to file list and get the file descriptor
+ int res = lstadd(&files, file, &fd);
+ if (res) {
+ free(file->fs_file);
+ free(file);
+ errno = res;
+ return -1;
+ }
+
+ // Translate POSIX flags to file system flags
+ int ramfs_flags = 0;
+
+ // Access mode
+ int access_mode = flags & O_ACCMODE;
+
+ if (access_mode == O_RDONLY) {
+ ramfs_flags |= RAMFS_O_RDONLY;
+ } else if (access_mode == O_WRONLY) {
+ ramfs_flags |= RAMFS_O_WRONLY;
+ } else if (access_mode == O_RDWR) {
+ ramfs_flags |= RAMFS_O_RDWR;
+ }
+
+ // File status
+ if (flags & O_CREAT) {
+ ramfs_flags |= RAMFS_O_CREAT;
+ }
+
+ if (flags & O_EXCL) {
+ ramfs_flags |= RAMFS_O_EXCL;
+ }
+
+ if (flags & O_TRUNC) {
+ ramfs_flags |= RAMFS_O_TRUNC;
+ }
+
+ if (flags & O_APPEND) {
+ ramfs_flags |= RAMFS_O_APPEND;
+ }
+
+ if ((result = ramfs_file_open(&fs, file->fs_file, path, ramfs_flags)) != RAMFS_ERR_OK) {
+ errno = ramfs_to_errno(result);
+ lstremove(&files, fd, 0);
+
+ free(file->fs_file);
+ free(file);
+
+ return -1;
+ }
+
+ return fd;
+}
+
+static ssize_t vfs_ramfs_write(int fd, const void *data, size_t size) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Write to file
+ result = ramfs_file_write(&fs, file->fs_file, (void *)data, size);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return result;
+}
+
+static ssize_t vfs_ramfs_read(int fd, void *dst, size_t size) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Read from file
+ result = ramfs_file_read(&fs, file->fs_file, dst, size);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return result;
+}
+
+static int vfs_ramfs_fstat(int fd, struct stat *st) {
+ vfs_file_t *file;
+ ramfs_info_t info;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Init stats
+ memset(st, 0, sizeof(struct stat));
+
+ // Get the file stats
+ result = ramfs_file_stat(&fs, file->fs_file, &info);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ st->st_size = info.size;
+ st->st_mode = ((info.type==RAMFS_FILE)?S_IFREG:S_IFDIR);
+ st->st_blksize = fs.block_size;
+
+ return 0;
+}
+
+static int vfs_ramfs_close(int fd) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Close file
+ result = ramfs_file_close(&fs, file->fs_file);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ // Remove file from file list
+ lstremove(&files, fd, 0);
+
+ free(file->fs_file);
+ free(file);
+
+ return 0;
+}
+
+static off_t vfs_ramfs_lseek(int fd, off_t size, int mode) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Convert POSIX whence to file system whence
+ int whence = RAMFS_SEEK_CUR;
+
+ switch (mode) {
+ case SEEK_SET:
+ whence = RAMFS_SEEK_SET;
+ break;
+ case SEEK_CUR:
+ whence = RAMFS_SEEK_CUR;
+ break;
+ case SEEK_END:
+ whence = RAMFS_SEEK_END;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ result = ramfs_file_seek(&fs, file->fs_file, size, whence);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return result;
+}
+
+static int vfs_ramfs_stat(const char *path, struct stat *st) {
+ ramfs_info_t info;
+ int result;
+
+ // Init stats
+ memset(st, 0, sizeof(struct stat));
+
+ // Get the file stats
+ result = ramfs_stat(&fs, path, &info);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ st->st_size = info.size;
+ st->st_mode = ((info.type==RAMFS_FILE)?S_IFREG:S_IFDIR);
+ st->st_blksize = fs.block_size;
+
+ return 0;
+}
+
+static int vfs_ramfs_access(const char *path, int amode) {
+#if 0
+ struct stat s;
+
+ if (vfs_ramfs_stat(path, &s) < 0) {
+ return -1;
+ }
+
+ if (s.st_mode != S_IFREG) {
+ errno = EACCES;
+ return -1;
+ }
+
+ return 0;
+#endif
+ return 0;
+
+}
+
+static int vfs_ramfs_unlink(const char *path) {
+ int result;
+
+ if ((result = ramfs_unlink(&fs, path)) < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vfs_ramfs_rename(const char *src, const char *dst) {
+ int result;
+
+ if ((result = ramfs_rename(&fs, src, dst)) < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return 0;
+}
+
+static DIR* vfs_ramfs_opendir(const char *name) {
+ int result;
+
+ vfs_dir_t *dir = vfs_allocate_dir("ramfs", name);
+ if (!dir) {
+ return NULL;
+ }
+
+ // Open directory
+ if ((result = ramfs_dir_open(&fs, dir->fs_dir, name)) < 0) {
+ errno = ramfs_to_errno(result);
+ vfs_free_dir(dir);
+
+ return NULL;
+ }
+
+ return (DIR *)dir;
+}
+
+static int vfs_ramfs_rmdir(const char *name) {
+ int result;
+
+ if ((result = ramfs_rmdir(&fs, name)) < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct dirent *vfs_ramfs_readdir(DIR *pdir) {
+ vfs_dir_t *dir = (vfs_dir_t *)pdir;
+ struct dirent *ent = &dir->ent;
+ int result;
+
+ // Clear current entry
+ memset(ent, 0, sizeof(struct dirent));
+
+ // If there are mount points to read, read them first
+ if (dir->mount) {
+ struct dirent *ment = mount_readdir((DIR *)dir);
+ if (ment) {
+ return ment;
+ }
+ }
+
+ // Read next directory entry
+ if ((result = ramfs_dir_read(&fs, ((vfs_dir_t *)pdir)->fs_dir, (ramfs_info_t *)dir->fs_info)) == RAMFS_ERR_OK) {
+ ent->d_type = (((ramfs_info_t *)dir->fs_info)->type == RAMFS_FILE)?DT_REG:DT_DIR;
+ ent->d_fsize = ((ramfs_info_t *)dir->fs_info)->size;
+ strlcpy(ent->d_name, ((ramfs_info_t *)dir->fs_info)->name, MAXNAMLEN);
+
+ return ent;
+ }
+
+ if (result != RAMFS_ERR_NOENT) {
+ errno = ramfs_to_errno(result);
+ }
+
+ return NULL;
+}
+
+static long vfs_ramfs_telldir(DIR *dirp) {
+ vfs_dir_t *dir = (vfs_dir_t *)dirp;
+ ramfs_off_t offset;
+
+ offset = ramfs_telldir(&fs, dir->fs_dir);
+ if (offset < 0) {
+ errno = ramfs_to_errno(offset);
+ return -1;
+ }
+
+ return (long)offset;
+}
+
+static int vfs_ramfs_closedir(DIR *pdir) {
+ vfs_dir_t *dir = ((vfs_dir_t *)pdir);
+ int result;
+
+ if ((result = ramfs_dir_close(&fs, dir->fs_dir)) < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ vfs_free_dir(dir);
+
+ return 0;
+}
+
+static int vfs_ramfs_mkdir(const char *path, mode_t mode) {
+ int result;
+
+ if ((result = ramfs_mkdir(&fs, path)) < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vfs_ramfs_fsync(int fd) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ result = ramfs_file_sync(&fs, file->fs_file);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vfs_ramfs_ftruncate(int fd, off_t length) {
+ vfs_file_t *file;
+ int result;
+
+ // Get file from file list
+ result = lstget(&files, fd, (void **) &file);
+ if (result) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if ((result = ramfs_file_truncate(&fs, file->fs_file, length)) != RAMFS_ERR_OK) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vfs_ramfs_truncate(const char *path, off_t length) {
+ ramfs_file_t file;
+ int result;
+
+ if ((result = ramfs_file_open(&fs, &file, path, RAMFS_O_RDWR)) != RAMFS_ERR_OK) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ if ((result = ramfs_file_truncate(&fs, &file, length)) != RAMFS_ERR_OK) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ result = ramfs_file_close(&fs, &file);
+ if (result < 0) {
+ errno = ramfs_to_errno(result);
+ return -1;
+ }
+
+ return 0;
+}
+
+int vfs_ramfs_mount(const char *target) {
+ esp_vfs_t vfs = {
+ .flags = ESP_VFS_FLAG_DEFAULT,
+ .write = &vfs_ramfs_write,
+ .open = &vfs_ramfs_open,
+ .fstat = &vfs_ramfs_fstat,
+ .close = &vfs_ramfs_close,
+ .read = &vfs_ramfs_read,
+ .lseek = &vfs_ramfs_lseek,
+ .stat = &vfs_ramfs_stat,
+ .link = NULL,
+ .unlink = &vfs_ramfs_unlink,
+ .rename = &vfs_ramfs_rename,
+ .mkdir = &vfs_ramfs_mkdir,
+ .opendir = &vfs_ramfs_opendir,
+ .readdir = &vfs_ramfs_readdir,
+ .closedir = &vfs_ramfs_closedir,
+ .rmdir = &vfs_ramfs_rmdir,
+ .fsync = &vfs_ramfs_fsync,
+ .access = &vfs_ramfs_access,
+ .telldir = &vfs_ramfs_telldir,
+ .truncate = &vfs_ramfs_truncate,
+ .ftruncate = &vfs_ramfs_ftruncate,
+ };
+
+ int res;
+ ramfs_config_t config;
+
+ config.size = CONFIG_LUA_RTOS_RAM_FS_SIZE;
+ config.block_size = CONFIG_LUA_RTOS_RAM_FS_BLOCK_SIZE;
+
+ syslog(LOG_INFO, "ramfs size %d Kb, block size %d bytes", config.size / 1024, config.block_size);
+
+ if ((res = ramfs_mount(&fs, &config)) == RAMFS_ERR_OK) {
+ lstinit(&files, 0, LIST_DEFAULT);
+
+ // Register the file system
+ ESP_ERROR_CHECK(esp_vfs_register("/ramfs", &vfs, NULL));
+
+ syslog(LOG_INFO, "ramfs mounted on %s", target);
+
+ return 0;
+ } else {
+ syslog(LOG_INFO, "ramfs mount error");
+ }
+
+ return -1;
+}
+
+int vfs_ramfs_umount(const char *target) {
+ ramfs_umount(&fs);
+ esp_vfs_unregister("/ramfs");
+
+ syslog(LOG_INFO, "ramfs unmounted");
+
+ return 0;
+}
+
+int vfs_ramfs_format(const char *target) {
+ vfs_ramfs_umount(target);
+ vfs_ramfs_mount(target);
+
+ return 0;
+}
+
+int vfs_ramfs_fsstat(const char *target, u32_t *total, u32_t *used) {
+
+ if (total) {
+ *total = fs.size;
+ }
+
+ if (used) {
+ *used = fs.current_size;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/libiot/vfs/spiffs.c b/libiot/vfs/spiffs.c
new file mode 100644
index 0000000..cdd445a
--- /dev/null
+++ b/libiot/vfs/spiffs.c
@@ -0,0 +1,1423 @@
+/*
+ * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L.
+ * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * * The WHITECAT logotype cannot be changed, you can remove it, but you
+ * cannot change it in any way. The WHITECAT logotype is:
+ *
+ * /\ /\
+ * / \_____/ \
+ * /_____________\
+ * W H I T E C A T
+ *
+ * * Redistributions in binary form must retain all copyright notices printed
+ * to any local or remote output device. This include any reference to
+ * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may
+ * appear in the future.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Lua RTOS spiffs vfs
+ *
+ */
+
+//#include "sdkconfig.h"
+
+//#if CONFIG_LUA_RTOS_USE_SPIFFS
+
+//#include "esp_partition.h"
+
+#include <FreeRTOS.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include <sys/stat.h>
+
+#include "esp_vfs.h"
+//#include "esp_attr.h"
+#include <errno.h>
+
+#include "../spiffs/spiffs.h"
+#include "../spiffs/k210_spiffs.h"
+#include "../spiffs/spiffs_nucleus.h"
+//#include <sys/syslog.h>
+//#include <sys/mount.h>
+#include "../sys/mutex.h"
+#include "../sys/list.h"
+#include <sys/fcntl.h>
+#include "vfs.h"
+#include <dirent.h>
+
+#include "../hal/w25qxx.h"
+
+#define syslog(a, ...) printf(__VA_ARGS__)
+
+static int vfs_spiffs_open(const char *path, int flags, int mode);
+static ssize_t vfs_spiffs_write(int fdi, const void *data, size_t size);
+static ssize_t vfs_spiffs_read(int fdi, void * dst, size_t size);
+static int vfs_spiffs_fstat(int fdi, struct stat * st);
+static int vfs_spiffs_close(int fdi);
+static off_t vfs_spiffs_lseek(int fdi, off_t size, int mode);
+static int vfs_spiffs_access(const char *path, int amode);
+
+#if !defined(max)
+#define max(A,B) ( (A) > (B) ? (A):(B))
+#endif
+
+#if !defined(min)
+#define min(A,B) ( (A) < (B) ? (A):(B))
+#endif
+
+#define VFS_SPIFFS_FLAGS_READONLY (1 << 0)
+
+static spiffs fs;
+static struct list files;
+
+static u8_t *my_spiffs_work_buf = NULL;
+static u8_t *my_spiffs_fds = NULL;
+static u8_t *my_spiffs_cache = NULL;
+
+static struct mtx vfs_mtx;
+static struct mtx ll_mtx;
+
+
+typedef struct fd_itm fd_itm;
+
+struct fd_itm {
+ long fd;
+ fd_itm* next;
+};
+
+fd_itm* fds_root = NULL;
+fd_itm** fds_root_last = &fds_root;
+int fds_cnt = 0;
+
+
+static int new_fd_itm(long fd){
+ fd_itm *f = malloc(sizeof(fd_itm));
+
+ f->next = NULL;
+ f->fd = fd;
+
+ *fds_root_last = f;
+ fds_root_last = &f->next;
+
+ fds_cnt++;
+
+ return fds_cnt - 1;
+}
+
+static long del_fd_itm(int fdi){
+ long fd = -1;
+ int i;
+ fd_itm *f = fds_root;
+ fd_itm **pref = &fds_root;
+
+ if(fdi < 0 || fdi >= fds_cnt || f == NULL)
+ return -1;
+
+ for(i = 0; i < fdi; i++){
+ pref = &f->next;
+ f = f->next;
+ }
+ *pref = f->next;
+
+ fd = f->fd;
+
+ free(f);
+
+ fds_cnt--;
+
+ return fd;
+}
+
+static long get_fd_itm(int fdi){
+ long fd = -1;
+ int i;
+ fd_itm *f = fds_root;
+
+ if(fdi < 0 || fdi >= fds_cnt || f == NULL)
+ return -1;
+
+ for(i = 0; i < fdi; i++){
+ f = f->next;
+ }
+ fd = f->fd;
+
+ return fd;
+}
+
+
+
+static void dir_path(char *npath, uint8_t base) {
+ int len = strlen(npath);
+
+ if (base) {
+ char *c;
+
+ c = &npath[len - 1];
+ while (c >= npath) {
+ if (*c == '/') {
+ break;
+ }
+
+ len--;
+ c--;
+ }
+ }
+
+ if (len > 1) {
+ if (npath[len - 1] == '.') {
+ npath[len - 1] = '\0';
+
+ if (npath[len - 2] == '/') {
+ npath[len - 2] = '\0';
+ }
+ } else {
+ if (npath[len - 1] == '/') {
+ npath[len - 1] = '\0';
+ }
+ }
+ } else {
+ if ((npath[len - 1] == '/') || (npath[len - 1] == '.')) {
+ npath[len - 1] = '\0';
+ }
+ }
+
+ strlcat(npath, "/.", PATH_MAX);
+}
+
+static void check_path(const char *path, uint8_t *base_is_dir,
+ uint8_t *full_is_dir, uint8_t *is_file, int *filenum) {
+ char bpath[PATH_MAX + 1]; // Base path
+ char fpath[PATH_MAX + 1]; // Full path
+ struct spiffs_dirent e;
+ spiffs_DIR d;
+ int file_num = 0;
+
+ *filenum = 0;
+ *base_is_dir = 0;
+ *full_is_dir = 0;
+ *is_file = 0;
+
+ // Get base directory name
+ strlcpy(bpath, path, PATH_MAX);
+ dir_path(bpath, 1);
+
+ // Get full directory name
+ strlcpy(fpath, path, PATH_MAX);
+ dir_path(fpath, 0);
+
+ SPIFFS_opendir(&fs, "/", &d);
+ while (SPIFFS_readdir(&d, &e)) {
+ if (!strcmp(bpath, (const char *) e.name)) {
+ *base_is_dir = 1;
+ }
+
+ if (!strcmp(fpath, (const char *) e.name)) {
+ *full_is_dir = 1;
+ }
+
+ if (!strcmp(path, (const char *) e.name)) {
+ *is_file = 1;
+ }
+
+ if (!strncmp(fpath, (const char *) e.name, min(strlen((char * )e.name), strlen(fpath) - 1))) {
+ if (strlen((const char *) e.name) >= strlen(fpath) && strcmp(fpath, (const char *) e.name)) {
+ file_num++;
+ }
+ }
+ }
+ SPIFFS_closedir(&d);
+
+ *filenum = file_num;
+}
+
+/*
+ * This function translate error codes from SPIFFS to errno error codes
+ *
+ */
+static int spiffs_result(int res) {
+ switch(res) {
+ case SPIFFS_OK :
+ case SPIFFS_ERR_END_OF_OBJECT:
+ return 0;
+ case SPIFFS_ERR_NOT_WRITABLE:
+ case SPIFFS_ERR_NOT_READABLE:
+ return EACCES;
+ case SPIFFS_ERR_NOT_MOUNTED :
+ case SPIFFS_ERR_NOT_A_FS :
+ return ENODEV;
+ case SPIFFS_ERR_FULL :
+ return ENOSPC;
+ case SPIFFS_ERR_BAD_DESCRIPTOR :
+ return EBADF;
+ case SPIFFS_ERR_MOUNTED :
+ return EEXIST;
+ case SPIFFS_ERR_FILE_EXISTS :
+ return EEXIST;
+ case SPIFFS_ERR_NOT_FOUND :
+ case SPIFFS_ERR_CONFLICTING_NAME:
+ case SPIFFS_ERR_NOT_A_FILE :
+ case SPIFFS_ERR_DELETED :
+ case SPIFFS_ERR_FILE_DELETED :
+ return ENOENT;
+ case SPIFFS_ERR_NAME_TOO_LONG :
+ return ENAMETOOLONG;
+ case SPIFFS_ERR_RO_NOT_IMPL :
+ case SPIFFS_ERR_RO_ABORTED_OPERATION :
+ return EROFS;
+ default:
+ return EIO;
+ }
+
+ return ENOTSUP;
+}
+
+static int vfs_spiffs_traverse(const char *pathp, int *pis_dir, int *filenum, int *valid_prefix) {
+ char path[PATH_MAX + 1];
+ char current[PATH_MAX + 3]; // Current path
+ char *dir; // Current directory
+ spiffs_stat stat;
+ int res;
+
+ int is_dir = 0;
+
+ if (pis_dir) {
+ *pis_dir = 0;
+ }
+
+ if (filenum) {
+ *filenum = 0;
+ }
+
+ if (valid_prefix) {
+ *valid_prefix = 1;
+ }
+
+ if (strlen(pathp) > PATH_MAX - 3) {
+ return ENAMETOOLONG;
+ }
+
+ // Copy original path, because the strtok function changes the original string
+ strlcpy(path, pathp, PATH_MAX);
+
+ // Current directory is empty
+ strcpy(current, "");
+
+ // Get the first directory in path
+ dir = strtok((char *)path, "/");
+ while (dir) {
+ // Append current directory to the current path
+ strncat(current, "/", PATH_MAX);
+ strncat(current, dir, PATH_MAX);
+
+ // To check if the current path is a directory
+ // we must append /.
+ strncat(current, "/.", PATH_MAX);
+
+ res = SPIFFS_stat(&fs, current, &stat);
+
+ // Remove /. from the current path to check if the current path
+ // corresponds to a file in case that is required later
+ *(current + strlen(current) - 2) = '\0';
+
+ // Get next directory in path
+ dir = strtok(NULL, "/");
+
+ if (res != SPIFFS_OK) {
+ // Current path is not a directory, then check if it is a file
+ res = SPIFFS_stat(&fs, current, &stat);
+ if (res != SPIFFS_OK) {
+ // Current path is not a directory, and it is not a file.
+ if (dir) {
+ if (valid_prefix) {
+ *valid_prefix = 0;
+ }
+ }
+
+ // Error: A component in pathname does not exist
+ return ENOENT;
+ } else {
+ // Current path is a file
+ if (dir) {
+ // There are more directories in path to check.
+
+ if (valid_prefix) {
+ *valid_prefix = 0;
+ }
+
+ // Error: a component used as a directory in pathname is not,
+ // in fact, a directory
+ return ENOTDIR;
+ }
+
+ is_dir = 0;
+ }
+ } else {
+ // Current path is a directory
+ is_dir = 1;
+ }
+ }
+
+ if (is_dir && filenum) {
+ // Count files in directory
+ struct spiffs_dirent e;
+ spiffs_DIR d;
+
+ // Get full directory name
+ char fpath[PATH_MAX + 1];
+
+ strlcpy(fpath, pathp, PATH_MAX);
+ dir_path(fpath, 0);
+
+ SPIFFS_opendir(&fs, "/", &d);
+ while (SPIFFS_readdir(&d, &e)) {
+ if (!strncmp(fpath, (const char *) e.name, min(strlen((char * )e.name), strlen(fpath) - 1))) {
+ if (strlen((const char *) e.name) >= strlen(fpath) && strcmp(fpath, (const char *) e.name)) {
+ *filenum = *filenum + 1;
+ }
+ }
+ }
+ SPIFFS_closedir(&d);
+ }
+
+ if (pis_dir) {
+ *pis_dir = (is_dir || (strcmp(pathp, "/") == 0));
+ }
+
+ return EEXIST;
+}
+
+static int vfs_spiffs_open(const char *path, int flags, int mode) {
+ char npath[PATH_MAX + 1];
+ long fd;
+ int result = 0;
+
+ // Allocate new file
+ vfs_file_t *file = calloc(1, sizeof(vfs_file_t));
+ if (!file) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ file->fs_file = (void *)calloc(1, sizeof(spiffs_file));
+ if (!file->fs_file) {
+ free(file);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // Add file to file list. List index is file descriptor.
+ int res = lstadd(&files, file, &fd);
+ if (res) {
+ free(file->fs_file);
+ free(file);
+ errno = res;
+ return -1;
+ }
+
+ // Open file
+ spiffs_flags spiffs_flgs = 0;
+
+ // Translate flags to SPIFFS flags
+ if (flags == O_RDONLY)
+ spiffs_flgs |= SPIFFS_RDONLY;
+
+ if (flags & O_WRONLY)
+ spiffs_flgs |= SPIFFS_WRONLY;
+
+ if (flags & O_RDWR)
+ spiffs_flgs = SPIFFS_RDWR;
+
+ if (flags & O_EXCL)
+ spiffs_flgs |= SPIFFS_EXCL;
+
+ if (flags & O_CREAT)
+ spiffs_flgs |= SPIFFS_CREAT;
+
+ if (flags & O_TRUNC)
+ spiffs_flgs |= SPIFFS_TRUNC;
+
+ // Check path
+ uint8_t base_is_dir = 0;
+ uint8_t full_is_dir = 0;
+ uint8_t is_file = 0;
+ int file_num = 0;
+
+ mtx_lock(&vfs_mtx);
+
+ check_path(path, &base_is_dir, &full_is_dir, &is_file, &file_num);
+
+ if (full_is_dir) {
+ // We want to open a directory.
+ // If in flags are set some write access mode this is an error, because we only
+ // can open a directory in read mode.
+ if (spiffs_flgs & (SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC)) {
+ lstremove(&files, fd, 0);
+ free(file->fs_file);
+ free(file);
+ mtx_unlock(&vfs_mtx);
+ errno = EISDIR;
+ return -1;
+ }
+
+ // Open the directory
+ strlcpy(npath, path, PATH_MAX);
+ dir_path((char *) npath, 0);
+
+ // Open SPIFFS file
+ *((spiffs_file *)file->fs_file) = SPIFFS_open(&fs, npath, SPIFFS_RDONLY, 0);
+ if (*((spiffs_file *)file->fs_file) < 0) {
+ result = spiffs_result(fs.err_code);
+ }
+
+ file->is_dir = 1;
+ } else {
+ if (!base_is_dir) {
+ // If base path is not a directory we return an error
+ lstremove(&files, fd, 0);
+ free(file->fs_file);
+ free(file);
+ mtx_unlock(&vfs_mtx);
+ errno = ENOENT;
+ return -1;
+ } else {
+ // Open SPIFFS file
+ *((spiffs_file *)file->fs_file) = SPIFFS_open(&fs, path, spiffs_flgs, 0);
+ if (*((spiffs_file *)file->fs_file) < 0) {
+ result = spiffs_result(fs.err_code);
+ }
+ }
+ }
+
+ if (result != 0) {
+ lstremove(&files, fd, 0);
+ free(file->fs_file);
+ free(file);
+ mtx_unlock(&vfs_mtx);
+ errno = result;
+ return -1;
+ }
+
+ int fdi = new_fd_itm(fd);
+
+ mtx_unlock(&vfs_mtx);
+
+ return fdi; //fd;
+}
+
+static ssize_t vfs_spiffs_write(int fdi, const void *data, size_t size) {
+ vfs_file_t *file;
+ int res;
+ long fd = get_fd_itm(fdi);
+
+ res = lstget(&files, fd, (void **) &file);
+ if (res || file->is_dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ // Write SPIFFS file
+ res = SPIFFS_write(&fs, *((spiffs_file *)file->fs_file), (void *) data, size);
+ if (res >= 0) {
+ mtx_unlock(&vfs_mtx);
+ return res;
+ } else {
+ res = spiffs_result(fs.err_code);
+ if (res != 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return -1;
+}
+
+static ssize_t vfs_spiffs_read(int fdi, void * dst, size_t size) {
+ vfs_file_t *file;
+ int res;
+ long fd = get_fd_itm(fdi);
+
+ res = lstget(&files, fd, (void **) &file);
+ if (res || file->is_dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ // Read SPIFFS file
+ res = SPIFFS_read(&fs, *((spiffs_file *)file->fs_file), dst, size);
+ if (res >= 0) {
+ mtx_unlock(&vfs_mtx);
+ return res;
+ } else {
+ res = spiffs_result(fs.err_code);
+ if (res != 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ // EOF
+ mtx_unlock(&vfs_mtx);
+ return 0;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return -1;
+}
+
+static int vfs_spiffs_fstat(int fdi, struct stat * st) {
+ vfs_file_t *file;
+ spiffs_stat stat;
+ int res;
+ long fd = get_fd_itm(fdi);
+
+ res = lstget(&files, fd, (void **) &file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // We have not time in SPIFFS
+ st->st_mtime = 0;
+ st->st_atime = 0;
+ st->st_ctime = 0;
+
+ // Set block size for this file system
+ st->st_blksize = w25qxx_FLASH_PAGE_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_LOG_PAGE_SIZE;
+
+ // First test if it's a directory entry
+ if (file->is_dir) {
+ st->st_mode = S_IFDIR;
+ st->st_size = 0;
+ return 0;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ // If is not a directory get file statistics
+ res = SPIFFS_fstat(&fs, *((spiffs_file *)file->fs_file), &stat);
+ if (res == SPIFFS_OK) {
+ st->st_size = stat.size;
+ } else {
+ st->st_size = 0;
+ res = spiffs_result(fs.err_code);
+ }
+
+ st->st_mode = S_IFREG;
+
+ if (res < 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static int vfs_spiffs_close(int fdi) {
+ vfs_file_t *file;
+ int res;
+ long fd = del_fd_itm(fdi);
+
+ res = lstget(&files, fd, (void **) &file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ res = SPIFFS_close(&fs, *((spiffs_file *)file->fs_file));
+ if (res) {
+ res = spiffs_result(fs.err_code);
+ }
+
+ if (res < 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ lstremove(&files, fd, 0);
+
+ free(file->fs_file);
+ free(file);
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static off_t vfs_spiffs_lseek(int fdi, off_t size, int mode) {
+ vfs_file_t *file;
+ int res;
+ long fd = get_fd_itm(fdi);
+
+ res = lstget(&files, fd, (void **) &file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (file->is_dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ int whence = SPIFFS_SEEK_CUR;
+
+ switch (mode) {
+ case SEEK_SET:
+ whence = SPIFFS_SEEK_SET;
+ break;
+ case SEEK_CUR:
+ whence = SPIFFS_SEEK_CUR;
+ break;
+ case SEEK_END:
+ whence = SPIFFS_SEEK_END;
+ break;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ res = SPIFFS_lseek(&fs, *((spiffs_file *)file->fs_file), size, whence);
+ if (res < 0) {
+ res = spiffs_result(fs.err_code);
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return res;
+}
+
+static int vfs_spiffs_stat(const char * path, struct stat * st) {
+ int fd;
+ int res;
+
+ mtx_lock(&vfs_mtx);
+
+ fd = vfs_spiffs_open(path, 0, 0);
+ if (fd < 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+
+ res = vfs_spiffs_fstat(fd, st);
+ if (fd < 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+
+ vfs_spiffs_close(fd);
+
+ mtx_unlock(&vfs_mtx);
+
+ return res;
+}
+
+static int vfs_spiffs_access(const char *path, int amode) {
+ struct stat s;
+
+ mtx_lock(&vfs_mtx);
+
+ if (vfs_spiffs_stat(path, &s) < 0) {
+ mtx_unlock(&vfs_mtx);
+ return -1;
+ }
+
+ if (s.st_mode != S_IFREG) {
+ mtx_unlock(&vfs_mtx);
+ errno = EACCES;
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static int vfs_spiffs_unlink(const char *path) {
+ int is_dir;
+
+ mtx_lock(&vfs_mtx);
+
+ int res = vfs_spiffs_traverse(path, &is_dir, NULL, NULL);
+ if ((res != 0) && (res != EEXIST)) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ if (is_dir) {
+ mtx_unlock(&vfs_mtx);
+ errno = EPERM;
+ return -1;
+ }
+
+ res = SPIFFS_remove(&fs, path);
+ if (res) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static int vfs_spiffs_rename(const char *src, const char *dst) {
+ int src_is_dir;
+ int dst_is_dir;
+ int dst_files;
+
+ mtx_lock(&vfs_mtx);
+
+ int res = vfs_spiffs_traverse(src, &src_is_dir, NULL, NULL);
+ if ((res != 0) && (res != EEXIST) && (res != ENOENT)) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ res = vfs_spiffs_traverse(dst, &dst_is_dir, &dst_files, NULL);
+ if ((res != 0) && (res != EEXIST) && (res != ENOENT)) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ if (dst_is_dir && !src_is_dir) {
+ mtx_unlock(&vfs_mtx);
+ errno = EISDIR;
+ return -1;
+ }
+
+ if (dst_is_dir && (dst_files > 0)) {
+ mtx_unlock(&vfs_mtx);
+ errno = ENOTEMPTY;
+ return -1;
+ }
+
+ char dpath[PATH_MAX + 1];
+ char *csrc;
+ char *cname;
+
+ if (src_is_dir) {
+ // We need to rename all tree
+ struct spiffs_dirent e;
+ spiffs_DIR d;
+
+ SPIFFS_opendir(&fs, "/", &d);
+ while (SPIFFS_readdir(&d, &e)) {
+ if (!strncmp(src, (const char *) e.name, strlen(src)) && e.name[strlen(src)] == '/') {
+ strlcpy(dpath, dst, PATH_MAX);
+ csrc = (char *) src;
+ cname = (char *) e.name;
+
+ while (*csrc && *cname && (*csrc == *cname)) {
+ ++csrc;
+ ++cname;
+ }
+
+ strlcat(dpath, cname, PATH_MAX);
+
+ if (SPIFFS_rename(&fs, (char *) e.name, dpath) != SPIFFS_OK) {
+ if (fs.err_code != SPIFFS_ERR_CONFLICTING_NAME) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ } else {
+ // This only happens when e.name and dpath are directories. In this case
+ // remove e.name
+ if (SPIFFS_remove(&fs, (char *) e.name) != SPIFFS_OK) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+ }
+ }
+ }
+ }
+ SPIFFS_closedir(&d);
+ } else {
+ if (SPIFFS_rename(&fs, src, dst) < 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static DIR* vfs_spiffs_opendir(const char* name) {
+ int is_dir;
+
+ vfs_dir_t *dir = vfs_allocate_dir("spiffs", name);
+ if (!dir) {
+ return NULL;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ int res = vfs_spiffs_traverse(name, &is_dir, NULL, NULL);
+ if ((res != 0) && (res != EEXIST)) {
+ vfs_free_dir(dir);
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return NULL;
+ }
+
+ if ((res == EEXIST) && !is_dir) {
+ vfs_free_dir(dir);
+ mtx_unlock(&vfs_mtx);
+ errno = ENOTDIR;
+ return NULL;
+ }
+
+ if (!is_dir) {
+ vfs_free_dir(dir);
+ mtx_unlock(&vfs_mtx);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!SPIFFS_opendir(&fs, name, (spiffs_DIR *)dir->fs_dir)) {
+ vfs_free_dir(dir);
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return NULL;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return (DIR *) dir;
+}
+
+static int vfs_spiffs_rmdir(const char *path) {
+ int is_dir;
+ int filenum;
+
+ if (strcmp(path,"/") == 0) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ int res = vfs_spiffs_traverse(path, &is_dir, &filenum, NULL);
+ if ((res != 0) && (res != EEXIST)) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ if (!is_dir) {
+ mtx_unlock(&vfs_mtx);
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ if (filenum > 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = ENOTEMPTY;
+ return -1;
+ }
+
+ // Remove directory
+ char npath[PATH_MAX + 1];
+
+ strlcpy(npath, path, PATH_MAX);
+ dir_path(npath, 0);
+
+ res = SPIFFS_remove(&fs, npath);
+ if (res) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static struct dirent* vfs_spiffs_readdir(DIR* pdir) {
+ int res = 0, len = 0, entries = 0;
+ vfs_dir_t *dir = (vfs_dir_t *) pdir;
+
+ struct dirent *ent = &dir->ent;
+
+ char *fn;
+
+ // Clear the current dirent
+ memset(ent, 0, sizeof(struct dirent));
+
+ // If there are mount points to read, read them first
+ if (dir->mount) {
+ struct dirent *ment = mount_readdir((DIR *)dir);
+ if (ment) {
+ return ment;
+ }
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ // Search for next entry
+ for (;;) {
+ // Read directory
+ dir->fs_info = (void *)SPIFFS_readdir((spiffs_DIR *)dir->fs_dir, (struct spiffs_dirent *)dir->fs_info);
+ if (!dir->fs_info) {
+ if (fs.err_code != SPIFFS_VIS_END) {
+ res = spiffs_result(fs.err_code);
+ errno = res;
+ }
+
+ break;
+ }
+
+ // Break condition
+ if (((struct spiffs_dirent *)(dir->fs_info))->name[0] == 0)
+ break;
+
+ // Get name and length
+ fn = (char *)(((struct spiffs_dirent *)(dir->fs_info))->name);
+ len = strlen(fn);
+
+ // Get entry type and size
+ ent->d_type = DT_REG;
+
+ if (len >= 2) {
+ if (fn[len - 1] == '.') {
+ if (fn[len - 2] == '/') {
+ ent->d_type = DT_DIR;
+
+ fn[len - 2] = '\0';
+
+ len = strlen(fn);
+
+ // Skip root dir
+ if (len == 0) {
+ continue;
+ }
+ }
+ }
+ }
+
+ // Skip entries not belonged to path
+ if (strncmp(fn, dir->path, strlen(dir->path)) != 0) {
+ continue;
+ }
+
+ if (strlen(dir->path) > 1) {
+ if (*(fn + strlen(dir->path)) != '/') {
+ continue;
+ }
+ }
+
+ // Skip root directory
+ fn = fn + strlen(dir->path);
+ len = strlen(fn);
+ if (len == 0) {
+ continue;
+ }
+
+ // Skip initial /
+ if (len > 1) {
+ if (*fn == '/') {
+ fn = fn + 1;
+ len--;
+ }
+ }
+
+ // Skip subdirectories
+ if (strchr(fn, '/')) {
+ continue;
+ }
+
+ ent->d_fsize = ((struct spiffs_dirent *)(dir->fs_info))->size;
+
+ strlcpy(ent->d_name, fn, MAXNAMLEN);
+
+ entries++;
+ dir->offset++;
+
+ break;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ if (entries > 0) {
+ return ent;
+ } else {
+ return NULL;
+ }
+}
+
+static long vfs_spiffs_telldir(DIR *dirp) {
+ vfs_dir_t *dir = (vfs_dir_t *)dirp;
+
+ if (dir->offset < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ return (long)dir->offset;
+}
+
+static int vfs_spiffs_closedir(DIR* pdir) {
+ vfs_dir_t *dir = (vfs_dir_t *) pdir;
+ int res;
+
+ if (!pdir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ if ((res = SPIFFS_closedir((spiffs_DIR *)dir->fs_dir)) < 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+
+ vfs_free_dir(dir);
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static int vfs_spiffs_mkdir(const char *path, mode_t mode) {
+ char npath[PATH_MAX + 1];
+ int res;
+ int valid_prefix;
+
+ mtx_lock(&vfs_mtx);
+
+ res = vfs_spiffs_traverse(path, NULL, NULL, &valid_prefix);
+ if ((res != 0) && ((res != ENOENT) || (!valid_prefix))) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ // Create directory
+ strlcpy(npath, path, PATH_MAX);
+ dir_path(npath, 0);
+
+ spiffs_file fd = SPIFFS_open(&fs, npath, SPIFFS_CREAT | SPIFFS_RDWR, 0);
+ if (fd < 0) {
+ mtx_unlock(&vfs_mtx);
+ res = spiffs_result(fs.err_code);
+ errno = res;
+ return -1;
+ }
+
+ if (SPIFFS_close(&fs, fd) < 0) {
+ mtx_unlock(&vfs_mtx);
+ res = spiffs_result(fs.err_code);
+ errno = res;
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static int vfs_spiffs_fsync(int fdi) {
+ vfs_file_t *file;
+ int res;
+ long fd = get_fd_itm(fdi);
+
+ res = lstget(&files, fd, (void **) &file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (file->is_dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ mtx_lock(&vfs_mtx);
+
+ res = SPIFFS_fflush(&fs, *((spiffs_file *)file->fs_file));
+ if (res >= 0) {
+ mtx_unlock(&vfs_mtx);
+ return res;
+ } else {
+ res = spiffs_result(fs.err_code);
+ if (res != 0) {
+ mtx_unlock(&vfs_mtx);
+ errno = res;
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+ errno = EIO;
+ return -1;
+ }
+
+ mtx_unlock(&vfs_mtx);
+
+ return 0;
+}
+
+static void vfs_spiffs_free_resources() {
+ if (my_spiffs_work_buf) free(my_spiffs_work_buf);
+ if (my_spiffs_fds) free(my_spiffs_fds);
+ if (my_spiffs_cache) free(my_spiffs_cache);
+
+ mtx_destroy(&vfs_mtx);
+ mtx_destroy(&ll_mtx);
+}
+
+int vfs_spiffs_mount(const char *target) {
+ esp_vfs_t vfs = {
+ .flags = ESP_VFS_FLAG_DEFAULT,
+ .write = &vfs_spiffs_write,
+ .open = &vfs_spiffs_open,
+ .fstat = &vfs_spiffs_fstat,
+ .close = &vfs_spiffs_close,
+ .read = &vfs_spiffs_read,
+ .lseek = &vfs_spiffs_lseek,
+ .stat = &vfs_spiffs_stat,
+ .link = NULL,
+ .unlink = &vfs_spiffs_unlink,
+ .rename = &vfs_spiffs_rename,
+ .mkdir = &vfs_spiffs_mkdir,
+ .opendir = &vfs_spiffs_opendir,
+ .readdir = &vfs_spiffs_readdir,
+ .closedir = &vfs_spiffs_closedir,
+ .rmdir = &vfs_spiffs_rmdir,
+ .fsync = &vfs_spiffs_fsync,
+ .access = &vfs_spiffs_access,
+ .telldir = &vfs_spiffs_telldir,
+ };
+
+ // Mount spiffs file system
+ spiffs_config cfg;
+ int res = 0;
+ int retries = 0;
+
+ // Find partition
+ const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, LUA_RTOS_SPIFFS_PART, NULL);
+
+ if (!partition) {
+ vfs_spiffs_free_resources();
+ syslog(LOG_ERR, "spiffs can't find a valid partition");
+ return -1;
+ } else {
+ cfg.phys_addr = partition->address;
+ cfg.phys_size = partition->size;
+ }
+
+ cfg.phys_erase_block = w25qxx_FLASH_SECTOR_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_ERASE_SIZE;
+ cfg.log_page_size = w25qxx_FLASH_PAGE_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_LOG_PAGE_SIZE;
+ cfg.log_block_size = w25qxx_FLASH_SECTOR_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_LOG_BLOCK_SIZE;
+
+ if (partition) {
+ syslog(LOG_INFO,
+ "spiffs start address at 0x%x, size %d Kb",
+ cfg.phys_addr, cfg.phys_size / 1024);
+ } else {
+ syslog(LOG_INFO, "spiffs start address at 0x%x, size %d Kb",
+ cfg.phys_addr, cfg.phys_size / 1024);
+ }
+
+ cfg.hal_read_f = (spiffs_read) low_spiffs_read;
+ cfg.hal_write_f = (spiffs_write) low_spiffs_write;
+ cfg.hal_erase_f = (spiffs_erase) low_spiffs_erase;
+
+ my_spiffs_work_buf = malloc(cfg.log_page_size * 2);
+ if (!my_spiffs_work_buf) {
+ vfs_spiffs_free_resources();
+ syslog(LOG_ERR, "spiffs can't allocate memory for file system");
+ return -1;
+ }
+
+ int fds_len = sizeof(spiffs_fd) * 5;
+ my_spiffs_fds = malloc(fds_len);
+ if (!my_spiffs_fds) {
+ vfs_spiffs_free_resources();
+ syslog(LOG_ERR, "spiffs can't allocate memory for file system");
+ return -1;
+ }
+
+ int cache_len = cfg.log_page_size * 5;
+ my_spiffs_cache = malloc(cache_len);
+ if (!my_spiffs_cache) {
+ vfs_spiffs_free_resources();
+ syslog(LOG_ERR, "spiffs can't allocate memory for file system");
+ return -1;
+ }
+
+ // Init mutex
+ mtx_init(&vfs_mtx, NULL, NULL, MTX_RECURSE);
+
+ mtx_init(&ll_mtx, NULL, NULL, 0);
+ fs.user_data = &ll_mtx;
+
+ while (retries < 2) {
+ res = SPIFFS_mount(&fs, &cfg, my_spiffs_work_buf, my_spiffs_fds,
+ fds_len, my_spiffs_cache, cache_len, NULL);
+
+ if (res < 0) {
+ if (fs.err_code == SPIFFS_ERR_NOT_A_FS) {
+ syslog(LOG_ERR, "spiffs no file system detected, formating...");
+ SPIFFS_unmount(&fs);
+ res = SPIFFS_format(&fs);
+ if (res < 0) {
+ vfs_spiffs_free_resources();
+ syslog(LOG_ERR, "spiffs format error");
+ return -1;
+ }
+ } else {
+ vfs_spiffs_free_resources();
+ syslog(LOG_ERR, "spiff can't mount file system (%s)",
+ strerror(spiffs_result(fs.err_code)));
+ return -1;
+ }
+ } else {
+ break;
+ }
+
+ retries++;
+ }
+
+ if (retries > 0) {
+ syslog(LOG_INFO, "spiffs creating root folder");
+
+ // Create the root folder
+ spiffs_file fd = SPIFFS_open(&fs, "/.", SPIFFS_CREAT | SPIFFS_RDWR, 0);
+ if (fd < 0) {
+ vfs_spiffs_umount(target);
+ syslog(LOG_ERR, "spiffs can't create root folder (%s)",
+ strerror(spiffs_result(fs.err_code)));
+ return -1;
+ }
+
+ if (SPIFFS_close(&fs, fd) < 0) {
+ vfs_spiffs_umount(target);
+ syslog(LOG_ERR, "spiffs can't create root folder (%s)",
+ strerror(spiffs_result(fs.err_code)));
+ return -1;
+ }
+ }
+
+ lstinit(&files, 0, LIST_DEFAULT);
+
+ ESP_ERROR_CHECK(esp_vfs_register("/spiffs", &vfs, NULL));
+
+ syslog(LOG_INFO, "spiffs mounted on %s", target);
+
+ return 0;
+}
+
+int vfs_spiffs_umount(const char *target) {
+ esp_vfs_unregister("/spiffs");
+ SPIFFS_unmount(&fs);
+
+ vfs_spiffs_free_resources();
+
+ syslog(LOG_INFO, "spiffs unmounted");
+
+ return 0;
+}
+
+int vfs_spiffs_format(const char *target) {
+ vfs_spiffs_umount(target);
+
+ mtx_init(&ll_mtx, NULL, NULL, 0);
+
+ int res = SPIFFS_format(&fs);
+ if (res < 0) {
+ syslog(LOG_ERR, "spiffs format error");
+ return -1;
+ }
+
+ mtx_destroy(&ll_mtx);
+
+ vfs_spiffs_mount(target);
+
+ syslog(LOG_INFO, "spiffs creating root folder");
+
+ // Create the root folder
+ spiffs_file fd = SPIFFS_open(&fs, "/.", SPIFFS_CREAT | SPIFFS_RDWR, 0);
+ if (fd < 0) {
+ vfs_spiffs_umount(target);
+ syslog(LOG_ERR, "spiffs can't create root folder (%s)",
+ strerror(spiffs_result(fs.err_code)));
+ return -1;
+ }
+
+ if (SPIFFS_close(&fs, fd) < 0) {
+ vfs_spiffs_umount(target);
+ syslog(LOG_ERR, "spiffs can't create root folder (%s)",
+ strerror(spiffs_result(fs.err_code)));
+ return -1;
+ }
+
+ return 0;
+}
+
+int vfs_spiffs_fsstat(const char *target, u32_t *total, u32_t *used) {
+
+ if (SPIFFS_info(&fs, total, used) != SPIFFS_OK) {
+ syslog(LOG_ERR, "spiffs get fs info of '%s' (%s)", target,
+ strerror(spiffs_result(fs.err_code)));
+ return -1;
+ }
+
+ return 0;
+}
+
+//#endif
diff --git a/libiot/vfs/vfs.h b/libiot/vfs/vfs.h
new file mode 100644
index 0000000..eb743fd
--- /dev/null
+++ b/libiot/vfs/vfs.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L.
+ * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * * The WHITECAT logotype cannot be changed, you can remove it, but you
+ * cannot change it in any way. The WHITECAT logotype is:
+ *
+ * /\ /\
+ * / \_____/ \
+ * /_____________\
+ * W H I T E C A T
+ *
+ * * Redistributions in binary form must retain all copyright notices printed
+ * to any local or remote output device. This include any reference to
+ * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may
+ * appear in the future.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Lua RTOS common vfs functions
+ *
+ */
+
+#include "esp_vfs.h"
+
+#include <stdarg.h>
+#include <unistd.h>
+
+//#include <sys/mount.h>
+
+#define LUA_RTOS_SPIFFS_PART 0x40
+#define LUA_RTOS_LFS_PART 0x41
+
+typedef struct {
+ void *fs_file;
+ char *path;
+ uint8_t is_dir;
+} vfs_file_t;
+
+typedef struct {
+ DIR dir;
+ long offset;
+ void *fs_dir;
+ void *fs_info;
+ char *path;
+ struct dirent ent;
+ const struct mount_pt *mount;
+} vfs_dir_t;
+
+typedef struct {
+ int flags; // FD flags
+} vfs_fd_local_storage_t;
+
+// Return if there are available bytes for read from the file descriptor.
+// This function is blocking.
+typedef int(*vfs_has_bytes)(int, int);
+
+// Return the bytes available for write to the file descriptor.
+// This function is non blocking.
+typedef int(*vfs_free_bytes)(int);
+
+// Get one byte from the file descriptor.
+// This functions is blocking.
+typedef int(*vfs_get_byte)(int,char *);
+
+// Put one byte to the file descriptor.
+// This function is blocking.
+typedef void(*vfs_put_byte)(int,char *);
+
+/*
+int vfs_fat_mount(const char *target);
+int vfs_fat_umount(const char *target);
+int vfs_fat_format(const char *target);
+int vfs_fat_fsstat(const char *target, u32_t *total, u32_t *used);
+*/
+
+int vfs_spiffs_mount(const char *target);
+int vfs_spiffs_umount(const char *target);
+int vfs_spiffs_format(const char *target);
+int vfs_spiffs_fsstat(const char *target, u32_t *total, u32_t *used);
+
+/*
+int vfs_lfs_mount(const char *target);
+int vfs_lfs_umount(const char *target);
+int vfs_lfs_format(const char *target);
+int vfs_lfs_fsstat(const char *target, u32_t *total, u32_t *used);
+
+int vfs_ramfs_mount(const char *target);
+int vfs_ramfs_umount(const char *target);
+int vfs_ramfs_format(const char *target);
+int vfs_ramfs_fsstat(const char *target, u32_t *total, u32_t *used);
+
+int vfs_romfs_mount(const char *target);
+int vfs_romfs_umount(const char *target);
+int vfs_romfs_fsstat(const char *target, u32_t *total, u32_t *used);
+
+int vfs_generic_fcntl(vfs_fd_local_storage_t *local_storage, int fd, int cmd, va_list args);
+ssize_t vfs_generic_read(vfs_fd_local_storage_t *local_storage, vfs_has_bytes has_bytes, vfs_get_byte get, int fd, void * dst, size_t size);
+ssize_t vfs_generic_write(vfs_fd_local_storage_t *local_storage, vfs_put_byte put, int fd, const void *data, size_t size);
+ssize_t vfs_generic_writev(vfs_fd_local_storage_t *local_storage, vfs_put_byte put, int fd, const struct iovec *iov, int iovcnt);
+int vfs_generic_select(vfs_fd_local_storage_t *local_storage, vfs_has_bytes has_bytes, vfs_free_bytes free, int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
+
+vfs_dir_t *vfs_allocate_dir(const char *vfs, const char *name);
+void vfs_free_dir(vfs_dir_t *dir);
+vfs_fd_local_storage_t *vfs_create_fd_local_storage(int num);
+*/