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:19 UTC

[12/14] incubator-mynewt-larva git commit: Relocate libs/fs -> fs/fs libs/nffs -> fs/nffs.

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