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;
-}