aboutsummaryrefslogtreecommitdiff
path: root/libiot/ramfs
diff options
context:
space:
mode:
Diffstat (limited to 'libiot/ramfs')
-rw-r--r--libiot/ramfs/ramfs.c1088
-rw-r--r--libiot/ramfs/ramfs.h274
2 files changed, 1362 insertions, 0 deletions
diff --git a/libiot/ramfs/ramfs.c b/libiot/ramfs/ramfs.c
new file mode 100644
index 0000000..3362814
--- /dev/null
+++ b/libiot/ramfs/ramfs.c
@@ -0,0 +1,1088 @@
+/*
+ * 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 "ramfs.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+static int add_reference(ramfs_t *fs, ramfs_entry_t *entry);
+static void remove_reference(ramfs_t *fs, ramfs_entry_t *entry);
+static int get_reference_uses(ramfs_t *fs, ramfs_entry_t *entry);
+static void remove_block(ramfs_t *fs, ramfs_file_t *file);
+static int add_block(ramfs_t *fs, ramfs_file_t *file);
+static int add_entry(ramfs_t *fs, const char *name, ramfs_entry_t *parent, ramfs_entry_t **entry, ramfs_entry_type_t entry_type);
+static void remove_entry(ramfs_t *fs, ramfs_entry_t *entry, ramfs_entry_t *parent_entry, ramfs_entry_t *prev_entry, int remove);
+static int traverse(ramfs_t *fs, const char *path, ramfs_entry_t **entry, ramfs_entry_t **parent_entry, ramfs_entry_t **prev_entry, int creat, ramfs_entry_type_t type);
+static ramfs_off_t ramfs_file_seek_internal(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t offset, ramfs_whence_t whence);
+static int ramfs_file_truncate_internal(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t size);
+
+static int add_reference(ramfs_t *fs, ramfs_entry_t *entry) {
+ ramfs_entry_ref_t *cref;
+
+ // Find entry into the reference set
+ cref = fs->ref;
+ while (cref && (cref->entry != entry)) {
+ cref = cref->next;
+ }
+
+ if (!cref) {
+ // Entry not found, add it
+ ramfs_entry_ref_t *ref = calloc(1, sizeof(ramfs_entry_ref_t));
+ if (!ref) {
+ return RAMFS_ERR_NOMEM;
+ }
+
+ ref->entry = entry;
+ ref->next = fs->ref;
+ ref->uses = 1;
+
+ fs->ref = ref;
+ } else {
+ // Entry found, just increment the uses counter
+ cref->uses++;
+ }
+
+ return RAMFS_ERR_OK;
+}
+
+static void remove_reference(ramfs_t *fs, ramfs_entry_t *entry) {
+ ramfs_entry_ref_t *cref;
+ ramfs_entry_ref_t *prev_ref = NULL;
+
+ if (!entry) return;
+
+ // Find entry into the reference set
+ cref = fs->ref;
+ while (cref && (cref->entry != entry)) {
+ prev_ref = cref;
+ cref = cref->next;
+ }
+
+ if (cref) {
+ // Entry found
+ if (cref->uses > 0) {
+ // Decrement the uses counter
+ cref->uses--;
+ }
+
+ if (cref->uses == 0) {
+ // Not used, remove it
+ if (!prev_ref) {
+ // It is the first reference
+ fs->ref = cref->next;
+ } else {
+ prev_ref->next = cref->next;
+ }
+
+ free(cref);
+
+ if (entry->flags & RAMFS_ENTRY_RM_LEN_MSK) {
+ // The entry is marked for remove, remove now
+ remove_entry(fs, entry, NULL, NULL, ((entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_FILE));
+ }
+ }
+ }
+}
+
+static int get_reference_uses(ramfs_t *fs, ramfs_entry_t *entry) {
+ ramfs_entry_ref_t *cref;
+
+ // Find entry into the reference set
+ cref = fs->ref;
+ while (cref && (cref->entry != entry)) {
+ cref = cref->next;
+ }
+
+ int uses = 0;
+
+ if (cref) {
+ uses = cref->uses;
+ }
+
+ return uses;
+}
+
+static void remove_block(ramfs_t *fs, ramfs_file_t *file) {
+ ramfs_block_t *block;
+
+ block = file->entry->file.header->tail;
+ if (block) {
+ // Find the block into the file block chain to
+ // update the block chain
+ ramfs_block_t *prev_block = NULL;
+ ramfs_block_t *cblock = file->entry->file.header->head;
+
+ while (cblock && (cblock != block)) {
+ prev_block = cblock;
+ cblock = cblock->next;
+ }
+
+ if (prev_block) {
+ prev_block->next = NULL;
+ file->entry->file.header->tail = prev_block;
+ } else {
+ file->entry->file.header->head = NULL;
+ file->entry->file.header->tail = NULL;
+ }
+
+ // Free block
+ free(block);
+
+ // Update the file system size
+ fs->current_size -= sizeof(ramfs_block_t) + fs->block_size - 1;
+ }
+}
+
+static int add_block(ramfs_t *fs, ramfs_file_t *file) {
+ // Check for space
+ ramfs_size_t size = sizeof(ramfs_block_t) + fs->block_size - 1;
+
+ if (fs->current_size + size > fs->size) {
+ return RAMFS_ERR_NOSPC;
+ }
+
+ // Create block
+ ramfs_block_t *block = (ramfs_block_t *)calloc(1, size);
+ if (!block) {
+ return RAMFS_ERR_NOMEM;
+ }
+
+ if (!file->entry->file.header->head) {
+ // First block of the file
+ file->entry->file.header->head = block;
+ file->entry->file.header->tail = block;
+ } else {
+ // Add the block to the end of the file block chain
+ file->entry->file.header->tail->next = block;
+ file->entry->file.header->tail = block;
+ }
+
+ file->block = block;
+ file->ptr = block->data;
+
+ // Update the file system size
+ fs->current_size += size;
+
+ return RAMFS_ERR_OK;
+}
+
+static int add_entry(ramfs_t *fs, const char *name, ramfs_entry_t *parent, ramfs_entry_t **entry, ramfs_entry_type_t entry_type) {
+ int name_len = strlen(name);
+
+ // Check for space
+ ramfs_size_t entry_size = sizeof(ramfs_entry_t) + name_len - 1;
+ ramfs_size_t header_size = 0;
+
+ if (entry_type == RAMFS_FILE) {
+ header_size = sizeof(ram_file_header_t);
+ }
+
+ if (fs->current_size + entry_size + header_size > fs->size) {
+ return RAMFS_ERR_NOSPC;
+ }
+
+ // Create the entry
+ *entry = (ramfs_entry_t *)calloc(1, entry_size);
+ if (!*entry) {
+ return RAMFS_ERR_NOMEM;
+ }
+
+ (*entry)->flags |= entry_type;
+
+ // Create the file header
+ if (entry_type == RAMFS_FILE) {
+ (*entry)->file.header = (ram_file_header_t *)calloc(1, header_size);
+ if (!((*entry)->file.header)) {
+ free(*entry);
+
+ return RAMFS_ERR_NOMEM;
+ }
+
+ (*entry)->file.header->head = NULL;
+ (*entry)->file.header->tail = NULL;
+ (*entry)->file.header->size = 0;
+ }
+
+ // Set the entry name and len
+ memcpy((*entry)->name, name, name_len);
+
+ (*entry)->flags |= (name_len << RAMFS_ENTRY_NAME_LEN_POS);
+
+ // Update the file system size
+ fs->current_size += entry_size + header_size;
+
+ // Add the entry to the file system
+ if (!fs->child) {
+ // The entry is the first child of the root directory
+ fs->child = *entry;
+ } else {
+ if (parent) {
+ // Add the entry to its parent (which is always a directory)
+ if (!parent->dir.child) {
+ // The entry is the first child of the directory
+ parent->dir.child = *entry;
+ } else {
+ // Add the entry into the directory child chain
+ ramfs_entry_t *centry = parent->dir.child;
+
+ while (centry->next) {
+ centry = centry->next;
+ }
+
+ centry->next = *entry;
+ }
+ } else {
+ // Add the entry to the child chain of the root directory
+ ramfs_entry_t *centry = fs->child;
+
+ while (centry->next) {
+ centry = centry->next;
+ }
+
+ centry->next = *entry;
+ }
+ }
+
+ return RAMFS_ERR_OK;
+}
+
+static void remove_entry(ramfs_t *fs, ramfs_entry_t *entry, ramfs_entry_t *parent_entry, ramfs_entry_t *prev_entry, int remove) {
+ if (!entry) return;
+
+ // Update the chain
+ if (prev_entry) {
+ prev_entry->next = entry->next;
+ }
+
+ if (parent_entry && (parent_entry->dir.child == entry)) {
+ parent_entry->dir.child = entry->next;
+ } else if (!parent_entry) {
+ if (fs->child == entry) {
+ fs->child = entry->next;
+ }
+ }
+
+ // If the entry to remove is used, mark it for remove later
+ if (get_reference_uses(fs, entry) > 0) {
+ entry->flags |= RAMFS_ENTRY_RM_LEN_MSK;
+ return;
+ }
+
+ // While removing the entry, compute the entry size to update the file system size later
+ ramfs_size_t size = sizeof(ramfs_entry_t) + ((entry->flags & RAMFS_ENTRY_NAME_LEN_MSK) >> RAMFS_ENTRY_NAME_LEN_POS) - 1;
+
+ if (remove && ((entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_FILE)) {
+ // Free file blocks
+ ramfs_block_t *block = entry->file.header->head;
+ ramfs_block_t *tmp;
+
+ size += sizeof(ram_file_header_t);
+
+ while (block) {
+ size += sizeof(ramfs_block_t) + fs->block_size - 1;
+
+ tmp = block;
+ block = block->next;
+
+ free(tmp);
+ }
+
+ free(entry->file.header);
+ }
+
+ free(entry);
+
+ // Update the file system size
+ fs->current_size -= size;
+}
+
+static int traverse(ramfs_t *fs, const char *path, ramfs_entry_t **entry, ramfs_entry_t **parent_entry, ramfs_entry_t **prev_entry, int creat, ramfs_entry_type_t type) {
+ ramfs_error_t ret;
+
+ ramfs_entry_t *centry; // Curren entry
+
+ // A copy of path to use with strtok_r
+ char path_copy[PATH_MAX + 1];
+
+ char *component; // Current path component
+ char *rest; // Rest of path
+
+ *entry = NULL;
+
+ if (prev_entry) {
+ *prev_entry = NULL;
+ }
+
+ if (!path || !*path) {
+ return RAMFS_ERR_NOENT;
+ }
+
+ if (strlen(path) > PATH_MAX) {
+ return RAMFS_ERR_NAMETOOLONG;
+ }
+
+ // Make a copy of path to use it with strtok_r
+ strcpy(path_copy, path);
+ rest = path_copy;
+
+ // Start at the root directory
+ centry = fs->child;
+ *entry = centry;
+
+ if (!centry) {
+ // The file system doesn't contain any entry yet, then create a new entry
+ component = strtok_r(rest, "/", &rest);
+
+ if (creat && ((ret = add_entry(fs, component, NULL, entry, type)) != RAMFS_ERR_OK)) {
+ return ret;
+ }
+
+ return RAMFS_ERR_NOENT;
+ }
+
+
+ int name_len;
+
+ ramfs_entry_t *parent = NULL;
+
+ if (parent_entry) {
+ *parent_entry = parent;
+ }
+
+ while ((component = strtok_r(rest, "/", &rest))) {
+ while (centry) {
+ name_len = ((centry->flags & RAMFS_ENTRY_NAME_LEN_MSK) >> RAMFS_ENTRY_NAME_LEN_POS);
+ if ((strlen(component) == name_len) && bcmp(centry->name, component, name_len) == 0) {
+ // The entry has been found
+ *entry = centry;
+ break;
+ } else {
+ // Next entry
+ if (prev_entry) {
+ *prev_entry = centry;
+ }
+
+ centry = centry->next;
+ }
+ }
+
+ if (!centry) {
+ // The entry was not found, create it, if it corresponds to the last
+ // path component
+ if (!rest) {
+ if (creat && ((ret = add_entry(fs, component, parent, entry, type)) != RAMFS_ERR_OK)) {
+ return ret;
+ }
+ } else {
+ *entry = NULL;
+
+ if (parent_entry) {
+ *parent_entry = NULL;
+ }
+
+ if (prev_entry) {
+ *prev_entry = NULL;
+ }
+ }
+
+ return RAMFS_ERR_NOENT;
+ } else {
+ parent = centry;
+
+ if ((centry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_DIR) {
+ // For next path component, start the search into the directory child chain
+ centry = centry->dir.child;
+
+ if (rest) {
+ if (parent_entry) {
+ *parent_entry = parent;
+ }
+
+ if (prev_entry) {
+ *prev_entry = NULL;
+ }
+ }
+ } else {
+ // The current path component is a file, check that there are not more
+ // path components
+ if (rest) {
+ return RAMFS_ERR_NOTDIR;
+ }
+ }
+ }
+ }
+
+ return RAMFS_ERR_OK;
+}
+
+static ramfs_off_t ramfs_file_seek_internal(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t offset, ramfs_whence_t whence) {
+ if (whence == RAMFS_SEEK_SET) {
+ if (offset >= 0) {
+ file->offset = offset;
+ } else {
+ return RAMFS_ERR_INVAL;
+ }
+ } else if (whence == RAMFS_SEEK_CUR) {
+ if (file->offset + offset >= 0) {
+ file->offset += offset;
+ } else {
+ return RAMFS_ERR_INVAL;
+ }
+ } else if (whence == RAMFS_SEEK_END) {
+ if (offset + file->entry->file.header->size >= 0) {
+ file->offset = offset + file->entry->file.header->size;
+ } else {
+ return RAMFS_ERR_INVAL;
+ }
+ } else {
+ return RAMFS_ERR_INVAL;
+ }
+
+ int block_num = file->offset / fs->block_size;
+ int block_off = file->offset % fs->block_size;
+
+ if (file->entry->file.header->head) {
+ ramfs_block_t *block = file->entry->file.header->head;
+ int curr_block;
+
+ for(curr_block = 0;block && (curr_block < block_num);curr_block++) {
+ block = block->next;
+ }
+
+ file->block = block;
+ file->ptr = (block?block->data + block_off:NULL);
+ } else {
+ file->block = NULL;
+ file->ptr = NULL;
+ }
+
+ return block_num * fs->block_size + block_off;
+}
+
+static int ramfs_file_truncate_internal(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t size) {
+ ramfs_error_t ret;
+
+ int access_mode = (file->flags & RAMFS_ACCMODE);
+
+ if ((access_mode != RAMFS_O_WRONLY) && (access_mode != RAMFS_O_RDWR)) {
+ return RAMFS_ERR_BADF;
+ }
+
+ if ((file->entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_DIR) {
+ return RAMFS_ERR_ISDIR;
+ }
+
+ if (size < 0) {
+ return RAMFS_ERR_INVAL;
+ }
+
+ int block_delta = ((size - 1) / fs->block_size) - ((file->entry->file.header->size - 1) / fs->block_size);
+
+ if (block_delta < 0) {
+ // Decrease size
+ while (block_delta < 0) {
+ remove_block(fs, file);
+ block_delta++;
+ }
+ } else if (block_delta > 0) {
+ // Increase size
+ while (block_delta > 0) {
+ if ((ret = add_block(fs, file)) != RAMFS_ERR_OK) {
+ return ret;
+ }
+
+ block_delta--;
+ }
+ }
+
+ file->entry->file.header->size = size;
+
+ ramfs_file_seek_internal(fs, file, file->offset, RAMFS_SEEK_SET);
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_mount(ramfs_t *fs, ramfs_config_t *config) {
+ memset(fs, 0, sizeof(ramfs_t));
+
+ ramfs_lock_init(fs->lock);
+
+ fs->size = config->size;
+ fs->block_size = config->block_size;
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_umount(ramfs_t *fs) {
+ int top = 0;
+ ramfs_entry_t *stack[256];
+ ramfs_entry_t *centry;
+ ramfs_entry_t *next_entry = NULL;
+ ramfs_entry_t *parent_entry = NULL;
+
+ ramfs_lock(fs->lock);
+
+ stack[top] = NULL;
+
+ while (top >= 0) {
+ centry = stack[top];
+ if (!centry) {
+ top--;
+ centry = fs->child;
+ parent_entry = NULL;
+ } else {
+ parent_entry = centry;
+ centry = centry->dir.child;
+ }
+
+ while (centry) {
+ next_entry = centry->next;
+
+ if ((centry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_FILE) {
+ remove_entry(fs, centry, NULL, NULL, 1);
+ } else if ((centry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_DIR) {
+ if (!centry->dir.child) {
+ remove_entry(fs, centry, NULL, NULL, 1);
+ } else {
+ stack[++top] = centry;
+ }
+ }
+
+ centry = next_entry;
+ }
+
+ if (parent_entry && (stack[top] == parent_entry)) {
+ remove_entry(fs, parent_entry, NULL, NULL, 1);
+ top--;
+ }
+ }
+
+ ramfs_lock_destroy(fs->lock);
+
+ memset(fs, 0, sizeof(ramfs_t));
+
+ return RAMFS_ERR_OK;
+}
+
+
+int ramfs_mkdir(ramfs_t *fs, const char *path) {
+ ramfs_entry_t *entry;
+ ramfs_error_t ret;
+
+ if (strcmp(path,"/") == 0) {
+ return RAMFS_ERR_EXIST;
+ }
+
+ ramfs_lock(fs->lock);
+ ret = traverse(fs, path, &entry, NULL, NULL, 1, RAMFS_DIR);
+ ramfs_unlock(fs->lock);
+
+ if (ret != RAMFS_ERR_OK) {
+ if (ret == RAMFS_ERR_NOENT) {
+ if (entry) {
+ return RAMFS_ERR_OK;
+ } else {
+ return RAMFS_ERR_NOENT;
+ }
+ } else {
+ return ret;
+ }
+ } else {
+ return RAMFS_ERR_EXIST;
+ }
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_dir_open(ramfs_t *fs, ramfs_dir_t *dir, const char *path) {
+ ramfs_entry_t *entry;
+ ramfs_error_t ret;
+
+ memset(dir, 0, sizeof(ramfs_dir_t));
+
+ if (strcmp(path,"/") == 0) {
+ ramfs_lock(fs->lock);
+ dir->child = fs->child;
+ ramfs_unlock(fs->lock);
+ } else {
+ ramfs_lock(fs->lock);
+
+ ret = traverse(fs, path, &entry, NULL, NULL, 0, 0);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+
+ dir->offset = -1;
+
+ return ret;
+ }
+
+ if ((entry->flags & RAMFS_ENTRY_TYPE_MSK) != RAMFS_DIR) {
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_NOTDIR;
+ }
+
+ ret = add_reference(fs, entry);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+
+ return ret;
+ }
+
+ dir->entry = entry;
+ dir->child = entry->dir.child;
+
+ ramfs_unlock(fs->lock);
+ }
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_dir_read(ramfs_t *fs, ramfs_dir_t *dir, ramfs_info_t *info) {
+ if (dir->offset < 0) {
+ return RAMFS_ERR_BADF;
+ }
+
+ if (!dir->child) {
+ dir->offset = -1;
+
+ return RAMFS_ERR_NOENT;
+ }
+
+ int name_len = ((dir->child->flags & RAMFS_ENTRY_NAME_LEN_MSK) >> RAMFS_ENTRY_NAME_LEN_POS);
+
+ memcpy(info->name, dir->child->name, name_len);
+ *(info->name + name_len) = 0;
+
+ info->type = (dir->child->flags & RAMFS_ENTRY_TYPE_MSK);
+ info->size = 0;
+
+ if (info->type == RAMFS_FILE) {
+ info->size = dir->child->file.header->size;
+ }
+
+ dir->child = dir->child->next;
+
+ dir->offset++;
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_dir_close(ramfs_t *fs, ramfs_dir_t *dir) {
+ ramfs_lock(fs->lock);
+ remove_reference(fs, dir->entry);
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_OK;
+}
+
+ramfs_off_t ramfs_telldir(ramfs_t *fs, ramfs_dir_t *dir) {
+ if (dir->offset < 0) {
+ return RAMFS_ERR_BADF;
+ }
+
+ return dir->offset;
+}
+
+int ramfs_stat(ramfs_t *fs, const char *path, ramfs_info_t *info) {
+ ramfs_entry_t *entry;
+ ramfs_error_t ret;
+
+ if (strcmp(path,"/") == 0) {
+ strcpy(info->name, "");
+ info->type = RAMFS_DIR;
+ } else {
+ ramfs_lock(fs->lock);
+
+ ret = traverse(fs, path, &entry, NULL, NULL, 0, 0);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+
+ return ret;
+ }
+
+ int name_len = ((entry->flags & RAMFS_ENTRY_NAME_LEN_MSK) >> RAMFS_ENTRY_NAME_LEN_POS);
+
+ memcpy(info->name, entry->name, name_len);
+ *(info->name + name_len) = 0;
+
+ info->type = entry->flags & RAMFS_ENTRY_TYPE_MSK;
+ info->size = 0;
+
+ if (info->type == RAMFS_FILE) {
+ info->size = entry->file.header->size;
+ }
+
+ ramfs_unlock(fs->lock);
+ }
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_file_stat(ramfs_t *fs, ramfs_file_t *file, ramfs_info_t *info) {
+ if (!file->entry) {
+ return RAMFS_ERR_BADF;
+
+ }
+
+ ramfs_lock(fs->lock);
+
+ int name_len = ((file->entry->flags & RAMFS_ENTRY_NAME_LEN_MSK) >> RAMFS_ENTRY_NAME_LEN_POS);
+
+ memcpy(info->name, file->entry->name, name_len);
+ *(info->name + name_len) = 0;
+
+ info->type = file->entry->flags & RAMFS_ENTRY_TYPE_MSK;
+ info->size = 0;
+
+ if (info->type == RAMFS_FILE) {
+ info->size = file->entry->file.header->size;
+ }
+
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_file_open(ramfs_t *fs, ramfs_file_t *file, const char *path, int flags) {
+ ramfs_entry_t *entry;
+ ramfs_error_t ret;
+
+ int access_mode = (flags & RAMFS_ACCMODE);
+ if ((access_mode != RAMFS_O_RDONLY) && (access_mode != RAMFS_O_WRONLY) && (access_mode != RAMFS_O_RDWR)) {
+ return RAMFS_ERR_ACCESS;
+ }
+
+ ramfs_lock(fs->lock);
+
+ ret = traverse(fs, path, &entry, NULL, NULL, flags & RAMFS_O_CREAT, RAMFS_FILE);
+ if (ret == RAMFS_ERR_NOENT) {
+ // File doesn't exist
+ if (!(flags & RAMFS_O_CREAT)) {
+ ramfs_unlock(fs->lock);
+
+ // If no O_CREAT file is present, exit
+ return RAMFS_ERR_NOENT;
+ }
+ } else if ((ret == RAMFS_ERR_OK) && (flags & RAMFS_O_EXCL) && (flags & RAMFS_O_CREAT)) {
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_EXIST;
+ } else if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+
+ return ret;
+ }
+
+ ret = add_reference(fs, entry);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+
+ return ret;
+ }
+
+ // Prepare file structure
+ memset(file, 0, sizeof(ramfs_file_t));
+
+ file->entry = entry;
+ file->flags = flags;
+
+ // Truncate file, if required
+ if ((flags & RAMFS_O_TRUNC) && ((access_mode == RAMFS_O_WRONLY) || (access_mode == RAMFS_O_RDWR))) {
+ ret = ramfs_file_truncate_internal(fs, file, 0);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+
+ return ret;
+ }
+ }
+
+ // Set file position
+ if (flags & RAMFS_O_APPEND){
+ ret = ramfs_file_seek_internal(fs, file, 0, RAMFS_SEEK_END);
+ assert(ret >= 0);
+ } else {
+ ret = ramfs_file_seek_internal(fs, file, 0, RAMFS_SEEK_SET);
+ assert(ret >= 0);
+ }
+
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_file_sync(ramfs_t *fs, ramfs_file_t *file) {
+ return RAMFS_ERR_OK;
+}
+
+ramfs_size_t ramfs_file_read(ramfs_t *fs, ramfs_file_t *file, void *buffer, ramfs_size_t size) {
+ int access_mode = (file->flags & RAMFS_ACCMODE);
+
+ if ((access_mode != RAMFS_O_RDONLY) && (access_mode != RAMFS_O_RDWR)) {
+ return RAMFS_ERR_BADF;
+ }
+
+ ramfs_lock(fs->lock);
+
+ ramfs_size_t reads = 0;
+ while ((reads < size) && (file->offset < file->entry->file.header->size)) {
+ if ((!file->block) || (file->ptr > file->block->data + fs->block_size - 1)) {
+ if (file->block) {
+ file->block = file->block->next;
+ if (file->block) {
+ file->ptr = file->block->data;
+ } else {
+ file->ptr = NULL;
+ ramfs_unlock(fs->lock);
+
+ return reads;
+ }
+ } else {
+ file->block = NULL;
+ file->ptr = NULL;
+
+ ramfs_unlock(fs->lock);
+ return reads;
+ }
+ }
+
+ *(((uint8_t *)buffer++)) = *(file->ptr++);
+
+ reads++;
+ file->offset++;
+ }
+
+ ramfs_unlock(fs->lock);
+
+ return reads;
+}
+
+ramfs_size_t ramfs_file_write(ramfs_t *fs, ramfs_file_t *file, const void *buffer, ramfs_size_t size) {
+ ramfs_error_t ret;
+
+ int access_mode = (file->flags & RAMFS_ACCMODE);
+
+ if ((access_mode != RAMFS_O_WRONLY) && (access_mode != RAMFS_O_RDWR)) {
+ return RAMFS_ERR_BADF;
+ }
+
+ ramfs_lock(fs->lock);
+
+ ramfs_size_t writes = 0;
+ while (writes < size) {
+ if ((!file->block) || (!file->ptr) || (file->ptr > file->block->data + fs->block_size - 1)) {
+ if (file->block && file->block->next) {
+ file->block = file->block->next;
+ file->ptr = file->block->data;
+ } else {
+ ret = add_block(fs, file);
+ if (ret != RAMFS_ERR_OK) {
+ file->block = NULL;
+ file->ptr = NULL;
+ ramfs_unlock(fs->lock);
+ return ret;
+ }
+ }
+ }
+
+ *(file->ptr++) = *(((uint8_t *)buffer++));
+
+ writes++;
+ file->offset++;
+
+ if (file->offset > file->entry->file.header->size) {
+ file->entry->file.header->size++;
+ }
+ }
+
+ ramfs_unlock(fs->lock);
+
+ return writes;
+}
+
+int ramfs_file_truncate(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t size) {
+ ramfs_error_t ret;
+
+ ramfs_lock(fs->lock);
+ ret = ramfs_file_truncate_internal(fs, file, size);
+ ramfs_unlock(fs->lock);
+
+ return ret;
+}
+
+int ramfs_file_close(ramfs_t *fs, ramfs_file_t *file) {
+ ramfs_lock(fs->lock);
+ remove_reference(fs, file->entry);
+ ramfs_unlock(fs->lock);
+
+ memset(file, 0, sizeof(ramfs_file_t));
+
+ return RAMFS_ERR_OK;
+}
+
+ramfs_off_t ramfs_file_seek(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t offset, ramfs_whence_t whence) {
+ ramfs_off_t ret;
+
+ ramfs_lock(fs->lock);
+ ret = ramfs_file_seek_internal(fs, file, offset, whence);
+ ramfs_unlock(fs->lock);
+
+ return ret;
+}
+
+int ramfs_rename(ramfs_t *fs, const char *oldpath, const char *newpath) {
+ ramfs_entry_t *old_entry;
+ ramfs_entry_t *parent_old_entry;
+ ramfs_entry_t *prev_old_entry;
+ ramfs_entry_t *new_entry;
+ ramfs_error_t ret;
+
+ ramfs_lock(fs->lock);
+
+ ret = traverse(fs, oldpath, &old_entry, &parent_old_entry, &prev_old_entry, 0, 0);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+ return ret;
+ }
+
+ ret = traverse(fs, newpath, &new_entry, NULL, NULL, 1, old_entry->flags & RAMFS_ENTRY_TYPE_MSK);
+ if (ret == RAMFS_ERR_OK) {
+ if (((new_entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_DIR) && ((old_entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_FILE)) {
+ ramfs_unlock(fs->lock);
+ return RAMFS_ERR_ISDIR;
+ } else if (((new_entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_DIR) && (new_entry->dir.child)) {
+ ramfs_unlock(fs->lock);
+ return RAMFS_ERR_NOTEMPTY;
+ }
+ } else if (ret != RAMFS_ERR_NOENT) {
+ ramfs_unlock(fs->lock);
+ return ret;
+ }
+
+ if ((new_entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_DIR) {
+ // New and old entries are directories
+ // New directory is empty
+
+ // Move all sub-directories and files of the old entry to the new entry
+ new_entry->dir.child = old_entry->dir.child;
+ } else if ((old_entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_DIR) {
+ new_entry->dir.child = old_entry->dir.child;
+ } else if ((old_entry->flags & RAMFS_ENTRY_TYPE_MSK) == RAMFS_FILE) {
+ new_entry->file.header->head = old_entry->file.header->head;
+ new_entry->file.header->tail = old_entry->file.header->tail;
+ new_entry->file.header->size = old_entry->file.header->size;
+ }
+
+ remove_entry(fs, old_entry, parent_old_entry, prev_old_entry, 0);
+
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_rmdir(ramfs_t *fs, const char *path) {
+ ramfs_entry_t *entry;
+ ramfs_entry_t *parent_entry;
+ ramfs_entry_t *prev_entry;
+ ramfs_error_t ret;
+
+ if (strcmp(path,"/") == 0) {
+ return RAMFS_ERR_BUSY;
+ }
+
+ ramfs_lock(fs->lock);
+
+ ret = traverse(fs, path, &entry, &parent_entry, &prev_entry, 0, 0);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+ return ret;
+ }
+
+ if ((entry->flags & RAMFS_ENTRY_TYPE_MSK) != RAMFS_DIR) {
+ ramfs_unlock(fs->lock);
+ return RAMFS_ERR_NOTDIR;
+ }
+
+ if (entry->dir.child) {
+ ramfs_unlock(fs->lock);
+ return RAMFS_ERR_NOTEMPTY;
+ }
+
+ remove_entry(fs, entry, parent_entry, prev_entry, 0);
+
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_OK;
+}
+
+int ramfs_unlink(ramfs_t *fs, const char *pathname) {
+ ramfs_entry_t *entry;
+ ramfs_entry_t *parent_entry;
+ ramfs_entry_t *prev_entry;
+ ramfs_error_t ret;
+
+ ramfs_lock(fs->lock);
+
+ ret = traverse(fs, pathname, &entry, &parent_entry, &prev_entry, 0, 0);
+ if (ret != RAMFS_ERR_OK) {
+ ramfs_unlock(fs->lock);
+ return ret;
+ }
+
+ if ((entry->flags & RAMFS_ENTRY_TYPE_MSK) != RAMFS_FILE) {
+ ramfs_unlock(fs->lock);
+ return RAMFS_ERR_PERM;
+ }
+
+ remove_entry(fs, entry, parent_entry, prev_entry, 1);
+
+ ramfs_unlock(fs->lock);
+
+ return RAMFS_ERR_OK;
+}
diff --git a/libiot/ramfs/ramfs.h b/libiot/ramfs/ramfs.h
new file mode 100644
index 0000000..cde76cf
--- /dev/null
+++ b/libiot/ramfs/ramfs.h
@@ -0,0 +1,274 @@
+/*
+ * 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
+ *
+ */
+
+/**
+ * @brief RAM file system (RAMFS)
+ *
+ * The RAMFS is a POSIX-compliance file system entirely stored in RAM, without
+ * persistence, which means that all the data stored in the file system is
+ * lost on each reboot.
+ *
+ * In RAMFS, the file system is stored in a tree structure, in which there are
+ * 2 types of nodes (entries): directories, and files.
+ *
+ * RAMFS structure overview:
+ *
+ * ----------
+ * - RAMFS -
+ * ----------
+ * |
+ * | child
+ * \|/
+ * ---------------- next ---------------- next ----------------
+ * - directory or - ------> - file - ------> - directory -
+ * - file entry - - entry - - entry -
+ * ---------------- ---------------- ----------------
+ * | |
+ * | header | child
+ * \|/ \|/
+ * --------------- ----------------
+ * - file header - - directory or -
+ * --------------- - file entry -
+ * | ----------------
+ * |
+ * \|/
+ * --------- next ---------
+ * - block - ----> - block -
+ * --------- ---------
+ */
+
+#ifndef _RAMFS_H_
+#define _RAMFS_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mutex.h>
+
+#if PATH_MAX > 64
+#error "ramfs, PATH_MAX must be <= 64"
+#endif
+
+/*
+ * To use the file system in a multi-threaded environment,
+ * define the lock type (mutex) used in your platform.
+ */
+#define ramfs_lock_t struct mtx
+
+/*
+ * If ramfs_lock_t is defined, define the following macros:
+ *
+ * ramfs_lock_init(l): initialize / create the lock
+ * ramfs_lock_destroy(l): destroy the lock
+ * ramfs_lock(l): obtain the lock
+ * ramfs_unlock(l): release the lock
+ */
+#ifdef ramfs_lock_t
+#define ramfs_lock_init(l) \
+ mtx_init(&l, NULL, NULL, 0);
+
+#define ramfs_lock_destroy(l) \
+ mtx_destroy(&l);
+
+#define ramfs_lock(l) \
+ mtx_lock(&l);
+
+#define ramfs_unlock(l) \
+ mtx_unlock(&l);
+#else
+#define ramfs_lock_init()
+#define ramfs_lock_destroy()
+#define ramfs_lock()
+#define ramfs_unlock()
+#endif
+
+typedef int32_t ramfs_off_t;
+typedef int32_t ramfs_size_t;
+
+/**
+ * @brief File system entry flags.
+ *
+ * bit0..bit0: entry type
+ * bit1..bit6: length of the name of the entry
+ * bit7..bit7: entry is deleted from the file system, but not still removed
+ *
+ */
+typedef uint8_t ramfs_entry_flags_t;
+
+#define RAMFS_ENTRY_TYPE_MSK 0b00000001
+#define RAMFS_ENTRY_NAME_LEN_MSK 0b01111110
+#define RAMFS_ENTRY_NAME_LEN_POS 1
+#define RAMFS_ENTRY_RM_LEN_MSK 0b10000000
+
+typedef enum {
+ RAMFS_DIR = 0,
+ RAMFS_FILE = 1
+} ramfs_entry_type_t;
+
+typedef enum {
+ RAMFS_ERR_OK = 0,
+ RAMFS_ERR_NOMEM = -1,
+ RAMFS_ERR_NOENT = -2,
+ RAMFS_ERR_EXIST = -3,
+ RAMFS_ERR_NOTDIR = -4,
+ RAMFS_ERR_BADF = -5,
+ RAMFS_ERR_ACCESS = -6,
+ RAMFS_ERR_NOSPC = -7,
+ RAMFS_ERR_INVAL = -8,
+ RAMFS_ERR_ISDIR = -9,
+ RAMFS_ERR_NOTEMPTY = -10,
+ RAMFS_ERR_BUSY = -11,
+ RAMFS_ERR_PERM = -12,
+ RAMFS_ERR_NAMETOOLONG = -13,
+} ramfs_error_t;
+
+typedef enum {
+ RAMFS_O_RDONLY = 1, // Open a file as read only
+ RAMFS_O_WRONLY = 2, // Open a file as write only
+ RAMFS_O_RDWR = 3, // Open a file as read and write
+ RAMFS_O_CREAT = 0x0100, // Create a file if it does not exist
+ RAMFS_O_EXCL = 0x0200, // Fail if a file already exists
+ RAMFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
+ RAMFS_O_APPEND = 0x0800, // Move to end of file on every write
+} ramfs_flags_t;
+
+#define RAMFS_ACCMODE (RAMFS_O_RDONLY | RAMFS_O_WRONLY | RAMFS_O_RDWR)
+
+typedef enum {
+ RAMFS_SEEK_SET = 1,
+ RAMFS_SEEK_CUR = 2,
+ RAMFS_SEEK_END = 3,
+} ramfs_whence_t;
+
+typedef struct ramfs_block {
+ struct ramfs_block *next; /*!< Next block in chain */
+ uint8_t data[1]; /*!< Block data */
+} ramfs_block_t;
+
+typedef struct ram_file_header {
+ ramfs_block_t *head; /*!< File head */
+ ramfs_block_t *tail; /*!< File tail */
+ ramfs_size_t size; /*!< File size */
+} ram_file_header_t;
+
+typedef struct ramfs_entry {
+ ramfs_entry_flags_t flags; /*!< Entry flags */
+ struct ramfs_entry *next; /*!< Next entry */
+ union {
+ struct {
+ struct ram_file_header *header; /*!< File header */
+ } file;
+ struct {
+ struct ramfs_entry *child; /*!< Entry type */
+ } dir;
+ };
+ char name[1]; /*!< Entry name */
+} ramfs_entry_t;
+
+typedef struct {
+ ramfs_off_t offset; /*!< Current seek offset */
+ ramfs_entry_t *entry; /*!< Directory entry */
+ ramfs_entry_t *child; /*!< Directory child chain */
+} ramfs_dir_t;
+
+typedef struct {
+ ramfs_entry_t *entry; /*!< File entry reference */
+ uint32_t flags; /*!< Open flags */
+ ramfs_off_t offset; /*!< Current seek offset */
+ ramfs_block_t *block; /*!< Current read/write block */
+ uint8_t *ptr; /*!< Current read/write pointer into current block */
+} ramfs_file_t;
+
+typedef struct {
+ char name[PATH_MAX + 1];
+ ramfs_entry_type_t type;
+ ramfs_size_t size;
+} ramfs_info_t;
+
+typedef struct ramfs_entry_ref {
+ ramfs_entry_t *entry;
+ uint8_t uses;
+ struct ramfs_entry_ref *next;
+} ramfs_entry_ref_t;
+
+typedef struct {
+ ramfs_entry_t *child; /*!< Root directory child chain */
+ ramfs_entry_ref_t *ref; /*< Open references to file system entries */
+ ramfs_size_t size;
+ ramfs_size_t current_size;
+ ramfs_size_t block_size;
+#ifdef ramfs_lock_t
+ ramfs_lock_t lock;
+#endif
+} ramfs_t;
+
+typedef struct {
+ ramfs_size_t size;
+ ramfs_size_t block_size;
+} ramfs_config_t;
+
+int ramfs_mount(ramfs_t *fs, ramfs_config_t *config);
+int ramfs_umount(ramfs_t *fs);
+int ramfs_mkdir(ramfs_t *fs, const char *path);
+int ramfs_dir_open(ramfs_t *fs, ramfs_dir_t *dir, const char *path);
+int ramfs_dir_read(ramfs_t *fs, ramfs_dir_t *dir, ramfs_info_t *info);
+int ramfs_dir_close(ramfs_t *fs, ramfs_dir_t *dir);
+int ramfs_stat(ramfs_t *fs, const char *path, ramfs_info_t *info);
+int ramfs_file_open(ramfs_t *fs, ramfs_file_t *file, const char *path, int flags);
+int ramfs_file_sync(ramfs_t *fs, ramfs_file_t *file);
+ramfs_size_t ramfs_file_read(ramfs_t *fs, ramfs_file_t *file, void *buffer, ramfs_size_t size);
+ramfs_size_t ramfs_file_write(ramfs_t *fs, ramfs_file_t *file, const void *buffer, ramfs_size_t size);
+int ramfs_file_close(ramfs_t *fs, ramfs_file_t *file);
+ramfs_off_t ramfs_file_seek(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t offset, ramfs_whence_t whence);
+int ramfs_rename(ramfs_t *fs, const char *oldpath, const char *newpath);
+int ramfs_rmdir(ramfs_t *fs, const char *path);
+int ramfs_unlink(ramfs_t *fs, const char *pathname);
+ramfs_off_t ramfs_telldir(ramfs_t *fs, ramfs_dir_t *dir);
+int ramfs_file_truncate(ramfs_t *fs, ramfs_file_t *file, ramfs_off_t size);
+int ramfs_file_stat(ramfs_t *fs, ramfs_file_t *file, ramfs_info_t *info);
+
+#endif /* _RAMFS_H_ */