diff options
| author | bhgv <bhgv.empire@gmail.com> | 2020-05-10 02:59:23 +0300 |
|---|---|---|
| committer | bhgv <bhgv.empire@gmail.com> | 2020-05-10 02:59:23 +0300 |
| commit | 31b4edc67b75658ce5e2d41f2fc87331f4b26d49 (patch) | |
| tree | a7b6ea659fe62e0a7239f29170024f524595fb4d /libiot/vfs/spiffs.c | |
| parent | c76314f0f38f4ed028610a6db4452879a556b35f (diff) | |
a try to add support of FreeRTOS riscV-64 (k210 cpu). first step
Diffstat (limited to 'libiot/vfs/spiffs.c')
| -rw-r--r-- | libiot/vfs/spiffs.c | 1423 |
1 files changed, 1423 insertions, 0 deletions
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 |
