You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2016/01/15 18:56:08 UTC
[01/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Repository: incubator-mynewt-larva
Updated Branches:
refs/heads/master 8f2fb6c7e -> 61b2b011e
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/testreport/egg.yml
----------------------------------------------------------------------
diff --git a/libs/testreport/egg.yml b/libs/testreport/egg.yml
index 4487dd7..1b69e77 100644
--- a/libs/testreport/egg.yml
+++ b/libs/testreport/egg.yml
@@ -1,5 +1,5 @@
egg.name: libs/testreport
egg.vers: 0.1
egg.deps:
+ - fs/nffs
- libs/testutil
- - libs/nffs
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/project/boot/boot.yml
----------------------------------------------------------------------
diff --git a/project/boot/boot.yml b/project/boot/boot.yml
index 00a0863..1681382 100644
--- a/project/boot/boot.yml
+++ b/project/boot/boot.yml
@@ -3,7 +3,7 @@ project.identities: bootloader
project.eggs:
- libs/os
- libs/bootutil
- - libs/nffs
+ - fs/nffs
- libs/console/stub
- libs/util
- libs/baselibc
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/project/boot/egg.yml
----------------------------------------------------------------------
diff --git a/project/boot/egg.yml b/project/boot/egg.yml
index 865e5d6..68840d0 100644
--- a/project/boot/egg.yml
+++ b/project/boot/egg.yml
@@ -1,9 +1,9 @@
egg.name: project/boot
egg.vers: 0.1
egg.deps:
+ - fs/nffs
- libs/os
- libs/bootutil
- - libs/nffs
- libs/console/stub
- libs/util
- libs/baselibc
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/project/ffs2native/egg.yml
----------------------------------------------------------------------
diff --git a/project/ffs2native/egg.yml b/project/ffs2native/egg.yml
new file mode 100644
index 0000000..5cabab9
--- /dev/null
+++ b/project/ffs2native/egg.yml
@@ -0,0 +1,6 @@
+project.name: ffs2native
+project.eggs:
+ - fs/nffs
+ - libs/os
+ - libs/console/full
+ - hw/hal
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/project/ffs2native/ffs2native.yml
----------------------------------------------------------------------
diff --git a/project/ffs2native/ffs2native.yml b/project/ffs2native/ffs2native.yml
index e1cbc96..5cabab9 100644
--- a/project/ffs2native/ffs2native.yml
+++ b/project/ffs2native/ffs2native.yml
@@ -1,6 +1,6 @@
project.name: ffs2native
project.eggs:
+ - fs/nffs
- libs/os
- - libs/nffs
- libs/console/full
- hw/hal
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/project/luatest/luatest.yml
----------------------------------------------------------------------
diff --git a/project/luatest/luatest.yml b/project/luatest/luatest.yml
index 75df6f8..dd74f30 100644
--- a/project/luatest/luatest.yml
+++ b/project/luatest/luatest.yml
@@ -1,9 +1,9 @@
project.name: luatest
project.eggs:
+ - fs/nffs
- libs/os
- libs/elua/elua_base
- libs/shell
- libs/console/full
- libs/util
- - libs/nffs
- libs/baselibc
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/project/test/egg.yml
----------------------------------------------------------------------
diff --git a/project/test/egg.yml b/project/test/egg.yml
index 6ece3b4..62a7322 100644
--- a/project/test/egg.yml
+++ b/project/test/egg.yml
@@ -1,8 +1,8 @@
egg.name: project/test
egg.vers: 0.1
egg.deps:
+ - fs/nffs
- libs/testutil
- libs/os
- - libs/nffs
- libs/bootutil
- libs/testreport
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/project/test/test.yml
----------------------------------------------------------------------
diff --git a/project/test/test.yml b/project/test/test.yml
index 84c3950..1ef8aeb 100644
--- a/project/test/test.yml
+++ b/project/test/test.yml
@@ -1,8 +1,8 @@
project.name: test
project.eggs:
+ - fs/nffs
- libs/testutil
- libs/os
- - libs/nffs
- libs/bootutil
- libs/testreport
- libs/console/full
[09/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/test/arch/sim/nffs_test.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/test/arch/sim/nffs_test.c b/fs/nffs/src/test/arch/sim/nffs_test.c
new file mode 100644
index 0000000..f2604d4
--- /dev/null
+++ b/fs/nffs/src/test/arch/sim/nffs_test.c
@@ -0,0 +1,2441 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "hal/hal_flash.h"
+#include "testutil/testutil.h"
+#include "fs/fs.h"
+#include "nffs/nffs.h"
+#include "nffs/nffs_test.h"
+#include "nffs_test_priv.h"
+#include "nffs_priv.h"
+
+int flash_native_memset(uint32_t offset, uint8_t c, uint32_t len);
+
+static const struct nffs_area_desc nffs_area_descs[] = {
+ { 0x00000000, 16 * 1024 },
+ { 0x00004000, 16 * 1024 },
+ { 0x00008000, 16 * 1024 },
+ { 0x0000c000, 16 * 1024 },
+ { 0x00010000, 64 * 1024 },
+ { 0x00020000, 128 * 1024 },
+ { 0x00040000, 128 * 1024 },
+ { 0x00060000, 128 * 1024 },
+ { 0x00080000, 128 * 1024 },
+ { 0x000a0000, 128 * 1024 },
+ { 0x000c0000, 128 * 1024 },
+ { 0x000e0000, 128 * 1024 },
+ { 0, 0 },
+};
+
+static void
+nffs_test_util_assert_ent_name(struct fs_dirent *dirent,
+ const char *expected_name)
+{
+ char name[NFFS_FILENAME_MAX_LEN + 1];
+ uint8_t name_len;
+ int rc;
+
+ rc = fs_dirent_name(dirent, sizeof name, name, &name_len);
+ TEST_ASSERT(rc == 0);
+ if (rc == 0) {
+ TEST_ASSERT(strcmp(name, expected_name) == 0);
+ }
+}
+
+static void
+nffs_test_util_assert_file_len(struct fs_file *file, uint32_t expected)
+{
+ uint32_t len;
+ int rc;
+
+ rc = fs_filelen(file, &len);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(len == expected);
+}
+
+static void
+nffs_test_util_assert_cache_is_sane(const char *filename)
+{
+ struct nffs_cache_inode *cache_inode;
+ struct nffs_cache_block *cache_block;
+ struct fs_file *fs_file;
+ struct nffs_file *file;
+ uint32_t cache_start;
+ uint32_t cache_end;
+ uint32_t block_end;
+ int rc;
+
+ rc = fs_open(filename, FS_ACCESS_READ, &fs_file);
+ TEST_ASSERT(rc == 0);
+
+ file = (struct nffs_file *)fs_file;
+ rc = nffs_cache_inode_ensure(&cache_inode, file->nf_inode_entry);
+ TEST_ASSERT(rc == 0);
+
+ nffs_cache_inode_range(cache_inode, &cache_start, &cache_end);
+
+ if (TAILQ_EMPTY(&cache_inode->nci_block_list)) {
+ TEST_ASSERT(cache_start == 0 && cache_end == 0);
+ } else {
+ block_end = 0; /* Pacify gcc. */
+ TAILQ_FOREACH(cache_block, &cache_inode->nci_block_list, ncb_link) {
+ if (cache_block == TAILQ_FIRST(&cache_inode->nci_block_list)) {
+ TEST_ASSERT(cache_block->ncb_file_offset == cache_start);
+ } else {
+ /* Ensure no gap between this block and its predecessor. */
+ TEST_ASSERT(cache_block->ncb_file_offset == block_end);
+ }
+
+ block_end = cache_block->ncb_file_offset +
+ cache_block->ncb_block.nb_data_len;
+ if (cache_block == TAILQ_LAST(&cache_inode->nci_block_list,
+ nffs_cache_block_list)) {
+
+ TEST_ASSERT(block_end == cache_end);
+ }
+ }
+ }
+
+ rc = fs_close(fs_file);
+ TEST_ASSERT(rc == 0);
+}
+
+static void
+nffs_test_util_assert_contents(const char *filename, const char *contents,
+ int contents_len)
+{
+ struct fs_file *file;
+ uint32_t bytes_read;
+ void *buf;
+ int rc;
+
+ rc = fs_open(filename, FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == 0);
+
+ buf = malloc(contents_len + 1);
+ TEST_ASSERT(buf != NULL);
+
+ rc = fs_read(file, contents_len + 1, buf, &bytes_read);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(bytes_read == contents_len);
+ TEST_ASSERT(memcmp(buf, contents, contents_len) == 0);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ free(buf);
+
+ nffs_test_util_assert_cache_is_sane(filename);
+}
+
+static int
+nffs_test_util_block_count(const char *filename)
+{
+ struct nffs_hash_entry *entry;
+ struct nffs_block block;
+ struct nffs_file *file;
+ struct fs_file *fs_file;
+ int count;
+ int rc;
+
+ rc = fs_open(filename, FS_ACCESS_READ, &fs_file);
+ TEST_ASSERT(rc == 0);
+
+ file = (struct nffs_file *)fs_file;
+ count = 0;
+ entry = file->nf_inode_entry->nie_last_block_entry;
+ while (entry != NULL) {
+ count++;
+ rc = nffs_block_from_hash_entry(&block, entry);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(block.nb_prev != entry);
+ entry = block.nb_prev;
+ }
+
+ rc = fs_close(fs_file);
+ TEST_ASSERT(rc == 0);
+
+ return count;
+}
+
+static void
+nffs_test_util_assert_block_count(const char *filename, int expected_count)
+{
+ TEST_ASSERT(nffs_test_util_block_count(filename) == expected_count);
+}
+
+static void
+nffs_test_util_assert_cache_range(const char *filename,
+ uint32_t expected_cache_start,
+ uint32_t expected_cache_end)
+{
+ struct nffs_cache_inode *cache_inode;
+ struct nffs_file *file;
+ struct fs_file *fs_file;
+ uint32_t cache_start;
+ uint32_t cache_end;
+ int rc;
+
+ rc = fs_open(filename, FS_ACCESS_READ, &fs_file);
+ TEST_ASSERT(rc == 0);
+
+ file = (struct nffs_file *)fs_file;
+ rc = nffs_cache_inode_ensure(&cache_inode, file->nf_inode_entry);
+ TEST_ASSERT(rc == 0);
+
+ nffs_cache_inode_range(cache_inode, &cache_start, &cache_end);
+ TEST_ASSERT(cache_start == expected_cache_start);
+ TEST_ASSERT(cache_end == expected_cache_end);
+
+ rc = fs_close(fs_file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_cache_is_sane(filename);
+}
+
+static void
+nffs_test_util_create_file_blocks(const char *filename,
+ const struct nffs_test_block_desc *blocks,
+ int num_blocks)
+{
+ struct fs_file *file;
+ uint32_t total_len;
+ uint32_t offset;
+ char *buf;
+ int num_writes;
+ int rc;
+ int i;
+
+ rc = fs_open(filename, FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
+ TEST_ASSERT(rc == 0);
+
+ total_len = 0;
+ if (num_blocks <= 0) {
+ num_writes = 1;
+ } else {
+ num_writes = num_blocks;
+ }
+ for (i = 0; i < num_writes; i++) {
+ rc = fs_write(file, blocks[i].data, blocks[i].data_len);
+ TEST_ASSERT(rc == 0);
+
+ total_len += blocks[i].data_len;
+ }
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ buf = malloc(total_len);
+ TEST_ASSERT(buf != NULL);
+
+ offset = 0;
+ for (i = 0; i < num_writes; i++) {
+ memcpy(buf + offset, blocks[i].data, blocks[i].data_len);
+ offset += blocks[i].data_len;
+ }
+ TEST_ASSERT(offset == total_len);
+
+ nffs_test_util_assert_contents(filename, buf, total_len);
+ if (num_blocks > 0) {
+ nffs_test_util_assert_block_count(filename, num_blocks);
+ }
+
+ free(buf);
+}
+
+static void
+nffs_test_util_create_file(const char *filename, const char *contents,
+ int contents_len)
+{
+ struct nffs_test_block_desc block;
+
+ block.data = contents;
+ block.data_len = contents_len;
+
+ nffs_test_util_create_file_blocks(filename, &block, 0);
+}
+
+static void
+nffs_test_util_append_file(const char *filename, const char *contents,
+ int contents_len)
+{
+ struct fs_file *file;
+ int rc;
+
+ rc = fs_open(filename, FS_ACCESS_WRITE | FS_ACCESS_APPEND, &file);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_write(file, contents, contents_len);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+}
+
+static void
+nffs_test_copy_area(const struct nffs_area_desc *from,
+ const struct nffs_area_desc *to)
+{
+ void *buf;
+ int rc;
+
+ TEST_ASSERT(from->nad_length == to->nad_length);
+
+ buf = malloc(from->nad_length);
+ TEST_ASSERT(buf != NULL);
+
+ rc = hal_flash_read(from->nad_flash_id, from->nad_offset, buf,
+ from->nad_length);
+ TEST_ASSERT(rc == 0);
+
+ rc = hal_flash_erase(from->nad_flash_id, to->nad_offset, to->nad_length);
+ TEST_ASSERT(rc == 0);
+
+ rc = hal_flash_write(to->nad_flash_id, to->nad_offset, buf, to->nad_length);
+ TEST_ASSERT(rc == 0);
+
+ free(buf);
+}
+
+static void
+nffs_test_util_create_subtree(const char *parent_path,
+ const struct nffs_test_file_desc *elem)
+{
+ char *path;
+ int rc;
+ int i;
+
+ if (parent_path == NULL) {
+ path = malloc(1);
+ TEST_ASSERT(path != NULL);
+ path[0] = '\0';
+ } else {
+ path = malloc(strlen(parent_path) + 1 + strlen(elem->filename) + 1);
+ TEST_ASSERT(path != NULL);
+
+ sprintf(path, "%s/%s", parent_path, elem->filename);
+ }
+
+ if (elem->is_dir) {
+ if (parent_path != NULL) {
+ rc = fs_mkdir(path);
+ TEST_ASSERT(rc == 0);
+ }
+
+ if (elem->children != NULL) {
+ for (i = 0; elem->children[i].filename != NULL; i++) {
+ nffs_test_util_create_subtree(path, elem->children + i);
+ }
+ }
+ } else {
+ nffs_test_util_create_file(path, elem->contents, elem->contents_len);
+ }
+
+ free(path);
+}
+
+static void
+nffs_test_util_create_tree(const struct nffs_test_file_desc *root_dir)
+{
+ nffs_test_util_create_subtree(NULL, root_dir);
+}
+
+#define NFFS_TEST_TOUCHED_ARR_SZ (16 * 1024)
+static struct nffs_hash_entry
+ *nffs_test_touched_entries[NFFS_TEST_TOUCHED_ARR_SZ];
+static int nffs_test_num_touched_entries;
+
+static void
+nffs_test_assert_file(const struct nffs_test_file_desc *file,
+ struct nffs_inode_entry *inode_entry,
+ const char *path)
+{
+ const struct nffs_test_file_desc *child_file;
+ struct nffs_inode inode;
+ struct nffs_inode_entry *child_inode_entry;
+ char *child_path;
+ int child_filename_len;
+ int path_len;
+ int rc;
+
+ TEST_ASSERT(nffs_test_num_touched_entries < NFFS_TEST_TOUCHED_ARR_SZ);
+ nffs_test_touched_entries[nffs_test_num_touched_entries] =
+ &inode_entry->nie_hash_entry;
+ nffs_test_num_touched_entries++;
+
+ path_len = strlen(path);
+
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ TEST_ASSERT(rc == 0);
+
+ if (nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)) {
+ for (child_file = file->children;
+ child_file != NULL && child_file->filename != NULL;
+ child_file++) {
+
+ child_filename_len = strlen(child_file->filename);
+ child_path = malloc(path_len + 1 + child_filename_len + 1);
+ TEST_ASSERT(child_path != NULL);
+ memcpy(child_path, path, path_len);
+ child_path[path_len] = '/';
+ memcpy(child_path + path_len + 1, child_file->filename,
+ child_filename_len);
+ child_path[path_len + 1 + child_filename_len] = '\0';
+
+ rc = nffs_path_find_inode_entry(child_path, &child_inode_entry);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_assert_file(child_file, child_inode_entry, child_path);
+
+ free(child_path);
+ }
+ } else {
+ nffs_test_util_assert_contents(path, file->contents,
+ file->contents_len);
+ }
+}
+
+static void
+nffs_test_assert_branch_touched(struct nffs_inode_entry *inode_entry)
+{
+ struct nffs_inode_entry *child;
+ int i;
+
+ if (inode_entry == nffs_lost_found_dir) {
+ return;
+ }
+
+ for (i = 0; i < nffs_test_num_touched_entries; i++) {
+ if (nffs_test_touched_entries[i] == &inode_entry->nie_hash_entry) {
+ break;
+ }
+ }
+ TEST_ASSERT(i < nffs_test_num_touched_entries);
+ nffs_test_touched_entries[i] = NULL;
+
+ if (nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)) {
+ SLIST_FOREACH(child, &inode_entry->nie_child_list, nie_sibling_next) {
+ nffs_test_assert_branch_touched(child);
+ }
+ }
+}
+
+static void
+nffs_test_assert_child_inode_present(struct nffs_inode_entry *child)
+{
+ const struct nffs_inode_entry *inode_entry;
+ const struct nffs_inode_entry *parent;
+ struct nffs_inode inode;
+ int rc;
+
+ rc = nffs_inode_from_entry(&inode, child);
+ TEST_ASSERT(rc == 0);
+
+ parent = inode.ni_parent;
+ TEST_ASSERT(parent != NULL);
+ TEST_ASSERT(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
+
+ SLIST_FOREACH(inode_entry, &parent->nie_child_list, nie_sibling_next) {
+ if (inode_entry == child) {
+ return;
+ }
+ }
+
+ TEST_ASSERT(0);
+}
+
+static void
+nffs_test_assert_block_present(struct nffs_hash_entry *block_entry)
+{
+ const struct nffs_inode_entry *inode_entry;
+ struct nffs_hash_entry *cur;
+ struct nffs_block block;
+ int rc;
+
+ rc = nffs_block_from_hash_entry(&block, block_entry);
+ TEST_ASSERT(rc == 0);
+
+ inode_entry = block.nb_inode_entry;
+ TEST_ASSERT(inode_entry != NULL);
+ TEST_ASSERT(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
+
+ cur = inode_entry->nie_last_block_entry;
+ while (cur != NULL) {
+ if (cur == block_entry) {
+ return;
+ }
+
+ rc = nffs_block_from_hash_entry(&block, cur);
+ TEST_ASSERT(rc == 0);
+ cur = block.nb_prev;
+ }
+
+ TEST_ASSERT(0);
+}
+
+static void
+nffs_test_assert_children_sorted(struct nffs_inode_entry *inode_entry)
+{
+ struct nffs_inode_entry *child_entry;
+ struct nffs_inode_entry *prev_entry;
+ struct nffs_inode child_inode;
+ struct nffs_inode prev_inode;
+ int cmp;
+ int rc;
+
+ prev_entry = NULL;
+ SLIST_FOREACH(child_entry, &inode_entry->nie_child_list,
+ nie_sibling_next) {
+ rc = nffs_inode_from_entry(&child_inode, child_entry);
+ TEST_ASSERT(rc == 0);
+
+ if (prev_entry != NULL) {
+ rc = nffs_inode_from_entry(&prev_inode, prev_entry);
+ TEST_ASSERT(rc == 0);
+
+ rc = nffs_inode_filename_cmp_flash(&prev_inode, &child_inode,
+ &cmp);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(cmp < 0);
+ }
+
+ if (nffs_hash_id_is_dir(child_entry->nie_hash_entry.nhe_id)) {
+ nffs_test_assert_children_sorted(child_entry);
+ }
+
+ prev_entry = child_entry;
+ }
+}
+
+static void
+nffs_test_assert_system_once(const struct nffs_test_file_desc *root_dir)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_hash_entry *entry;
+ int i;
+
+ nffs_test_num_touched_entries = 0;
+ nffs_test_assert_file(root_dir, nffs_root_dir, "");
+ nffs_test_assert_branch_touched(nffs_root_dir);
+
+ /* Ensure no orphaned inodes or blocks. */
+ NFFS_HASH_FOREACH(entry, i) {
+ TEST_ASSERT(entry->nhe_flash_loc != NFFS_FLASH_LOC_NONE);
+ if (nffs_hash_id_is_inode(entry->nhe_id)) {
+ inode_entry = (void *)entry;
+ TEST_ASSERT(inode_entry->nie_refcnt == 1);
+ if (entry->nhe_id == NFFS_ID_ROOT_DIR) {
+ TEST_ASSERT(inode_entry == nffs_root_dir);
+ } else {
+ nffs_test_assert_child_inode_present(inode_entry);
+ }
+ } else {
+ nffs_test_assert_block_present(entry);
+ }
+ }
+
+ /* Ensure proper sorting. */
+ nffs_test_assert_children_sorted(nffs_root_dir);
+}
+
+static void
+nffs_test_assert_system(const struct nffs_test_file_desc *root_dir,
+ const struct nffs_area_desc *area_descs)
+{
+ int rc;
+
+ /* Ensure files are as specified, and that there are no other files or
+ * orphaned inodes / blocks.
+ */
+ nffs_test_assert_system_once(root_dir);
+
+ /* Force a garbage collection cycle. */
+ rc = nffs_gc(NULL);
+ TEST_ASSERT(rc == 0);
+
+ /* Ensure file system is still as expected. */
+ nffs_test_assert_system_once(root_dir);
+
+ /* Clear cached data and restore from flash (i.e, simulate a reboot). */
+ rc = nffs_misc_reset();
+ TEST_ASSERT(rc == 0);
+ rc = nffs_detect(area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /* Ensure file system is still as expected. */
+ nffs_test_assert_system_once(root_dir);
+}
+
+static void
+nffs_test_assert_area_seqs(int seq1, int count1, int seq2, int count2)
+{
+ struct nffs_disk_area disk_area;
+ int cur1;
+ int cur2;
+ int rc;
+ int i;
+
+ cur1 = 0;
+ cur2 = 0;
+
+ for (i = 0; i < nffs_num_areas; i++) {
+ rc = nffs_flash_read(i, 0, &disk_area, sizeof disk_area);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(nffs_area_magic_is_set(&disk_area));
+ TEST_ASSERT(disk_area.nda_gc_seq == nffs_areas[i].na_gc_seq);
+ if (i == nffs_scratch_area_idx) {
+ TEST_ASSERT(disk_area.nda_id == NFFS_AREA_ID_NONE);
+ }
+
+ if (nffs_areas[i].na_gc_seq == seq1) {
+ cur1++;
+ } else if (nffs_areas[i].na_gc_seq == seq2) {
+ cur2++;
+ } else {
+ TEST_ASSERT(0);
+ }
+ }
+
+ TEST_ASSERT(cur1 == count1 && cur2 == count2);
+}
+
+static void
+nffs_test_mkdir(void)
+{
+ struct fs_file *file;
+ int rc;
+
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/a/b/c/d");
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ rc = fs_mkdir("asdf");
+ TEST_ASSERT(rc == FS_EINVAL);
+
+ rc = fs_mkdir("/a");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/a/b");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/a/b/c");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/a/b/c/d");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_open("/a/b/c/d/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "a",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "b",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "c",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "d",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = NULL,
+ .contents_len = 0,
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_unlink)
+{
+ struct fs_file *file0;
+ struct fs_file *file1;
+ struct fs_file *file2;
+ struct nffs_file *nfs_file;
+ uint8_t buf[64];
+ uint32_t bytes_read;
+ int rc;
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/file0.txt", "0", 1);
+
+ rc = fs_open("/file0.txt", FS_ACCESS_READ | FS_ACCESS_WRITE, &file0);
+ TEST_ASSERT(rc == 0);
+ nfs_file = (struct nffs_file *)file0;
+ TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 2);
+
+ rc = fs_unlink("/file0.txt");
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 1);
+
+ rc = fs_open("/file0.txt", FS_ACCESS_READ, &file2);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ rc = fs_write(file0, "00", 2);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_seek(file0, 0);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_read(file0, sizeof buf, buf, &bytes_read);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(bytes_read == 2);
+ TEST_ASSERT(memcmp(buf, "00", 2) == 0);
+
+ rc = fs_close(file0);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_open("/file0.txt", FS_ACCESS_READ, &file0);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ /* Nested unlink. */
+ rc = fs_mkdir("/mydir");
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_create_file("/mydir/file1.txt", "1", 2);
+
+ rc = fs_open("/mydir/file1.txt", FS_ACCESS_READ | FS_ACCESS_WRITE,
+ &file1);
+ TEST_ASSERT(rc == 0);
+ nfs_file = (struct nffs_file *)file1;
+ TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 2);
+
+ rc = fs_unlink("/mydir");
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 1);
+
+ rc = fs_open("/mydir/file1.txt", FS_ACCESS_READ, &file2);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ rc = fs_write(file1, "11", 2);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_seek(file1, 0);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_read(file1, sizeof buf, buf, &bytes_read);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(bytes_read == 2);
+ TEST_ASSERT(memcmp(buf, "11", 2) == 0);
+
+ rc = fs_close(file1);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_open("/mydir/file1.txt", FS_ACCESS_READ, &file1);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_rename)
+{
+ struct fs_file *file;
+ const char contents[] = "contents";
+ int rc;
+
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_rename("/nonexistent.txt", "/newname.txt");
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ /*** Rename file. */
+ nffs_test_util_create_file("/myfile.txt", contents, sizeof contents);
+
+ rc = fs_rename("/myfile.txt", "badname");
+ TEST_ASSERT(rc == FS_EINVAL);
+
+ rc = fs_rename("/myfile.txt", "/myfile2.txt");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_open("/myfile.txt", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ nffs_test_util_assert_contents("/myfile2.txt", contents, sizeof contents);
+
+ rc = fs_mkdir("/mydir");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_rename("/myfile2.txt", "/mydir/myfile2.txt");
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/mydir/myfile2.txt", contents,
+ sizeof contents);
+
+ /*** Rename directory. */
+ rc = fs_rename("/mydir", "badname");
+ TEST_ASSERT(rc == FS_EINVAL);
+
+ rc = fs_rename("/mydir", "/mydir2");
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/mydir2/myfile2.txt", contents,
+ sizeof contents);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "mydir2",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile2.txt",
+ .contents = "contents",
+ .contents_len = 9,
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_truncate)
+{
+ struct fs_file *file;
+ int rc;
+
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 0);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "abcdefgh", 8);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 8);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "abcdefgh", 8);
+
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 0);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "1234", 4);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 4);
+ TEST_ASSERT(fs_getpos(file) == 4);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "1234", 4);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = "1234",
+ .contents_len = 4,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_append)
+{
+ struct fs_file *file;
+ int rc;
+
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_APPEND, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 0);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "abcdefgh", 8);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 8);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "abcdefgh", 8);
+
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_APPEND, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 8);
+
+ /* File position should always be at the end of a file after an append.
+ * Seek to the middle prior to writing to test this.
+ */
+ rc = fs_seek(file, 2);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 2);
+
+ rc = fs_write(file, "ijklmnop", 8);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 16);
+ rc = fs_write(file, "qrstuvwx", 8);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 24);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt",
+ "abcdefghijklmnopqrstuvwx", 24);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = "abcdefghijklmnopqrstuvwx",
+ .contents_len = 24,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_read)
+{
+ struct fs_file *file;
+ uint8_t buf[16];
+ uint32_t bytes_read;
+ int rc;
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/myfile.txt", "1234567890", 10);
+
+ rc = fs_open("/myfile.txt", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 10);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_read(file, 4, buf, &bytes_read);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(bytes_read == 4);
+ TEST_ASSERT(memcmp(buf, "1234", 4) == 0);
+ TEST_ASSERT(fs_getpos(file) == 4);
+
+ rc = fs_read(file, sizeof buf - 4, buf + 4, &bytes_read);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(bytes_read == 6);
+ TEST_ASSERT(memcmp(buf, "1234567890", 10) == 0);
+ TEST_ASSERT(fs_getpos(file) == 10);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+}
+
+TEST_CASE(nffs_test_open)
+{
+ struct fs_file *file;
+ struct fs_dir *dir;
+ int rc;
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /*** Fail to open an invalid path (not rooted). */
+ rc = fs_open("file", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == FS_EINVAL);
+
+ /*** Fail to open a directory (root directory). */
+ rc = fs_open("/", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == FS_EINVAL);
+
+ /*** Fail to open a nonexistent file for reading. */
+ rc = fs_open("/1234", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ /*** Fail to open a child of a nonexistent directory. */
+ rc = fs_open("/dir/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == FS_ENOENT);
+ rc = fs_opendir("/dir", &dir);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ rc = fs_mkdir("/dir");
+ TEST_ASSERT(rc == 0);
+
+ /*** Fail to open a directory. */
+ rc = fs_open("/dir", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == FS_EINVAL);
+
+ /*** Successfully open an existing file for reading. */
+ nffs_test_util_create_file("/dir/file.txt", "1234567890", 10);
+ rc = fs_open("/dir/file.txt", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == 0);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ /*** Successfully open an nonexistent file for writing. */
+ rc = fs_open("/dir/file2.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ /*** Ensure the file can be reopened. */
+ rc = fs_open("/dir/file.txt", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == 0);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+}
+
+TEST_CASE(nffs_test_overwrite_one)
+{
+ struct fs_file *file;
+ int rc;
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_append_file("/myfile.txt", "abcdefgh", 8);
+
+ /*** Overwrite within one block (middle). */
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 3);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 3);
+
+ rc = fs_write(file, "12", 2);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 5);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "abc12fgh", 8);
+ nffs_test_util_assert_block_count("/myfile.txt", 1);
+
+ /*** Overwrite within one block (start). */
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "xy", 2);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 2);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "xyc12fgh", 8);
+ nffs_test_util_assert_block_count("/myfile.txt", 1);
+
+ /*** Overwrite within one block (end). */
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 6);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 6);
+
+ rc = fs_write(file, "<>", 2);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 8);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "xyc12f<>", 8);
+ nffs_test_util_assert_block_count("/myfile.txt", 1);
+
+ /*** Overwrite one block middle, extend. */
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 4);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 8);
+ TEST_ASSERT(fs_getpos(file) == 4);
+
+ rc = fs_write(file, "abcdefgh", 8);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 12);
+ TEST_ASSERT(fs_getpos(file) == 12);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "xyc1abcdefgh", 12);
+ nffs_test_util_assert_block_count("/myfile.txt", 1);
+
+ /*** Overwrite one block start, extend. */
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 12);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "abcdefghijklmnop", 16);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 16);
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents("/myfile.txt", "abcdefghijklmnop", 16);
+ nffs_test_util_assert_block_count("/myfile.txt", 1);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = "abcdefghijklmnop",
+ .contents_len = 16,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_overwrite_two)
+{
+ struct nffs_test_block_desc *blocks = (struct nffs_test_block_desc[]) { {
+ .data = "abcdefgh",
+ .data_len = 8,
+ }, {
+ .data = "ijklmnop",
+ .data_len = 8,
+ } };
+
+ struct fs_file *file;
+ int rc;
+
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /*** Overwrite two blocks (middle). */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 7);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 7);
+
+ rc = fs_write(file, "123", 3);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 10);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt", "abcdefg123klmnop", 16);
+ nffs_test_util_assert_block_count("/myfile.txt", 2);
+
+ /*** Overwrite two blocks (start). */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "ABCDEFGHIJ", 10);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 10);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt", "ABCDEFGHIJklmnop", 16);
+ nffs_test_util_assert_block_count("/myfile.txt", 2);
+
+ /*** Overwrite two blocks (end). */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 6);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 6);
+
+ rc = fs_write(file, "1234567890", 10);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 16);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt", "abcdef1234567890", 16);
+ nffs_test_util_assert_block_count("/myfile.txt", 2);
+
+ /*** Overwrite two blocks middle, extend. */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 6);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 6);
+
+ rc = fs_write(file, "1234567890!@#$", 14);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 20);
+ TEST_ASSERT(fs_getpos(file) == 20);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt", "abcdef1234567890!@#$", 20);
+ nffs_test_util_assert_block_count("/myfile.txt", 2);
+
+ /*** Overwrite two blocks start, extend. */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 16);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "1234567890!@#$%^&*()", 20);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 20);
+ TEST_ASSERT(fs_getpos(file) == 20);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt", "1234567890!@#$%^&*()", 20);
+ nffs_test_util_assert_block_count("/myfile.txt", 2);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = "1234567890!@#$%^&*()",
+ .contents_len = 20,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_overwrite_three)
+{
+ struct nffs_test_block_desc *blocks = (struct nffs_test_block_desc[]) { {
+ .data = "abcdefgh",
+ .data_len = 8,
+ }, {
+ .data = "ijklmnop",
+ .data_len = 8,
+ }, {
+ .data = "qrstuvwx",
+ .data_len = 8,
+ } };
+
+ struct fs_file *file;
+ int rc;
+
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /*** Overwrite three blocks (middle). */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 6);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 6);
+
+ rc = fs_write(file, "1234567890!@", 12);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 18);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt",
+ "abcdef1234567890!@stuvwx", 24);
+ nffs_test_util_assert_block_count("/myfile.txt", 3);
+
+ /*** Overwrite three blocks (start). */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "1234567890!@#$%^&*()", 20);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 20);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt",
+ "1234567890!@#$%^&*()uvwx", 24);
+ nffs_test_util_assert_block_count("/myfile.txt", 3);
+
+ /*** Overwrite three blocks (end). */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 6);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 6);
+
+ rc = fs_write(file, "1234567890!@#$%^&*", 18);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 24);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt",
+ "abcdef1234567890!@#$%^&*", 24);
+ nffs_test_util_assert_block_count("/myfile.txt", 3);
+
+ /*** Overwrite three blocks middle, extend. */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 6);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 6);
+
+ rc = fs_write(file, "1234567890!@#$%^&*()", 20);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 26);
+ TEST_ASSERT(fs_getpos(file) == 26);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt",
+ "abcdef1234567890!@#$%^&*()", 26);
+ nffs_test_util_assert_block_count("/myfile.txt", 3);
+
+ /*** Overwrite three blocks start, extend. */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_write(file, "1234567890!@#$%^&*()abcdefghij", 30);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 30);
+ TEST_ASSERT(fs_getpos(file) == 30);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt",
+ "1234567890!@#$%^&*()abcdefghij", 30);
+ nffs_test_util_assert_block_count("/myfile.txt", 3);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = "1234567890!@#$%^&*()abcdefghij",
+ .contents_len = 30,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_overwrite_many)
+{
+ struct nffs_test_block_desc *blocks = (struct nffs_test_block_desc[]) { {
+ .data = "abcdefgh",
+ .data_len = 8,
+ }, {
+ .data = "ijklmnop",
+ .data_len = 8,
+ }, {
+ .data = "qrstuvwx",
+ .data_len = 8,
+ } };
+
+ struct fs_file *file;
+ int rc;
+
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /*** Overwrite middle of first block. */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 3);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 3);
+
+ rc = fs_write(file, "12", 2);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 5);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt",
+ "abc12fghijklmnopqrstuvwx", 24);
+ nffs_test_util_assert_block_count("/myfile.txt", 3);
+
+ /*** Overwrite end of first block, start of second. */
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
+ rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 0);
+
+ rc = fs_seek(file, 6);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 6);
+
+ rc = fs_write(file, "1234", 4);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_file_len(file, 24);
+ TEST_ASSERT(fs_getpos(file) == 10);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_contents( "/myfile.txt",
+ "abcdef1234klmnopqrstuvwx", 24);
+ nffs_test_util_assert_block_count("/myfile.txt", 3);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = "abcdef1234klmnopqrstuvwx",
+ .contents_len = 24,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_long_filename)
+{
+ int rc;
+
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/12345678901234567890.txt", "contents", 8);
+
+ rc = fs_mkdir("/longdir12345678901234567890");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_rename("/12345678901234567890.txt",
+ "/longdir12345678901234567890/12345678901234567890.txt");
+ TEST_ASSERT(rc == 0);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "longdir12345678901234567890",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "/12345678901234567890.txt",
+ .contents = "contents",
+ .contents_len = 8,
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_large_write)
+{
+ static char data[NFFS_BLOCK_MAX_DATA_SZ_MAX * 5];
+ int rc;
+ int i;
+
+ static const struct nffs_area_desc area_descs_two[] = {
+ { 0x00020000, 128 * 1024 },
+ { 0x00040000, 128 * 1024 },
+ { 0, 0 },
+ };
+
+
+
+ /*** Setup. */
+ rc = nffs_format(area_descs_two);
+ TEST_ASSERT(rc == 0);
+
+ for (i = 0; i < sizeof data; i++) {
+ data[i] = i;
+ }
+
+ nffs_test_util_create_file("/myfile.txt", data, sizeof data);
+
+ /* Ensure large write was split across the appropriate number of data
+ * blocks.
+ */
+ TEST_ASSERT(nffs_test_util_block_count("/myfile.txt") ==
+ sizeof data / NFFS_BLOCK_MAX_DATA_SZ_MAX);
+
+ /* Garbage collect and then ensure the large file is still properly divided
+ * according to max data block size.
+ */
+ nffs_gc(NULL);
+ TEST_ASSERT(nffs_test_util_block_count("/myfile.txt") ==
+ sizeof data / NFFS_BLOCK_MAX_DATA_SZ_MAX);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "myfile.txt",
+ .contents = data,
+ .contents_len = sizeof data,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, area_descs_two);
+}
+
+TEST_CASE(nffs_test_many_children)
+{
+ int rc;
+
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/zasdf", NULL, 0);
+ nffs_test_util_create_file("/FfD", NULL, 0);
+ nffs_test_util_create_file("/4Zvv", NULL, 0);
+ nffs_test_util_create_file("/*(*2fs", NULL, 0);
+ nffs_test_util_create_file("/pzzd", NULL, 0);
+ nffs_test_util_create_file("/zasdf0", NULL, 0);
+ nffs_test_util_create_file("/23132.bin", NULL, 0);
+ nffs_test_util_create_file("/asldkfjaldskfadsfsdf.txt", NULL, 0);
+ nffs_test_util_create_file("/sdgaf", NULL, 0);
+ nffs_test_util_create_file("/939302**", NULL, 0);
+ rc = fs_mkdir("/dir");
+ nffs_test_util_create_file("/dir/itw82", NULL, 0);
+ nffs_test_util_create_file("/dir/124", NULL, 0);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ { "zasdf" },
+ { "FfD" },
+ { "4Zvv" },
+ { "*(*2fs" },
+ { "pzzd" },
+ { "zasdf0" },
+ { "23132.bin" },
+ { "asldkfjaldskfadsfsdf.txt" },
+ { "sdgaf" },
+ { "939302**" },
+ {
+ .filename = "dir",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ { "itw82" },
+ { "124" },
+ { NULL },
+ },
+ },
+ { NULL },
+ }
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_gc)
+{
+ int rc;
+
+ static const struct nffs_area_desc area_descs_two[] = {
+ { 0x00020000, 128 * 1024 },
+ { 0x00040000, 128 * 1024 },
+ { 0, 0 },
+ };
+
+ struct nffs_test_block_desc blocks[8] = { {
+ .data = "1",
+ .data_len = 1,
+ }, {
+ .data = "2",
+ .data_len = 1,
+ }, {
+ .data = "3",
+ .data_len = 1,
+ }, {
+ .data = "4",
+ .data_len = 1,
+ }, {
+ .data = "5",
+ .data_len = 1,
+ }, {
+ .data = "6",
+ .data_len = 1,
+ }, {
+ .data = "7",
+ .data_len = 1,
+ }, {
+ .data = "8",
+ .data_len = 1,
+ } };
+
+
+ rc = nffs_format(area_descs_two);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file_blocks("/myfile.txt", blocks, 8);
+
+ nffs_gc(NULL);
+
+ nffs_test_util_assert_block_count("/myfile.txt", 1);
+}
+
+TEST_CASE(nffs_test_wear_level)
+{
+ int rc;
+ int i;
+ int j;
+
+ static const struct nffs_area_desc area_descs_uniform[] = {
+ { 0x00000000, 2 * 1024 },
+ { 0x00020000, 2 * 1024 },
+ { 0x00040000, 2 * 1024 },
+ { 0x00060000, 2 * 1024 },
+ { 0x00080000, 2 * 1024 },
+ { 0, 0 },
+ };
+
+
+ /*** Setup. */
+ rc = nffs_format(area_descs_uniform);
+ TEST_ASSERT(rc == 0);
+
+ /* Ensure areas rotate properly. */
+ for (i = 0; i < 255; i++) {
+ for (j = 0; j < nffs_num_areas; j++) {
+ nffs_test_assert_area_seqs(i, nffs_num_areas - j, i + 1, j);
+ nffs_gc(NULL);
+ }
+ }
+
+ /* Ensure proper rollover of sequence numbers. */
+ for (j = 0; j < nffs_num_areas; j++) {
+ nffs_test_assert_area_seqs(255, nffs_num_areas - j, 0, j);
+ nffs_gc(NULL);
+ }
+ for (j = 0; j < nffs_num_areas; j++) {
+ nffs_test_assert_area_seqs(0, nffs_num_areas - j, 1, j);
+ nffs_gc(NULL);
+ }
+}
+
+TEST_CASE(nffs_test_corrupt_scratch)
+{
+ int non_scratch_id;
+ int scratch_id;
+ int rc;
+
+ static const struct nffs_area_desc area_descs_two[] = {
+ { 0x00020000, 128 * 1024 },
+ { 0x00040000, 128 * 1024 },
+ { 0, 0 },
+ };
+
+
+ /*** Setup. */
+ rc = nffs_format(area_descs_two);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/myfile.txt", "contents", 8);
+
+ /* Copy the current contents of the non-scratch area to the scratch area.
+ * This will make the scratch area look like it only partially participated
+ * in a garbage collection cycle.
+ */
+ scratch_id = nffs_scratch_area_idx;
+ non_scratch_id = scratch_id ^ 1;
+ nffs_test_copy_area(area_descs_two + non_scratch_id,
+ area_descs_two + nffs_scratch_area_idx);
+
+ /* Add some more data to the non-scratch area. */
+ rc = fs_mkdir("/mydir");
+ TEST_ASSERT(rc == 0);
+
+ /* Ensure the file system is successfully detected and valid, despite
+ * corruption.
+ */
+
+ rc = nffs_misc_reset();
+ TEST_ASSERT(rc == 0);
+
+ rc = nffs_detect(area_descs_two);
+ TEST_ASSERT(rc == 0);
+
+ TEST_ASSERT(nffs_scratch_area_idx == scratch_id);
+
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "mydir",
+ .is_dir = 1,
+ }, {
+ .filename = "myfile.txt",
+ .contents = "contents",
+ .contents_len = 8,
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, area_descs_two);
+}
+
+TEST_CASE(nffs_test_incomplete_block)
+{
+ struct nffs_block block;
+ struct fs_file *fs_file;
+ struct nffs_file *file;
+ uint32_t flash_offset;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/mydir");
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/mydir/a", "aaaa", 4);
+ nffs_test_util_create_file("/mydir/b", "bbbb", 4);
+ nffs_test_util_create_file("/mydir/c", "cccc", 4);
+
+ /* Add a second block to the 'b' file. */
+ nffs_test_util_append_file("/mydir/b", "1234", 4);
+
+ /* Corrupt the 'b' file; make it look like the second block only got half
+ * written.
+ */
+ rc = fs_open("/mydir/b", FS_ACCESS_READ, &fs_file);
+ TEST_ASSERT(rc == 0);
+ file = (struct nffs_file *)fs_file;
+
+ rc = nffs_block_from_hash_entry(&block,
+ file->nf_inode_entry->nie_last_block_entry);
+ TEST_ASSERT(rc == 0);
+
+ nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc, &area_idx,
+ &area_offset);
+ flash_offset = nffs_areas[area_idx].na_offset + area_offset;
+ rc = flash_native_memset(
+ flash_offset + sizeof (struct nffs_disk_block) + 2, 0xff, 2);
+ TEST_ASSERT(rc == 0);
+
+ rc = nffs_misc_reset();
+ TEST_ASSERT(rc == 0);
+ rc = nffs_detect(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /* The entire second block should be removed; the file should only contain
+ * the first block.
+ */
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "mydir",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "a",
+ .contents = "aaaa",
+ .contents_len = 4,
+ }, {
+ .filename = "b",
+ .contents = "bbbb",
+ .contents_len = 4,
+ }, {
+ .filename = "c",
+ .contents = "cccc",
+ .contents_len = 4,
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_corrupt_block)
+{
+ struct nffs_block block;
+ struct fs_file *fs_file;
+ struct nffs_file *file;
+ uint32_t flash_offset;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/mydir");
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/mydir/a", "aaaa", 4);
+ nffs_test_util_create_file("/mydir/b", "bbbb", 4);
+ nffs_test_util_create_file("/mydir/c", "cccc", 4);
+
+ /* Add a second block to the 'b' file. */
+ nffs_test_util_append_file("/mydir/b", "1234", 4);
+
+ /* Corrupt the 'b' file; overwrite the second block's magic number. */
+ rc = fs_open("/mydir/b", FS_ACCESS_READ, &fs_file);
+ TEST_ASSERT(rc == 0);
+ file = (struct nffs_file *)fs_file;
+
+ rc = nffs_block_from_hash_entry(&block,
+ file->nf_inode_entry->nie_last_block_entry);
+ TEST_ASSERT(rc == 0);
+
+ nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc, &area_idx,
+ &area_offset);
+ flash_offset = nffs_areas[area_idx].na_offset + area_offset;
+ rc = flash_native_memset(flash_offset, 0x43, 4);
+ TEST_ASSERT(rc == 0);
+
+ /* Write a fourth file. This file should get restored even though the
+ * previous object has an invalid magic number.
+ */
+ nffs_test_util_create_file("/mydir/d", "dddd", 4);
+
+ rc = nffs_misc_reset();
+ TEST_ASSERT(rc == 0);
+ rc = nffs_detect(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /* The entire second block should be removed; the file should only contain
+ * the first block.
+ */
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "mydir",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "a",
+ .contents = "aaaa",
+ .contents_len = 4,
+ }, {
+ .filename = "b",
+ .contents = "bbbb",
+ .contents_len = 4,
+ }, {
+ .filename = "c",
+ .contents = "cccc",
+ .contents_len = 4,
+ }, {
+ .filename = "d",
+ .contents = "dddd",
+ .contents_len = 4,
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_large_unlink)
+{
+ static char file_contents[1024 * 4];
+ char filename[256];
+ int rc;
+ int i;
+ int j;
+ int k;
+
+
+ /*** Setup. */
+ nffs_config.nc_num_inodes = 1024;
+ nffs_config.nc_num_blocks = 1024;
+
+ rc = nffs_init();
+ TEST_ASSERT(rc == 0);
+
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ for (i = 0; i < 5; i++) {
+ snprintf(filename, sizeof filename, "/dir0_%d", i);
+ rc = fs_mkdir(filename);
+ TEST_ASSERT(rc == 0);
+
+ for (j = 0; j < 5; j++) {
+ snprintf(filename, sizeof filename, "/dir0_%d/dir1_%d", i, j);
+ rc = fs_mkdir(filename);
+ TEST_ASSERT(rc == 0);
+
+ for (k = 0; k < 5; k++) {
+ snprintf(filename, sizeof filename,
+ "/dir0_%d/dir1_%d/file2_%d", i, j, k);
+ nffs_test_util_create_file(filename, file_contents,
+ sizeof file_contents);
+ }
+ }
+
+ for (j = 0; j < 15; j++) {
+ snprintf(filename, sizeof filename, "/dir0_%d/file1_%d", i, j);
+ nffs_test_util_create_file(filename, file_contents,
+ sizeof file_contents);
+ }
+ }
+
+ for (i = 0; i < 5; i++) {
+ snprintf(filename, sizeof filename, "/dir0_%d", i);
+ rc = fs_unlink(filename);
+ TEST_ASSERT(rc == 0);
+ }
+
+ /* The entire file system should be empty. */
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_large_system)
+{
+ int rc;
+
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_create_tree(nffs_test_system_01);
+
+ nffs_test_assert_system(nffs_test_system_01, nffs_area_descs);
+
+ rc = fs_unlink("/lvl1dir-0000");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_unlink("/lvl1dir-0004");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/lvl1dir-0000");
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_assert_system(nffs_test_system_01_rm_1014_mk10, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_lost_found)
+{
+ char buf[32];
+ struct nffs_inode_entry *inode_entry;
+ uint32_t flash_offset;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_mkdir("/mydir");
+ TEST_ASSERT(rc == 0);
+ rc = fs_mkdir("/mydir/dir1");
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/mydir/file1", "aaaa", 4);
+ nffs_test_util_create_file("/mydir/dir1/file2", "bbbb", 4);
+
+ /* Corrupt the mydir inode. */
+ rc = nffs_path_find_inode_entry("/mydir", &inode_entry);
+ TEST_ASSERT(rc == 0);
+
+ snprintf(buf, sizeof buf, "%lu",
+ (unsigned long)inode_entry->nie_hash_entry.nhe_id);
+
+ nffs_flash_loc_expand(inode_entry->nie_hash_entry.nhe_flash_loc,
+ &area_idx, &area_offset);
+ flash_offset = nffs_areas[area_idx].na_offset + area_offset;
+ rc = flash_native_memset(flash_offset + 10, 0xff, 1);
+ TEST_ASSERT(rc == 0);
+
+ /* Clear cached data and restore from flash (i.e, simulate a reboot). */
+ rc = nffs_misc_reset();
+ TEST_ASSERT(rc == 0);
+ rc = nffs_detect(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ /* All contents should now be in the lost+found dir. */
+ struct nffs_test_file_desc *expected_system =
+ (struct nffs_test_file_desc[]) { {
+ .filename = "",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "lost+found",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = buf,
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "file1",
+ .contents = "aaaa",
+ .contents_len = 4,
+ }, {
+ .filename = "dir1",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) { {
+ .filename = "file2",
+ .contents = "bbbb",
+ .contents_len = 4,
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } },
+ }, {
+ .filename = NULL,
+ } }
+ } };
+
+ nffs_test_assert_system(expected_system, nffs_area_descs);
+}
+
+TEST_CASE(nffs_test_cache_large_file)
+{
+ static char data[NFFS_BLOCK_MAX_DATA_SZ_MAX * 5];
+ struct fs_file *file;
+ uint8_t b;
+ int rc;
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_create_file("/myfile.txt", data, sizeof data);
+ nffs_cache_clear();
+
+ /* Opening a file should not cause any blocks to get cached. */
+ rc = fs_open("/myfile.txt", FS_ACCESS_READ, &file);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_cache_range("/myfile.txt", 0, 0);
+
+ /* Cache first block. */
+ rc = fs_seek(file, nffs_block_max_data_sz * 0);
+ TEST_ASSERT(rc == 0);
+ rc = fs_read(file, 1, &b, NULL);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_cache_range("/myfile.txt",
+ nffs_block_max_data_sz * 0,
+ nffs_block_max_data_sz * 1);
+
+ /* Cache second block. */
+ rc = fs_seek(file, nffs_block_max_data_sz * 1);
+ TEST_ASSERT(rc == 0);
+ rc = fs_read(file, 1, &b, NULL);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_cache_range("/myfile.txt",
+ nffs_block_max_data_sz * 0,
+ nffs_block_max_data_sz * 2);
+
+
+ /* Cache fourth block; prior cache should get erased. */
+ rc = fs_seek(file, nffs_block_max_data_sz * 3);
+ TEST_ASSERT(rc == 0);
+ rc = fs_read(file, 1, &b, NULL);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_cache_range("/myfile.txt",
+ nffs_block_max_data_sz * 3,
+ nffs_block_max_data_sz * 4);
+
+ /* Cache second and third blocks. */
+ rc = fs_seek(file, nffs_block_max_data_sz * 1);
+ TEST_ASSERT(rc == 0);
+ rc = fs_read(file, 1, &b, NULL);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_cache_range("/myfile.txt",
+ nffs_block_max_data_sz * 1,
+ nffs_block_max_data_sz * 4);
+
+ /* Cache fifth block. */
+ rc = fs_seek(file, nffs_block_max_data_sz * 4);
+ TEST_ASSERT(rc == 0);
+ rc = fs_read(file, 1, &b, NULL);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_cache_range("/myfile.txt",
+ nffs_block_max_data_sz * 1,
+ nffs_block_max_data_sz * 5);
+
+ rc = fs_close(file);
+ TEST_ASSERT(rc == 0);
+}
+
+TEST_CASE(nffs_test_readdir)
+{
+ struct fs_dirent *dirent;
+ struct fs_dir *dir;
+ int rc;
+
+ /*** Setup. */
+ rc = nffs_format(nffs_area_descs);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ rc = fs_mkdir("/mydir");
+ TEST_ASSERT_FATAL(rc == 0);
+
+ nffs_test_util_create_file("/mydir/b", "bbbb", 4);
+ nffs_test_util_create_file("/mydir/a", "aaaa", 4);
+ rc = fs_mkdir("/mydir/c");
+ TEST_ASSERT_FATAL(rc == 0);
+
+ /* Nonexistent directory. */
+ rc = fs_opendir("/asdf", &dir);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ /* Fail to opendir a file. */
+ rc = fs_opendir("/mydir/a", &dir);
+ TEST_ASSERT(rc == FS_EINVAL);
+
+ /* Real directory (with trailing slash). */
+ rc = fs_opendir("/mydir/", &dir);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_ent_name(dirent, "a");
+ TEST_ASSERT(fs_dirent_is_dir(dirent) == 0);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_ent_name(dirent, "b");
+ TEST_ASSERT(fs_dirent_is_dir(dirent) == 0);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_ent_name(dirent, "c");
+ TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ rc = fs_closedir(dir);
+ TEST_ASSERT(rc == 0);
+
+ /* Root directory. */
+ rc = fs_opendir("/", &dir);
+ TEST_ASSERT(rc == 0);
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_ent_name(dirent, "lost+found");
+ TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == 0);
+ nffs_test_util_assert_ent_name(dirent, "mydir");
+ TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
+
+ rc = fs_closedir(dir);
+ TEST_ASSERT(rc == 0);
+
+ /* Delete entries while iterating. */
+ rc = fs_opendir("/mydir", &dir);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_ent_name(dirent, "a");
+ TEST_ASSERT(fs_dirent_is_dir(dirent) == 0);
+
+ rc = fs_unlink("/mydir/b");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_unlink("/mydir/c");
+ TEST_ASSERT(rc == 0);
+
+ rc = fs_unlink("/mydir");
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_util_assert_ent_name(dirent, "c");
+ TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
+
+ rc = fs_readdir(dir, &dirent);
+ TEST_ASSERT(rc == FS_ENOENT);
+
+ rc = fs_closedir(dir);
+ TEST_ASSERT(rc == 0);
+
+ /* Ensure directory is gone. */
+ rc = fs_opendir("/mydir", &dir);
+ TEST_ASSERT(rc == FS_ENOENT);
+}
+
+TEST_SUITE(nffs_suite_cache)
+{
+ int rc;
+
+ memset(&nffs_config, 0, sizeof nffs_config);
+ nffs_config.nc_num_cache_inodes = 4;
+ nffs_config.nc_num_cache_blocks = 64;
+
+ rc = nffs_init();
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_cache_large_file();
+}
+
+static void
+nffs_test_gen(void)
+{
+ int rc;
+
+ rc = nffs_init();
+ TEST_ASSERT(rc == 0);
+
+ nffs_test_unlink();
+ nffs_test_mkdir();
+ nffs_test_rename();
+ nffs_test_truncate();
+ nffs_test_append();
+ nffs_test_read();
+ nffs_test_open();
+ nffs_test_overwrite_one();
+ nffs_test_overwrite_two();
+ nffs_test_overwrite_three();
+ nffs_test_overwrite_many();
+ nffs_test_long_filename();
+ nffs_test_large_write();
+ nffs_test_many_children();
+ nffs_test_gc();
+ nffs_test_wear_level();
+ nffs_test_corrupt_scratch();
+ nffs_test_incomplete_block();
+ nffs_test_corrupt_block();
+ nffs_test_large_unlink();
+ nffs_test_large_system();
+ nffs_test_lost_found();
+ nffs_test_readdir();
+}
+
+TEST_SUITE(gen_1_1)
+{
+ nffs_config.nc_num_cache_inodes = 1;
+ nffs_config.nc_num_cache_blocks = 1;
+ nffs_test_gen();
+}
+
+TEST_SUITE(gen_4_32)
+{
+ nffs_config.nc_num_cache_inodes = 4;
+ nffs_config.nc_num_cache_blocks = 32;
+ nffs_test_gen();
+}
+
+TEST_SUITE(gen_32_1024)
+{
+ nffs_config.nc_num_cache_inodes = 32;
+ nffs_config.nc_num_cache_blocks = 1024;
+ nffs_test_gen();
+}
+
+int
+nffs_test_all(void)
+{
+ gen_1_1();
+ gen_4_32();
+ gen_32_1024();
+ nffs_suite_cache();
+
+ return tu_any_failed;
+}
+
+#ifdef PKG_TEST
+
+int
+main(void)
+{
+ tu_config.tc_print_results = 1;
+ tu_init();
+
+ nffs_test_all();
+
+ return tu_any_failed;
+}
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/test/arch/sim/nffs_test_priv.h
----------------------------------------------------------------------
diff --git a/fs/nffs/src/test/arch/sim/nffs_test_priv.h b/fs/nffs/src/test/arch/sim/nffs_test_priv.h
new file mode 100644
index 0000000..12736ad
--- /dev/null
+++ b/fs/nffs/src/test/arch/sim/nffs_test_priv.h
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 H_NFFS_TEST_PRIV_
+#define H_NFFS_TEST_PRIV_
+
+struct nffs_test_block_desc {
+ const char *data;
+ int data_len;
+};
+
+struct nffs_test_file_desc {
+ const char *filename;
+ int is_dir;
+ const char *contents;
+ int contents_len;
+ struct nffs_test_file_desc *children;
+};
+
+int nffs_test(void);
+
+extern const struct nffs_test_file_desc *nffs_test_system_01;
+extern const struct nffs_test_file_desc *nffs_test_system_01_rm_1014_mk10;
+
+#endif
+
[07/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/hw/hal/egg.yml
----------------------------------------------------------------------
diff --git a/hw/hal/egg.yml b/hw/hal/egg.yml
index d8de62b..7c4971b 100644
--- a/hw/hal/egg.yml
+++ b/hw/hal/egg.yml
@@ -2,5 +2,5 @@ egg.name: hw/hal
egg.vers: 0.1
egg.deps: libs/os
egg.deps.NFFS:
- - libs/nffs
+ - fs/nffs
egg.cflags.NFFS: -DNFFS_PRESENT
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/bootutil/egg.yml
----------------------------------------------------------------------
diff --git a/libs/bootutil/egg.yml b/libs/bootutil/egg.yml
index 4139773..4946830 100644
--- a/libs/bootutil/egg.yml
+++ b/libs/bootutil/egg.yml
@@ -1,7 +1,7 @@
egg.name: libs/bootutil
egg.vers: 0.1
egg.deps:
+ - fs/nffs
- libs/os
- - libs/nffs
- libs/testutil
- hw/hal
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/elua/elua_base/egg.yml
----------------------------------------------------------------------
diff --git a/libs/elua/elua_base/egg.yml b/libs/elua/elua_base/egg.yml
index 4af3263..79b17b2 100644
--- a/libs/elua/elua_base/egg.yml
+++ b/libs/elua/elua_base/egg.yml
@@ -4,5 +4,5 @@ egg.cflags: -DLUA_OPTIMIZE_MEMORY=2 -DLUA_CROSS_COMPILER -DLUA_USE_MKSTEMP -DLUA
egg.req_caps:
- console
egg.deps:
- - libs/fs
+ - fs/fs
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/egg.yml
----------------------------------------------------------------------
diff --git a/libs/fs/egg.yml b/libs/fs/egg.yml
deleted file mode 100644
index ea8f21f..0000000
--- a/libs/fs/egg.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-egg.name: libs/fs
-egg.deps.SHELL:
- - libs/shell
- - libs/console/full
-egg.cflags.SHELL: -DSHELL_PRESENT
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/include/fs/fs.h
----------------------------------------------------------------------
diff --git a/libs/fs/include/fs/fs.h b/libs/fs/include/fs/fs.h
deleted file mode 100644
index 872b863..0000000
--- a/libs/fs/include/fs/fs.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 __FS_H__
-#define __FS_H__
-
-#include <stddef.h>
-#include <inttypes.h>
-
-/*
- * Common interface to access files.
- */
-struct fs_file;
-struct fs_dir;
-struct fs_dirent;
-
-int fs_open(const char *filename, uint8_t access_flags, struct fs_file **);
-int fs_close(struct fs_file *);
-int fs_read(struct fs_file *, uint32_t len, void *out_data, uint32_t *out_len);
-int fs_write(struct fs_file *, const void *data, int len);
-int fs_seek(struct fs_file *, uint32_t offset);
-uint32_t fs_getpos(const struct fs_file *);
-int fs_filelen(const struct fs_file *, uint32_t *out_len);
-
-int fs_unlink(const char *filename);
-int fs_rename(const char *from, const char *to);
-int fs_mkdir(const char *path);
-
-int fs_opendir(const char *path, struct fs_dir **);
-int fs_readdir(struct fs_dir *, struct fs_dirent **);
-int fs_closedir(struct fs_dir *);
-int fs_dirent_name(const struct fs_dirent *, size_t max_len,
- char *out_name, uint8_t *out_name_len);
-int fs_dirent_is_dir(const struct fs_dirent *);
-
-/*
- * File access flags.
- */
-#define FS_ACCESS_READ 0x01
-#define FS_ACCESS_WRITE 0x02
-#define FS_ACCESS_APPEND 0x04
-#define FS_ACCESS_TRUNCATE 0x08
-
-/*
- * File access return codes.
- */
-#define FS_EOK 0 /* OK */
-#define FS_ECORRUPT 1 /* Filesystem corrupt */
-#define FS_HW_ERROR 2 /* Error access storage medium */
-#define FS_ERANGE 3
-#define FS_EINVAL 4
-#define FS_ENOMEM 5 /* out of memory */
-#define FS_ENOENT 6 /* no such file */
-#define FS_EEMPTY 7
-#define FS_EFULL 8
-#define FS_EUNEXP 9
-#define FS_EOS 10
-#define FS_EEXIST 11
-#define FS_EACCESS 12
-#define FS_EUNINIT 13
-
-#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/include/fs/fs_if.h
----------------------------------------------------------------------
diff --git a/libs/fs/include/fs/fs_if.h b/libs/fs/include/fs/fs_if.h
deleted file mode 100644
index 9236cc6..0000000
--- a/libs/fs/include/fs/fs_if.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 __FS_IF_H__
-#define __FS_IF_H__
-
-/*
- * Common interface filesystem(s) provide.
- */
-struct fs_ops {
- int (*f_open)(const char *filename, uint8_t access_flags,
- struct fs_file **out_file);
- int (*f_close)(struct fs_file *file);
- int (*f_read)(struct fs_file *file, uint32_t len, void *out_data,
- uint32_t *out_len);
- int (*f_write)(struct fs_file *file, const void *data, int len);
-
- int (*f_seek)(struct fs_file *file, uint32_t offset);
- uint32_t (*f_getpos)(const struct fs_file *file);
- int (*f_filelen)(const struct fs_file *file, uint32_t *out_len);
-
- int (*f_unlink)(const char *filename);
- int (*f_rename)(const char *from, const char *to);
- int (*f_mkdir)(const char *path);
-
- int (*f_opendir)(const char *path, struct fs_dir **out_dir);
- int (*f_readdir)(struct fs_dir *dir, struct fs_dirent **out_dirent);
- int (*f_closedir)(struct fs_dir *dir);
-
- int (*f_dirent_name)(const struct fs_dirent *dirent, size_t max_len,
- char *out_name, uint8_t *out_name_len);
- int (*f_dirent_is_dir)(const struct fs_dirent *dirent);
-
- const char *f_name;
-};
-
-/*
- * Currently allow only one type of FS, starts at root.
- */
-int fs_register(const struct fs_ops *);
-
-#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/include/fs/fsutil.h
----------------------------------------------------------------------
diff --git a/libs/fs/include/fs/fsutil.h b/libs/fs/include/fs/fsutil.h
deleted file mode 100644
index 59bfaf3..0000000
--- a/libs/fs/include/fs/fsutil.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 H_FSUTIL_
-#define H_FSUTIL_
-
-#include <inttypes.h>
-
-int fsutil_read_file(const char *path, uint32_t offset, uint32_t len,
- void *dst, uint32_t *out_len);
-int fsutil_write_file(const char *path, const void *data, uint32_t len);
-
-#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/src/fs_cli.c
----------------------------------------------------------------------
diff --git a/libs/fs/src/fs_cli.c b/libs/fs/src/fs_cli.c
deleted file mode 100644
index e26e003..0000000
--- a/libs/fs/src/fs_cli.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#ifdef SHELL_PRESENT
-
-#include <inttypes.h>
-#include <string.h>
-
-#include <shell/shell.h>
-#include <console/console.h>
-
-#include "fs/fs.h"
-
-static struct shell_cmd fs_ls_struct;
-
-static void
-fs_ls_file(const char *name, struct fs_file *file)
-{
- uint32_t len;
-
- len = 0;
- fs_filelen(file, &len);
- console_printf("\t%6lu %s\n", (unsigned long)len, name);
-}
-
-static void
-fs_ls_dir(const char *name)
-{
- console_printf("\t%6s %s\n", "dir", name);
-}
-
-static int
-fs_ls_cmd(int argc, char **argv)
-{
- int rc, file_cnt = 0;
- char *path;
- struct fs_file *file;
- struct fs_dir *dir;
- struct fs_dirent *dirent;
- char name[64];
- int plen;
- uint8_t namelen;
-
- switch (argc) {
- case 1:
- path = "/";
- break;
- case 2:
- path = argv[1];
- break;
- default:
- console_printf("ls <path>\n");
- return 1;
- }
-
- rc = fs_open(path, FS_ACCESS_READ, &file);
- if (rc == 0) {
- fs_ls_file(path, file);
- fs_close(file);
- file_cnt = 1;
- goto done;
- }
-
- plen = strlen(path);
- strncpy(name, path, sizeof(name) - 2);
- if (name[plen - 1] != '/') {
- name[plen++] = '/';
- name[plen] = '\0';
- }
-
- rc = fs_opendir(path, &dir);
- if (rc == 0) {
- do {
- rc = fs_readdir(dir, &dirent);
- if (rc) {
- break;
- }
- if (fs_dirent_name(dirent, sizeof(name) - plen, &name[plen],
- &namelen)) {
- break;
- }
- rc = fs_open(name, FS_ACCESS_READ, &file);
- if (rc == 0) {
- fs_ls_file(name, file);
- fs_close(file);
- } else {
- fs_ls_dir(name);
- }
- file_cnt++;
- } while (1);
- fs_closedir(dir);
- goto done;
- }
- console_printf("Error listing %s - %d\n", path, rc);
-done:
- console_printf("%d files\n", file_cnt);
- return 0;
-}
-
-void
-fs_cli_init(void)
-{
- int rc;
-
- rc = shell_cmd_register(&fs_ls_struct, "ls", fs_ls_cmd);
- if (rc != 0) {
- return;
- }
-}
-#endif /* SHELL_PRESENT */
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/src/fs_dirent.c
----------------------------------------------------------------------
diff --git a/libs/fs/src/fs_dirent.c b/libs/fs/src/fs_dirent.c
deleted file mode 100644
index e4428de..0000000
--- a/libs/fs/src/fs_dirent.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-#include <fs/fs.h>
-#include <fs/fs_if.h>
-#include "fs_priv.h"
-
-int
-fs_opendir(const char *path, struct fs_dir **out_dir)
-{
- return fs_root_ops->f_opendir(path, out_dir);
-}
-
-int
-fs_readdir(struct fs_dir *dir, struct fs_dirent **out_dirent)
-{
- return fs_root_ops->f_readdir(dir, out_dirent);
-}
-
-int
-fs_closedir(struct fs_dir *dir)
-{
- return fs_root_ops->f_closedir(dir);
-}
-
-int
-fs_dirent_name(const struct fs_dirent *dirent, size_t max_len,
- char *out_name, uint8_t *out_name_len)
-{
- return fs_root_ops->f_dirent_name(dirent, max_len, out_name, out_name_len);
-}
-
-int
-fs_dirent_is_dir(const struct fs_dirent *dirent)
-{
- return fs_root_ops->f_dirent_is_dir(dirent);
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/src/fs_file.c
----------------------------------------------------------------------
diff --git a/libs/fs/src/fs_file.c b/libs/fs/src/fs_file.c
deleted file mode 100644
index ec2697b..0000000
--- a/libs/fs/src/fs_file.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-#include <fs/fs.h>
-#include <fs/fs_if.h>
-
-#include "fs_priv.h"
-
-int
-fs_open(const char *filename, uint8_t access_flags, struct fs_file **out_file)
-{
- return fs_root_ops->f_open(filename, access_flags, out_file);
-}
-
-int
-fs_close(struct fs_file *file)
-{
- return fs_root_ops->f_close(file);
-}
-
-int
-fs_read(struct fs_file *file, uint32_t len, void *out_data, uint32_t *out_len)
-{
- return fs_root_ops->f_read(file, len, out_data, out_len);
-}
-
-int
-fs_write(struct fs_file *file, const void *data, int len)
-{
- return fs_root_ops->f_write(file, data, len);
-}
-
-int
-fs_seek(struct fs_file *file, uint32_t offset)
-{
- return fs_root_ops->f_seek(file, offset);
-}
-
-uint32_t
-fs_getpos(const struct fs_file *file)
-{
- return fs_root_ops->f_getpos(file);
-}
-
-int
-fs_filelen(const struct fs_file *file, uint32_t *out_len)
-{
- return fs_root_ops->f_filelen(file, out_len);
-}
-
-int
-fs_unlink(const char *filename)
-{
- return fs_root_ops->f_unlink(filename);
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/src/fs_mkdir.c
----------------------------------------------------------------------
diff --git a/libs/fs/src/fs_mkdir.c b/libs/fs/src/fs_mkdir.c
deleted file mode 100644
index 4be7a0a..0000000
--- a/libs/fs/src/fs_mkdir.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-#include <fs/fs.h>
-#include <fs/fs_if.h>
-
-#include "fs_priv.h"
-
-int
-fs_rename(const char *from, const char *to)
-{
- return fs_root_ops->f_rename(from, to);
-}
-
-int
-fs_mkdir(const char *path)
-{
- return fs_root_ops->f_mkdir(path);
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/src/fs_mount.c
----------------------------------------------------------------------
diff --git a/libs/fs/src/fs_mount.c b/libs/fs/src/fs_mount.c
deleted file mode 100644
index e452cf2..0000000
--- a/libs/fs/src/fs_mount.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-#include <fs/fs.h>
-#include <fs/fs_if.h>
-#include "fs_priv.h"
-
-const struct fs_ops *fs_root_ops;
-
-int
-fs_register(const struct fs_ops *fops)
-{
- if (fs_root_ops) {
- return FS_EEXIST;
- }
- fs_root_ops = fops;
-
-#ifdef SHELL_PRESENT
- fs_cli_init();
-#endif
-
- return FS_EOK;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/src/fs_priv.h
----------------------------------------------------------------------
diff --git a/libs/fs/src/fs_priv.h b/libs/fs/src/fs_priv.h
deleted file mode 100644
index c3d2ba6..0000000
--- a/libs/fs/src/fs_priv.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 __FS_PRIV_H__
-#define __FS_PRIV_H__
-
-struct fs_ops;
-extern const struct fs_ops *fs_root_ops;
-
-#ifdef SHELL_PRESENT
-void fs_cli_init(void);
-#endif /* SHELL_PRESENT */
-
-#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/fs/src/fsutil.c
----------------------------------------------------------------------
diff --git a/libs/fs/src/fsutil.c b/libs/fs/src/fsutil.c
deleted file mode 100644
index 97fa84a..0000000
--- a/libs/fs/src/fsutil.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include "fs/fs.h"
-
-int
-fsutil_read_file(const char *path, uint32_t offset, uint32_t len, void *dst,
- uint32_t *out_len)
-{
- struct fs_file *file;
- int rc;
-
- rc = fs_open(path, FS_ACCESS_READ, &file);
- if (rc != 0) {
- goto done;
- }
-
- rc = fs_read(file, len, dst, out_len);
- if (rc != 0) {
- goto done;
- }
-
- rc = 0;
-
-done:
- fs_close(file);
- return rc;
-}
-
-int
-fsutil_write_file(const char *path, const void *data, uint32_t len)
-{
- struct fs_file *file;
- int rc;
-
- rc = fs_open(path, FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
- if (rc != 0) {
- goto done;
- }
-
- rc = fs_write(file, data, len);
- if (rc != 0) {
- goto done;
- }
-
- rc = 0;
-
-done:
- fs_close(file);
- return rc;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/imgmgr/egg.yml
----------------------------------------------------------------------
diff --git a/libs/imgmgr/egg.yml b/libs/imgmgr/egg.yml
index 127cc4f..59f9b39 100644
--- a/libs/imgmgr/egg.yml
+++ b/libs/imgmgr/egg.yml
@@ -1,6 +1,6 @@
egg.name: libs/imgmgr
egg.vers: 0.1
egg.deps:
+ - fs/fs
- libs/newtmgr
- libs/bootutil
- - libs/fs
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/LICENSE
----------------------------------------------------------------------
diff --git a/libs/nffs/LICENSE b/libs/nffs/LICENSE
deleted file mode 100644
index 8f71f43..0000000
--- a/libs/nffs/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- 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.
-
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/README.md
----------------------------------------------------------------------
diff --git a/libs/nffs/README.md b/libs/nffs/README.md
deleted file mode 100644
index 67071c5..0000000
--- a/libs/nffs/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# ffs
-
-
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/design.txt
----------------------------------------------------------------------
diff --git a/libs/nffs/design.txt b/libs/nffs/design.txt
deleted file mode 100644
index 76dce2c..0000000
--- a/libs/nffs/design.txt
+++ /dev/null
@@ -1,866 +0,0 @@
-***** NFFS
-
-*** HISTORY
-Rev. Date Changes
-6 2015/10/12 Add directory-reading API.
-5 2015/09/28 Rename to Newtron Flash File System.
-4 2015/09/08 Fix some badly-specified CRC behavior; clarification of
- behavior during restore of corrupt disk.
-3 2015/08/20 More cache specifics; updated nffs_read() function
- type.
-2 2015/08/17 Addition of crc16; clarification of sweep phase.
-1 2015/08/13
-
-
-*** SUMMARY
-
-Newtron Flash File System (nffs) is a flash file system with the following
-priorities:
- * Minimal RAM usage
- * Reliability
-
-
-*** DISK STRUCTURE
-
-At the top level, an nffs disk is partitioned into areas. An area is a region
-of disk with the following properties:
- (1) An area can be fully erased without affecting any other areas.
- (2) A write to one area does not restrict writes to other areas.
-
-Clarification of point (2): some flash hardware divides its memory space into
-"blocks." Writes within a block must be sequential, but writes to one block
-have no effect on what parts of other blocks can be written. Thus, for flash
-hardware with such a restriction, each area must comprise a discrete number of
-blocks.
-
-While not strictly necessary, it is recommended that all areas have the same
-size.
-
-On disk, each area is prefixed with the following header:
-
-/** On-disk representation of an area header. */
-struct nffs_disk_area {
- uint32_t nda_magic[4]; /* NFFS_AREA_MAGIC{0,1,2,3} */
- uint32_t nda_length; /* Total size of area, in bytes. */
- uint8_t nda_ver; /* Current nffs version: 0 */
- uint8_t nda_gc_seq; /* Garbage collection count. */
- uint8_t reserved8;
- uint8_t nda_id; /* 0xff if scratch area. */
-};
-
-Beyond its header, an area contains a sequence of disk objects, representing
-the contents of the file system. There are two types of objects: inodes and
-data blocks. An inode represents a file or directory; a data block represents
-part of a file's contents.
-
-/** On-disk representation of an inode (file or directory). */
-struct nffs_disk_inode {
- uint32_t ndi_magic; /* NFFS_INODE_MAGIC */
- uint32_t ndi_id; /* Unique object ID. */
- uint32_t ndi_seq; /* Sequence number; greater supersedes
- lesser. */
- uint32_t ndi_parent_id; /* Object ID of parent directory inode. */
- uint8_t reserved8;
- uint8_t ndi_filename_len; /* Length of filename, in bytes. */
- uint16_t ndi_crc16; /* Covers rest of header and filename. */
- /* Followed by filename. */
-};
-
-An inode filename's length cannot exceed 256 bytes. The filename is not
-null-terminated. The following ASCII characters are not allowed in a
-filename:
- * / (slash character)
- * \0 (NUL character)
-
-/** On-disk representation of a data block. */
-struct nffs_disk_block {
- uint32_t ndb_magic; /* NFFS_BLOCK_MAGIC */
- uint32_t ndb_id; /* Unique object ID. */
- uint32_t ndb_seq; /* Sequence number; greater supersedes lesser. */
- uint32_t ndb_inode_id; /* Object ID of owning inode. */
- uint32_t ndb_prev_id; /* Object ID of previous block in file;
- NFFS_ID_NONE if this is the first block. */
- uint16_t ndb_data_len; /* Length of data contents, in bytes. */
- uint16_t ndb_crc16; /* Covers rest of header and data. */
- /* Followed by 'ndb_data_len' bytes of data. */
-};
-
-Each data block contains the ID of the previous data block in the file.
-Together, the set of blocks in a file form a reverse singly-linked list.
-
-The maximum number of data bytes that a block can contain is determined at
-initialization-time. The result is the greatest number which satisfies all of
-the following restrictions:
- o No more than 2048.
- o At least two maximum-sized blocks can fit in the smallest area.
-
-The 2048 number was chosen somewhat arbitrarily, and may change in the future.
-
-
-*** ID SPACE
-
-All disk objects have a unique 32-bit ID. The ID space is partitioned as
-follows:
- * 0x00000000 - 0x0fffffff: Directory inodes.
- * 0x10000000 - 0x7fffffff: File inodes.
- * 0x80000000 - 0xfffffffe: Data blocks.
- * 0xffffffff : Reserved (NFFS_ID_NONE)
-
-
-*** SCRATCH AREA
-
-A valid nffs file system must contain a single "scratch area." The scratch
-area does not contain any objects of its own, and is only used during garbage
-collection. The scratch area must have a size greater than or equal to each
-of the other areas in flash.
-
-
-*** RAM REPRESENTATION
-
-The file system comprises a set of objects of the following two types:
- 1) inode
- 2) data block
-
-Every object in the file system is stored in a 256-entry hash table. An
-object's hash key is derived from its 32-bit ID. Each list in the hash table
-is sorted by time of use; most-recently-used is at the front of the list. All
-objects are represented by the following structure:
-
-/**
- * What gets stored in the hash table. Each entry represents a data block or
- * an inode.
- */
-struct nffs_hash_entry {
- SLIST_ENTRY(nffs_hash_entry) nhe_next;
- uint32_t nhe_id; /* 0 - 0x7fffffff if inode; else if block. */
- uint32_t nhe_flash_loc; /* Upper-byte = area idx; rest = area offset. */
-};
-
-For each data block, the above structure is all that is stored in RAM. To
-acquire more information about a data block, the block header must be read
-from flash.
-
-Inodes require a fuller RAM representation to capture the structure of the
-file system. There are two types of inodes: files and directories. Each
-inode hash entry is actually an instance of the following structure:
-
-/** Each inode hash entry is actually one of these. */
-struct nffs_inode_entry {
- struct nffs_hash_entry nie_hash_entry;
- SLIST_ENTRY(nffs_inode_entry) nie_sibling_next;
- union {
- struct nffs_inode_list nie_child_list; /* If directory */
- struct nffs_hash_entry *nie_last_block_entry; /* If file */
- };
- uint8_t nie_refcnt;
-};
-
-A directory inode contains a list of its child files and directories
-(fie_child_list). These entries are sorted alphabetically using the ASCII
-character set.
-
-A file inode contains a pointer to the last data block in the file
-(nie_last_block_entry). For most file operations, the reversed block list must
-be walked backwards. This introduces a number of speed inefficiencies:
- * All data blocks must be read to determine the length of the file.
- * Data blocks often need to be processed sequentially. The reversed
- nature of the block list transforms this from linear time to an O(n^2)
- operation.
-
-Furthermore, obtaining information about any constituent data block requires a
-separate flash read.
-
-
-*** INODE CACHE AND DATA BLOCK CACHE
-The speed issues are addressed by a pair of caches. Cached inodes entries
-contain the file length and a much more convenient doubly-linked list of
-cached data blocks. The benefit of using caches is that the size of the
-caches need not be proportional to the size of the file system. In other
-words, caches can address speed efficiency concerns without negatively
-impacting the file system's scalability.
-
-nffs requires both caches during normal operation, so it is not possible to
-disable them. However, the cache sizes are configurable, and both caches can
-be configured with a size of one if RAM usage must be minimized.
-
-The following data structures are used in the inode and data block caches.
-
-/** Full data block representation; not stored permanently in RAM. */
-struct nffs_block {
- struct nffs_hash_entry *nb_hash_entry; /* Points to real block entry. */
- uint32_t nb_seq; /* Sequence number; greater
- supersedes lesser. */
- struct nffs_inode_entry *nb_inode_entry; /* Owning inode. */
- struct nffs_hash_entry *nb_prev; /* Previous block in file. */
- uint16_t nb_data_len; /* # of data bytes in block. */
- uint16_t reserved16;
-};
-
-/** Represents a single cached data block. */
-struct nffs_cache_block {
- TAILQ_ENTRY(nffs_cache_block) ncb_link; /* Next / prev cached block. */
- struct nffs_block ncb_block; /* Full data block. */
- uint32_t ncb_file_offset; /* File offset of this block. */
-};
-
-/** Full inode representation; not stored permanently in RAM. */
-struct nffs_inode {
- struct nffs_inode_entry *ni_inode_entry; /* Points to real inode entry. */
- uint32_t ni_seq; /* Sequence number; greater
- supersedes lesser. */
- struct nffs_inode_entry *ni_parent; /* Points to parent directory. */
- uint8_t ni_filename_len; /* # chars in filename. */
- uint8_t ni_filename[NFFS_SHORT_FILENAME_LEN]; /* First 3 bytes. */
-};
-
-/** Doubly-linked tail queue of cached blocks; contained in cached inodes. */
-TAILQ_HEAD(nffs_block_cache_list, nffs_block_cache_entry);
-
-/** Represents a single cached file inode. */
-struct nffs_cache_inode {
- TAILQ_ENTRY(nffs_cache_inode) nci_link; /* Sorted; LRU at tail. */
- struct nffs_inode nci_inode; /* Full inode. */
- struct nffs_cache_block_list nci_block_list; /* List of cached blocks. */
- uint32_t nci_file_size; /* Total file size. */
-};
-
-Only file inodes are cached; directory inodes are never cached.
-
-Within a cached inode, all cached data blocks are contiguous. E.g., if the
-start and end of a file are cached, then the middle must also be cached. A
-data block is only cached if its owning file is also cached.
-
-Internally, cached inodes are stored in a singly-linked list, ordered by time
-of use. The most-recently-used entry is the first element in the list. If a
-new inode needs to be cached, but the inode cache is full, the
-least-recently-used entry is freed to make room for the new one. The
-following operations cause an inode to be cached:
- * Querying a file's length.
- * Seeking within a file.
- * Reading from a file.
- * Writing to a file.
-
-The following operations cause a data block to be cached:
- * Reading from the block.
- * Writing to the block.
-
-If one of the above operations is applied to a data block that is not currently
-cached, nffs uses the following procedure to cache the necessary block:
- 1. If none of the owning inode's blocks are currently cached, allocate a
- cached block entry corresponding to the requested block and insert it
- into the inode's list.
- 2. Else if the requested file offset is less than that of the first cached
- block, bridge the gap between the inode's sequence of cached blocks and
- the block that now needs to be cached. This is accomplished by caching
- each block in the gap, finishing with the requested block.
- 3. Else (the requested offset is beyond the end of the cache),
- a. If the requested offset belongs to the block that immediately
- follows the end of the cache, cache the block and append it to the
- list.
- b. Else, clear the cache, and populate it with the single entry
- corresponding to the requested block.
-
-If the system is unable to allocate a cached block entry at any point during
-the above procedure, the system frees up other blocks currently in the cache.
-This is accomplished as follows:
- 1. Iterate the inode cache in reverse (i.e., start with the
- least-recently-used entry). For each entry:
- a. If the entry's cached block list is empty, advance to the next
- entry.
- b. Else, free all the cached blocks in the entry's list.
-
-Because the system imposes a minimum block cache size of one, the above
-procedure will always reclaim at least one cache block entry. The above
-procedure may result in the freeing of the block list that belongs to the very
-inode being operated on. This is OK, as the final block to get cached is
-always the block being requested.
-
-
-*** CONFIGURATION
-The file system is configured by populating fields in a global structure.
-Each field in the structure corresponds to a setting. All configuration must
-be done prior to calling nffs_init(). The configuration structure is defined
-as follows:
-
-struct nffs_config {
- /** Maximum number of inodes; default=1024. */
- uint32_t nc_num_inodes;
-
- /** Maximum number of data blocks; default=4096. */
- uint32_t nc_num_blocks;
-
- /** Maximum number of open files; default=4. */
- uint32_t nc_num_files;
-
- /** Inode cache size; default=4. */
- uint32_t nc_num_cache_inodes;
-
- /** Data block cache size; default=64. */
- uint32_t nc_num_cache_blocks;
-};
-
-extern struct nffs_config nffs_config;
-
-Any fields that are set to 0 (or not set at all) inherit the corresponding
-default value. This means that it is impossible to configure any setting with
-a value of zero.
-
-
-*** INITIALIZATION
-
-There are two means of initializing an nffs file system:
- (1) Restore an existing file system via detection.
- (2) Create a new file system via formatting.
-
-Both methods require the user to describe how the flash memory is divided into
-areas. This is accomplished with an array of struct nffs_area_desc, defined as
-follows:
-
-struct nffs_area_desc {
- uint32_t nad_offset; /* Flash offset of start of area. */
- uint32_t nad_length; /* Size of area, in bytes. */
-};
-
-An array of area descriptors is terminated by an entry with a fad_length field
-of 0.
-
-One common initialization sequence is the following:
-
- (1) Detect an nffs file system anywhere in flash.
- (2) If no file system detected, format a new file system in a specific
- region of flash.
-
-
-*** DETECTION
-
-The file system detection process consists of scanning a specified set of
-flash regions for valid nffs areas, and then populating the RAM representation
-of the file system with the detected objects. Detection is initiated with the
-following function:
-
-/**
- * Searches for a valid nffs file system among the specified areas. This
- * function succeeds if a file system is detected among any subset of the
- * supplied areas. If the area set does not contain a valid file system,
- * a new one can be created via a separate call to nffs_format().
- *
- * @param area_descs The area set to search. This array must be
- * terminated with a 0-length area.
- *
- * @return 0 on success;
- * NFFS_ECORRUPT if no valid file system was detected;
- * other nonzero on error.
- */
-int nffs_detect(const struct nffs_area_desc *area_descs);
-
-As indicated, not every area descriptor needs to reference a valid nffs area.
-Detection is successful as long as a complete file system is detected
-somewhere in the specified regions of flash. If an application is unsure
-where a file system might be located, it can initiate detection across the
-entire flash region.
-
-A detected file system is valid if:
- (1) At least one non-scratch area is present.
- (2) At least one scratch area is present (only the first gets used if
- there is more than one).
- (3) The root directory inode is present.
-
-During detection, each indicated region of flash is checked for a valid area
-header. The contents of each valid non-scratch area are then restored into
-the nffs RAM representation. The following procedure is applied to each object
-in the area:
-
- (1) Verify the object's integrity via a crc16 check. If invalid, the
- object is discarded and the procedure restarts on the next object in
- the area.
-
- (2) Convert the disk object into its corresponding RAM representation and
- insert it into the hash table. If the object is an inode, its
- reference count is initialized to 1, indicating ownership by its
- parent directory.
-
- (3) If an object with the same ID is already present, then one supersedes
- the other. Accept the object with the greater sequence number and
- discard the other.
-
- (4) If the object references a nonexistant inode (parent directory in the
- case of an inode; owning file in the case of a data block), insert a
- temporary "dummy" inode into the hash table so that inter-object links
- can be maintained until the absent inode is eventually restored. Dummy
- inodes are identified by a reference count of 0.
-
- (5) If a delete record for an inode is encountered, the inode's parent
- pointer is set to null to indicate that it should be removed from RAM.
-
-If nffs encounters an object that cannot be identified (i.e., its magic number
-is not valid), it scans the remainder of the flash area for the next valid
-magic number. Upon encountering a valid object, nffs resumes the procedure
-described above.
-
-After all areas have been restored, a sweep is performed across the entire RAM
-representation so that invalid inodes can be deleted from memory.
-
-For each directory inode:
- * If its reference count is 0 (i.e., it is a dummy), migrate its children
- to the /lost+found directory, and delete it from the RAM representation.
- This should only happen in the case of file system corruption.
- * If its parent reference is null (i.e., it was deleted), delete it and all
- its children from the RAM representation.
-
-For each file inode:
- * If its reference count is 0 (i.e., it is a dummy), delete it from the RAM
- representation. This should only happen in the case of file system
- corruption. (We should try to migrate the file to the lost+found
- directory in this case, as mentioned in the todo section).
-
-When an object is deleted during this sweep, it is only deleted from the RAM
-representation; nothing is written to disk.
-
-When objects are migrated to the lost+found directory, their parent inode
-reference is permanently updated on the disk.
-
-In addition, a single scratch area is identified during the detection process.
-The first area whose 'fda_id' value is set to 0xff is designated as the file
-system scratch area. If no valid scratch area is found, the cause could be
-that the system was restarted while a garbage collection cycle was in progress.
-Such a condition is identified by the presence of two areas with the same ID.
-In such a case, the shorter of the two areas is erased and designated as the
-scratch area.
-
-
-*** FORMATTING
-
-A new file system is created via formatting. Formatting is achieved via the
-following function:
-
-/**
- * Erases all the specified areas and initializes them with a clean nffs
- * file system.
- *
- * @param area_descs The set of areas to format.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-int nffs_format(const struct nffs_area_desc *area_descs);
-
-On success, an area header is written to each of the specified locations. The
-largest area in the set is designated as the initial scratch area.
-
-
-*** FLASH WRITES
-
-The nffs implementation always writes in a strictly sequential fashion within an
-area. For each area, the system keeps track of the current offset. Whenever
-an object gets written to an area, it gets written to that area's current
-offset, and the offset is increased by the object's disk size.
-
-When a write needs to be performed, the nffs implementation selects the
-appropriate destination area by iterating though each area until one with
-sufficient free space is encountered.
-
-There is no write buffering. Each call to a write function results in a write
-operation being sent to the flash hardware.
-
-
-*** NEW OBJECTS
-
-Whenever a new object is written to disk, it is assigned the following
-properties:
- * ID: A unique value is selected from the 32-bit ID space, as appropriate
- for the object's type.
- * Sequence number: 0
-
-When a new file or directory is created, a corresponding inode is written to
-flash. Likewise, a new data block also results in the writing of a
-corresponding disk object.
-
-
-*** MOVING / RENAMING FILES AND DIRECTORIES
-
-When a file or directory is moved or renamed, its corresponding inode is
-rewritten to flash with the following properties:
- * ID: Unchanged
- * Sequence number: Previous value plus one.
- * Parent inode: As specified by the move / rename operation.
- * Filename: As specified by the move / rename operation.
-
-Because the inode's ID is unchanged, all dependent objects remain valid.
-
-
-*** UNLINKING FILES AND DIRECTORIES
-
-When a file or directory is unlinked from its parent directory, a deletion
-record for the unlinked inode gets written to flash. The deletion record is an
-inode with the following properties:
- * ID: Unchanged
- * Sequence number: Previous value plus one.
- * Parent inode ID: NFFS_ID_NONE
-
-When an inode is unlinked, no deletion records need to be written for the
-inode's dependent objects (constituent data blocks or child inodes). During
-the next file system detection, it is recognized that the objects belong to
-a deleted inode, so they are not restored into the RAM representation.
-
-If a file has an open handle at the time it gets unlinked, application code
-can continued to use the file handle to read and write data. All files retain
-a reference count, and a file isn't deleted from the RAM representation until
-its reference code drops to 0. Any attempt to open an unlinked file fails,
-even if the file is referenced by other file handles.
-
-
-*** WRITING TO A FILE
-
-The following procedure is used whenever the application code writes to a file.
-First, if the write operation specifies too much data to fit into a single
-block, the operation is split into several separate write operations. Then,
-for each write operation:
-
- (1) Determine which existing blocks the write operation overlaps
- (n = number of overwritten blocks).
-
- (2) If n = 0, this is an append operation. Write a data block with the
- following properties:
- * ID: New unique value.
- * Sequence number: 0.
-
- (3) Else (n > 1), this write overlaps existing data.
- (a) For each block in [1, 2, ... n-1], write a new block
- containing the updated contents. Each new block supersedes the
- block it overwrites. That is, each block has the following
- properties:
- * ID: Unchanged
- * Sequence number: Previous value plus one.
-
- (b) Write the nth block. The nth block includes all appended data,
- if any. As with the other blocks, its ID is unchanged and its
- sequence number is incremented.
-
-Appended data can only be written to the end of the file. That is, "holes" are
-not supported.
-
-
-*** GARBAGE COLLECTION
-
-When the file system is too full to accomodate a write operation, the system
-must perform garbage collection to make room. The garbage collection
-procedure is described below:
-
- (1) The non-scratch area with the lowest garbage collection sequence
- number is selected as the "source area." If there are other areas
- with the same sequence number, the one with the smallest flash offset
- is selected.
-
- (2) The source area's ID is written to the scratch area's header,
- transforming it into a non-scratch ID. This former scratch area is now
- known as the "destination area."
-
- (3) The RAM representation is exhaustively searched for collectible
- objects. The following procedure is applied to each inode in the
- system:
-
- (a) If the inode is resident in the source area, copy the inode record
- to the destination area.
-
- (b) If the inode is a file inode, walk the inode's list of data blocks,
- starting with the last block in the file. Each block that is
- resident in the source area is copied to the destination area. If
- there is a run of two or more blocks that are resident in the
- source area, they are consolidated and copied to the destination
- area as a single new block (subject to the maximum block size
- restriction).
-
- (4) The source area is reformatted as a scratch sector (i.e., is is fully
- erased, and its header is rewritten with an ID of 0xff). The area's
- garbage collection sequence number is incremented prior to rewriting
- the header. This area is now the new scratch sector.
-
-
-*** MISC
-
- * RAM usage:
- o 24 bytes per inode
- o 12 bytes per data block
- o 36 bytes per inode cache entry
- o 32 bytes per data block cache entry
- * Maximum filename size: 256 characters (no null terminator required)
- * Disallowed filename characters: '/' and '\0'
-
-
-*** FUTURE ENHANCEMENTS
-
- * API function to traverse a directory.
- * Migrate corrupt files to the /lost+found directory during restore, rather
- than discarding them from RAM.
- * Error correction.
- * Encryption.
- * Compression.
-
-
-*** API
-
-struct nffs_file;
-
-/**
- * Opens a file at the specified path. The result of opening a nonexistent
- * file depends on the access flags specified. All intermediate directories
- * must already exist.
- *
- * The mode strings passed to fopen() map to nffs_open()'s access flags as
- * follows:
- * "r" - NFFS_ACCESS_READ
- * "r+" - NFFS_ACCESS_READ | NFFS_ACCESS_WRITE
- * "w" - NFFS_ACCESS_WRITE | NFFS_ACCESS_TRUNCATE
- * "w+" - NFFS_ACCESS_READ | NFFS_ACCESS_WRITE | NFFS_ACCESS_TRUNCATE
- * "a" - NFFS_ACCESS_WRITE | NFFS_ACCESS_APPEND
- * "a+" - NFFS_ACCESS_READ | NFFS_ACCESS_WRITE | NFFS_ACCESS_APPEND
- *
- * @param out_file On success, a pointer to the newly-created file
- * handle gets written here.
- * @param path The path of the file to open.
- * @param access_flags Flags controlling file access; see above table.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_open(const char *path, uint8_t access_flags,
- struct nffs_file **out_file);
-
-
-/**
- * Closes the specified file and invalidates the file handle. If the file has
- * already been unlinked, and this is the last open handle to the file, this
- * operation causes the file to be deleted from disk.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_close(struct nffs_file *file);
-
-
-/**
- * Positions a file's read and write pointer at the specified offset. The
- * offset is expressed as the number of bytes from the start of the file (i.e.,
- * seeking to offset 0 places the pointer at the first byte in the file).
- *
- * @param file The file to reposition.
- * @param offset The 0-based file offset to seek to.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_seek(struct nffs_file *file, uint32_t offset);
-
-
-/**
- * Retrieves the current read and write position of the specified open file.
- *
- * @param file The file to query.
- *
- * @return The file offset, in bytes.
- */
-uint32_t nffs_getpos(const struct nffs_file *file);
-
-
-/**
- * Retrieves the current length of the specified open file.
- *
- * @param file The file to query.
- * @param out_len On success, the number of bytes in the file gets
- * written here.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_file_len(const struct nffs_file *file, uint32_t *out_len)
-
-
-/**
- * Reads data from the specified file. If more data is requested than remains
- * in the file, all available data is retrieved. Note: this type of short read
- * results in a success return code.
- *
- * @param file The file to read from.
- * @param len The number of bytes to attempt to read.
- * @param out_data The destination buffer to read into.
- * @param out_len On success, the number of bytes actually read gets
- * written here. Pass null if you don't care.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_read(struct nffs_file *file, uint32_t len, void *out_data,
- uint32_t *out_len)
-
-
-/**
- * Writes the supplied data to the current offset of the specified file handle.
- *
- * @param file The file to write to.
- * @param data The data to write.
- * @param len The number of bytes to write.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_write(struct nffs_file *file, const void *data, int len);
-
-
-/**
- * Unlinks the file or directory at the specified path. If the path refers to
- * a directory, all the directory's descendants are recursively unlinked. Any
- * open file handles refering to an unlinked file remain valid, and can be
- * read from and written to.
- *
- * @path The path of the file or directory to unlink.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_unlink(const char *path);
-
-
-/**
- * Performs a rename and / or move of the specified source path to the
- * specified destination. The source path can refer to either a file or a
- * directory. All intermediate directories in the destination path must
- * already exist. If the source path refers to a file, the destination path
- * must contain a full filename path, rather than just the new parent
- * directory. If an object already exists at the specified destination path,
- * this function causes it to be unlinked prior to the rename (i.e., the
- * destination gets clobbered).
- *
- * @param from The source path.
- * @param to The destination path.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-int nffs_rename(const char *from, const char *to);
-
-
-/**
- * Creates the directory represented by the specified path. All intermediate
- * directories must already exist. The specified path must start with a '/'
- * character.
- *
- * @param path The directory to create.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-int nffs_mkdir(const char *path);
-
-
-/**
- * Erases all the specified areas and initializes them with a clean nffs
- * file system.
- *
- * @param area_descs The set of areas to format.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-int nffs_format(const struct nffs_area_desc *area_descs);
-
-
-/**
- * Opens the directory at the specified path. The directory's contents can be
- * read with subsequent calls to nffs_readdir(). When you are done with the
- * directory handle, close it with nffs_closedir().
- *
- * Unlinking files from the directory while it is open may result in
- * unpredictable behavior. New files can be created inside the directory.
- *
- * @param path The directory to open.
- * @param out_dir On success, points to the directory handle.
- *
- * @return 0 on success;
- * NFFS_ENOENT if the specified directory does not
- * exist;
- * other nonzero on error.
- */
-int nffs_opendir(const char *path, struct nffs_dir **out_dir);
-
-
-/**
- * Reads the next entry in an open directory.
- *
- * @param dir The directory handle to read from.
- * @param out_dirent On success, points to the next child entry in
- * the specified directory.
- *
- * @return 0 on success;
- * NFFS_ENOENT if there are no more entries in the
- * parent directory;
- * other nonzero on error.
- */
-int nffs_readdir(struct nffs_dir *dir, struct nffs_dirent **out_dirent);
-
-
-/**
- * Closes the specified directory handle.
- *
- * @param dir The directory to close.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_closedir(struct nffs_dir *dir);
-
-
-/**
- * Retrieves the filename of the specified directory entry. The retrieved
- * filename is always null-terminated. To ensure enough space to hold the full
- * filename plus a null-termintor, a destination buffer of size
- * (NFFS_FILENAME_MAX_LEN + 1) should be used.
- *
- * @param dirent The directory entry to query.
- * @param max_len The size of the "out_name" character buffer.
- * @param out_name On success, the entry's filename is written
- * here; always null-terminated.
- * @param out_name_len On success, contains the actual length of the
- * filename, NOT including the
- * null-terminator.
- *
- * @return 0 on success; nonzero on failure.
- */
-int nffs_dirent_name(struct nffs_dirent *dirent, size_t max_len,
- char *out_name, uint8_t *out_name_len);
-
-
-/**
- * Tells you whether the specified directory entry is a sub-directory or a
- * regular file.
- *
- * @param dirent The directory entry to query.
- *
- * @return 1: The entry is a directory;
- * 0: The entry is a regular file.
- */
-int nffs_dirent_is_dir(const struct nffs_dirent *dirent);
-
-/**
- * Searches for a valid nffs file system among the specified areas. This
- * function succeeds if a file system is detected among any subset of the
- * supplied areas. If the area set does not contain a valid file system,
- * a new one can be created via a call to nffs_format().
- *
- * @param area_descs The area set to search. This array must be
- * terminated with a 0-length area.
- *
- * @return 0 on success;
- * NFFS_ECORRUPT if no valid file system was detected;
- * other nonzero on error.
- */
-int nffs_detect(const struct nffs_area_desc *area_descs);
-
-
-/**
- * Indicates whether a valid filesystem has been initialized, either via
- * detection or formatting.
- *
- * @return 1 if a file system is present; 0 otherwise.
- */
-int nffs_ready(void);
-
-
-/**
- * Initializes the nffs memory and data structures. This must be called before
- * any nffs operations are attempted.
- *
- * @return 0 on success; nonzero on error.
- */
-int nffs_init(void);
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/egg.yml
----------------------------------------------------------------------
diff --git a/libs/nffs/egg.yml b/libs/nffs/egg.yml
deleted file mode 100644
index e630132..0000000
--- a/libs/nffs/egg.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-egg.name: libs/nffs
-egg.vers: 0.1
-egg.identities: NFFS
-egg.deps:
- - libs/os
- - libs/fs
- - libs/testutil
- - hw/hal
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/include/nffs/nffs.h
----------------------------------------------------------------------
diff --git a/libs/nffs/include/nffs/nffs.h b/libs/nffs/include/nffs/nffs.h
deleted file mode 100644
index 9854bc8..0000000
--- a/libs/nffs/include/nffs/nffs.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 H_NFFS_
-#define H_NFFS_
-
-#include <stddef.h>
-#include <inttypes.h>
-
-#define NFFS_FILENAME_MAX_LEN 256 /* Does not require null terminator. */
-#define NFFS_MAX_AREAS 256
-
-struct nffs_config {
- /** Maximum number of inodes; default=1024. */
- uint32_t nc_num_inodes;
-
- /** Maximum number of data blocks; default=4096. */
- uint32_t nc_num_blocks;
-
- /** Maximum number of open files; default=4. */
- uint32_t nc_num_files;
-
- /** Maximum number of open directories; default=4. */
- uint32_t nc_num_dirs;
-
- /** Inode cache size; default=4. */
- uint32_t nc_num_cache_inodes;
-
- /** Data block cache size; default=64. */
- uint32_t nc_num_cache_blocks;
-};
-
-extern struct nffs_config nffs_config;
-
-struct nffs_area_desc {
- uint32_t nad_offset; /* Flash offset of start of area. */
- uint32_t nad_length; /* Size of area, in bytes. */
- uint8_t nad_flash_id; /* Logical flash id */
-};
-
-int nffs_init(void);
-int nffs_detect(const struct nffs_area_desc *area_descs);
-int nffs_format(const struct nffs_area_desc *area_descs);
-int nffs_ready(void);
-
-
-#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/include/nffs/nffs_test.h
----------------------------------------------------------------------
diff --git a/libs/nffs/include/nffs/nffs_test.h b/libs/nffs/include/nffs/nffs_test.h
deleted file mode 100644
index 12d2ea4..0000000
--- a/libs/nffs/include/nffs/nffs_test.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 H_NFFS_TEST_
-#define H_NFFS_TEST_
-
-int nffs_test_all(void);
-
-#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/crc16.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/crc16.c b/libs/nffs/src/crc16.c
deleted file mode 100644
index 85995c4..0000000
--- a/libs/nffs/src/crc16.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-/*
- * Copyright 2001-2010 Georges Menie (www.menie.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 University of California, Berkeley nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS AND CONTRIBUTORS 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.
- */
-
-#include <inttypes.h>
-#include "crc16.h"
-
-/* CRC16 implementation acording to CCITT standards */
-
-static const uint16_t crc16tab[256]= {
- 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
- 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
- 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
- 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
- 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
- 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
- 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
- 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
- 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
- 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
- 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
- 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
- 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
- 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
- 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
- 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
- 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
- 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
- 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
- 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
- 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
- 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
- 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
- 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
- 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
- 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
- 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
- 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
- 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
- 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
- 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
- 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
-};
-
-uint16_t
-crc16_ccitt(uint16_t initial_crc, const void *buf, int len)
-{
- const uint8_t *ptr;
- uint16_t crc;
- int counter;
-
- crc = initial_crc;
- ptr = buf;
-
- for (counter = 0; counter < len; counter++) {
- crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *ptr++)&0x00FF];
- }
-
- return crc;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/crc16.h
----------------------------------------------------------------------
diff --git a/libs/nffs/src/crc16.h b/libs/nffs/src/crc16.h
deleted file mode 100644
index f0937ca..0000000
--- a/libs/nffs/src/crc16.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-
-/*
- * Copyright 2001-2010 Georges Menie (www.menie.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 University of California, Berkeley nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS AND CONTRIBUTORS 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.
- */
-
-#ifndef _CRC16_H_
-#define _CRC16_H_
-
-#include <inttypes.h>
-
-unsigned short crc16_ccitt(uint16_t initial_crc, const void *buf, int len);
-
-#endif /* _CRC16_H_ */
[13/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
Relocate libs/fs -> fs/fs libs/nffs -> fs/nffs.
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/commit/cf40853d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/cf40853d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/cf40853d
Branch: refs/heads/master
Commit: cf40853d2bc455ed13ea3844afaf0a51a4ed2ca2
Parents: a4dd006
Author: Marko Kiiskila <ma...@runtime.io>
Authored: Fri Jan 15 09:54:35 2016 -0800
Committer: Marko Kiiskila <ma...@runtime.io>
Committed: Fri Jan 15 09:54:35 2016 -0800
----------------------------------------------------------------------
fs/fs/egg.yml | 5 +
fs/fs/include/fs/fs.h | 75 +
fs/fs/include/fs/fs_if.h | 55 +
fs/fs/include/fs/fsutil.h | 26 +
fs/fs/src/fs_cli.c | 123 +
fs/fs/src/fs_dirent.c | 49 +
fs/fs/src/fs_file.c | 67 +
fs/fs/src/fs_mkdir.c | 31 +
fs/fs/src/fs_mount.c | 35 +
fs/fs/src/fs_priv.h | 26 +
fs/fs/src/fsutil.c | 64 +
fs/nffs/LICENSE | 202 +
fs/nffs/README.md | 3 +
fs/nffs/design.txt | 866 +++
fs/nffs/egg.yml | 8 +
fs/nffs/include/nffs/nffs.h | 60 +
fs/nffs/include/nffs/nffs_test.h | 22 +
fs/nffs/src/crc16.c | 99 +
fs/nffs/src/crc16.h | 52 +
fs/nffs/src/nffs.c | 692 ++
fs/nffs/src/nffs_area.c | 109 +
fs/nffs/src/nffs_block.c | 298 +
fs/nffs/src/nffs_cache.c | 445 ++
fs/nffs/src/nffs_config.c | 51 +
fs/nffs/src/nffs_crc.c | 173 +
fs/nffs/src/nffs_dir.c | 136 +
fs/nffs/src/nffs_file.c | 346 +
fs/nffs/src/nffs_flash.c | 174 +
fs/nffs/src/nffs_format.c | 183 +
fs/nffs/src/nffs_gc.c | 523 ++
fs/nffs/src/nffs_hash.c | 151 +
fs/nffs/src/nffs_inode.c | 906 +++
fs/nffs/src/nffs_misc.c | 352 +
fs/nffs/src/nffs_path.c | 337 +
fs/nffs/src/nffs_priv.h | 427 ++
fs/nffs/src/nffs_restore.c | 1034 +++
fs/nffs/src/nffs_write.c | 397 ++
fs/nffs/src/test/arch/cortex_m4/nffs_test.c | 24 +
fs/nffs/src/test/arch/sim/nffs_test.c | 2441 +++++++
fs/nffs/src/test/arch/sim/nffs_test_priv.h | 39 +
fs/nffs/src/test/arch/sim/nffs_test_system_01.c | 6534 ++++++++++++++++++
fs/nffs/todo.txt | 4 +
hw/hal/egg.yml | 2 +-
libs/bootutil/egg.yml | 2 +-
libs/elua/elua_base/egg.yml | 2 +-
libs/fs/egg.yml | 5 -
libs/fs/include/fs/fs.h | 75 -
libs/fs/include/fs/fs_if.h | 55 -
libs/fs/include/fs/fsutil.h | 26 -
libs/fs/src/fs_cli.c | 123 -
libs/fs/src/fs_dirent.c | 49 -
libs/fs/src/fs_file.c | 67 -
libs/fs/src/fs_mkdir.c | 31 -
libs/fs/src/fs_mount.c | 35 -
libs/fs/src/fs_priv.h | 26 -
libs/fs/src/fsutil.c | 64 -
libs/imgmgr/egg.yml | 2 +-
libs/nffs/LICENSE | 202 -
libs/nffs/README.md | 3 -
libs/nffs/design.txt | 866 ---
libs/nffs/egg.yml | 8 -
libs/nffs/include/nffs/nffs.h | 60 -
libs/nffs/include/nffs/nffs_test.h | 22 -
libs/nffs/src/crc16.c | 99 -
libs/nffs/src/crc16.h | 52 -
libs/nffs/src/nffs.c | 692 --
libs/nffs/src/nffs_area.c | 109 -
libs/nffs/src/nffs_block.c | 298 -
libs/nffs/src/nffs_cache.c | 445 --
libs/nffs/src/nffs_config.c | 51 -
libs/nffs/src/nffs_crc.c | 173 -
libs/nffs/src/nffs_dir.c | 136 -
libs/nffs/src/nffs_file.c | 346 -
libs/nffs/src/nffs_flash.c | 174 -
libs/nffs/src/nffs_format.c | 183 -
libs/nffs/src/nffs_gc.c | 523 --
libs/nffs/src/nffs_hash.c | 151 -
libs/nffs/src/nffs_inode.c | 906 ---
libs/nffs/src/nffs_misc.c | 352 -
libs/nffs/src/nffs_path.c | 337 -
libs/nffs/src/nffs_priv.h | 427 --
libs/nffs/src/nffs_restore.c | 1034 ---
libs/nffs/src/nffs_write.c | 397 --
libs/nffs/src/test/arch/cortex_m4/nffs_test.c | 24 -
libs/nffs/src/test/arch/sim/nffs_test.c | 2441 -------
libs/nffs/src/test/arch/sim/nffs_test_priv.h | 39 -
.../src/test/arch/sim/nffs_test_system_01.c | 6534 ------------------
libs/nffs/todo.txt | 4 -
libs/testreport/egg.yml | 2 +-
project/boot/boot.yml | 2 +-
project/boot/egg.yml | 2 +-
project/ffs2native/egg.yml | 6 +
project/ffs2native/ffs2native.yml | 2 +-
project/luatest/luatest.yml | 2 +-
project/test/egg.yml | 2 +-
project/test/test.yml | 2 +-
96 files changed, 17661 insertions(+), 17655 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/egg.yml
----------------------------------------------------------------------
diff --git a/fs/fs/egg.yml b/fs/fs/egg.yml
new file mode 100644
index 0000000..8f1d729
--- /dev/null
+++ b/fs/fs/egg.yml
@@ -0,0 +1,5 @@
+egg.name: fs/fs
+egg.deps.SHELL:
+ - libs/shell
+ - libs/console/full
+egg.cflags.SHELL: -DSHELL_PRESENT
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/include/fs/fs.h
----------------------------------------------------------------------
diff --git a/fs/fs/include/fs/fs.h b/fs/fs/include/fs/fs.h
new file mode 100644
index 0000000..872b863
--- /dev/null
+++ b/fs/fs/include/fs/fs.h
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 __FS_H__
+#define __FS_H__
+
+#include <stddef.h>
+#include <inttypes.h>
+
+/*
+ * Common interface to access files.
+ */
+struct fs_file;
+struct fs_dir;
+struct fs_dirent;
+
+int fs_open(const char *filename, uint8_t access_flags, struct fs_file **);
+int fs_close(struct fs_file *);
+int fs_read(struct fs_file *, uint32_t len, void *out_data, uint32_t *out_len);
+int fs_write(struct fs_file *, const void *data, int len);
+int fs_seek(struct fs_file *, uint32_t offset);
+uint32_t fs_getpos(const struct fs_file *);
+int fs_filelen(const struct fs_file *, uint32_t *out_len);
+
+int fs_unlink(const char *filename);
+int fs_rename(const char *from, const char *to);
+int fs_mkdir(const char *path);
+
+int fs_opendir(const char *path, struct fs_dir **);
+int fs_readdir(struct fs_dir *, struct fs_dirent **);
+int fs_closedir(struct fs_dir *);
+int fs_dirent_name(const struct fs_dirent *, size_t max_len,
+ char *out_name, uint8_t *out_name_len);
+int fs_dirent_is_dir(const struct fs_dirent *);
+
+/*
+ * File access flags.
+ */
+#define FS_ACCESS_READ 0x01
+#define FS_ACCESS_WRITE 0x02
+#define FS_ACCESS_APPEND 0x04
+#define FS_ACCESS_TRUNCATE 0x08
+
+/*
+ * File access return codes.
+ */
+#define FS_EOK 0 /* OK */
+#define FS_ECORRUPT 1 /* Filesystem corrupt */
+#define FS_HW_ERROR 2 /* Error access storage medium */
+#define FS_ERANGE 3
+#define FS_EINVAL 4
+#define FS_ENOMEM 5 /* out of memory */
+#define FS_ENOENT 6 /* no such file */
+#define FS_EEMPTY 7
+#define FS_EFULL 8
+#define FS_EUNEXP 9
+#define FS_EOS 10
+#define FS_EEXIST 11
+#define FS_EACCESS 12
+#define FS_EUNINIT 13
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/include/fs/fs_if.h
----------------------------------------------------------------------
diff --git a/fs/fs/include/fs/fs_if.h b/fs/fs/include/fs/fs_if.h
new file mode 100644
index 0000000..9236cc6
--- /dev/null
+++ b/fs/fs/include/fs/fs_if.h
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 __FS_IF_H__
+#define __FS_IF_H__
+
+/*
+ * Common interface filesystem(s) provide.
+ */
+struct fs_ops {
+ int (*f_open)(const char *filename, uint8_t access_flags,
+ struct fs_file **out_file);
+ int (*f_close)(struct fs_file *file);
+ int (*f_read)(struct fs_file *file, uint32_t len, void *out_data,
+ uint32_t *out_len);
+ int (*f_write)(struct fs_file *file, const void *data, int len);
+
+ int (*f_seek)(struct fs_file *file, uint32_t offset);
+ uint32_t (*f_getpos)(const struct fs_file *file);
+ int (*f_filelen)(const struct fs_file *file, uint32_t *out_len);
+
+ int (*f_unlink)(const char *filename);
+ int (*f_rename)(const char *from, const char *to);
+ int (*f_mkdir)(const char *path);
+
+ int (*f_opendir)(const char *path, struct fs_dir **out_dir);
+ int (*f_readdir)(struct fs_dir *dir, struct fs_dirent **out_dirent);
+ int (*f_closedir)(struct fs_dir *dir);
+
+ int (*f_dirent_name)(const struct fs_dirent *dirent, size_t max_len,
+ char *out_name, uint8_t *out_name_len);
+ int (*f_dirent_is_dir)(const struct fs_dirent *dirent);
+
+ const char *f_name;
+};
+
+/*
+ * Currently allow only one type of FS, starts at root.
+ */
+int fs_register(const struct fs_ops *);
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/include/fs/fsutil.h
----------------------------------------------------------------------
diff --git a/fs/fs/include/fs/fsutil.h b/fs/fs/include/fs/fsutil.h
new file mode 100644
index 0000000..59bfaf3
--- /dev/null
+++ b/fs/fs/include/fs/fsutil.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 H_FSUTIL_
+#define H_FSUTIL_
+
+#include <inttypes.h>
+
+int fsutil_read_file(const char *path, uint32_t offset, uint32_t len,
+ void *dst, uint32_t *out_len);
+int fsutil_write_file(const char *path, const void *data, uint32_t len);
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/src/fs_cli.c
----------------------------------------------------------------------
diff --git a/fs/fs/src/fs_cli.c b/fs/fs/src/fs_cli.c
new file mode 100644
index 0000000..e26e003
--- /dev/null
+++ b/fs/fs/src/fs_cli.c
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#ifdef SHELL_PRESENT
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <shell/shell.h>
+#include <console/console.h>
+
+#include "fs/fs.h"
+
+static struct shell_cmd fs_ls_struct;
+
+static void
+fs_ls_file(const char *name, struct fs_file *file)
+{
+ uint32_t len;
+
+ len = 0;
+ fs_filelen(file, &len);
+ console_printf("\t%6lu %s\n", (unsigned long)len, name);
+}
+
+static void
+fs_ls_dir(const char *name)
+{
+ console_printf("\t%6s %s\n", "dir", name);
+}
+
+static int
+fs_ls_cmd(int argc, char **argv)
+{
+ int rc, file_cnt = 0;
+ char *path;
+ struct fs_file *file;
+ struct fs_dir *dir;
+ struct fs_dirent *dirent;
+ char name[64];
+ int plen;
+ uint8_t namelen;
+
+ switch (argc) {
+ case 1:
+ path = "/";
+ break;
+ case 2:
+ path = argv[1];
+ break;
+ default:
+ console_printf("ls <path>\n");
+ return 1;
+ }
+
+ rc = fs_open(path, FS_ACCESS_READ, &file);
+ if (rc == 0) {
+ fs_ls_file(path, file);
+ fs_close(file);
+ file_cnt = 1;
+ goto done;
+ }
+
+ plen = strlen(path);
+ strncpy(name, path, sizeof(name) - 2);
+ if (name[plen - 1] != '/') {
+ name[plen++] = '/';
+ name[plen] = '\0';
+ }
+
+ rc = fs_opendir(path, &dir);
+ if (rc == 0) {
+ do {
+ rc = fs_readdir(dir, &dirent);
+ if (rc) {
+ break;
+ }
+ if (fs_dirent_name(dirent, sizeof(name) - plen, &name[plen],
+ &namelen)) {
+ break;
+ }
+ rc = fs_open(name, FS_ACCESS_READ, &file);
+ if (rc == 0) {
+ fs_ls_file(name, file);
+ fs_close(file);
+ } else {
+ fs_ls_dir(name);
+ }
+ file_cnt++;
+ } while (1);
+ fs_closedir(dir);
+ goto done;
+ }
+ console_printf("Error listing %s - %d\n", path, rc);
+done:
+ console_printf("%d files\n", file_cnt);
+ return 0;
+}
+
+void
+fs_cli_init(void)
+{
+ int rc;
+
+ rc = shell_cmd_register(&fs_ls_struct, "ls", fs_ls_cmd);
+ if (rc != 0) {
+ return;
+ }
+}
+#endif /* SHELL_PRESENT */
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/src/fs_dirent.c
----------------------------------------------------------------------
diff --git a/fs/fs/src/fs_dirent.c b/fs/fs/src/fs_dirent.c
new file mode 100644
index 0000000..e4428de
--- /dev/null
+++ b/fs/fs/src/fs_dirent.c
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+#include <fs/fs.h>
+#include <fs/fs_if.h>
+#include "fs_priv.h"
+
+int
+fs_opendir(const char *path, struct fs_dir **out_dir)
+{
+ return fs_root_ops->f_opendir(path, out_dir);
+}
+
+int
+fs_readdir(struct fs_dir *dir, struct fs_dirent **out_dirent)
+{
+ return fs_root_ops->f_readdir(dir, out_dirent);
+}
+
+int
+fs_closedir(struct fs_dir *dir)
+{
+ return fs_root_ops->f_closedir(dir);
+}
+
+int
+fs_dirent_name(const struct fs_dirent *dirent, size_t max_len,
+ char *out_name, uint8_t *out_name_len)
+{
+ return fs_root_ops->f_dirent_name(dirent, max_len, out_name, out_name_len);
+}
+
+int
+fs_dirent_is_dir(const struct fs_dirent *dirent)
+{
+ return fs_root_ops->f_dirent_is_dir(dirent);
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/src/fs_file.c
----------------------------------------------------------------------
diff --git a/fs/fs/src/fs_file.c b/fs/fs/src/fs_file.c
new file mode 100644
index 0000000..ec2697b
--- /dev/null
+++ b/fs/fs/src/fs_file.c
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+#include <fs/fs.h>
+#include <fs/fs_if.h>
+
+#include "fs_priv.h"
+
+int
+fs_open(const char *filename, uint8_t access_flags, struct fs_file **out_file)
+{
+ return fs_root_ops->f_open(filename, access_flags, out_file);
+}
+
+int
+fs_close(struct fs_file *file)
+{
+ return fs_root_ops->f_close(file);
+}
+
+int
+fs_read(struct fs_file *file, uint32_t len, void *out_data, uint32_t *out_len)
+{
+ return fs_root_ops->f_read(file, len, out_data, out_len);
+}
+
+int
+fs_write(struct fs_file *file, const void *data, int len)
+{
+ return fs_root_ops->f_write(file, data, len);
+}
+
+int
+fs_seek(struct fs_file *file, uint32_t offset)
+{
+ return fs_root_ops->f_seek(file, offset);
+}
+
+uint32_t
+fs_getpos(const struct fs_file *file)
+{
+ return fs_root_ops->f_getpos(file);
+}
+
+int
+fs_filelen(const struct fs_file *file, uint32_t *out_len)
+{
+ return fs_root_ops->f_filelen(file, out_len);
+}
+
+int
+fs_unlink(const char *filename)
+{
+ return fs_root_ops->f_unlink(filename);
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/src/fs_mkdir.c
----------------------------------------------------------------------
diff --git a/fs/fs/src/fs_mkdir.c b/fs/fs/src/fs_mkdir.c
new file mode 100644
index 0000000..4be7a0a
--- /dev/null
+++ b/fs/fs/src/fs_mkdir.c
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+#include <fs/fs.h>
+#include <fs/fs_if.h>
+
+#include "fs_priv.h"
+
+int
+fs_rename(const char *from, const char *to)
+{
+ return fs_root_ops->f_rename(from, to);
+}
+
+int
+fs_mkdir(const char *path)
+{
+ return fs_root_ops->f_mkdir(path);
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/src/fs_mount.c
----------------------------------------------------------------------
diff --git a/fs/fs/src/fs_mount.c b/fs/fs/src/fs_mount.c
new file mode 100644
index 0000000..e452cf2
--- /dev/null
+++ b/fs/fs/src/fs_mount.c
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+#include <fs/fs.h>
+#include <fs/fs_if.h>
+#include "fs_priv.h"
+
+const struct fs_ops *fs_root_ops;
+
+int
+fs_register(const struct fs_ops *fops)
+{
+ if (fs_root_ops) {
+ return FS_EEXIST;
+ }
+ fs_root_ops = fops;
+
+#ifdef SHELL_PRESENT
+ fs_cli_init();
+#endif
+
+ return FS_EOK;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/src/fs_priv.h
----------------------------------------------------------------------
diff --git a/fs/fs/src/fs_priv.h b/fs/fs/src/fs_priv.h
new file mode 100644
index 0000000..c3d2ba6
--- /dev/null
+++ b/fs/fs/src/fs_priv.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 __FS_PRIV_H__
+#define __FS_PRIV_H__
+
+struct fs_ops;
+extern const struct fs_ops *fs_root_ops;
+
+#ifdef SHELL_PRESENT
+void fs_cli_init(void);
+#endif /* SHELL_PRESENT */
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/fs/src/fsutil.c
----------------------------------------------------------------------
diff --git a/fs/fs/src/fsutil.c b/fs/fs/src/fsutil.c
new file mode 100644
index 0000000..97fa84a
--- /dev/null
+++ b/fs/fs/src/fsutil.c
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include "fs/fs.h"
+
+int
+fsutil_read_file(const char *path, uint32_t offset, uint32_t len, void *dst,
+ uint32_t *out_len)
+{
+ struct fs_file *file;
+ int rc;
+
+ rc = fs_open(path, FS_ACCESS_READ, &file);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = fs_read(file, len, dst, out_len);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ fs_close(file);
+ return rc;
+}
+
+int
+fsutil_write_file(const char *path, const void *data, uint32_t len)
+{
+ struct fs_file *file;
+ int rc;
+
+ rc = fs_open(path, FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = fs_write(file, data, len);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ fs_close(file);
+ return rc;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/LICENSE
----------------------------------------------------------------------
diff --git a/fs/nffs/LICENSE b/fs/nffs/LICENSE
new file mode 100644
index 0000000..8f71f43
--- /dev/null
+++ b/fs/nffs/LICENSE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/README.md
----------------------------------------------------------------------
diff --git a/fs/nffs/README.md b/fs/nffs/README.md
new file mode 100644
index 0000000..67071c5
--- /dev/null
+++ b/fs/nffs/README.md
@@ -0,0 +1,3 @@
+# ffs
+
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/design.txt
----------------------------------------------------------------------
diff --git a/fs/nffs/design.txt b/fs/nffs/design.txt
new file mode 100644
index 0000000..76dce2c
--- /dev/null
+++ b/fs/nffs/design.txt
@@ -0,0 +1,866 @@
+***** NFFS
+
+*** HISTORY
+Rev. Date Changes
+6 2015/10/12 Add directory-reading API.
+5 2015/09/28 Rename to Newtron Flash File System.
+4 2015/09/08 Fix some badly-specified CRC behavior; clarification of
+ behavior during restore of corrupt disk.
+3 2015/08/20 More cache specifics; updated nffs_read() function
+ type.
+2 2015/08/17 Addition of crc16; clarification of sweep phase.
+1 2015/08/13
+
+
+*** SUMMARY
+
+Newtron Flash File System (nffs) is a flash file system with the following
+priorities:
+ * Minimal RAM usage
+ * Reliability
+
+
+*** DISK STRUCTURE
+
+At the top level, an nffs disk is partitioned into areas. An area is a region
+of disk with the following properties:
+ (1) An area can be fully erased without affecting any other areas.
+ (2) A write to one area does not restrict writes to other areas.
+
+Clarification of point (2): some flash hardware divides its memory space into
+"blocks." Writes within a block must be sequential, but writes to one block
+have no effect on what parts of other blocks can be written. Thus, for flash
+hardware with such a restriction, each area must comprise a discrete number of
+blocks.
+
+While not strictly necessary, it is recommended that all areas have the same
+size.
+
+On disk, each area is prefixed with the following header:
+
+/** On-disk representation of an area header. */
+struct nffs_disk_area {
+ uint32_t nda_magic[4]; /* NFFS_AREA_MAGIC{0,1,2,3} */
+ uint32_t nda_length; /* Total size of area, in bytes. */
+ uint8_t nda_ver; /* Current nffs version: 0 */
+ uint8_t nda_gc_seq; /* Garbage collection count. */
+ uint8_t reserved8;
+ uint8_t nda_id; /* 0xff if scratch area. */
+};
+
+Beyond its header, an area contains a sequence of disk objects, representing
+the contents of the file system. There are two types of objects: inodes and
+data blocks. An inode represents a file or directory; a data block represents
+part of a file's contents.
+
+/** On-disk representation of an inode (file or directory). */
+struct nffs_disk_inode {
+ uint32_t ndi_magic; /* NFFS_INODE_MAGIC */
+ uint32_t ndi_id; /* Unique object ID. */
+ uint32_t ndi_seq; /* Sequence number; greater supersedes
+ lesser. */
+ uint32_t ndi_parent_id; /* Object ID of parent directory inode. */
+ uint8_t reserved8;
+ uint8_t ndi_filename_len; /* Length of filename, in bytes. */
+ uint16_t ndi_crc16; /* Covers rest of header and filename. */
+ /* Followed by filename. */
+};
+
+An inode filename's length cannot exceed 256 bytes. The filename is not
+null-terminated. The following ASCII characters are not allowed in a
+filename:
+ * / (slash character)
+ * \0 (NUL character)
+
+/** On-disk representation of a data block. */
+struct nffs_disk_block {
+ uint32_t ndb_magic; /* NFFS_BLOCK_MAGIC */
+ uint32_t ndb_id; /* Unique object ID. */
+ uint32_t ndb_seq; /* Sequence number; greater supersedes lesser. */
+ uint32_t ndb_inode_id; /* Object ID of owning inode. */
+ uint32_t ndb_prev_id; /* Object ID of previous block in file;
+ NFFS_ID_NONE if this is the first block. */
+ uint16_t ndb_data_len; /* Length of data contents, in bytes. */
+ uint16_t ndb_crc16; /* Covers rest of header and data. */
+ /* Followed by 'ndb_data_len' bytes of data. */
+};
+
+Each data block contains the ID of the previous data block in the file.
+Together, the set of blocks in a file form a reverse singly-linked list.
+
+The maximum number of data bytes that a block can contain is determined at
+initialization-time. The result is the greatest number which satisfies all of
+the following restrictions:
+ o No more than 2048.
+ o At least two maximum-sized blocks can fit in the smallest area.
+
+The 2048 number was chosen somewhat arbitrarily, and may change in the future.
+
+
+*** ID SPACE
+
+All disk objects have a unique 32-bit ID. The ID space is partitioned as
+follows:
+ * 0x00000000 - 0x0fffffff: Directory inodes.
+ * 0x10000000 - 0x7fffffff: File inodes.
+ * 0x80000000 - 0xfffffffe: Data blocks.
+ * 0xffffffff : Reserved (NFFS_ID_NONE)
+
+
+*** SCRATCH AREA
+
+A valid nffs file system must contain a single "scratch area." The scratch
+area does not contain any objects of its own, and is only used during garbage
+collection. The scratch area must have a size greater than or equal to each
+of the other areas in flash.
+
+
+*** RAM REPRESENTATION
+
+The file system comprises a set of objects of the following two types:
+ 1) inode
+ 2) data block
+
+Every object in the file system is stored in a 256-entry hash table. An
+object's hash key is derived from its 32-bit ID. Each list in the hash table
+is sorted by time of use; most-recently-used is at the front of the list. All
+objects are represented by the following structure:
+
+/**
+ * What gets stored in the hash table. Each entry represents a data block or
+ * an inode.
+ */
+struct nffs_hash_entry {
+ SLIST_ENTRY(nffs_hash_entry) nhe_next;
+ uint32_t nhe_id; /* 0 - 0x7fffffff if inode; else if block. */
+ uint32_t nhe_flash_loc; /* Upper-byte = area idx; rest = area offset. */
+};
+
+For each data block, the above structure is all that is stored in RAM. To
+acquire more information about a data block, the block header must be read
+from flash.
+
+Inodes require a fuller RAM representation to capture the structure of the
+file system. There are two types of inodes: files and directories. Each
+inode hash entry is actually an instance of the following structure:
+
+/** Each inode hash entry is actually one of these. */
+struct nffs_inode_entry {
+ struct nffs_hash_entry nie_hash_entry;
+ SLIST_ENTRY(nffs_inode_entry) nie_sibling_next;
+ union {
+ struct nffs_inode_list nie_child_list; /* If directory */
+ struct nffs_hash_entry *nie_last_block_entry; /* If file */
+ };
+ uint8_t nie_refcnt;
+};
+
+A directory inode contains a list of its child files and directories
+(fie_child_list). These entries are sorted alphabetically using the ASCII
+character set.
+
+A file inode contains a pointer to the last data block in the file
+(nie_last_block_entry). For most file operations, the reversed block list must
+be walked backwards. This introduces a number of speed inefficiencies:
+ * All data blocks must be read to determine the length of the file.
+ * Data blocks often need to be processed sequentially. The reversed
+ nature of the block list transforms this from linear time to an O(n^2)
+ operation.
+
+Furthermore, obtaining information about any constituent data block requires a
+separate flash read.
+
+
+*** INODE CACHE AND DATA BLOCK CACHE
+The speed issues are addressed by a pair of caches. Cached inodes entries
+contain the file length and a much more convenient doubly-linked list of
+cached data blocks. The benefit of using caches is that the size of the
+caches need not be proportional to the size of the file system. In other
+words, caches can address speed efficiency concerns without negatively
+impacting the file system's scalability.
+
+nffs requires both caches during normal operation, so it is not possible to
+disable them. However, the cache sizes are configurable, and both caches can
+be configured with a size of one if RAM usage must be minimized.
+
+The following data structures are used in the inode and data block caches.
+
+/** Full data block representation; not stored permanently in RAM. */
+struct nffs_block {
+ struct nffs_hash_entry *nb_hash_entry; /* Points to real block entry. */
+ uint32_t nb_seq; /* Sequence number; greater
+ supersedes lesser. */
+ struct nffs_inode_entry *nb_inode_entry; /* Owning inode. */
+ struct nffs_hash_entry *nb_prev; /* Previous block in file. */
+ uint16_t nb_data_len; /* # of data bytes in block. */
+ uint16_t reserved16;
+};
+
+/** Represents a single cached data block. */
+struct nffs_cache_block {
+ TAILQ_ENTRY(nffs_cache_block) ncb_link; /* Next / prev cached block. */
+ struct nffs_block ncb_block; /* Full data block. */
+ uint32_t ncb_file_offset; /* File offset of this block. */
+};
+
+/** Full inode representation; not stored permanently in RAM. */
+struct nffs_inode {
+ struct nffs_inode_entry *ni_inode_entry; /* Points to real inode entry. */
+ uint32_t ni_seq; /* Sequence number; greater
+ supersedes lesser. */
+ struct nffs_inode_entry *ni_parent; /* Points to parent directory. */
+ uint8_t ni_filename_len; /* # chars in filename. */
+ uint8_t ni_filename[NFFS_SHORT_FILENAME_LEN]; /* First 3 bytes. */
+};
+
+/** Doubly-linked tail queue of cached blocks; contained in cached inodes. */
+TAILQ_HEAD(nffs_block_cache_list, nffs_block_cache_entry);
+
+/** Represents a single cached file inode. */
+struct nffs_cache_inode {
+ TAILQ_ENTRY(nffs_cache_inode) nci_link; /* Sorted; LRU at tail. */
+ struct nffs_inode nci_inode; /* Full inode. */
+ struct nffs_cache_block_list nci_block_list; /* List of cached blocks. */
+ uint32_t nci_file_size; /* Total file size. */
+};
+
+Only file inodes are cached; directory inodes are never cached.
+
+Within a cached inode, all cached data blocks are contiguous. E.g., if the
+start and end of a file are cached, then the middle must also be cached. A
+data block is only cached if its owning file is also cached.
+
+Internally, cached inodes are stored in a singly-linked list, ordered by time
+of use. The most-recently-used entry is the first element in the list. If a
+new inode needs to be cached, but the inode cache is full, the
+least-recently-used entry is freed to make room for the new one. The
+following operations cause an inode to be cached:
+ * Querying a file's length.
+ * Seeking within a file.
+ * Reading from a file.
+ * Writing to a file.
+
+The following operations cause a data block to be cached:
+ * Reading from the block.
+ * Writing to the block.
+
+If one of the above operations is applied to a data block that is not currently
+cached, nffs uses the following procedure to cache the necessary block:
+ 1. If none of the owning inode's blocks are currently cached, allocate a
+ cached block entry corresponding to the requested block and insert it
+ into the inode's list.
+ 2. Else if the requested file offset is less than that of the first cached
+ block, bridge the gap between the inode's sequence of cached blocks and
+ the block that now needs to be cached. This is accomplished by caching
+ each block in the gap, finishing with the requested block.
+ 3. Else (the requested offset is beyond the end of the cache),
+ a. If the requested offset belongs to the block that immediately
+ follows the end of the cache, cache the block and append it to the
+ list.
+ b. Else, clear the cache, and populate it with the single entry
+ corresponding to the requested block.
+
+If the system is unable to allocate a cached block entry at any point during
+the above procedure, the system frees up other blocks currently in the cache.
+This is accomplished as follows:
+ 1. Iterate the inode cache in reverse (i.e., start with the
+ least-recently-used entry). For each entry:
+ a. If the entry's cached block list is empty, advance to the next
+ entry.
+ b. Else, free all the cached blocks in the entry's list.
+
+Because the system imposes a minimum block cache size of one, the above
+procedure will always reclaim at least one cache block entry. The above
+procedure may result in the freeing of the block list that belongs to the very
+inode being operated on. This is OK, as the final block to get cached is
+always the block being requested.
+
+
+*** CONFIGURATION
+The file system is configured by populating fields in a global structure.
+Each field in the structure corresponds to a setting. All configuration must
+be done prior to calling nffs_init(). The configuration structure is defined
+as follows:
+
+struct nffs_config {
+ /** Maximum number of inodes; default=1024. */
+ uint32_t nc_num_inodes;
+
+ /** Maximum number of data blocks; default=4096. */
+ uint32_t nc_num_blocks;
+
+ /** Maximum number of open files; default=4. */
+ uint32_t nc_num_files;
+
+ /** Inode cache size; default=4. */
+ uint32_t nc_num_cache_inodes;
+
+ /** Data block cache size; default=64. */
+ uint32_t nc_num_cache_blocks;
+};
+
+extern struct nffs_config nffs_config;
+
+Any fields that are set to 0 (or not set at all) inherit the corresponding
+default value. This means that it is impossible to configure any setting with
+a value of zero.
+
+
+*** INITIALIZATION
+
+There are two means of initializing an nffs file system:
+ (1) Restore an existing file system via detection.
+ (2) Create a new file system via formatting.
+
+Both methods require the user to describe how the flash memory is divided into
+areas. This is accomplished with an array of struct nffs_area_desc, defined as
+follows:
+
+struct nffs_area_desc {
+ uint32_t nad_offset; /* Flash offset of start of area. */
+ uint32_t nad_length; /* Size of area, in bytes. */
+};
+
+An array of area descriptors is terminated by an entry with a fad_length field
+of 0.
+
+One common initialization sequence is the following:
+
+ (1) Detect an nffs file system anywhere in flash.
+ (2) If no file system detected, format a new file system in a specific
+ region of flash.
+
+
+*** DETECTION
+
+The file system detection process consists of scanning a specified set of
+flash regions for valid nffs areas, and then populating the RAM representation
+of the file system with the detected objects. Detection is initiated with the
+following function:
+
+/**
+ * Searches for a valid nffs file system among the specified areas. This
+ * function succeeds if a file system is detected among any subset of the
+ * supplied areas. If the area set does not contain a valid file system,
+ * a new one can be created via a separate call to nffs_format().
+ *
+ * @param area_descs The area set to search. This array must be
+ * terminated with a 0-length area.
+ *
+ * @return 0 on success;
+ * NFFS_ECORRUPT if no valid file system was detected;
+ * other nonzero on error.
+ */
+int nffs_detect(const struct nffs_area_desc *area_descs);
+
+As indicated, not every area descriptor needs to reference a valid nffs area.
+Detection is successful as long as a complete file system is detected
+somewhere in the specified regions of flash. If an application is unsure
+where a file system might be located, it can initiate detection across the
+entire flash region.
+
+A detected file system is valid if:
+ (1) At least one non-scratch area is present.
+ (2) At least one scratch area is present (only the first gets used if
+ there is more than one).
+ (3) The root directory inode is present.
+
+During detection, each indicated region of flash is checked for a valid area
+header. The contents of each valid non-scratch area are then restored into
+the nffs RAM representation. The following procedure is applied to each object
+in the area:
+
+ (1) Verify the object's integrity via a crc16 check. If invalid, the
+ object is discarded and the procedure restarts on the next object in
+ the area.
+
+ (2) Convert the disk object into its corresponding RAM representation and
+ insert it into the hash table. If the object is an inode, its
+ reference count is initialized to 1, indicating ownership by its
+ parent directory.
+
+ (3) If an object with the same ID is already present, then one supersedes
+ the other. Accept the object with the greater sequence number and
+ discard the other.
+
+ (4) If the object references a nonexistant inode (parent directory in the
+ case of an inode; owning file in the case of a data block), insert a
+ temporary "dummy" inode into the hash table so that inter-object links
+ can be maintained until the absent inode is eventually restored. Dummy
+ inodes are identified by a reference count of 0.
+
+ (5) If a delete record for an inode is encountered, the inode's parent
+ pointer is set to null to indicate that it should be removed from RAM.
+
+If nffs encounters an object that cannot be identified (i.e., its magic number
+is not valid), it scans the remainder of the flash area for the next valid
+magic number. Upon encountering a valid object, nffs resumes the procedure
+described above.
+
+After all areas have been restored, a sweep is performed across the entire RAM
+representation so that invalid inodes can be deleted from memory.
+
+For each directory inode:
+ * If its reference count is 0 (i.e., it is a dummy), migrate its children
+ to the /lost+found directory, and delete it from the RAM representation.
+ This should only happen in the case of file system corruption.
+ * If its parent reference is null (i.e., it was deleted), delete it and all
+ its children from the RAM representation.
+
+For each file inode:
+ * If its reference count is 0 (i.e., it is a dummy), delete it from the RAM
+ representation. This should only happen in the case of file system
+ corruption. (We should try to migrate the file to the lost+found
+ directory in this case, as mentioned in the todo section).
+
+When an object is deleted during this sweep, it is only deleted from the RAM
+representation; nothing is written to disk.
+
+When objects are migrated to the lost+found directory, their parent inode
+reference is permanently updated on the disk.
+
+In addition, a single scratch area is identified during the detection process.
+The first area whose 'fda_id' value is set to 0xff is designated as the file
+system scratch area. If no valid scratch area is found, the cause could be
+that the system was restarted while a garbage collection cycle was in progress.
+Such a condition is identified by the presence of two areas with the same ID.
+In such a case, the shorter of the two areas is erased and designated as the
+scratch area.
+
+
+*** FORMATTING
+
+A new file system is created via formatting. Formatting is achieved via the
+following function:
+
+/**
+ * Erases all the specified areas and initializes them with a clean nffs
+ * file system.
+ *
+ * @param area_descs The set of areas to format.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+int nffs_format(const struct nffs_area_desc *area_descs);
+
+On success, an area header is written to each of the specified locations. The
+largest area in the set is designated as the initial scratch area.
+
+
+*** FLASH WRITES
+
+The nffs implementation always writes in a strictly sequential fashion within an
+area. For each area, the system keeps track of the current offset. Whenever
+an object gets written to an area, it gets written to that area's current
+offset, and the offset is increased by the object's disk size.
+
+When a write needs to be performed, the nffs implementation selects the
+appropriate destination area by iterating though each area until one with
+sufficient free space is encountered.
+
+There is no write buffering. Each call to a write function results in a write
+operation being sent to the flash hardware.
+
+
+*** NEW OBJECTS
+
+Whenever a new object is written to disk, it is assigned the following
+properties:
+ * ID: A unique value is selected from the 32-bit ID space, as appropriate
+ for the object's type.
+ * Sequence number: 0
+
+When a new file or directory is created, a corresponding inode is written to
+flash. Likewise, a new data block also results in the writing of a
+corresponding disk object.
+
+
+*** MOVING / RENAMING FILES AND DIRECTORIES
+
+When a file or directory is moved or renamed, its corresponding inode is
+rewritten to flash with the following properties:
+ * ID: Unchanged
+ * Sequence number: Previous value plus one.
+ * Parent inode: As specified by the move / rename operation.
+ * Filename: As specified by the move / rename operation.
+
+Because the inode's ID is unchanged, all dependent objects remain valid.
+
+
+*** UNLINKING FILES AND DIRECTORIES
+
+When a file or directory is unlinked from its parent directory, a deletion
+record for the unlinked inode gets written to flash. The deletion record is an
+inode with the following properties:
+ * ID: Unchanged
+ * Sequence number: Previous value plus one.
+ * Parent inode ID: NFFS_ID_NONE
+
+When an inode is unlinked, no deletion records need to be written for the
+inode's dependent objects (constituent data blocks or child inodes). During
+the next file system detection, it is recognized that the objects belong to
+a deleted inode, so they are not restored into the RAM representation.
+
+If a file has an open handle at the time it gets unlinked, application code
+can continued to use the file handle to read and write data. All files retain
+a reference count, and a file isn't deleted from the RAM representation until
+its reference code drops to 0. Any attempt to open an unlinked file fails,
+even if the file is referenced by other file handles.
+
+
+*** WRITING TO A FILE
+
+The following procedure is used whenever the application code writes to a file.
+First, if the write operation specifies too much data to fit into a single
+block, the operation is split into several separate write operations. Then,
+for each write operation:
+
+ (1) Determine which existing blocks the write operation overlaps
+ (n = number of overwritten blocks).
+
+ (2) If n = 0, this is an append operation. Write a data block with the
+ following properties:
+ * ID: New unique value.
+ * Sequence number: 0.
+
+ (3) Else (n > 1), this write overlaps existing data.
+ (a) For each block in [1, 2, ... n-1], write a new block
+ containing the updated contents. Each new block supersedes the
+ block it overwrites. That is, each block has the following
+ properties:
+ * ID: Unchanged
+ * Sequence number: Previous value plus one.
+
+ (b) Write the nth block. The nth block includes all appended data,
+ if any. As with the other blocks, its ID is unchanged and its
+ sequence number is incremented.
+
+Appended data can only be written to the end of the file. That is, "holes" are
+not supported.
+
+
+*** GARBAGE COLLECTION
+
+When the file system is too full to accomodate a write operation, the system
+must perform garbage collection to make room. The garbage collection
+procedure is described below:
+
+ (1) The non-scratch area with the lowest garbage collection sequence
+ number is selected as the "source area." If there are other areas
+ with the same sequence number, the one with the smallest flash offset
+ is selected.
+
+ (2) The source area's ID is written to the scratch area's header,
+ transforming it into a non-scratch ID. This former scratch area is now
+ known as the "destination area."
+
+ (3) The RAM representation is exhaustively searched for collectible
+ objects. The following procedure is applied to each inode in the
+ system:
+
+ (a) If the inode is resident in the source area, copy the inode record
+ to the destination area.
+
+ (b) If the inode is a file inode, walk the inode's list of data blocks,
+ starting with the last block in the file. Each block that is
+ resident in the source area is copied to the destination area. If
+ there is a run of two or more blocks that are resident in the
+ source area, they are consolidated and copied to the destination
+ area as a single new block (subject to the maximum block size
+ restriction).
+
+ (4) The source area is reformatted as a scratch sector (i.e., is is fully
+ erased, and its header is rewritten with an ID of 0xff). The area's
+ garbage collection sequence number is incremented prior to rewriting
+ the header. This area is now the new scratch sector.
+
+
+*** MISC
+
+ * RAM usage:
+ o 24 bytes per inode
+ o 12 bytes per data block
+ o 36 bytes per inode cache entry
+ o 32 bytes per data block cache entry
+ * Maximum filename size: 256 characters (no null terminator required)
+ * Disallowed filename characters: '/' and '\0'
+
+
+*** FUTURE ENHANCEMENTS
+
+ * API function to traverse a directory.
+ * Migrate corrupt files to the /lost+found directory during restore, rather
+ than discarding them from RAM.
+ * Error correction.
+ * Encryption.
+ * Compression.
+
+
+*** API
+
+struct nffs_file;
+
+/**
+ * Opens a file at the specified path. The result of opening a nonexistent
+ * file depends on the access flags specified. All intermediate directories
+ * must already exist.
+ *
+ * The mode strings passed to fopen() map to nffs_open()'s access flags as
+ * follows:
+ * "r" - NFFS_ACCESS_READ
+ * "r+" - NFFS_ACCESS_READ | NFFS_ACCESS_WRITE
+ * "w" - NFFS_ACCESS_WRITE | NFFS_ACCESS_TRUNCATE
+ * "w+" - NFFS_ACCESS_READ | NFFS_ACCESS_WRITE | NFFS_ACCESS_TRUNCATE
+ * "a" - NFFS_ACCESS_WRITE | NFFS_ACCESS_APPEND
+ * "a+" - NFFS_ACCESS_READ | NFFS_ACCESS_WRITE | NFFS_ACCESS_APPEND
+ *
+ * @param out_file On success, a pointer to the newly-created file
+ * handle gets written here.
+ * @param path The path of the file to open.
+ * @param access_flags Flags controlling file access; see above table.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_open(const char *path, uint8_t access_flags,
+ struct nffs_file **out_file);
+
+
+/**
+ * Closes the specified file and invalidates the file handle. If the file has
+ * already been unlinked, and this is the last open handle to the file, this
+ * operation causes the file to be deleted from disk.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_close(struct nffs_file *file);
+
+
+/**
+ * Positions a file's read and write pointer at the specified offset. The
+ * offset is expressed as the number of bytes from the start of the file (i.e.,
+ * seeking to offset 0 places the pointer at the first byte in the file).
+ *
+ * @param file The file to reposition.
+ * @param offset The 0-based file offset to seek to.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_seek(struct nffs_file *file, uint32_t offset);
+
+
+/**
+ * Retrieves the current read and write position of the specified open file.
+ *
+ * @param file The file to query.
+ *
+ * @return The file offset, in bytes.
+ */
+uint32_t nffs_getpos(const struct nffs_file *file);
+
+
+/**
+ * Retrieves the current length of the specified open file.
+ *
+ * @param file The file to query.
+ * @param out_len On success, the number of bytes in the file gets
+ * written here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_file_len(const struct nffs_file *file, uint32_t *out_len)
+
+
+/**
+ * Reads data from the specified file. If more data is requested than remains
+ * in the file, all available data is retrieved. Note: this type of short read
+ * results in a success return code.
+ *
+ * @param file The file to read from.
+ * @param len The number of bytes to attempt to read.
+ * @param out_data The destination buffer to read into.
+ * @param out_len On success, the number of bytes actually read gets
+ * written here. Pass null if you don't care.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_read(struct nffs_file *file, uint32_t len, void *out_data,
+ uint32_t *out_len)
+
+
+/**
+ * Writes the supplied data to the current offset of the specified file handle.
+ *
+ * @param file The file to write to.
+ * @param data The data to write.
+ * @param len The number of bytes to write.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_write(struct nffs_file *file, const void *data, int len);
+
+
+/**
+ * Unlinks the file or directory at the specified path. If the path refers to
+ * a directory, all the directory's descendants are recursively unlinked. Any
+ * open file handles refering to an unlinked file remain valid, and can be
+ * read from and written to.
+ *
+ * @path The path of the file or directory to unlink.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_unlink(const char *path);
+
+
+/**
+ * Performs a rename and / or move of the specified source path to the
+ * specified destination. The source path can refer to either a file or a
+ * directory. All intermediate directories in the destination path must
+ * already exist. If the source path refers to a file, the destination path
+ * must contain a full filename path, rather than just the new parent
+ * directory. If an object already exists at the specified destination path,
+ * this function causes it to be unlinked prior to the rename (i.e., the
+ * destination gets clobbered).
+ *
+ * @param from The source path.
+ * @param to The destination path.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+int nffs_rename(const char *from, const char *to);
+
+
+/**
+ * Creates the directory represented by the specified path. All intermediate
+ * directories must already exist. The specified path must start with a '/'
+ * character.
+ *
+ * @param path The directory to create.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+int nffs_mkdir(const char *path);
+
+
+/**
+ * Erases all the specified areas and initializes them with a clean nffs
+ * file system.
+ *
+ * @param area_descs The set of areas to format.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+int nffs_format(const struct nffs_area_desc *area_descs);
+
+
+/**
+ * Opens the directory at the specified path. The directory's contents can be
+ * read with subsequent calls to nffs_readdir(). When you are done with the
+ * directory handle, close it with nffs_closedir().
+ *
+ * Unlinking files from the directory while it is open may result in
+ * unpredictable behavior. New files can be created inside the directory.
+ *
+ * @param path The directory to open.
+ * @param out_dir On success, points to the directory handle.
+ *
+ * @return 0 on success;
+ * NFFS_ENOENT if the specified directory does not
+ * exist;
+ * other nonzero on error.
+ */
+int nffs_opendir(const char *path, struct nffs_dir **out_dir);
+
+
+/**
+ * Reads the next entry in an open directory.
+ *
+ * @param dir The directory handle to read from.
+ * @param out_dirent On success, points to the next child entry in
+ * the specified directory.
+ *
+ * @return 0 on success;
+ * NFFS_ENOENT if there are no more entries in the
+ * parent directory;
+ * other nonzero on error.
+ */
+int nffs_readdir(struct nffs_dir *dir, struct nffs_dirent **out_dirent);
+
+
+/**
+ * Closes the specified directory handle.
+ *
+ * @param dir The directory to close.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_closedir(struct nffs_dir *dir);
+
+
+/**
+ * Retrieves the filename of the specified directory entry. The retrieved
+ * filename is always null-terminated. To ensure enough space to hold the full
+ * filename plus a null-termintor, a destination buffer of size
+ * (NFFS_FILENAME_MAX_LEN + 1) should be used.
+ *
+ * @param dirent The directory entry to query.
+ * @param max_len The size of the "out_name" character buffer.
+ * @param out_name On success, the entry's filename is written
+ * here; always null-terminated.
+ * @param out_name_len On success, contains the actual length of the
+ * filename, NOT including the
+ * null-terminator.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int nffs_dirent_name(struct nffs_dirent *dirent, size_t max_len,
+ char *out_name, uint8_t *out_name_len);
+
+
+/**
+ * Tells you whether the specified directory entry is a sub-directory or a
+ * regular file.
+ *
+ * @param dirent The directory entry to query.
+ *
+ * @return 1: The entry is a directory;
+ * 0: The entry is a regular file.
+ */
+int nffs_dirent_is_dir(const struct nffs_dirent *dirent);
+
+/**
+ * Searches for a valid nffs file system among the specified areas. This
+ * function succeeds if a file system is detected among any subset of the
+ * supplied areas. If the area set does not contain a valid file system,
+ * a new one can be created via a call to nffs_format().
+ *
+ * @param area_descs The area set to search. This array must be
+ * terminated with a 0-length area.
+ *
+ * @return 0 on success;
+ * NFFS_ECORRUPT if no valid file system was detected;
+ * other nonzero on error.
+ */
+int nffs_detect(const struct nffs_area_desc *area_descs);
+
+
+/**
+ * Indicates whether a valid filesystem has been initialized, either via
+ * detection or formatting.
+ *
+ * @return 1 if a file system is present; 0 otherwise.
+ */
+int nffs_ready(void);
+
+
+/**
+ * Initializes the nffs memory and data structures. This must be called before
+ * any nffs operations are attempted.
+ *
+ * @return 0 on success; nonzero on error.
+ */
+int nffs_init(void);
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/egg.yml
----------------------------------------------------------------------
diff --git a/fs/nffs/egg.yml b/fs/nffs/egg.yml
new file mode 100644
index 0000000..6019839
--- /dev/null
+++ b/fs/nffs/egg.yml
@@ -0,0 +1,8 @@
+egg.name: fs/nffs
+egg.vers: 0.1
+egg.identities: NFFS
+egg.deps:
+ - fs/fs
+ - libs/os
+ - libs/testutil
+ - hw/hal
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/include/nffs/nffs.h
----------------------------------------------------------------------
diff --git a/fs/nffs/include/nffs/nffs.h b/fs/nffs/include/nffs/nffs.h
new file mode 100644
index 0000000..9854bc8
--- /dev/null
+++ b/fs/nffs/include/nffs/nffs.h
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 H_NFFS_
+#define H_NFFS_
+
+#include <stddef.h>
+#include <inttypes.h>
+
+#define NFFS_FILENAME_MAX_LEN 256 /* Does not require null terminator. */
+#define NFFS_MAX_AREAS 256
+
+struct nffs_config {
+ /** Maximum number of inodes; default=1024. */
+ uint32_t nc_num_inodes;
+
+ /** Maximum number of data blocks; default=4096. */
+ uint32_t nc_num_blocks;
+
+ /** Maximum number of open files; default=4. */
+ uint32_t nc_num_files;
+
+ /** Maximum number of open directories; default=4. */
+ uint32_t nc_num_dirs;
+
+ /** Inode cache size; default=4. */
+ uint32_t nc_num_cache_inodes;
+
+ /** Data block cache size; default=64. */
+ uint32_t nc_num_cache_blocks;
+};
+
+extern struct nffs_config nffs_config;
+
+struct nffs_area_desc {
+ uint32_t nad_offset; /* Flash offset of start of area. */
+ uint32_t nad_length; /* Size of area, in bytes. */
+ uint8_t nad_flash_id; /* Logical flash id */
+};
+
+int nffs_init(void);
+int nffs_detect(const struct nffs_area_desc *area_descs);
+int nffs_format(const struct nffs_area_desc *area_descs);
+int nffs_ready(void);
+
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/include/nffs/nffs_test.h
----------------------------------------------------------------------
diff --git a/fs/nffs/include/nffs/nffs_test.h b/fs/nffs/include/nffs/nffs_test.h
new file mode 100644
index 0000000..12d2ea4
--- /dev/null
+++ b/fs/nffs/include/nffs/nffs_test.h
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 H_NFFS_TEST_
+#define H_NFFS_TEST_
+
+int nffs_test_all(void);
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/crc16.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/crc16.c b/fs/nffs/src/crc16.c
new file mode 100644
index 0000000..85995c4
--- /dev/null
+++ b/fs/nffs/src/crc16.c
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Copyright 2001-2010 Georges Menie (www.menie.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 University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS AND CONTRIBUTORS 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.
+ */
+
+#include <inttypes.h>
+#include "crc16.h"
+
+/* CRC16 implementation acording to CCITT standards */
+
+static const uint16_t crc16tab[256]= {
+ 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
+ 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
+ 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
+ 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
+ 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
+ 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
+ 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
+ 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
+ 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
+ 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
+ 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
+ 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
+ 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
+ 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
+ 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
+ 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
+ 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
+ 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
+ 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
+ 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
+ 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
+ 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
+ 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
+ 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
+ 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
+ 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
+ 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
+ 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
+ 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
+ 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
+ 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
+ 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
+};
+
+uint16_t
+crc16_ccitt(uint16_t initial_crc, const void *buf, int len)
+{
+ const uint8_t *ptr;
+ uint16_t crc;
+ int counter;
+
+ crc = initial_crc;
+ ptr = buf;
+
+ for (counter = 0; counter < len; counter++) {
+ crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *ptr++)&0x00FF];
+ }
+
+ return crc;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/crc16.h
----------------------------------------------------------------------
diff --git a/fs/nffs/src/crc16.h b/fs/nffs/src/crc16.h
new file mode 100644
index 0000000..f0937ca
--- /dev/null
+++ b/fs/nffs/src/crc16.h
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+
+/*
+ * Copyright 2001-2010 Georges Menie (www.menie.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 University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS AND CONTRIBUTORS 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.
+ */
+
+#ifndef _CRC16_H_
+#define _CRC16_H_
+
+#include <inttypes.h>
+
+unsigned short crc16_ccitt(uint16_t initial_crc, const void *buf, int len);
+
+#endif /* _CRC16_H_ */
[10/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_restore.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_restore.c b/fs/nffs/src/nffs_restore.c
new file mode 100644
index 0000000..b94b5fb
--- /dev/null
+++ b/fs/nffs/src/nffs_restore.c
@@ -0,0 +1,1034 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "hal/hal_flash.h"
+#include "os/os_mempool.h"
+#include "os/os_malloc.h"
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+
+/**
+ * The size of the largest data block encountered during detection. This is
+ * used to ensure that the maximum block data size is not set lower than the
+ * size of an existing block.
+ */
+static uint16_t nffs_restore_largest_block_data_len;
+
+/**
+ * Checks that each block a chain of data blocks was properly restored.
+ *
+ * @param last_block_entry The entry corresponding to the last block in
+ * the chain.
+ *
+ * @return 0 if the block chain is OK;
+ * FS_ECORRUPT if corruption is detected;
+ * nonzero on other error.
+ */
+static int
+nffs_restore_validate_block_chain(struct nffs_hash_entry *last_block_entry)
+{
+ struct nffs_disk_block disk_block;
+ struct nffs_hash_entry *cur;
+ struct nffs_block block;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ cur = last_block_entry;
+
+ while (cur != NULL) {
+ nffs_flash_loc_expand(cur->nhe_flash_loc, &area_idx, &area_offset);
+
+ rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_block_from_hash_entry(&block, cur);
+ if (rc != 0) {
+ return rc;
+ }
+
+ cur = block.nb_prev;
+ }
+
+ return 0;
+}
+
+static void
+u32toa(char *dst, uint32_t val)
+{
+ uint8_t tmp;
+ int idx = 0;
+ int i;
+ int print = 0;
+
+ for (i = 0; i < 8; i++) {
+ tmp = val >> 28;
+ if (tmp || i == 7) {
+ print = 1;
+ }
+ if (tmp < 10) {
+ tmp += '0';
+ } else {
+ tmp += 'a' - 10;
+ }
+ if (print) {
+ dst[idx++] = tmp;
+ }
+ val <<= 4;
+ }
+ dst[idx++] = '\0';
+}
+
+/**
+ * If the specified inode entry is a dummy directory, this function moves
+ * all its children to the lost+found directory.
+ *
+ * @param inode_entry The parent inode to test and empty.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_migrate_orphan_children(struct nffs_inode_entry *inode_entry)
+{
+ struct nffs_inode_entry *lost_found_sub;
+ struct nffs_inode_entry *child_entry;
+ char buf[32];
+ int rc;
+
+ if (!nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)) {
+ /* Not a directory. */
+ return 0;
+ }
+
+ if (inode_entry->nie_refcnt != 0) {
+ /* Not a dummy. */
+ return 0;
+ }
+
+ if (SLIST_EMPTY(&inode_entry->nie_child_list)) {
+ /* No children to migrate. */
+ return 0;
+ }
+
+ /* Create a directory in lost+found to hold the dummy directory's
+ * contents.
+ */
+ strcpy(buf, "/lost+found/");
+ u32toa(&buf[strlen(buf)], inode_entry->nie_hash_entry.nhe_id);
+
+ rc = nffs_path_new_dir(buf, &lost_found_sub);
+ if (rc != 0 && rc != FS_EEXIST) {
+ return rc;
+ }
+
+ /* Move each child into the new subdirectory. */
+ while ((child_entry = SLIST_FIRST(&inode_entry->nie_child_list)) != NULL) {
+ rc = nffs_inode_rename(child_entry, lost_found_sub, NULL);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int
+nffs_restore_should_sweep_inode_entry(struct nffs_inode_entry *inode_entry,
+ int *out_should_sweep)
+{
+ struct nffs_inode inode;
+ int rc;
+
+ /* Determine if the inode is a dummy. Dummy inodes have a reference count
+ * of 0. If it is a dummy, increment its reference count back to 1 so that
+ * it can be properly deleted. The presence of a dummy inode during the
+ * final sweep step indicates file system corruption. If the inode is a
+ * directory, all its children should have been migrated to the /lost+found
+ * directory prior to this.
+ */
+ if (inode_entry->nie_refcnt == 0) {
+ *out_should_sweep = 1;
+ inode_entry->nie_refcnt++;
+ return 0;
+ }
+
+ /* Determine if the inode has been deleted. If an inode has no parent (and
+ * it isn't the root directory), it has been deleted from the disk and
+ * should be swept from the RAM representation.
+ */
+ if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ if (rc != 0) {
+ *out_should_sweep = 0;
+ return rc;
+ }
+
+ if (inode.ni_parent == NULL) {
+ *out_should_sweep = 1;
+ return 0;
+ }
+ }
+
+ /* If this is a file inode, verify that all of its constituent blocks are
+ * present.
+ */
+ if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
+ rc = nffs_restore_validate_block_chain(
+ inode_entry->nie_last_block_entry);
+ if (rc == FS_ECORRUPT) {
+ *out_should_sweep = 1;
+ return 0;
+ } else if (rc != 0) {
+ *out_should_sweep = 0;
+ return rc;
+ }
+ }
+
+ /* This is a valid inode; don't sweep it. */
+ *out_should_sweep = 0;
+ return 0;
+}
+
+static void
+nffs_restore_inode_from_dummy_entry(struct nffs_inode *out_inode,
+ struct nffs_inode_entry *inode_entry)
+{
+ memset(out_inode, 0, sizeof *out_inode);
+ out_inode->ni_inode_entry = inode_entry;
+}
+
+/**
+ * Performs a sweep of the RAM representation at the end of a successful
+ * restore. The sweep phase performs the following actions of each inode in
+ * the file system:
+ * 1. If the inode is a dummy directory, its children are migrated to the
+ * lost+found directory.
+ * 2. Else if the inode is a dummy file, it is fully deleted from RAM.
+ * 3. Else, a CRC check is performed on each of the inode's constituent
+ * blocks. If corruption is detected, the inode is fully deleted from
+ * RAM.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_restore_sweep(void)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_hash_entry *entry;
+ struct nffs_hash_entry *next;
+ struct nffs_hash_list *list;
+ struct nffs_inode inode;
+ int del;
+ int rc;
+ int i;
+
+ /* Iterate through every object in the hash table, deleting all inodes that
+ * should be removed.
+ */
+ for (i = 0; i < NFFS_HASH_SIZE; i++) {
+ list = nffs_hash + i;
+
+ entry = SLIST_FIRST(list);
+ while (entry != NULL) {
+ next = SLIST_NEXT(entry, nhe_next);
+ if (nffs_hash_id_is_inode(entry->nhe_id)) {
+ inode_entry = (struct nffs_inode_entry *)entry;
+
+ /* If this is a dummy inode directory, the file system is
+ * corrupted. Move the directory's children inodes to the
+ * lost+found directory.
+ */
+ rc = nffs_restore_migrate_orphan_children(inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Determine if this inode needs to be deleted. */
+ rc = nffs_restore_should_sweep_inode_entry(inode_entry, &del);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (del) {
+ if (inode_entry->nie_hash_entry.nhe_flash_loc ==
+ NFFS_FLASH_LOC_NONE) {
+
+ nffs_restore_inode_from_dummy_entry(&inode,
+ inode_entry);
+ } else {
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ /* Remove the inode and all its children from RAM. */
+ rc = nffs_inode_unlink_from_ram(&inode, &next);
+ if (rc != 0) {
+ return rc;
+ }
+ next = SLIST_FIRST(list);
+ }
+ }
+
+ entry = next;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Creates a dummy inode and inserts it into the hash table. A dummy inode is
+ * a temporary placeholder for a real inode that has not been restored yet.
+ * These are necessary so that the inter-object links can be maintained until
+ * the absent inode is eventually restored. Dummy inodes are identified by a
+ * reference count of 0.
+ *
+ * @param id The ID of the dummy inode to create.
+ * @param out_inode_entry On success, the dummy inode gets written here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_dummy_inode(uint32_t id,
+ struct nffs_inode_entry **out_inode_entry)
+{
+ struct nffs_inode_entry *inode_entry;
+
+ inode_entry = nffs_inode_entry_alloc();
+ if (inode_entry == NULL) {
+ return FS_ENOMEM;
+ }
+ inode_entry->nie_hash_entry.nhe_id = id;
+ inode_entry->nie_hash_entry.nhe_flash_loc = NFFS_FLASH_LOC_NONE;
+ inode_entry->nie_refcnt = 0;
+
+ nffs_hash_insert(&inode_entry->nie_hash_entry);
+
+ *out_inode_entry = inode_entry;
+
+ return 0;
+}
+
+/**
+ * Determines if an already-restored inode should be replaced by another inode
+ * just read from flash. This function should only be called if both inodes
+ * share the same ID. The existing inode gets replaced if:
+ * o It is a dummy inode.
+ * o Its sequence number is less than that of the new inode.
+ *
+ * @param old_inode_entry The already-restored inode to test.
+ * @param disk_inode The inode just read from flash.
+ * @param out_should_replace On success, 0=don't replace; 1=do replace.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_inode_gets_replaced(struct nffs_inode_entry *old_inode_entry,
+ const struct nffs_disk_inode *disk_inode,
+ int *out_should_replace)
+{
+ struct nffs_inode old_inode;
+ int rc;
+
+ assert(old_inode_entry->nie_hash_entry.nhe_id == disk_inode->ndi_id);
+
+ if (old_inode_entry->nie_refcnt == 0) {
+ *out_should_replace = 1;
+ return 0;
+ }
+
+ rc = nffs_inode_from_entry(&old_inode, old_inode_entry);
+ if (rc != 0) {
+ *out_should_replace = 0;
+ return rc;
+ }
+
+ if (old_inode.ni_seq < disk_inode->ndi_seq) {
+ *out_should_replace = 1;
+ return 0;
+ }
+
+ if (old_inode.ni_seq == disk_inode->ndi_seq) {
+ /* This is a duplicate of a previously-read inode. This should never
+ * happen.
+ */
+ *out_should_replace = 0;
+ return FS_ECORRUPT;
+ }
+
+ *out_should_replace = 0;
+ return 0;
+}
+
+/**
+ * Determines if the specified inode should be added to the RAM representation
+ * and adds it if appropriate.
+ *
+ * @param disk_inode The inode just read from flash.
+ * @param area_idx The index of the area containing the inode.
+ * @param area_offset The offset within the area of the inode.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_inode(const struct nffs_disk_inode *disk_inode, uint8_t area_idx,
+ uint32_t area_offset)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_inode_entry *parent;
+ struct nffs_inode inode;
+ int new_inode;
+ int do_add;
+ int rc;
+
+ new_inode = 0;
+
+ /* Check the inode's CRC. If the inode is corrupt, discard it. */
+ rc = nffs_crc_disk_inode_validate(disk_inode, area_idx, area_offset);
+ if (rc != 0) {
+ goto err;
+ }
+
+ inode_entry = nffs_hash_find_inode(disk_inode->ndi_id);
+ if (inode_entry != NULL) {
+ rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode,
+ &do_add);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (do_add) {
+ if (inode_entry->nie_hash_entry.nhe_flash_loc !=
+ NFFS_FLASH_LOC_NONE) {
+
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ if (inode.ni_parent != NULL) {
+ nffs_inode_remove_child(&inode);
+ }
+ }
+
+ inode_entry->nie_hash_entry.nhe_flash_loc =
+ nffs_flash_loc(area_idx, area_offset);
+ }
+ } else {
+ inode_entry = nffs_inode_entry_alloc();
+ if (inode_entry == NULL) {
+ rc = FS_ENOMEM;
+ goto err;
+ }
+ new_inode = 1;
+ do_add = 1;
+
+ inode_entry->nie_hash_entry.nhe_id = disk_inode->ndi_id;
+ inode_entry->nie_hash_entry.nhe_flash_loc =
+ nffs_flash_loc(area_idx, area_offset);
+
+ nffs_hash_insert(&inode_entry->nie_hash_entry);
+ }
+
+ if (do_add) {
+ inode_entry->nie_refcnt = 1;
+
+ if (disk_inode->ndi_parent_id != NFFS_ID_NONE) {
+ parent = nffs_hash_find_inode(disk_inode->ndi_parent_id);
+ if (parent == NULL) {
+ rc = nffs_restore_dummy_inode(disk_inode->ndi_parent_id,
+ &parent);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+
+ rc = nffs_inode_add_child(parent, inode_entry);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+
+
+ if (inode_entry->nie_hash_entry.nhe_id == NFFS_ID_ROOT_DIR) {
+ nffs_root_dir = inode_entry;
+ }
+ }
+
+ if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
+ if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_file_id) {
+ nffs_hash_next_file_id = inode_entry->nie_hash_entry.nhe_id + 1;
+ }
+ } else {
+ if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_dir_id) {
+ nffs_hash_next_dir_id = inode_entry->nie_hash_entry.nhe_id + 1;
+ }
+ }
+
+ return 0;
+
+err:
+ if (new_inode) {
+ nffs_inode_entry_free(inode_entry);
+ }
+ return rc;
+}
+
+/**
+ * Indicates whether the specified data block is superseded by the just-read
+ * disk data block. A data block supersedes another if its ID is equal and its
+ * sequence number is greater than that of the other block.
+ *
+ * @param out_should_replace On success, 0 or 1 gets written here, to
+ * indicate whether replacement should occur.
+ * @param old_block The data block which has already been read and
+ * converted to its RAM representation. This
+ * is the block that may be superseded.
+ * @param disk_block The disk data block that was just read from
+ * flash. This is the block which may
+ * supersede the other.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_block_gets_replaced(const struct nffs_block *old_block,
+ const struct nffs_disk_block *disk_block,
+ int *out_should_replace)
+{
+ assert(old_block->nb_hash_entry->nhe_id == disk_block->ndb_id);
+
+ if (old_block->nb_seq < disk_block->ndb_seq) {
+ *out_should_replace = 1;
+ return 0;
+ }
+
+ if (old_block->nb_seq == disk_block->ndb_seq) {
+ /* This is a duplicate of an previously-read inode. This should never
+ * happen.
+ */
+ return FS_ECORRUPT;
+ }
+
+ *out_should_replace = 0;
+ return 0;
+}
+
+/**
+ * Populates the nffs RAM state with the memory representation of the specified
+ * disk data block.
+ *
+ * @param disk_block The source disk block to insert.
+ * @param area_idx The ID of the area containing the block.
+ * @param area_offset The area_offset within the area of the block.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_block(const struct nffs_disk_block *disk_block, uint8_t area_idx,
+ uint32_t area_offset)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_hash_entry *entry;
+ struct nffs_block block;
+ int do_replace;
+ int new_block;
+ int rc;
+
+ new_block = 0;
+
+ /* Check the block's CRC. If the block is corrupt, discard it. If this
+ * block would have superseded another, the old block becomes current.
+ */
+ rc = nffs_crc_disk_block_validate(disk_block, area_idx, area_offset);
+ if (rc != 0) {
+ goto err;
+ }
+
+ entry = nffs_hash_find_block(disk_block->ndb_id);
+ if (entry != NULL) {
+ rc = nffs_block_from_hash_entry_no_ptrs(&block, entry);
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = nffs_restore_block_gets_replaced(&block, disk_block, &do_replace);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (!do_replace) {
+ /* The new block is superseded by the old; nothing to do. */
+ return 0;
+ }
+
+ nffs_block_delete_from_ram(entry);
+ }
+
+ entry = nffs_block_entry_alloc();
+ if (entry == NULL) {
+ rc = FS_ENOMEM;
+ goto err;
+ }
+ new_block = 1;
+ entry->nhe_id = disk_block->ndb_id;
+ entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
+
+ /* The block is ready to be inserted into the hash. */
+
+ inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);
+ if (inode_entry == NULL) {
+ rc = nffs_restore_dummy_inode(disk_block->ndb_inode_id, &inode_entry);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+
+ if (inode_entry->nie_last_block_entry == NULL ||
+ inode_entry->nie_last_block_entry->nhe_id == disk_block->ndb_prev_id) {
+
+ inode_entry->nie_last_block_entry = entry;
+ }
+
+ nffs_hash_insert(entry);
+
+ if (disk_block->ndb_id >= nffs_hash_next_block_id) {
+ nffs_hash_next_block_id = disk_block->ndb_id + 1;
+ }
+
+ /* Make sure the maximum block data size is not set lower than the size of
+ * an existing block.
+ */
+ if (disk_block->ndb_data_len > nffs_restore_largest_block_data_len) {
+ nffs_restore_largest_block_data_len = disk_block->ndb_data_len;
+ }
+
+ return 0;
+
+err:
+ if (new_block) {
+ nffs_block_entry_free(entry);
+ }
+ return rc;
+}
+
+/**
+ * Populates the nffs RAM state with the memory representation of the specified
+ * disk object.
+ *
+ * @param disk_object The source disk object to convert.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_object(const struct nffs_disk_object *disk_object)
+{
+ int rc;
+
+ switch (disk_object->ndo_type) {
+ case NFFS_OBJECT_TYPE_INODE:
+ rc = nffs_restore_inode(&disk_object->ndo_disk_inode,
+ disk_object->ndo_area_idx,
+ disk_object->ndo_offset);
+ break;
+
+ case NFFS_OBJECT_TYPE_BLOCK:
+ rc = nffs_restore_block(&disk_object->ndo_disk_block,
+ disk_object->ndo_area_idx,
+ disk_object->ndo_offset);
+ break;
+
+ default:
+ assert(0);
+ rc = FS_EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Reads a single disk object from flash.
+ *
+ * @param area_idx The area to read the object from.
+ * @param area_offset The offset within the area to read from.
+ * @param out_disk_object On success, the restored object gets written
+ * here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_disk_object(int area_idx, uint32_t area_offset,
+ struct nffs_disk_object *out_disk_object)
+{
+ uint32_t magic;
+ int rc;
+
+ rc = nffs_flash_read(area_idx, area_offset, &magic, sizeof magic);
+ if (rc != 0) {
+ return rc;
+ }
+
+ switch (magic) {
+ case NFFS_INODE_MAGIC:
+ out_disk_object->ndo_type = NFFS_OBJECT_TYPE_INODE;
+ rc = nffs_inode_read_disk(area_idx, area_offset,
+ &out_disk_object->ndo_disk_inode);
+ break;
+
+ case NFFS_BLOCK_MAGIC:
+ out_disk_object->ndo_type = NFFS_OBJECT_TYPE_BLOCK;
+ rc = nffs_block_read_disk(area_idx, area_offset,
+ &out_disk_object->ndo_disk_block);
+ break;
+
+ case 0xffffffff:
+ rc = FS_EEMPTY;
+ break;
+
+ default:
+ rc = FS_ECORRUPT;
+ break;
+ }
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ out_disk_object->ndo_area_idx = area_idx;
+ out_disk_object->ndo_offset = area_offset;
+
+ return 0;
+}
+
+/**
+ * Calculates the disk space occupied by the specified disk object.
+ *
+ * @param disk_object
+ */
+static int
+nffs_restore_disk_object_size(const struct nffs_disk_object *disk_object)
+{
+ switch (disk_object->ndo_type) {
+ case NFFS_OBJECT_TYPE_INODE:
+ return sizeof disk_object->ndo_disk_inode +
+ disk_object->ndo_disk_inode.ndi_filename_len;
+
+ case NFFS_OBJECT_TYPE_BLOCK:
+ return sizeof disk_object->ndo_disk_block +
+ disk_object->ndo_disk_block.ndb_data_len;
+
+ default:
+ assert(0);
+ return 1;
+ }
+}
+
+/**
+ * Reads the specified area from disk and loads its contents into the RAM
+ * representation.
+ *
+ * @param area_idx The index of the area to read.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_area_contents(int area_idx)
+{
+ struct nffs_disk_object disk_object;
+ struct nffs_area *area;
+ int rc;
+
+ area = nffs_areas + area_idx;
+
+ area->na_cur = sizeof (struct nffs_disk_area);
+ while (1) {
+ rc = nffs_restore_disk_object(area_idx, area->na_cur, &disk_object);
+ switch (rc) {
+ case 0:
+ /* Valid object; restore it into the RAM representation. */
+ nffs_restore_object(&disk_object);
+ area->na_cur += nffs_restore_disk_object_size(&disk_object);
+ break;
+
+ case FS_ECORRUPT:
+ /* Invalid object; keep scanning for a valid magic number. */
+ area->na_cur++;
+ break;
+
+ case FS_EEMPTY:
+ case FS_ERANGE:
+ /* End of disk encountered; area fully restored. */
+ return 0;
+
+ default:
+ return rc;
+ }
+ }
+}
+
+/**
+ * Reads and parses one area header. This function does not read the area's
+ * contents.
+ *
+ * @param out_is_scratch On success, 0 or 1 gets written here,
+ * indicating whether the area is a scratch
+ * area.
+ * @param area_offset The flash offset of the start of the area.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+static int
+nffs_restore_detect_one_area(uint8_t flash_id, uint32_t area_offset,
+ struct nffs_disk_area *out_disk_area)
+{
+ int rc;
+
+ rc = hal_flash_read(flash_id, area_offset, out_disk_area,
+ sizeof *out_disk_area);
+ if (rc != 0) {
+ return FS_HW_ERROR;
+ }
+
+ if (!nffs_area_magic_is_set(out_disk_area)) {
+ return FS_ECORRUPT;
+ }
+
+ return 0;
+}
+
+/**
+ * Repairs the effects of a corrupt scratch area. Scratch area corruption can
+ * occur when the system resets while a garbage collection cycle is in
+ * progress.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_restore_corrupt_scratch(void)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_hash_entry *entry;
+ struct nffs_hash_entry *next;
+ uint32_t area_offset;
+ uint16_t good_idx;
+ uint16_t bad_idx;
+ uint8_t area_idx;
+ int rc;
+ int i;
+
+ /* Search for a pair of areas with identical IDs. If found, these areas
+ * represent the source and destination areas of a garbage collection
+ * cycle. The shorter of the two areas was the destination area. Since
+ * the garbage collection cycle did not finish, the source area contains a
+ * more complete set of objects than the destination area.
+ *
+ * good_idx = index of source area.
+ * bad_idx = index of destination area; this will be turned into the
+ * scratch area.
+ */
+ rc = nffs_area_find_corrupt_scratch(&good_idx, &bad_idx);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Invalidate all objects resident in the bad area. */
+ for (i = 0; i < NFFS_HASH_SIZE; i++) {
+ entry = SLIST_FIRST(&nffs_hash[i]);
+ while (entry != NULL) {
+ next = SLIST_NEXT(entry, nhe_next);
+
+ nffs_flash_loc_expand(entry->nhe_flash_loc,
+ &area_idx, &area_offset);
+ if (area_idx == bad_idx) {
+ if (nffs_hash_id_is_block(entry->nhe_id)) {
+ rc = nffs_block_delete_from_ram(entry);
+ if (rc != 0) {
+ return rc;
+ }
+ } else {
+ inode_entry = (struct nffs_inode_entry *)entry;
+ inode_entry->nie_refcnt = 0;
+ }
+ }
+
+ entry = next;
+ }
+ }
+
+ /* Now that the objects in the scratch area have been invalidated, reload
+ * everything from the good area.
+ */
+ rc = nffs_restore_area_contents(good_idx);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Convert the bad area into a scratch area. */
+ rc = nffs_format_area(bad_idx, 1);
+ if (rc != 0) {
+ return rc;
+ }
+ nffs_scratch_area_idx = bad_idx;
+
+ return 0;
+}
+
+/**
+ * Searches for a valid nffs file system among the specified areas. This
+ * function succeeds if a file system is detected among any subset of the
+ * supplied areas. If the area set does not contain a valid file system,
+ * a new one can be created via a call to nffs_format().
+ *
+ * @param area_descs The area set to search. This array must be
+ * terminated with a 0-length area.
+ *
+ * @return 0 on success;
+ * FS_ECORRUPT if no valid file system was detected;
+ * other nonzero on error.
+ */
+int
+nffs_restore_full(const struct nffs_area_desc *area_descs)
+{
+ struct nffs_disk_area disk_area;
+ int cur_area_idx;
+ int use_area;
+ int rc;
+ int i;
+
+ /* Start from a clean state. */
+ rc = nffs_misc_reset();
+ if (rc) {
+ return rc;
+ }
+ nffs_restore_largest_block_data_len = 0;
+
+ /* Read each area from flash. */
+ for (i = 0; area_descs[i].nad_length != 0; i++) {
+ if (i > NFFS_MAX_AREAS) {
+ rc = FS_EINVAL;
+ goto err;
+ }
+
+ rc = nffs_restore_detect_one_area(area_descs[i].nad_flash_id,
+ area_descs[i].nad_offset,
+ &disk_area);
+ switch (rc) {
+ case 0:
+ use_area = 1;
+ break;
+
+ case FS_ECORRUPT:
+ use_area = 0;
+ break;
+
+ default:
+ goto err;
+ }
+
+ if (use_area) {
+ if (disk_area.nda_id == NFFS_AREA_ID_NONE &&
+ nffs_scratch_area_idx != NFFS_AREA_ID_NONE) {
+
+ /* Don't allow more than one scratch area. */
+ use_area = 0;
+ }
+ }
+
+ if (use_area) {
+ /* Populate RAM with a representation of this area. */
+ cur_area_idx = nffs_num_areas;
+
+ rc = nffs_misc_set_num_areas(nffs_num_areas + 1);
+ if (rc != 0) {
+ goto err;
+ }
+
+ nffs_areas[cur_area_idx].na_offset = area_descs[i].nad_offset;
+ nffs_areas[cur_area_idx].na_length = area_descs[i].nad_length;
+ nffs_areas[cur_area_idx].na_flash_id = area_descs[i].nad_flash_id;
+ nffs_areas[cur_area_idx].na_gc_seq = disk_area.nda_gc_seq;
+ nffs_areas[cur_area_idx].na_id = disk_area.nda_id;
+
+ if (disk_area.nda_id == NFFS_AREA_ID_NONE) {
+ nffs_areas[cur_area_idx].na_cur = NFFS_AREA_OFFSET_ID;
+ nffs_scratch_area_idx = cur_area_idx;
+ } else {
+ nffs_areas[cur_area_idx].na_cur =
+ sizeof (struct nffs_disk_area);
+ nffs_restore_area_contents(cur_area_idx);
+ }
+ }
+ }
+
+ /* All areas have been restored from flash. */
+
+ if (nffs_scratch_area_idx == NFFS_AREA_ID_NONE) {
+ /* No scratch area. The system may have been rebooted in the middle of
+ * a garbage collection cycle. Look for a candidate scratch area.
+ */
+ rc = nffs_restore_corrupt_scratch();
+ if (rc != 0) {
+ if (rc == FS_ENOENT) {
+ rc = FS_ECORRUPT;
+ }
+ goto err;
+ }
+ }
+
+ /* Ensure this file system contains a valid scratch area. */
+ rc = nffs_misc_validate_scratch();
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* Make sure the file system contains a valid root directory. */
+ rc = nffs_misc_validate_root_dir();
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* Ensure there is a "/lost+found" directory. */
+ rc = nffs_misc_create_lost_found_dir();
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* Delete from RAM any objects that were invalidated when subsequent areas
+ * were restored.
+ */
+ nffs_restore_sweep();
+
+ /* Set the maximum data block size according to the size of the smallest
+ * area.
+ */
+ rc = nffs_misc_set_max_block_data_len(nffs_restore_largest_block_data_len);
+ if (rc != 0) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ nffs_misc_reset();
+ return rc;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_write.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_write.c b/fs/nffs/src/nffs_write.c
new file mode 100644
index 0000000..3d5325b
--- /dev/null
+++ b/fs/nffs/src/nffs_write.c
@@ -0,0 +1,397 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include "testutil/testutil.h"
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+#include "crc16.h"
+
+static int
+nffs_write_fill_crc16_overwrite(struct nffs_disk_block *disk_block,
+ uint8_t src_area_idx, uint32_t src_area_offset,
+ uint16_t left_copy_len, uint16_t right_copy_len,
+ const void *new_data, uint16_t new_data_len)
+{
+ uint16_t block_off;
+ uint16_t crc16;
+ int rc;
+
+ block_off = 0;
+
+ crc16 = nffs_crc_disk_block_hdr(disk_block);
+ block_off += sizeof *disk_block;
+
+ /* Copy data from the start of the old block, in case the new data starts
+ * at a non-zero offset.
+ */
+ if (left_copy_len > 0) {
+ rc = nffs_crc_flash(crc16, src_area_idx, src_area_offset + block_off,
+ left_copy_len, &crc16);
+ if (rc != 0) {
+ return rc;
+ }
+ block_off += left_copy_len;
+ }
+
+ /* Write the new data into the data block. This may extend the block's
+ * length beyond its old value.
+ */
+ crc16 = crc16_ccitt(crc16, new_data, new_data_len);
+ block_off += new_data_len;
+
+ /* Copy data from the end of the old block, in case the new data doesn't
+ * extend to the end of the block.
+ */
+ if (right_copy_len > 0) {
+ rc = nffs_crc_flash(crc16, src_area_idx, src_area_offset + block_off,
+ right_copy_len, &crc16);
+ if (rc != 0) {
+ return rc;
+ }
+ block_off += right_copy_len;
+ }
+
+ assert(block_off == sizeof *disk_block + disk_block->ndb_data_len);
+
+ disk_block->ndb_crc16 = crc16;
+
+ return 0;
+}
+
+/**
+ * Overwrites an existing data block. The resulting block has the same ID as
+ * the old one, but it supersedes it with a greater sequence number.
+ *
+ * @param entry The data block to overwrite.
+ * @param left_copy_len The number of bytes of existing data to retain
+ * before the new data begins.
+ * @param new_data The new data to write to the block.
+ * @param new_data_len The number of new bytes to write to the block.
+ * If this value plus left_copy_len is less
+ * than the existing block's data length,
+ * previous data at the end of the block is
+ * retained.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_write_over_block(struct nffs_hash_entry *entry, uint16_t left_copy_len,
+ const void *new_data, uint16_t new_data_len)
+{
+ struct nffs_disk_block disk_block;
+ struct nffs_block block;
+ uint32_t src_area_offset;
+ uint32_t dst_area_offset;
+ uint16_t right_copy_len;
+ uint16_t block_off;
+ uint8_t src_area_idx;
+ uint8_t dst_area_idx;
+ int rc;
+
+ rc = nffs_block_from_hash_entry(&block, entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ assert(left_copy_len <= block.nb_data_len);
+
+ /* Determine how much old data at the end of the block needs to be
+ * retained. If the new data doesn't extend to the end of the block, the
+ * the rest of the block retains its old contents.
+ */
+ if (left_copy_len + new_data_len > block.nb_data_len) {
+ right_copy_len = 0;
+ } else {
+ right_copy_len = block.nb_data_len - left_copy_len - new_data_len;
+ }
+
+ block.nb_seq++;
+ block.nb_data_len = left_copy_len + new_data_len + right_copy_len;
+ nffs_block_to_disk(&block, &disk_block);
+
+ nffs_flash_loc_expand(entry->nhe_flash_loc,
+ &src_area_idx, &src_area_offset);
+
+ rc = nffs_write_fill_crc16_overwrite(&disk_block,
+ src_area_idx, src_area_offset,
+ left_copy_len, right_copy_len,
+ new_data, new_data_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_misc_reserve_space(sizeof disk_block + disk_block.ndb_data_len,
+ &dst_area_idx, &dst_area_offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ block_off = 0;
+
+ /* Write the block header. */
+ rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off,
+ &disk_block, sizeof disk_block);
+ if (rc != 0) {
+ return rc;
+ }
+ block_off += sizeof disk_block;
+
+ /* Copy data from the start of the old block, in case the new data starts
+ * at a non-zero offset.
+ */
+ if (left_copy_len > 0) {
+ rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off,
+ dst_area_idx, dst_area_offset + block_off,
+ left_copy_len);
+ if (rc != 0) {
+ return rc;
+ }
+ block_off += left_copy_len;
+ }
+
+ /* Write the new data into the data block. This may extend the block's
+ * length beyond its old value.
+ */
+ rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off,
+ new_data, new_data_len);
+ if (rc != 0) {
+ return rc;
+ }
+ block_off += new_data_len;
+
+ /* Copy data from the end of the old block, in case the new data doesn't
+ * extend to the end of the block.
+ */
+ if (right_copy_len > 0) {
+ rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off,
+ dst_area_idx, dst_area_offset + block_off,
+ right_copy_len);
+ if (rc != 0) {
+ return rc;
+ }
+ block_off += right_copy_len;
+ }
+
+ assert(block_off == sizeof disk_block + block.nb_data_len);
+
+ entry->nhe_flash_loc = nffs_flash_loc(dst_area_idx, dst_area_offset);
+
+ ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, dst_area_idx,
+ dst_area_offset) == 0);
+
+ return 0;
+}
+
+/**
+ * Appends a new block to an inode block chain.
+ *
+ * @param inode_entry The inode to append a block to.
+ * @param data The contents of the new block.
+ * @param len The number of bytes of data to write.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_write_append(struct nffs_cache_inode *cache_inode, const void *data,
+ uint16_t len)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_hash_entry *entry;
+ struct nffs_disk_block disk_block;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ entry = nffs_block_entry_alloc();
+ if (entry == NULL) {
+ return FS_ENOMEM;
+ }
+
+ inode_entry = cache_inode->nci_inode.ni_inode_entry;
+
+ disk_block.ndb_magic = NFFS_BLOCK_MAGIC;
+ disk_block.ndb_id = nffs_hash_next_block_id++;
+ disk_block.ndb_seq = 0;
+ disk_block.ndb_inode_id = inode_entry->nie_hash_entry.nhe_id;
+ if (inode_entry->nie_last_block_entry == NULL) {
+ disk_block.ndb_prev_id = NFFS_ID_NONE;
+ } else {
+ disk_block.ndb_prev_id = inode_entry->nie_last_block_entry->nhe_id;
+ }
+ disk_block.ndb_data_len = len;
+ nffs_crc_disk_block_fill(&disk_block, data);
+
+ rc = nffs_block_write_disk(&disk_block, data, &area_idx, &area_offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ entry->nhe_id = disk_block.ndb_id;
+ entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
+ nffs_hash_insert(entry);
+
+ inode_entry->nie_last_block_entry = entry;
+
+ /* Update cached inode with the new file size. */
+ cache_inode->nci_file_size += len;
+
+ /* Add appended block to the cache. */
+ nffs_cache_seek(cache_inode, cache_inode->nci_file_size - 1, NULL);
+
+ return 0;
+}
+
+/**
+ * Performs a single write operation. The data written must be no greater
+ * than the maximum block data length. If old data gets overwritten, then
+ * the existing data blocks are superseded as necessary.
+ *
+ * @param write_info Describes the write operation being perfomred.
+ * @param inode_entry The file inode to write to.
+ * @param data The new data to write.
+ * @param data_len The number of bytes of new data to write.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_write_chunk(struct nffs_cache_inode *cache_inode, uint32_t file_offset,
+ const void *data, uint16_t data_len)
+{
+ struct nffs_cache_block *cache_block;
+ uint32_t append_len;
+ uint32_t data_offset;
+ uint32_t block_end;
+ uint32_t dst_off;
+ uint16_t chunk_off;
+ uint16_t chunk_sz;
+ int rc;
+
+ assert(data_len <= nffs_block_max_data_sz);
+
+ /** Handle the simple append case first. */
+ if (file_offset == cache_inode->nci_file_size) {
+ rc = nffs_write_append(cache_inode, data, data_len);
+ return rc;
+ }
+
+ /** This is not an append; i.e., old data is getting overwritten. */
+
+ dst_off = file_offset + data_len;
+ data_offset = data_len;
+ cache_block = NULL;
+
+ if (dst_off > cache_inode->nci_file_size) {
+ append_len = dst_off - cache_inode->nci_file_size;
+ } else {
+ append_len = 0;
+ }
+
+ do {
+ if (cache_block == NULL) {
+ rc = nffs_cache_seek(cache_inode, dst_off - 1, &cache_block);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ if (cache_block->ncb_file_offset < file_offset) {
+ chunk_off = file_offset - cache_block->ncb_file_offset;
+ } else {
+ chunk_off = 0;
+ }
+
+ chunk_sz = cache_block->ncb_block.nb_data_len - chunk_off;
+ block_end = cache_block->ncb_file_offset +
+ cache_block->ncb_block.nb_data_len;
+ if (block_end != dst_off) {
+ chunk_sz += (int)(dst_off - block_end);
+ }
+
+ data_offset = cache_block->ncb_file_offset + chunk_off - file_offset;
+ rc = nffs_write_over_block(cache_block->ncb_block.nb_hash_entry,
+ chunk_off, data + data_offset, chunk_sz);
+ if (rc != 0) {
+ return rc;
+ }
+
+ dst_off -= chunk_sz;
+ cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list, ncb_link);
+ } while (data_offset > 0);
+
+ cache_inode->nci_file_size += append_len;
+ return 0;
+}
+
+/**
+ * Writes a chunk of contiguous data to a file.
+ *
+ * @param file The file to write to.
+ * @param data The data to write.
+ * @param len The length of data to write.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_write_to_file(struct nffs_file *file, const void *data, int len)
+{
+ struct nffs_cache_inode *cache_inode;
+ const uint8_t *data_ptr;
+ uint16_t chunk_size;
+ int rc;
+
+ if (!(file->nf_access_flags & FS_ACCESS_WRITE)) {
+ return FS_EACCESS;
+ }
+
+ if (len == 0) {
+ return 0;
+ }
+
+ rc = nffs_cache_inode_ensure(&cache_inode, file->nf_inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* The append flag forces all writes to the end of the file, regardless of
+ * seek position.
+ */
+ if (file->nf_access_flags & FS_ACCESS_APPEND) {
+ file->nf_offset = cache_inode->nci_file_size;
+ }
+
+ /* Write data as a sequence of blocks. */
+ data_ptr = data;
+ while (len > 0) {
+ if (len > nffs_block_max_data_sz) {
+ chunk_size = nffs_block_max_data_sz;
+ } else {
+ chunk_size = len;
+ }
+
+ rc = nffs_write_chunk(cache_inode, file->nf_offset, data_ptr,
+ chunk_size);
+ if (rc != 0) {
+ return rc;
+ }
+
+ len -= chunk_size;
+ data_ptr += chunk_size;
+ file->nf_offset += chunk_size;
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/test/arch/cortex_m4/nffs_test.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/test/arch/cortex_m4/nffs_test.c b/fs/nffs/src/test/arch/cortex_m4/nffs_test.c
new file mode 100644
index 0000000..f15447d
--- /dev/null
+++ b/fs/nffs/src/test/arch/cortex_m4/nffs_test.c
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+
+#include "nffs/nffs_test.h"
+
+int
+nffs_test_all(void)
+{
+ return 0;
+}
[08/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/test/arch/sim/nffs_test_system_01.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/test/arch/sim/nffs_test_system_01.c b/fs/nffs/src/test/arch/sim/nffs_test_system_01.c
new file mode 100644
index 0000000..4d9cc03
--- /dev/null
+++ b/fs/nffs/src/test/arch/sim/nffs_test_system_01.c
@@ -0,0 +1,6534 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+
+/** Generated by makefs.rb */
+
+#include <stddef.h>
+#include "nffs_test_priv.h"
+
+const struct nffs_test_file_desc *nffs_test_system_01 =
+ (struct nffs_test_file_desc[]) {
+{
+.filename = "",
+.is_dir = 1,
+.children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl1dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl2dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl1dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl2dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl1dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl2dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl1dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl2dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl1dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl2dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+{ .filename = NULL, } }
+},
+};
+
+const struct nffs_test_file_desc *nffs_test_system_01_rm_1014_mk10 =
+ (struct nffs_test_file_desc[]) {
+{
+.filename = "",
+.is_dir = 1,
+.children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl1dir-0000",
+ .is_dir = 1,
+ },
+ {
+ .filename = "lvl1dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl2dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0001",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0003",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl3dir-0004",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+ { .filename = NULL, } }
+ },
+ { .filename = NULL, } }
+ },
+ {
+ .filename = "lvl2dir-0002",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl3dir-0000",
+ .is_dir = 1,
+ .children = (struct nffs_test_file_desc[]) {
+ {
+ .filename = "lvl4file-0000",
+ .contents = "0",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0001",
+ .contents = "1",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0002",
+ .contents = "2",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0003",
+ .contents = "3",
+ .contents_len = 1,
+ },
+ {
+ .filename = "lvl4file-0004",
+ .contents = "4",
+ .contents_len = 1,
+ },
+
<TRUNCATED>
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/todo.txt
----------------------------------------------------------------------
diff --git a/fs/nffs/todo.txt b/fs/nffs/todo.txt
new file mode 100644
index 0000000..629741b
--- /dev/null
+++ b/fs/nffs/todo.txt
@@ -0,0 +1,4 @@
+Long term:
+* ECC
+* Compression
+* Encryption
[06/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs.c b/libs/nffs/src/nffs.c
deleted file mode 100644
index 2aa3df4..0000000
--- a/libs/nffs/src/nffs.c
+++ /dev/null
@@ -1,692 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include "hal/hal_flash.h"
-#include "os/os_mempool.h"
-#include "os/os_mutex.h"
-#include "os/os_malloc.h"
-#include "nffs_priv.h"
-#include "nffs/nffs.h"
-#include "fs/fs_if.h"
-
-struct nffs_area *nffs_areas;
-uint8_t nffs_num_areas;
-uint8_t nffs_scratch_area_idx;
-uint16_t nffs_block_max_data_sz;
-
-struct os_mempool nffs_file_pool;
-struct os_mempool nffs_dir_pool;
-struct os_mempool nffs_inode_entry_pool;
-struct os_mempool nffs_block_entry_pool;
-struct os_mempool nffs_cache_inode_pool;
-struct os_mempool nffs_cache_block_pool;
-
-void *nffs_file_mem;
-void *nffs_inode_mem;
-void *nffs_block_entry_mem;
-void *nffs_cache_inode_mem;
-void *nffs_cache_block_mem;
-void *nffs_dir_mem;
-
-struct nffs_inode_entry *nffs_root_dir;
-struct nffs_inode_entry *nffs_lost_found_dir;
-
-static struct os_mutex nffs_mutex;
-
-static int nffs_open(const char *path, uint8_t access_flags,
- struct fs_file **out_file);
-static int nffs_close(struct fs_file *fs_file);
-static int nffs_read(struct fs_file *fs_file, uint32_t len, void *out_data,
- uint32_t *out_len);
-static int nffs_write(struct fs_file *fs_file, const void *data, int len);
-static int nffs_seek(struct fs_file *fs_file, uint32_t offset);
-static uint32_t nffs_getpos(const struct fs_file *fs_file);
-static int nffs_file_len(const struct fs_file *fs_file, uint32_t *out_len);
-static int nffs_unlink(const char *path);
-static int nffs_rename(const char *from, const char *to);
-static int nffs_mkdir(const char *path);
-static int nffs_opendir(const char *path, struct fs_dir **out_fs_dir);
-static int nffs_readdir(struct fs_dir *dir, struct fs_dirent **out_dirent);
-static int nffs_closedir(struct fs_dir *dir);
-static int nffs_dirent_name(const struct fs_dirent *fs_dirent, size_t max_len,
- char *out_name, uint8_t *out_name_len);
-static int nffs_dirent_is_dir(const struct fs_dirent *fs_dirent);
-
-static const struct fs_ops nffs_ops = {
- .f_open = nffs_open,
- .f_close = nffs_close,
- .f_read = nffs_read,
- .f_write = nffs_write,
-
- .f_seek = nffs_seek,
- .f_getpos = nffs_getpos,
- .f_filelen = nffs_file_len,
-
- .f_unlink = nffs_unlink,
- .f_rename = nffs_rename,
- .f_mkdir = nffs_mkdir,
-
- .f_opendir = nffs_opendir,
- .f_readdir = nffs_readdir,
- .f_closedir = nffs_closedir,
-
- .f_dirent_name = nffs_dirent_name,
- .f_dirent_is_dir = nffs_dirent_is_dir,
-
- .f_name = "nffs"
-};
-
-static void
-nffs_lock(void)
-{
- int rc;
-
- rc = os_mutex_pend(&nffs_mutex, 0xffffffff);
- assert(rc == 0 || rc == OS_NOT_STARTED);
-}
-
-static void
-nffs_unlock(void)
-{
- int rc;
-
- rc = os_mutex_release(&nffs_mutex);
- assert(rc == 0 || rc == OS_NOT_STARTED);
-}
-
-/**
- * Opens a file at the specified path. The result of opening a nonexistent
- * file depends on the access flags specified. All intermediate directories
- * must already exist.
- *
- * The mode strings passed to fopen() map to nffs_open()'s access flags as
- * follows:
- * "r" - FS_ACCESS_READ
- * "r+" - FS_ACCESS_READ | FS_ACCESS_WRITE
- * "w" - FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE
- * "w+" - FS_ACCESS_READ | FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE
- * "a" - FS_ACCESS_WRITE | FS_ACCESS_APPEND
- * "a+" - FS_ACCESS_READ | FS_ACCESS_WRITE | FS_ACCESS_APPEND
- *
- * @param path The path of the file to open.
- * @param access_flags Flags controlling file access; see above table.
- * @param out_file On success, a pointer to the newly-created file
- * handle gets written here.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_open(const char *path, uint8_t access_flags, struct fs_file **out_fs_file)
-{
- int rc;
- struct nffs_file *out_file;
-
- nffs_lock();
-
- if (!nffs_ready()) {
- rc = FS_EUNINIT;
- goto done;
- }
-
- rc = nffs_file_open(&out_file, path, access_flags);
- if (rc != 0) {
- goto done;
- }
- *out_fs_file = (struct fs_file *)out_file;
-done:
- nffs_unlock();
- if (rc != 0) {
- *out_fs_file = NULL;
- }
- return rc;
-}
-
-/**
- * Closes the specified file and invalidates the file handle. If the file has
- * already been unlinked, and this is the last open handle to the file, this
- * operation causes the file to be deleted from disk.
- *
- * @param file The file handle to close.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_close(struct fs_file *fs_file)
-{
- int rc;
- struct nffs_file *file = (struct nffs_file *)fs_file;
-
- if (file == NULL) {
- return 0;
- }
-
- nffs_lock();
- rc = nffs_file_close(file);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Positions a file's read and write pointer at the specified offset. The
- * offset is expressed as the number of bytes from the start of the file (i.e.,
- * seeking to offset 0 places the pointer at the first byte in the file).
- *
- * @param file The file to reposition.
- * @param offset The 0-based file offset to seek to.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_seek(struct fs_file *fs_file, uint32_t offset)
-{
- int rc;
- struct nffs_file *file = (struct nffs_file *)fs_file;
-
- nffs_lock();
- rc = nffs_file_seek(file, offset);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Retrieves the current read and write position of the specified open file.
- *
- * @param file The file to query.
- *
- * @return The file offset, in bytes.
- */
-static uint32_t
-nffs_getpos(const struct fs_file *fs_file)
-{
- uint32_t offset;
- const struct nffs_file *file = (const struct nffs_file *)fs_file;
-
- nffs_lock();
- offset = file->nf_offset;
- nffs_unlock();
-
- return offset;
-}
-
-/**
- * Retrieves the current length of the specified open file.
- *
- * @param file The file to query.
- * @param out_len On success, the number of bytes in the file gets
- * written here.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_file_len(const struct fs_file *fs_file, uint32_t *out_len)
-{
- int rc;
- const struct nffs_file *file = (const struct nffs_file *)fs_file;
-
- nffs_lock();
- rc = nffs_inode_data_len(file->nf_inode_entry, out_len);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Reads data from the specified file. If more data is requested than remains
- * in the file, all available data is retrieved. Note: this type of short read
- * results in a success return code.
- *
- * @param file The file to read from.
- * @param len The number of bytes to attempt to read.
- * @param out_data The destination buffer to read into.
- * @param out_len On success, the number of bytes actually read gets
- * written here. Pass null if you don't care.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_read(struct fs_file *fs_file, uint32_t len, void *out_data,
- uint32_t *out_len)
-{
- int rc;
- struct nffs_file *file = (struct nffs_file *)fs_file;
-
- nffs_lock();
- rc = nffs_file_read(file, len, out_data, out_len);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Writes the supplied data to the current offset of the specified file handle.
- *
- * @param file The file to write to.
- * @param data The data to write.
- * @param len The number of bytes to write.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_write(struct fs_file *fs_file, const void *data, int len)
-{
- int rc;
- struct nffs_file *file = (struct nffs_file *)fs_file;
-
- nffs_lock();
-
- if (!nffs_ready()) {
- rc = FS_EUNINIT;
- goto done;
- }
-
- rc = nffs_write_to_file(file, data, len);
- if (rc != 0) {
- goto done;
- }
-
- rc = 0;
-
-done:
- nffs_unlock();
- return rc;
-}
-
-/**
- * Unlinks the file or directory at the specified path. If the path refers to
- * a directory, all the directory's descendants are recursively unlinked. Any
- * open file handles refering to an unlinked file remain valid, and can be
- * read from and written to.
- *
- * @path The path of the file or directory to unlink.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_unlink(const char *path)
-{
- int rc;
-
- nffs_lock();
-
- if (!nffs_ready()) {
- rc = FS_EUNINIT;
- goto done;
- }
-
- rc = nffs_path_unlink(path);
- if (rc != 0) {
- goto done;
- }
-
- rc = 0;
-
-done:
- nffs_unlock();
- return rc;
-}
-
-/**
- * Performs a rename and / or move of the specified source path to the
- * specified destination. The source path can refer to either a file or a
- * directory. All intermediate directories in the destination path must
- * already exist. If the source path refers to a file, the destination path
- * must contain a full filename path, rather than just the new parent
- * directory. If an object already exists at the specified destination path,
- * this function causes it to be unlinked prior to the rename (i.e., the
- * destination gets clobbered).
- *
- * @param from The source path.
- * @param to The destination path.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-static int
-nffs_rename(const char *from, const char *to)
-{
- int rc;
-
- nffs_lock();
-
- if (!nffs_ready()) {
- rc = FS_EUNINIT;
- goto done;
- }
-
- rc = nffs_path_rename(from, to);
- if (rc != 0) {
- goto done;
- }
-
- rc = 0;
-
-done:
- nffs_unlock();
- return rc;
-}
-
-/**
- * Creates the directory represented by the specified path. All intermediate
- * directories must already exist. The specified path must start with a '/'
- * character.
- *
- * @param path The directory to create.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-static int
-nffs_mkdir(const char *path)
-{
- int rc;
-
- nffs_lock();
-
- if (!nffs_ready()) {
- rc = FS_EUNINIT;
- goto done;
- }
-
- rc = nffs_path_new_dir(path, NULL);
- if (rc != 0) {
- goto done;
- }
-
-done:
- nffs_unlock();
- return rc;
-}
-
-/**
- * Opens the directory at the specified path. The directory's contents can be
- * read with subsequent calls to nffs_readdir(). When you are done with the
- * directory handle, close it with nffs_closedir().
- *
- * Unlinking files from the directory while it is open may result in
- * unpredictable behavior. New files can be created inside the directory.
- *
- * @param path The directory to open.
- * @param out_dir On success, points to the directory handle.
- *
- * @return 0 on success;
- * FS_ENOENT if the specified directory does not
- * exist;
- * other nonzero on error.
- */
-static int
-nffs_opendir(const char *path, struct fs_dir **out_fs_dir)
-{
- int rc;
- struct nffs_dir **out_dir = (struct nffs_dir **)out_fs_dir;
-
- nffs_lock();
-
- if (!nffs_ready()) {
- rc = FS_EUNINIT;
- goto done;
- }
-
- rc = nffs_dir_open(path, out_dir);
-
-done:
- nffs_unlock();
- return rc;
-}
-
-/**
- * Reads the next entry in an open directory.
- *
- * @param dir The directory handle to read from.
- * @param out_dirent On success, points to the next child entry in
- * the specified directory.
- *
- * @return 0 on success;
- * FS_ENOENT if there are no more entries in the
- * parent directory;
- * other nonzero on error.
- */
-static int
-nffs_readdir(struct fs_dir *fs_dir, struct fs_dirent **out_fs_dirent)
-{
- int rc;
- struct nffs_dir *dir = (struct nffs_dir *)fs_dir;
- struct nffs_dirent **out_dirent = (struct nffs_dirent **)out_fs_dirent;
-
- nffs_lock();
- rc = nffs_dir_read(dir, out_dirent);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Closes the specified directory handle.
- *
- * @param dir The directory to close.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_closedir(struct fs_dir *fs_dir)
-{
- int rc;
- struct nffs_dir *dir = (struct nffs_dir *)fs_dir;
-
- nffs_lock();
- rc = nffs_dir_close(dir);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Retrieves the filename of the specified directory entry. The retrieved
- * filename is always null-terminated. To ensure enough space to hold the full
- * filename plus a null-termintor, a destination buffer of size
- * (NFFS_FILENAME_MAX_LEN + 1) should be used.
- *
- * @param dirent The directory entry to query.
- * @param max_len The size of the "out_name" character buffer.
- * @param out_name On success, the entry's filename is written
- * here; always null-terminated.
- * @param out_name_len On success, contains the actual length of the
- * filename, NOT including the
- * null-terminator.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_dirent_name(const struct fs_dirent *fs_dirent, size_t max_len,
- char *out_name, uint8_t *out_name_len)
-{
- int rc;
- struct nffs_dirent *dirent = (struct nffs_dirent *)fs_dirent;
-
- nffs_lock();
-
- assert(dirent != NULL && dirent->nde_inode_entry != NULL);
- rc = nffs_inode_read_filename(dirent->nde_inode_entry, max_len, out_name,
- out_name_len);
-
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Tells you whether the specified directory entry is a sub-directory or a
- * regular file.
- *
- * @param dirent The directory entry to query.
- *
- * @return 1: The entry is a directory;
- * 0: The entry is a regular file.
- */
-static int
-nffs_dirent_is_dir(const struct fs_dirent *fs_dirent)
-{
- uint32_t id;
- const struct nffs_dirent *dirent = (const struct nffs_dirent *)fs_dirent;
-
- nffs_lock();
-
- assert(dirent != NULL && dirent->nde_inode_entry != NULL);
- id = dirent->nde_inode_entry->nie_hash_entry.nhe_id;
-
- nffs_unlock();
-
- return nffs_hash_id_is_dir(id);
-}
-
-/**
- * Erases all the specified areas and initializes them with a clean nffs
- * file system.
- *
- * @param area_descs The set of areas to format.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-int
-nffs_format(const struct nffs_area_desc *area_descs)
-{
- int rc;
-
- nffs_lock();
- rc = nffs_format_full(area_descs);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Searches for a valid nffs file system among the specified areas. This
- * function succeeds if a file system is detected among any subset of the
- * supplied areas. If the area set does not contain a valid file system,
- * a new one can be created via a separate call to nffs_format().
- *
- * @param area_descs The area set to search. This array must be
- * terminated with a 0-length area.
- *
- * @return 0 on success;
- * FS_ECORRUPT if no valid file system was detected;
- * other nonzero on error.
- */
-int
-nffs_detect(const struct nffs_area_desc *area_descs)
-{
- int rc;
-
- nffs_lock();
- rc = nffs_restore_full(area_descs);
- nffs_unlock();
-
- return rc;
-}
-
-/**
- * Indicates whether a valid filesystem has been initialized, either via
- * detection or formatting.
- *
- * @return 1 if a file system is present; 0 otherwise.
- */
-int
-nffs_ready(void)
-{
- return nffs_root_dir != NULL;
-}
-
-/**
- * Initializes the nffs memory and data structures. This must be called before
- * any nffs operations are attempted.
- *
- * @return 0 on success; nonzero on error.
- */
-int
-nffs_init(void)
-{
- int rc;
-
- nffs_config_init();
-
- nffs_cache_clear();
-
- rc = os_mutex_init(&nffs_mutex);
- if (rc != 0) {
- return FS_EOS;
- }
-
- free(nffs_file_mem);
- nffs_file_mem = malloc(
- OS_MEMPOOL_BYTES(nffs_config.nc_num_files, sizeof (struct nffs_file)));
- if (nffs_file_mem == NULL) {
- return FS_ENOMEM;
- }
-
- free(nffs_inode_mem);
- nffs_inode_mem = malloc(
- OS_MEMPOOL_BYTES(nffs_config.nc_num_inodes,
- sizeof (struct nffs_inode_entry)));
- if (nffs_inode_mem == NULL) {
- return FS_ENOMEM;
- }
-
- free(nffs_block_entry_mem);
- nffs_block_entry_mem = malloc(
- OS_MEMPOOL_BYTES(nffs_config.nc_num_blocks,
- sizeof (struct nffs_hash_entry)));
- if (nffs_block_entry_mem == NULL) {
- return FS_ENOMEM;
- }
-
- free(nffs_cache_inode_mem);
- nffs_cache_inode_mem = malloc(
- OS_MEMPOOL_BYTES(nffs_config.nc_num_cache_inodes,
- sizeof (struct nffs_cache_inode)));
- if (nffs_cache_inode_mem == NULL) {
- return FS_ENOMEM;
- }
-
- free(nffs_cache_block_mem);
- nffs_cache_block_mem = malloc(
- OS_MEMPOOL_BYTES(nffs_config.nc_num_cache_blocks,
- sizeof (struct nffs_cache_block)));
- if (nffs_cache_block_mem == NULL) {
- return FS_ENOMEM;
- }
-
- free(nffs_dir_mem);
- nffs_dir_mem = malloc(
- OS_MEMPOOL_BYTES(nffs_config.nc_num_dirs,
- sizeof (struct nffs_dir)));
- if (nffs_dir_mem == NULL) {
- return FS_ENOMEM;
- }
-
- rc = nffs_misc_reset();
- if (rc != 0) {
- return rc;
- }
-
- fs_register(&nffs_ops);
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_area.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_area.c b/libs/nffs/src/nffs_area.c
deleted file mode 100644
index 82163db..0000000
--- a/libs/nffs/src/nffs_area.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "nffs_priv.h"
-#include "nffs/nffs.h"
-
-static void
-nffs_area_set_magic(struct nffs_disk_area *disk_area)
-{
- disk_area->nda_magic[0] = NFFS_AREA_MAGIC0;
- disk_area->nda_magic[1] = NFFS_AREA_MAGIC1;
- disk_area->nda_magic[2] = NFFS_AREA_MAGIC2;
- disk_area->nda_magic[3] = NFFS_AREA_MAGIC3;
-}
-
-int
-nffs_area_magic_is_set(const struct nffs_disk_area *disk_area)
-{
- return disk_area->nda_magic[0] == NFFS_AREA_MAGIC0 &&
- disk_area->nda_magic[1] == NFFS_AREA_MAGIC1 &&
- disk_area->nda_magic[2] == NFFS_AREA_MAGIC2 &&
- disk_area->nda_magic[3] == NFFS_AREA_MAGIC3;
-}
-
-int
-nffs_area_is_scratch(const struct nffs_disk_area *disk_area)
-{
- return nffs_area_magic_is_set(disk_area) &&
- disk_area->nda_id == NFFS_AREA_ID_NONE;
-}
-
-void
-nffs_area_to_disk(const struct nffs_area *area,
- struct nffs_disk_area *out_disk_area)
-{
- memset(out_disk_area, 0, sizeof *out_disk_area);
- nffs_area_set_magic(out_disk_area);
- out_disk_area->nda_length = area->na_length;
- out_disk_area->nda_ver = NFFS_AREA_VER;
- out_disk_area->nda_gc_seq = area->na_gc_seq;
- out_disk_area->nda_id = area->na_id;
-}
-
-uint32_t
-nffs_area_free_space(const struct nffs_area *area)
-{
- return area->na_length - area->na_cur;
-}
-
-/**
- * Finds a corrupt scratch area. An area is indentified as a corrupt scratch
- * area if it and another area share the same ID. Among two areas with the
- * same ID, the one with fewer bytes written is the corrupt scratch area.
- *
- * @param out_good_idx On success, the index of the good area (longer
- * of the two areas) gets written here.
- * @param out_bad_idx On success, the index of the corrupt scratch
- * area gets written here.
- *
- * @return 0 if a corrupt scratch area was identified;
- * FS_ENOENT if one was not found.
- */
-int
-nffs_area_find_corrupt_scratch(uint16_t *out_good_idx, uint16_t *out_bad_idx)
-{
- const struct nffs_area *iarea;
- const struct nffs_area *jarea;
- int i;
- int j;
-
- for (i = 0; i < nffs_num_areas; i++) {
- iarea = nffs_areas + i;
- for (j = i + 1; j < nffs_num_areas; j++) {
- jarea = nffs_areas + j;
-
- if (jarea->na_id == iarea->na_id) {
- /* Found a duplicate. The shorter of the two areas should be
- * used as scratch.
- */
- if (iarea->na_cur < jarea->na_cur) {
- *out_good_idx = j;
- *out_bad_idx = i;
- } else {
- *out_good_idx = i;
- *out_bad_idx = j;
- }
-
- return 0;
- }
- }
- }
-
- return FS_ENOENT;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_block.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_block.c b/libs/nffs/src/nffs_block.c
deleted file mode 100644
index 6221664..0000000
--- a/libs/nffs/src/nffs_block.c
+++ /dev/null
@@ -1,298 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <stddef.h>
-#include <assert.h>
-#include <string.h>
-#include "testutil/testutil.h"
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-#include "crc16.h"
-
-struct nffs_hash_entry *
-nffs_block_entry_alloc(void)
-{
- struct nffs_hash_entry *entry;
-
- entry = os_memblock_get(&nffs_block_entry_pool);
- if (entry != NULL) {
- memset(entry, 0, sizeof *entry);
- }
-
- return entry;
-}
-
-void
-nffs_block_entry_free(struct nffs_hash_entry *entry)
-{
- assert(nffs_hash_id_is_block(entry->nhe_id));
- os_memblock_put(&nffs_block_entry_pool, entry);
-}
-
-/**
- * Reads a data block header from flash.
- *
- * @param area_idx The index of the area to read from.
- * @param area_offset The offset within the area to read from.
- * @param out_disk_block On success, the block header is writteh here.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_block_read_disk(uint8_t area_idx, uint32_t area_offset,
- struct nffs_disk_block *out_disk_block)
-{
- int rc;
-
- rc = nffs_flash_read(area_idx, area_offset, out_disk_block,
- sizeof *out_disk_block);
- if (rc != 0) {
- return rc;
- }
- if (out_disk_block->ndb_magic != NFFS_BLOCK_MAGIC) {
- return FS_EUNEXP;
- }
-
- return 0;
-}
-
-/**
- * Writes the specified data block to a suitable location in flash.
- *
- * @param disk_block Points to the disk block to write.
- * @param data The contents of the data block.
- * @param out_area_idx On success, contains the index of the area
- * written to.
- * @param out_area_offset On success, contains the offset within the area
- * written to.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_block_write_disk(const struct nffs_disk_block *disk_block,
- const void *data,
- uint8_t *out_area_idx, uint32_t *out_area_offset)
-{
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- rc = nffs_misc_reserve_space(sizeof *disk_block + disk_block->ndb_data_len,
- &area_idx, &area_offset);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_flash_write(area_idx, area_offset, disk_block,
- sizeof *disk_block);
- if (rc != 0) {
- return rc;
- }
-
- if (disk_block->ndb_data_len > 0) {
- rc = nffs_flash_write(area_idx, area_offset + sizeof *disk_block,
- data, disk_block->ndb_data_len);
- if (rc != 0) {
- return rc;
- }
- }
-
- *out_area_idx = area_idx;
- *out_area_offset = area_offset;
-
- ASSERT_IF_TEST(nffs_crc_disk_block_validate(disk_block, area_idx,
- area_offset) == 0);
-
- return 0;
-}
-
-static void
-nffs_block_from_disk_no_ptrs(struct nffs_block *out_block,
- const struct nffs_disk_block *disk_block)
-{
- out_block->nb_seq = disk_block->ndb_seq;
- out_block->nb_inode_entry = NULL;
- out_block->nb_prev = NULL;
- out_block->nb_data_len = disk_block->ndb_data_len;
-}
-
-static int
-nffs_block_from_disk(struct nffs_block *out_block,
- const struct nffs_disk_block *disk_block,
- uint8_t area_idx, uint32_t area_offset)
-{
- nffs_block_from_disk_no_ptrs(out_block, disk_block);
-
- out_block->nb_inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);
- if (out_block->nb_inode_entry == NULL) {
- return FS_ECORRUPT;
- }
-
- if (disk_block->ndb_prev_id != NFFS_ID_NONE) {
- out_block->nb_prev = nffs_hash_find_block(disk_block->ndb_prev_id);
- if (out_block->nb_prev == NULL) {
- return FS_ECORRUPT;
- }
- }
-
- return 0;
-}
-
-/**
- * Constructs a disk-representation of the specified data block.
- *
- * @param block The source block to convert.
- * @param out_disk_block The disk block to write to.
- */
-void
-nffs_block_to_disk(const struct nffs_block *block,
- struct nffs_disk_block *out_disk_block)
-{
- assert(block->nb_inode_entry != NULL);
-
- out_disk_block->ndb_magic = NFFS_BLOCK_MAGIC;
- out_disk_block->ndb_id = block->nb_hash_entry->nhe_id;
- out_disk_block->ndb_seq = block->nb_seq;
- out_disk_block->ndb_inode_id =
- block->nb_inode_entry->nie_hash_entry.nhe_id;
- if (block->nb_prev == NULL) {
- out_disk_block->ndb_prev_id = NFFS_ID_NONE;
- } else {
- out_disk_block->ndb_prev_id = block->nb_prev->nhe_id;
- }
- out_disk_block->ndb_data_len = block->nb_data_len;
-}
-
-/**
- * Deletes the specified block entry from the nffs RAM representation.
- *
- * @param block_entry The block entry to delete.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_block_delete_from_ram(struct nffs_hash_entry *block_entry)
-{
- struct nffs_block block;
- int rc;
-
- rc = nffs_block_from_hash_entry(&block, block_entry);
- if (rc != 0) {
- return rc;
- }
-
- assert(block.nb_inode_entry != NULL);
- if (block.nb_inode_entry->nie_last_block_entry == block_entry) {
- block.nb_inode_entry->nie_last_block_entry = block.nb_prev;
- }
-
- nffs_hash_remove(block_entry);
- nffs_block_entry_free(block_entry);
-
- return 0;
-}
-
-/**
- * Constructs a full data block representation from the specified minimal
- * block entry. However, the resultant block's pointers are set to null,
- * rather than populated via hash table lookups. This behavior is useful when
- * the RAM representation has not been fully constructed yet.
- *
- * @param out_block On success, this gets populated with the data
- * block information.
- * @param block_entry The source block entry to convert.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_block_from_hash_entry_no_ptrs(struct nffs_block *out_block,
- struct nffs_hash_entry *block_entry)
-{
- struct nffs_disk_block disk_block;
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- assert(nffs_hash_id_is_block(block_entry->nhe_id));
-
- nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
- rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
- if (rc != 0) {
- return rc;
- }
-
- out_block->nb_hash_entry = block_entry;
- nffs_block_from_disk_no_ptrs(out_block, &disk_block);
-
- return 0;
-}
-
-/**
- * Constructs a full data block representation from the specified minimal block
- * entry. The resultant block's pointers are populated via hash table lookups.
- *
- * @param out_block On success, this gets populated with the data
- * block information.
- * @param block_entry The source block entry to convert.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_block_from_hash_entry(struct nffs_block *out_block,
- struct nffs_hash_entry *block_entry)
-{
- struct nffs_disk_block disk_block;
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- assert(nffs_hash_id_is_block(block_entry->nhe_id));
-
- nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
- rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
- if (rc != 0) {
- return rc;
- }
-
- out_block->nb_hash_entry = block_entry;
- rc = nffs_block_from_disk(out_block, &disk_block, area_idx, area_offset);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-int
-nffs_block_read_data(const struct nffs_block *block, uint16_t offset,
- uint16_t length, void *dst)
-{
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- nffs_flash_loc_expand(block->nb_hash_entry->nhe_flash_loc,
- &area_idx, &area_offset);
- area_offset += sizeof (struct nffs_disk_block);
- area_offset += offset;
-
- rc = nffs_flash_read(area_idx, area_offset, dst, length);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_cache.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_cache.c b/libs/nffs/src/nffs_cache.c
deleted file mode 100644
index 32a42eb..0000000
--- a/libs/nffs/src/nffs_cache.c
+++ /dev/null
@@ -1,445 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-
-TAILQ_HEAD(nffs_cache_inode_list, nffs_cache_inode);
-static struct nffs_cache_inode_list nffs_cache_inode_list =
- TAILQ_HEAD_INITIALIZER(nffs_cache_inode_list);
-
-static void nffs_cache_collect_blocks(void);
-
-static struct nffs_cache_block *
-nffs_cache_block_alloc(void)
-{
- struct nffs_cache_block *entry;
-
- entry = os_memblock_get(&nffs_cache_block_pool);
- if (entry != NULL) {
- memset(entry, 0, sizeof *entry);
- }
-
- return entry;
-}
-
-static void
-nffs_cache_block_free(struct nffs_cache_block *entry)
-{
- if (entry != NULL) {
- os_memblock_put(&nffs_cache_block_pool, entry);
- }
-}
-
-static struct nffs_cache_block *
-nffs_cache_block_acquire(void)
-{
- struct nffs_cache_block *cache_block;
-
- cache_block = nffs_cache_block_alloc();
- if (cache_block == NULL) {
- nffs_cache_collect_blocks();
- cache_block = nffs_cache_block_alloc();
- }
-
- assert(cache_block != NULL);
-
- return cache_block;
-}
-
-static int
-nffs_cache_block_populate(struct nffs_cache_block *cache_block,
- struct nffs_hash_entry *block_entry,
- uint32_t end_offset)
-{
- int rc;
-
- rc = nffs_block_from_hash_entry(&cache_block->ncb_block, block_entry);
- if (rc != 0) {
- return rc;
- }
-
- cache_block->ncb_file_offset = end_offset -
- cache_block->ncb_block.nb_data_len;
-
- return 0;
-}
-
-static struct nffs_cache_inode *
-nffs_cache_inode_alloc(void)
-{
- struct nffs_cache_inode *entry;
-
- entry = os_memblock_get(&nffs_cache_inode_pool);
- if (entry != NULL) {
- memset(entry, 0, sizeof *entry);
- entry->nci_block_list = (struct nffs_cache_block_list)
- TAILQ_HEAD_INITIALIZER(entry->nci_block_list);
- }
-
- return entry;
-}
-
-static void
-nffs_cache_inode_free_blocks(struct nffs_cache_inode *cache_inode)
-{
- struct nffs_cache_block *cache_block;
-
- while ((cache_block = TAILQ_FIRST(&cache_inode->nci_block_list)) != NULL) {
- TAILQ_REMOVE(&cache_inode->nci_block_list, cache_block, ncb_link);
- nffs_cache_block_free(cache_block);
- }
-}
-
-static void
-nffs_cache_inode_free(struct nffs_cache_inode *entry)
-{
- if (entry != NULL) {
- nffs_cache_inode_free_blocks(entry);
- os_memblock_put(&nffs_cache_inode_pool, entry);
- }
-}
-
-static struct nffs_cache_inode *
-nffs_cache_inode_acquire(void)
-{
- struct nffs_cache_inode *entry;
-
- entry = nffs_cache_inode_alloc();
- if (entry == NULL) {
- entry = TAILQ_LAST(&nffs_cache_inode_list, nffs_cache_inode_list);
- assert(entry != NULL);
-
- TAILQ_REMOVE(&nffs_cache_inode_list, entry, nci_link);
- nffs_cache_inode_free(entry);
-
- entry = nffs_cache_inode_alloc();
- }
-
- assert(entry != NULL);
-
- return entry;
-}
-
-static int
-nffs_cache_inode_populate(struct nffs_cache_inode *cache_inode,
- struct nffs_inode_entry *inode_entry)
-{
- int rc;
-
- memset(cache_inode, 0, sizeof *cache_inode);
-
- rc = nffs_inode_from_entry(&cache_inode->nci_inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_calc_data_length(cache_inode->nci_inode.ni_inode_entry,
- &cache_inode->nci_file_size);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Retrieves the block entry corresponding to the last cached block in the
- * specified inode's list. If the inode has no cached blocks, this function
- * returns null.
- */
-static struct nffs_hash_entry *
-nffs_cache_inode_last_entry(struct nffs_cache_inode *cache_inode)
-{
- struct nffs_cache_block *cache_block;
-
- if (TAILQ_EMPTY(&cache_inode->nci_block_list)) {
- return NULL;
- }
-
- cache_block = TAILQ_LAST(&cache_inode->nci_block_list,
- nffs_cache_block_list);
- return cache_block->ncb_block.nb_hash_entry;
-}
-
-static struct nffs_cache_inode *
-nffs_cache_inode_find(const struct nffs_inode_entry *inode_entry)
-{
- struct nffs_cache_inode *cur;
-
- TAILQ_FOREACH(cur, &nffs_cache_inode_list, nci_link) {
- if (cur->nci_inode.ni_inode_entry == inode_entry) {
- return cur;
- }
- }
-
- return NULL;
-}
-
-void
-nffs_cache_inode_range(const struct nffs_cache_inode *cache_inode,
- uint32_t *out_start, uint32_t *out_end)
-{
- struct nffs_cache_block *cache_block;
-
- cache_block = TAILQ_FIRST(&cache_inode->nci_block_list);
- if (cache_block == NULL) {
- *out_start = 0;
- *out_end = 0;
- return;
- }
-
- *out_start = cache_block->ncb_file_offset;
-
- cache_block = TAILQ_LAST(&cache_inode->nci_block_list,
- nffs_cache_block_list);
- *out_end = cache_block->ncb_file_offset +
- cache_block->ncb_block.nb_data_len;
-}
-
-static void
-nffs_cache_collect_blocks(void)
-{
- struct nffs_cache_inode *cache_inode;
-
- TAILQ_FOREACH_REVERSE(cache_inode, &nffs_cache_inode_list,
- nffs_cache_inode_list, nci_link) {
- if (!TAILQ_EMPTY(&cache_inode->nci_block_list)) {
- nffs_cache_inode_free_blocks(cache_inode);
- return;
- }
- }
-
- assert(0);
-}
-
-void
-nffs_cache_inode_delete(const struct nffs_inode_entry *inode_entry)
-{
- struct nffs_cache_inode *entry;
-
- entry = nffs_cache_inode_find(inode_entry);
- if (entry == NULL) {
- return;
- }
-
- TAILQ_REMOVE(&nffs_cache_inode_list, entry, nci_link);
- nffs_cache_inode_free(entry);
-}
-
-int
-nffs_cache_inode_ensure(struct nffs_cache_inode **out_cache_inode,
- struct nffs_inode_entry *inode_entry)
-{
- struct nffs_cache_inode *cache_inode;
- int rc;
-
- cache_inode = nffs_cache_inode_find(inode_entry);
- if (cache_inode != NULL) {
- rc = 0;
- goto done;
- }
-
- cache_inode = nffs_cache_inode_acquire();
- rc = nffs_cache_inode_populate(cache_inode, inode_entry);
- if (rc != 0) {
- goto done;
- }
-
- TAILQ_INSERT_HEAD(&nffs_cache_inode_list, cache_inode, nci_link);
-
- rc = 0;
-
-done:
- if (rc == 0) {
- *out_cache_inode = cache_inode;
- } else {
- nffs_cache_inode_free(cache_inode);
- *out_cache_inode = NULL;
- }
- return rc;
-}
-
-/**
- * Finds the data block containing the specified offset within a file inode.
- * If the block is not yet cached, it gets cached as a result of this
- * operation. This function modifies the inode's cached block list according
- * to the following procedure:
- *
- * 1. If none of the owning inode's blocks are currently cached, allocate a
- * cached block entry and insert it into the inode's list.
- * 2. Else if the requested file offset is less than that of the first cached
- * block, bridge the gap between the inode's sequence of cached blocks and
- * the block that now needs to be cached. This is accomplished by caching
- * each block in the gap, finishing with the requested block.
- * 3. Else (the requested offset is beyond the end of the cache),
- * a. If the requested offset belongs to the block that immediately
- * follows the end of the cache, cache the block and append it to the
- * list.
- * b. Else, clear the cache, and populate it with the single entry
- * corresponding to the requested block.
- *
- * @param cache_inode The cached file inode to seek within.
- * @param seek_offset The file offset to seek to.
- * @param out_cache_block On success, the requested cached block gets
- * written here; pass null if you don't need
- * this.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_cache_seek(struct nffs_cache_inode *cache_inode, uint32_t seek_offset,
- struct nffs_cache_block **out_cache_block)
-{
- struct nffs_cache_block *cache_block;
- struct nffs_hash_entry *last_cached_entry;
- struct nffs_hash_entry *block_entry;
- struct nffs_hash_entry *pred_entry;
- struct nffs_block block;
- uint32_t cache_start;
- uint32_t cache_end;
- uint32_t block_start;
- uint32_t block_end;
- int rc;
-
- /* Empty files have no blocks that can be cached. */
- if (cache_inode->nci_file_size == 0) {
- return FS_ENOENT;
- }
-
- nffs_cache_inode_range(cache_inode, &cache_start, &cache_end);
- if (cache_end != 0 && seek_offset < cache_start) {
- /* Seeking prior to cache. Iterate backwards from cache start. */
- cache_block = TAILQ_FIRST(&cache_inode->nci_block_list);
- block_entry = cache_block->ncb_block.nb_prev;
- block_end = cache_block->ncb_file_offset;
- cache_block = NULL;
- } else if (seek_offset < cache_end) {
- /* Seeking within cache. Iterate backwards from cache end. */
- cache_block = TAILQ_LAST(&cache_inode->nci_block_list,
- nffs_cache_block_list);
- block_entry = cache_block->ncb_block.nb_hash_entry;
- block_end = cache_end;
- } else {
- /* Seeking beyond end of cache. Iterate backwards from file end. If
- * sought-after block is adjacent to cache end, its cache entry will
- * get appended to the current cache. Otherwise, the current cache
- * will be freed and replaced with the single requested block.
- */
- cache_block = NULL;
- block_entry =
- cache_inode->nci_inode.ni_inode_entry->nie_last_block_entry;
- block_end = cache_inode->nci_file_size;
- }
-
- /* Scan backwards until we find the block containing the seek offest. */
- while (1) {
- if (block_end <= cache_start) {
- /* We are looking before the start of the cache. Allocate a new
- * cache block and prepend it to the cache.
- */
- assert(cache_block == NULL);
- cache_block = nffs_cache_block_acquire();
- rc = nffs_cache_block_populate(cache_block, block_entry,
- block_end);
- if (rc != 0) {
- return rc;
- }
-
- TAILQ_INSERT_HEAD(&cache_inode->nci_block_list, cache_block,
- ncb_link);
- }
-
- /* Calculate the file offset of the start of this block. This is used
- * to determine if this block contains the sought-after offset.
- */
- if (cache_block != NULL) {
- /* Current block is cached. */
- block_start = cache_block->ncb_file_offset;
- pred_entry = cache_block->ncb_block.nb_prev;
- } else {
- /* We are looking beyond the end of the cache. Read the data block
- * from flash.
- */
- rc = nffs_block_from_hash_entry(&block, block_entry);
- if (rc != 0) {
- return rc;
- }
-
- block_start = block_end - block.nb_data_len;
- pred_entry = block.nb_prev;
- }
-
- if (block_start <= seek_offset) {
- /* This block contains the requested address; iteration is
- * complete.
- */
- if (cache_block == NULL) {
- /* The block isn't cached, so it must come after the cache end.
- * Append it to the cache if it directly follows. Otherwise,
- * erase the current cache and populate it with this single
- * block.
- */
- cache_block = nffs_cache_block_acquire();
- cache_block->ncb_block = block;
- cache_block->ncb_file_offset = block_start;
-
- last_cached_entry = nffs_cache_inode_last_entry(cache_inode);
- if (last_cached_entry != NULL &&
- last_cached_entry == pred_entry) {
-
- TAILQ_INSERT_TAIL(&cache_inode->nci_block_list,
- cache_block, ncb_link);
- } else {
- nffs_cache_inode_free_blocks(cache_inode);
- TAILQ_INSERT_HEAD(&cache_inode->nci_block_list,
- cache_block, ncb_link);
- }
- }
-
- if (out_cache_block != NULL) {
- *out_cache_block = cache_block;
- }
- break;
- }
-
- /* Prepare for next iteration. */
- if (cache_block != NULL) {
- cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list,
- ncb_link);
- }
- block_entry = pred_entry;
- block_end = block_start;
- }
-
- return 0;
-}
-
-/**
- * Frees all cached inodes and blocks.
- */
-void
-nffs_cache_clear(void)
-{
- struct nffs_cache_inode *entry;
-
- while ((entry = TAILQ_FIRST(&nffs_cache_inode_list)) != NULL) {
- TAILQ_REMOVE(&nffs_cache_inode_list, entry, nci_link);
- nffs_cache_inode_free(entry);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_config.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_config.c b/libs/nffs/src/nffs_config.c
deleted file mode 100644
index f5efc60..0000000
--- a/libs/nffs/src/nffs_config.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include "nffs/nffs.h"
-
-struct nffs_config nffs_config;
-
-const struct nffs_config nffs_config_dflt = {
- .nc_num_inodes = 100,
- .nc_num_blocks = 100,
- .nc_num_files = 4,
- .nc_num_cache_inodes = 4,
- .nc_num_cache_blocks = 64,
- .nc_num_dirs = 4,
-};
-
-void
-nffs_config_init(void)
-{
- if (nffs_config.nc_num_inodes == 0) {
- nffs_config.nc_num_inodes = nffs_config_dflt.nc_num_inodes;
- }
- if (nffs_config.nc_num_blocks == 0) {
- nffs_config.nc_num_blocks = nffs_config_dflt.nc_num_blocks;
- }
- if (nffs_config.nc_num_files == 0) {
- nffs_config.nc_num_files = nffs_config_dflt.nc_num_files;
- }
- if (nffs_config.nc_num_cache_inodes == 0) {
- nffs_config.nc_num_cache_inodes = nffs_config_dflt.nc_num_cache_inodes;
- }
- if (nffs_config.nc_num_cache_blocks == 0) {
- nffs_config.nc_num_cache_blocks = nffs_config_dflt.nc_num_cache_blocks;
- }
- if (nffs_config.nc_num_dirs == 0) {
- nffs_config.nc_num_dirs = nffs_config_dflt.nc_num_dirs;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_crc.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_crc.c b/libs/nffs/src/nffs_crc.c
deleted file mode 100644
index 97d6b47..0000000
--- a/libs/nffs/src/nffs_crc.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-
-#include "nffs_priv.h"
-#include "crc16.h"
-
-int
-nffs_crc_flash(uint16_t initial_crc, uint8_t area_idx, uint32_t area_offset,
- uint32_t len, uint16_t *out_crc)
-{
- uint32_t chunk_len;
- uint16_t crc;
- int rc;
-
- crc = initial_crc;
-
- /* Copy data in chunks small enough to fit in the flash buffer. */
- while (len > 0) {
- if (len > sizeof nffs_flash_buf) {
- chunk_len = sizeof nffs_flash_buf;
- } else {
- chunk_len = len;
- }
-
- rc = nffs_flash_read(area_idx, area_offset, nffs_flash_buf, chunk_len);
- if (rc != 0) {
- return rc;
- }
-
- crc = crc16_ccitt(crc, nffs_flash_buf, chunk_len);
-
- area_offset += chunk_len;
- len -= chunk_len;
- }
-
- *out_crc = crc;
- return 0;
-}
-
-uint16_t
-nffs_crc_disk_block_hdr(const struct nffs_disk_block *disk_block)
-{
- uint16_t crc;
-
- crc = crc16_ccitt(0, disk_block, NFFS_DISK_BLOCK_OFFSET_CRC);
-
- return crc;
-}
-
-static int
-nffs_crc_disk_block(const struct nffs_disk_block *disk_block,
- uint8_t area_idx, uint32_t area_offset,
- uint16_t *out_crc)
-{
- uint16_t crc;
- int rc;
-
- crc = nffs_crc_disk_block_hdr(disk_block);
-
- rc = nffs_crc_flash(crc, area_idx, area_offset + sizeof *disk_block,
- disk_block->ndb_data_len, &crc);
- if (rc != 0) {
- return rc;
- }
-
- *out_crc = crc;
- return 0;
-}
-
-int
-nffs_crc_disk_block_validate(const struct nffs_disk_block *disk_block,
- uint8_t area_idx, uint32_t area_offset)
-{
- uint16_t crc;
- int rc;
-
- rc = nffs_crc_disk_block(disk_block, area_idx, area_offset, &crc);
- if (rc != 0) {
- return rc;
- }
-
- if (crc != disk_block->ndb_crc16) {
- return FS_ECORRUPT;
- }
-
- return 0;
-}
-
-void
-nffs_crc_disk_block_fill(struct nffs_disk_block *disk_block, const void *data)
-{
- uint16_t crc16;
-
- crc16 = nffs_crc_disk_block_hdr(disk_block);
- crc16 = crc16_ccitt(crc16, data, disk_block->ndb_data_len);
-
- disk_block->ndb_crc16 = crc16;
-}
-
-static uint16_t
-nffs_crc_disk_inode_hdr(const struct nffs_disk_inode *disk_inode)
-{
- uint16_t crc;
-
- crc = crc16_ccitt(0, disk_inode, NFFS_DISK_INODE_OFFSET_CRC);
-
- return crc;
-}
-
-static int
-nffs_crc_disk_inode(const struct nffs_disk_inode *disk_inode,
- uint8_t area_idx, uint32_t area_offset,
- uint16_t *out_crc)
-{
- uint16_t crc;
- int rc;
-
- crc = nffs_crc_disk_inode_hdr(disk_inode);
-
- rc = nffs_crc_flash(crc, area_idx, area_offset + sizeof *disk_inode,
- disk_inode->ndi_filename_len, &crc);
- if (rc != 0) {
- return rc;
- }
-
- *out_crc = crc;
- return 0;
-}
-
-int
-nffs_crc_disk_inode_validate(const struct nffs_disk_inode *disk_inode,
- uint8_t area_idx, uint32_t area_offset)
-{
- uint16_t crc;
- int rc;
-
- rc = nffs_crc_disk_inode(disk_inode, area_idx, area_offset, &crc);
- if (rc != 0) {
- return rc;
- }
-
- if (crc != disk_inode->ndi_crc16) {
- return FS_ECORRUPT;
- }
-
- return 0;
-}
-
-void
-nffs_crc_disk_inode_fill(struct nffs_disk_inode *disk_inode,
- const char *filename)
-{
- uint16_t crc16;
-
- crc16 = nffs_crc_disk_inode_hdr(disk_inode);
- crc16 = crc16_ccitt(crc16, filename, disk_inode->ndi_filename_len);
-
- disk_inode->ndi_crc16 = crc16;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_dir.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_dir.c b/libs/nffs/src/nffs_dir.c
deleted file mode 100644
index 59e37d2..0000000
--- a/libs/nffs/src/nffs_dir.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "nffs_priv.h"
-#include "nffs/nffs.h"
-
-static struct nffs_dir *
-nffs_dir_alloc(void)
-{
- struct nffs_dir *dir;
-
- dir = os_memblock_get(&nffs_dir_pool);
- if (dir != NULL) {
- memset(dir, 0, sizeof *dir);
- }
-
- return dir;
-}
-
-static int
-nffs_dir_free(struct nffs_dir *dir)
-{
- int rc;
-
- if (dir != NULL) {
- rc = os_memblock_put(&nffs_dir_pool, dir);
- if (rc != 0) {
- return FS_EOS;
- }
- }
-
- return 0;
-}
-
-int
-nffs_dir_open(const char *path, struct nffs_dir **out_dir)
-{
- struct nffs_inode_entry *parent_inode_entry;
- struct nffs_dir *dir;
- int rc;
-
- rc = nffs_path_find_inode_entry(path, &parent_inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- if (!nffs_hash_id_is_dir(parent_inode_entry->nie_hash_entry.nhe_id)) {
- return FS_EINVAL;
- }
-
- dir = nffs_dir_alloc();
- if (dir == NULL) {
- return FS_ENOMEM;
- }
-
- dir->nd_parent_inode_entry = parent_inode_entry;
- dir->nd_parent_inode_entry->nie_refcnt++;
- memset(&dir->nd_dirent, 0, sizeof dir->nd_dirent);
-
- *out_dir = dir;
-
- return 0;
-}
-
-int
-nffs_dir_read(struct nffs_dir *dir, struct nffs_dirent **out_dirent)
-{
- struct nffs_inode_entry *child;
- int rc;
-
- if (dir->nd_dirent.nde_inode_entry == NULL) {
- child = SLIST_FIRST(&dir->nd_parent_inode_entry->nie_child_list);
- } else {
- child = SLIST_NEXT(dir->nd_dirent.nde_inode_entry, nie_sibling_next);
- rc = nffs_inode_dec_refcnt(dir->nd_dirent.nde_inode_entry);
- if (rc != 0) {
- /* XXX: Need to clean up anything? */
- return rc;
- }
- }
- dir->nd_dirent.nde_inode_entry = child;
-
- if (child == NULL) {
- *out_dirent = NULL;
- return FS_ENOENT;
- }
-
- child->nie_refcnt++;
- *out_dirent = &dir->nd_dirent;
-
- return 0;
-}
-
-int
-nffs_dir_close(struct nffs_dir *dir)
-{
- int rc;
-
- if (dir == NULL) {
- return 0;
- }
-
- if (dir->nd_dirent.nde_inode_entry != NULL) {
- rc = nffs_inode_dec_refcnt(dir->nd_dirent.nde_inode_entry);
- if (rc != 0) {
- return rc;
- }
- }
-
- rc = nffs_inode_dec_refcnt(dir->nd_parent_inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_dir_free(dir);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_file.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_file.c b/libs/nffs/src/nffs_file.c
deleted file mode 100644
index b412730..0000000
--- a/libs/nffs/src/nffs_file.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "nffs_priv.h"
-#include "nffs/nffs.h"
-
-static struct nffs_file *
-nffs_file_alloc(void)
-{
- struct nffs_file *file;
-
- file = os_memblock_get(&nffs_file_pool);
- if (file != NULL) {
- memset(file, 0, sizeof *file);
- }
-
- return file;
-}
-
-static int
-nffs_file_free(struct nffs_file *file)
-{
- int rc;
-
- if (file != NULL) {
- rc = os_memblock_put(&nffs_file_pool, file);
- if (rc != 0) {
- return FS_EOS;
- }
- }
-
- return 0;
-}
-
-/**
- * Creates a new empty file and writes it to the file system. If a file with
- * the specified path already exists, the behavior is undefined.
- *
- * @param parent The parent directory to insert the new file in.
- * @param filename The name of the file to create.
- * @param filename_len The length of the filename, in characters.
- * @param is_dir 1 if this is a directory; 0 if it is a normal
- * file.
- * @param out_inode_entry On success, this points to the inode
- * corresponding to the new file.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_file_new(struct nffs_inode_entry *parent, const char *filename,
- uint8_t filename_len, int is_dir,
- struct nffs_inode_entry **out_inode_entry)
-{
- struct nffs_disk_inode disk_inode;
- struct nffs_inode_entry *inode_entry;
- uint32_t offset;
- uint8_t area_idx;
- int rc;
-
- inode_entry = nffs_inode_entry_alloc();
- if (inode_entry == NULL) {
- rc = FS_ENOMEM;
- goto err;
- }
-
- rc = nffs_misc_reserve_space(sizeof disk_inode + filename_len,
- &area_idx, &offset);
- if (rc != 0) {
- goto err;
- }
-
- memset(&disk_inode, 0xff, sizeof disk_inode);
- disk_inode.ndi_magic = NFFS_INODE_MAGIC;
- if (is_dir) {
- disk_inode.ndi_id = nffs_hash_next_dir_id++;
- } else {
- disk_inode.ndi_id = nffs_hash_next_file_id++;
- }
- disk_inode.ndi_seq = 0;
- if (parent == NULL) {
- disk_inode.ndi_parent_id = NFFS_ID_NONE;
- } else {
- disk_inode.ndi_parent_id = parent->nie_hash_entry.nhe_id;
- }
- disk_inode.ndi_filename_len = filename_len;
- nffs_crc_disk_inode_fill(&disk_inode, filename);
-
- rc = nffs_inode_write_disk(&disk_inode, filename, area_idx, offset);
- if (rc != 0) {
- goto err;
- }
-
- inode_entry->nie_hash_entry.nhe_id = disk_inode.ndi_id;
- inode_entry->nie_hash_entry.nhe_flash_loc =
- nffs_flash_loc(area_idx, offset);
- inode_entry->nie_refcnt = 1;
-
- if (parent != NULL) {
- rc = nffs_inode_add_child(parent, inode_entry);
- if (rc != 0) {
- goto err;
- }
- } else {
- assert(disk_inode.ndi_id == NFFS_ID_ROOT_DIR);
- }
-
- nffs_hash_insert(&inode_entry->nie_hash_entry);
- *out_inode_entry = inode_entry;
-
- return 0;
-
-err:
- nffs_inode_entry_free(inode_entry);
- return rc;
-}
-
-/**
- * Performs a file open operation.
- *
- * @param out_file On success, a pointer to the newly-created file
- * handle gets written here.
- * @param path The path of the file to open.
- * @param access_flags Flags controlling file access; see nffs_open() for
- * details.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_file_open(struct nffs_file **out_file, const char *path,
- uint8_t access_flags)
-{
- struct nffs_path_parser parser;
- struct nffs_inode_entry *parent;
- struct nffs_inode_entry *inode;
- struct nffs_file *file;
- int rc;
-
- file = NULL;
-
- /* Reject invalid access flag combinations. */
- if (!(access_flags & (FS_ACCESS_READ | FS_ACCESS_WRITE))) {
- rc = FS_EINVAL;
- goto err;
- }
- if (access_flags & (FS_ACCESS_APPEND | FS_ACCESS_TRUNCATE) &&
- !(access_flags & FS_ACCESS_WRITE)) {
-
- rc = FS_EINVAL;
- goto err;
- }
- if (access_flags & FS_ACCESS_APPEND &&
- access_flags & FS_ACCESS_TRUNCATE) {
-
- rc = FS_EINVAL;
- goto err;
- }
-
- file = nffs_file_alloc();
- if (file == NULL) {
- rc = FS_ENOMEM;
- goto err;
- }
-
- nffs_path_parser_new(&parser, path);
- rc = nffs_path_find(&parser, &inode, &parent);
- if (rc == FS_ENOENT && parser.npp_token_type == NFFS_PATH_TOKEN_LEAF) {
- /* The path is valid, but the file does not exist. This is an error
- * for read-only opens.
- */
- if (!(access_flags & FS_ACCESS_WRITE)) {
- goto err;
- }
-
- /* Make sure the parent directory exists. */
- if (parent == NULL) {
- goto err;
- }
-
- /* Create a new file at the specified path. */
- rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len, 0,
- &file->nf_inode_entry);
- if (rc != 0) {
- goto err;
- }
- } else if (rc == 0) {
- /* The file already exists. */
-
- /* Reject an attempt to open a directory. */
- if (nffs_hash_id_is_dir(inode->nie_hash_entry.nhe_id)) {
- rc = FS_EINVAL;
- goto err;
- }
-
- if (access_flags & FS_ACCESS_TRUNCATE) {
- /* The user is truncating the file. Unlink the old file and create
- * a new one in its place.
- */
- nffs_path_unlink(path);
- rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len,
- 0, &file->nf_inode_entry);
- if (rc != 0) {
- goto err;
- }
- } else {
- /* The user is not truncating the file. Point the file handle to
- * the existing inode.
- */
- file->nf_inode_entry = inode;
- }
- } else {
- /* Invalid path. */
- goto err;
- }
-
- if (access_flags & FS_ACCESS_APPEND) {
- rc = nffs_inode_data_len(file->nf_inode_entry, &file->nf_offset);
- if (rc != 0) {
- goto err;
- }
- } else {
- file->nf_offset = 0;
- }
- file->nf_inode_entry->nie_refcnt++;
- file->nf_access_flags = access_flags;
-
- *out_file = file;
-
- return 0;
-
-err:
- nffs_file_free(file);
- return rc;
-}
-
-/**
- * Positions a file's read and write pointer at the specified offset. The
- * offset is expressed as the number of bytes from the start of the file (i.e.,
- * seeking to 0 places the pointer at the first byte in the file).
- *
- * @param file The file to reposition.
- * @param offset The offset from the start of the file to seek to.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_file_seek(struct nffs_file *file, uint32_t offset)
-{
- uint32_t len;
- int rc;
-
- rc = nffs_inode_data_len(file->nf_inode_entry, &len);
- if (rc != 0) {
- return rc;
- }
-
- if (offset > len) {
- return FS_ERANGE;
- }
-
- file->nf_offset = offset;
- return 0;
-}
-
-/**
- * Reads data from the specified file. If more data is requested than remains
- * in the file, all available data is retrieved. Note: this type of short read
- * results in a success return code.
- *
- * @param file The file to read from.
- * @param len The number of bytes to attempt to read.
- * @param out_data The destination buffer to read into.
- * @param out_len On success, the number of bytes actually read gets
- * written here. Pass null if you don't care.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_file_read(struct nffs_file *file, uint32_t len, void *out_data,
- uint32_t *out_len)
-{
- uint32_t bytes_read;
- int rc;
-
- if (!nffs_ready()) {
- return FS_EUNINIT;
- }
-
- if (!(file->nf_access_flags & FS_ACCESS_READ)) {
- return FS_EACCESS;
- }
-
- rc = nffs_inode_read(file->nf_inode_entry, file->nf_offset, len, out_data,
- &bytes_read);
- if (rc != 0) {
- return rc;
- }
-
- file->nf_offset += bytes_read;
- if (out_len != NULL) {
- *out_len = bytes_read;
- }
-
- return 0;
-}
-
-/**
- * Closes the specified file and invalidates the file handle. If the file has
- * already been unlinked, and this is the last open handle to the file, this
- * operation causes the file to be deleted.
- *
- * @param file The file handle to close.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_file_close(struct nffs_file *file)
-{
- int rc;
-
- rc = nffs_inode_dec_refcnt(file->nf_inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_file_free(file);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_flash.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_flash.c b/libs/nffs/src/nffs_flash.c
deleted file mode 100644
index 94456b4..0000000
--- a/libs/nffs/src/nffs_flash.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include "hal/hal_flash.h"
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-
-/** A buffer used for flash reads; shared across all of nffs. */
-uint8_t nffs_flash_buf[NFFS_FLASH_BUF_SZ];
-
-/**
- * Reads a chunk of data from flash.
- *
- * @param area_idx The index of the area to read from.
- * @param area_offset The offset within the area to read from.
- * @param data On success, the flash contents are written
- * here.
- * @param len The number of bytes to read.
- *
- * @return 0 on success;
- * FS_ERANGE on an attempt to read an invalid
- * address range;
- * FS_HW_ERROR on flash error.
- */
-int
-nffs_flash_read(uint8_t area_idx, uint32_t area_offset, void *data,
- uint32_t len)
-{
- const struct nffs_area *area;
- int rc;
-
- assert(area_idx < nffs_num_areas);
-
- area = nffs_areas + area_idx;
-
- if (area_offset + len > area->na_length) {
- return FS_ERANGE;
- }
-
- rc = hal_flash_read(area->na_flash_id, area->na_offset + area_offset, data,
- len);
- if (rc != 0) {
- return FS_HW_ERROR;
- }
-
- return 0;
-}
-
-/**
- * Writes a chunk of data to flash.
- *
- * @param area_idx The index of the area to write to.
- * @param area_offset The offset within the area to write to.
- * @param data The data to write to flash.
- * @param len The number of bytes to write.
- *
- * @return 0 on success;
- * FS_ERANGE on an attempt to write to an
- * invalid address range, or on an attempt to
- * perform a non-strictly-sequential write;
- * FS_EFLASH_ERROR on flash error.
- */
-int
-nffs_flash_write(uint8_t area_idx, uint32_t area_offset, const void *data,
- uint32_t len)
-{
- struct nffs_area *area;
- int rc;
-
- assert(area_idx < nffs_num_areas);
- area = nffs_areas + area_idx;
-
- if (area_offset + len > area->na_length) {
- return FS_ERANGE;
- }
-
- if (area_offset < area->na_cur) {
- return FS_ERANGE;
- }
-
- rc = hal_flash_write(area->na_flash_id, area->na_offset + area_offset, data,
- len);
- if (rc != 0) {
- return FS_HW_ERROR;
- }
-
- area->na_cur = area_offset + len;
-
- return 0;
-}
-
-/**
- * Copies a chunk of data from one region of flash to another.
- *
- * @param area_idx_from The index of the area to copy from.
- * @param area_offset_from The offset within the area to copy from.
- * @param area_idx_to The index of the area to copy to.
- * @param area_offset_to The offset within the area to copy to.
- * @param len The number of bytes to copy.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_flash_copy(uint8_t area_idx_from, uint32_t area_offset_from,
- uint8_t area_idx_to, uint32_t area_offset_to,
- uint32_t len)
-{
- uint32_t chunk_len;
- int rc;
-
- /* Copy data in chunks small enough to fit in the flash buffer. */
- while (len > 0) {
- if (len > sizeof nffs_flash_buf) {
- chunk_len = sizeof nffs_flash_buf;
- } else {
- chunk_len = len;
- }
-
- rc = nffs_flash_read(area_idx_from, area_offset_from, nffs_flash_buf,
- chunk_len);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_flash_write(area_idx_to, area_offset_to, nffs_flash_buf,
- chunk_len);
- if (rc != 0) {
- return rc;
- }
-
- area_offset_from += chunk_len;
- area_offset_to += chunk_len;
- len -= chunk_len;
- }
-
- return 0;
-}
-
-/**
- * Compresses a flash-area-index,flash-area-offset pair into a 32-bit flash
- * location.
- */
-uint32_t
-nffs_flash_loc(uint8_t area_idx, uint32_t area_offset)
-{
- assert(area_offset <= 0x00ffffff);
- return area_idx << 24 | area_offset;
-}
-
-/**
- * Expands a compressed 32-bit flash location into a
- * flash-area-index,flash-area-offset pair.
- */
-void
-nffs_flash_loc_expand(uint32_t flash_loc, uint8_t *out_area_idx,
- uint32_t *out_area_offset)
-{
- *out_area_idx = flash_loc >> 24;
- *out_area_offset = flash_loc & 0x00ffffff;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_format.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_format.c b/libs/nffs/src/nffs_format.c
deleted file mode 100644
index 56a040f..0000000
--- a/libs/nffs/src/nffs_format.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "hal/hal_flash.h"
-#include "nffs_priv.h"
-#include "nffs/nffs.h"
-
-/**
- * Turns a scratch area into a non-scratch area. If the specified area is not
- * actually a scratch area, this function falls back to a slower full format
- * operation.
- */
-int
-nffs_format_from_scratch_area(uint8_t area_idx, uint8_t area_id)
-{
- struct nffs_disk_area disk_area;
- int rc;
-
- assert(area_idx < nffs_num_areas);
- rc = nffs_flash_read(area_idx, 0, &disk_area, sizeof disk_area);
- if (rc != 0) {
- return rc;
- }
-
- nffs_areas[area_idx].na_id = area_id;
- if (!nffs_area_is_scratch(&disk_area)) {
- rc = nffs_format_area(area_idx, 0);
- if (rc != 0) {
- return rc;
- }
- } else {
- disk_area.nda_id = area_id;
- rc = nffs_flash_write(area_idx, NFFS_AREA_OFFSET_ID,
- &disk_area.nda_id, sizeof disk_area.nda_id);
- if (rc != 0) {
- return rc;
- }
- }
-
- return 0;
-}
-
-/**
- * Formats a single scratch area.
- */
-int
-nffs_format_area(uint8_t area_idx, int is_scratch)
-{
- struct nffs_disk_area disk_area;
- struct nffs_area *area;
- uint32_t write_len;
- int rc;
-
- area = nffs_areas + area_idx;
-
- rc = hal_flash_erase(area->na_flash_id, area->na_offset, area->na_length);
- if (rc != 0) {
- return rc;
- }
- area->na_cur = 0;
-
- nffs_area_to_disk(area, &disk_area);
-
- if (is_scratch) {
- nffs_areas[area_idx].na_id = NFFS_AREA_ID_NONE;
- write_len = sizeof disk_area - sizeof disk_area.nda_id;
- } else {
- write_len = sizeof disk_area;
- }
-
- rc = nffs_flash_write(area_idx, 0, &disk_area.nda_magic, write_len);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Erases all the specified areas and initializes them with a clean nffs
- * file system.
- *
- * @param area_descs The set of areas to format.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-int
-nffs_format_full(const struct nffs_area_desc *area_descs)
-{
- int rc;
- int i;
-
- /* Start from a clean state. */
- nffs_misc_reset();
-
- /* Select largest area to be the initial scratch area. */
- nffs_scratch_area_idx = 0;
- for (i = 1; area_descs[i].nad_length != 0; i++) {
- if (i >= NFFS_MAX_AREAS) {
- rc = FS_EINVAL;
- goto err;
- }
-
- if (area_descs[i].nad_length >
- area_descs[nffs_scratch_area_idx].nad_length) {
-
- nffs_scratch_area_idx = i;
- }
- }
-
- rc = nffs_misc_set_num_areas(i);
- if (rc != 0) {
- goto err;
- }
-
- for (i = 0; i < nffs_num_areas; i++) {
- nffs_areas[i].na_offset = area_descs[i].nad_offset;
- nffs_areas[i].na_length = area_descs[i].nad_length;
- nffs_areas[i].na_flash_id = area_descs[i].nad_flash_id;
- nffs_areas[i].na_cur = 0;
- nffs_areas[i].na_gc_seq = 0;
-
- if (i == nffs_scratch_area_idx) {
- nffs_areas[i].na_id = NFFS_AREA_ID_NONE;
- } else {
- nffs_areas[i].na_id = i;
- }
-
- rc = nffs_format_area(i, i == nffs_scratch_area_idx);
- if (rc != 0) {
- goto err;
- }
- }
-
- rc = nffs_misc_validate_scratch();
- if (rc != 0) {
- goto err;
- }
-
- /* Create root directory. */
- rc = nffs_file_new(NULL, "", 0, 1, &nffs_root_dir);
- if (rc != 0) {
- goto err;
- }
-
- /* Create "lost+found" directory. */
- rc = nffs_misc_create_lost_found_dir();
- if (rc != 0) {
- goto err;
- }
-
- rc = nffs_misc_validate_root_dir();
- if (rc != 0) {
- goto err;
- }
-
- rc = nffs_misc_set_max_block_data_len(0);
- if (rc != 0) {
- goto err;
- }
-
- return 0;
-
-err:
- nffs_misc_reset();
- return rc;
-}
[03/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/test/arch/sim/nffs_test.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/test/arch/sim/nffs_test.c b/libs/nffs/src/test/arch/sim/nffs_test.c
deleted file mode 100644
index f2604d4..0000000
--- a/libs/nffs/src/test/arch/sim/nffs_test.c
+++ /dev/null
@@ -1,2441 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <errno.h>
-#include "hal/hal_flash.h"
-#include "testutil/testutil.h"
-#include "fs/fs.h"
-#include "nffs/nffs.h"
-#include "nffs/nffs_test.h"
-#include "nffs_test_priv.h"
-#include "nffs_priv.h"
-
-int flash_native_memset(uint32_t offset, uint8_t c, uint32_t len);
-
-static const struct nffs_area_desc nffs_area_descs[] = {
- { 0x00000000, 16 * 1024 },
- { 0x00004000, 16 * 1024 },
- { 0x00008000, 16 * 1024 },
- { 0x0000c000, 16 * 1024 },
- { 0x00010000, 64 * 1024 },
- { 0x00020000, 128 * 1024 },
- { 0x00040000, 128 * 1024 },
- { 0x00060000, 128 * 1024 },
- { 0x00080000, 128 * 1024 },
- { 0x000a0000, 128 * 1024 },
- { 0x000c0000, 128 * 1024 },
- { 0x000e0000, 128 * 1024 },
- { 0, 0 },
-};
-
-static void
-nffs_test_util_assert_ent_name(struct fs_dirent *dirent,
- const char *expected_name)
-{
- char name[NFFS_FILENAME_MAX_LEN + 1];
- uint8_t name_len;
- int rc;
-
- rc = fs_dirent_name(dirent, sizeof name, name, &name_len);
- TEST_ASSERT(rc == 0);
- if (rc == 0) {
- TEST_ASSERT(strcmp(name, expected_name) == 0);
- }
-}
-
-static void
-nffs_test_util_assert_file_len(struct fs_file *file, uint32_t expected)
-{
- uint32_t len;
- int rc;
-
- rc = fs_filelen(file, &len);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(len == expected);
-}
-
-static void
-nffs_test_util_assert_cache_is_sane(const char *filename)
-{
- struct nffs_cache_inode *cache_inode;
- struct nffs_cache_block *cache_block;
- struct fs_file *fs_file;
- struct nffs_file *file;
- uint32_t cache_start;
- uint32_t cache_end;
- uint32_t block_end;
- int rc;
-
- rc = fs_open(filename, FS_ACCESS_READ, &fs_file);
- TEST_ASSERT(rc == 0);
-
- file = (struct nffs_file *)fs_file;
- rc = nffs_cache_inode_ensure(&cache_inode, file->nf_inode_entry);
- TEST_ASSERT(rc == 0);
-
- nffs_cache_inode_range(cache_inode, &cache_start, &cache_end);
-
- if (TAILQ_EMPTY(&cache_inode->nci_block_list)) {
- TEST_ASSERT(cache_start == 0 && cache_end == 0);
- } else {
- block_end = 0; /* Pacify gcc. */
- TAILQ_FOREACH(cache_block, &cache_inode->nci_block_list, ncb_link) {
- if (cache_block == TAILQ_FIRST(&cache_inode->nci_block_list)) {
- TEST_ASSERT(cache_block->ncb_file_offset == cache_start);
- } else {
- /* Ensure no gap between this block and its predecessor. */
- TEST_ASSERT(cache_block->ncb_file_offset == block_end);
- }
-
- block_end = cache_block->ncb_file_offset +
- cache_block->ncb_block.nb_data_len;
- if (cache_block == TAILQ_LAST(&cache_inode->nci_block_list,
- nffs_cache_block_list)) {
-
- TEST_ASSERT(block_end == cache_end);
- }
- }
- }
-
- rc = fs_close(fs_file);
- TEST_ASSERT(rc == 0);
-}
-
-static void
-nffs_test_util_assert_contents(const char *filename, const char *contents,
- int contents_len)
-{
- struct fs_file *file;
- uint32_t bytes_read;
- void *buf;
- int rc;
-
- rc = fs_open(filename, FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == 0);
-
- buf = malloc(contents_len + 1);
- TEST_ASSERT(buf != NULL);
-
- rc = fs_read(file, contents_len + 1, buf, &bytes_read);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(bytes_read == contents_len);
- TEST_ASSERT(memcmp(buf, contents, contents_len) == 0);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- free(buf);
-
- nffs_test_util_assert_cache_is_sane(filename);
-}
-
-static int
-nffs_test_util_block_count(const char *filename)
-{
- struct nffs_hash_entry *entry;
- struct nffs_block block;
- struct nffs_file *file;
- struct fs_file *fs_file;
- int count;
- int rc;
-
- rc = fs_open(filename, FS_ACCESS_READ, &fs_file);
- TEST_ASSERT(rc == 0);
-
- file = (struct nffs_file *)fs_file;
- count = 0;
- entry = file->nf_inode_entry->nie_last_block_entry;
- while (entry != NULL) {
- count++;
- rc = nffs_block_from_hash_entry(&block, entry);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(block.nb_prev != entry);
- entry = block.nb_prev;
- }
-
- rc = fs_close(fs_file);
- TEST_ASSERT(rc == 0);
-
- return count;
-}
-
-static void
-nffs_test_util_assert_block_count(const char *filename, int expected_count)
-{
- TEST_ASSERT(nffs_test_util_block_count(filename) == expected_count);
-}
-
-static void
-nffs_test_util_assert_cache_range(const char *filename,
- uint32_t expected_cache_start,
- uint32_t expected_cache_end)
-{
- struct nffs_cache_inode *cache_inode;
- struct nffs_file *file;
- struct fs_file *fs_file;
- uint32_t cache_start;
- uint32_t cache_end;
- int rc;
-
- rc = fs_open(filename, FS_ACCESS_READ, &fs_file);
- TEST_ASSERT(rc == 0);
-
- file = (struct nffs_file *)fs_file;
- rc = nffs_cache_inode_ensure(&cache_inode, file->nf_inode_entry);
- TEST_ASSERT(rc == 0);
-
- nffs_cache_inode_range(cache_inode, &cache_start, &cache_end);
- TEST_ASSERT(cache_start == expected_cache_start);
- TEST_ASSERT(cache_end == expected_cache_end);
-
- rc = fs_close(fs_file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_cache_is_sane(filename);
-}
-
-static void
-nffs_test_util_create_file_blocks(const char *filename,
- const struct nffs_test_block_desc *blocks,
- int num_blocks)
-{
- struct fs_file *file;
- uint32_t total_len;
- uint32_t offset;
- char *buf;
- int num_writes;
- int rc;
- int i;
-
- rc = fs_open(filename, FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
- TEST_ASSERT(rc == 0);
-
- total_len = 0;
- if (num_blocks <= 0) {
- num_writes = 1;
- } else {
- num_writes = num_blocks;
- }
- for (i = 0; i < num_writes; i++) {
- rc = fs_write(file, blocks[i].data, blocks[i].data_len);
- TEST_ASSERT(rc == 0);
-
- total_len += blocks[i].data_len;
- }
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- buf = malloc(total_len);
- TEST_ASSERT(buf != NULL);
-
- offset = 0;
- for (i = 0; i < num_writes; i++) {
- memcpy(buf + offset, blocks[i].data, blocks[i].data_len);
- offset += blocks[i].data_len;
- }
- TEST_ASSERT(offset == total_len);
-
- nffs_test_util_assert_contents(filename, buf, total_len);
- if (num_blocks > 0) {
- nffs_test_util_assert_block_count(filename, num_blocks);
- }
-
- free(buf);
-}
-
-static void
-nffs_test_util_create_file(const char *filename, const char *contents,
- int contents_len)
-{
- struct nffs_test_block_desc block;
-
- block.data = contents;
- block.data_len = contents_len;
-
- nffs_test_util_create_file_blocks(filename, &block, 0);
-}
-
-static void
-nffs_test_util_append_file(const char *filename, const char *contents,
- int contents_len)
-{
- struct fs_file *file;
- int rc;
-
- rc = fs_open(filename, FS_ACCESS_WRITE | FS_ACCESS_APPEND, &file);
- TEST_ASSERT(rc == 0);
-
- rc = fs_write(file, contents, contents_len);
- TEST_ASSERT(rc == 0);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-}
-
-static void
-nffs_test_copy_area(const struct nffs_area_desc *from,
- const struct nffs_area_desc *to)
-{
- void *buf;
- int rc;
-
- TEST_ASSERT(from->nad_length == to->nad_length);
-
- buf = malloc(from->nad_length);
- TEST_ASSERT(buf != NULL);
-
- rc = hal_flash_read(from->nad_flash_id, from->nad_offset, buf,
- from->nad_length);
- TEST_ASSERT(rc == 0);
-
- rc = hal_flash_erase(from->nad_flash_id, to->nad_offset, to->nad_length);
- TEST_ASSERT(rc == 0);
-
- rc = hal_flash_write(to->nad_flash_id, to->nad_offset, buf, to->nad_length);
- TEST_ASSERT(rc == 0);
-
- free(buf);
-}
-
-static void
-nffs_test_util_create_subtree(const char *parent_path,
- const struct nffs_test_file_desc *elem)
-{
- char *path;
- int rc;
- int i;
-
- if (parent_path == NULL) {
- path = malloc(1);
- TEST_ASSERT(path != NULL);
- path[0] = '\0';
- } else {
- path = malloc(strlen(parent_path) + 1 + strlen(elem->filename) + 1);
- TEST_ASSERT(path != NULL);
-
- sprintf(path, "%s/%s", parent_path, elem->filename);
- }
-
- if (elem->is_dir) {
- if (parent_path != NULL) {
- rc = fs_mkdir(path);
- TEST_ASSERT(rc == 0);
- }
-
- if (elem->children != NULL) {
- for (i = 0; elem->children[i].filename != NULL; i++) {
- nffs_test_util_create_subtree(path, elem->children + i);
- }
- }
- } else {
- nffs_test_util_create_file(path, elem->contents, elem->contents_len);
- }
-
- free(path);
-}
-
-static void
-nffs_test_util_create_tree(const struct nffs_test_file_desc *root_dir)
-{
- nffs_test_util_create_subtree(NULL, root_dir);
-}
-
-#define NFFS_TEST_TOUCHED_ARR_SZ (16 * 1024)
-static struct nffs_hash_entry
- *nffs_test_touched_entries[NFFS_TEST_TOUCHED_ARR_SZ];
-static int nffs_test_num_touched_entries;
-
-static void
-nffs_test_assert_file(const struct nffs_test_file_desc *file,
- struct nffs_inode_entry *inode_entry,
- const char *path)
-{
- const struct nffs_test_file_desc *child_file;
- struct nffs_inode inode;
- struct nffs_inode_entry *child_inode_entry;
- char *child_path;
- int child_filename_len;
- int path_len;
- int rc;
-
- TEST_ASSERT(nffs_test_num_touched_entries < NFFS_TEST_TOUCHED_ARR_SZ);
- nffs_test_touched_entries[nffs_test_num_touched_entries] =
- &inode_entry->nie_hash_entry;
- nffs_test_num_touched_entries++;
-
- path_len = strlen(path);
-
- rc = nffs_inode_from_entry(&inode, inode_entry);
- TEST_ASSERT(rc == 0);
-
- if (nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)) {
- for (child_file = file->children;
- child_file != NULL && child_file->filename != NULL;
- child_file++) {
-
- child_filename_len = strlen(child_file->filename);
- child_path = malloc(path_len + 1 + child_filename_len + 1);
- TEST_ASSERT(child_path != NULL);
- memcpy(child_path, path, path_len);
- child_path[path_len] = '/';
- memcpy(child_path + path_len + 1, child_file->filename,
- child_filename_len);
- child_path[path_len + 1 + child_filename_len] = '\0';
-
- rc = nffs_path_find_inode_entry(child_path, &child_inode_entry);
- TEST_ASSERT(rc == 0);
-
- nffs_test_assert_file(child_file, child_inode_entry, child_path);
-
- free(child_path);
- }
- } else {
- nffs_test_util_assert_contents(path, file->contents,
- file->contents_len);
- }
-}
-
-static void
-nffs_test_assert_branch_touched(struct nffs_inode_entry *inode_entry)
-{
- struct nffs_inode_entry *child;
- int i;
-
- if (inode_entry == nffs_lost_found_dir) {
- return;
- }
-
- for (i = 0; i < nffs_test_num_touched_entries; i++) {
- if (nffs_test_touched_entries[i] == &inode_entry->nie_hash_entry) {
- break;
- }
- }
- TEST_ASSERT(i < nffs_test_num_touched_entries);
- nffs_test_touched_entries[i] = NULL;
-
- if (nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)) {
- SLIST_FOREACH(child, &inode_entry->nie_child_list, nie_sibling_next) {
- nffs_test_assert_branch_touched(child);
- }
- }
-}
-
-static void
-nffs_test_assert_child_inode_present(struct nffs_inode_entry *child)
-{
- const struct nffs_inode_entry *inode_entry;
- const struct nffs_inode_entry *parent;
- struct nffs_inode inode;
- int rc;
-
- rc = nffs_inode_from_entry(&inode, child);
- TEST_ASSERT(rc == 0);
-
- parent = inode.ni_parent;
- TEST_ASSERT(parent != NULL);
- TEST_ASSERT(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
-
- SLIST_FOREACH(inode_entry, &parent->nie_child_list, nie_sibling_next) {
- if (inode_entry == child) {
- return;
- }
- }
-
- TEST_ASSERT(0);
-}
-
-static void
-nffs_test_assert_block_present(struct nffs_hash_entry *block_entry)
-{
- const struct nffs_inode_entry *inode_entry;
- struct nffs_hash_entry *cur;
- struct nffs_block block;
- int rc;
-
- rc = nffs_block_from_hash_entry(&block, block_entry);
- TEST_ASSERT(rc == 0);
-
- inode_entry = block.nb_inode_entry;
- TEST_ASSERT(inode_entry != NULL);
- TEST_ASSERT(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
-
- cur = inode_entry->nie_last_block_entry;
- while (cur != NULL) {
- if (cur == block_entry) {
- return;
- }
-
- rc = nffs_block_from_hash_entry(&block, cur);
- TEST_ASSERT(rc == 0);
- cur = block.nb_prev;
- }
-
- TEST_ASSERT(0);
-}
-
-static void
-nffs_test_assert_children_sorted(struct nffs_inode_entry *inode_entry)
-{
- struct nffs_inode_entry *child_entry;
- struct nffs_inode_entry *prev_entry;
- struct nffs_inode child_inode;
- struct nffs_inode prev_inode;
- int cmp;
- int rc;
-
- prev_entry = NULL;
- SLIST_FOREACH(child_entry, &inode_entry->nie_child_list,
- nie_sibling_next) {
- rc = nffs_inode_from_entry(&child_inode, child_entry);
- TEST_ASSERT(rc == 0);
-
- if (prev_entry != NULL) {
- rc = nffs_inode_from_entry(&prev_inode, prev_entry);
- TEST_ASSERT(rc == 0);
-
- rc = nffs_inode_filename_cmp_flash(&prev_inode, &child_inode,
- &cmp);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(cmp < 0);
- }
-
- if (nffs_hash_id_is_dir(child_entry->nie_hash_entry.nhe_id)) {
- nffs_test_assert_children_sorted(child_entry);
- }
-
- prev_entry = child_entry;
- }
-}
-
-static void
-nffs_test_assert_system_once(const struct nffs_test_file_desc *root_dir)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_hash_entry *entry;
- int i;
-
- nffs_test_num_touched_entries = 0;
- nffs_test_assert_file(root_dir, nffs_root_dir, "");
- nffs_test_assert_branch_touched(nffs_root_dir);
-
- /* Ensure no orphaned inodes or blocks. */
- NFFS_HASH_FOREACH(entry, i) {
- TEST_ASSERT(entry->nhe_flash_loc != NFFS_FLASH_LOC_NONE);
- if (nffs_hash_id_is_inode(entry->nhe_id)) {
- inode_entry = (void *)entry;
- TEST_ASSERT(inode_entry->nie_refcnt == 1);
- if (entry->nhe_id == NFFS_ID_ROOT_DIR) {
- TEST_ASSERT(inode_entry == nffs_root_dir);
- } else {
- nffs_test_assert_child_inode_present(inode_entry);
- }
- } else {
- nffs_test_assert_block_present(entry);
- }
- }
-
- /* Ensure proper sorting. */
- nffs_test_assert_children_sorted(nffs_root_dir);
-}
-
-static void
-nffs_test_assert_system(const struct nffs_test_file_desc *root_dir,
- const struct nffs_area_desc *area_descs)
-{
- int rc;
-
- /* Ensure files are as specified, and that there are no other files or
- * orphaned inodes / blocks.
- */
- nffs_test_assert_system_once(root_dir);
-
- /* Force a garbage collection cycle. */
- rc = nffs_gc(NULL);
- TEST_ASSERT(rc == 0);
-
- /* Ensure file system is still as expected. */
- nffs_test_assert_system_once(root_dir);
-
- /* Clear cached data and restore from flash (i.e, simulate a reboot). */
- rc = nffs_misc_reset();
- TEST_ASSERT(rc == 0);
- rc = nffs_detect(area_descs);
- TEST_ASSERT(rc == 0);
-
- /* Ensure file system is still as expected. */
- nffs_test_assert_system_once(root_dir);
-}
-
-static void
-nffs_test_assert_area_seqs(int seq1, int count1, int seq2, int count2)
-{
- struct nffs_disk_area disk_area;
- int cur1;
- int cur2;
- int rc;
- int i;
-
- cur1 = 0;
- cur2 = 0;
-
- for (i = 0; i < nffs_num_areas; i++) {
- rc = nffs_flash_read(i, 0, &disk_area, sizeof disk_area);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(nffs_area_magic_is_set(&disk_area));
- TEST_ASSERT(disk_area.nda_gc_seq == nffs_areas[i].na_gc_seq);
- if (i == nffs_scratch_area_idx) {
- TEST_ASSERT(disk_area.nda_id == NFFS_AREA_ID_NONE);
- }
-
- if (nffs_areas[i].na_gc_seq == seq1) {
- cur1++;
- } else if (nffs_areas[i].na_gc_seq == seq2) {
- cur2++;
- } else {
- TEST_ASSERT(0);
- }
- }
-
- TEST_ASSERT(cur1 == count1 && cur2 == count2);
-}
-
-static void
-nffs_test_mkdir(void)
-{
- struct fs_file *file;
- int rc;
-
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/a/b/c/d");
- TEST_ASSERT(rc == FS_ENOENT);
-
- rc = fs_mkdir("asdf");
- TEST_ASSERT(rc == FS_EINVAL);
-
- rc = fs_mkdir("/a");
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/a/b");
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/a/b/c");
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/a/b/c/d");
- TEST_ASSERT(rc == 0);
-
- rc = fs_open("/a/b/c/d/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "a",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "b",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "c",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "d",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = NULL,
- .contents_len = 0,
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_unlink)
-{
- struct fs_file *file0;
- struct fs_file *file1;
- struct fs_file *file2;
- struct nffs_file *nfs_file;
- uint8_t buf[64];
- uint32_t bytes_read;
- int rc;
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/file0.txt", "0", 1);
-
- rc = fs_open("/file0.txt", FS_ACCESS_READ | FS_ACCESS_WRITE, &file0);
- TEST_ASSERT(rc == 0);
- nfs_file = (struct nffs_file *)file0;
- TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 2);
-
- rc = fs_unlink("/file0.txt");
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 1);
-
- rc = fs_open("/file0.txt", FS_ACCESS_READ, &file2);
- TEST_ASSERT(rc == FS_ENOENT);
-
- rc = fs_write(file0, "00", 2);
- TEST_ASSERT(rc == 0);
-
- rc = fs_seek(file0, 0);
- TEST_ASSERT(rc == 0);
-
- rc = fs_read(file0, sizeof buf, buf, &bytes_read);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(bytes_read == 2);
- TEST_ASSERT(memcmp(buf, "00", 2) == 0);
-
- rc = fs_close(file0);
- TEST_ASSERT(rc == 0);
-
- rc = fs_open("/file0.txt", FS_ACCESS_READ, &file0);
- TEST_ASSERT(rc == FS_ENOENT);
-
- /* Nested unlink. */
- rc = fs_mkdir("/mydir");
- TEST_ASSERT(rc == 0);
- nffs_test_util_create_file("/mydir/file1.txt", "1", 2);
-
- rc = fs_open("/mydir/file1.txt", FS_ACCESS_READ | FS_ACCESS_WRITE,
- &file1);
- TEST_ASSERT(rc == 0);
- nfs_file = (struct nffs_file *)file1;
- TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 2);
-
- rc = fs_unlink("/mydir");
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(nfs_file->nf_inode_entry->nie_refcnt == 1);
-
- rc = fs_open("/mydir/file1.txt", FS_ACCESS_READ, &file2);
- TEST_ASSERT(rc == FS_ENOENT);
-
- rc = fs_write(file1, "11", 2);
- TEST_ASSERT(rc == 0);
-
- rc = fs_seek(file1, 0);
- TEST_ASSERT(rc == 0);
-
- rc = fs_read(file1, sizeof buf, buf, &bytes_read);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(bytes_read == 2);
- TEST_ASSERT(memcmp(buf, "11", 2) == 0);
-
- rc = fs_close(file1);
- TEST_ASSERT(rc == 0);
-
- rc = fs_open("/mydir/file1.txt", FS_ACCESS_READ, &file1);
- TEST_ASSERT(rc == FS_ENOENT);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_rename)
-{
- struct fs_file *file;
- const char contents[] = "contents";
- int rc;
-
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- rc = fs_rename("/nonexistent.txt", "/newname.txt");
- TEST_ASSERT(rc == FS_ENOENT);
-
- /*** Rename file. */
- nffs_test_util_create_file("/myfile.txt", contents, sizeof contents);
-
- rc = fs_rename("/myfile.txt", "badname");
- TEST_ASSERT(rc == FS_EINVAL);
-
- rc = fs_rename("/myfile.txt", "/myfile2.txt");
- TEST_ASSERT(rc == 0);
-
- rc = fs_open("/myfile.txt", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == FS_ENOENT);
-
- nffs_test_util_assert_contents("/myfile2.txt", contents, sizeof contents);
-
- rc = fs_mkdir("/mydir");
- TEST_ASSERT(rc == 0);
-
- rc = fs_rename("/myfile2.txt", "/mydir/myfile2.txt");
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/mydir/myfile2.txt", contents,
- sizeof contents);
-
- /*** Rename directory. */
- rc = fs_rename("/mydir", "badname");
- TEST_ASSERT(rc == FS_EINVAL);
-
- rc = fs_rename("/mydir", "/mydir2");
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/mydir2/myfile2.txt", contents,
- sizeof contents);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "mydir2",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile2.txt",
- .contents = "contents",
- .contents_len = 9,
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_truncate)
-{
- struct fs_file *file;
- int rc;
-
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 0);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "abcdefgh", 8);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 8);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "abcdefgh", 8);
-
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 0);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "1234", 4);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 4);
- TEST_ASSERT(fs_getpos(file) == 4);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "1234", 4);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = "1234",
- .contents_len = 4,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_append)
-{
- struct fs_file *file;
- int rc;
-
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_APPEND, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 0);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "abcdefgh", 8);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 8);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "abcdefgh", 8);
-
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE | FS_ACCESS_APPEND, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 8);
-
- /* File position should always be at the end of a file after an append.
- * Seek to the middle prior to writing to test this.
- */
- rc = fs_seek(file, 2);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 2);
-
- rc = fs_write(file, "ijklmnop", 8);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 16);
- rc = fs_write(file, "qrstuvwx", 8);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 24);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt",
- "abcdefghijklmnopqrstuvwx", 24);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = "abcdefghijklmnopqrstuvwx",
- .contents_len = 24,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_read)
-{
- struct fs_file *file;
- uint8_t buf[16];
- uint32_t bytes_read;
- int rc;
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/myfile.txt", "1234567890", 10);
-
- rc = fs_open("/myfile.txt", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 10);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_read(file, 4, buf, &bytes_read);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(bytes_read == 4);
- TEST_ASSERT(memcmp(buf, "1234", 4) == 0);
- TEST_ASSERT(fs_getpos(file) == 4);
-
- rc = fs_read(file, sizeof buf - 4, buf + 4, &bytes_read);
- TEST_ASSERT(rc == 0);
- TEST_ASSERT(bytes_read == 6);
- TEST_ASSERT(memcmp(buf, "1234567890", 10) == 0);
- TEST_ASSERT(fs_getpos(file) == 10);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-}
-
-TEST_CASE(nffs_test_open)
-{
- struct fs_file *file;
- struct fs_dir *dir;
- int rc;
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- /*** Fail to open an invalid path (not rooted). */
- rc = fs_open("file", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == FS_EINVAL);
-
- /*** Fail to open a directory (root directory). */
- rc = fs_open("/", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == FS_EINVAL);
-
- /*** Fail to open a nonexistent file for reading. */
- rc = fs_open("/1234", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == FS_ENOENT);
-
- /*** Fail to open a child of a nonexistent directory. */
- rc = fs_open("/dir/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == FS_ENOENT);
- rc = fs_opendir("/dir", &dir);
- TEST_ASSERT(rc == FS_ENOENT);
-
- rc = fs_mkdir("/dir");
- TEST_ASSERT(rc == 0);
-
- /*** Fail to open a directory. */
- rc = fs_open("/dir", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == FS_EINVAL);
-
- /*** Successfully open an existing file for reading. */
- nffs_test_util_create_file("/dir/file.txt", "1234567890", 10);
- rc = fs_open("/dir/file.txt", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == 0);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- /*** Successfully open an nonexistent file for writing. */
- rc = fs_open("/dir/file2.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- /*** Ensure the file can be reopened. */
- rc = fs_open("/dir/file.txt", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == 0);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-}
-
-TEST_CASE(nffs_test_overwrite_one)
-{
- struct fs_file *file;
- int rc;
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_append_file("/myfile.txt", "abcdefgh", 8);
-
- /*** Overwrite within one block (middle). */
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 3);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 3);
-
- rc = fs_write(file, "12", 2);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 5);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "abc12fgh", 8);
- nffs_test_util_assert_block_count("/myfile.txt", 1);
-
- /*** Overwrite within one block (start). */
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "xy", 2);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 2);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "xyc12fgh", 8);
- nffs_test_util_assert_block_count("/myfile.txt", 1);
-
- /*** Overwrite within one block (end). */
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 6);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 6);
-
- rc = fs_write(file, "<>", 2);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 8);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "xyc12f<>", 8);
- nffs_test_util_assert_block_count("/myfile.txt", 1);
-
- /*** Overwrite one block middle, extend. */
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 4);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 8);
- TEST_ASSERT(fs_getpos(file) == 4);
-
- rc = fs_write(file, "abcdefgh", 8);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 12);
- TEST_ASSERT(fs_getpos(file) == 12);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "xyc1abcdefgh", 12);
- nffs_test_util_assert_block_count("/myfile.txt", 1);
-
- /*** Overwrite one block start, extend. */
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 12);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "abcdefghijklmnop", 16);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 16);
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents("/myfile.txt", "abcdefghijklmnop", 16);
- nffs_test_util_assert_block_count("/myfile.txt", 1);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = "abcdefghijklmnop",
- .contents_len = 16,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_overwrite_two)
-{
- struct nffs_test_block_desc *blocks = (struct nffs_test_block_desc[]) { {
- .data = "abcdefgh",
- .data_len = 8,
- }, {
- .data = "ijklmnop",
- .data_len = 8,
- } };
-
- struct fs_file *file;
- int rc;
-
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- /*** Overwrite two blocks (middle). */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 7);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 7);
-
- rc = fs_write(file, "123", 3);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 10);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt", "abcdefg123klmnop", 16);
- nffs_test_util_assert_block_count("/myfile.txt", 2);
-
- /*** Overwrite two blocks (start). */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "ABCDEFGHIJ", 10);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 10);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt", "ABCDEFGHIJklmnop", 16);
- nffs_test_util_assert_block_count("/myfile.txt", 2);
-
- /*** Overwrite two blocks (end). */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 6);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 6);
-
- rc = fs_write(file, "1234567890", 10);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 16);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt", "abcdef1234567890", 16);
- nffs_test_util_assert_block_count("/myfile.txt", 2);
-
- /*** Overwrite two blocks middle, extend. */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 6);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 6);
-
- rc = fs_write(file, "1234567890!@#$", 14);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 20);
- TEST_ASSERT(fs_getpos(file) == 20);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt", "abcdef1234567890!@#$", 20);
- nffs_test_util_assert_block_count("/myfile.txt", 2);
-
- /*** Overwrite two blocks start, extend. */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 2);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 16);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "1234567890!@#$%^&*()", 20);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 20);
- TEST_ASSERT(fs_getpos(file) == 20);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt", "1234567890!@#$%^&*()", 20);
- nffs_test_util_assert_block_count("/myfile.txt", 2);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = "1234567890!@#$%^&*()",
- .contents_len = 20,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_overwrite_three)
-{
- struct nffs_test_block_desc *blocks = (struct nffs_test_block_desc[]) { {
- .data = "abcdefgh",
- .data_len = 8,
- }, {
- .data = "ijklmnop",
- .data_len = 8,
- }, {
- .data = "qrstuvwx",
- .data_len = 8,
- } };
-
- struct fs_file *file;
- int rc;
-
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- /*** Overwrite three blocks (middle). */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 6);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 6);
-
- rc = fs_write(file, "1234567890!@", 12);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 18);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt",
- "abcdef1234567890!@stuvwx", 24);
- nffs_test_util_assert_block_count("/myfile.txt", 3);
-
- /*** Overwrite three blocks (start). */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "1234567890!@#$%^&*()", 20);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 20);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt",
- "1234567890!@#$%^&*()uvwx", 24);
- nffs_test_util_assert_block_count("/myfile.txt", 3);
-
- /*** Overwrite three blocks (end). */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 6);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 6);
-
- rc = fs_write(file, "1234567890!@#$%^&*", 18);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 24);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt",
- "abcdef1234567890!@#$%^&*", 24);
- nffs_test_util_assert_block_count("/myfile.txt", 3);
-
- /*** Overwrite three blocks middle, extend. */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 6);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 6);
-
- rc = fs_write(file, "1234567890!@#$%^&*()", 20);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 26);
- TEST_ASSERT(fs_getpos(file) == 26);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt",
- "abcdef1234567890!@#$%^&*()", 26);
- nffs_test_util_assert_block_count("/myfile.txt", 3);
-
- /*** Overwrite three blocks start, extend. */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_write(file, "1234567890!@#$%^&*()abcdefghij", 30);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 30);
- TEST_ASSERT(fs_getpos(file) == 30);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt",
- "1234567890!@#$%^&*()abcdefghij", 30);
- nffs_test_util_assert_block_count("/myfile.txt", 3);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = "1234567890!@#$%^&*()abcdefghij",
- .contents_len = 30,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_overwrite_many)
-{
- struct nffs_test_block_desc *blocks = (struct nffs_test_block_desc[]) { {
- .data = "abcdefgh",
- .data_len = 8,
- }, {
- .data = "ijklmnop",
- .data_len = 8,
- }, {
- .data = "qrstuvwx",
- .data_len = 8,
- } };
-
- struct fs_file *file;
- int rc;
-
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- /*** Overwrite middle of first block. */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 3);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 3);
-
- rc = fs_write(file, "12", 2);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 5);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt",
- "abc12fghijklmnopqrstuvwx", 24);
- nffs_test_util_assert_block_count("/myfile.txt", 3);
-
- /*** Overwrite end of first block, start of second. */
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 3);
- rc = fs_open("/myfile.txt", FS_ACCESS_WRITE, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 0);
-
- rc = fs_seek(file, 6);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 6);
-
- rc = fs_write(file, "1234", 4);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_file_len(file, 24);
- TEST_ASSERT(fs_getpos(file) == 10);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_contents( "/myfile.txt",
- "abcdef1234klmnopqrstuvwx", 24);
- nffs_test_util_assert_block_count("/myfile.txt", 3);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = "abcdef1234klmnopqrstuvwx",
- .contents_len = 24,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_long_filename)
-{
- int rc;
-
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/12345678901234567890.txt", "contents", 8);
-
- rc = fs_mkdir("/longdir12345678901234567890");
- TEST_ASSERT(rc == 0);
-
- rc = fs_rename("/12345678901234567890.txt",
- "/longdir12345678901234567890/12345678901234567890.txt");
- TEST_ASSERT(rc == 0);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "longdir12345678901234567890",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "/12345678901234567890.txt",
- .contents = "contents",
- .contents_len = 8,
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_large_write)
-{
- static char data[NFFS_BLOCK_MAX_DATA_SZ_MAX * 5];
- int rc;
- int i;
-
- static const struct nffs_area_desc area_descs_two[] = {
- { 0x00020000, 128 * 1024 },
- { 0x00040000, 128 * 1024 },
- { 0, 0 },
- };
-
-
-
- /*** Setup. */
- rc = nffs_format(area_descs_two);
- TEST_ASSERT(rc == 0);
-
- for (i = 0; i < sizeof data; i++) {
- data[i] = i;
- }
-
- nffs_test_util_create_file("/myfile.txt", data, sizeof data);
-
- /* Ensure large write was split across the appropriate number of data
- * blocks.
- */
- TEST_ASSERT(nffs_test_util_block_count("/myfile.txt") ==
- sizeof data / NFFS_BLOCK_MAX_DATA_SZ_MAX);
-
- /* Garbage collect and then ensure the large file is still properly divided
- * according to max data block size.
- */
- nffs_gc(NULL);
- TEST_ASSERT(nffs_test_util_block_count("/myfile.txt") ==
- sizeof data / NFFS_BLOCK_MAX_DATA_SZ_MAX);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "myfile.txt",
- .contents = data,
- .contents_len = sizeof data,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, area_descs_two);
-}
-
-TEST_CASE(nffs_test_many_children)
-{
- int rc;
-
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/zasdf", NULL, 0);
- nffs_test_util_create_file("/FfD", NULL, 0);
- nffs_test_util_create_file("/4Zvv", NULL, 0);
- nffs_test_util_create_file("/*(*2fs", NULL, 0);
- nffs_test_util_create_file("/pzzd", NULL, 0);
- nffs_test_util_create_file("/zasdf0", NULL, 0);
- nffs_test_util_create_file("/23132.bin", NULL, 0);
- nffs_test_util_create_file("/asldkfjaldskfadsfsdf.txt", NULL, 0);
- nffs_test_util_create_file("/sdgaf", NULL, 0);
- nffs_test_util_create_file("/939302**", NULL, 0);
- rc = fs_mkdir("/dir");
- nffs_test_util_create_file("/dir/itw82", NULL, 0);
- nffs_test_util_create_file("/dir/124", NULL, 0);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- { "zasdf" },
- { "FfD" },
- { "4Zvv" },
- { "*(*2fs" },
- { "pzzd" },
- { "zasdf0" },
- { "23132.bin" },
- { "asldkfjaldskfadsfsdf.txt" },
- { "sdgaf" },
- { "939302**" },
- {
- .filename = "dir",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- { "itw82" },
- { "124" },
- { NULL },
- },
- },
- { NULL },
- }
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_gc)
-{
- int rc;
-
- static const struct nffs_area_desc area_descs_two[] = {
- { 0x00020000, 128 * 1024 },
- { 0x00040000, 128 * 1024 },
- { 0, 0 },
- };
-
- struct nffs_test_block_desc blocks[8] = { {
- .data = "1",
- .data_len = 1,
- }, {
- .data = "2",
- .data_len = 1,
- }, {
- .data = "3",
- .data_len = 1,
- }, {
- .data = "4",
- .data_len = 1,
- }, {
- .data = "5",
- .data_len = 1,
- }, {
- .data = "6",
- .data_len = 1,
- }, {
- .data = "7",
- .data_len = 1,
- }, {
- .data = "8",
- .data_len = 1,
- } };
-
-
- rc = nffs_format(area_descs_two);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file_blocks("/myfile.txt", blocks, 8);
-
- nffs_gc(NULL);
-
- nffs_test_util_assert_block_count("/myfile.txt", 1);
-}
-
-TEST_CASE(nffs_test_wear_level)
-{
- int rc;
- int i;
- int j;
-
- static const struct nffs_area_desc area_descs_uniform[] = {
- { 0x00000000, 2 * 1024 },
- { 0x00020000, 2 * 1024 },
- { 0x00040000, 2 * 1024 },
- { 0x00060000, 2 * 1024 },
- { 0x00080000, 2 * 1024 },
- { 0, 0 },
- };
-
-
- /*** Setup. */
- rc = nffs_format(area_descs_uniform);
- TEST_ASSERT(rc == 0);
-
- /* Ensure areas rotate properly. */
- for (i = 0; i < 255; i++) {
- for (j = 0; j < nffs_num_areas; j++) {
- nffs_test_assert_area_seqs(i, nffs_num_areas - j, i + 1, j);
- nffs_gc(NULL);
- }
- }
-
- /* Ensure proper rollover of sequence numbers. */
- for (j = 0; j < nffs_num_areas; j++) {
- nffs_test_assert_area_seqs(255, nffs_num_areas - j, 0, j);
- nffs_gc(NULL);
- }
- for (j = 0; j < nffs_num_areas; j++) {
- nffs_test_assert_area_seqs(0, nffs_num_areas - j, 1, j);
- nffs_gc(NULL);
- }
-}
-
-TEST_CASE(nffs_test_corrupt_scratch)
-{
- int non_scratch_id;
- int scratch_id;
- int rc;
-
- static const struct nffs_area_desc area_descs_two[] = {
- { 0x00020000, 128 * 1024 },
- { 0x00040000, 128 * 1024 },
- { 0, 0 },
- };
-
-
- /*** Setup. */
- rc = nffs_format(area_descs_two);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/myfile.txt", "contents", 8);
-
- /* Copy the current contents of the non-scratch area to the scratch area.
- * This will make the scratch area look like it only partially participated
- * in a garbage collection cycle.
- */
- scratch_id = nffs_scratch_area_idx;
- non_scratch_id = scratch_id ^ 1;
- nffs_test_copy_area(area_descs_two + non_scratch_id,
- area_descs_two + nffs_scratch_area_idx);
-
- /* Add some more data to the non-scratch area. */
- rc = fs_mkdir("/mydir");
- TEST_ASSERT(rc == 0);
-
- /* Ensure the file system is successfully detected and valid, despite
- * corruption.
- */
-
- rc = nffs_misc_reset();
- TEST_ASSERT(rc == 0);
-
- rc = nffs_detect(area_descs_two);
- TEST_ASSERT(rc == 0);
-
- TEST_ASSERT(nffs_scratch_area_idx == scratch_id);
-
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "mydir",
- .is_dir = 1,
- }, {
- .filename = "myfile.txt",
- .contents = "contents",
- .contents_len = 8,
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, area_descs_two);
-}
-
-TEST_CASE(nffs_test_incomplete_block)
-{
- struct nffs_block block;
- struct fs_file *fs_file;
- struct nffs_file *file;
- uint32_t flash_offset;
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/mydir");
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/mydir/a", "aaaa", 4);
- nffs_test_util_create_file("/mydir/b", "bbbb", 4);
- nffs_test_util_create_file("/mydir/c", "cccc", 4);
-
- /* Add a second block to the 'b' file. */
- nffs_test_util_append_file("/mydir/b", "1234", 4);
-
- /* Corrupt the 'b' file; make it look like the second block only got half
- * written.
- */
- rc = fs_open("/mydir/b", FS_ACCESS_READ, &fs_file);
- TEST_ASSERT(rc == 0);
- file = (struct nffs_file *)fs_file;
-
- rc = nffs_block_from_hash_entry(&block,
- file->nf_inode_entry->nie_last_block_entry);
- TEST_ASSERT(rc == 0);
-
- nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc, &area_idx,
- &area_offset);
- flash_offset = nffs_areas[area_idx].na_offset + area_offset;
- rc = flash_native_memset(
- flash_offset + sizeof (struct nffs_disk_block) + 2, 0xff, 2);
- TEST_ASSERT(rc == 0);
-
- rc = nffs_misc_reset();
- TEST_ASSERT(rc == 0);
- rc = nffs_detect(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- /* The entire second block should be removed; the file should only contain
- * the first block.
- */
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "mydir",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "a",
- .contents = "aaaa",
- .contents_len = 4,
- }, {
- .filename = "b",
- .contents = "bbbb",
- .contents_len = 4,
- }, {
- .filename = "c",
- .contents = "cccc",
- .contents_len = 4,
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_corrupt_block)
-{
- struct nffs_block block;
- struct fs_file *fs_file;
- struct nffs_file *file;
- uint32_t flash_offset;
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/mydir");
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/mydir/a", "aaaa", 4);
- nffs_test_util_create_file("/mydir/b", "bbbb", 4);
- nffs_test_util_create_file("/mydir/c", "cccc", 4);
-
- /* Add a second block to the 'b' file. */
- nffs_test_util_append_file("/mydir/b", "1234", 4);
-
- /* Corrupt the 'b' file; overwrite the second block's magic number. */
- rc = fs_open("/mydir/b", FS_ACCESS_READ, &fs_file);
- TEST_ASSERT(rc == 0);
- file = (struct nffs_file *)fs_file;
-
- rc = nffs_block_from_hash_entry(&block,
- file->nf_inode_entry->nie_last_block_entry);
- TEST_ASSERT(rc == 0);
-
- nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc, &area_idx,
- &area_offset);
- flash_offset = nffs_areas[area_idx].na_offset + area_offset;
- rc = flash_native_memset(flash_offset, 0x43, 4);
- TEST_ASSERT(rc == 0);
-
- /* Write a fourth file. This file should get restored even though the
- * previous object has an invalid magic number.
- */
- nffs_test_util_create_file("/mydir/d", "dddd", 4);
-
- rc = nffs_misc_reset();
- TEST_ASSERT(rc == 0);
- rc = nffs_detect(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- /* The entire second block should be removed; the file should only contain
- * the first block.
- */
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "mydir",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "a",
- .contents = "aaaa",
- .contents_len = 4,
- }, {
- .filename = "b",
- .contents = "bbbb",
- .contents_len = 4,
- }, {
- .filename = "c",
- .contents = "cccc",
- .contents_len = 4,
- }, {
- .filename = "d",
- .contents = "dddd",
- .contents_len = 4,
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_large_unlink)
-{
- static char file_contents[1024 * 4];
- char filename[256];
- int rc;
- int i;
- int j;
- int k;
-
-
- /*** Setup. */
- nffs_config.nc_num_inodes = 1024;
- nffs_config.nc_num_blocks = 1024;
-
- rc = nffs_init();
- TEST_ASSERT(rc == 0);
-
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- for (i = 0; i < 5; i++) {
- snprintf(filename, sizeof filename, "/dir0_%d", i);
- rc = fs_mkdir(filename);
- TEST_ASSERT(rc == 0);
-
- for (j = 0; j < 5; j++) {
- snprintf(filename, sizeof filename, "/dir0_%d/dir1_%d", i, j);
- rc = fs_mkdir(filename);
- TEST_ASSERT(rc == 0);
-
- for (k = 0; k < 5; k++) {
- snprintf(filename, sizeof filename,
- "/dir0_%d/dir1_%d/file2_%d", i, j, k);
- nffs_test_util_create_file(filename, file_contents,
- sizeof file_contents);
- }
- }
-
- for (j = 0; j < 15; j++) {
- snprintf(filename, sizeof filename, "/dir0_%d/file1_%d", i, j);
- nffs_test_util_create_file(filename, file_contents,
- sizeof file_contents);
- }
- }
-
- for (i = 0; i < 5; i++) {
- snprintf(filename, sizeof filename, "/dir0_%d", i);
- rc = fs_unlink(filename);
- TEST_ASSERT(rc == 0);
- }
-
- /* The entire file system should be empty. */
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_large_system)
-{
- int rc;
-
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
- nffs_test_util_create_tree(nffs_test_system_01);
-
- nffs_test_assert_system(nffs_test_system_01, nffs_area_descs);
-
- rc = fs_unlink("/lvl1dir-0000");
- TEST_ASSERT(rc == 0);
-
- rc = fs_unlink("/lvl1dir-0004");
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/lvl1dir-0000");
- TEST_ASSERT(rc == 0);
-
- nffs_test_assert_system(nffs_test_system_01_rm_1014_mk10, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_lost_found)
-{
- char buf[32];
- struct nffs_inode_entry *inode_entry;
- uint32_t flash_offset;
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- rc = fs_mkdir("/mydir");
- TEST_ASSERT(rc == 0);
- rc = fs_mkdir("/mydir/dir1");
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/mydir/file1", "aaaa", 4);
- nffs_test_util_create_file("/mydir/dir1/file2", "bbbb", 4);
-
- /* Corrupt the mydir inode. */
- rc = nffs_path_find_inode_entry("/mydir", &inode_entry);
- TEST_ASSERT(rc == 0);
-
- snprintf(buf, sizeof buf, "%lu",
- (unsigned long)inode_entry->nie_hash_entry.nhe_id);
-
- nffs_flash_loc_expand(inode_entry->nie_hash_entry.nhe_flash_loc,
- &area_idx, &area_offset);
- flash_offset = nffs_areas[area_idx].na_offset + area_offset;
- rc = flash_native_memset(flash_offset + 10, 0xff, 1);
- TEST_ASSERT(rc == 0);
-
- /* Clear cached data and restore from flash (i.e, simulate a reboot). */
- rc = nffs_misc_reset();
- TEST_ASSERT(rc == 0);
- rc = nffs_detect(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- /* All contents should now be in the lost+found dir. */
- struct nffs_test_file_desc *expected_system =
- (struct nffs_test_file_desc[]) { {
- .filename = "",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "lost+found",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = buf,
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "file1",
- .contents = "aaaa",
- .contents_len = 4,
- }, {
- .filename = "dir1",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) { {
- .filename = "file2",
- .contents = "bbbb",
- .contents_len = 4,
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } },
- }, {
- .filename = NULL,
- } }
- } };
-
- nffs_test_assert_system(expected_system, nffs_area_descs);
-}
-
-TEST_CASE(nffs_test_cache_large_file)
-{
- static char data[NFFS_BLOCK_MAX_DATA_SZ_MAX * 5];
- struct fs_file *file;
- uint8_t b;
- int rc;
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_create_file("/myfile.txt", data, sizeof data);
- nffs_cache_clear();
-
- /* Opening a file should not cause any blocks to get cached. */
- rc = fs_open("/myfile.txt", FS_ACCESS_READ, &file);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_cache_range("/myfile.txt", 0, 0);
-
- /* Cache first block. */
- rc = fs_seek(file, nffs_block_max_data_sz * 0);
- TEST_ASSERT(rc == 0);
- rc = fs_read(file, 1, &b, NULL);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_cache_range("/myfile.txt",
- nffs_block_max_data_sz * 0,
- nffs_block_max_data_sz * 1);
-
- /* Cache second block. */
- rc = fs_seek(file, nffs_block_max_data_sz * 1);
- TEST_ASSERT(rc == 0);
- rc = fs_read(file, 1, &b, NULL);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_cache_range("/myfile.txt",
- nffs_block_max_data_sz * 0,
- nffs_block_max_data_sz * 2);
-
-
- /* Cache fourth block; prior cache should get erased. */
- rc = fs_seek(file, nffs_block_max_data_sz * 3);
- TEST_ASSERT(rc == 0);
- rc = fs_read(file, 1, &b, NULL);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_cache_range("/myfile.txt",
- nffs_block_max_data_sz * 3,
- nffs_block_max_data_sz * 4);
-
- /* Cache second and third blocks. */
- rc = fs_seek(file, nffs_block_max_data_sz * 1);
- TEST_ASSERT(rc == 0);
- rc = fs_read(file, 1, &b, NULL);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_cache_range("/myfile.txt",
- nffs_block_max_data_sz * 1,
- nffs_block_max_data_sz * 4);
-
- /* Cache fifth block. */
- rc = fs_seek(file, nffs_block_max_data_sz * 4);
- TEST_ASSERT(rc == 0);
- rc = fs_read(file, 1, &b, NULL);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_cache_range("/myfile.txt",
- nffs_block_max_data_sz * 1,
- nffs_block_max_data_sz * 5);
-
- rc = fs_close(file);
- TEST_ASSERT(rc == 0);
-}
-
-TEST_CASE(nffs_test_readdir)
-{
- struct fs_dirent *dirent;
- struct fs_dir *dir;
- int rc;
-
- /*** Setup. */
- rc = nffs_format(nffs_area_descs);
- TEST_ASSERT_FATAL(rc == 0);
-
- rc = fs_mkdir("/mydir");
- TEST_ASSERT_FATAL(rc == 0);
-
- nffs_test_util_create_file("/mydir/b", "bbbb", 4);
- nffs_test_util_create_file("/mydir/a", "aaaa", 4);
- rc = fs_mkdir("/mydir/c");
- TEST_ASSERT_FATAL(rc == 0);
-
- /* Nonexistent directory. */
- rc = fs_opendir("/asdf", &dir);
- TEST_ASSERT(rc == FS_ENOENT);
-
- /* Fail to opendir a file. */
- rc = fs_opendir("/mydir/a", &dir);
- TEST_ASSERT(rc == FS_EINVAL);
-
- /* Real directory (with trailing slash). */
- rc = fs_opendir("/mydir/", &dir);
- TEST_ASSERT_FATAL(rc == 0);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_ent_name(dirent, "a");
- TEST_ASSERT(fs_dirent_is_dir(dirent) == 0);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_ent_name(dirent, "b");
- TEST_ASSERT(fs_dirent_is_dir(dirent) == 0);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_ent_name(dirent, "c");
- TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == FS_ENOENT);
-
- rc = fs_closedir(dir);
- TEST_ASSERT(rc == 0);
-
- /* Root directory. */
- rc = fs_opendir("/", &dir);
- TEST_ASSERT(rc == 0);
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_ent_name(dirent, "lost+found");
- TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == 0);
- nffs_test_util_assert_ent_name(dirent, "mydir");
- TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
-
- rc = fs_closedir(dir);
- TEST_ASSERT(rc == 0);
-
- /* Delete entries while iterating. */
- rc = fs_opendir("/mydir", &dir);
- TEST_ASSERT_FATAL(rc == 0);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_ent_name(dirent, "a");
- TEST_ASSERT(fs_dirent_is_dir(dirent) == 0);
-
- rc = fs_unlink("/mydir/b");
- TEST_ASSERT(rc == 0);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == 0);
-
- rc = fs_unlink("/mydir/c");
- TEST_ASSERT(rc == 0);
-
- rc = fs_unlink("/mydir");
- TEST_ASSERT(rc == 0);
-
- nffs_test_util_assert_ent_name(dirent, "c");
- TEST_ASSERT(fs_dirent_is_dir(dirent) == 1);
-
- rc = fs_readdir(dir, &dirent);
- TEST_ASSERT(rc == FS_ENOENT);
-
- rc = fs_closedir(dir);
- TEST_ASSERT(rc == 0);
-
- /* Ensure directory is gone. */
- rc = fs_opendir("/mydir", &dir);
- TEST_ASSERT(rc == FS_ENOENT);
-}
-
-TEST_SUITE(nffs_suite_cache)
-{
- int rc;
-
- memset(&nffs_config, 0, sizeof nffs_config);
- nffs_config.nc_num_cache_inodes = 4;
- nffs_config.nc_num_cache_blocks = 64;
-
- rc = nffs_init();
- TEST_ASSERT(rc == 0);
-
- nffs_test_cache_large_file();
-}
-
-static void
-nffs_test_gen(void)
-{
- int rc;
-
- rc = nffs_init();
- TEST_ASSERT(rc == 0);
-
- nffs_test_unlink();
- nffs_test_mkdir();
- nffs_test_rename();
- nffs_test_truncate();
- nffs_test_append();
- nffs_test_read();
- nffs_test_open();
- nffs_test_overwrite_one();
- nffs_test_overwrite_two();
- nffs_test_overwrite_three();
- nffs_test_overwrite_many();
- nffs_test_long_filename();
- nffs_test_large_write();
- nffs_test_many_children();
- nffs_test_gc();
- nffs_test_wear_level();
- nffs_test_corrupt_scratch();
- nffs_test_incomplete_block();
- nffs_test_corrupt_block();
- nffs_test_large_unlink();
- nffs_test_large_system();
- nffs_test_lost_found();
- nffs_test_readdir();
-}
-
-TEST_SUITE(gen_1_1)
-{
- nffs_config.nc_num_cache_inodes = 1;
- nffs_config.nc_num_cache_blocks = 1;
- nffs_test_gen();
-}
-
-TEST_SUITE(gen_4_32)
-{
- nffs_config.nc_num_cache_inodes = 4;
- nffs_config.nc_num_cache_blocks = 32;
- nffs_test_gen();
-}
-
-TEST_SUITE(gen_32_1024)
-{
- nffs_config.nc_num_cache_inodes = 32;
- nffs_config.nc_num_cache_blocks = 1024;
- nffs_test_gen();
-}
-
-int
-nffs_test_all(void)
-{
- gen_1_1();
- gen_4_32();
- gen_32_1024();
- nffs_suite_cache();
-
- return tu_any_failed;
-}
-
-#ifdef PKG_TEST
-
-int
-main(void)
-{
- tu_config.tc_print_results = 1;
- tu_init();
-
- nffs_test_all();
-
- return tu_any_failed;
-}
-
-#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/test/arch/sim/nffs_test_priv.h
----------------------------------------------------------------------
diff --git a/libs/nffs/src/test/arch/sim/nffs_test_priv.h b/libs/nffs/src/test/arch/sim/nffs_test_priv.h
deleted file mode 100644
index 12736ad..0000000
--- a/libs/nffs/src/test/arch/sim/nffs_test_priv.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 H_NFFS_TEST_PRIV_
-#define H_NFFS_TEST_PRIV_
-
-struct nffs_test_block_desc {
- const char *data;
- int data_len;
-};
-
-struct nffs_test_file_desc {
- const char *filename;
- int is_dir;
- const char *contents;
- int contents_len;
- struct nffs_test_file_desc *children;
-};
-
-int nffs_test(void);
-
-extern const struct nffs_test_file_desc *nffs_test_system_01;
-extern const struct nffs_test_file_desc *nffs_test_system_01_rm_1014_mk10;
-
-#endif
-
[05/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_gc.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_gc.c b/libs/nffs/src/nffs_gc.c
deleted file mode 100644
index a51460e..0000000
--- a/libs/nffs/src/nffs_gc.c
+++ /dev/null
@@ -1,523 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "os/os_malloc.h"
-#include "testutil/testutil.h"
-#include "nffs_priv.h"
-#include "nffs/nffs.h"
-
-static int
-nffs_gc_copy_object(struct nffs_hash_entry *entry, uint16_t object_size,
- uint8_t to_area_idx)
-{
- uint32_t from_area_offset;
- uint32_t to_area_offset;
- uint8_t from_area_idx;
- int rc;
-
- nffs_flash_loc_expand(entry->nhe_flash_loc,
- &from_area_idx, &from_area_offset);
- to_area_offset = nffs_areas[to_area_idx].na_cur;
-
- rc = nffs_flash_copy(from_area_idx, from_area_offset, to_area_idx,
- to_area_offset, object_size);
- if (rc != 0) {
- return rc;
- }
-
- entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset);
-
- return 0;
-}
-
-static int
-nffs_gc_copy_inode(struct nffs_inode_entry *inode_entry, uint8_t to_area_idx)
-{
- struct nffs_inode inode;
- uint16_t copy_len;
- int rc;
-
- rc = nffs_inode_from_entry(&inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
- copy_len = sizeof (struct nffs_disk_inode) + inode.ni_filename_len;
-
- rc = nffs_gc_copy_object(&inode_entry->nie_hash_entry, copy_len,
- to_area_idx);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Selects the most appropriate area for garbage collection.
- *
- * @return The ID of the area to garbage collect.
- */
-static uint16_t
-nffs_gc_select_area(void)
-{
- const struct nffs_area *area;
- uint8_t best_area_idx;
- int8_t diff;
- int i;
-
- best_area_idx = 0;
- for (i = 1; i < nffs_num_areas; i++) {
- if (i == nffs_scratch_area_idx) {
- continue;
- }
-
- area = nffs_areas + i;
- if (area->na_length > nffs_areas[best_area_idx].na_length) {
- best_area_idx = i;
- } else if (best_area_idx == nffs_scratch_area_idx) {
- best_area_idx = i;
- } else {
- diff = nffs_areas[i].na_gc_seq -
- nffs_areas[best_area_idx].na_gc_seq;
- if (diff < 0) {
- best_area_idx = i;
- }
- }
- }
-
- assert(best_area_idx != nffs_scratch_area_idx);
-
- return best_area_idx;
-}
-
-static int
-nffs_gc_block_chain_copy(struct nffs_hash_entry *last_entry, uint32_t data_len,
- uint8_t to_area_idx)
-{
- struct nffs_hash_entry *entry;
- struct nffs_block block;
- uint32_t data_bytes_copied;
- uint16_t copy_len;
- int rc;
-
- data_bytes_copied = 0;
- entry = last_entry;
-
- while (data_bytes_copied < data_len) {
- assert(entry != NULL);
-
- rc = nffs_block_from_hash_entry(&block, entry);
- if (rc != 0) {
- return rc;
- }
-
- copy_len = sizeof (struct nffs_disk_block) + block.nb_data_len;
- rc = nffs_gc_copy_object(entry, copy_len, to_area_idx);
- if (rc != 0) {
- return rc;
- }
- data_bytes_copied += block.nb_data_len;
-
- entry = block.nb_prev;
- }
-
- return 0;
-}
-
-/**
- * Moves a chain of blocks from one area to another. This function attempts to
- * collate the blocks into a single new block in the destination area.
- *
- * @param last_entry The last block entry in the chain.
- * @param data_len The total length of data to collate.
- * @param to_area_idx The index of the area to copy to.
- * @param inout_next This parameter is only necessary if you are
- * calling this function during an iteration
- * of the entire hash table; pass null
- * otherwise.
- * On input, this points to the next hash entry
- * you plan on processing.
- * On output, this points to the next hash entry
- * that should be processed.
- *
- * @return 0 on success;
- * FS_ENOMEM if there is insufficient heap;
- * other nonzero on failure.
- */
-static int
-nffs_gc_block_chain_collate(struct nffs_hash_entry *last_entry,
- uint32_t data_len, uint8_t to_area_idx,
- struct nffs_hash_entry **inout_next)
-{
- struct nffs_disk_block disk_block;
- struct nffs_hash_entry *entry;
- struct nffs_area *to_area;
- struct nffs_block block;
- uint32_t to_area_offset;
- uint32_t from_area_offset;
- uint32_t data_offset;
- uint8_t *data;
- uint8_t from_area_idx;
- int rc;
-
- data = malloc(data_len);
- if (data == NULL) {
- rc = FS_ENOMEM;
- goto done;
- }
-
- to_area = nffs_areas + to_area_idx;
-
- entry = last_entry;
- data_offset = data_len;
- while (data_offset > 0) {
- rc = nffs_block_from_hash_entry(&block, entry);
- if (rc != 0) {
- goto done;
- }
- data_offset -= block.nb_data_len;
-
- nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc,
- &from_area_idx, &from_area_offset);
- from_area_offset += sizeof disk_block;
- rc = nffs_flash_read(from_area_idx, from_area_offset,
- data + data_offset, block.nb_data_len);
- if (rc != 0) {
- goto done;
- }
-
- if (entry != last_entry) {
- if (inout_next != NULL && *inout_next == entry) {
- *inout_next = SLIST_NEXT(entry, nhe_next);
- }
- nffs_block_delete_from_ram(entry);
- }
- entry = block.nb_prev;
- }
-
- memset(&disk_block, 0, sizeof disk_block);
- disk_block.ndb_magic = NFFS_BLOCK_MAGIC;
- disk_block.ndb_id = block.nb_hash_entry->nhe_id;
- disk_block.ndb_seq = block.nb_seq + 1;
- disk_block.ndb_inode_id = block.nb_inode_entry->nie_hash_entry.nhe_id;
- if (entry == NULL) {
- disk_block.ndb_prev_id = NFFS_ID_NONE;
- } else {
- disk_block.ndb_prev_id = entry->nhe_id;
- }
- disk_block.ndb_data_len = data_len;
- nffs_crc_disk_block_fill(&disk_block, data);
-
- to_area_offset = to_area->na_cur;
- rc = nffs_flash_write(to_area_idx, to_area_offset,
- &disk_block, sizeof disk_block);
- if (rc != 0) {
- goto done;
- }
-
- rc = nffs_flash_write(to_area_idx, to_area_offset + sizeof disk_block,
- data, data_len);
- if (rc != 0) {
- goto done;
- }
-
- last_entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset);
-
- rc = 0;
-
- ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, to_area_idx,
- to_area_offset) == 0);
-
-done:
- free(data);
- return rc;
-}
-
-/**
- * Moves a chain of blocks from one area to another. This function attempts to
- * collate the blocks into a single new block in the destination area. If
- * there is insufficient heap memory do to this, the function falls back to
- * copying each block separately.
- *
- * @param last_entry The last block entry in the chain.
- * @param multiple_blocks 0=single block; 1=more than one block.
- * @param data_len The total length of data to collate.
- * @param to_area_idx The index of the area to copy to.
- * @param inout_next This parameter is only necessary if you are
- * calling this function during an iteration
- * of the entire hash table; pass null
- * otherwise.
- * On input, this points to the next hash entry
- * you plan on processing.
- * On output, this points to the next hash entry
- * that should be processed.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_gc_block_chain(struct nffs_hash_entry *last_entry, int multiple_blocks,
- uint32_t data_len, uint8_t to_area_idx,
- struct nffs_hash_entry **inout_next)
-{
- int rc;
-
- if (!multiple_blocks) {
- /* If there is only one block, collation has the same effect as a
- * simple copy. Just perform the more efficient copy.
- */
- rc = nffs_gc_block_chain_copy(last_entry, data_len, to_area_idx);
- } else {
- rc = nffs_gc_block_chain_collate(last_entry, data_len, to_area_idx,
- inout_next);
- if (rc == FS_ENOMEM) {
- /* Insufficient heap for collation; just copy each block one by
- * one.
- */
- rc = nffs_gc_block_chain_copy(last_entry, data_len, to_area_idx);
- }
- }
-
- return rc;
-}
-
-static int
-nffs_gc_inode_blocks(struct nffs_inode_entry *inode_entry,
- uint8_t from_area_idx, uint8_t to_area_idx,
- struct nffs_hash_entry **inout_next)
-{
- struct nffs_hash_entry *last_entry;
- struct nffs_hash_entry *entry;
- struct nffs_block block;
- uint32_t prospective_data_len;
- uint32_t area_offset;
- uint32_t data_len;
- uint8_t area_idx;
- int multiple_blocks;
- int rc;
-
- assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
-
- data_len = 0;
- last_entry = NULL;
- multiple_blocks = 0;
- entry = inode_entry->nie_last_block_entry;
- while (entry != NULL) {
- rc = nffs_block_from_hash_entry(&block, entry);
- if (rc != 0) {
- return rc;
- }
-
- nffs_flash_loc_expand(entry->nhe_flash_loc, &area_idx, &area_offset);
- if (area_idx == from_area_idx) {
- if (last_entry == NULL) {
- last_entry = entry;
- }
-
- prospective_data_len = data_len + block.nb_data_len;
- if (prospective_data_len <= nffs_block_max_data_sz) {
- data_len = prospective_data_len;
- if (last_entry != entry) {
- multiple_blocks = 1;
- }
- } else {
- rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
- to_area_idx, inout_next);
- if (rc != 0) {
- return rc;
- }
- last_entry = entry;
- data_len = block.nb_data_len;
- multiple_blocks = 0;
- }
- } else {
- if (last_entry != NULL) {
- rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
- to_area_idx, inout_next);
- if (rc != 0) {
- return rc;
- }
-
- last_entry = NULL;
- data_len = 0;
- multiple_blocks = 0;
- }
- }
-
- entry = block.nb_prev;
- }
-
- if (last_entry != NULL) {
- rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
- to_area_idx, inout_next);
- if (rc != 0) {
- return rc;
- }
- }
-
- return 0;
-}
-
-/**
- * Triggers a garbage collection cycle. This is implemented as follows:
- *
- * (1) The non-scratch area with the lowest garbage collection sequence
- * number is selected as the "source area." If there are other areas
- * with the same sequence number, the first one encountered is selected.
- *
- * (2) The source area's ID is written to the scratch area's header,
- * transforming it into a non-scratch ID. The former scratch area is now
- * known as the "destination area."
- *
- * (3) The RAM representation is exhaustively searched for objects which are
- * resident in the source area. The copy is accomplished as follows:
- *
- * For each inode:
- * (a) If the inode is resident in the source area, copy the inode
- * record to the destination area.
- *
- * (b) Walk the inode's list of data blocks, starting with the last
- * block in the file. Each block that is resident in the source
- * area is copied to the destination area. If there is a run of
- * two or more blocks that are resident in the source area, they
- * are consolidated and copied to the destination area as a single
- * new block.
- *
- * (4) The source area is reformatted as a scratch sector (i.e., its header
- * indicates an ID of 0xffff). The area's garbage collection sequence
- * number is incremented prior to rewriting the header. This area is now
- * the new scratch sector.
- *
- * @param out_area_idx On success, the ID of the cleaned up area gets
- * written here. Pass null if you do not need
- * this information.
- *
- * @return 0 on success; nonzero on error.
- */
-int
-nffs_gc(uint8_t *out_area_idx)
-{
- struct nffs_hash_entry *entry;
- struct nffs_hash_entry *next;
- struct nffs_area *from_area;
- struct nffs_area *to_area;
- struct nffs_inode_entry *inode_entry;
- uint32_t area_offset;
- uint8_t from_area_idx;
- uint8_t area_idx;
- int rc;
- int i;
-
- from_area_idx = nffs_gc_select_area();
- from_area = nffs_areas + from_area_idx;
- to_area = nffs_areas + nffs_scratch_area_idx;
-
- rc = nffs_format_from_scratch_area(nffs_scratch_area_idx,
- from_area->na_id);
- if (rc != 0) {
- return rc;
- }
-
- for (i = 0; i < NFFS_HASH_SIZE; i++) {
- entry = SLIST_FIRST(nffs_hash + i);
- while (entry != NULL) {
- next = SLIST_NEXT(entry, nhe_next);
-
- if (nffs_hash_id_is_inode(entry->nhe_id)) {
- /* The inode gets copied if it is in the source area. */
- nffs_flash_loc_expand(entry->nhe_flash_loc,
- &area_idx, &area_offset);
- inode_entry = (struct nffs_inode_entry *)entry;
- if (area_idx == from_area_idx) {
- rc = nffs_gc_copy_inode(inode_entry,
- nffs_scratch_area_idx);
- if (rc != 0) {
- return rc;
- }
- }
-
- /* If the inode is a file, all constituent data blocks that are
- * resident in the source area get copied.
- */
- if (nffs_hash_id_is_file(entry->nhe_id)) {
- rc = nffs_gc_inode_blocks(inode_entry, from_area_idx,
- nffs_scratch_area_idx, &next);
- if (rc != 0) {
- return rc;
- }
- }
- }
-
- entry = next;
- }
- }
-
- /* The amount of written data should never increase as a result of a gc
- * cycle.
- */
- assert(to_area->na_cur <= from_area->na_cur);
-
- /* Turn the source area into the new scratch area. */
- from_area->na_gc_seq++;
- rc = nffs_format_area(from_area_idx, 1);
- if (rc != 0) {
- return rc;
- }
-
- if (out_area_idx != NULL) {
- *out_area_idx = nffs_scratch_area_idx;
- }
-
- nffs_scratch_area_idx = from_area_idx;
-
- return 0;
-}
-
-/**
- * Repeatedly performs garbage collection cycles until there is enough free
- * space to accommodate an object of the specified size. If there still isn't
- * enough free space after every area has been garbage collected, this function
- * fails.
- *
- * @param space The number of bytes of free space required.
- * @param out_area_idx On success, the index of the area which can
- * accommodate the necessary data.
- *
- * @return 0 on success;
- * FS_EFULL if the necessary space could not be
- * freed.
- * nonzero on other failure.
- */
-int
-nffs_gc_until(uint32_t space, uint8_t *out_area_idx)
-{
- int rc;
- int i;
-
- for (i = 0; i < nffs_num_areas; i++) {
- rc = nffs_gc(out_area_idx);
- if (rc != 0) {
- return rc;
- }
-
- if (nffs_area_free_space(nffs_areas + *out_area_idx) >= space) {
- return 0;
- }
- }
-
- return FS_EFULL;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_hash.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_hash.c b/libs/nffs/src/nffs_hash.c
deleted file mode 100644
index 4d6bff2..0000000
--- a/libs/nffs/src/nffs_hash.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <stddef.h>
-#include <string.h>
-#include <assert.h>
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-
-struct nffs_hash_list *nffs_hash;
-
-uint32_t nffs_hash_next_dir_id;
-uint32_t nffs_hash_next_file_id;
-uint32_t nffs_hash_next_block_id;
-
-int
-nffs_hash_id_is_dir(uint32_t id)
-{
- return id >= NFFS_ID_DIR_MIN && id < NFFS_ID_DIR_MAX;
-}
-
-int
-nffs_hash_id_is_file(uint32_t id)
-{
- return id >= NFFS_ID_FILE_MIN && id < NFFS_ID_FILE_MAX;
-}
-
-int
-nffs_hash_id_is_inode(uint32_t id)
-{
- return nffs_hash_id_is_dir(id) || nffs_hash_id_is_file(id);
-}
-
-int
-nffs_hash_id_is_block(uint32_t id)
-{
- return id >= NFFS_ID_BLOCK_MIN && id < NFFS_ID_BLOCK_MAX;
-}
-
-static int
-nffs_hash_fn(uint32_t id)
-{
- return id % NFFS_HASH_SIZE;
-}
-
-struct nffs_hash_entry *
-nffs_hash_find(uint32_t id)
-{
- struct nffs_hash_entry *entry;
- struct nffs_hash_entry *prev;
- struct nffs_hash_list *list;
- int idx;
-
- idx = nffs_hash_fn(id);
- list = nffs_hash + idx;
-
- prev = NULL;
- SLIST_FOREACH(entry, list, nhe_next) {
- if (entry->nhe_id == id) {
- /* Put entry at the front of the list. */
- if (prev != NULL) {
- SLIST_NEXT(prev, nhe_next) = SLIST_NEXT(entry, nhe_next);
- SLIST_INSERT_HEAD(list, entry, nhe_next);
- }
- return entry;
- }
-
- prev = entry;
- }
-
- return NULL;
-}
-
-struct nffs_inode_entry *
-nffs_hash_find_inode(uint32_t id)
-{
- struct nffs_hash_entry *entry;
-
- assert(nffs_hash_id_is_inode(id));
-
- entry = nffs_hash_find(id);
- return (struct nffs_inode_entry *)entry;
-}
-
-struct nffs_hash_entry *
-nffs_hash_find_block(uint32_t id)
-{
- struct nffs_hash_entry *entry;
-
- assert(nffs_hash_id_is_block(id));
-
- entry = nffs_hash_find(id);
- return entry;
-}
-
-void
-nffs_hash_insert(struct nffs_hash_entry *entry)
-{
- struct nffs_hash_list *list;
- int idx;
-
- idx = nffs_hash_fn(entry->nhe_id);
- list = nffs_hash + idx;
-
- SLIST_INSERT_HEAD(list, entry, nhe_next);
-}
-
-void
-nffs_hash_remove(struct nffs_hash_entry *entry)
-{
- struct nffs_hash_list *list;
- int idx;
-
- idx = nffs_hash_fn(entry->nhe_id);
- list = nffs_hash + idx;
-
- SLIST_REMOVE(list, entry, nffs_hash_entry, nhe_next);
-}
-
-int
-nffs_hash_init(void)
-{
- int i;
-
- free(nffs_hash);
-
- nffs_hash = malloc(NFFS_HASH_SIZE * sizeof *nffs_hash);
- if (nffs_hash == NULL) {
- return FS_ENOMEM;
- }
-
- for (i = 0; i < NFFS_HASH_SIZE; i++) {
- SLIST_INIT(nffs_hash + i);
- }
-
- return 0;
-}
-
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_inode.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_inode.c b/libs/nffs/src/nffs_inode.c
deleted file mode 100644
index f4d1e46..0000000
--- a/libs/nffs/src/nffs_inode.c
+++ /dev/null
@@ -1,906 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <stddef.h>
-#include <string.h>
-#include <assert.h>
-#include "testutil/testutil.h"
-#include "os/os_mempool.h"
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-#include "crc16.h"
-
-/* Partition the flash buffer into two equal halves; used for filename
- * comparisons.
- */
-#define NFFS_INODE_FILENAME_BUF_SZ (sizeof nffs_flash_buf / 2)
-static uint8_t *nffs_inode_filename_buf0 = nffs_flash_buf;
-static uint8_t *nffs_inode_filename_buf1 =
- nffs_flash_buf + NFFS_INODE_FILENAME_BUF_SZ;
-
-/** A list of directory inodes with pending unlink operations. */
-static struct nffs_hash_list nffs_inode_unlink_list;
-
-struct nffs_inode_entry *
-nffs_inode_entry_alloc(void)
-{
- struct nffs_inode_entry *inode_entry;
-
- inode_entry = os_memblock_get(&nffs_inode_entry_pool);
- if (inode_entry != NULL) {
- memset(inode_entry, 0, sizeof *inode_entry);
- }
-
- return inode_entry;
-}
-
-void
-nffs_inode_entry_free(struct nffs_inode_entry *inode_entry)
-{
- if (inode_entry != NULL) {
- assert(nffs_hash_id_is_inode(inode_entry->nie_hash_entry.nhe_id));
- os_memblock_put(&nffs_inode_entry_pool, inode_entry);
- }
-}
-
-uint32_t
-nffs_inode_disk_size(const struct nffs_inode *inode)
-{
- return sizeof (struct nffs_disk_inode) + inode->ni_filename_len;
-}
-
-int
-nffs_inode_read_disk(uint8_t area_idx, uint32_t offset,
- struct nffs_disk_inode *out_disk_inode)
-{
- int rc;
-
- rc = nffs_flash_read(area_idx, offset, out_disk_inode,
- sizeof *out_disk_inode);
- if (rc != 0) {
- return rc;
- }
- if (out_disk_inode->ndi_magic != NFFS_INODE_MAGIC) {
- return FS_EUNEXP;
- }
-
- return 0;
-}
-
-int
-nffs_inode_write_disk(const struct nffs_disk_inode *disk_inode,
- const char *filename, uint8_t area_idx,
- uint32_t area_offset)
-{
- int rc;
-
- rc = nffs_flash_write(area_idx, area_offset, disk_inode,
- sizeof *disk_inode);
- if (rc != 0) {
- return rc;
- }
-
- if (disk_inode->ndi_filename_len != 0) {
- rc = nffs_flash_write(area_idx, area_offset + sizeof *disk_inode,
- filename, disk_inode->ndi_filename_len);
- if (rc != 0) {
- return rc;
- }
- }
-
- ASSERT_IF_TEST(nffs_crc_disk_inode_validate(disk_inode, area_idx,
- area_offset) == 0);
-
- return 0;
-}
-
-int
-nffs_inode_calc_data_length(struct nffs_inode_entry *inode_entry,
- uint32_t *out_len)
-{
- struct nffs_hash_entry *cur;
- struct nffs_block block;
- int rc;
-
- assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
-
- *out_len = 0;
-
- cur = inode_entry->nie_last_block_entry;
- while (cur != NULL) {
- rc = nffs_block_from_hash_entry(&block, cur);
- if (rc != 0) {
- return rc;
- }
-
- *out_len += block.nb_data_len;
-
- cur = block.nb_prev;
- }
-
- return 0;
-}
-
-int
-nffs_inode_data_len(struct nffs_inode_entry *inode_entry, uint32_t *out_len)
-{
- struct nffs_cache_inode *cache_inode;
- int rc;
-
- rc = nffs_cache_inode_ensure(&cache_inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- *out_len = cache_inode->nci_file_size;
-
- return 0;
-}
-
-int
-nffs_inode_from_entry(struct nffs_inode *out_inode,
- struct nffs_inode_entry *entry)
-{
- struct nffs_disk_inode disk_inode;
- uint32_t area_offset;
- uint8_t area_idx;
- int cached_name_len;
- int rc;
-
- nffs_flash_loc_expand(entry->nie_hash_entry.nhe_flash_loc,
- &area_idx, &area_offset);
-
- rc = nffs_inode_read_disk(area_idx, area_offset, &disk_inode);
- if (rc != 0) {
- return rc;
- }
-
- out_inode->ni_inode_entry = entry;
- out_inode->ni_seq = disk_inode.ndi_seq;
- if (disk_inode.ndi_parent_id == NFFS_ID_NONE) {
- out_inode->ni_parent = NULL;
- } else {
- out_inode->ni_parent = nffs_hash_find_inode(disk_inode.ndi_parent_id);
- }
- out_inode->ni_filename_len = disk_inode.ndi_filename_len;
-
- if (out_inode->ni_filename_len > NFFS_SHORT_FILENAME_LEN) {
- cached_name_len = NFFS_SHORT_FILENAME_LEN;
- } else {
- cached_name_len = out_inode->ni_filename_len;
- }
- if (cached_name_len != 0) {
- rc = nffs_flash_read(area_idx, area_offset + sizeof disk_inode,
- out_inode->ni_filename, cached_name_len);
- if (rc != 0) {
- return rc;
- }
- }
-
- return 0;
-}
-
-uint32_t
-nffs_inode_parent_id(const struct nffs_inode *inode)
-{
- if (inode->ni_parent == NULL) {
- return NFFS_ID_NONE;
- } else {
- return inode->ni_parent->nie_hash_entry.nhe_id;
- }
-}
-
-static int
-nffs_inode_delete_blocks_from_ram(struct nffs_inode_entry *inode_entry)
-{
- int rc;
-
- assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
-
- while (inode_entry->nie_last_block_entry != NULL) {
- rc = nffs_block_delete_from_ram(inode_entry->nie_last_block_entry);
- if (rc != 0) {
- return rc;
- }
- }
-
- return 0;
-}
-
-static int
-nffs_inode_delete_from_ram(struct nffs_inode_entry *inode_entry)
-{
- int rc;
-
- if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
- rc = nffs_inode_delete_blocks_from_ram(inode_entry);
- if (rc != 0) {
- return rc;
- }
- }
-
- nffs_cache_inode_delete(inode_entry);
- nffs_hash_remove(&inode_entry->nie_hash_entry);
- nffs_inode_entry_free(inode_entry);
-
- return 0;
-}
-
-/**
- * Inserts the specified inode entry into the unlink list. Because a hash
- * entry only has a single 'next' pointer, this function removes the entry from
- * the hash table prior to inserting it into the unlink list.
- *
- * @param inode_entry The inode entry to insert.
- */
-static void
-nffs_inode_insert_unlink_list(struct nffs_inode_entry *inode_entry)
-{
- nffs_hash_remove(&inode_entry->nie_hash_entry);
- SLIST_INSERT_HEAD(&nffs_inode_unlink_list, &inode_entry->nie_hash_entry,
- nhe_next);
-}
-
-/**
- * Decrements the reference count of the specified inode entry.
- *
- * @param inode_entry The inode entry whose reference count
- * should be decremented.
- */
-int
-nffs_inode_dec_refcnt(struct nffs_inode_entry *inode_entry)
-{
- int rc;
-
- assert(inode_entry->nie_refcnt > 0);
-
- inode_entry->nie_refcnt--;
- if (inode_entry->nie_refcnt == 0) {
- if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
- rc = nffs_inode_delete_from_ram(inode_entry);
- if (rc != 0) {
- return rc;
- }
- } else {
- nffs_inode_insert_unlink_list(inode_entry);
- }
- }
-
- return 0;
-}
-
-/**
- * Unlinks every directory inode entry present in the unlink list. After this
- * function completes:
- * o Each descendant directory is deleted from RAM.
- * o Each descendant file has its reference count decremented (and deleted
- * from RAM if its reference count reaches zero).
- *
- * @param inout_next This parameter is only necessary if you are
- * calling this function during an iteration
- * of the entire hash table; pass null
- * otherwise.
- * On input, this points to the next hash entry
- * you plan on processing.
- * On output, this points to the next hash entry
- * that should be processed.
- */
-static int
-nffs_inode_process_unlink_list(struct nffs_hash_entry **inout_next)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_inode_entry *child_next;
- struct nffs_inode_entry *child;
- struct nffs_hash_entry *hash_entry;
- int rc;
-
- while ((hash_entry = SLIST_FIRST(&nffs_inode_unlink_list)) != NULL) {
- assert(nffs_hash_id_is_dir(hash_entry->nhe_id));
-
- SLIST_REMOVE_HEAD(&nffs_inode_unlink_list, nhe_next);
-
- inode_entry = (struct nffs_inode_entry *)hash_entry;
-
- /* Recursively unlink each child. */
- child = SLIST_FIRST(&inode_entry->nie_child_list);
- while (child != NULL) {
- child_next = SLIST_NEXT(child, nie_sibling_next);
-
- if (inout_next != NULL && *inout_next == &child->nie_hash_entry) {
- *inout_next = &child_next->nie_hash_entry;
- }
-
- rc = nffs_inode_dec_refcnt(child);
- if (rc != 0) {
- return rc;
- }
-
- child = child_next;
- }
-
- /* The directory is already removed from the hash table; just free its
- * memory.
- */
- nffs_inode_entry_free(inode_entry);
- }
-
- return 0;
-}
-
-int
-nffs_inode_delete_from_disk(struct nffs_inode *inode)
-{
- struct nffs_disk_inode disk_inode;
- uint32_t offset;
- uint8_t area_idx;
- int rc;
-
- /* Make sure it isn't already deleted. */
- assert(inode->ni_parent != NULL);
-
- rc = nffs_misc_reserve_space(sizeof disk_inode, &area_idx, &offset);
- if (rc != 0) {
- return rc;
- }
-
- inode->ni_seq++;
-
- disk_inode.ndi_magic = NFFS_INODE_MAGIC;
- disk_inode.ndi_id = inode->ni_inode_entry->nie_hash_entry.nhe_id;
- disk_inode.ndi_seq = inode->ni_seq;
- disk_inode.ndi_parent_id = NFFS_ID_NONE;
- disk_inode.ndi_filename_len = 0;
- nffs_crc_disk_inode_fill(&disk_inode, "");
-
- rc = nffs_inode_write_disk(&disk_inode, "", area_idx, offset);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-int
-nffs_inode_rename(struct nffs_inode_entry *inode_entry,
- struct nffs_inode_entry *new_parent,
- const char *new_filename)
-{
- struct nffs_disk_inode disk_inode;
- struct nffs_inode inode;
- uint32_t area_offset;
- uint8_t area_idx;
- int filename_len;
- int rc;
-
- rc = nffs_inode_from_entry(&inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- if (inode.ni_parent != new_parent) {
- if (inode.ni_parent != NULL) {
- nffs_inode_remove_child(&inode);
- }
- if (new_parent != NULL) {
- rc = nffs_inode_add_child(new_parent, inode.ni_inode_entry);
- if (rc != 0) {
- return rc;
- }
- }
- inode.ni_parent = new_parent;
- }
-
- if (new_filename != NULL) {
- filename_len = strlen(new_filename);
- } else {
- filename_len = inode.ni_filename_len;
- nffs_flash_loc_expand(inode_entry->nie_hash_entry.nhe_flash_loc,
- &area_idx, &area_offset);
- rc = nffs_flash_read(area_idx,
- area_offset + sizeof (struct nffs_disk_inode),
- nffs_flash_buf, filename_len);
- if (rc != 0) {
- return rc;
- }
-
- new_filename = (char *)nffs_flash_buf;
- }
-
- rc = nffs_misc_reserve_space(sizeof disk_inode + filename_len,
- &area_idx, &area_offset);
- if (rc != 0) {
- return rc;
- }
-
- disk_inode.ndi_magic = NFFS_INODE_MAGIC;
- disk_inode.ndi_id = inode_entry->nie_hash_entry.nhe_id;
- disk_inode.ndi_seq = inode.ni_seq + 1;
- disk_inode.ndi_parent_id = nffs_inode_parent_id(&inode);
- disk_inode.ndi_filename_len = filename_len;
- nffs_crc_disk_inode_fill(&disk_inode, new_filename);
-
- rc = nffs_inode_write_disk(&disk_inode, new_filename, area_idx,
- area_offset);
- if (rc != 0) {
- return rc;
- }
-
- inode_entry->nie_hash_entry.nhe_flash_loc =
- nffs_flash_loc(area_idx, area_offset);
-
- return 0;
-}
-
-static int
-nffs_inode_read_filename_chunk(const struct nffs_inode *inode,
- uint8_t filename_offset, void *buf, int len)
-{
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- assert(filename_offset + len <= inode->ni_filename_len);
-
- nffs_flash_loc_expand(inode->ni_inode_entry->nie_hash_entry.nhe_flash_loc,
- &area_idx, &area_offset);
- area_offset += sizeof (struct nffs_disk_inode) + filename_offset;
-
- rc = nffs_flash_read(area_idx, area_offset, buf, len);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Retrieves the filename of the specified inode. The retrieved
- * filename is always null-terminated. To ensure enough space to hold the full
- * filename plus a null-termintor, a destination buffer of size
- * (NFFS_FILENAME_MAX_LEN + 1) should be used.
- *
- * @param inode_entry The inode entry to query.
- * @param max_len The size of the "out_name" character buffer.
- * @param out_name On success, the entry's filename is written
- * here; always null-terminated.
- * @param out_name_len On success, contains the actual length of the
- * filename, NOT including the
- * null-terminator.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_inode_read_filename(struct nffs_inode_entry *inode_entry, size_t max_len,
- char *out_name, uint8_t *out_full_len)
-{
- struct nffs_inode inode;
- int read_len;
- int rc;
-
- rc = nffs_inode_from_entry(&inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- if (max_len > inode.ni_filename_len) {
- read_len = inode.ni_filename_len;
- } else {
- read_len = max_len - 1;
- }
-
- rc = nffs_inode_read_filename_chunk(&inode, 0, out_name, read_len);
- if (rc != 0) {
- return rc;
- }
-
- out_name[read_len] = '\0';
-
- return 0;
-}
-
-int
-nffs_inode_add_child(struct nffs_inode_entry *parent,
- struct nffs_inode_entry *child)
-{
- struct nffs_inode_entry *prev;
- struct nffs_inode_entry *cur;
- struct nffs_inode child_inode;
- struct nffs_inode cur_inode;
- int cmp;
- int rc;
-
- assert(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
-
- rc = nffs_inode_from_entry(&child_inode, child);
- if (rc != 0) {
- return rc;
- }
-
- prev = NULL;
- SLIST_FOREACH(cur, &parent->nie_child_list, nie_sibling_next) {
- assert(cur != child);
- rc = nffs_inode_from_entry(&cur_inode, cur);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_filename_cmp_flash(&child_inode, &cur_inode, &cmp);
- if (rc != 0) {
- return rc;
- }
-
- if (cmp < 0) {
- break;
- }
-
- prev = cur;
- }
-
- if (prev == NULL) {
- SLIST_INSERT_HEAD(&parent->nie_child_list, child, nie_sibling_next);
- } else {
- SLIST_INSERT_AFTER(prev, child, nie_sibling_next);
- }
-
- return 0;
-}
-
-void
-nffs_inode_remove_child(struct nffs_inode *child)
-{
- struct nffs_inode_entry *parent;
-
- parent = child->ni_parent;
- assert(parent != NULL);
- assert(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
- SLIST_REMOVE(&parent->nie_child_list, child->ni_inode_entry,
- nffs_inode_entry, nie_sibling_next);
- SLIST_NEXT(child->ni_inode_entry, nie_sibling_next) = NULL;
-}
-
-int
-nffs_inode_filename_cmp_ram(const struct nffs_inode *inode,
- const char *name, int name_len,
- int *result)
-{
- int short_len;
- int chunk_len;
- int rem_len;
- int off;
- int rc;
-
- if (name_len < inode->ni_filename_len) {
- short_len = name_len;
- } else {
- short_len = inode->ni_filename_len;
- }
-
- if (short_len <= NFFS_SHORT_FILENAME_LEN) {
- chunk_len = short_len;
- } else {
- chunk_len = NFFS_SHORT_FILENAME_LEN;
- }
- *result = strncmp((char *)inode->ni_filename, name, chunk_len);
-
- off = chunk_len;
- while (*result == 0 && off < short_len) {
- rem_len = short_len - off;
- if (rem_len > NFFS_INODE_FILENAME_BUF_SZ) {
- chunk_len = NFFS_INODE_FILENAME_BUF_SZ;
- } else {
- chunk_len = rem_len;
- }
-
- rc = nffs_inode_read_filename_chunk(inode, off,
- nffs_inode_filename_buf0,
- chunk_len);
- if (rc != 0) {
- return rc;
- }
-
- *result = strncmp((char *)nffs_inode_filename_buf0, name + off,
- chunk_len);
- off += chunk_len;
- }
-
- if (*result == 0) {
- *result = inode->ni_filename_len - name_len;
- }
-
- return 0;
-}
-
-int
-nffs_inode_filename_cmp_flash(const struct nffs_inode *inode1,
- const struct nffs_inode *inode2,
- int *result)
-{
- int short_len;
- int chunk_len;
- int rem_len;
- int off;
- int rc;
-
- if (inode1->ni_filename_len < inode2->ni_filename_len) {
- short_len = inode1->ni_filename_len;
- } else {
- short_len = inode2->ni_filename_len;
- }
-
- if (short_len <= NFFS_SHORT_FILENAME_LEN) {
- chunk_len = short_len;
- } else {
- chunk_len = NFFS_SHORT_FILENAME_LEN;
- }
- *result = strncmp((char *)inode1->ni_filename,
- (char *)inode2->ni_filename,
- chunk_len);
-
- off = chunk_len;
- while (*result == 0 && off < short_len) {
- rem_len = short_len - off;
- if (rem_len > NFFS_INODE_FILENAME_BUF_SZ) {
- chunk_len = NFFS_INODE_FILENAME_BUF_SZ;
- } else {
- chunk_len = rem_len;
- }
-
- rc = nffs_inode_read_filename_chunk(inode1, off,
- nffs_inode_filename_buf0,
- chunk_len);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_read_filename_chunk(inode2, off,
- nffs_inode_filename_buf1,
- chunk_len);
- if (rc != 0) {
- return rc;
- }
-
- *result = strncmp((char *)nffs_inode_filename_buf0,
- (char *)nffs_inode_filename_buf1,
- chunk_len);
- off += chunk_len;
- }
-
- if (*result == 0) {
- *result = inode1->ni_filename_len - inode2->ni_filename_len;
- }
-
- return 0;
-}
-
-/**
- * Finds the set of blocks composing the specified address range within the
- * supplied file inode. This information is returned in the form of a
- * seek_info object.
- *
- * @param inode_entry The file inode to seek within.
- * @param offset The start address of the region to seek to.
- * @param length The length of the region to seek to.
- * @param out_seek_info On success, this gets populated with the result
- * of the seek operation.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_inode_seek(struct nffs_inode_entry *inode_entry, uint32_t offset,
- uint32_t length, struct nffs_seek_info *out_seek_info)
-{
- struct nffs_cache_inode *cache_inode;
- struct nffs_hash_entry *cur_entry;
- struct nffs_block block;
- uint32_t block_start;
- uint32_t cur_offset;
- uint32_t seek_end;
- int rc;
-
- assert(length > 0);
- assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
-
- rc = nffs_cache_inode_ensure(&cache_inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- if (offset > cache_inode->nci_file_size) {
- return FS_ERANGE;
- }
- if (offset == cache_inode->nci_file_size) {
- memset(&out_seek_info->nsi_last_block, 0,
- sizeof out_seek_info->nsi_last_block);
- out_seek_info->nsi_last_block.nb_hash_entry = NULL;
- out_seek_info->nsi_block_file_off = 0;
- out_seek_info->nsi_file_len = cache_inode->nci_file_size;
- return 0;
- }
-
- seek_end = offset + length;
-
- cur_entry = inode_entry->nie_last_block_entry;
- cur_offset = cache_inode->nci_file_size;
-
- while (1) {
- rc = nffs_block_from_hash_entry(&block, cur_entry);
- if (rc != 0) {
- return rc;
- }
-
- block_start = cur_offset - block.nb_data_len;
- if (seek_end > block_start) {
- out_seek_info->nsi_last_block = block;
- out_seek_info->nsi_block_file_off = block_start;
- out_seek_info->nsi_file_len = cache_inode->nci_file_size;
- return 0;
- }
-
- cur_offset = block_start;
- cur_entry = block.nb_prev;
- }
-}
-
-/**
- * Reads data from the specified file inode.
- *
- * @param inode_entry The inode to read from.
- * @param offset The offset within the file to start the read
- * at.
- * @param len The number of bytes to attempt to read.
- * @param out_data On success, the read data gets written here.
- * @param out_len On success, the number of bytes actually read
- * gets written here.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_inode_read(struct nffs_inode_entry *inode_entry, uint32_t offset,
- uint32_t len, void *out_data, uint32_t *out_len)
-{
- struct nffs_cache_inode *cache_inode;
- struct nffs_cache_block *cache_block;
- uint32_t block_end;
- uint32_t dst_off;
- uint32_t src_off;
- uint32_t src_end;
- uint16_t block_off;
- uint16_t chunk_sz;
- uint8_t *dptr;
- int rc;
-
- if (len == 0) {
- if (out_len != NULL) {
- *out_len = 0;
- }
- return 0;
- }
-
- rc = nffs_cache_inode_ensure(&cache_inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- src_end = offset + len;
- if (src_end > cache_inode->nci_file_size) {
- src_end = cache_inode->nci_file_size;
- }
-
- /* Initialize variables for the first iteration. */
- dst_off = src_end - offset;
- src_off = src_end;
- dptr = out_data;
- cache_block = NULL;
-
- /* Read each relevant block into the destination buffer, iterating in
- * reverse.
- */
- while (dst_off > 0) {
- if (cache_block == NULL) {
- rc = nffs_cache_seek(cache_inode, src_off - 1, &cache_block);
- if (rc != 0) {
- return rc;
- }
- }
-
- if (cache_block->ncb_file_offset < offset) {
- block_off = offset - cache_block->ncb_file_offset;
- } else {
- block_off = 0;
- }
-
- block_end = cache_block->ncb_file_offset +
- cache_block->ncb_block.nb_data_len;
- chunk_sz = cache_block->ncb_block.nb_data_len - block_off;
- if (block_end > src_end) {
- chunk_sz -= block_end - src_end;
- }
-
- dst_off -= chunk_sz;
- src_off -= chunk_sz;
-
- rc = nffs_block_read_data(&cache_block->ncb_block, block_off, chunk_sz,
- dptr + dst_off);
- if (rc != 0) {
- return rc;
- }
-
- cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list, ncb_link);
- }
-
- if (out_len != NULL) {
- *out_len = src_end - offset;
- }
-
- return 0;
-}
-
-int
-nffs_inode_unlink_from_ram(struct nffs_inode *inode,
- struct nffs_hash_entry **out_next)
-{
- int rc;
-
- if (inode->ni_parent != NULL) {
- nffs_inode_remove_child(inode);
- }
-
- if (nffs_hash_id_is_dir(inode->ni_inode_entry->nie_hash_entry.nhe_id)) {
- nffs_inode_insert_unlink_list(inode->ni_inode_entry);
- rc = nffs_inode_process_unlink_list(out_next);
- } else {
- rc = nffs_inode_dec_refcnt(inode->ni_inode_entry);
- }
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Unlinks the file or directory represented by the specified inode. If the
- * inode represents a directory, all the directory's descendants are
- * recursively unlinked. Any open file handles refering to an unlinked file
- * remain valid, and can be read from and written to.
- *
- * When an inode is unlinked, the following events occur:
- * o inode deletion record is written to disk.
- * o inode is removed from parent's child list.
- * o inode's reference count is decreased (if this brings it to zero, the
- * inode is fully deleted from RAM).
- *
- * @param inode The inode to unlink.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_inode_unlink(struct nffs_inode *inode)
-{
- int rc;
-
- rc = nffs_inode_delete_from_disk(inode);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_unlink_from_ram(inode, NULL);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_misc.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_misc.c b/libs/nffs/src/nffs_misc.c
deleted file mode 100644
index e613b2c..0000000
--- a/libs/nffs/src/nffs_misc.c
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include "os/os_malloc.h"
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-#include "crc16.h"
-
-/**
- * Determines if the file system contains a valid root directory. For the root
- * directory to be valid, it must be present and have the following traits:
- * o ID equal to NFFS_ID_ROOT_DIR.
- * o No parent inode.
- *
- * @return 0 if there is a valid root directory;
- * FS_ECORRUPT if there is not a valid root
- * directory;
- * nonzero on other error.
- */
-int
-nffs_misc_validate_root_dir(void)
-{
- struct nffs_inode inode;
- int rc;
-
- if (nffs_root_dir == NULL) {
- return FS_ECORRUPT;
- }
-
- if (nffs_root_dir->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
- return FS_ECORRUPT;
- }
-
- rc = nffs_inode_from_entry(&inode, nffs_root_dir);
- if (rc != 0) {
- return rc;
- }
-
- if (inode.ni_parent != NULL) {
- return FS_ECORRUPT;
- }
-
- return 0;
-}
-
-/**
- * Determines if the system contains a valid scratch area. For a scratch area
- * to be valid, it must be at least as large as the other areas in the file
- * system.
- *
- * @return 0 if there is a valid scratch area;
- * FS_ECORRUPT otherwise.
- */
-int
-nffs_misc_validate_scratch(void)
-{
- uint32_t scratch_len;
- int i;
-
- if (nffs_scratch_area_idx == NFFS_AREA_ID_NONE) {
- /* No scratch area. */
- return FS_ECORRUPT;
- }
-
- scratch_len = nffs_areas[nffs_scratch_area_idx].na_length;
- for (i = 0; i < nffs_num_areas; i++) {
- if (nffs_areas[i].na_length > scratch_len) {
- return FS_ECORRUPT;
- }
- }
-
- return 0;
-}
-
-/**
- * Reserves the specified number of bytes within the specified area.
- *
- * @param area_idx The index of the area to reserve from.
- * @param space The number of bytes of free space required.
- * @param out_area_offset On success, the offset within the area gets
- * written here.
- *
- * @return 0 on success;
- * FS_EFULL if the area has insufficient free
- * space.
- */
-static int
-nffs_misc_reserve_space_area(uint8_t area_idx, uint16_t space,
- uint32_t *out_area_offset)
-{
- const struct nffs_area *area;
- uint32_t available;
-
- area = nffs_areas + area_idx;
- available = area->na_length - area->na_cur;
- if (available >= space) {
- *out_area_offset = area->na_cur;
- return 0;
- }
-
- return FS_EFULL;
-}
-
-/**
- * Finds an area that can accommodate an object of the specified size. If no
- * such area exists, this function performs a garbage collection cycle.
- *
- * @param space The number of bytes of free space required.
- * @param out_area_idx On success, the index of the suitable area gets
- * written here.
- * @param out_area_offset On success, the offset within the suitable area
- * gets written here.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_misc_reserve_space(uint16_t space,
- uint8_t *out_area_idx, uint32_t *out_area_offset)
-{
- uint8_t area_idx;
- int rc;
- int i;
-
- /* Find the first area with sufficient free space. */
- for (i = 0; i < nffs_num_areas; i++) {
- if (i != nffs_scratch_area_idx) {
- rc = nffs_misc_reserve_space_area(i, space, out_area_offset);
- if (rc == 0) {
- *out_area_idx = i;
- return 0;
- }
- }
- }
-
- /* No area can accommodate the request. Garbage collect until an area
- * has enough space.
- */
- rc = nffs_gc_until(space, &area_idx);
- if (rc != 0) {
- return rc;
- }
-
- /* Now try to reserve space. If insufficient space was reclaimed with
- * garbage collection, the above call would have failed, so this should
- * succeed.
- */
- rc = nffs_misc_reserve_space_area(area_idx, space, out_area_offset);
- assert(rc == 0);
-
- *out_area_idx = area_idx;
-
- return rc;
-}
-
-int
-nffs_misc_set_num_areas(uint8_t num_areas)
-{
- if (num_areas == 0) {
- free(nffs_areas);
- nffs_areas = NULL;
- } else {
- nffs_areas = realloc(nffs_areas, num_areas * sizeof *nffs_areas);
- if (nffs_areas == NULL) {
- return FS_ENOMEM;
- }
- }
-
- nffs_num_areas = num_areas;
-
- return 0;
-}
-
-/**
- * Calculates the data length of the largest block that could fit in an area of
- * the specified size.
- */
-static uint32_t
-nffs_misc_area_capacity_one(uint32_t area_length)
-{
- return area_length -
- sizeof (struct nffs_disk_area) -
- sizeof (struct nffs_disk_block);
-}
-
-/**
- * Calculates the data length of the largest block that could fit as a pair in
- * an area of the specified size.
- */
-static uint32_t
-nffs_misc_area_capacity_two(uint32_t area_length)
-{
- return (area_length - sizeof (struct nffs_disk_area)) / 2 -
- sizeof (struct nffs_disk_block);
-}
-
-/**
- * Calculates and sets the maximum block data length that the system supports.
- * The result of the calculation is the greatest number which satisfies all of
- * the following restrictions:
- * o No more than half the size of the smallest area.
- * o No more than 2048.
- * o No smaller than the data length of any existing data block.
- *
- * @param min_size The minimum allowed data length. This is the
- * data length of the largest block currently
- * in the file system.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_misc_set_max_block_data_len(uint16_t min_data_len)
-{
- uint32_t smallest_area;
- uint32_t half_smallest;
- int i;
-
- smallest_area = -1;
- for (i = 0; i < nffs_num_areas; i++) {
- if (nffs_areas[i].na_length < smallest_area) {
- smallest_area = nffs_areas[i].na_length;
- }
- }
-
- /* Don't allow a data block size bigger than the smallest area. */
- if (nffs_misc_area_capacity_one(smallest_area) < min_data_len) {
- return FS_ECORRUPT;
- }
-
- half_smallest = nffs_misc_area_capacity_two(smallest_area);
- if (half_smallest < NFFS_BLOCK_MAX_DATA_SZ_MAX) {
- nffs_block_max_data_sz = half_smallest;
- } else {
- nffs_block_max_data_sz = NFFS_BLOCK_MAX_DATA_SZ_MAX;
- }
-
- if (nffs_block_max_data_sz < min_data_len) {
- nffs_block_max_data_sz = min_data_len;
- }
-
- return 0;
-}
-
-int
-nffs_misc_create_lost_found_dir(void)
-{
- int rc;
-
- rc = nffs_path_new_dir("/lost+found", &nffs_lost_found_dir);
- switch (rc) {
- case 0:
- return 0;
-
- case FS_EEXIST:
- rc = nffs_path_find_inode_entry("/lost+found", &nffs_lost_found_dir);
- return rc;
-
- default:
- return rc;
- }
-}
-
-
-/**
- * Fully resets the nffs RAM representation.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_misc_reset(void)
-{
- int rc;
-
- nffs_cache_clear();
-
- rc = os_mempool_init(&nffs_file_pool, nffs_config.nc_num_files,
- sizeof (struct nffs_file), nffs_file_mem,
- "nffs_file_pool");
- if (rc != 0) {
- return FS_EOS;
- }
-
- rc = os_mempool_init(&nffs_inode_entry_pool, nffs_config.nc_num_inodes,
- sizeof (struct nffs_inode_entry), nffs_inode_mem,
- "nffs_inode_entry_pool");
- if (rc != 0) {
- return FS_EOS;
- }
-
- rc = os_mempool_init(&nffs_block_entry_pool, nffs_config.nc_num_blocks,
- sizeof (struct nffs_hash_entry), nffs_block_entry_mem,
- "nffs_block_entry_pool");
- if (rc != 0) {
- return FS_EOS;
- }
-
- rc = os_mempool_init(&nffs_cache_inode_pool,
- nffs_config.nc_num_cache_inodes,
- sizeof (struct nffs_cache_inode),
- nffs_cache_inode_mem, "nffs_cache_inode_pool");
- if (rc != 0) {
- return FS_EOS;
- }
-
- rc = os_mempool_init(&nffs_cache_block_pool,
- nffs_config.nc_num_cache_blocks,
- sizeof (struct nffs_cache_block),
- nffs_cache_block_mem, "nffs_cache_block_pool");
- if (rc != 0) {
- return FS_EOS;
- }
-
- rc = os_mempool_init(&nffs_dir_pool,
- nffs_config.nc_num_dirs,
- sizeof (struct nffs_dir),
- nffs_dir_mem, "nffs_dir_pool");
- if (rc != 0) {
- return FS_EOS;
- }
-
- rc = nffs_hash_init();
- if (rc != 0) {
- return rc;
- }
-
- free(nffs_areas);
- nffs_areas = NULL;
- nffs_num_areas = 0;
-
- nffs_root_dir = NULL;
- nffs_lost_found_dir = NULL;
- nffs_scratch_area_idx = NFFS_AREA_ID_NONE;
-
- nffs_hash_next_file_id = NFFS_ID_FILE_MIN;
- nffs_hash_next_dir_id = NFFS_ID_DIR_MIN;
- nffs_hash_next_block_id = NFFS_ID_BLOCK_MIN;
-
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_path.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_path.c b/libs/nffs/src/nffs_path.c
deleted file mode 100644
index 1dc9289..0000000
--- a/libs/nffs/src/nffs_path.c
+++ /dev/null
@@ -1,337 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-
-int
-nffs_path_parse_next(struct nffs_path_parser *parser)
-{
- const char *slash_start;
- int token_end;
- int token_len;
-
- if (parser->npp_token_type == NFFS_PATH_TOKEN_LEAF) {
- return FS_EINVAL;
- }
-
- slash_start = strchr(parser->npp_path + parser->npp_off, '/');
- if (slash_start == NULL) {
- if (parser->npp_token_type == NFFS_PATH_TOKEN_NONE) {
- return FS_EINVAL;
- }
- parser->npp_token_type = NFFS_PATH_TOKEN_LEAF;
- token_len = strlen(parser->npp_path + parser->npp_off);
- } else {
- parser->npp_token_type = NFFS_PATH_TOKEN_BRANCH;
- token_end = slash_start - parser->npp_path;
- token_len = token_end - parser->npp_off;
- }
-
- if (token_len > NFFS_FILENAME_MAX_LEN) {
- return FS_EINVAL;
- }
-
- parser->npp_token = parser->npp_path + parser->npp_off;
- parser->npp_token_len = token_len;
- parser->npp_off += token_len + 1;
-
- return 0;
-}
-
-void
-nffs_path_parser_new(struct nffs_path_parser *parser, const char *path)
-{
- parser->npp_token_type = NFFS_PATH_TOKEN_NONE;
- parser->npp_path = path;
- parser->npp_off = 0;
-}
-
-static int
-nffs_path_find_child(struct nffs_inode_entry *parent,
- const char *name, int name_len,
- struct nffs_inode_entry **out_inode_entry)
-{
- struct nffs_inode_entry *cur;
- struct nffs_inode inode;
- int cmp;
- int rc;
-
- SLIST_FOREACH(cur, &parent->nie_child_list, nie_sibling_next) {
- rc = nffs_inode_from_entry(&inode, cur);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_filename_cmp_ram(&inode, name, name_len, &cmp);
- if (rc != 0) {
- return rc;
- }
-
- if (cmp == 0) {
- *out_inode_entry = cur;
- return 0;
- }
- if (cmp > 0) {
- break;
- }
- }
-
- return FS_ENOENT;
-}
-
-int
-nffs_path_find(struct nffs_path_parser *parser,
- struct nffs_inode_entry **out_inode_entry,
- struct nffs_inode_entry **out_parent)
-{
- struct nffs_inode_entry *parent;
- struct nffs_inode_entry *inode_entry;
- int rc;
-
- *out_inode_entry = NULL;
- if (out_parent != NULL) {
- *out_parent = NULL;
- }
-
- inode_entry = NULL;
- while (1) {
- parent = inode_entry;
- rc = nffs_path_parse_next(parser);
- if (rc != 0) {
- return rc;
- }
-
- switch (parser->npp_token_type) {
- case NFFS_PATH_TOKEN_BRANCH:
- if (parent == NULL) {
- /* First directory must be root. */
- if (parser->npp_token_len != 0) {
- return FS_ENOENT;
- }
-
- inode_entry = nffs_root_dir;
- } else {
- /* Ignore empty intermediate directory names. */
- if (parser->npp_token_len == 0) {
- break;
- }
-
- rc = nffs_path_find_child(parent, parser->npp_token,
- parser->npp_token_len, &inode_entry);
- if (rc != 0) {
- goto done;
- }
- }
- break;
- case NFFS_PATH_TOKEN_LEAF:
- if (parent == NULL) {
- /* First token must be root directory. */
- return FS_ENOENT;
- }
-
- if (parser->npp_token_len == 0) {
- /* If the path ends with a slash, the leaf is the parent, not
- * the trailing empty token.
- */
- inode_entry = parent;
- rc = 0;
- } else {
- rc = nffs_path_find_child(parent, parser->npp_token,
- parser->npp_token_len, &inode_entry);
- }
- goto done;
- }
- }
-
-done:
- *out_inode_entry = inode_entry;
- if (out_parent != NULL) {
- *out_parent = parent;
- }
- return rc;
-}
-
-int
-nffs_path_find_inode_entry(const char *filename,
- struct nffs_inode_entry **out_inode_entry)
-{
- struct nffs_path_parser parser;
- int rc;
-
- nffs_path_parser_new(&parser, filename);
- rc = nffs_path_find(&parser, out_inode_entry, NULL);
-
- return rc;
-}
-
-/**
- * Unlinks the file or directory at the specified path. If the path refers to
- * a directory, all the directory's descendants are recursively unlinked. Any
- * open file handles refering to an unlinked file remain valid, and can be
- * read from and written to.
- *
- * @path The path of the file or directory to unlink.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_path_unlink(const char *path)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_inode inode;
- int rc;
-
- rc = nffs_path_find_inode_entry(path, &inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_from_entry(&inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_unlink(&inode);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Performs a rename and / or move of the specified source path to the
- * specified destination. The source path can refer to either a file or a
- * directory. All intermediate directories in the destination path must
- * already have been created. If the source path refers to a file, the
- * destination path must contain a full filename path (i.e., if performing a
- * move, the destination path should end with the same filename in the source
- * path). If an object already exists at the specified destination path, this
- * function causes it to be unlinked prior to the rename (i.e., the destination
- * gets clobbered).
- *
- * @param from The source path.
- * @param to The destination path.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-int
-nffs_path_rename(const char *from, const char *to)
-{
- struct nffs_path_parser parser;
- struct nffs_inode_entry *from_inode_entry;
- struct nffs_inode_entry *to_inode_entry;
- struct nffs_inode_entry *from_parent;
- struct nffs_inode_entry *to_parent;
- struct nffs_inode inode;
- int rc;
-
- nffs_path_parser_new(&parser, from);
- rc = nffs_path_find(&parser, &from_inode_entry, &from_parent);
- if (rc != 0) {
- return rc;
- }
-
- nffs_path_parser_new(&parser, to);
- rc = nffs_path_find(&parser, &to_inode_entry, &to_parent);
- switch (rc) {
- case 0:
- /* The user is clobbering something with the rename. */
- if (nffs_hash_id_is_dir(from_inode_entry->nie_hash_entry.nhe_id) ^
- nffs_hash_id_is_dir(to_inode_entry->nie_hash_entry.nhe_id)) {
-
- /* Cannot clobber one type of file with another. */
- return FS_EINVAL;
- }
-
- rc = nffs_inode_from_entry(&inode, to_inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_inode_unlink(&inode);
- if (rc != 0) {
- return rc;
- }
- break;
-
- case FS_ENOENT:
- assert(to_parent != NULL);
- if (parser.npp_token_type != NFFS_PATH_TOKEN_LEAF) {
- /* Intermediate directory doesn't exist. */
- return FS_EINVAL;
- }
- break;
-
- default:
- return rc;
- }
-
- rc = nffs_inode_rename(from_inode_entry, to_parent, parser.npp_token);
- if (rc != 0) {
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Creates a new directory at the specified path.
- *
- * @param path The path of the directory to create.
- *
- * @return 0 on success;
- * FS_EEXIST if there is another file or
- * directory at the specified path.
- * FS_ENONT if a required intermediate directory
- * does not exist.
- */
-int
-nffs_path_new_dir(const char *path, struct nffs_inode_entry **out_inode_entry)
-{
- struct nffs_path_parser parser;
- struct nffs_inode_entry *inode_entry;
- struct nffs_inode_entry *parent;
- int rc;
-
- nffs_path_parser_new(&parser, path);
- rc = nffs_path_find(&parser, &inode_entry, &parent);
- if (rc == 0) {
- return FS_EEXIST;
- }
- if (rc != FS_ENOENT) {
- return rc;
- }
- if (parser.npp_token_type != NFFS_PATH_TOKEN_LEAF || parent == NULL) {
- return FS_ENOENT;
- }
-
- rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len, 1,
- &inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- if (out_inode_entry != NULL) {
- *out_inode_entry = inode_entry;
- }
-
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_priv.h
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_priv.h b/libs/nffs/src/nffs_priv.h
deleted file mode 100644
index d8d1dd0..0000000
--- a/libs/nffs/src/nffs_priv.h
+++ /dev/null
@@ -1,427 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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 H_NFFS_PRIV_
-#define H_NFFS_PRIV_
-
-#include <inttypes.h>
-#include "os/queue.h"
-#include "os/os_mempool.h"
-#include "nffs/nffs.h"
-#include "fs/fs.h"
-
-#define NFFS_HASH_SIZE 256
-
-#define NFFS_ID_DIR_MIN 0
-#define NFFS_ID_DIR_MAX 0x10000000
-#define NFFS_ID_FILE_MIN 0x10000000
-#define NFFS_ID_FILE_MAX 0x80000000
-#define NFFS_ID_BLOCK_MIN 0x80000000
-#define NFFS_ID_BLOCK_MAX 0xffffffff
-
-#define NFFS_ID_ROOT_DIR 0
-#define NFFS_ID_NONE 0xffffffff
-
-#define NFFS_AREA_MAGIC0 0xb98a31e2
-#define NFFS_AREA_MAGIC1 0x7fb0428c
-#define NFFS_AREA_MAGIC2 0xace08253
-#define NFFS_AREA_MAGIC3 0xb185fc8e
-#define NFFS_BLOCK_MAGIC 0x53ba23b9
-#define NFFS_INODE_MAGIC 0x925f8bc0
-
-#define NFFS_AREA_ID_NONE 0xff
-#define NFFS_AREA_VER 0
-#define NFFS_AREA_OFFSET_ID 23
-
-#define NFFS_SHORT_FILENAME_LEN 3
-
-#define NFFS_BLOCK_MAX_DATA_SZ_MAX 2048
-
-/** On-disk representation of an area header. */
-struct nffs_disk_area {
- uint32_t nda_magic[4]; /* NFFS_AREA_MAGIC{0,1,2,3} */
- uint32_t nda_length; /* Total size of area, in bytes. */
- uint8_t nda_ver; /* Current nffs version: 0 */
- uint8_t nda_gc_seq; /* Garbage collection count. */
- uint8_t reserved8;
- uint8_t nda_id; /* 0xff if scratch area. */
-};
-
-/** On-disk representation of an inode (file or directory). */
-struct nffs_disk_inode {
- uint32_t ndi_magic; /* NFFS_INODE_MAGIC */
- uint32_t ndi_id; /* Unique object ID. */
- uint32_t ndi_seq; /* Sequence number; greater supersedes
- lesser. */
- uint32_t ndi_parent_id; /* Object ID of parent directory inode. */
- uint8_t reserved8;
- uint8_t ndi_filename_len; /* Length of filename, in bytes. */
- uint16_t ndi_crc16; /* Covers rest of header and filename. */
- /* Followed by filename. */
-};
-
-#define NFFS_DISK_INODE_OFFSET_CRC 18
-
-/** On-disk representation of a data block. */
-struct nffs_disk_block {
- uint32_t ndb_magic; /* NFFS_BLOCK_MAGIC */
- uint32_t ndb_id; /* Unique object ID. */
- uint32_t ndb_seq; /* Sequence number; greater supersedes lesser. */
- uint32_t ndb_inode_id; /* Object ID of owning inode. */
- uint32_t ndb_prev_id; /* Object ID of previous block in file;
- NFFS_ID_NONE if this is the first block. */
- uint16_t ndb_data_len; /* Length of data contents, in bytes. */
- uint16_t ndb_crc16; /* Covers rest of header and data. */
- /* Followed by 'ndb_data_len' bytes of data. */
-};
-
-#define NFFS_DISK_BLOCK_OFFSET_CRC 20
-
-/**
- * What gets stored in the hash table. Each entry represents a data block or
- * an inode.
- */
-struct nffs_hash_entry {
- SLIST_ENTRY(nffs_hash_entry) nhe_next;
- uint32_t nhe_id; /* 0 - 0x7fffffff if inode; else if block. */
- uint32_t nhe_flash_loc; /* Upper-byte = area idx; rest = area offset. */
-};
-
-
-SLIST_HEAD(nffs_hash_list, nffs_hash_entry);
-SLIST_HEAD(nffs_inode_list, nffs_inode_entry);
-
-/** Each inode hash entry is actually one of these. */
-struct nffs_inode_entry {
- struct nffs_hash_entry nie_hash_entry;
- SLIST_ENTRY(nffs_inode_entry) nie_sibling_next;
- union {
- struct nffs_inode_list nie_child_list; /* If directory */
- struct nffs_hash_entry *nie_last_block_entry; /* If file */
- };
- uint8_t nie_refcnt;
-};
-
-/** Full inode representation; not stored permanently RAM. */
-struct nffs_inode {
- struct nffs_inode_entry *ni_inode_entry; /* Points to real inode entry. */
- uint32_t ni_seq; /* Sequence number; greater
- supersedes lesser. */
- struct nffs_inode_entry *ni_parent; /* Points to parent directory. */
- uint8_t ni_filename_len; /* # chars in filename. */
- uint8_t ni_filename[NFFS_SHORT_FILENAME_LEN]; /* First 3 bytes. */
-};
-
-/** Full data block representation; not stored permanently RAM. */
-struct nffs_block {
- struct nffs_hash_entry *nb_hash_entry; /* Points to real block entry. */
- uint32_t nb_seq; /* Sequence number; greater
- supersedes lesser. */
- struct nffs_inode_entry *nb_inode_entry; /* Owning inode. */
- struct nffs_hash_entry *nb_prev; /* Previous block in file. */
- uint16_t nb_data_len; /* # of data bytes in block. */
- uint16_t reserved16;
-};
-
-struct nffs_file {
- struct nffs_inode_entry *nf_inode_entry;
- uint32_t nf_offset;
- uint8_t nf_access_flags;
-};
-
-struct nffs_area {
- uint32_t na_offset;
- uint32_t na_length;
- uint32_t na_cur;
- uint16_t na_id;
- uint8_t na_gc_seq;
- uint8_t na_flash_id;
-};
-
-struct nffs_disk_object {
- int ndo_type;
- uint8_t ndo_area_idx;
- uint32_t ndo_offset;
- union {
- struct nffs_disk_inode ndo_disk_inode;
- struct nffs_disk_block ndo_disk_block;
- };
-};
-
-struct nffs_seek_info {
- struct nffs_block nsi_last_block;
- uint32_t nsi_block_file_off;
- uint32_t nsi_file_len;
-};
-
-#define NFFS_OBJECT_TYPE_INODE 1
-#define NFFS_OBJECT_TYPE_BLOCK 2
-
-#define NFFS_PATH_TOKEN_NONE 0
-#define NFFS_PATH_TOKEN_BRANCH 1
-#define NFFS_PATH_TOKEN_LEAF 2
-
-struct nffs_path_parser {
- int npp_token_type;
- const char *npp_path;
- const char *npp_token;
- int npp_token_len;
- int npp_off;
-};
-
-/** Represents a single cached data block. */
-struct nffs_cache_block {
- TAILQ_ENTRY(nffs_cache_block) ncb_link; /* Next / prev cached block. */
- struct nffs_block ncb_block; /* Full data block. */
- uint32_t ncb_file_offset; /* File offset of this block. */
-};
-
-TAILQ_HEAD(nffs_cache_block_list, nffs_cache_block);
-
-/** Represents a single cached file inode. */
-struct nffs_cache_inode {
- TAILQ_ENTRY(nffs_cache_inode) nci_link; /* Sorted; LRU at tail. */
- struct nffs_inode nci_inode; /* Full inode. */
- struct nffs_cache_block_list nci_block_list; /* List of cached blocks. */
- uint32_t nci_file_size; /* Total file size. */
-};
-
-struct nffs_dirent {
- struct nffs_inode_entry *nde_inode_entry;
-};
-
-struct nffs_dir {
- struct nffs_inode_entry *nd_parent_inode_entry;
- struct nffs_dirent nd_dirent;
-};
-
-extern void *nffs_file_mem;
-extern void *nffs_block_entry_mem;
-extern void *nffs_inode_mem;
-extern void *nffs_cache_inode_mem;
-extern void *nffs_cache_block_mem;
-extern void *nffs_dir_mem;
-extern struct os_mempool nffs_file_pool;
-extern struct os_mempool nffs_dir_pool;
-extern struct os_mempool nffs_inode_entry_pool;
-extern struct os_mempool nffs_block_entry_pool;
-extern struct os_mempool nffs_cache_inode_pool;
-extern struct os_mempool nffs_cache_block_pool;
-extern uint32_t nffs_hash_next_file_id;
-extern uint32_t nffs_hash_next_dir_id;
-extern uint32_t nffs_hash_next_block_id;
-extern struct nffs_area *nffs_areas;
-extern uint8_t nffs_num_areas;
-extern uint8_t nffs_scratch_area_idx;
-extern uint16_t nffs_block_max_data_sz;
-
-#define NFFS_FLASH_BUF_SZ 256
-extern uint8_t nffs_flash_buf[NFFS_FLASH_BUF_SZ];
-
-extern struct nffs_hash_list *nffs_hash;
-extern struct nffs_inode_entry *nffs_root_dir;
-extern struct nffs_inode_entry *nffs_lost_found_dir;
-
-/* @area */
-int nffs_area_magic_is_set(const struct nffs_disk_area *disk_area);
-int nffs_area_is_scratch(const struct nffs_disk_area *disk_area);
-void nffs_area_to_disk(const struct nffs_area *area,
- struct nffs_disk_area *out_disk_area);
-uint32_t nffs_area_free_space(const struct nffs_area *area);
-int nffs_area_find_corrupt_scratch(uint16_t *out_good_idx,
- uint16_t *out_bad_idx);
-
-/* @block */
-struct nffs_hash_entry *nffs_block_entry_alloc(void);
-void nffs_block_entry_free(struct nffs_hash_entry *entry);
-int nffs_block_read_disk(uint8_t area_idx, uint32_t area_offset,
- struct nffs_disk_block *out_disk_block);
-int nffs_block_write_disk(const struct nffs_disk_block *disk_block,
- const void *data,
- uint8_t *out_area_idx, uint32_t *out_area_offset);
-int nffs_block_delete_from_ram(struct nffs_hash_entry *entry);
-void nffs_block_delete_list_from_ram(struct nffs_block *first,
- struct nffs_block *last);
-void nffs_block_delete_list_from_disk(const struct nffs_block *first,
- const struct nffs_block *last);
-void nffs_block_to_disk(const struct nffs_block *block,
- struct nffs_disk_block *out_disk_block);
-int nffs_block_from_hash_entry_no_ptrs(struct nffs_block *out_block,
- struct nffs_hash_entry *entry);
-int nffs_block_from_hash_entry(struct nffs_block *out_block,
- struct nffs_hash_entry *entry);
-int nffs_block_read_data(const struct nffs_block *block, uint16_t offset,
- uint16_t length, void *dst);
-
-/* @cache */
-void nffs_cache_inode_delete(const struct nffs_inode_entry *inode_entry);
-int nffs_cache_inode_ensure(struct nffs_cache_inode **out_entry,
- struct nffs_inode_entry *inode_entry);
-void nffs_cache_inode_range(const struct nffs_cache_inode *cache_inode,
- uint32_t *out_start, uint32_t *out_end);
-int nffs_cache_seek(struct nffs_cache_inode *cache_inode, uint32_t to,
- struct nffs_cache_block **out_cache_block);
-void nffs_cache_clear(void);
-
-/* @crc */
-int nffs_crc_flash(uint16_t initial_crc, uint8_t area_idx,
- uint32_t area_offset, uint32_t len, uint16_t *out_crc);
-uint16_t nffs_crc_disk_block_hdr(const struct nffs_disk_block *disk_block);
-int nffs_crc_disk_block_validate(const struct nffs_disk_block *disk_block,
- uint8_t area_idx, uint32_t area_offset);
-void nffs_crc_disk_block_fill(struct nffs_disk_block *disk_block,
- const void *data);
-int nffs_crc_disk_inode_validate(const struct nffs_disk_inode *disk_inode,
- uint8_t area_idx, uint32_t area_offset);
-void nffs_crc_disk_inode_fill(struct nffs_disk_inode *disk_inode,
- const char *filename);
-
-/* @config */
-void nffs_config_init(void);
-
-/* @dir */
-int nffs_dir_open(const char *path, struct nffs_dir **out_dir);
-int nffs_dir_read(struct nffs_dir *dir, struct nffs_dirent **out_dirent);
-int nffs_dir_close(struct nffs_dir *dir);
-
-/* @file */
-int nffs_file_open(struct nffs_file **out_file, const char *filename,
- uint8_t access_flags);
-int nffs_file_seek(struct nffs_file *file, uint32_t offset);
-int nffs_file_read(struct nffs_file *file, uint32_t len, void *out_data,
- uint32_t *out_len);
-int nffs_file_close(struct nffs_file *file);
-int nffs_file_new(struct nffs_inode_entry *parent, const char *filename,
- uint8_t filename_len, int is_dir,
- struct nffs_inode_entry **out_inode_entry);
-
-/* @format */
-int nffs_format_area(uint8_t area_idx, int is_scratch);
-int nffs_format_from_scratch_area(uint8_t area_idx, uint8_t area_id);
-int nffs_format_full(const struct nffs_area_desc *area_descs);
-
-/* @gc */
-int nffs_gc(uint8_t *out_area_idx);
-int nffs_gc_until(uint32_t space, uint8_t *out_area_idx);
-
-/* @flash */
-struct nffs_area *nffs_flash_find_area(uint16_t logical_id);
-int nffs_flash_read(uint8_t area_idx, uint32_t offset,
- void *data, uint32_t len);
-int nffs_flash_write(uint8_t area_idx, uint32_t offset,
- const void *data, uint32_t len);
-int nffs_flash_copy(uint8_t area_id_from, uint32_t offset_from,
- uint8_t area_id_to, uint32_t offset_to,
- uint32_t len);
-uint32_t nffs_flash_loc(uint8_t area_idx, uint32_t offset);
-void nffs_flash_loc_expand(uint32_t flash_loc, uint8_t *out_area_idx,
- uint32_t *out_area_offset);
-
-/* @hash */
-int nffs_hash_id_is_dir(uint32_t id);
-int nffs_hash_id_is_file(uint32_t id);
-int nffs_hash_id_is_inode(uint32_t id);
-int nffs_hash_id_is_block(uint32_t id);
-struct nffs_hash_entry *nffs_hash_find(uint32_t id);
-struct nffs_inode_entry *nffs_hash_find_inode(uint32_t id);
-struct nffs_hash_entry *nffs_hash_find_block(uint32_t id);
-void nffs_hash_insert(struct nffs_hash_entry *entry);
-void nffs_hash_remove(struct nffs_hash_entry *entry);
-int nffs_hash_init(void);
-
-/* @inode */
-struct nffs_inode_entry *nffs_inode_entry_alloc(void);
-void nffs_inode_entry_free(struct nffs_inode_entry *inode_entry);
-int nffs_inode_calc_data_length(struct nffs_inode_entry *inode_entry,
- uint32_t *out_len);
-int nffs_inode_data_len(struct nffs_inode_entry *inode_entry,
- uint32_t *out_len);
-uint32_t nffs_inode_parent_id(const struct nffs_inode *inode);
-int nffs_inode_delete_from_disk(struct nffs_inode *inode);
-int nffs_inode_entry_from_disk(struct nffs_inode_entry *out_inode,
- const struct nffs_disk_inode *disk_inode,
- uint8_t area_idx, uint32_t offset);
-int nffs_inode_rename(struct nffs_inode_entry *inode_entry,
- struct nffs_inode_entry *new_parent,
- const char *new_filename);
-void nffs_inode_insert_block(struct nffs_inode *inode,
- struct nffs_block *block);
-int nffs_inode_read_disk(uint8_t area_idx, uint32_t offset,
- struct nffs_disk_inode *out_disk_inode);
-int nffs_inode_write_disk(const struct nffs_disk_inode *disk_inode,
- const char *filename, uint8_t area_idx,
- uint32_t offset);
-int nffs_inode_dec_refcnt(struct nffs_inode_entry *inode_entry);
-int nffs_inode_add_child(struct nffs_inode_entry *parent,
- struct nffs_inode_entry *child);
-void nffs_inode_remove_child(struct nffs_inode *child);
-int nffs_inode_is_root(const struct nffs_disk_inode *disk_inode);
-int nffs_inode_read_filename(struct nffs_inode_entry *inode_entry,
- size_t max_len, char *out_name,
- uint8_t *out_full_len);
-int nffs_inode_filename_cmp_ram(const struct nffs_inode *inode,
- const char *name, int name_len,
- int *result);
-int nffs_inode_filename_cmp_flash(const struct nffs_inode *inode1,
- const struct nffs_inode *inode2,
- int *result);
-int nffs_inode_read(struct nffs_inode_entry *inode_entry, uint32_t offset,
- uint32_t len, void *data, uint32_t *out_len);
-int nffs_inode_seek(struct nffs_inode_entry *inode_entry, uint32_t offset,
- uint32_t length, struct nffs_seek_info *out_seek_info);
-int nffs_inode_from_entry(struct nffs_inode *out_inode,
- struct nffs_inode_entry *entry);
-int nffs_inode_unlink_from_ram(struct nffs_inode *inode,
- struct nffs_hash_entry **out_next);
-int nffs_inode_unlink(struct nffs_inode *inode);
-
-/* @misc */
-int nffs_misc_reserve_space(uint16_t space,
- uint8_t *out_area_idx, uint32_t *out_area_offset);
-int nffs_misc_set_num_areas(uint8_t num_areas);
-int nffs_misc_validate_root_dir(void);
-int nffs_misc_validate_scratch(void);
-int nffs_misc_create_lost_found_dir(void);
-int nffs_misc_set_max_block_data_len(uint16_t min_data_len);
-int nffs_misc_reset(void);
-
-/* @path */
-int nffs_path_parse_next(struct nffs_path_parser *parser);
-void nffs_path_parser_new(struct nffs_path_parser *parser, const char *path);
-int nffs_path_find(struct nffs_path_parser *parser,
- struct nffs_inode_entry **out_inode_entry,
- struct nffs_inode_entry **out_parent);
-int nffs_path_find_inode_entry(const char *filename,
- struct nffs_inode_entry **out_inode_entry);
-int nffs_path_unlink(const char *filename);
-int nffs_path_rename(const char *from, const char *to);
-int nffs_path_new_dir(const char *path,
- struct nffs_inode_entry **out_inode_entry);
-
-/* @restore */
-int nffs_restore_full(const struct nffs_area_desc *area_descs);
-
-/* @write */
-int nffs_write_to_file(struct nffs_file *file, const void *data, int len);
-
-
-#define NFFS_HASH_FOREACH(entry, i) \
- for ((i) = 0; (i) < NFFS_HASH_SIZE; (i)++) \
- SLIST_FOREACH((entry), &nffs_hash[i], nhe_next)
-
-#define NFFS_FLASH_LOC_NONE nffs_flash_loc(NFFS_AREA_ID_NONE, 0)
-
-#endif
[12/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs.c b/fs/nffs/src/nffs.c
new file mode 100644
index 0000000..2aa3df4
--- /dev/null
+++ b/fs/nffs/src/nffs.c
@@ -0,0 +1,692 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "hal/hal_flash.h"
+#include "os/os_mempool.h"
+#include "os/os_mutex.h"
+#include "os/os_malloc.h"
+#include "nffs_priv.h"
+#include "nffs/nffs.h"
+#include "fs/fs_if.h"
+
+struct nffs_area *nffs_areas;
+uint8_t nffs_num_areas;
+uint8_t nffs_scratch_area_idx;
+uint16_t nffs_block_max_data_sz;
+
+struct os_mempool nffs_file_pool;
+struct os_mempool nffs_dir_pool;
+struct os_mempool nffs_inode_entry_pool;
+struct os_mempool nffs_block_entry_pool;
+struct os_mempool nffs_cache_inode_pool;
+struct os_mempool nffs_cache_block_pool;
+
+void *nffs_file_mem;
+void *nffs_inode_mem;
+void *nffs_block_entry_mem;
+void *nffs_cache_inode_mem;
+void *nffs_cache_block_mem;
+void *nffs_dir_mem;
+
+struct nffs_inode_entry *nffs_root_dir;
+struct nffs_inode_entry *nffs_lost_found_dir;
+
+static struct os_mutex nffs_mutex;
+
+static int nffs_open(const char *path, uint8_t access_flags,
+ struct fs_file **out_file);
+static int nffs_close(struct fs_file *fs_file);
+static int nffs_read(struct fs_file *fs_file, uint32_t len, void *out_data,
+ uint32_t *out_len);
+static int nffs_write(struct fs_file *fs_file, const void *data, int len);
+static int nffs_seek(struct fs_file *fs_file, uint32_t offset);
+static uint32_t nffs_getpos(const struct fs_file *fs_file);
+static int nffs_file_len(const struct fs_file *fs_file, uint32_t *out_len);
+static int nffs_unlink(const char *path);
+static int nffs_rename(const char *from, const char *to);
+static int nffs_mkdir(const char *path);
+static int nffs_opendir(const char *path, struct fs_dir **out_fs_dir);
+static int nffs_readdir(struct fs_dir *dir, struct fs_dirent **out_dirent);
+static int nffs_closedir(struct fs_dir *dir);
+static int nffs_dirent_name(const struct fs_dirent *fs_dirent, size_t max_len,
+ char *out_name, uint8_t *out_name_len);
+static int nffs_dirent_is_dir(const struct fs_dirent *fs_dirent);
+
+static const struct fs_ops nffs_ops = {
+ .f_open = nffs_open,
+ .f_close = nffs_close,
+ .f_read = nffs_read,
+ .f_write = nffs_write,
+
+ .f_seek = nffs_seek,
+ .f_getpos = nffs_getpos,
+ .f_filelen = nffs_file_len,
+
+ .f_unlink = nffs_unlink,
+ .f_rename = nffs_rename,
+ .f_mkdir = nffs_mkdir,
+
+ .f_opendir = nffs_opendir,
+ .f_readdir = nffs_readdir,
+ .f_closedir = nffs_closedir,
+
+ .f_dirent_name = nffs_dirent_name,
+ .f_dirent_is_dir = nffs_dirent_is_dir,
+
+ .f_name = "nffs"
+};
+
+static void
+nffs_lock(void)
+{
+ int rc;
+
+ rc = os_mutex_pend(&nffs_mutex, 0xffffffff);
+ assert(rc == 0 || rc == OS_NOT_STARTED);
+}
+
+static void
+nffs_unlock(void)
+{
+ int rc;
+
+ rc = os_mutex_release(&nffs_mutex);
+ assert(rc == 0 || rc == OS_NOT_STARTED);
+}
+
+/**
+ * Opens a file at the specified path. The result of opening a nonexistent
+ * file depends on the access flags specified. All intermediate directories
+ * must already exist.
+ *
+ * The mode strings passed to fopen() map to nffs_open()'s access flags as
+ * follows:
+ * "r" - FS_ACCESS_READ
+ * "r+" - FS_ACCESS_READ | FS_ACCESS_WRITE
+ * "w" - FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE
+ * "w+" - FS_ACCESS_READ | FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE
+ * "a" - FS_ACCESS_WRITE | FS_ACCESS_APPEND
+ * "a+" - FS_ACCESS_READ | FS_ACCESS_WRITE | FS_ACCESS_APPEND
+ *
+ * @param path The path of the file to open.
+ * @param access_flags Flags controlling file access; see above table.
+ * @param out_file On success, a pointer to the newly-created file
+ * handle gets written here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_open(const char *path, uint8_t access_flags, struct fs_file **out_fs_file)
+{
+ int rc;
+ struct nffs_file *out_file;
+
+ nffs_lock();
+
+ if (!nffs_ready()) {
+ rc = FS_EUNINIT;
+ goto done;
+ }
+
+ rc = nffs_file_open(&out_file, path, access_flags);
+ if (rc != 0) {
+ goto done;
+ }
+ *out_fs_file = (struct fs_file *)out_file;
+done:
+ nffs_unlock();
+ if (rc != 0) {
+ *out_fs_file = NULL;
+ }
+ return rc;
+}
+
+/**
+ * Closes the specified file and invalidates the file handle. If the file has
+ * already been unlinked, and this is the last open handle to the file, this
+ * operation causes the file to be deleted from disk.
+ *
+ * @param file The file handle to close.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_close(struct fs_file *fs_file)
+{
+ int rc;
+ struct nffs_file *file = (struct nffs_file *)fs_file;
+
+ if (file == NULL) {
+ return 0;
+ }
+
+ nffs_lock();
+ rc = nffs_file_close(file);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Positions a file's read and write pointer at the specified offset. The
+ * offset is expressed as the number of bytes from the start of the file (i.e.,
+ * seeking to offset 0 places the pointer at the first byte in the file).
+ *
+ * @param file The file to reposition.
+ * @param offset The 0-based file offset to seek to.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_seek(struct fs_file *fs_file, uint32_t offset)
+{
+ int rc;
+ struct nffs_file *file = (struct nffs_file *)fs_file;
+
+ nffs_lock();
+ rc = nffs_file_seek(file, offset);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Retrieves the current read and write position of the specified open file.
+ *
+ * @param file The file to query.
+ *
+ * @return The file offset, in bytes.
+ */
+static uint32_t
+nffs_getpos(const struct fs_file *fs_file)
+{
+ uint32_t offset;
+ const struct nffs_file *file = (const struct nffs_file *)fs_file;
+
+ nffs_lock();
+ offset = file->nf_offset;
+ nffs_unlock();
+
+ return offset;
+}
+
+/**
+ * Retrieves the current length of the specified open file.
+ *
+ * @param file The file to query.
+ * @param out_len On success, the number of bytes in the file gets
+ * written here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_file_len(const struct fs_file *fs_file, uint32_t *out_len)
+{
+ int rc;
+ const struct nffs_file *file = (const struct nffs_file *)fs_file;
+
+ nffs_lock();
+ rc = nffs_inode_data_len(file->nf_inode_entry, out_len);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Reads data from the specified file. If more data is requested than remains
+ * in the file, all available data is retrieved. Note: this type of short read
+ * results in a success return code.
+ *
+ * @param file The file to read from.
+ * @param len The number of bytes to attempt to read.
+ * @param out_data The destination buffer to read into.
+ * @param out_len On success, the number of bytes actually read gets
+ * written here. Pass null if you don't care.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_read(struct fs_file *fs_file, uint32_t len, void *out_data,
+ uint32_t *out_len)
+{
+ int rc;
+ struct nffs_file *file = (struct nffs_file *)fs_file;
+
+ nffs_lock();
+ rc = nffs_file_read(file, len, out_data, out_len);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Writes the supplied data to the current offset of the specified file handle.
+ *
+ * @param file The file to write to.
+ * @param data The data to write.
+ * @param len The number of bytes to write.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_write(struct fs_file *fs_file, const void *data, int len)
+{
+ int rc;
+ struct nffs_file *file = (struct nffs_file *)fs_file;
+
+ nffs_lock();
+
+ if (!nffs_ready()) {
+ rc = FS_EUNINIT;
+ goto done;
+ }
+
+ rc = nffs_write_to_file(file, data, len);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ nffs_unlock();
+ return rc;
+}
+
+/**
+ * Unlinks the file or directory at the specified path. If the path refers to
+ * a directory, all the directory's descendants are recursively unlinked. Any
+ * open file handles refering to an unlinked file remain valid, and can be
+ * read from and written to.
+ *
+ * @path The path of the file or directory to unlink.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_unlink(const char *path)
+{
+ int rc;
+
+ nffs_lock();
+
+ if (!nffs_ready()) {
+ rc = FS_EUNINIT;
+ goto done;
+ }
+
+ rc = nffs_path_unlink(path);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ nffs_unlock();
+ return rc;
+}
+
+/**
+ * Performs a rename and / or move of the specified source path to the
+ * specified destination. The source path can refer to either a file or a
+ * directory. All intermediate directories in the destination path must
+ * already exist. If the source path refers to a file, the destination path
+ * must contain a full filename path, rather than just the new parent
+ * directory. If an object already exists at the specified destination path,
+ * this function causes it to be unlinked prior to the rename (i.e., the
+ * destination gets clobbered).
+ *
+ * @param from The source path.
+ * @param to The destination path.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+static int
+nffs_rename(const char *from, const char *to)
+{
+ int rc;
+
+ nffs_lock();
+
+ if (!nffs_ready()) {
+ rc = FS_EUNINIT;
+ goto done;
+ }
+
+ rc = nffs_path_rename(from, to);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ nffs_unlock();
+ return rc;
+}
+
+/**
+ * Creates the directory represented by the specified path. All intermediate
+ * directories must already exist. The specified path must start with a '/'
+ * character.
+ *
+ * @param path The directory to create.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+static int
+nffs_mkdir(const char *path)
+{
+ int rc;
+
+ nffs_lock();
+
+ if (!nffs_ready()) {
+ rc = FS_EUNINIT;
+ goto done;
+ }
+
+ rc = nffs_path_new_dir(path, NULL);
+ if (rc != 0) {
+ goto done;
+ }
+
+done:
+ nffs_unlock();
+ return rc;
+}
+
+/**
+ * Opens the directory at the specified path. The directory's contents can be
+ * read with subsequent calls to nffs_readdir(). When you are done with the
+ * directory handle, close it with nffs_closedir().
+ *
+ * Unlinking files from the directory while it is open may result in
+ * unpredictable behavior. New files can be created inside the directory.
+ *
+ * @param path The directory to open.
+ * @param out_dir On success, points to the directory handle.
+ *
+ * @return 0 on success;
+ * FS_ENOENT if the specified directory does not
+ * exist;
+ * other nonzero on error.
+ */
+static int
+nffs_opendir(const char *path, struct fs_dir **out_fs_dir)
+{
+ int rc;
+ struct nffs_dir **out_dir = (struct nffs_dir **)out_fs_dir;
+
+ nffs_lock();
+
+ if (!nffs_ready()) {
+ rc = FS_EUNINIT;
+ goto done;
+ }
+
+ rc = nffs_dir_open(path, out_dir);
+
+done:
+ nffs_unlock();
+ return rc;
+}
+
+/**
+ * Reads the next entry in an open directory.
+ *
+ * @param dir The directory handle to read from.
+ * @param out_dirent On success, points to the next child entry in
+ * the specified directory.
+ *
+ * @return 0 on success;
+ * FS_ENOENT if there are no more entries in the
+ * parent directory;
+ * other nonzero on error.
+ */
+static int
+nffs_readdir(struct fs_dir *fs_dir, struct fs_dirent **out_fs_dirent)
+{
+ int rc;
+ struct nffs_dir *dir = (struct nffs_dir *)fs_dir;
+ struct nffs_dirent **out_dirent = (struct nffs_dirent **)out_fs_dirent;
+
+ nffs_lock();
+ rc = nffs_dir_read(dir, out_dirent);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Closes the specified directory handle.
+ *
+ * @param dir The directory to close.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_closedir(struct fs_dir *fs_dir)
+{
+ int rc;
+ struct nffs_dir *dir = (struct nffs_dir *)fs_dir;
+
+ nffs_lock();
+ rc = nffs_dir_close(dir);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Retrieves the filename of the specified directory entry. The retrieved
+ * filename is always null-terminated. To ensure enough space to hold the full
+ * filename plus a null-termintor, a destination buffer of size
+ * (NFFS_FILENAME_MAX_LEN + 1) should be used.
+ *
+ * @param dirent The directory entry to query.
+ * @param max_len The size of the "out_name" character buffer.
+ * @param out_name On success, the entry's filename is written
+ * here; always null-terminated.
+ * @param out_name_len On success, contains the actual length of the
+ * filename, NOT including the
+ * null-terminator.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_dirent_name(const struct fs_dirent *fs_dirent, size_t max_len,
+ char *out_name, uint8_t *out_name_len)
+{
+ int rc;
+ struct nffs_dirent *dirent = (struct nffs_dirent *)fs_dirent;
+
+ nffs_lock();
+
+ assert(dirent != NULL && dirent->nde_inode_entry != NULL);
+ rc = nffs_inode_read_filename(dirent->nde_inode_entry, max_len, out_name,
+ out_name_len);
+
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Tells you whether the specified directory entry is a sub-directory or a
+ * regular file.
+ *
+ * @param dirent The directory entry to query.
+ *
+ * @return 1: The entry is a directory;
+ * 0: The entry is a regular file.
+ */
+static int
+nffs_dirent_is_dir(const struct fs_dirent *fs_dirent)
+{
+ uint32_t id;
+ const struct nffs_dirent *dirent = (const struct nffs_dirent *)fs_dirent;
+
+ nffs_lock();
+
+ assert(dirent != NULL && dirent->nde_inode_entry != NULL);
+ id = dirent->nde_inode_entry->nie_hash_entry.nhe_id;
+
+ nffs_unlock();
+
+ return nffs_hash_id_is_dir(id);
+}
+
+/**
+ * Erases all the specified areas and initializes them with a clean nffs
+ * file system.
+ *
+ * @param area_descs The set of areas to format.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+int
+nffs_format(const struct nffs_area_desc *area_descs)
+{
+ int rc;
+
+ nffs_lock();
+ rc = nffs_format_full(area_descs);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Searches for a valid nffs file system among the specified areas. This
+ * function succeeds if a file system is detected among any subset of the
+ * supplied areas. If the area set does not contain a valid file system,
+ * a new one can be created via a separate call to nffs_format().
+ *
+ * @param area_descs The area set to search. This array must be
+ * terminated with a 0-length area.
+ *
+ * @return 0 on success;
+ * FS_ECORRUPT if no valid file system was detected;
+ * other nonzero on error.
+ */
+int
+nffs_detect(const struct nffs_area_desc *area_descs)
+{
+ int rc;
+
+ nffs_lock();
+ rc = nffs_restore_full(area_descs);
+ nffs_unlock();
+
+ return rc;
+}
+
+/**
+ * Indicates whether a valid filesystem has been initialized, either via
+ * detection or formatting.
+ *
+ * @return 1 if a file system is present; 0 otherwise.
+ */
+int
+nffs_ready(void)
+{
+ return nffs_root_dir != NULL;
+}
+
+/**
+ * Initializes the nffs memory and data structures. This must be called before
+ * any nffs operations are attempted.
+ *
+ * @return 0 on success; nonzero on error.
+ */
+int
+nffs_init(void)
+{
+ int rc;
+
+ nffs_config_init();
+
+ nffs_cache_clear();
+
+ rc = os_mutex_init(&nffs_mutex);
+ if (rc != 0) {
+ return FS_EOS;
+ }
+
+ free(nffs_file_mem);
+ nffs_file_mem = malloc(
+ OS_MEMPOOL_BYTES(nffs_config.nc_num_files, sizeof (struct nffs_file)));
+ if (nffs_file_mem == NULL) {
+ return FS_ENOMEM;
+ }
+
+ free(nffs_inode_mem);
+ nffs_inode_mem = malloc(
+ OS_MEMPOOL_BYTES(nffs_config.nc_num_inodes,
+ sizeof (struct nffs_inode_entry)));
+ if (nffs_inode_mem == NULL) {
+ return FS_ENOMEM;
+ }
+
+ free(nffs_block_entry_mem);
+ nffs_block_entry_mem = malloc(
+ OS_MEMPOOL_BYTES(nffs_config.nc_num_blocks,
+ sizeof (struct nffs_hash_entry)));
+ if (nffs_block_entry_mem == NULL) {
+ return FS_ENOMEM;
+ }
+
+ free(nffs_cache_inode_mem);
+ nffs_cache_inode_mem = malloc(
+ OS_MEMPOOL_BYTES(nffs_config.nc_num_cache_inodes,
+ sizeof (struct nffs_cache_inode)));
+ if (nffs_cache_inode_mem == NULL) {
+ return FS_ENOMEM;
+ }
+
+ free(nffs_cache_block_mem);
+ nffs_cache_block_mem = malloc(
+ OS_MEMPOOL_BYTES(nffs_config.nc_num_cache_blocks,
+ sizeof (struct nffs_cache_block)));
+ if (nffs_cache_block_mem == NULL) {
+ return FS_ENOMEM;
+ }
+
+ free(nffs_dir_mem);
+ nffs_dir_mem = malloc(
+ OS_MEMPOOL_BYTES(nffs_config.nc_num_dirs,
+ sizeof (struct nffs_dir)));
+ if (nffs_dir_mem == NULL) {
+ return FS_ENOMEM;
+ }
+
+ rc = nffs_misc_reset();
+ if (rc != 0) {
+ return rc;
+ }
+
+ fs_register(&nffs_ops);
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_area.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_area.c b/fs/nffs/src/nffs_area.c
new file mode 100644
index 0000000..82163db
--- /dev/null
+++ b/fs/nffs/src/nffs_area.c
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "nffs_priv.h"
+#include "nffs/nffs.h"
+
+static void
+nffs_area_set_magic(struct nffs_disk_area *disk_area)
+{
+ disk_area->nda_magic[0] = NFFS_AREA_MAGIC0;
+ disk_area->nda_magic[1] = NFFS_AREA_MAGIC1;
+ disk_area->nda_magic[2] = NFFS_AREA_MAGIC2;
+ disk_area->nda_magic[3] = NFFS_AREA_MAGIC3;
+}
+
+int
+nffs_area_magic_is_set(const struct nffs_disk_area *disk_area)
+{
+ return disk_area->nda_magic[0] == NFFS_AREA_MAGIC0 &&
+ disk_area->nda_magic[1] == NFFS_AREA_MAGIC1 &&
+ disk_area->nda_magic[2] == NFFS_AREA_MAGIC2 &&
+ disk_area->nda_magic[3] == NFFS_AREA_MAGIC3;
+}
+
+int
+nffs_area_is_scratch(const struct nffs_disk_area *disk_area)
+{
+ return nffs_area_magic_is_set(disk_area) &&
+ disk_area->nda_id == NFFS_AREA_ID_NONE;
+}
+
+void
+nffs_area_to_disk(const struct nffs_area *area,
+ struct nffs_disk_area *out_disk_area)
+{
+ memset(out_disk_area, 0, sizeof *out_disk_area);
+ nffs_area_set_magic(out_disk_area);
+ out_disk_area->nda_length = area->na_length;
+ out_disk_area->nda_ver = NFFS_AREA_VER;
+ out_disk_area->nda_gc_seq = area->na_gc_seq;
+ out_disk_area->nda_id = area->na_id;
+}
+
+uint32_t
+nffs_area_free_space(const struct nffs_area *area)
+{
+ return area->na_length - area->na_cur;
+}
+
+/**
+ * Finds a corrupt scratch area. An area is indentified as a corrupt scratch
+ * area if it and another area share the same ID. Among two areas with the
+ * same ID, the one with fewer bytes written is the corrupt scratch area.
+ *
+ * @param out_good_idx On success, the index of the good area (longer
+ * of the two areas) gets written here.
+ * @param out_bad_idx On success, the index of the corrupt scratch
+ * area gets written here.
+ *
+ * @return 0 if a corrupt scratch area was identified;
+ * FS_ENOENT if one was not found.
+ */
+int
+nffs_area_find_corrupt_scratch(uint16_t *out_good_idx, uint16_t *out_bad_idx)
+{
+ const struct nffs_area *iarea;
+ const struct nffs_area *jarea;
+ int i;
+ int j;
+
+ for (i = 0; i < nffs_num_areas; i++) {
+ iarea = nffs_areas + i;
+ for (j = i + 1; j < nffs_num_areas; j++) {
+ jarea = nffs_areas + j;
+
+ if (jarea->na_id == iarea->na_id) {
+ /* Found a duplicate. The shorter of the two areas should be
+ * used as scratch.
+ */
+ if (iarea->na_cur < jarea->na_cur) {
+ *out_good_idx = j;
+ *out_bad_idx = i;
+ } else {
+ *out_good_idx = i;
+ *out_bad_idx = j;
+ }
+
+ return 0;
+ }
+ }
+ }
+
+ return FS_ENOENT;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_block.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_block.c b/fs/nffs/src/nffs_block.c
new file mode 100644
index 0000000..6221664
--- /dev/null
+++ b/fs/nffs/src/nffs_block.c
@@ -0,0 +1,298 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+#include <string.h>
+#include "testutil/testutil.h"
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+#include "crc16.h"
+
+struct nffs_hash_entry *
+nffs_block_entry_alloc(void)
+{
+ struct nffs_hash_entry *entry;
+
+ entry = os_memblock_get(&nffs_block_entry_pool);
+ if (entry != NULL) {
+ memset(entry, 0, sizeof *entry);
+ }
+
+ return entry;
+}
+
+void
+nffs_block_entry_free(struct nffs_hash_entry *entry)
+{
+ assert(nffs_hash_id_is_block(entry->nhe_id));
+ os_memblock_put(&nffs_block_entry_pool, entry);
+}
+
+/**
+ * Reads a data block header from flash.
+ *
+ * @param area_idx The index of the area to read from.
+ * @param area_offset The offset within the area to read from.
+ * @param out_disk_block On success, the block header is writteh here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_block_read_disk(uint8_t area_idx, uint32_t area_offset,
+ struct nffs_disk_block *out_disk_block)
+{
+ int rc;
+
+ rc = nffs_flash_read(area_idx, area_offset, out_disk_block,
+ sizeof *out_disk_block);
+ if (rc != 0) {
+ return rc;
+ }
+ if (out_disk_block->ndb_magic != NFFS_BLOCK_MAGIC) {
+ return FS_EUNEXP;
+ }
+
+ return 0;
+}
+
+/**
+ * Writes the specified data block to a suitable location in flash.
+ *
+ * @param disk_block Points to the disk block to write.
+ * @param data The contents of the data block.
+ * @param out_area_idx On success, contains the index of the area
+ * written to.
+ * @param out_area_offset On success, contains the offset within the area
+ * written to.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_block_write_disk(const struct nffs_disk_block *disk_block,
+ const void *data,
+ uint8_t *out_area_idx, uint32_t *out_area_offset)
+{
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ rc = nffs_misc_reserve_space(sizeof *disk_block + disk_block->ndb_data_len,
+ &area_idx, &area_offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_flash_write(area_idx, area_offset, disk_block,
+ sizeof *disk_block);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (disk_block->ndb_data_len > 0) {
+ rc = nffs_flash_write(area_idx, area_offset + sizeof *disk_block,
+ data, disk_block->ndb_data_len);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ *out_area_idx = area_idx;
+ *out_area_offset = area_offset;
+
+ ASSERT_IF_TEST(nffs_crc_disk_block_validate(disk_block, area_idx,
+ area_offset) == 0);
+
+ return 0;
+}
+
+static void
+nffs_block_from_disk_no_ptrs(struct nffs_block *out_block,
+ const struct nffs_disk_block *disk_block)
+{
+ out_block->nb_seq = disk_block->ndb_seq;
+ out_block->nb_inode_entry = NULL;
+ out_block->nb_prev = NULL;
+ out_block->nb_data_len = disk_block->ndb_data_len;
+}
+
+static int
+nffs_block_from_disk(struct nffs_block *out_block,
+ const struct nffs_disk_block *disk_block,
+ uint8_t area_idx, uint32_t area_offset)
+{
+ nffs_block_from_disk_no_ptrs(out_block, disk_block);
+
+ out_block->nb_inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);
+ if (out_block->nb_inode_entry == NULL) {
+ return FS_ECORRUPT;
+ }
+
+ if (disk_block->ndb_prev_id != NFFS_ID_NONE) {
+ out_block->nb_prev = nffs_hash_find_block(disk_block->ndb_prev_id);
+ if (out_block->nb_prev == NULL) {
+ return FS_ECORRUPT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Constructs a disk-representation of the specified data block.
+ *
+ * @param block The source block to convert.
+ * @param out_disk_block The disk block to write to.
+ */
+void
+nffs_block_to_disk(const struct nffs_block *block,
+ struct nffs_disk_block *out_disk_block)
+{
+ assert(block->nb_inode_entry != NULL);
+
+ out_disk_block->ndb_magic = NFFS_BLOCK_MAGIC;
+ out_disk_block->ndb_id = block->nb_hash_entry->nhe_id;
+ out_disk_block->ndb_seq = block->nb_seq;
+ out_disk_block->ndb_inode_id =
+ block->nb_inode_entry->nie_hash_entry.nhe_id;
+ if (block->nb_prev == NULL) {
+ out_disk_block->ndb_prev_id = NFFS_ID_NONE;
+ } else {
+ out_disk_block->ndb_prev_id = block->nb_prev->nhe_id;
+ }
+ out_disk_block->ndb_data_len = block->nb_data_len;
+}
+
+/**
+ * Deletes the specified block entry from the nffs RAM representation.
+ *
+ * @param block_entry The block entry to delete.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_block_delete_from_ram(struct nffs_hash_entry *block_entry)
+{
+ struct nffs_block block;
+ int rc;
+
+ rc = nffs_block_from_hash_entry(&block, block_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ assert(block.nb_inode_entry != NULL);
+ if (block.nb_inode_entry->nie_last_block_entry == block_entry) {
+ block.nb_inode_entry->nie_last_block_entry = block.nb_prev;
+ }
+
+ nffs_hash_remove(block_entry);
+ nffs_block_entry_free(block_entry);
+
+ return 0;
+}
+
+/**
+ * Constructs a full data block representation from the specified minimal
+ * block entry. However, the resultant block's pointers are set to null,
+ * rather than populated via hash table lookups. This behavior is useful when
+ * the RAM representation has not been fully constructed yet.
+ *
+ * @param out_block On success, this gets populated with the data
+ * block information.
+ * @param block_entry The source block entry to convert.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_block_from_hash_entry_no_ptrs(struct nffs_block *out_block,
+ struct nffs_hash_entry *block_entry)
+{
+ struct nffs_disk_block disk_block;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ assert(nffs_hash_id_is_block(block_entry->nhe_id));
+
+ nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
+ rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
+ if (rc != 0) {
+ return rc;
+ }
+
+ out_block->nb_hash_entry = block_entry;
+ nffs_block_from_disk_no_ptrs(out_block, &disk_block);
+
+ return 0;
+}
+
+/**
+ * Constructs a full data block representation from the specified minimal block
+ * entry. The resultant block's pointers are populated via hash table lookups.
+ *
+ * @param out_block On success, this gets populated with the data
+ * block information.
+ * @param block_entry The source block entry to convert.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_block_from_hash_entry(struct nffs_block *out_block,
+ struct nffs_hash_entry *block_entry)
+{
+ struct nffs_disk_block disk_block;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ assert(nffs_hash_id_is_block(block_entry->nhe_id));
+
+ nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
+ rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
+ if (rc != 0) {
+ return rc;
+ }
+
+ out_block->nb_hash_entry = block_entry;
+ rc = nffs_block_from_disk(out_block, &disk_block, area_idx, area_offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+nffs_block_read_data(const struct nffs_block *block, uint16_t offset,
+ uint16_t length, void *dst)
+{
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ nffs_flash_loc_expand(block->nb_hash_entry->nhe_flash_loc,
+ &area_idx, &area_offset);
+ area_offset += sizeof (struct nffs_disk_block);
+ area_offset += offset;
+
+ rc = nffs_flash_read(area_idx, area_offset, dst, length);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_cache.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_cache.c b/fs/nffs/src/nffs_cache.c
new file mode 100644
index 0000000..32a42eb
--- /dev/null
+++ b/fs/nffs/src/nffs_cache.c
@@ -0,0 +1,445 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+
+TAILQ_HEAD(nffs_cache_inode_list, nffs_cache_inode);
+static struct nffs_cache_inode_list nffs_cache_inode_list =
+ TAILQ_HEAD_INITIALIZER(nffs_cache_inode_list);
+
+static void nffs_cache_collect_blocks(void);
+
+static struct nffs_cache_block *
+nffs_cache_block_alloc(void)
+{
+ struct nffs_cache_block *entry;
+
+ entry = os_memblock_get(&nffs_cache_block_pool);
+ if (entry != NULL) {
+ memset(entry, 0, sizeof *entry);
+ }
+
+ return entry;
+}
+
+static void
+nffs_cache_block_free(struct nffs_cache_block *entry)
+{
+ if (entry != NULL) {
+ os_memblock_put(&nffs_cache_block_pool, entry);
+ }
+}
+
+static struct nffs_cache_block *
+nffs_cache_block_acquire(void)
+{
+ struct nffs_cache_block *cache_block;
+
+ cache_block = nffs_cache_block_alloc();
+ if (cache_block == NULL) {
+ nffs_cache_collect_blocks();
+ cache_block = nffs_cache_block_alloc();
+ }
+
+ assert(cache_block != NULL);
+
+ return cache_block;
+}
+
+static int
+nffs_cache_block_populate(struct nffs_cache_block *cache_block,
+ struct nffs_hash_entry *block_entry,
+ uint32_t end_offset)
+{
+ int rc;
+
+ rc = nffs_block_from_hash_entry(&cache_block->ncb_block, block_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ cache_block->ncb_file_offset = end_offset -
+ cache_block->ncb_block.nb_data_len;
+
+ return 0;
+}
+
+static struct nffs_cache_inode *
+nffs_cache_inode_alloc(void)
+{
+ struct nffs_cache_inode *entry;
+
+ entry = os_memblock_get(&nffs_cache_inode_pool);
+ if (entry != NULL) {
+ memset(entry, 0, sizeof *entry);
+ entry->nci_block_list = (struct nffs_cache_block_list)
+ TAILQ_HEAD_INITIALIZER(entry->nci_block_list);
+ }
+
+ return entry;
+}
+
+static void
+nffs_cache_inode_free_blocks(struct nffs_cache_inode *cache_inode)
+{
+ struct nffs_cache_block *cache_block;
+
+ while ((cache_block = TAILQ_FIRST(&cache_inode->nci_block_list)) != NULL) {
+ TAILQ_REMOVE(&cache_inode->nci_block_list, cache_block, ncb_link);
+ nffs_cache_block_free(cache_block);
+ }
+}
+
+static void
+nffs_cache_inode_free(struct nffs_cache_inode *entry)
+{
+ if (entry != NULL) {
+ nffs_cache_inode_free_blocks(entry);
+ os_memblock_put(&nffs_cache_inode_pool, entry);
+ }
+}
+
+static struct nffs_cache_inode *
+nffs_cache_inode_acquire(void)
+{
+ struct nffs_cache_inode *entry;
+
+ entry = nffs_cache_inode_alloc();
+ if (entry == NULL) {
+ entry = TAILQ_LAST(&nffs_cache_inode_list, nffs_cache_inode_list);
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&nffs_cache_inode_list, entry, nci_link);
+ nffs_cache_inode_free(entry);
+
+ entry = nffs_cache_inode_alloc();
+ }
+
+ assert(entry != NULL);
+
+ return entry;
+}
+
+static int
+nffs_cache_inode_populate(struct nffs_cache_inode *cache_inode,
+ struct nffs_inode_entry *inode_entry)
+{
+ int rc;
+
+ memset(cache_inode, 0, sizeof *cache_inode);
+
+ rc = nffs_inode_from_entry(&cache_inode->nci_inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_calc_data_length(cache_inode->nci_inode.ni_inode_entry,
+ &cache_inode->nci_file_size);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Retrieves the block entry corresponding to the last cached block in the
+ * specified inode's list. If the inode has no cached blocks, this function
+ * returns null.
+ */
+static struct nffs_hash_entry *
+nffs_cache_inode_last_entry(struct nffs_cache_inode *cache_inode)
+{
+ struct nffs_cache_block *cache_block;
+
+ if (TAILQ_EMPTY(&cache_inode->nci_block_list)) {
+ return NULL;
+ }
+
+ cache_block = TAILQ_LAST(&cache_inode->nci_block_list,
+ nffs_cache_block_list);
+ return cache_block->ncb_block.nb_hash_entry;
+}
+
+static struct nffs_cache_inode *
+nffs_cache_inode_find(const struct nffs_inode_entry *inode_entry)
+{
+ struct nffs_cache_inode *cur;
+
+ TAILQ_FOREACH(cur, &nffs_cache_inode_list, nci_link) {
+ if (cur->nci_inode.ni_inode_entry == inode_entry) {
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+void
+nffs_cache_inode_range(const struct nffs_cache_inode *cache_inode,
+ uint32_t *out_start, uint32_t *out_end)
+{
+ struct nffs_cache_block *cache_block;
+
+ cache_block = TAILQ_FIRST(&cache_inode->nci_block_list);
+ if (cache_block == NULL) {
+ *out_start = 0;
+ *out_end = 0;
+ return;
+ }
+
+ *out_start = cache_block->ncb_file_offset;
+
+ cache_block = TAILQ_LAST(&cache_inode->nci_block_list,
+ nffs_cache_block_list);
+ *out_end = cache_block->ncb_file_offset +
+ cache_block->ncb_block.nb_data_len;
+}
+
+static void
+nffs_cache_collect_blocks(void)
+{
+ struct nffs_cache_inode *cache_inode;
+
+ TAILQ_FOREACH_REVERSE(cache_inode, &nffs_cache_inode_list,
+ nffs_cache_inode_list, nci_link) {
+ if (!TAILQ_EMPTY(&cache_inode->nci_block_list)) {
+ nffs_cache_inode_free_blocks(cache_inode);
+ return;
+ }
+ }
+
+ assert(0);
+}
+
+void
+nffs_cache_inode_delete(const struct nffs_inode_entry *inode_entry)
+{
+ struct nffs_cache_inode *entry;
+
+ entry = nffs_cache_inode_find(inode_entry);
+ if (entry == NULL) {
+ return;
+ }
+
+ TAILQ_REMOVE(&nffs_cache_inode_list, entry, nci_link);
+ nffs_cache_inode_free(entry);
+}
+
+int
+nffs_cache_inode_ensure(struct nffs_cache_inode **out_cache_inode,
+ struct nffs_inode_entry *inode_entry)
+{
+ struct nffs_cache_inode *cache_inode;
+ int rc;
+
+ cache_inode = nffs_cache_inode_find(inode_entry);
+ if (cache_inode != NULL) {
+ rc = 0;
+ goto done;
+ }
+
+ cache_inode = nffs_cache_inode_acquire();
+ rc = nffs_cache_inode_populate(cache_inode, inode_entry);
+ if (rc != 0) {
+ goto done;
+ }
+
+ TAILQ_INSERT_HEAD(&nffs_cache_inode_list, cache_inode, nci_link);
+
+ rc = 0;
+
+done:
+ if (rc == 0) {
+ *out_cache_inode = cache_inode;
+ } else {
+ nffs_cache_inode_free(cache_inode);
+ *out_cache_inode = NULL;
+ }
+ return rc;
+}
+
+/**
+ * Finds the data block containing the specified offset within a file inode.
+ * If the block is not yet cached, it gets cached as a result of this
+ * operation. This function modifies the inode's cached block list according
+ * to the following procedure:
+ *
+ * 1. If none of the owning inode's blocks are currently cached, allocate a
+ * cached block entry and insert it into the inode's list.
+ * 2. Else if the requested file offset is less than that of the first cached
+ * block, bridge the gap between the inode's sequence of cached blocks and
+ * the block that now needs to be cached. This is accomplished by caching
+ * each block in the gap, finishing with the requested block.
+ * 3. Else (the requested offset is beyond the end of the cache),
+ * a. If the requested offset belongs to the block that immediately
+ * follows the end of the cache, cache the block and append it to the
+ * list.
+ * b. Else, clear the cache, and populate it with the single entry
+ * corresponding to the requested block.
+ *
+ * @param cache_inode The cached file inode to seek within.
+ * @param seek_offset The file offset to seek to.
+ * @param out_cache_block On success, the requested cached block gets
+ * written here; pass null if you don't need
+ * this.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_cache_seek(struct nffs_cache_inode *cache_inode, uint32_t seek_offset,
+ struct nffs_cache_block **out_cache_block)
+{
+ struct nffs_cache_block *cache_block;
+ struct nffs_hash_entry *last_cached_entry;
+ struct nffs_hash_entry *block_entry;
+ struct nffs_hash_entry *pred_entry;
+ struct nffs_block block;
+ uint32_t cache_start;
+ uint32_t cache_end;
+ uint32_t block_start;
+ uint32_t block_end;
+ int rc;
+
+ /* Empty files have no blocks that can be cached. */
+ if (cache_inode->nci_file_size == 0) {
+ return FS_ENOENT;
+ }
+
+ nffs_cache_inode_range(cache_inode, &cache_start, &cache_end);
+ if (cache_end != 0 && seek_offset < cache_start) {
+ /* Seeking prior to cache. Iterate backwards from cache start. */
+ cache_block = TAILQ_FIRST(&cache_inode->nci_block_list);
+ block_entry = cache_block->ncb_block.nb_prev;
+ block_end = cache_block->ncb_file_offset;
+ cache_block = NULL;
+ } else if (seek_offset < cache_end) {
+ /* Seeking within cache. Iterate backwards from cache end. */
+ cache_block = TAILQ_LAST(&cache_inode->nci_block_list,
+ nffs_cache_block_list);
+ block_entry = cache_block->ncb_block.nb_hash_entry;
+ block_end = cache_end;
+ } else {
+ /* Seeking beyond end of cache. Iterate backwards from file end. If
+ * sought-after block is adjacent to cache end, its cache entry will
+ * get appended to the current cache. Otherwise, the current cache
+ * will be freed and replaced with the single requested block.
+ */
+ cache_block = NULL;
+ block_entry =
+ cache_inode->nci_inode.ni_inode_entry->nie_last_block_entry;
+ block_end = cache_inode->nci_file_size;
+ }
+
+ /* Scan backwards until we find the block containing the seek offest. */
+ while (1) {
+ if (block_end <= cache_start) {
+ /* We are looking before the start of the cache. Allocate a new
+ * cache block and prepend it to the cache.
+ */
+ assert(cache_block == NULL);
+ cache_block = nffs_cache_block_acquire();
+ rc = nffs_cache_block_populate(cache_block, block_entry,
+ block_end);
+ if (rc != 0) {
+ return rc;
+ }
+
+ TAILQ_INSERT_HEAD(&cache_inode->nci_block_list, cache_block,
+ ncb_link);
+ }
+
+ /* Calculate the file offset of the start of this block. This is used
+ * to determine if this block contains the sought-after offset.
+ */
+ if (cache_block != NULL) {
+ /* Current block is cached. */
+ block_start = cache_block->ncb_file_offset;
+ pred_entry = cache_block->ncb_block.nb_prev;
+ } else {
+ /* We are looking beyond the end of the cache. Read the data block
+ * from flash.
+ */
+ rc = nffs_block_from_hash_entry(&block, block_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ block_start = block_end - block.nb_data_len;
+ pred_entry = block.nb_prev;
+ }
+
+ if (block_start <= seek_offset) {
+ /* This block contains the requested address; iteration is
+ * complete.
+ */
+ if (cache_block == NULL) {
+ /* The block isn't cached, so it must come after the cache end.
+ * Append it to the cache if it directly follows. Otherwise,
+ * erase the current cache and populate it with this single
+ * block.
+ */
+ cache_block = nffs_cache_block_acquire();
+ cache_block->ncb_block = block;
+ cache_block->ncb_file_offset = block_start;
+
+ last_cached_entry = nffs_cache_inode_last_entry(cache_inode);
+ if (last_cached_entry != NULL &&
+ last_cached_entry == pred_entry) {
+
+ TAILQ_INSERT_TAIL(&cache_inode->nci_block_list,
+ cache_block, ncb_link);
+ } else {
+ nffs_cache_inode_free_blocks(cache_inode);
+ TAILQ_INSERT_HEAD(&cache_inode->nci_block_list,
+ cache_block, ncb_link);
+ }
+ }
+
+ if (out_cache_block != NULL) {
+ *out_cache_block = cache_block;
+ }
+ break;
+ }
+
+ /* Prepare for next iteration. */
+ if (cache_block != NULL) {
+ cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list,
+ ncb_link);
+ }
+ block_entry = pred_entry;
+ block_end = block_start;
+ }
+
+ return 0;
+}
+
+/**
+ * Frees all cached inodes and blocks.
+ */
+void
+nffs_cache_clear(void)
+{
+ struct nffs_cache_inode *entry;
+
+ while ((entry = TAILQ_FIRST(&nffs_cache_inode_list)) != NULL) {
+ TAILQ_REMOVE(&nffs_cache_inode_list, entry, nci_link);
+ nffs_cache_inode_free(entry);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_config.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_config.c b/fs/nffs/src/nffs_config.c
new file mode 100644
index 0000000..f5efc60
--- /dev/null
+++ b/fs/nffs/src/nffs_config.c
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include "nffs/nffs.h"
+
+struct nffs_config nffs_config;
+
+const struct nffs_config nffs_config_dflt = {
+ .nc_num_inodes = 100,
+ .nc_num_blocks = 100,
+ .nc_num_files = 4,
+ .nc_num_cache_inodes = 4,
+ .nc_num_cache_blocks = 64,
+ .nc_num_dirs = 4,
+};
+
+void
+nffs_config_init(void)
+{
+ if (nffs_config.nc_num_inodes == 0) {
+ nffs_config.nc_num_inodes = nffs_config_dflt.nc_num_inodes;
+ }
+ if (nffs_config.nc_num_blocks == 0) {
+ nffs_config.nc_num_blocks = nffs_config_dflt.nc_num_blocks;
+ }
+ if (nffs_config.nc_num_files == 0) {
+ nffs_config.nc_num_files = nffs_config_dflt.nc_num_files;
+ }
+ if (nffs_config.nc_num_cache_inodes == 0) {
+ nffs_config.nc_num_cache_inodes = nffs_config_dflt.nc_num_cache_inodes;
+ }
+ if (nffs_config.nc_num_cache_blocks == 0) {
+ nffs_config.nc_num_cache_blocks = nffs_config_dflt.nc_num_cache_blocks;
+ }
+ if (nffs_config.nc_num_dirs == 0) {
+ nffs_config.nc_num_dirs = nffs_config_dflt.nc_num_dirs;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_crc.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_crc.c b/fs/nffs/src/nffs_crc.c
new file mode 100644
index 0000000..97d6b47
--- /dev/null
+++ b/fs/nffs/src/nffs_crc.c
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+
+#include "nffs_priv.h"
+#include "crc16.h"
+
+int
+nffs_crc_flash(uint16_t initial_crc, uint8_t area_idx, uint32_t area_offset,
+ uint32_t len, uint16_t *out_crc)
+{
+ uint32_t chunk_len;
+ uint16_t crc;
+ int rc;
+
+ crc = initial_crc;
+
+ /* Copy data in chunks small enough to fit in the flash buffer. */
+ while (len > 0) {
+ if (len > sizeof nffs_flash_buf) {
+ chunk_len = sizeof nffs_flash_buf;
+ } else {
+ chunk_len = len;
+ }
+
+ rc = nffs_flash_read(area_idx, area_offset, nffs_flash_buf, chunk_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ crc = crc16_ccitt(crc, nffs_flash_buf, chunk_len);
+
+ area_offset += chunk_len;
+ len -= chunk_len;
+ }
+
+ *out_crc = crc;
+ return 0;
+}
+
+uint16_t
+nffs_crc_disk_block_hdr(const struct nffs_disk_block *disk_block)
+{
+ uint16_t crc;
+
+ crc = crc16_ccitt(0, disk_block, NFFS_DISK_BLOCK_OFFSET_CRC);
+
+ return crc;
+}
+
+static int
+nffs_crc_disk_block(const struct nffs_disk_block *disk_block,
+ uint8_t area_idx, uint32_t area_offset,
+ uint16_t *out_crc)
+{
+ uint16_t crc;
+ int rc;
+
+ crc = nffs_crc_disk_block_hdr(disk_block);
+
+ rc = nffs_crc_flash(crc, area_idx, area_offset + sizeof *disk_block,
+ disk_block->ndb_data_len, &crc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *out_crc = crc;
+ return 0;
+}
+
+int
+nffs_crc_disk_block_validate(const struct nffs_disk_block *disk_block,
+ uint8_t area_idx, uint32_t area_offset)
+{
+ uint16_t crc;
+ int rc;
+
+ rc = nffs_crc_disk_block(disk_block, area_idx, area_offset, &crc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (crc != disk_block->ndb_crc16) {
+ return FS_ECORRUPT;
+ }
+
+ return 0;
+}
+
+void
+nffs_crc_disk_block_fill(struct nffs_disk_block *disk_block, const void *data)
+{
+ uint16_t crc16;
+
+ crc16 = nffs_crc_disk_block_hdr(disk_block);
+ crc16 = crc16_ccitt(crc16, data, disk_block->ndb_data_len);
+
+ disk_block->ndb_crc16 = crc16;
+}
+
+static uint16_t
+nffs_crc_disk_inode_hdr(const struct nffs_disk_inode *disk_inode)
+{
+ uint16_t crc;
+
+ crc = crc16_ccitt(0, disk_inode, NFFS_DISK_INODE_OFFSET_CRC);
+
+ return crc;
+}
+
+static int
+nffs_crc_disk_inode(const struct nffs_disk_inode *disk_inode,
+ uint8_t area_idx, uint32_t area_offset,
+ uint16_t *out_crc)
+{
+ uint16_t crc;
+ int rc;
+
+ crc = nffs_crc_disk_inode_hdr(disk_inode);
+
+ rc = nffs_crc_flash(crc, area_idx, area_offset + sizeof *disk_inode,
+ disk_inode->ndi_filename_len, &crc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *out_crc = crc;
+ return 0;
+}
+
+int
+nffs_crc_disk_inode_validate(const struct nffs_disk_inode *disk_inode,
+ uint8_t area_idx, uint32_t area_offset)
+{
+ uint16_t crc;
+ int rc;
+
+ rc = nffs_crc_disk_inode(disk_inode, area_idx, area_offset, &crc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (crc != disk_inode->ndi_crc16) {
+ return FS_ECORRUPT;
+ }
+
+ return 0;
+}
+
+void
+nffs_crc_disk_inode_fill(struct nffs_disk_inode *disk_inode,
+ const char *filename)
+{
+ uint16_t crc16;
+
+ crc16 = nffs_crc_disk_inode_hdr(disk_inode);
+ crc16 = crc16_ccitt(crc16, filename, disk_inode->ndi_filename_len);
+
+ disk_inode->ndi_crc16 = crc16;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_dir.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_dir.c b/fs/nffs/src/nffs_dir.c
new file mode 100644
index 0000000..59e37d2
--- /dev/null
+++ b/fs/nffs/src/nffs_dir.c
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "nffs_priv.h"
+#include "nffs/nffs.h"
+
+static struct nffs_dir *
+nffs_dir_alloc(void)
+{
+ struct nffs_dir *dir;
+
+ dir = os_memblock_get(&nffs_dir_pool);
+ if (dir != NULL) {
+ memset(dir, 0, sizeof *dir);
+ }
+
+ return dir;
+}
+
+static int
+nffs_dir_free(struct nffs_dir *dir)
+{
+ int rc;
+
+ if (dir != NULL) {
+ rc = os_memblock_put(&nffs_dir_pool, dir);
+ if (rc != 0) {
+ return FS_EOS;
+ }
+ }
+
+ return 0;
+}
+
+int
+nffs_dir_open(const char *path, struct nffs_dir **out_dir)
+{
+ struct nffs_inode_entry *parent_inode_entry;
+ struct nffs_dir *dir;
+ int rc;
+
+ rc = nffs_path_find_inode_entry(path, &parent_inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (!nffs_hash_id_is_dir(parent_inode_entry->nie_hash_entry.nhe_id)) {
+ return FS_EINVAL;
+ }
+
+ dir = nffs_dir_alloc();
+ if (dir == NULL) {
+ return FS_ENOMEM;
+ }
+
+ dir->nd_parent_inode_entry = parent_inode_entry;
+ dir->nd_parent_inode_entry->nie_refcnt++;
+ memset(&dir->nd_dirent, 0, sizeof dir->nd_dirent);
+
+ *out_dir = dir;
+
+ return 0;
+}
+
+int
+nffs_dir_read(struct nffs_dir *dir, struct nffs_dirent **out_dirent)
+{
+ struct nffs_inode_entry *child;
+ int rc;
+
+ if (dir->nd_dirent.nde_inode_entry == NULL) {
+ child = SLIST_FIRST(&dir->nd_parent_inode_entry->nie_child_list);
+ } else {
+ child = SLIST_NEXT(dir->nd_dirent.nde_inode_entry, nie_sibling_next);
+ rc = nffs_inode_dec_refcnt(dir->nd_dirent.nde_inode_entry);
+ if (rc != 0) {
+ /* XXX: Need to clean up anything? */
+ return rc;
+ }
+ }
+ dir->nd_dirent.nde_inode_entry = child;
+
+ if (child == NULL) {
+ *out_dirent = NULL;
+ return FS_ENOENT;
+ }
+
+ child->nie_refcnt++;
+ *out_dirent = &dir->nd_dirent;
+
+ return 0;
+}
+
+int
+nffs_dir_close(struct nffs_dir *dir)
+{
+ int rc;
+
+ if (dir == NULL) {
+ return 0;
+ }
+
+ if (dir->nd_dirent.nde_inode_entry != NULL) {
+ rc = nffs_inode_dec_refcnt(dir->nd_dirent.nde_inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ rc = nffs_inode_dec_refcnt(dir->nd_parent_inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_dir_free(dir);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_file.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_file.c b/fs/nffs/src/nffs_file.c
new file mode 100644
index 0000000..b412730
--- /dev/null
+++ b/fs/nffs/src/nffs_file.c
@@ -0,0 +1,346 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "nffs_priv.h"
+#include "nffs/nffs.h"
+
+static struct nffs_file *
+nffs_file_alloc(void)
+{
+ struct nffs_file *file;
+
+ file = os_memblock_get(&nffs_file_pool);
+ if (file != NULL) {
+ memset(file, 0, sizeof *file);
+ }
+
+ return file;
+}
+
+static int
+nffs_file_free(struct nffs_file *file)
+{
+ int rc;
+
+ if (file != NULL) {
+ rc = os_memblock_put(&nffs_file_pool, file);
+ if (rc != 0) {
+ return FS_EOS;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Creates a new empty file and writes it to the file system. If a file with
+ * the specified path already exists, the behavior is undefined.
+ *
+ * @param parent The parent directory to insert the new file in.
+ * @param filename The name of the file to create.
+ * @param filename_len The length of the filename, in characters.
+ * @param is_dir 1 if this is a directory; 0 if it is a normal
+ * file.
+ * @param out_inode_entry On success, this points to the inode
+ * corresponding to the new file.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_file_new(struct nffs_inode_entry *parent, const char *filename,
+ uint8_t filename_len, int is_dir,
+ struct nffs_inode_entry **out_inode_entry)
+{
+ struct nffs_disk_inode disk_inode;
+ struct nffs_inode_entry *inode_entry;
+ uint32_t offset;
+ uint8_t area_idx;
+ int rc;
+
+ inode_entry = nffs_inode_entry_alloc();
+ if (inode_entry == NULL) {
+ rc = FS_ENOMEM;
+ goto err;
+ }
+
+ rc = nffs_misc_reserve_space(sizeof disk_inode + filename_len,
+ &area_idx, &offset);
+ if (rc != 0) {
+ goto err;
+ }
+
+ memset(&disk_inode, 0xff, sizeof disk_inode);
+ disk_inode.ndi_magic = NFFS_INODE_MAGIC;
+ if (is_dir) {
+ disk_inode.ndi_id = nffs_hash_next_dir_id++;
+ } else {
+ disk_inode.ndi_id = nffs_hash_next_file_id++;
+ }
+ disk_inode.ndi_seq = 0;
+ if (parent == NULL) {
+ disk_inode.ndi_parent_id = NFFS_ID_NONE;
+ } else {
+ disk_inode.ndi_parent_id = parent->nie_hash_entry.nhe_id;
+ }
+ disk_inode.ndi_filename_len = filename_len;
+ nffs_crc_disk_inode_fill(&disk_inode, filename);
+
+ rc = nffs_inode_write_disk(&disk_inode, filename, area_idx, offset);
+ if (rc != 0) {
+ goto err;
+ }
+
+ inode_entry->nie_hash_entry.nhe_id = disk_inode.ndi_id;
+ inode_entry->nie_hash_entry.nhe_flash_loc =
+ nffs_flash_loc(area_idx, offset);
+ inode_entry->nie_refcnt = 1;
+
+ if (parent != NULL) {
+ rc = nffs_inode_add_child(parent, inode_entry);
+ if (rc != 0) {
+ goto err;
+ }
+ } else {
+ assert(disk_inode.ndi_id == NFFS_ID_ROOT_DIR);
+ }
+
+ nffs_hash_insert(&inode_entry->nie_hash_entry);
+ *out_inode_entry = inode_entry;
+
+ return 0;
+
+err:
+ nffs_inode_entry_free(inode_entry);
+ return rc;
+}
+
+/**
+ * Performs a file open operation.
+ *
+ * @param out_file On success, a pointer to the newly-created file
+ * handle gets written here.
+ * @param path The path of the file to open.
+ * @param access_flags Flags controlling file access; see nffs_open() for
+ * details.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_file_open(struct nffs_file **out_file, const char *path,
+ uint8_t access_flags)
+{
+ struct nffs_path_parser parser;
+ struct nffs_inode_entry *parent;
+ struct nffs_inode_entry *inode;
+ struct nffs_file *file;
+ int rc;
+
+ file = NULL;
+
+ /* Reject invalid access flag combinations. */
+ if (!(access_flags & (FS_ACCESS_READ | FS_ACCESS_WRITE))) {
+ rc = FS_EINVAL;
+ goto err;
+ }
+ if (access_flags & (FS_ACCESS_APPEND | FS_ACCESS_TRUNCATE) &&
+ !(access_flags & FS_ACCESS_WRITE)) {
+
+ rc = FS_EINVAL;
+ goto err;
+ }
+ if (access_flags & FS_ACCESS_APPEND &&
+ access_flags & FS_ACCESS_TRUNCATE) {
+
+ rc = FS_EINVAL;
+ goto err;
+ }
+
+ file = nffs_file_alloc();
+ if (file == NULL) {
+ rc = FS_ENOMEM;
+ goto err;
+ }
+
+ nffs_path_parser_new(&parser, path);
+ rc = nffs_path_find(&parser, &inode, &parent);
+ if (rc == FS_ENOENT && parser.npp_token_type == NFFS_PATH_TOKEN_LEAF) {
+ /* The path is valid, but the file does not exist. This is an error
+ * for read-only opens.
+ */
+ if (!(access_flags & FS_ACCESS_WRITE)) {
+ goto err;
+ }
+
+ /* Make sure the parent directory exists. */
+ if (parent == NULL) {
+ goto err;
+ }
+
+ /* Create a new file at the specified path. */
+ rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len, 0,
+ &file->nf_inode_entry);
+ if (rc != 0) {
+ goto err;
+ }
+ } else if (rc == 0) {
+ /* The file already exists. */
+
+ /* Reject an attempt to open a directory. */
+ if (nffs_hash_id_is_dir(inode->nie_hash_entry.nhe_id)) {
+ rc = FS_EINVAL;
+ goto err;
+ }
+
+ if (access_flags & FS_ACCESS_TRUNCATE) {
+ /* The user is truncating the file. Unlink the old file and create
+ * a new one in its place.
+ */
+ nffs_path_unlink(path);
+ rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len,
+ 0, &file->nf_inode_entry);
+ if (rc != 0) {
+ goto err;
+ }
+ } else {
+ /* The user is not truncating the file. Point the file handle to
+ * the existing inode.
+ */
+ file->nf_inode_entry = inode;
+ }
+ } else {
+ /* Invalid path. */
+ goto err;
+ }
+
+ if (access_flags & FS_ACCESS_APPEND) {
+ rc = nffs_inode_data_len(file->nf_inode_entry, &file->nf_offset);
+ if (rc != 0) {
+ goto err;
+ }
+ } else {
+ file->nf_offset = 0;
+ }
+ file->nf_inode_entry->nie_refcnt++;
+ file->nf_access_flags = access_flags;
+
+ *out_file = file;
+
+ return 0;
+
+err:
+ nffs_file_free(file);
+ return rc;
+}
+
+/**
+ * Positions a file's read and write pointer at the specified offset. The
+ * offset is expressed as the number of bytes from the start of the file (i.e.,
+ * seeking to 0 places the pointer at the first byte in the file).
+ *
+ * @param file The file to reposition.
+ * @param offset The offset from the start of the file to seek to.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_file_seek(struct nffs_file *file, uint32_t offset)
+{
+ uint32_t len;
+ int rc;
+
+ rc = nffs_inode_data_len(file->nf_inode_entry, &len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (offset > len) {
+ return FS_ERANGE;
+ }
+
+ file->nf_offset = offset;
+ return 0;
+}
+
+/**
+ * Reads data from the specified file. If more data is requested than remains
+ * in the file, all available data is retrieved. Note: this type of short read
+ * results in a success return code.
+ *
+ * @param file The file to read from.
+ * @param len The number of bytes to attempt to read.
+ * @param out_data The destination buffer to read into.
+ * @param out_len On success, the number of bytes actually read gets
+ * written here. Pass null if you don't care.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_file_read(struct nffs_file *file, uint32_t len, void *out_data,
+ uint32_t *out_len)
+{
+ uint32_t bytes_read;
+ int rc;
+
+ if (!nffs_ready()) {
+ return FS_EUNINIT;
+ }
+
+ if (!(file->nf_access_flags & FS_ACCESS_READ)) {
+ return FS_EACCESS;
+ }
+
+ rc = nffs_inode_read(file->nf_inode_entry, file->nf_offset, len, out_data,
+ &bytes_read);
+ if (rc != 0) {
+ return rc;
+ }
+
+ file->nf_offset += bytes_read;
+ if (out_len != NULL) {
+ *out_len = bytes_read;
+ }
+
+ return 0;
+}
+
+/**
+ * Closes the specified file and invalidates the file handle. If the file has
+ * already been unlinked, and this is the last open handle to the file, this
+ * operation causes the file to be deleted.
+ *
+ * @param file The file handle to close.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_file_close(struct nffs_file *file)
+{
+ int rc;
+
+ rc = nffs_inode_dec_refcnt(file->nf_inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_file_free(file);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_flash.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_flash.c b/fs/nffs/src/nffs_flash.c
new file mode 100644
index 0000000..94456b4
--- /dev/null
+++ b/fs/nffs/src/nffs_flash.c
@@ -0,0 +1,174 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include "hal/hal_flash.h"
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+
+/** A buffer used for flash reads; shared across all of nffs. */
+uint8_t nffs_flash_buf[NFFS_FLASH_BUF_SZ];
+
+/**
+ * Reads a chunk of data from flash.
+ *
+ * @param area_idx The index of the area to read from.
+ * @param area_offset The offset within the area to read from.
+ * @param data On success, the flash contents are written
+ * here.
+ * @param len The number of bytes to read.
+ *
+ * @return 0 on success;
+ * FS_ERANGE on an attempt to read an invalid
+ * address range;
+ * FS_HW_ERROR on flash error.
+ */
+int
+nffs_flash_read(uint8_t area_idx, uint32_t area_offset, void *data,
+ uint32_t len)
+{
+ const struct nffs_area *area;
+ int rc;
+
+ assert(area_idx < nffs_num_areas);
+
+ area = nffs_areas + area_idx;
+
+ if (area_offset + len > area->na_length) {
+ return FS_ERANGE;
+ }
+
+ rc = hal_flash_read(area->na_flash_id, area->na_offset + area_offset, data,
+ len);
+ if (rc != 0) {
+ return FS_HW_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * Writes a chunk of data to flash.
+ *
+ * @param area_idx The index of the area to write to.
+ * @param area_offset The offset within the area to write to.
+ * @param data The data to write to flash.
+ * @param len The number of bytes to write.
+ *
+ * @return 0 on success;
+ * FS_ERANGE on an attempt to write to an
+ * invalid address range, or on an attempt to
+ * perform a non-strictly-sequential write;
+ * FS_EFLASH_ERROR on flash error.
+ */
+int
+nffs_flash_write(uint8_t area_idx, uint32_t area_offset, const void *data,
+ uint32_t len)
+{
+ struct nffs_area *area;
+ int rc;
+
+ assert(area_idx < nffs_num_areas);
+ area = nffs_areas + area_idx;
+
+ if (area_offset + len > area->na_length) {
+ return FS_ERANGE;
+ }
+
+ if (area_offset < area->na_cur) {
+ return FS_ERANGE;
+ }
+
+ rc = hal_flash_write(area->na_flash_id, area->na_offset + area_offset, data,
+ len);
+ if (rc != 0) {
+ return FS_HW_ERROR;
+ }
+
+ area->na_cur = area_offset + len;
+
+ return 0;
+}
+
+/**
+ * Copies a chunk of data from one region of flash to another.
+ *
+ * @param area_idx_from The index of the area to copy from.
+ * @param area_offset_from The offset within the area to copy from.
+ * @param area_idx_to The index of the area to copy to.
+ * @param area_offset_to The offset within the area to copy to.
+ * @param len The number of bytes to copy.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_flash_copy(uint8_t area_idx_from, uint32_t area_offset_from,
+ uint8_t area_idx_to, uint32_t area_offset_to,
+ uint32_t len)
+{
+ uint32_t chunk_len;
+ int rc;
+
+ /* Copy data in chunks small enough to fit in the flash buffer. */
+ while (len > 0) {
+ if (len > sizeof nffs_flash_buf) {
+ chunk_len = sizeof nffs_flash_buf;
+ } else {
+ chunk_len = len;
+ }
+
+ rc = nffs_flash_read(area_idx_from, area_offset_from, nffs_flash_buf,
+ chunk_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_flash_write(area_idx_to, area_offset_to, nffs_flash_buf,
+ chunk_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ area_offset_from += chunk_len;
+ area_offset_to += chunk_len;
+ len -= chunk_len;
+ }
+
+ return 0;
+}
+
+/**
+ * Compresses a flash-area-index,flash-area-offset pair into a 32-bit flash
+ * location.
+ */
+uint32_t
+nffs_flash_loc(uint8_t area_idx, uint32_t area_offset)
+{
+ assert(area_offset <= 0x00ffffff);
+ return area_idx << 24 | area_offset;
+}
+
+/**
+ * Expands a compressed 32-bit flash location into a
+ * flash-area-index,flash-area-offset pair.
+ */
+void
+nffs_flash_loc_expand(uint32_t flash_loc, uint8_t *out_area_idx,
+ uint32_t *out_area_offset)
+{
+ *out_area_idx = flash_loc >> 24;
+ *out_area_offset = flash_loc & 0x00ffffff;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_format.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_format.c b/fs/nffs/src/nffs_format.c
new file mode 100644
index 0000000..56a040f
--- /dev/null
+++ b/fs/nffs/src/nffs_format.c
@@ -0,0 +1,183 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "hal/hal_flash.h"
+#include "nffs_priv.h"
+#include "nffs/nffs.h"
+
+/**
+ * Turns a scratch area into a non-scratch area. If the specified area is not
+ * actually a scratch area, this function falls back to a slower full format
+ * operation.
+ */
+int
+nffs_format_from_scratch_area(uint8_t area_idx, uint8_t area_id)
+{
+ struct nffs_disk_area disk_area;
+ int rc;
+
+ assert(area_idx < nffs_num_areas);
+ rc = nffs_flash_read(area_idx, 0, &disk_area, sizeof disk_area);
+ if (rc != 0) {
+ return rc;
+ }
+
+ nffs_areas[area_idx].na_id = area_id;
+ if (!nffs_area_is_scratch(&disk_area)) {
+ rc = nffs_format_area(area_idx, 0);
+ if (rc != 0) {
+ return rc;
+ }
+ } else {
+ disk_area.nda_id = area_id;
+ rc = nffs_flash_write(area_idx, NFFS_AREA_OFFSET_ID,
+ &disk_area.nda_id, sizeof disk_area.nda_id);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Formats a single scratch area.
+ */
+int
+nffs_format_area(uint8_t area_idx, int is_scratch)
+{
+ struct nffs_disk_area disk_area;
+ struct nffs_area *area;
+ uint32_t write_len;
+ int rc;
+
+ area = nffs_areas + area_idx;
+
+ rc = hal_flash_erase(area->na_flash_id, area->na_offset, area->na_length);
+ if (rc != 0) {
+ return rc;
+ }
+ area->na_cur = 0;
+
+ nffs_area_to_disk(area, &disk_area);
+
+ if (is_scratch) {
+ nffs_areas[area_idx].na_id = NFFS_AREA_ID_NONE;
+ write_len = sizeof disk_area - sizeof disk_area.nda_id;
+ } else {
+ write_len = sizeof disk_area;
+ }
+
+ rc = nffs_flash_write(area_idx, 0, &disk_area.nda_magic, write_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Erases all the specified areas and initializes them with a clean nffs
+ * file system.
+ *
+ * @param area_descs The set of areas to format.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+int
+nffs_format_full(const struct nffs_area_desc *area_descs)
+{
+ int rc;
+ int i;
+
+ /* Start from a clean state. */
+ nffs_misc_reset();
+
+ /* Select largest area to be the initial scratch area. */
+ nffs_scratch_area_idx = 0;
+ for (i = 1; area_descs[i].nad_length != 0; i++) {
+ if (i >= NFFS_MAX_AREAS) {
+ rc = FS_EINVAL;
+ goto err;
+ }
+
+ if (area_descs[i].nad_length >
+ area_descs[nffs_scratch_area_idx].nad_length) {
+
+ nffs_scratch_area_idx = i;
+ }
+ }
+
+ rc = nffs_misc_set_num_areas(i);
+ if (rc != 0) {
+ goto err;
+ }
+
+ for (i = 0; i < nffs_num_areas; i++) {
+ nffs_areas[i].na_offset = area_descs[i].nad_offset;
+ nffs_areas[i].na_length = area_descs[i].nad_length;
+ nffs_areas[i].na_flash_id = area_descs[i].nad_flash_id;
+ nffs_areas[i].na_cur = 0;
+ nffs_areas[i].na_gc_seq = 0;
+
+ if (i == nffs_scratch_area_idx) {
+ nffs_areas[i].na_id = NFFS_AREA_ID_NONE;
+ } else {
+ nffs_areas[i].na_id = i;
+ }
+
+ rc = nffs_format_area(i, i == nffs_scratch_area_idx);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+
+ rc = nffs_misc_validate_scratch();
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* Create root directory. */
+ rc = nffs_file_new(NULL, "", 0, 1, &nffs_root_dir);
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* Create "lost+found" directory. */
+ rc = nffs_misc_create_lost_found_dir();
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = nffs_misc_validate_root_dir();
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = nffs_misc_set_max_block_data_len(0);
+ if (rc != 0) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ nffs_misc_reset();
+ return rc;
+}
[11/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_gc.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_gc.c b/fs/nffs/src/nffs_gc.c
new file mode 100644
index 0000000..a51460e
--- /dev/null
+++ b/fs/nffs/src/nffs_gc.c
@@ -0,0 +1,523 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "os/os_malloc.h"
+#include "testutil/testutil.h"
+#include "nffs_priv.h"
+#include "nffs/nffs.h"
+
+static int
+nffs_gc_copy_object(struct nffs_hash_entry *entry, uint16_t object_size,
+ uint8_t to_area_idx)
+{
+ uint32_t from_area_offset;
+ uint32_t to_area_offset;
+ uint8_t from_area_idx;
+ int rc;
+
+ nffs_flash_loc_expand(entry->nhe_flash_loc,
+ &from_area_idx, &from_area_offset);
+ to_area_offset = nffs_areas[to_area_idx].na_cur;
+
+ rc = nffs_flash_copy(from_area_idx, from_area_offset, to_area_idx,
+ to_area_offset, object_size);
+ if (rc != 0) {
+ return rc;
+ }
+
+ entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset);
+
+ return 0;
+}
+
+static int
+nffs_gc_copy_inode(struct nffs_inode_entry *inode_entry, uint8_t to_area_idx)
+{
+ struct nffs_inode inode;
+ uint16_t copy_len;
+ int rc;
+
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ copy_len = sizeof (struct nffs_disk_inode) + inode.ni_filename_len;
+
+ rc = nffs_gc_copy_object(&inode_entry->nie_hash_entry, copy_len,
+ to_area_idx);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Selects the most appropriate area for garbage collection.
+ *
+ * @return The ID of the area to garbage collect.
+ */
+static uint16_t
+nffs_gc_select_area(void)
+{
+ const struct nffs_area *area;
+ uint8_t best_area_idx;
+ int8_t diff;
+ int i;
+
+ best_area_idx = 0;
+ for (i = 1; i < nffs_num_areas; i++) {
+ if (i == nffs_scratch_area_idx) {
+ continue;
+ }
+
+ area = nffs_areas + i;
+ if (area->na_length > nffs_areas[best_area_idx].na_length) {
+ best_area_idx = i;
+ } else if (best_area_idx == nffs_scratch_area_idx) {
+ best_area_idx = i;
+ } else {
+ diff = nffs_areas[i].na_gc_seq -
+ nffs_areas[best_area_idx].na_gc_seq;
+ if (diff < 0) {
+ best_area_idx = i;
+ }
+ }
+ }
+
+ assert(best_area_idx != nffs_scratch_area_idx);
+
+ return best_area_idx;
+}
+
+static int
+nffs_gc_block_chain_copy(struct nffs_hash_entry *last_entry, uint32_t data_len,
+ uint8_t to_area_idx)
+{
+ struct nffs_hash_entry *entry;
+ struct nffs_block block;
+ uint32_t data_bytes_copied;
+ uint16_t copy_len;
+ int rc;
+
+ data_bytes_copied = 0;
+ entry = last_entry;
+
+ while (data_bytes_copied < data_len) {
+ assert(entry != NULL);
+
+ rc = nffs_block_from_hash_entry(&block, entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ copy_len = sizeof (struct nffs_disk_block) + block.nb_data_len;
+ rc = nffs_gc_copy_object(entry, copy_len, to_area_idx);
+ if (rc != 0) {
+ return rc;
+ }
+ data_bytes_copied += block.nb_data_len;
+
+ entry = block.nb_prev;
+ }
+
+ return 0;
+}
+
+/**
+ * Moves a chain of blocks from one area to another. This function attempts to
+ * collate the blocks into a single new block in the destination area.
+ *
+ * @param last_entry The last block entry in the chain.
+ * @param data_len The total length of data to collate.
+ * @param to_area_idx The index of the area to copy to.
+ * @param inout_next This parameter is only necessary if you are
+ * calling this function during an iteration
+ * of the entire hash table; pass null
+ * otherwise.
+ * On input, this points to the next hash entry
+ * you plan on processing.
+ * On output, this points to the next hash entry
+ * that should be processed.
+ *
+ * @return 0 on success;
+ * FS_ENOMEM if there is insufficient heap;
+ * other nonzero on failure.
+ */
+static int
+nffs_gc_block_chain_collate(struct nffs_hash_entry *last_entry,
+ uint32_t data_len, uint8_t to_area_idx,
+ struct nffs_hash_entry **inout_next)
+{
+ struct nffs_disk_block disk_block;
+ struct nffs_hash_entry *entry;
+ struct nffs_area *to_area;
+ struct nffs_block block;
+ uint32_t to_area_offset;
+ uint32_t from_area_offset;
+ uint32_t data_offset;
+ uint8_t *data;
+ uint8_t from_area_idx;
+ int rc;
+
+ data = malloc(data_len);
+ if (data == NULL) {
+ rc = FS_ENOMEM;
+ goto done;
+ }
+
+ to_area = nffs_areas + to_area_idx;
+
+ entry = last_entry;
+ data_offset = data_len;
+ while (data_offset > 0) {
+ rc = nffs_block_from_hash_entry(&block, entry);
+ if (rc != 0) {
+ goto done;
+ }
+ data_offset -= block.nb_data_len;
+
+ nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc,
+ &from_area_idx, &from_area_offset);
+ from_area_offset += sizeof disk_block;
+ rc = nffs_flash_read(from_area_idx, from_area_offset,
+ data + data_offset, block.nb_data_len);
+ if (rc != 0) {
+ goto done;
+ }
+
+ if (entry != last_entry) {
+ if (inout_next != NULL && *inout_next == entry) {
+ *inout_next = SLIST_NEXT(entry, nhe_next);
+ }
+ nffs_block_delete_from_ram(entry);
+ }
+ entry = block.nb_prev;
+ }
+
+ memset(&disk_block, 0, sizeof disk_block);
+ disk_block.ndb_magic = NFFS_BLOCK_MAGIC;
+ disk_block.ndb_id = block.nb_hash_entry->nhe_id;
+ disk_block.ndb_seq = block.nb_seq + 1;
+ disk_block.ndb_inode_id = block.nb_inode_entry->nie_hash_entry.nhe_id;
+ if (entry == NULL) {
+ disk_block.ndb_prev_id = NFFS_ID_NONE;
+ } else {
+ disk_block.ndb_prev_id = entry->nhe_id;
+ }
+ disk_block.ndb_data_len = data_len;
+ nffs_crc_disk_block_fill(&disk_block, data);
+
+ to_area_offset = to_area->na_cur;
+ rc = nffs_flash_write(to_area_idx, to_area_offset,
+ &disk_block, sizeof disk_block);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = nffs_flash_write(to_area_idx, to_area_offset + sizeof disk_block,
+ data, data_len);
+ if (rc != 0) {
+ goto done;
+ }
+
+ last_entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset);
+
+ rc = 0;
+
+ ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, to_area_idx,
+ to_area_offset) == 0);
+
+done:
+ free(data);
+ return rc;
+}
+
+/**
+ * Moves a chain of blocks from one area to another. This function attempts to
+ * collate the blocks into a single new block in the destination area. If
+ * there is insufficient heap memory do to this, the function falls back to
+ * copying each block separately.
+ *
+ * @param last_entry The last block entry in the chain.
+ * @param multiple_blocks 0=single block; 1=more than one block.
+ * @param data_len The total length of data to collate.
+ * @param to_area_idx The index of the area to copy to.
+ * @param inout_next This parameter is only necessary if you are
+ * calling this function during an iteration
+ * of the entire hash table; pass null
+ * otherwise.
+ * On input, this points to the next hash entry
+ * you plan on processing.
+ * On output, this points to the next hash entry
+ * that should be processed.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+nffs_gc_block_chain(struct nffs_hash_entry *last_entry, int multiple_blocks,
+ uint32_t data_len, uint8_t to_area_idx,
+ struct nffs_hash_entry **inout_next)
+{
+ int rc;
+
+ if (!multiple_blocks) {
+ /* If there is only one block, collation has the same effect as a
+ * simple copy. Just perform the more efficient copy.
+ */
+ rc = nffs_gc_block_chain_copy(last_entry, data_len, to_area_idx);
+ } else {
+ rc = nffs_gc_block_chain_collate(last_entry, data_len, to_area_idx,
+ inout_next);
+ if (rc == FS_ENOMEM) {
+ /* Insufficient heap for collation; just copy each block one by
+ * one.
+ */
+ rc = nffs_gc_block_chain_copy(last_entry, data_len, to_area_idx);
+ }
+ }
+
+ return rc;
+}
+
+static int
+nffs_gc_inode_blocks(struct nffs_inode_entry *inode_entry,
+ uint8_t from_area_idx, uint8_t to_area_idx,
+ struct nffs_hash_entry **inout_next)
+{
+ struct nffs_hash_entry *last_entry;
+ struct nffs_hash_entry *entry;
+ struct nffs_block block;
+ uint32_t prospective_data_len;
+ uint32_t area_offset;
+ uint32_t data_len;
+ uint8_t area_idx;
+ int multiple_blocks;
+ int rc;
+
+ assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
+
+ data_len = 0;
+ last_entry = NULL;
+ multiple_blocks = 0;
+ entry = inode_entry->nie_last_block_entry;
+ while (entry != NULL) {
+ rc = nffs_block_from_hash_entry(&block, entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ nffs_flash_loc_expand(entry->nhe_flash_loc, &area_idx, &area_offset);
+ if (area_idx == from_area_idx) {
+ if (last_entry == NULL) {
+ last_entry = entry;
+ }
+
+ prospective_data_len = data_len + block.nb_data_len;
+ if (prospective_data_len <= nffs_block_max_data_sz) {
+ data_len = prospective_data_len;
+ if (last_entry != entry) {
+ multiple_blocks = 1;
+ }
+ } else {
+ rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
+ to_area_idx, inout_next);
+ if (rc != 0) {
+ return rc;
+ }
+ last_entry = entry;
+ data_len = block.nb_data_len;
+ multiple_blocks = 0;
+ }
+ } else {
+ if (last_entry != NULL) {
+ rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
+ to_area_idx, inout_next);
+ if (rc != 0) {
+ return rc;
+ }
+
+ last_entry = NULL;
+ data_len = 0;
+ multiple_blocks = 0;
+ }
+ }
+
+ entry = block.nb_prev;
+ }
+
+ if (last_entry != NULL) {
+ rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
+ to_area_idx, inout_next);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Triggers a garbage collection cycle. This is implemented as follows:
+ *
+ * (1) The non-scratch area with the lowest garbage collection sequence
+ * number is selected as the "source area." If there are other areas
+ * with the same sequence number, the first one encountered is selected.
+ *
+ * (2) The source area's ID is written to the scratch area's header,
+ * transforming it into a non-scratch ID. The former scratch area is now
+ * known as the "destination area."
+ *
+ * (3) The RAM representation is exhaustively searched for objects which are
+ * resident in the source area. The copy is accomplished as follows:
+ *
+ * For each inode:
+ * (a) If the inode is resident in the source area, copy the inode
+ * record to the destination area.
+ *
+ * (b) Walk the inode's list of data blocks, starting with the last
+ * block in the file. Each block that is resident in the source
+ * area is copied to the destination area. If there is a run of
+ * two or more blocks that are resident in the source area, they
+ * are consolidated and copied to the destination area as a single
+ * new block.
+ *
+ * (4) The source area is reformatted as a scratch sector (i.e., its header
+ * indicates an ID of 0xffff). The area's garbage collection sequence
+ * number is incremented prior to rewriting the header. This area is now
+ * the new scratch sector.
+ *
+ * @param out_area_idx On success, the ID of the cleaned up area gets
+ * written here. Pass null if you do not need
+ * this information.
+ *
+ * @return 0 on success; nonzero on error.
+ */
+int
+nffs_gc(uint8_t *out_area_idx)
+{
+ struct nffs_hash_entry *entry;
+ struct nffs_hash_entry *next;
+ struct nffs_area *from_area;
+ struct nffs_area *to_area;
+ struct nffs_inode_entry *inode_entry;
+ uint32_t area_offset;
+ uint8_t from_area_idx;
+ uint8_t area_idx;
+ int rc;
+ int i;
+
+ from_area_idx = nffs_gc_select_area();
+ from_area = nffs_areas + from_area_idx;
+ to_area = nffs_areas + nffs_scratch_area_idx;
+
+ rc = nffs_format_from_scratch_area(nffs_scratch_area_idx,
+ from_area->na_id);
+ if (rc != 0) {
+ return rc;
+ }
+
+ for (i = 0; i < NFFS_HASH_SIZE; i++) {
+ entry = SLIST_FIRST(nffs_hash + i);
+ while (entry != NULL) {
+ next = SLIST_NEXT(entry, nhe_next);
+
+ if (nffs_hash_id_is_inode(entry->nhe_id)) {
+ /* The inode gets copied if it is in the source area. */
+ nffs_flash_loc_expand(entry->nhe_flash_loc,
+ &area_idx, &area_offset);
+ inode_entry = (struct nffs_inode_entry *)entry;
+ if (area_idx == from_area_idx) {
+ rc = nffs_gc_copy_inode(inode_entry,
+ nffs_scratch_area_idx);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ /* If the inode is a file, all constituent data blocks that are
+ * resident in the source area get copied.
+ */
+ if (nffs_hash_id_is_file(entry->nhe_id)) {
+ rc = nffs_gc_inode_blocks(inode_entry, from_area_idx,
+ nffs_scratch_area_idx, &next);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+ }
+
+ entry = next;
+ }
+ }
+
+ /* The amount of written data should never increase as a result of a gc
+ * cycle.
+ */
+ assert(to_area->na_cur <= from_area->na_cur);
+
+ /* Turn the source area into the new scratch area. */
+ from_area->na_gc_seq++;
+ rc = nffs_format_area(from_area_idx, 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (out_area_idx != NULL) {
+ *out_area_idx = nffs_scratch_area_idx;
+ }
+
+ nffs_scratch_area_idx = from_area_idx;
+
+ return 0;
+}
+
+/**
+ * Repeatedly performs garbage collection cycles until there is enough free
+ * space to accommodate an object of the specified size. If there still isn't
+ * enough free space after every area has been garbage collected, this function
+ * fails.
+ *
+ * @param space The number of bytes of free space required.
+ * @param out_area_idx On success, the index of the area which can
+ * accommodate the necessary data.
+ *
+ * @return 0 on success;
+ * FS_EFULL if the necessary space could not be
+ * freed.
+ * nonzero on other failure.
+ */
+int
+nffs_gc_until(uint32_t space, uint8_t *out_area_idx)
+{
+ int rc;
+ int i;
+
+ for (i = 0; i < nffs_num_areas; i++) {
+ rc = nffs_gc(out_area_idx);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (nffs_area_free_space(nffs_areas + *out_area_idx) >= space) {
+ return 0;
+ }
+ }
+
+ return FS_EFULL;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_hash.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_hash.c b/fs/nffs/src/nffs_hash.c
new file mode 100644
index 0000000..4d6bff2
--- /dev/null
+++ b/fs/nffs/src/nffs_hash.c
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+
+struct nffs_hash_list *nffs_hash;
+
+uint32_t nffs_hash_next_dir_id;
+uint32_t nffs_hash_next_file_id;
+uint32_t nffs_hash_next_block_id;
+
+int
+nffs_hash_id_is_dir(uint32_t id)
+{
+ return id >= NFFS_ID_DIR_MIN && id < NFFS_ID_DIR_MAX;
+}
+
+int
+nffs_hash_id_is_file(uint32_t id)
+{
+ return id >= NFFS_ID_FILE_MIN && id < NFFS_ID_FILE_MAX;
+}
+
+int
+nffs_hash_id_is_inode(uint32_t id)
+{
+ return nffs_hash_id_is_dir(id) || nffs_hash_id_is_file(id);
+}
+
+int
+nffs_hash_id_is_block(uint32_t id)
+{
+ return id >= NFFS_ID_BLOCK_MIN && id < NFFS_ID_BLOCK_MAX;
+}
+
+static int
+nffs_hash_fn(uint32_t id)
+{
+ return id % NFFS_HASH_SIZE;
+}
+
+struct nffs_hash_entry *
+nffs_hash_find(uint32_t id)
+{
+ struct nffs_hash_entry *entry;
+ struct nffs_hash_entry *prev;
+ struct nffs_hash_list *list;
+ int idx;
+
+ idx = nffs_hash_fn(id);
+ list = nffs_hash + idx;
+
+ prev = NULL;
+ SLIST_FOREACH(entry, list, nhe_next) {
+ if (entry->nhe_id == id) {
+ /* Put entry at the front of the list. */
+ if (prev != NULL) {
+ SLIST_NEXT(prev, nhe_next) = SLIST_NEXT(entry, nhe_next);
+ SLIST_INSERT_HEAD(list, entry, nhe_next);
+ }
+ return entry;
+ }
+
+ prev = entry;
+ }
+
+ return NULL;
+}
+
+struct nffs_inode_entry *
+nffs_hash_find_inode(uint32_t id)
+{
+ struct nffs_hash_entry *entry;
+
+ assert(nffs_hash_id_is_inode(id));
+
+ entry = nffs_hash_find(id);
+ return (struct nffs_inode_entry *)entry;
+}
+
+struct nffs_hash_entry *
+nffs_hash_find_block(uint32_t id)
+{
+ struct nffs_hash_entry *entry;
+
+ assert(nffs_hash_id_is_block(id));
+
+ entry = nffs_hash_find(id);
+ return entry;
+}
+
+void
+nffs_hash_insert(struct nffs_hash_entry *entry)
+{
+ struct nffs_hash_list *list;
+ int idx;
+
+ idx = nffs_hash_fn(entry->nhe_id);
+ list = nffs_hash + idx;
+
+ SLIST_INSERT_HEAD(list, entry, nhe_next);
+}
+
+void
+nffs_hash_remove(struct nffs_hash_entry *entry)
+{
+ struct nffs_hash_list *list;
+ int idx;
+
+ idx = nffs_hash_fn(entry->nhe_id);
+ list = nffs_hash + idx;
+
+ SLIST_REMOVE(list, entry, nffs_hash_entry, nhe_next);
+}
+
+int
+nffs_hash_init(void)
+{
+ int i;
+
+ free(nffs_hash);
+
+ nffs_hash = malloc(NFFS_HASH_SIZE * sizeof *nffs_hash);
+ if (nffs_hash == NULL) {
+ return FS_ENOMEM;
+ }
+
+ for (i = 0; i < NFFS_HASH_SIZE; i++) {
+ SLIST_INIT(nffs_hash + i);
+ }
+
+ return 0;
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_inode.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_inode.c b/fs/nffs/src/nffs_inode.c
new file mode 100644
index 0000000..f4d1e46
--- /dev/null
+++ b/fs/nffs/src/nffs_inode.c
@@ -0,0 +1,906 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include "testutil/testutil.h"
+#include "os/os_mempool.h"
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+#include "crc16.h"
+
+/* Partition the flash buffer into two equal halves; used for filename
+ * comparisons.
+ */
+#define NFFS_INODE_FILENAME_BUF_SZ (sizeof nffs_flash_buf / 2)
+static uint8_t *nffs_inode_filename_buf0 = nffs_flash_buf;
+static uint8_t *nffs_inode_filename_buf1 =
+ nffs_flash_buf + NFFS_INODE_FILENAME_BUF_SZ;
+
+/** A list of directory inodes with pending unlink operations. */
+static struct nffs_hash_list nffs_inode_unlink_list;
+
+struct nffs_inode_entry *
+nffs_inode_entry_alloc(void)
+{
+ struct nffs_inode_entry *inode_entry;
+
+ inode_entry = os_memblock_get(&nffs_inode_entry_pool);
+ if (inode_entry != NULL) {
+ memset(inode_entry, 0, sizeof *inode_entry);
+ }
+
+ return inode_entry;
+}
+
+void
+nffs_inode_entry_free(struct nffs_inode_entry *inode_entry)
+{
+ if (inode_entry != NULL) {
+ assert(nffs_hash_id_is_inode(inode_entry->nie_hash_entry.nhe_id));
+ os_memblock_put(&nffs_inode_entry_pool, inode_entry);
+ }
+}
+
+uint32_t
+nffs_inode_disk_size(const struct nffs_inode *inode)
+{
+ return sizeof (struct nffs_disk_inode) + inode->ni_filename_len;
+}
+
+int
+nffs_inode_read_disk(uint8_t area_idx, uint32_t offset,
+ struct nffs_disk_inode *out_disk_inode)
+{
+ int rc;
+
+ rc = nffs_flash_read(area_idx, offset, out_disk_inode,
+ sizeof *out_disk_inode);
+ if (rc != 0) {
+ return rc;
+ }
+ if (out_disk_inode->ndi_magic != NFFS_INODE_MAGIC) {
+ return FS_EUNEXP;
+ }
+
+ return 0;
+}
+
+int
+nffs_inode_write_disk(const struct nffs_disk_inode *disk_inode,
+ const char *filename, uint8_t area_idx,
+ uint32_t area_offset)
+{
+ int rc;
+
+ rc = nffs_flash_write(area_idx, area_offset, disk_inode,
+ sizeof *disk_inode);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (disk_inode->ndi_filename_len != 0) {
+ rc = nffs_flash_write(area_idx, area_offset + sizeof *disk_inode,
+ filename, disk_inode->ndi_filename_len);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ ASSERT_IF_TEST(nffs_crc_disk_inode_validate(disk_inode, area_idx,
+ area_offset) == 0);
+
+ return 0;
+}
+
+int
+nffs_inode_calc_data_length(struct nffs_inode_entry *inode_entry,
+ uint32_t *out_len)
+{
+ struct nffs_hash_entry *cur;
+ struct nffs_block block;
+ int rc;
+
+ assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
+
+ *out_len = 0;
+
+ cur = inode_entry->nie_last_block_entry;
+ while (cur != NULL) {
+ rc = nffs_block_from_hash_entry(&block, cur);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *out_len += block.nb_data_len;
+
+ cur = block.nb_prev;
+ }
+
+ return 0;
+}
+
+int
+nffs_inode_data_len(struct nffs_inode_entry *inode_entry, uint32_t *out_len)
+{
+ struct nffs_cache_inode *cache_inode;
+ int rc;
+
+ rc = nffs_cache_inode_ensure(&cache_inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *out_len = cache_inode->nci_file_size;
+
+ return 0;
+}
+
+int
+nffs_inode_from_entry(struct nffs_inode *out_inode,
+ struct nffs_inode_entry *entry)
+{
+ struct nffs_disk_inode disk_inode;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int cached_name_len;
+ int rc;
+
+ nffs_flash_loc_expand(entry->nie_hash_entry.nhe_flash_loc,
+ &area_idx, &area_offset);
+
+ rc = nffs_inode_read_disk(area_idx, area_offset, &disk_inode);
+ if (rc != 0) {
+ return rc;
+ }
+
+ out_inode->ni_inode_entry = entry;
+ out_inode->ni_seq = disk_inode.ndi_seq;
+ if (disk_inode.ndi_parent_id == NFFS_ID_NONE) {
+ out_inode->ni_parent = NULL;
+ } else {
+ out_inode->ni_parent = nffs_hash_find_inode(disk_inode.ndi_parent_id);
+ }
+ out_inode->ni_filename_len = disk_inode.ndi_filename_len;
+
+ if (out_inode->ni_filename_len > NFFS_SHORT_FILENAME_LEN) {
+ cached_name_len = NFFS_SHORT_FILENAME_LEN;
+ } else {
+ cached_name_len = out_inode->ni_filename_len;
+ }
+ if (cached_name_len != 0) {
+ rc = nffs_flash_read(area_idx, area_offset + sizeof disk_inode,
+ out_inode->ni_filename, cached_name_len);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+uint32_t
+nffs_inode_parent_id(const struct nffs_inode *inode)
+{
+ if (inode->ni_parent == NULL) {
+ return NFFS_ID_NONE;
+ } else {
+ return inode->ni_parent->nie_hash_entry.nhe_id;
+ }
+}
+
+static int
+nffs_inode_delete_blocks_from_ram(struct nffs_inode_entry *inode_entry)
+{
+ int rc;
+
+ assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
+
+ while (inode_entry->nie_last_block_entry != NULL) {
+ rc = nffs_block_delete_from_ram(inode_entry->nie_last_block_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int
+nffs_inode_delete_from_ram(struct nffs_inode_entry *inode_entry)
+{
+ int rc;
+
+ if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
+ rc = nffs_inode_delete_blocks_from_ram(inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ nffs_cache_inode_delete(inode_entry);
+ nffs_hash_remove(&inode_entry->nie_hash_entry);
+ nffs_inode_entry_free(inode_entry);
+
+ return 0;
+}
+
+/**
+ * Inserts the specified inode entry into the unlink list. Because a hash
+ * entry only has a single 'next' pointer, this function removes the entry from
+ * the hash table prior to inserting it into the unlink list.
+ *
+ * @param inode_entry The inode entry to insert.
+ */
+static void
+nffs_inode_insert_unlink_list(struct nffs_inode_entry *inode_entry)
+{
+ nffs_hash_remove(&inode_entry->nie_hash_entry);
+ SLIST_INSERT_HEAD(&nffs_inode_unlink_list, &inode_entry->nie_hash_entry,
+ nhe_next);
+}
+
+/**
+ * Decrements the reference count of the specified inode entry.
+ *
+ * @param inode_entry The inode entry whose reference count
+ * should be decremented.
+ */
+int
+nffs_inode_dec_refcnt(struct nffs_inode_entry *inode_entry)
+{
+ int rc;
+
+ assert(inode_entry->nie_refcnt > 0);
+
+ inode_entry->nie_refcnt--;
+ if (inode_entry->nie_refcnt == 0) {
+ if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
+ rc = nffs_inode_delete_from_ram(inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ } else {
+ nffs_inode_insert_unlink_list(inode_entry);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Unlinks every directory inode entry present in the unlink list. After this
+ * function completes:
+ * o Each descendant directory is deleted from RAM.
+ * o Each descendant file has its reference count decremented (and deleted
+ * from RAM if its reference count reaches zero).
+ *
+ * @param inout_next This parameter is only necessary if you are
+ * calling this function during an iteration
+ * of the entire hash table; pass null
+ * otherwise.
+ * On input, this points to the next hash entry
+ * you plan on processing.
+ * On output, this points to the next hash entry
+ * that should be processed.
+ */
+static int
+nffs_inode_process_unlink_list(struct nffs_hash_entry **inout_next)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_inode_entry *child_next;
+ struct nffs_inode_entry *child;
+ struct nffs_hash_entry *hash_entry;
+ int rc;
+
+ while ((hash_entry = SLIST_FIRST(&nffs_inode_unlink_list)) != NULL) {
+ assert(nffs_hash_id_is_dir(hash_entry->nhe_id));
+
+ SLIST_REMOVE_HEAD(&nffs_inode_unlink_list, nhe_next);
+
+ inode_entry = (struct nffs_inode_entry *)hash_entry;
+
+ /* Recursively unlink each child. */
+ child = SLIST_FIRST(&inode_entry->nie_child_list);
+ while (child != NULL) {
+ child_next = SLIST_NEXT(child, nie_sibling_next);
+
+ if (inout_next != NULL && *inout_next == &child->nie_hash_entry) {
+ *inout_next = &child_next->nie_hash_entry;
+ }
+
+ rc = nffs_inode_dec_refcnt(child);
+ if (rc != 0) {
+ return rc;
+ }
+
+ child = child_next;
+ }
+
+ /* The directory is already removed from the hash table; just free its
+ * memory.
+ */
+ nffs_inode_entry_free(inode_entry);
+ }
+
+ return 0;
+}
+
+int
+nffs_inode_delete_from_disk(struct nffs_inode *inode)
+{
+ struct nffs_disk_inode disk_inode;
+ uint32_t offset;
+ uint8_t area_idx;
+ int rc;
+
+ /* Make sure it isn't already deleted. */
+ assert(inode->ni_parent != NULL);
+
+ rc = nffs_misc_reserve_space(sizeof disk_inode, &area_idx, &offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ inode->ni_seq++;
+
+ disk_inode.ndi_magic = NFFS_INODE_MAGIC;
+ disk_inode.ndi_id = inode->ni_inode_entry->nie_hash_entry.nhe_id;
+ disk_inode.ndi_seq = inode->ni_seq;
+ disk_inode.ndi_parent_id = NFFS_ID_NONE;
+ disk_inode.ndi_filename_len = 0;
+ nffs_crc_disk_inode_fill(&disk_inode, "");
+
+ rc = nffs_inode_write_disk(&disk_inode, "", area_idx, offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+nffs_inode_rename(struct nffs_inode_entry *inode_entry,
+ struct nffs_inode_entry *new_parent,
+ const char *new_filename)
+{
+ struct nffs_disk_inode disk_inode;
+ struct nffs_inode inode;
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int filename_len;
+ int rc;
+
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (inode.ni_parent != new_parent) {
+ if (inode.ni_parent != NULL) {
+ nffs_inode_remove_child(&inode);
+ }
+ if (new_parent != NULL) {
+ rc = nffs_inode_add_child(new_parent, inode.ni_inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+ inode.ni_parent = new_parent;
+ }
+
+ if (new_filename != NULL) {
+ filename_len = strlen(new_filename);
+ } else {
+ filename_len = inode.ni_filename_len;
+ nffs_flash_loc_expand(inode_entry->nie_hash_entry.nhe_flash_loc,
+ &area_idx, &area_offset);
+ rc = nffs_flash_read(area_idx,
+ area_offset + sizeof (struct nffs_disk_inode),
+ nffs_flash_buf, filename_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ new_filename = (char *)nffs_flash_buf;
+ }
+
+ rc = nffs_misc_reserve_space(sizeof disk_inode + filename_len,
+ &area_idx, &area_offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ disk_inode.ndi_magic = NFFS_INODE_MAGIC;
+ disk_inode.ndi_id = inode_entry->nie_hash_entry.nhe_id;
+ disk_inode.ndi_seq = inode.ni_seq + 1;
+ disk_inode.ndi_parent_id = nffs_inode_parent_id(&inode);
+ disk_inode.ndi_filename_len = filename_len;
+ nffs_crc_disk_inode_fill(&disk_inode, new_filename);
+
+ rc = nffs_inode_write_disk(&disk_inode, new_filename, area_idx,
+ area_offset);
+ if (rc != 0) {
+ return rc;
+ }
+
+ inode_entry->nie_hash_entry.nhe_flash_loc =
+ nffs_flash_loc(area_idx, area_offset);
+
+ return 0;
+}
+
+static int
+nffs_inode_read_filename_chunk(const struct nffs_inode *inode,
+ uint8_t filename_offset, void *buf, int len)
+{
+ uint32_t area_offset;
+ uint8_t area_idx;
+ int rc;
+
+ assert(filename_offset + len <= inode->ni_filename_len);
+
+ nffs_flash_loc_expand(inode->ni_inode_entry->nie_hash_entry.nhe_flash_loc,
+ &area_idx, &area_offset);
+ area_offset += sizeof (struct nffs_disk_inode) + filename_offset;
+
+ rc = nffs_flash_read(area_idx, area_offset, buf, len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Retrieves the filename of the specified inode. The retrieved
+ * filename is always null-terminated. To ensure enough space to hold the full
+ * filename plus a null-termintor, a destination buffer of size
+ * (NFFS_FILENAME_MAX_LEN + 1) should be used.
+ *
+ * @param inode_entry The inode entry to query.
+ * @param max_len The size of the "out_name" character buffer.
+ * @param out_name On success, the entry's filename is written
+ * here; always null-terminated.
+ * @param out_name_len On success, contains the actual length of the
+ * filename, NOT including the
+ * null-terminator.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_inode_read_filename(struct nffs_inode_entry *inode_entry, size_t max_len,
+ char *out_name, uint8_t *out_full_len)
+{
+ struct nffs_inode inode;
+ int read_len;
+ int rc;
+
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (max_len > inode.ni_filename_len) {
+ read_len = inode.ni_filename_len;
+ } else {
+ read_len = max_len - 1;
+ }
+
+ rc = nffs_inode_read_filename_chunk(&inode, 0, out_name, read_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ out_name[read_len] = '\0';
+
+ return 0;
+}
+
+int
+nffs_inode_add_child(struct nffs_inode_entry *parent,
+ struct nffs_inode_entry *child)
+{
+ struct nffs_inode_entry *prev;
+ struct nffs_inode_entry *cur;
+ struct nffs_inode child_inode;
+ struct nffs_inode cur_inode;
+ int cmp;
+ int rc;
+
+ assert(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
+
+ rc = nffs_inode_from_entry(&child_inode, child);
+ if (rc != 0) {
+ return rc;
+ }
+
+ prev = NULL;
+ SLIST_FOREACH(cur, &parent->nie_child_list, nie_sibling_next) {
+ assert(cur != child);
+ rc = nffs_inode_from_entry(&cur_inode, cur);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_filename_cmp_flash(&child_inode, &cur_inode, &cmp);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (cmp < 0) {
+ break;
+ }
+
+ prev = cur;
+ }
+
+ if (prev == NULL) {
+ SLIST_INSERT_HEAD(&parent->nie_child_list, child, nie_sibling_next);
+ } else {
+ SLIST_INSERT_AFTER(prev, child, nie_sibling_next);
+ }
+
+ return 0;
+}
+
+void
+nffs_inode_remove_child(struct nffs_inode *child)
+{
+ struct nffs_inode_entry *parent;
+
+ parent = child->ni_parent;
+ assert(parent != NULL);
+ assert(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
+ SLIST_REMOVE(&parent->nie_child_list, child->ni_inode_entry,
+ nffs_inode_entry, nie_sibling_next);
+ SLIST_NEXT(child->ni_inode_entry, nie_sibling_next) = NULL;
+}
+
+int
+nffs_inode_filename_cmp_ram(const struct nffs_inode *inode,
+ const char *name, int name_len,
+ int *result)
+{
+ int short_len;
+ int chunk_len;
+ int rem_len;
+ int off;
+ int rc;
+
+ if (name_len < inode->ni_filename_len) {
+ short_len = name_len;
+ } else {
+ short_len = inode->ni_filename_len;
+ }
+
+ if (short_len <= NFFS_SHORT_FILENAME_LEN) {
+ chunk_len = short_len;
+ } else {
+ chunk_len = NFFS_SHORT_FILENAME_LEN;
+ }
+ *result = strncmp((char *)inode->ni_filename, name, chunk_len);
+
+ off = chunk_len;
+ while (*result == 0 && off < short_len) {
+ rem_len = short_len - off;
+ if (rem_len > NFFS_INODE_FILENAME_BUF_SZ) {
+ chunk_len = NFFS_INODE_FILENAME_BUF_SZ;
+ } else {
+ chunk_len = rem_len;
+ }
+
+ rc = nffs_inode_read_filename_chunk(inode, off,
+ nffs_inode_filename_buf0,
+ chunk_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *result = strncmp((char *)nffs_inode_filename_buf0, name + off,
+ chunk_len);
+ off += chunk_len;
+ }
+
+ if (*result == 0) {
+ *result = inode->ni_filename_len - name_len;
+ }
+
+ return 0;
+}
+
+int
+nffs_inode_filename_cmp_flash(const struct nffs_inode *inode1,
+ const struct nffs_inode *inode2,
+ int *result)
+{
+ int short_len;
+ int chunk_len;
+ int rem_len;
+ int off;
+ int rc;
+
+ if (inode1->ni_filename_len < inode2->ni_filename_len) {
+ short_len = inode1->ni_filename_len;
+ } else {
+ short_len = inode2->ni_filename_len;
+ }
+
+ if (short_len <= NFFS_SHORT_FILENAME_LEN) {
+ chunk_len = short_len;
+ } else {
+ chunk_len = NFFS_SHORT_FILENAME_LEN;
+ }
+ *result = strncmp((char *)inode1->ni_filename,
+ (char *)inode2->ni_filename,
+ chunk_len);
+
+ off = chunk_len;
+ while (*result == 0 && off < short_len) {
+ rem_len = short_len - off;
+ if (rem_len > NFFS_INODE_FILENAME_BUF_SZ) {
+ chunk_len = NFFS_INODE_FILENAME_BUF_SZ;
+ } else {
+ chunk_len = rem_len;
+ }
+
+ rc = nffs_inode_read_filename_chunk(inode1, off,
+ nffs_inode_filename_buf0,
+ chunk_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_read_filename_chunk(inode2, off,
+ nffs_inode_filename_buf1,
+ chunk_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *result = strncmp((char *)nffs_inode_filename_buf0,
+ (char *)nffs_inode_filename_buf1,
+ chunk_len);
+ off += chunk_len;
+ }
+
+ if (*result == 0) {
+ *result = inode1->ni_filename_len - inode2->ni_filename_len;
+ }
+
+ return 0;
+}
+
+/**
+ * Finds the set of blocks composing the specified address range within the
+ * supplied file inode. This information is returned in the form of a
+ * seek_info object.
+ *
+ * @param inode_entry The file inode to seek within.
+ * @param offset The start address of the region to seek to.
+ * @param length The length of the region to seek to.
+ * @param out_seek_info On success, this gets populated with the result
+ * of the seek operation.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_inode_seek(struct nffs_inode_entry *inode_entry, uint32_t offset,
+ uint32_t length, struct nffs_seek_info *out_seek_info)
+{
+ struct nffs_cache_inode *cache_inode;
+ struct nffs_hash_entry *cur_entry;
+ struct nffs_block block;
+ uint32_t block_start;
+ uint32_t cur_offset;
+ uint32_t seek_end;
+ int rc;
+
+ assert(length > 0);
+ assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
+
+ rc = nffs_cache_inode_ensure(&cache_inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (offset > cache_inode->nci_file_size) {
+ return FS_ERANGE;
+ }
+ if (offset == cache_inode->nci_file_size) {
+ memset(&out_seek_info->nsi_last_block, 0,
+ sizeof out_seek_info->nsi_last_block);
+ out_seek_info->nsi_last_block.nb_hash_entry = NULL;
+ out_seek_info->nsi_block_file_off = 0;
+ out_seek_info->nsi_file_len = cache_inode->nci_file_size;
+ return 0;
+ }
+
+ seek_end = offset + length;
+
+ cur_entry = inode_entry->nie_last_block_entry;
+ cur_offset = cache_inode->nci_file_size;
+
+ while (1) {
+ rc = nffs_block_from_hash_entry(&block, cur_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ block_start = cur_offset - block.nb_data_len;
+ if (seek_end > block_start) {
+ out_seek_info->nsi_last_block = block;
+ out_seek_info->nsi_block_file_off = block_start;
+ out_seek_info->nsi_file_len = cache_inode->nci_file_size;
+ return 0;
+ }
+
+ cur_offset = block_start;
+ cur_entry = block.nb_prev;
+ }
+}
+
+/**
+ * Reads data from the specified file inode.
+ *
+ * @param inode_entry The inode to read from.
+ * @param offset The offset within the file to start the read
+ * at.
+ * @param len The number of bytes to attempt to read.
+ * @param out_data On success, the read data gets written here.
+ * @param out_len On success, the number of bytes actually read
+ * gets written here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_inode_read(struct nffs_inode_entry *inode_entry, uint32_t offset,
+ uint32_t len, void *out_data, uint32_t *out_len)
+{
+ struct nffs_cache_inode *cache_inode;
+ struct nffs_cache_block *cache_block;
+ uint32_t block_end;
+ uint32_t dst_off;
+ uint32_t src_off;
+ uint32_t src_end;
+ uint16_t block_off;
+ uint16_t chunk_sz;
+ uint8_t *dptr;
+ int rc;
+
+ if (len == 0) {
+ if (out_len != NULL) {
+ *out_len = 0;
+ }
+ return 0;
+ }
+
+ rc = nffs_cache_inode_ensure(&cache_inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ src_end = offset + len;
+ if (src_end > cache_inode->nci_file_size) {
+ src_end = cache_inode->nci_file_size;
+ }
+
+ /* Initialize variables for the first iteration. */
+ dst_off = src_end - offset;
+ src_off = src_end;
+ dptr = out_data;
+ cache_block = NULL;
+
+ /* Read each relevant block into the destination buffer, iterating in
+ * reverse.
+ */
+ while (dst_off > 0) {
+ if (cache_block == NULL) {
+ rc = nffs_cache_seek(cache_inode, src_off - 1, &cache_block);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ if (cache_block->ncb_file_offset < offset) {
+ block_off = offset - cache_block->ncb_file_offset;
+ } else {
+ block_off = 0;
+ }
+
+ block_end = cache_block->ncb_file_offset +
+ cache_block->ncb_block.nb_data_len;
+ chunk_sz = cache_block->ncb_block.nb_data_len - block_off;
+ if (block_end > src_end) {
+ chunk_sz -= block_end - src_end;
+ }
+
+ dst_off -= chunk_sz;
+ src_off -= chunk_sz;
+
+ rc = nffs_block_read_data(&cache_block->ncb_block, block_off, chunk_sz,
+ dptr + dst_off);
+ if (rc != 0) {
+ return rc;
+ }
+
+ cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list, ncb_link);
+ }
+
+ if (out_len != NULL) {
+ *out_len = src_end - offset;
+ }
+
+ return 0;
+}
+
+int
+nffs_inode_unlink_from_ram(struct nffs_inode *inode,
+ struct nffs_hash_entry **out_next)
+{
+ int rc;
+
+ if (inode->ni_parent != NULL) {
+ nffs_inode_remove_child(inode);
+ }
+
+ if (nffs_hash_id_is_dir(inode->ni_inode_entry->nie_hash_entry.nhe_id)) {
+ nffs_inode_insert_unlink_list(inode->ni_inode_entry);
+ rc = nffs_inode_process_unlink_list(out_next);
+ } else {
+ rc = nffs_inode_dec_refcnt(inode->ni_inode_entry);
+ }
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Unlinks the file or directory represented by the specified inode. If the
+ * inode represents a directory, all the directory's descendants are
+ * recursively unlinked. Any open file handles refering to an unlinked file
+ * remain valid, and can be read from and written to.
+ *
+ * When an inode is unlinked, the following events occur:
+ * o inode deletion record is written to disk.
+ * o inode is removed from parent's child list.
+ * o inode's reference count is decreased (if this brings it to zero, the
+ * inode is fully deleted from RAM).
+ *
+ * @param inode The inode to unlink.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_inode_unlink(struct nffs_inode *inode)
+{
+ int rc;
+
+ rc = nffs_inode_delete_from_disk(inode);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_unlink_from_ram(inode, NULL);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_misc.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_misc.c b/fs/nffs/src/nffs_misc.c
new file mode 100644
index 0000000..e613b2c
--- /dev/null
+++ b/fs/nffs/src/nffs_misc.c
@@ -0,0 +1,352 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include "os/os_malloc.h"
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+#include "crc16.h"
+
+/**
+ * Determines if the file system contains a valid root directory. For the root
+ * directory to be valid, it must be present and have the following traits:
+ * o ID equal to NFFS_ID_ROOT_DIR.
+ * o No parent inode.
+ *
+ * @return 0 if there is a valid root directory;
+ * FS_ECORRUPT if there is not a valid root
+ * directory;
+ * nonzero on other error.
+ */
+int
+nffs_misc_validate_root_dir(void)
+{
+ struct nffs_inode inode;
+ int rc;
+
+ if (nffs_root_dir == NULL) {
+ return FS_ECORRUPT;
+ }
+
+ if (nffs_root_dir->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
+ return FS_ECORRUPT;
+ }
+
+ rc = nffs_inode_from_entry(&inode, nffs_root_dir);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (inode.ni_parent != NULL) {
+ return FS_ECORRUPT;
+ }
+
+ return 0;
+}
+
+/**
+ * Determines if the system contains a valid scratch area. For a scratch area
+ * to be valid, it must be at least as large as the other areas in the file
+ * system.
+ *
+ * @return 0 if there is a valid scratch area;
+ * FS_ECORRUPT otherwise.
+ */
+int
+nffs_misc_validate_scratch(void)
+{
+ uint32_t scratch_len;
+ int i;
+
+ if (nffs_scratch_area_idx == NFFS_AREA_ID_NONE) {
+ /* No scratch area. */
+ return FS_ECORRUPT;
+ }
+
+ scratch_len = nffs_areas[nffs_scratch_area_idx].na_length;
+ for (i = 0; i < nffs_num_areas; i++) {
+ if (nffs_areas[i].na_length > scratch_len) {
+ return FS_ECORRUPT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Reserves the specified number of bytes within the specified area.
+ *
+ * @param area_idx The index of the area to reserve from.
+ * @param space The number of bytes of free space required.
+ * @param out_area_offset On success, the offset within the area gets
+ * written here.
+ *
+ * @return 0 on success;
+ * FS_EFULL if the area has insufficient free
+ * space.
+ */
+static int
+nffs_misc_reserve_space_area(uint8_t area_idx, uint16_t space,
+ uint32_t *out_area_offset)
+{
+ const struct nffs_area *area;
+ uint32_t available;
+
+ area = nffs_areas + area_idx;
+ available = area->na_length - area->na_cur;
+ if (available >= space) {
+ *out_area_offset = area->na_cur;
+ return 0;
+ }
+
+ return FS_EFULL;
+}
+
+/**
+ * Finds an area that can accommodate an object of the specified size. If no
+ * such area exists, this function performs a garbage collection cycle.
+ *
+ * @param space The number of bytes of free space required.
+ * @param out_area_idx On success, the index of the suitable area gets
+ * written here.
+ * @param out_area_offset On success, the offset within the suitable area
+ * gets written here.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_misc_reserve_space(uint16_t space,
+ uint8_t *out_area_idx, uint32_t *out_area_offset)
+{
+ uint8_t area_idx;
+ int rc;
+ int i;
+
+ /* Find the first area with sufficient free space. */
+ for (i = 0; i < nffs_num_areas; i++) {
+ if (i != nffs_scratch_area_idx) {
+ rc = nffs_misc_reserve_space_area(i, space, out_area_offset);
+ if (rc == 0) {
+ *out_area_idx = i;
+ return 0;
+ }
+ }
+ }
+
+ /* No area can accommodate the request. Garbage collect until an area
+ * has enough space.
+ */
+ rc = nffs_gc_until(space, &area_idx);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Now try to reserve space. If insufficient space was reclaimed with
+ * garbage collection, the above call would have failed, so this should
+ * succeed.
+ */
+ rc = nffs_misc_reserve_space_area(area_idx, space, out_area_offset);
+ assert(rc == 0);
+
+ *out_area_idx = area_idx;
+
+ return rc;
+}
+
+int
+nffs_misc_set_num_areas(uint8_t num_areas)
+{
+ if (num_areas == 0) {
+ free(nffs_areas);
+ nffs_areas = NULL;
+ } else {
+ nffs_areas = realloc(nffs_areas, num_areas * sizeof *nffs_areas);
+ if (nffs_areas == NULL) {
+ return FS_ENOMEM;
+ }
+ }
+
+ nffs_num_areas = num_areas;
+
+ return 0;
+}
+
+/**
+ * Calculates the data length of the largest block that could fit in an area of
+ * the specified size.
+ */
+static uint32_t
+nffs_misc_area_capacity_one(uint32_t area_length)
+{
+ return area_length -
+ sizeof (struct nffs_disk_area) -
+ sizeof (struct nffs_disk_block);
+}
+
+/**
+ * Calculates the data length of the largest block that could fit as a pair in
+ * an area of the specified size.
+ */
+static uint32_t
+nffs_misc_area_capacity_two(uint32_t area_length)
+{
+ return (area_length - sizeof (struct nffs_disk_area)) / 2 -
+ sizeof (struct nffs_disk_block);
+}
+
+/**
+ * Calculates and sets the maximum block data length that the system supports.
+ * The result of the calculation is the greatest number which satisfies all of
+ * the following restrictions:
+ * o No more than half the size of the smallest area.
+ * o No more than 2048.
+ * o No smaller than the data length of any existing data block.
+ *
+ * @param min_size The minimum allowed data length. This is the
+ * data length of the largest block currently
+ * in the file system.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_misc_set_max_block_data_len(uint16_t min_data_len)
+{
+ uint32_t smallest_area;
+ uint32_t half_smallest;
+ int i;
+
+ smallest_area = -1;
+ for (i = 0; i < nffs_num_areas; i++) {
+ if (nffs_areas[i].na_length < smallest_area) {
+ smallest_area = nffs_areas[i].na_length;
+ }
+ }
+
+ /* Don't allow a data block size bigger than the smallest area. */
+ if (nffs_misc_area_capacity_one(smallest_area) < min_data_len) {
+ return FS_ECORRUPT;
+ }
+
+ half_smallest = nffs_misc_area_capacity_two(smallest_area);
+ if (half_smallest < NFFS_BLOCK_MAX_DATA_SZ_MAX) {
+ nffs_block_max_data_sz = half_smallest;
+ } else {
+ nffs_block_max_data_sz = NFFS_BLOCK_MAX_DATA_SZ_MAX;
+ }
+
+ if (nffs_block_max_data_sz < min_data_len) {
+ nffs_block_max_data_sz = min_data_len;
+ }
+
+ return 0;
+}
+
+int
+nffs_misc_create_lost_found_dir(void)
+{
+ int rc;
+
+ rc = nffs_path_new_dir("/lost+found", &nffs_lost_found_dir);
+ switch (rc) {
+ case 0:
+ return 0;
+
+ case FS_EEXIST:
+ rc = nffs_path_find_inode_entry("/lost+found", &nffs_lost_found_dir);
+ return rc;
+
+ default:
+ return rc;
+ }
+}
+
+
+/**
+ * Fully resets the nffs RAM representation.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_misc_reset(void)
+{
+ int rc;
+
+ nffs_cache_clear();
+
+ rc = os_mempool_init(&nffs_file_pool, nffs_config.nc_num_files,
+ sizeof (struct nffs_file), nffs_file_mem,
+ "nffs_file_pool");
+ if (rc != 0) {
+ return FS_EOS;
+ }
+
+ rc = os_mempool_init(&nffs_inode_entry_pool, nffs_config.nc_num_inodes,
+ sizeof (struct nffs_inode_entry), nffs_inode_mem,
+ "nffs_inode_entry_pool");
+ if (rc != 0) {
+ return FS_EOS;
+ }
+
+ rc = os_mempool_init(&nffs_block_entry_pool, nffs_config.nc_num_blocks,
+ sizeof (struct nffs_hash_entry), nffs_block_entry_mem,
+ "nffs_block_entry_pool");
+ if (rc != 0) {
+ return FS_EOS;
+ }
+
+ rc = os_mempool_init(&nffs_cache_inode_pool,
+ nffs_config.nc_num_cache_inodes,
+ sizeof (struct nffs_cache_inode),
+ nffs_cache_inode_mem, "nffs_cache_inode_pool");
+ if (rc != 0) {
+ return FS_EOS;
+ }
+
+ rc = os_mempool_init(&nffs_cache_block_pool,
+ nffs_config.nc_num_cache_blocks,
+ sizeof (struct nffs_cache_block),
+ nffs_cache_block_mem, "nffs_cache_block_pool");
+ if (rc != 0) {
+ return FS_EOS;
+ }
+
+ rc = os_mempool_init(&nffs_dir_pool,
+ nffs_config.nc_num_dirs,
+ sizeof (struct nffs_dir),
+ nffs_dir_mem, "nffs_dir_pool");
+ if (rc != 0) {
+ return FS_EOS;
+ }
+
+ rc = nffs_hash_init();
+ if (rc != 0) {
+ return rc;
+ }
+
+ free(nffs_areas);
+ nffs_areas = NULL;
+ nffs_num_areas = 0;
+
+ nffs_root_dir = NULL;
+ nffs_lost_found_dir = NULL;
+ nffs_scratch_area_idx = NFFS_AREA_ID_NONE;
+
+ nffs_hash_next_file_id = NFFS_ID_FILE_MIN;
+ nffs_hash_next_dir_id = NFFS_ID_DIR_MIN;
+ nffs_hash_next_block_id = NFFS_ID_BLOCK_MIN;
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_path.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_path.c b/fs/nffs/src/nffs_path.c
new file mode 100644
index 0000000..1dc9289
--- /dev/null
+++ b/fs/nffs/src/nffs_path.c
@@ -0,0 +1,337 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "nffs/nffs.h"
+#include "nffs_priv.h"
+
+int
+nffs_path_parse_next(struct nffs_path_parser *parser)
+{
+ const char *slash_start;
+ int token_end;
+ int token_len;
+
+ if (parser->npp_token_type == NFFS_PATH_TOKEN_LEAF) {
+ return FS_EINVAL;
+ }
+
+ slash_start = strchr(parser->npp_path + parser->npp_off, '/');
+ if (slash_start == NULL) {
+ if (parser->npp_token_type == NFFS_PATH_TOKEN_NONE) {
+ return FS_EINVAL;
+ }
+ parser->npp_token_type = NFFS_PATH_TOKEN_LEAF;
+ token_len = strlen(parser->npp_path + parser->npp_off);
+ } else {
+ parser->npp_token_type = NFFS_PATH_TOKEN_BRANCH;
+ token_end = slash_start - parser->npp_path;
+ token_len = token_end - parser->npp_off;
+ }
+
+ if (token_len > NFFS_FILENAME_MAX_LEN) {
+ return FS_EINVAL;
+ }
+
+ parser->npp_token = parser->npp_path + parser->npp_off;
+ parser->npp_token_len = token_len;
+ parser->npp_off += token_len + 1;
+
+ return 0;
+}
+
+void
+nffs_path_parser_new(struct nffs_path_parser *parser, const char *path)
+{
+ parser->npp_token_type = NFFS_PATH_TOKEN_NONE;
+ parser->npp_path = path;
+ parser->npp_off = 0;
+}
+
+static int
+nffs_path_find_child(struct nffs_inode_entry *parent,
+ const char *name, int name_len,
+ struct nffs_inode_entry **out_inode_entry)
+{
+ struct nffs_inode_entry *cur;
+ struct nffs_inode inode;
+ int cmp;
+ int rc;
+
+ SLIST_FOREACH(cur, &parent->nie_child_list, nie_sibling_next) {
+ rc = nffs_inode_from_entry(&inode, cur);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_filename_cmp_ram(&inode, name, name_len, &cmp);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (cmp == 0) {
+ *out_inode_entry = cur;
+ return 0;
+ }
+ if (cmp > 0) {
+ break;
+ }
+ }
+
+ return FS_ENOENT;
+}
+
+int
+nffs_path_find(struct nffs_path_parser *parser,
+ struct nffs_inode_entry **out_inode_entry,
+ struct nffs_inode_entry **out_parent)
+{
+ struct nffs_inode_entry *parent;
+ struct nffs_inode_entry *inode_entry;
+ int rc;
+
+ *out_inode_entry = NULL;
+ if (out_parent != NULL) {
+ *out_parent = NULL;
+ }
+
+ inode_entry = NULL;
+ while (1) {
+ parent = inode_entry;
+ rc = nffs_path_parse_next(parser);
+ if (rc != 0) {
+ return rc;
+ }
+
+ switch (parser->npp_token_type) {
+ case NFFS_PATH_TOKEN_BRANCH:
+ if (parent == NULL) {
+ /* First directory must be root. */
+ if (parser->npp_token_len != 0) {
+ return FS_ENOENT;
+ }
+
+ inode_entry = nffs_root_dir;
+ } else {
+ /* Ignore empty intermediate directory names. */
+ if (parser->npp_token_len == 0) {
+ break;
+ }
+
+ rc = nffs_path_find_child(parent, parser->npp_token,
+ parser->npp_token_len, &inode_entry);
+ if (rc != 0) {
+ goto done;
+ }
+ }
+ break;
+ case NFFS_PATH_TOKEN_LEAF:
+ if (parent == NULL) {
+ /* First token must be root directory. */
+ return FS_ENOENT;
+ }
+
+ if (parser->npp_token_len == 0) {
+ /* If the path ends with a slash, the leaf is the parent, not
+ * the trailing empty token.
+ */
+ inode_entry = parent;
+ rc = 0;
+ } else {
+ rc = nffs_path_find_child(parent, parser->npp_token,
+ parser->npp_token_len, &inode_entry);
+ }
+ goto done;
+ }
+ }
+
+done:
+ *out_inode_entry = inode_entry;
+ if (out_parent != NULL) {
+ *out_parent = parent;
+ }
+ return rc;
+}
+
+int
+nffs_path_find_inode_entry(const char *filename,
+ struct nffs_inode_entry **out_inode_entry)
+{
+ struct nffs_path_parser parser;
+ int rc;
+
+ nffs_path_parser_new(&parser, filename);
+ rc = nffs_path_find(&parser, out_inode_entry, NULL);
+
+ return rc;
+}
+
+/**
+ * Unlinks the file or directory at the specified path. If the path refers to
+ * a directory, all the directory's descendants are recursively unlinked. Any
+ * open file handles refering to an unlinked file remain valid, and can be
+ * read from and written to.
+ *
+ * @path The path of the file or directory to unlink.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+nffs_path_unlink(const char *path)
+{
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_inode inode;
+ int rc;
+
+ rc = nffs_path_find_inode_entry(path, &inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_from_entry(&inode, inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_unlink(&inode);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Performs a rename and / or move of the specified source path to the
+ * specified destination. The source path can refer to either a file or a
+ * directory. All intermediate directories in the destination path must
+ * already have been created. If the source path refers to a file, the
+ * destination path must contain a full filename path (i.e., if performing a
+ * move, the destination path should end with the same filename in the source
+ * path). If an object already exists at the specified destination path, this
+ * function causes it to be unlinked prior to the rename (i.e., the destination
+ * gets clobbered).
+ *
+ * @param from The source path.
+ * @param to The destination path.
+ *
+ * @return 0 on success;
+ * nonzero on failure.
+ */
+int
+nffs_path_rename(const char *from, const char *to)
+{
+ struct nffs_path_parser parser;
+ struct nffs_inode_entry *from_inode_entry;
+ struct nffs_inode_entry *to_inode_entry;
+ struct nffs_inode_entry *from_parent;
+ struct nffs_inode_entry *to_parent;
+ struct nffs_inode inode;
+ int rc;
+
+ nffs_path_parser_new(&parser, from);
+ rc = nffs_path_find(&parser, &from_inode_entry, &from_parent);
+ if (rc != 0) {
+ return rc;
+ }
+
+ nffs_path_parser_new(&parser, to);
+ rc = nffs_path_find(&parser, &to_inode_entry, &to_parent);
+ switch (rc) {
+ case 0:
+ /* The user is clobbering something with the rename. */
+ if (nffs_hash_id_is_dir(from_inode_entry->nie_hash_entry.nhe_id) ^
+ nffs_hash_id_is_dir(to_inode_entry->nie_hash_entry.nhe_id)) {
+
+ /* Cannot clobber one type of file with another. */
+ return FS_EINVAL;
+ }
+
+ rc = nffs_inode_from_entry(&inode, to_inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = nffs_inode_unlink(&inode);
+ if (rc != 0) {
+ return rc;
+ }
+ break;
+
+ case FS_ENOENT:
+ assert(to_parent != NULL);
+ if (parser.npp_token_type != NFFS_PATH_TOKEN_LEAF) {
+ /* Intermediate directory doesn't exist. */
+ return FS_EINVAL;
+ }
+ break;
+
+ default:
+ return rc;
+ }
+
+ rc = nffs_inode_rename(from_inode_entry, to_parent, parser.npp_token);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Creates a new directory at the specified path.
+ *
+ * @param path The path of the directory to create.
+ *
+ * @return 0 on success;
+ * FS_EEXIST if there is another file or
+ * directory at the specified path.
+ * FS_ENONT if a required intermediate directory
+ * does not exist.
+ */
+int
+nffs_path_new_dir(const char *path, struct nffs_inode_entry **out_inode_entry)
+{
+ struct nffs_path_parser parser;
+ struct nffs_inode_entry *inode_entry;
+ struct nffs_inode_entry *parent;
+ int rc;
+
+ nffs_path_parser_new(&parser, path);
+ rc = nffs_path_find(&parser, &inode_entry, &parent);
+ if (rc == 0) {
+ return FS_EEXIST;
+ }
+ if (rc != FS_ENOENT) {
+ return rc;
+ }
+ if (parser.npp_token_type != NFFS_PATH_TOKEN_LEAF || parent == NULL) {
+ return FS_ENOENT;
+ }
+
+ rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len, 1,
+ &inode_entry);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (out_inode_entry != NULL) {
+ *out_inode_entry = inode_entry;
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/fs/nffs/src/nffs_priv.h
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_priv.h b/fs/nffs/src/nffs_priv.h
new file mode 100644
index 0000000..d8d1dd0
--- /dev/null
+++ b/fs/nffs/src/nffs_priv.h
@@ -0,0 +1,427 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * 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 H_NFFS_PRIV_
+#define H_NFFS_PRIV_
+
+#include <inttypes.h>
+#include "os/queue.h"
+#include "os/os_mempool.h"
+#include "nffs/nffs.h"
+#include "fs/fs.h"
+
+#define NFFS_HASH_SIZE 256
+
+#define NFFS_ID_DIR_MIN 0
+#define NFFS_ID_DIR_MAX 0x10000000
+#define NFFS_ID_FILE_MIN 0x10000000
+#define NFFS_ID_FILE_MAX 0x80000000
+#define NFFS_ID_BLOCK_MIN 0x80000000
+#define NFFS_ID_BLOCK_MAX 0xffffffff
+
+#define NFFS_ID_ROOT_DIR 0
+#define NFFS_ID_NONE 0xffffffff
+
+#define NFFS_AREA_MAGIC0 0xb98a31e2
+#define NFFS_AREA_MAGIC1 0x7fb0428c
+#define NFFS_AREA_MAGIC2 0xace08253
+#define NFFS_AREA_MAGIC3 0xb185fc8e
+#define NFFS_BLOCK_MAGIC 0x53ba23b9
+#define NFFS_INODE_MAGIC 0x925f8bc0
+
+#define NFFS_AREA_ID_NONE 0xff
+#define NFFS_AREA_VER 0
+#define NFFS_AREA_OFFSET_ID 23
+
+#define NFFS_SHORT_FILENAME_LEN 3
+
+#define NFFS_BLOCK_MAX_DATA_SZ_MAX 2048
+
+/** On-disk representation of an area header. */
+struct nffs_disk_area {
+ uint32_t nda_magic[4]; /* NFFS_AREA_MAGIC{0,1,2,3} */
+ uint32_t nda_length; /* Total size of area, in bytes. */
+ uint8_t nda_ver; /* Current nffs version: 0 */
+ uint8_t nda_gc_seq; /* Garbage collection count. */
+ uint8_t reserved8;
+ uint8_t nda_id; /* 0xff if scratch area. */
+};
+
+/** On-disk representation of an inode (file or directory). */
+struct nffs_disk_inode {
+ uint32_t ndi_magic; /* NFFS_INODE_MAGIC */
+ uint32_t ndi_id; /* Unique object ID. */
+ uint32_t ndi_seq; /* Sequence number; greater supersedes
+ lesser. */
+ uint32_t ndi_parent_id; /* Object ID of parent directory inode. */
+ uint8_t reserved8;
+ uint8_t ndi_filename_len; /* Length of filename, in bytes. */
+ uint16_t ndi_crc16; /* Covers rest of header and filename. */
+ /* Followed by filename. */
+};
+
+#define NFFS_DISK_INODE_OFFSET_CRC 18
+
+/** On-disk representation of a data block. */
+struct nffs_disk_block {
+ uint32_t ndb_magic; /* NFFS_BLOCK_MAGIC */
+ uint32_t ndb_id; /* Unique object ID. */
+ uint32_t ndb_seq; /* Sequence number; greater supersedes lesser. */
+ uint32_t ndb_inode_id; /* Object ID of owning inode. */
+ uint32_t ndb_prev_id; /* Object ID of previous block in file;
+ NFFS_ID_NONE if this is the first block. */
+ uint16_t ndb_data_len; /* Length of data contents, in bytes. */
+ uint16_t ndb_crc16; /* Covers rest of header and data. */
+ /* Followed by 'ndb_data_len' bytes of data. */
+};
+
+#define NFFS_DISK_BLOCK_OFFSET_CRC 20
+
+/**
+ * What gets stored in the hash table. Each entry represents a data block or
+ * an inode.
+ */
+struct nffs_hash_entry {
+ SLIST_ENTRY(nffs_hash_entry) nhe_next;
+ uint32_t nhe_id; /* 0 - 0x7fffffff if inode; else if block. */
+ uint32_t nhe_flash_loc; /* Upper-byte = area idx; rest = area offset. */
+};
+
+
+SLIST_HEAD(nffs_hash_list, nffs_hash_entry);
+SLIST_HEAD(nffs_inode_list, nffs_inode_entry);
+
+/** Each inode hash entry is actually one of these. */
+struct nffs_inode_entry {
+ struct nffs_hash_entry nie_hash_entry;
+ SLIST_ENTRY(nffs_inode_entry) nie_sibling_next;
+ union {
+ struct nffs_inode_list nie_child_list; /* If directory */
+ struct nffs_hash_entry *nie_last_block_entry; /* If file */
+ };
+ uint8_t nie_refcnt;
+};
+
+/** Full inode representation; not stored permanently RAM. */
+struct nffs_inode {
+ struct nffs_inode_entry *ni_inode_entry; /* Points to real inode entry. */
+ uint32_t ni_seq; /* Sequence number; greater
+ supersedes lesser. */
+ struct nffs_inode_entry *ni_parent; /* Points to parent directory. */
+ uint8_t ni_filename_len; /* # chars in filename. */
+ uint8_t ni_filename[NFFS_SHORT_FILENAME_LEN]; /* First 3 bytes. */
+};
+
+/** Full data block representation; not stored permanently RAM. */
+struct nffs_block {
+ struct nffs_hash_entry *nb_hash_entry; /* Points to real block entry. */
+ uint32_t nb_seq; /* Sequence number; greater
+ supersedes lesser. */
+ struct nffs_inode_entry *nb_inode_entry; /* Owning inode. */
+ struct nffs_hash_entry *nb_prev; /* Previous block in file. */
+ uint16_t nb_data_len; /* # of data bytes in block. */
+ uint16_t reserved16;
+};
+
+struct nffs_file {
+ struct nffs_inode_entry *nf_inode_entry;
+ uint32_t nf_offset;
+ uint8_t nf_access_flags;
+};
+
+struct nffs_area {
+ uint32_t na_offset;
+ uint32_t na_length;
+ uint32_t na_cur;
+ uint16_t na_id;
+ uint8_t na_gc_seq;
+ uint8_t na_flash_id;
+};
+
+struct nffs_disk_object {
+ int ndo_type;
+ uint8_t ndo_area_idx;
+ uint32_t ndo_offset;
+ union {
+ struct nffs_disk_inode ndo_disk_inode;
+ struct nffs_disk_block ndo_disk_block;
+ };
+};
+
+struct nffs_seek_info {
+ struct nffs_block nsi_last_block;
+ uint32_t nsi_block_file_off;
+ uint32_t nsi_file_len;
+};
+
+#define NFFS_OBJECT_TYPE_INODE 1
+#define NFFS_OBJECT_TYPE_BLOCK 2
+
+#define NFFS_PATH_TOKEN_NONE 0
+#define NFFS_PATH_TOKEN_BRANCH 1
+#define NFFS_PATH_TOKEN_LEAF 2
+
+struct nffs_path_parser {
+ int npp_token_type;
+ const char *npp_path;
+ const char *npp_token;
+ int npp_token_len;
+ int npp_off;
+};
+
+/** Represents a single cached data block. */
+struct nffs_cache_block {
+ TAILQ_ENTRY(nffs_cache_block) ncb_link; /* Next / prev cached block. */
+ struct nffs_block ncb_block; /* Full data block. */
+ uint32_t ncb_file_offset; /* File offset of this block. */
+};
+
+TAILQ_HEAD(nffs_cache_block_list, nffs_cache_block);
+
+/** Represents a single cached file inode. */
+struct nffs_cache_inode {
+ TAILQ_ENTRY(nffs_cache_inode) nci_link; /* Sorted; LRU at tail. */
+ struct nffs_inode nci_inode; /* Full inode. */
+ struct nffs_cache_block_list nci_block_list; /* List of cached blocks. */
+ uint32_t nci_file_size; /* Total file size. */
+};
+
+struct nffs_dirent {
+ struct nffs_inode_entry *nde_inode_entry;
+};
+
+struct nffs_dir {
+ struct nffs_inode_entry *nd_parent_inode_entry;
+ struct nffs_dirent nd_dirent;
+};
+
+extern void *nffs_file_mem;
+extern void *nffs_block_entry_mem;
+extern void *nffs_inode_mem;
+extern void *nffs_cache_inode_mem;
+extern void *nffs_cache_block_mem;
+extern void *nffs_dir_mem;
+extern struct os_mempool nffs_file_pool;
+extern struct os_mempool nffs_dir_pool;
+extern struct os_mempool nffs_inode_entry_pool;
+extern struct os_mempool nffs_block_entry_pool;
+extern struct os_mempool nffs_cache_inode_pool;
+extern struct os_mempool nffs_cache_block_pool;
+extern uint32_t nffs_hash_next_file_id;
+extern uint32_t nffs_hash_next_dir_id;
+extern uint32_t nffs_hash_next_block_id;
+extern struct nffs_area *nffs_areas;
+extern uint8_t nffs_num_areas;
+extern uint8_t nffs_scratch_area_idx;
+extern uint16_t nffs_block_max_data_sz;
+
+#define NFFS_FLASH_BUF_SZ 256
+extern uint8_t nffs_flash_buf[NFFS_FLASH_BUF_SZ];
+
+extern struct nffs_hash_list *nffs_hash;
+extern struct nffs_inode_entry *nffs_root_dir;
+extern struct nffs_inode_entry *nffs_lost_found_dir;
+
+/* @area */
+int nffs_area_magic_is_set(const struct nffs_disk_area *disk_area);
+int nffs_area_is_scratch(const struct nffs_disk_area *disk_area);
+void nffs_area_to_disk(const struct nffs_area *area,
+ struct nffs_disk_area *out_disk_area);
+uint32_t nffs_area_free_space(const struct nffs_area *area);
+int nffs_area_find_corrupt_scratch(uint16_t *out_good_idx,
+ uint16_t *out_bad_idx);
+
+/* @block */
+struct nffs_hash_entry *nffs_block_entry_alloc(void);
+void nffs_block_entry_free(struct nffs_hash_entry *entry);
+int nffs_block_read_disk(uint8_t area_idx, uint32_t area_offset,
+ struct nffs_disk_block *out_disk_block);
+int nffs_block_write_disk(const struct nffs_disk_block *disk_block,
+ const void *data,
+ uint8_t *out_area_idx, uint32_t *out_area_offset);
+int nffs_block_delete_from_ram(struct nffs_hash_entry *entry);
+void nffs_block_delete_list_from_ram(struct nffs_block *first,
+ struct nffs_block *last);
+void nffs_block_delete_list_from_disk(const struct nffs_block *first,
+ const struct nffs_block *last);
+void nffs_block_to_disk(const struct nffs_block *block,
+ struct nffs_disk_block *out_disk_block);
+int nffs_block_from_hash_entry_no_ptrs(struct nffs_block *out_block,
+ struct nffs_hash_entry *entry);
+int nffs_block_from_hash_entry(struct nffs_block *out_block,
+ struct nffs_hash_entry *entry);
+int nffs_block_read_data(const struct nffs_block *block, uint16_t offset,
+ uint16_t length, void *dst);
+
+/* @cache */
+void nffs_cache_inode_delete(const struct nffs_inode_entry *inode_entry);
+int nffs_cache_inode_ensure(struct nffs_cache_inode **out_entry,
+ struct nffs_inode_entry *inode_entry);
+void nffs_cache_inode_range(const struct nffs_cache_inode *cache_inode,
+ uint32_t *out_start, uint32_t *out_end);
+int nffs_cache_seek(struct nffs_cache_inode *cache_inode, uint32_t to,
+ struct nffs_cache_block **out_cache_block);
+void nffs_cache_clear(void);
+
+/* @crc */
+int nffs_crc_flash(uint16_t initial_crc, uint8_t area_idx,
+ uint32_t area_offset, uint32_t len, uint16_t *out_crc);
+uint16_t nffs_crc_disk_block_hdr(const struct nffs_disk_block *disk_block);
+int nffs_crc_disk_block_validate(const struct nffs_disk_block *disk_block,
+ uint8_t area_idx, uint32_t area_offset);
+void nffs_crc_disk_block_fill(struct nffs_disk_block *disk_block,
+ const void *data);
+int nffs_crc_disk_inode_validate(const struct nffs_disk_inode *disk_inode,
+ uint8_t area_idx, uint32_t area_offset);
+void nffs_crc_disk_inode_fill(struct nffs_disk_inode *disk_inode,
+ const char *filename);
+
+/* @config */
+void nffs_config_init(void);
+
+/* @dir */
+int nffs_dir_open(const char *path, struct nffs_dir **out_dir);
+int nffs_dir_read(struct nffs_dir *dir, struct nffs_dirent **out_dirent);
+int nffs_dir_close(struct nffs_dir *dir);
+
+/* @file */
+int nffs_file_open(struct nffs_file **out_file, const char *filename,
+ uint8_t access_flags);
+int nffs_file_seek(struct nffs_file *file, uint32_t offset);
+int nffs_file_read(struct nffs_file *file, uint32_t len, void *out_data,
+ uint32_t *out_len);
+int nffs_file_close(struct nffs_file *file);
+int nffs_file_new(struct nffs_inode_entry *parent, const char *filename,
+ uint8_t filename_len, int is_dir,
+ struct nffs_inode_entry **out_inode_entry);
+
+/* @format */
+int nffs_format_area(uint8_t area_idx, int is_scratch);
+int nffs_format_from_scratch_area(uint8_t area_idx, uint8_t area_id);
+int nffs_format_full(const struct nffs_area_desc *area_descs);
+
+/* @gc */
+int nffs_gc(uint8_t *out_area_idx);
+int nffs_gc_until(uint32_t space, uint8_t *out_area_idx);
+
+/* @flash */
+struct nffs_area *nffs_flash_find_area(uint16_t logical_id);
+int nffs_flash_read(uint8_t area_idx, uint32_t offset,
+ void *data, uint32_t len);
+int nffs_flash_write(uint8_t area_idx, uint32_t offset,
+ const void *data, uint32_t len);
+int nffs_flash_copy(uint8_t area_id_from, uint32_t offset_from,
+ uint8_t area_id_to, uint32_t offset_to,
+ uint32_t len);
+uint32_t nffs_flash_loc(uint8_t area_idx, uint32_t offset);
+void nffs_flash_loc_expand(uint32_t flash_loc, uint8_t *out_area_idx,
+ uint32_t *out_area_offset);
+
+/* @hash */
+int nffs_hash_id_is_dir(uint32_t id);
+int nffs_hash_id_is_file(uint32_t id);
+int nffs_hash_id_is_inode(uint32_t id);
+int nffs_hash_id_is_block(uint32_t id);
+struct nffs_hash_entry *nffs_hash_find(uint32_t id);
+struct nffs_inode_entry *nffs_hash_find_inode(uint32_t id);
+struct nffs_hash_entry *nffs_hash_find_block(uint32_t id);
+void nffs_hash_insert(struct nffs_hash_entry *entry);
+void nffs_hash_remove(struct nffs_hash_entry *entry);
+int nffs_hash_init(void);
+
+/* @inode */
+struct nffs_inode_entry *nffs_inode_entry_alloc(void);
+void nffs_inode_entry_free(struct nffs_inode_entry *inode_entry);
+int nffs_inode_calc_data_length(struct nffs_inode_entry *inode_entry,
+ uint32_t *out_len);
+int nffs_inode_data_len(struct nffs_inode_entry *inode_entry,
+ uint32_t *out_len);
+uint32_t nffs_inode_parent_id(const struct nffs_inode *inode);
+int nffs_inode_delete_from_disk(struct nffs_inode *inode);
+int nffs_inode_entry_from_disk(struct nffs_inode_entry *out_inode,
+ const struct nffs_disk_inode *disk_inode,
+ uint8_t area_idx, uint32_t offset);
+int nffs_inode_rename(struct nffs_inode_entry *inode_entry,
+ struct nffs_inode_entry *new_parent,
+ const char *new_filename);
+void nffs_inode_insert_block(struct nffs_inode *inode,
+ struct nffs_block *block);
+int nffs_inode_read_disk(uint8_t area_idx, uint32_t offset,
+ struct nffs_disk_inode *out_disk_inode);
+int nffs_inode_write_disk(const struct nffs_disk_inode *disk_inode,
+ const char *filename, uint8_t area_idx,
+ uint32_t offset);
+int nffs_inode_dec_refcnt(struct nffs_inode_entry *inode_entry);
+int nffs_inode_add_child(struct nffs_inode_entry *parent,
+ struct nffs_inode_entry *child);
+void nffs_inode_remove_child(struct nffs_inode *child);
+int nffs_inode_is_root(const struct nffs_disk_inode *disk_inode);
+int nffs_inode_read_filename(struct nffs_inode_entry *inode_entry,
+ size_t max_len, char *out_name,
+ uint8_t *out_full_len);
+int nffs_inode_filename_cmp_ram(const struct nffs_inode *inode,
+ const char *name, int name_len,
+ int *result);
+int nffs_inode_filename_cmp_flash(const struct nffs_inode *inode1,
+ const struct nffs_inode *inode2,
+ int *result);
+int nffs_inode_read(struct nffs_inode_entry *inode_entry, uint32_t offset,
+ uint32_t len, void *data, uint32_t *out_len);
+int nffs_inode_seek(struct nffs_inode_entry *inode_entry, uint32_t offset,
+ uint32_t length, struct nffs_seek_info *out_seek_info);
+int nffs_inode_from_entry(struct nffs_inode *out_inode,
+ struct nffs_inode_entry *entry);
+int nffs_inode_unlink_from_ram(struct nffs_inode *inode,
+ struct nffs_hash_entry **out_next);
+int nffs_inode_unlink(struct nffs_inode *inode);
+
+/* @misc */
+int nffs_misc_reserve_space(uint16_t space,
+ uint8_t *out_area_idx, uint32_t *out_area_offset);
+int nffs_misc_set_num_areas(uint8_t num_areas);
+int nffs_misc_validate_root_dir(void);
+int nffs_misc_validate_scratch(void);
+int nffs_misc_create_lost_found_dir(void);
+int nffs_misc_set_max_block_data_len(uint16_t min_data_len);
+int nffs_misc_reset(void);
+
+/* @path */
+int nffs_path_parse_next(struct nffs_path_parser *parser);
+void nffs_path_parser_new(struct nffs_path_parser *parser, const char *path);
+int nffs_path_find(struct nffs_path_parser *parser,
+ struct nffs_inode_entry **out_inode_entry,
+ struct nffs_inode_entry **out_parent);
+int nffs_path_find_inode_entry(const char *filename,
+ struct nffs_inode_entry **out_inode_entry);
+int nffs_path_unlink(const char *filename);
+int nffs_path_rename(const char *from, const char *to);
+int nffs_path_new_dir(const char *path,
+ struct nffs_inode_entry **out_inode_entry);
+
+/* @restore */
+int nffs_restore_full(const struct nffs_area_desc *area_descs);
+
+/* @write */
+int nffs_write_to_file(struct nffs_file *file, const void *data, int len);
+
+
+#define NFFS_HASH_FOREACH(entry, i) \
+ for ((i) = 0; (i) < NFFS_HASH_SIZE; (i)++) \
+ SLIST_FOREACH((entry), &nffs_hash[i], nhe_next)
+
+#define NFFS_FLASH_LOC_NONE nffs_flash_loc(NFFS_AREA_ID_NONE, 0)
+
+#endif
[02/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/test/arch/sim/nffs_test_system_01.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/test/arch/sim/nffs_test_system_01.c b/libs/nffs/src/test/arch/sim/nffs_test_system_01.c
deleted file mode 100644
index 4d9cc03..0000000
--- a/libs/nffs/src/test/arch/sim/nffs_test_system_01.c
+++ /dev/null
@@ -1,6534 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-
-/** Generated by makefs.rb */
-
-#include <stddef.h>
-#include "nffs_test_priv.h"
-
-const struct nffs_test_file_desc *nffs_test_system_01 =
- (struct nffs_test_file_desc[]) {
-{
-.filename = "",
-.is_dir = 1,
-.children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl1dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl2dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl1dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl2dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl1dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl2dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl1dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl2dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl1dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl2dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
-{ .filename = NULL, } }
-},
-};
-
-const struct nffs_test_file_desc *nffs_test_system_01_rm_1014_mk10 =
- (struct nffs_test_file_desc[]) {
-{
-.filename = "",
-.is_dir = 1,
-.children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl1dir-0000",
- .is_dir = 1,
- },
- {
- .filename = "lvl1dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl2dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0001",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0003",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl3dir-0004",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
- },
- { .filename = NULL, } }
- },
- { .filename = NULL, } }
- },
- {
- .filename = "lvl2dir-0002",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl3dir-0000",
- .is_dir = 1,
- .children = (struct nffs_test_file_desc[]) {
- {
- .filename = "lvl4file-0000",
- .contents = "0",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0001",
- .contents = "1",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0002",
- .contents = "2",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0003",
- .contents = "3",
- .contents_len = 1,
- },
- {
- .filename = "lvl4file-0004",
- .contents = "4",
- .contents_len = 1,
<TRUNCATED>
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/todo.txt
----------------------------------------------------------------------
diff --git a/libs/nffs/todo.txt b/libs/nffs/todo.txt
deleted file mode 100644
index 629741b..0000000
--- a/libs/nffs/todo.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Long term:
-* ECC
-* Compression
-* Encryption
[14/14] incubator-mynewt-larva git commit: Merge branch 'master' of
https://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva
Posted by ma...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/commit/61b2b011
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/61b2b011
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/61b2b011
Branch: refs/heads/master
Commit: 61b2b011eef83bbc0bb9710f7486c3354ca259d6
Parents: cf40853 8f2fb6c
Author: Marko Kiiskila <ma...@runtime.io>
Authored: Fri Jan 15 09:55:07 2016 -0800
Committer: Marko Kiiskila <ma...@runtime.io>
Committed: Fri Jan 15 09:55:07 2016 -0800
----------------------------------------------------------------------
net/nimble/host/include/host/ble_gap.h | 75 ++--
net/nimble/host/src/ble_gap_conn.c | 394 +++++++++++------
net/nimble/host/src/ble_gap_conn.h | 34 --
net/nimble/host/src/ble_gap_priv.h | 35 ++
net/nimble/host/src/ble_hci_ack.c | 2 +-
net/nimble/host/src/ble_hs.c | 2 +-
net/nimble/host/src/ble_hs_adv.h | 1 +
net/nimble/host/src/ble_hs_conn.h | 5 +
net/nimble/host/src/host_hci.c | 2 +-
net/nimble/host/src/test/ble_att_clt_test.c | 3 +-
net/nimble/host/src/test/ble_att_svr_test.c | 3 +-
net/nimble/host/src/test/ble_gap_test.c | 425 +++++++++++++++++--
net/nimble/host/src/test/ble_gatt_conn_test.c | 11 +-
net/nimble/host/src/test/ble_gatt_disc_c_test.c | 6 +-
net/nimble/host/src/test/ble_gatt_disc_d_test.c | 3 +-
net/nimble/host/src/test/ble_gatt_disc_s_test.c | 6 +-
net/nimble/host/src/test/ble_gatt_find_s_test.c | 12 +-
net/nimble/host/src/test/ble_gatt_read_test.c | 33 +-
net/nimble/host/src/test/ble_gatt_write_test.c | 12 +-
.../host/src/test/ble_gatts_notify_test.c | 3 +-
net/nimble/host/src/test/ble_hs_adv_test.c | 5 +-
net/nimble/host/src/test/ble_hs_conn_test.c | 22 +-
net/nimble/host/src/test/ble_hs_test_util.c | 7 +-
net/nimble/host/src/test/ble_hs_test_util.h | 5 +-
net/nimble/host/src/test/ble_l2cap_test.c | 3 +-
net/nimble/host/src/test/ble_os_test.c | 52 +--
project/centtest/src/main.c | 45 +-
project/prphtest/src/main.c | 21 +-
28 files changed, 857 insertions(+), 370 deletions(-)
----------------------------------------------------------------------
[04/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs
libs/nffs -> fs/nffs.
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_restore.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_restore.c b/libs/nffs/src/nffs_restore.c
deleted file mode 100644
index b94b5fb..0000000
--- a/libs/nffs/src/nffs_restore.c
+++ /dev/null
@@ -1,1034 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include "hal/hal_flash.h"
-#include "os/os_mempool.h"
-#include "os/os_malloc.h"
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-
-/**
- * The size of the largest data block encountered during detection. This is
- * used to ensure that the maximum block data size is not set lower than the
- * size of an existing block.
- */
-static uint16_t nffs_restore_largest_block_data_len;
-
-/**
- * Checks that each block a chain of data blocks was properly restored.
- *
- * @param last_block_entry The entry corresponding to the last block in
- * the chain.
- *
- * @return 0 if the block chain is OK;
- * FS_ECORRUPT if corruption is detected;
- * nonzero on other error.
- */
-static int
-nffs_restore_validate_block_chain(struct nffs_hash_entry *last_block_entry)
-{
- struct nffs_disk_block disk_block;
- struct nffs_hash_entry *cur;
- struct nffs_block block;
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- cur = last_block_entry;
-
- while (cur != NULL) {
- nffs_flash_loc_expand(cur->nhe_flash_loc, &area_idx, &area_offset);
-
- rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_block_from_hash_entry(&block, cur);
- if (rc != 0) {
- return rc;
- }
-
- cur = block.nb_prev;
- }
-
- return 0;
-}
-
-static void
-u32toa(char *dst, uint32_t val)
-{
- uint8_t tmp;
- int idx = 0;
- int i;
- int print = 0;
-
- for (i = 0; i < 8; i++) {
- tmp = val >> 28;
- if (tmp || i == 7) {
- print = 1;
- }
- if (tmp < 10) {
- tmp += '0';
- } else {
- tmp += 'a' - 10;
- }
- if (print) {
- dst[idx++] = tmp;
- }
- val <<= 4;
- }
- dst[idx++] = '\0';
-}
-
-/**
- * If the specified inode entry is a dummy directory, this function moves
- * all its children to the lost+found directory.
- *
- * @param inode_entry The parent inode to test and empty.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_migrate_orphan_children(struct nffs_inode_entry *inode_entry)
-{
- struct nffs_inode_entry *lost_found_sub;
- struct nffs_inode_entry *child_entry;
- char buf[32];
- int rc;
-
- if (!nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)) {
- /* Not a directory. */
- return 0;
- }
-
- if (inode_entry->nie_refcnt != 0) {
- /* Not a dummy. */
- return 0;
- }
-
- if (SLIST_EMPTY(&inode_entry->nie_child_list)) {
- /* No children to migrate. */
- return 0;
- }
-
- /* Create a directory in lost+found to hold the dummy directory's
- * contents.
- */
- strcpy(buf, "/lost+found/");
- u32toa(&buf[strlen(buf)], inode_entry->nie_hash_entry.nhe_id);
-
- rc = nffs_path_new_dir(buf, &lost_found_sub);
- if (rc != 0 && rc != FS_EEXIST) {
- return rc;
- }
-
- /* Move each child into the new subdirectory. */
- while ((child_entry = SLIST_FIRST(&inode_entry->nie_child_list)) != NULL) {
- rc = nffs_inode_rename(child_entry, lost_found_sub, NULL);
- if (rc != 0) {
- return rc;
- }
- }
-
- return 0;
-}
-
-static int
-nffs_restore_should_sweep_inode_entry(struct nffs_inode_entry *inode_entry,
- int *out_should_sweep)
-{
- struct nffs_inode inode;
- int rc;
-
- /* Determine if the inode is a dummy. Dummy inodes have a reference count
- * of 0. If it is a dummy, increment its reference count back to 1 so that
- * it can be properly deleted. The presence of a dummy inode during the
- * final sweep step indicates file system corruption. If the inode is a
- * directory, all its children should have been migrated to the /lost+found
- * directory prior to this.
- */
- if (inode_entry->nie_refcnt == 0) {
- *out_should_sweep = 1;
- inode_entry->nie_refcnt++;
- return 0;
- }
-
- /* Determine if the inode has been deleted. If an inode has no parent (and
- * it isn't the root directory), it has been deleted from the disk and
- * should be swept from the RAM representation.
- */
- if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
- rc = nffs_inode_from_entry(&inode, inode_entry);
- if (rc != 0) {
- *out_should_sweep = 0;
- return rc;
- }
-
- if (inode.ni_parent == NULL) {
- *out_should_sweep = 1;
- return 0;
- }
- }
-
- /* If this is a file inode, verify that all of its constituent blocks are
- * present.
- */
- if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
- rc = nffs_restore_validate_block_chain(
- inode_entry->nie_last_block_entry);
- if (rc == FS_ECORRUPT) {
- *out_should_sweep = 1;
- return 0;
- } else if (rc != 0) {
- *out_should_sweep = 0;
- return rc;
- }
- }
-
- /* This is a valid inode; don't sweep it. */
- *out_should_sweep = 0;
- return 0;
-}
-
-static void
-nffs_restore_inode_from_dummy_entry(struct nffs_inode *out_inode,
- struct nffs_inode_entry *inode_entry)
-{
- memset(out_inode, 0, sizeof *out_inode);
- out_inode->ni_inode_entry = inode_entry;
-}
-
-/**
- * Performs a sweep of the RAM representation at the end of a successful
- * restore. The sweep phase performs the following actions of each inode in
- * the file system:
- * 1. If the inode is a dummy directory, its children are migrated to the
- * lost+found directory.
- * 2. Else if the inode is a dummy file, it is fully deleted from RAM.
- * 3. Else, a CRC check is performed on each of the inode's constituent
- * blocks. If corruption is detected, the inode is fully deleted from
- * RAM.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_restore_sweep(void)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_hash_entry *entry;
- struct nffs_hash_entry *next;
- struct nffs_hash_list *list;
- struct nffs_inode inode;
- int del;
- int rc;
- int i;
-
- /* Iterate through every object in the hash table, deleting all inodes that
- * should be removed.
- */
- for (i = 0; i < NFFS_HASH_SIZE; i++) {
- list = nffs_hash + i;
-
- entry = SLIST_FIRST(list);
- while (entry != NULL) {
- next = SLIST_NEXT(entry, nhe_next);
- if (nffs_hash_id_is_inode(entry->nhe_id)) {
- inode_entry = (struct nffs_inode_entry *)entry;
-
- /* If this is a dummy inode directory, the file system is
- * corrupted. Move the directory's children inodes to the
- * lost+found directory.
- */
- rc = nffs_restore_migrate_orphan_children(inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- /* Determine if this inode needs to be deleted. */
- rc = nffs_restore_should_sweep_inode_entry(inode_entry, &del);
- if (rc != 0) {
- return rc;
- }
-
- if (del) {
- if (inode_entry->nie_hash_entry.nhe_flash_loc ==
- NFFS_FLASH_LOC_NONE) {
-
- nffs_restore_inode_from_dummy_entry(&inode,
- inode_entry);
- } else {
- rc = nffs_inode_from_entry(&inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
- }
-
- /* Remove the inode and all its children from RAM. */
- rc = nffs_inode_unlink_from_ram(&inode, &next);
- if (rc != 0) {
- return rc;
- }
- next = SLIST_FIRST(list);
- }
- }
-
- entry = next;
- }
- }
-
- return 0;
-}
-
-/**
- * Creates a dummy inode and inserts it into the hash table. A dummy inode is
- * a temporary placeholder for a real inode that has not been restored yet.
- * These are necessary so that the inter-object links can be maintained until
- * the absent inode is eventually restored. Dummy inodes are identified by a
- * reference count of 0.
- *
- * @param id The ID of the dummy inode to create.
- * @param out_inode_entry On success, the dummy inode gets written here.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_dummy_inode(uint32_t id,
- struct nffs_inode_entry **out_inode_entry)
-{
- struct nffs_inode_entry *inode_entry;
-
- inode_entry = nffs_inode_entry_alloc();
- if (inode_entry == NULL) {
- return FS_ENOMEM;
- }
- inode_entry->nie_hash_entry.nhe_id = id;
- inode_entry->nie_hash_entry.nhe_flash_loc = NFFS_FLASH_LOC_NONE;
- inode_entry->nie_refcnt = 0;
-
- nffs_hash_insert(&inode_entry->nie_hash_entry);
-
- *out_inode_entry = inode_entry;
-
- return 0;
-}
-
-/**
- * Determines if an already-restored inode should be replaced by another inode
- * just read from flash. This function should only be called if both inodes
- * share the same ID. The existing inode gets replaced if:
- * o It is a dummy inode.
- * o Its sequence number is less than that of the new inode.
- *
- * @param old_inode_entry The already-restored inode to test.
- * @param disk_inode The inode just read from flash.
- * @param out_should_replace On success, 0=don't replace; 1=do replace.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_inode_gets_replaced(struct nffs_inode_entry *old_inode_entry,
- const struct nffs_disk_inode *disk_inode,
- int *out_should_replace)
-{
- struct nffs_inode old_inode;
- int rc;
-
- assert(old_inode_entry->nie_hash_entry.nhe_id == disk_inode->ndi_id);
-
- if (old_inode_entry->nie_refcnt == 0) {
- *out_should_replace = 1;
- return 0;
- }
-
- rc = nffs_inode_from_entry(&old_inode, old_inode_entry);
- if (rc != 0) {
- *out_should_replace = 0;
- return rc;
- }
-
- if (old_inode.ni_seq < disk_inode->ndi_seq) {
- *out_should_replace = 1;
- return 0;
- }
-
- if (old_inode.ni_seq == disk_inode->ndi_seq) {
- /* This is a duplicate of a previously-read inode. This should never
- * happen.
- */
- *out_should_replace = 0;
- return FS_ECORRUPT;
- }
-
- *out_should_replace = 0;
- return 0;
-}
-
-/**
- * Determines if the specified inode should be added to the RAM representation
- * and adds it if appropriate.
- *
- * @param disk_inode The inode just read from flash.
- * @param area_idx The index of the area containing the inode.
- * @param area_offset The offset within the area of the inode.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_inode(const struct nffs_disk_inode *disk_inode, uint8_t area_idx,
- uint32_t area_offset)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_inode_entry *parent;
- struct nffs_inode inode;
- int new_inode;
- int do_add;
- int rc;
-
- new_inode = 0;
-
- /* Check the inode's CRC. If the inode is corrupt, discard it. */
- rc = nffs_crc_disk_inode_validate(disk_inode, area_idx, area_offset);
- if (rc != 0) {
- goto err;
- }
-
- inode_entry = nffs_hash_find_inode(disk_inode->ndi_id);
- if (inode_entry != NULL) {
- rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode,
- &do_add);
- if (rc != 0) {
- goto err;
- }
-
- if (do_add) {
- if (inode_entry->nie_hash_entry.nhe_flash_loc !=
- NFFS_FLASH_LOC_NONE) {
-
- rc = nffs_inode_from_entry(&inode, inode_entry);
- if (rc != 0) {
- return rc;
- }
- if (inode.ni_parent != NULL) {
- nffs_inode_remove_child(&inode);
- }
- }
-
- inode_entry->nie_hash_entry.nhe_flash_loc =
- nffs_flash_loc(area_idx, area_offset);
- }
- } else {
- inode_entry = nffs_inode_entry_alloc();
- if (inode_entry == NULL) {
- rc = FS_ENOMEM;
- goto err;
- }
- new_inode = 1;
- do_add = 1;
-
- inode_entry->nie_hash_entry.nhe_id = disk_inode->ndi_id;
- inode_entry->nie_hash_entry.nhe_flash_loc =
- nffs_flash_loc(area_idx, area_offset);
-
- nffs_hash_insert(&inode_entry->nie_hash_entry);
- }
-
- if (do_add) {
- inode_entry->nie_refcnt = 1;
-
- if (disk_inode->ndi_parent_id != NFFS_ID_NONE) {
- parent = nffs_hash_find_inode(disk_inode->ndi_parent_id);
- if (parent == NULL) {
- rc = nffs_restore_dummy_inode(disk_inode->ndi_parent_id,
- &parent);
- if (rc != 0) {
- goto err;
- }
- }
-
- rc = nffs_inode_add_child(parent, inode_entry);
- if (rc != 0) {
- goto err;
- }
- }
-
-
- if (inode_entry->nie_hash_entry.nhe_id == NFFS_ID_ROOT_DIR) {
- nffs_root_dir = inode_entry;
- }
- }
-
- if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
- if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_file_id) {
- nffs_hash_next_file_id = inode_entry->nie_hash_entry.nhe_id + 1;
- }
- } else {
- if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_dir_id) {
- nffs_hash_next_dir_id = inode_entry->nie_hash_entry.nhe_id + 1;
- }
- }
-
- return 0;
-
-err:
- if (new_inode) {
- nffs_inode_entry_free(inode_entry);
- }
- return rc;
-}
-
-/**
- * Indicates whether the specified data block is superseded by the just-read
- * disk data block. A data block supersedes another if its ID is equal and its
- * sequence number is greater than that of the other block.
- *
- * @param out_should_replace On success, 0 or 1 gets written here, to
- * indicate whether replacement should occur.
- * @param old_block The data block which has already been read and
- * converted to its RAM representation. This
- * is the block that may be superseded.
- * @param disk_block The disk data block that was just read from
- * flash. This is the block which may
- * supersede the other.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_block_gets_replaced(const struct nffs_block *old_block,
- const struct nffs_disk_block *disk_block,
- int *out_should_replace)
-{
- assert(old_block->nb_hash_entry->nhe_id == disk_block->ndb_id);
-
- if (old_block->nb_seq < disk_block->ndb_seq) {
- *out_should_replace = 1;
- return 0;
- }
-
- if (old_block->nb_seq == disk_block->ndb_seq) {
- /* This is a duplicate of an previously-read inode. This should never
- * happen.
- */
- return FS_ECORRUPT;
- }
-
- *out_should_replace = 0;
- return 0;
-}
-
-/**
- * Populates the nffs RAM state with the memory representation of the specified
- * disk data block.
- *
- * @param disk_block The source disk block to insert.
- * @param area_idx The ID of the area containing the block.
- * @param area_offset The area_offset within the area of the block.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_block(const struct nffs_disk_block *disk_block, uint8_t area_idx,
- uint32_t area_offset)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_hash_entry *entry;
- struct nffs_block block;
- int do_replace;
- int new_block;
- int rc;
-
- new_block = 0;
-
- /* Check the block's CRC. If the block is corrupt, discard it. If this
- * block would have superseded another, the old block becomes current.
- */
- rc = nffs_crc_disk_block_validate(disk_block, area_idx, area_offset);
- if (rc != 0) {
- goto err;
- }
-
- entry = nffs_hash_find_block(disk_block->ndb_id);
- if (entry != NULL) {
- rc = nffs_block_from_hash_entry_no_ptrs(&block, entry);
- if (rc != 0) {
- goto err;
- }
-
- rc = nffs_restore_block_gets_replaced(&block, disk_block, &do_replace);
- if (rc != 0) {
- goto err;
- }
-
- if (!do_replace) {
- /* The new block is superseded by the old; nothing to do. */
- return 0;
- }
-
- nffs_block_delete_from_ram(entry);
- }
-
- entry = nffs_block_entry_alloc();
- if (entry == NULL) {
- rc = FS_ENOMEM;
- goto err;
- }
- new_block = 1;
- entry->nhe_id = disk_block->ndb_id;
- entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
-
- /* The block is ready to be inserted into the hash. */
-
- inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);
- if (inode_entry == NULL) {
- rc = nffs_restore_dummy_inode(disk_block->ndb_inode_id, &inode_entry);
- if (rc != 0) {
- goto err;
- }
- }
-
- if (inode_entry->nie_last_block_entry == NULL ||
- inode_entry->nie_last_block_entry->nhe_id == disk_block->ndb_prev_id) {
-
- inode_entry->nie_last_block_entry = entry;
- }
-
- nffs_hash_insert(entry);
-
- if (disk_block->ndb_id >= nffs_hash_next_block_id) {
- nffs_hash_next_block_id = disk_block->ndb_id + 1;
- }
-
- /* Make sure the maximum block data size is not set lower than the size of
- * an existing block.
- */
- if (disk_block->ndb_data_len > nffs_restore_largest_block_data_len) {
- nffs_restore_largest_block_data_len = disk_block->ndb_data_len;
- }
-
- return 0;
-
-err:
- if (new_block) {
- nffs_block_entry_free(entry);
- }
- return rc;
-}
-
-/**
- * Populates the nffs RAM state with the memory representation of the specified
- * disk object.
- *
- * @param disk_object The source disk object to convert.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_object(const struct nffs_disk_object *disk_object)
-{
- int rc;
-
- switch (disk_object->ndo_type) {
- case NFFS_OBJECT_TYPE_INODE:
- rc = nffs_restore_inode(&disk_object->ndo_disk_inode,
- disk_object->ndo_area_idx,
- disk_object->ndo_offset);
- break;
-
- case NFFS_OBJECT_TYPE_BLOCK:
- rc = nffs_restore_block(&disk_object->ndo_disk_block,
- disk_object->ndo_area_idx,
- disk_object->ndo_offset);
- break;
-
- default:
- assert(0);
- rc = FS_EINVAL;
- break;
- }
-
- return rc;
-}
-
-/**
- * Reads a single disk object from flash.
- *
- * @param area_idx The area to read the object from.
- * @param area_offset The offset within the area to read from.
- * @param out_disk_object On success, the restored object gets written
- * here.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_disk_object(int area_idx, uint32_t area_offset,
- struct nffs_disk_object *out_disk_object)
-{
- uint32_t magic;
- int rc;
-
- rc = nffs_flash_read(area_idx, area_offset, &magic, sizeof magic);
- if (rc != 0) {
- return rc;
- }
-
- switch (magic) {
- case NFFS_INODE_MAGIC:
- out_disk_object->ndo_type = NFFS_OBJECT_TYPE_INODE;
- rc = nffs_inode_read_disk(area_idx, area_offset,
- &out_disk_object->ndo_disk_inode);
- break;
-
- case NFFS_BLOCK_MAGIC:
- out_disk_object->ndo_type = NFFS_OBJECT_TYPE_BLOCK;
- rc = nffs_block_read_disk(area_idx, area_offset,
- &out_disk_object->ndo_disk_block);
- break;
-
- case 0xffffffff:
- rc = FS_EEMPTY;
- break;
-
- default:
- rc = FS_ECORRUPT;
- break;
- }
-
- if (rc != 0) {
- return rc;
- }
-
- out_disk_object->ndo_area_idx = area_idx;
- out_disk_object->ndo_offset = area_offset;
-
- return 0;
-}
-
-/**
- * Calculates the disk space occupied by the specified disk object.
- *
- * @param disk_object
- */
-static int
-nffs_restore_disk_object_size(const struct nffs_disk_object *disk_object)
-{
- switch (disk_object->ndo_type) {
- case NFFS_OBJECT_TYPE_INODE:
- return sizeof disk_object->ndo_disk_inode +
- disk_object->ndo_disk_inode.ndi_filename_len;
-
- case NFFS_OBJECT_TYPE_BLOCK:
- return sizeof disk_object->ndo_disk_block +
- disk_object->ndo_disk_block.ndb_data_len;
-
- default:
- assert(0);
- return 1;
- }
-}
-
-/**
- * Reads the specified area from disk and loads its contents into the RAM
- * representation.
- *
- * @param area_idx The index of the area to read.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_area_contents(int area_idx)
-{
- struct nffs_disk_object disk_object;
- struct nffs_area *area;
- int rc;
-
- area = nffs_areas + area_idx;
-
- area->na_cur = sizeof (struct nffs_disk_area);
- while (1) {
- rc = nffs_restore_disk_object(area_idx, area->na_cur, &disk_object);
- switch (rc) {
- case 0:
- /* Valid object; restore it into the RAM representation. */
- nffs_restore_object(&disk_object);
- area->na_cur += nffs_restore_disk_object_size(&disk_object);
- break;
-
- case FS_ECORRUPT:
- /* Invalid object; keep scanning for a valid magic number. */
- area->na_cur++;
- break;
-
- case FS_EEMPTY:
- case FS_ERANGE:
- /* End of disk encountered; area fully restored. */
- return 0;
-
- default:
- return rc;
- }
- }
-}
-
-/**
- * Reads and parses one area header. This function does not read the area's
- * contents.
- *
- * @param out_is_scratch On success, 0 or 1 gets written here,
- * indicating whether the area is a scratch
- * area.
- * @param area_offset The flash offset of the start of the area.
- *
- * @return 0 on success;
- * nonzero on failure.
- */
-static int
-nffs_restore_detect_one_area(uint8_t flash_id, uint32_t area_offset,
- struct nffs_disk_area *out_disk_area)
-{
- int rc;
-
- rc = hal_flash_read(flash_id, area_offset, out_disk_area,
- sizeof *out_disk_area);
- if (rc != 0) {
- return FS_HW_ERROR;
- }
-
- if (!nffs_area_magic_is_set(out_disk_area)) {
- return FS_ECORRUPT;
- }
-
- return 0;
-}
-
-/**
- * Repairs the effects of a corrupt scratch area. Scratch area corruption can
- * occur when the system resets while a garbage collection cycle is in
- * progress.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_restore_corrupt_scratch(void)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_hash_entry *entry;
- struct nffs_hash_entry *next;
- uint32_t area_offset;
- uint16_t good_idx;
- uint16_t bad_idx;
- uint8_t area_idx;
- int rc;
- int i;
-
- /* Search for a pair of areas with identical IDs. If found, these areas
- * represent the source and destination areas of a garbage collection
- * cycle. The shorter of the two areas was the destination area. Since
- * the garbage collection cycle did not finish, the source area contains a
- * more complete set of objects than the destination area.
- *
- * good_idx = index of source area.
- * bad_idx = index of destination area; this will be turned into the
- * scratch area.
- */
- rc = nffs_area_find_corrupt_scratch(&good_idx, &bad_idx);
- if (rc != 0) {
- return rc;
- }
-
- /* Invalidate all objects resident in the bad area. */
- for (i = 0; i < NFFS_HASH_SIZE; i++) {
- entry = SLIST_FIRST(&nffs_hash[i]);
- while (entry != NULL) {
- next = SLIST_NEXT(entry, nhe_next);
-
- nffs_flash_loc_expand(entry->nhe_flash_loc,
- &area_idx, &area_offset);
- if (area_idx == bad_idx) {
- if (nffs_hash_id_is_block(entry->nhe_id)) {
- rc = nffs_block_delete_from_ram(entry);
- if (rc != 0) {
- return rc;
- }
- } else {
- inode_entry = (struct nffs_inode_entry *)entry;
- inode_entry->nie_refcnt = 0;
- }
- }
-
- entry = next;
- }
- }
-
- /* Now that the objects in the scratch area have been invalidated, reload
- * everything from the good area.
- */
- rc = nffs_restore_area_contents(good_idx);
- if (rc != 0) {
- return rc;
- }
-
- /* Convert the bad area into a scratch area. */
- rc = nffs_format_area(bad_idx, 1);
- if (rc != 0) {
- return rc;
- }
- nffs_scratch_area_idx = bad_idx;
-
- return 0;
-}
-
-/**
- * Searches for a valid nffs file system among the specified areas. This
- * function succeeds if a file system is detected among any subset of the
- * supplied areas. If the area set does not contain a valid file system,
- * a new one can be created via a call to nffs_format().
- *
- * @param area_descs The area set to search. This array must be
- * terminated with a 0-length area.
- *
- * @return 0 on success;
- * FS_ECORRUPT if no valid file system was detected;
- * other nonzero on error.
- */
-int
-nffs_restore_full(const struct nffs_area_desc *area_descs)
-{
- struct nffs_disk_area disk_area;
- int cur_area_idx;
- int use_area;
- int rc;
- int i;
-
- /* Start from a clean state. */
- rc = nffs_misc_reset();
- if (rc) {
- return rc;
- }
- nffs_restore_largest_block_data_len = 0;
-
- /* Read each area from flash. */
- for (i = 0; area_descs[i].nad_length != 0; i++) {
- if (i > NFFS_MAX_AREAS) {
- rc = FS_EINVAL;
- goto err;
- }
-
- rc = nffs_restore_detect_one_area(area_descs[i].nad_flash_id,
- area_descs[i].nad_offset,
- &disk_area);
- switch (rc) {
- case 0:
- use_area = 1;
- break;
-
- case FS_ECORRUPT:
- use_area = 0;
- break;
-
- default:
- goto err;
- }
-
- if (use_area) {
- if (disk_area.nda_id == NFFS_AREA_ID_NONE &&
- nffs_scratch_area_idx != NFFS_AREA_ID_NONE) {
-
- /* Don't allow more than one scratch area. */
- use_area = 0;
- }
- }
-
- if (use_area) {
- /* Populate RAM with a representation of this area. */
- cur_area_idx = nffs_num_areas;
-
- rc = nffs_misc_set_num_areas(nffs_num_areas + 1);
- if (rc != 0) {
- goto err;
- }
-
- nffs_areas[cur_area_idx].na_offset = area_descs[i].nad_offset;
- nffs_areas[cur_area_idx].na_length = area_descs[i].nad_length;
- nffs_areas[cur_area_idx].na_flash_id = area_descs[i].nad_flash_id;
- nffs_areas[cur_area_idx].na_gc_seq = disk_area.nda_gc_seq;
- nffs_areas[cur_area_idx].na_id = disk_area.nda_id;
-
- if (disk_area.nda_id == NFFS_AREA_ID_NONE) {
- nffs_areas[cur_area_idx].na_cur = NFFS_AREA_OFFSET_ID;
- nffs_scratch_area_idx = cur_area_idx;
- } else {
- nffs_areas[cur_area_idx].na_cur =
- sizeof (struct nffs_disk_area);
- nffs_restore_area_contents(cur_area_idx);
- }
- }
- }
-
- /* All areas have been restored from flash. */
-
- if (nffs_scratch_area_idx == NFFS_AREA_ID_NONE) {
- /* No scratch area. The system may have been rebooted in the middle of
- * a garbage collection cycle. Look for a candidate scratch area.
- */
- rc = nffs_restore_corrupt_scratch();
- if (rc != 0) {
- if (rc == FS_ENOENT) {
- rc = FS_ECORRUPT;
- }
- goto err;
- }
- }
-
- /* Ensure this file system contains a valid scratch area. */
- rc = nffs_misc_validate_scratch();
- if (rc != 0) {
- goto err;
- }
-
- /* Make sure the file system contains a valid root directory. */
- rc = nffs_misc_validate_root_dir();
- if (rc != 0) {
- goto err;
- }
-
- /* Ensure there is a "/lost+found" directory. */
- rc = nffs_misc_create_lost_found_dir();
- if (rc != 0) {
- goto err;
- }
-
- /* Delete from RAM any objects that were invalidated when subsequent areas
- * were restored.
- */
- nffs_restore_sweep();
-
- /* Set the maximum data block size according to the size of the smallest
- * area.
- */
- rc = nffs_misc_set_max_block_data_len(nffs_restore_largest_block_data_len);
- if (rc != 0) {
- goto err;
- }
-
- return 0;
-
-err:
- nffs_misc_reset();
- return rc;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/nffs_write.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/nffs_write.c b/libs/nffs/src/nffs_write.c
deleted file mode 100644
index 3d5325b..0000000
--- a/libs/nffs/src/nffs_write.c
+++ /dev/null
@@ -1,397 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-#include <assert.h>
-#include "testutil/testutil.h"
-#include "nffs/nffs.h"
-#include "nffs_priv.h"
-#include "crc16.h"
-
-static int
-nffs_write_fill_crc16_overwrite(struct nffs_disk_block *disk_block,
- uint8_t src_area_idx, uint32_t src_area_offset,
- uint16_t left_copy_len, uint16_t right_copy_len,
- const void *new_data, uint16_t new_data_len)
-{
- uint16_t block_off;
- uint16_t crc16;
- int rc;
-
- block_off = 0;
-
- crc16 = nffs_crc_disk_block_hdr(disk_block);
- block_off += sizeof *disk_block;
-
- /* Copy data from the start of the old block, in case the new data starts
- * at a non-zero offset.
- */
- if (left_copy_len > 0) {
- rc = nffs_crc_flash(crc16, src_area_idx, src_area_offset + block_off,
- left_copy_len, &crc16);
- if (rc != 0) {
- return rc;
- }
- block_off += left_copy_len;
- }
-
- /* Write the new data into the data block. This may extend the block's
- * length beyond its old value.
- */
- crc16 = crc16_ccitt(crc16, new_data, new_data_len);
- block_off += new_data_len;
-
- /* Copy data from the end of the old block, in case the new data doesn't
- * extend to the end of the block.
- */
- if (right_copy_len > 0) {
- rc = nffs_crc_flash(crc16, src_area_idx, src_area_offset + block_off,
- right_copy_len, &crc16);
- if (rc != 0) {
- return rc;
- }
- block_off += right_copy_len;
- }
-
- assert(block_off == sizeof *disk_block + disk_block->ndb_data_len);
-
- disk_block->ndb_crc16 = crc16;
-
- return 0;
-}
-
-/**
- * Overwrites an existing data block. The resulting block has the same ID as
- * the old one, but it supersedes it with a greater sequence number.
- *
- * @param entry The data block to overwrite.
- * @param left_copy_len The number of bytes of existing data to retain
- * before the new data begins.
- * @param new_data The new data to write to the block.
- * @param new_data_len The number of new bytes to write to the block.
- * If this value plus left_copy_len is less
- * than the existing block's data length,
- * previous data at the end of the block is
- * retained.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_write_over_block(struct nffs_hash_entry *entry, uint16_t left_copy_len,
- const void *new_data, uint16_t new_data_len)
-{
- struct nffs_disk_block disk_block;
- struct nffs_block block;
- uint32_t src_area_offset;
- uint32_t dst_area_offset;
- uint16_t right_copy_len;
- uint16_t block_off;
- uint8_t src_area_idx;
- uint8_t dst_area_idx;
- int rc;
-
- rc = nffs_block_from_hash_entry(&block, entry);
- if (rc != 0) {
- return rc;
- }
-
- assert(left_copy_len <= block.nb_data_len);
-
- /* Determine how much old data at the end of the block needs to be
- * retained. If the new data doesn't extend to the end of the block, the
- * the rest of the block retains its old contents.
- */
- if (left_copy_len + new_data_len > block.nb_data_len) {
- right_copy_len = 0;
- } else {
- right_copy_len = block.nb_data_len - left_copy_len - new_data_len;
- }
-
- block.nb_seq++;
- block.nb_data_len = left_copy_len + new_data_len + right_copy_len;
- nffs_block_to_disk(&block, &disk_block);
-
- nffs_flash_loc_expand(entry->nhe_flash_loc,
- &src_area_idx, &src_area_offset);
-
- rc = nffs_write_fill_crc16_overwrite(&disk_block,
- src_area_idx, src_area_offset,
- left_copy_len, right_copy_len,
- new_data, new_data_len);
- if (rc != 0) {
- return rc;
- }
-
- rc = nffs_misc_reserve_space(sizeof disk_block + disk_block.ndb_data_len,
- &dst_area_idx, &dst_area_offset);
- if (rc != 0) {
- return rc;
- }
-
- block_off = 0;
-
- /* Write the block header. */
- rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off,
- &disk_block, sizeof disk_block);
- if (rc != 0) {
- return rc;
- }
- block_off += sizeof disk_block;
-
- /* Copy data from the start of the old block, in case the new data starts
- * at a non-zero offset.
- */
- if (left_copy_len > 0) {
- rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off,
- dst_area_idx, dst_area_offset + block_off,
- left_copy_len);
- if (rc != 0) {
- return rc;
- }
- block_off += left_copy_len;
- }
-
- /* Write the new data into the data block. This may extend the block's
- * length beyond its old value.
- */
- rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off,
- new_data, new_data_len);
- if (rc != 0) {
- return rc;
- }
- block_off += new_data_len;
-
- /* Copy data from the end of the old block, in case the new data doesn't
- * extend to the end of the block.
- */
- if (right_copy_len > 0) {
- rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off,
- dst_area_idx, dst_area_offset + block_off,
- right_copy_len);
- if (rc != 0) {
- return rc;
- }
- block_off += right_copy_len;
- }
-
- assert(block_off == sizeof disk_block + block.nb_data_len);
-
- entry->nhe_flash_loc = nffs_flash_loc(dst_area_idx, dst_area_offset);
-
- ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, dst_area_idx,
- dst_area_offset) == 0);
-
- return 0;
-}
-
-/**
- * Appends a new block to an inode block chain.
- *
- * @param inode_entry The inode to append a block to.
- * @param data The contents of the new block.
- * @param len The number of bytes of data to write.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_write_append(struct nffs_cache_inode *cache_inode, const void *data,
- uint16_t len)
-{
- struct nffs_inode_entry *inode_entry;
- struct nffs_hash_entry *entry;
- struct nffs_disk_block disk_block;
- uint32_t area_offset;
- uint8_t area_idx;
- int rc;
-
- entry = nffs_block_entry_alloc();
- if (entry == NULL) {
- return FS_ENOMEM;
- }
-
- inode_entry = cache_inode->nci_inode.ni_inode_entry;
-
- disk_block.ndb_magic = NFFS_BLOCK_MAGIC;
- disk_block.ndb_id = nffs_hash_next_block_id++;
- disk_block.ndb_seq = 0;
- disk_block.ndb_inode_id = inode_entry->nie_hash_entry.nhe_id;
- if (inode_entry->nie_last_block_entry == NULL) {
- disk_block.ndb_prev_id = NFFS_ID_NONE;
- } else {
- disk_block.ndb_prev_id = inode_entry->nie_last_block_entry->nhe_id;
- }
- disk_block.ndb_data_len = len;
- nffs_crc_disk_block_fill(&disk_block, data);
-
- rc = nffs_block_write_disk(&disk_block, data, &area_idx, &area_offset);
- if (rc != 0) {
- return rc;
- }
-
- entry->nhe_id = disk_block.ndb_id;
- entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
- nffs_hash_insert(entry);
-
- inode_entry->nie_last_block_entry = entry;
-
- /* Update cached inode with the new file size. */
- cache_inode->nci_file_size += len;
-
- /* Add appended block to the cache. */
- nffs_cache_seek(cache_inode, cache_inode->nci_file_size - 1, NULL);
-
- return 0;
-}
-
-/**
- * Performs a single write operation. The data written must be no greater
- * than the maximum block data length. If old data gets overwritten, then
- * the existing data blocks are superseded as necessary.
- *
- * @param write_info Describes the write operation being perfomred.
- * @param inode_entry The file inode to write to.
- * @param data The new data to write.
- * @param data_len The number of bytes of new data to write.
- *
- * @return 0 on success; nonzero on failure.
- */
-static int
-nffs_write_chunk(struct nffs_cache_inode *cache_inode, uint32_t file_offset,
- const void *data, uint16_t data_len)
-{
- struct nffs_cache_block *cache_block;
- uint32_t append_len;
- uint32_t data_offset;
- uint32_t block_end;
- uint32_t dst_off;
- uint16_t chunk_off;
- uint16_t chunk_sz;
- int rc;
-
- assert(data_len <= nffs_block_max_data_sz);
-
- /** Handle the simple append case first. */
- if (file_offset == cache_inode->nci_file_size) {
- rc = nffs_write_append(cache_inode, data, data_len);
- return rc;
- }
-
- /** This is not an append; i.e., old data is getting overwritten. */
-
- dst_off = file_offset + data_len;
- data_offset = data_len;
- cache_block = NULL;
-
- if (dst_off > cache_inode->nci_file_size) {
- append_len = dst_off - cache_inode->nci_file_size;
- } else {
- append_len = 0;
- }
-
- do {
- if (cache_block == NULL) {
- rc = nffs_cache_seek(cache_inode, dst_off - 1, &cache_block);
- if (rc != 0) {
- return rc;
- }
- }
-
- if (cache_block->ncb_file_offset < file_offset) {
- chunk_off = file_offset - cache_block->ncb_file_offset;
- } else {
- chunk_off = 0;
- }
-
- chunk_sz = cache_block->ncb_block.nb_data_len - chunk_off;
- block_end = cache_block->ncb_file_offset +
- cache_block->ncb_block.nb_data_len;
- if (block_end != dst_off) {
- chunk_sz += (int)(dst_off - block_end);
- }
-
- data_offset = cache_block->ncb_file_offset + chunk_off - file_offset;
- rc = nffs_write_over_block(cache_block->ncb_block.nb_hash_entry,
- chunk_off, data + data_offset, chunk_sz);
- if (rc != 0) {
- return rc;
- }
-
- dst_off -= chunk_sz;
- cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list, ncb_link);
- } while (data_offset > 0);
-
- cache_inode->nci_file_size += append_len;
- return 0;
-}
-
-/**
- * Writes a chunk of contiguous data to a file.
- *
- * @param file The file to write to.
- * @param data The data to write.
- * @param len The length of data to write.
- *
- * @return 0 on success; nonzero on failure.
- */
-int
-nffs_write_to_file(struct nffs_file *file, const void *data, int len)
-{
- struct nffs_cache_inode *cache_inode;
- const uint8_t *data_ptr;
- uint16_t chunk_size;
- int rc;
-
- if (!(file->nf_access_flags & FS_ACCESS_WRITE)) {
- return FS_EACCESS;
- }
-
- if (len == 0) {
- return 0;
- }
-
- rc = nffs_cache_inode_ensure(&cache_inode, file->nf_inode_entry);
- if (rc != 0) {
- return rc;
- }
-
- /* The append flag forces all writes to the end of the file, regardless of
- * seek position.
- */
- if (file->nf_access_flags & FS_ACCESS_APPEND) {
- file->nf_offset = cache_inode->nci_file_size;
- }
-
- /* Write data as a sequence of blocks. */
- data_ptr = data;
- while (len > 0) {
- if (len > nffs_block_max_data_sz) {
- chunk_size = nffs_block_max_data_sz;
- } else {
- chunk_size = len;
- }
-
- rc = nffs_write_chunk(cache_inode, file->nf_offset, data_ptr,
- chunk_size);
- if (rc != 0) {
- return rc;
- }
-
- len -= chunk_size;
- data_ptr += chunk_size;
- file->nf_offset += chunk_size;
- }
-
- return 0;
-}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/cf40853d/libs/nffs/src/test/arch/cortex_m4/nffs_test.c
----------------------------------------------------------------------
diff --git a/libs/nffs/src/test/arch/cortex_m4/nffs_test.c b/libs/nffs/src/test/arch/cortex_m4/nffs_test.c
deleted file mode 100644
index f15447d..0000000
--- a/libs/nffs/src/test/arch/cortex_m4/nffs_test.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * 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.
- */
-
-
-#include "nffs/nffs_test.h"
-
-int
-nffs_test_all(void)
-{
- return 0;
-}