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