Implement file lastblock caching in flash

This change stores the last written block for each file in the metadata
stored in flash. It facilitates restoring and mounting the filesystem
during initialization by removing the requirement to search every block in
flash to find the last block of each file.

This change requires the previous commit changing the on-flash data structures
which include a reference to the lastblock. This change is not backwards
compatible with previous on-flash version of NFFS, and there is a check
to make sure that the current version is used. Users upgrading to this version
of NFFS will need to reformat their flash devices - currently there is no
facility for in-place upgrades of existing filesystems.

One side-effect of this change is that inode updates are written to flash
each time a new block is added to the file. Other changes include additional
state flags with checking to provide additional resilience with minimal
space and compute overhead. There is also additional debugging tools in
nffs_test.c.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/f9ef5686
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/f9ef5686
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/f9ef5686

Branch: refs/heads/master
Commit: f9ef5686269fb626a042bbd2195e4db7d500ddf5
Parents: 76e33d9
Author: Peter Snyder <g...@peterfs.com>
Authored: Tue Jun 28 16:15:03 2016 -0700
Committer: Peter Snyder <g...@peterfs.com>
Committed: Tue Jun 28 16:15:03 2016 -0700

----------------------------------------------------------------------
 apps/ffs2native/src/main.c            | 232 +++++++++--
 fs/nffs/src/nffs.c                    |   1 +
 fs/nffs/src/nffs_block.c              |  40 ++
 fs/nffs/src/nffs_dir.c                |   4 +-
 fs/nffs/src/nffs_file.c               |   7 +-
 fs/nffs/src/nffs_format.c             |   2 +
 fs/nffs/src/nffs_gc.c                 |   4 +-
 fs/nffs/src/nffs_hash.c               |  68 +++-
 fs/nffs/src/nffs_inode.c              | 216 +++++++++-
 fs/nffs/src/nffs_misc.c               |   5 +-
 fs/nffs/src/nffs_path.c               |  13 +
 fs/nffs/src/nffs_priv.h               |  70 ++--
 fs/nffs/src/nffs_restore.c            | 598 +++++++++++++++++++---------
 fs/nffs/src/nffs_write.c              |  12 +
 fs/nffs/src/test/arch/sim/nffs_test.c | 611 ++++++++++++++++++++++++++++-
 15 files changed, 1595 insertions(+), 288 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/apps/ffs2native/src/main.c
----------------------------------------------------------------------
diff --git a/apps/ffs2native/src/main.c b/apps/ffs2native/src/main.c
index 1796ca2..2b42159 100644
--- a/apps/ffs2native/src/main.c
+++ b/apps/ffs2native/src/main.c
@@ -17,11 +17,6 @@
  * under the License.
  */
 
-/**
- * XXX
- * This is a hack of a tool which prints the structure of an nffs file system.
- * It needs to be rewritten properly.
- */
 
 #include <assert.h>
 #include <stdio.h>
@@ -49,12 +44,43 @@ static struct log_handler nffs_log_console_handler;
 struct log nffs_log;
 static const char *copy_in_dir;
 static const char *progname;
+static int print_verbose;
 
 char *file_flash_area;
+size_t file_flash_size;
 int file_scratch_idx;
 
 #define MAX_AREAS      16
 static struct nffs_area_desc area_descs[MAX_AREAS];
+int nffs_version;
+
+/** On-disk representation of a version 0 inode (file or directory). */
+struct nffs_disk_inodeV0 {
+    uint32_t ndi_magic;         /* NFFS_INODE_MAGIC */
+    uint32_t ndi_id;            /* Unique object ID. */
+    uint32_t ndi_seq;           /* Sequence number; greater supersedes
+                                   lesser. */
+    uint32_t ndi_parent_id;     /* Object ID of parent directory inode. */
+    uint8_t reserved8;
+    uint8_t ndi_filename_len;   /* Length of filename, in bytes. */
+    uint16_t ndi_crc16;         /* Covers rest of header and filename. */
+    /* Followed by filename. */
+};
+#define NFFS_DISK_INODEV0_OFFSET_CRC  18
+
+/** On-disk representation of a version 0 data block. */
+struct nffs_disk_blockV0 {
+    uint32_t ndb_magic;     /* NFFS_BLOCK_MAGIC */
+    uint32_t ndb_id;        /* Unique object ID. */
+    uint32_t ndb_seq;       /* Sequence number; greater supersedes lesser. */
+    uint32_t ndb_inode_id;  /* Object ID of owning inode. */
+    uint32_t ndb_prev_id;   /* Object ID of previous block in file;
+                               NFFS_ID_NONE if this is the first block. */
+    uint16_t ndb_data_len;  /* Length of data contents, in bytes. */
+    uint16_t ndb_crc16;     /* Covers rest of header and data. */
+    /* Followed by 'ndb_data_len' bytes of data. */
+};
+#define NFFS_DISK_BLOCKV0_OFFSET_CRC  22
 
 static void usage(int rc);
 
@@ -125,6 +151,7 @@ process_inode_entry(struct nffs_inode_entry *inode_entry, 
int indent)
     }
 }
 
+#ifdef OBSOLETE /* XXX To be removed */
 static int
 print_nffs_inode(struct nffs_disk_inode *ndi, int idx, uint32_t off)
 {
@@ -186,7 +213,6 @@ print_nffs_darea(struct nffs_disk_area *darea)
                   darea->nda_gc_seq, darea->nda_id);
 }
 
-
 static void
 print_nffs_area(int idx)
 {
@@ -235,16 +261,8 @@ print_nffs_areas(void)
           area->na_offset, area->na_offset + area->na_length);
         print_nffs_area(i);
     }
-}
-
-static void
-printfs(void)
-{
-    printf("\n\nNFFS contents:\n");
-    process_inode_entry(nffs_root_dir, 0);
-    printf("\nNFFS areas:\n");
-    print_nffs_areas();
-}
+} 
+#endif /* OBSOLETE */
 
 static int
 copy_in_file(char *src, char *dst)
@@ -329,10 +347,11 @@ file_flash_read(uint32_t addr, void *dst, int byte_cnt)
        return 0;
 }
 
+#ifdef NOTYET
 static int
-print_flash_inode(struct nffs_area_desc *area, uint32_t off)
+print_nffsV0_flash_inode(struct nffs_disk_object *ndo, uint32_t off)
 {
-    struct nffs_disk_inode ndi;
+    struct nffs_disk_inode *ndi = &ndo->ndo_disk_inode;
     char filename[128];
     int len;
     int rc;
@@ -350,40 +369,104 @@ print_flash_inode(struct nffs_area_desc *area, uint32_t 
off)
                        (nffs_hash_id_is_dir(ndi.ndi_id) ? "Dir" : "???")),
                   ndi.ndi_id, ndi.ndi_seq, ndi.ndi_parent_id,
                   ndi.ndi_lastblock_id, filename);
+    return sizeof(struct nffs_disk_inode) + ndi->ndi_filename_len;
+}
+
+static int
+print_nffsV0_flash_block(struct nffs_disk_object *ndo, uint32_t off)
+{
+    struct nffs_disk_block *ndb = &ndo->ndo_disk_block;
+    int rc;
+
+    printf("  off %x len %d Block id %x seq %d prev %x own ino %x\n",
+                  off, ndb->ndb_data_len, ndb->ndb_id, ndb->ndb_seq,
+                  ndb->ndb_prev_id, ndb->ndb_inode_id);
+    return sizeof(struct nffs_disk_block) + ndb->ndb_data_len;
+}
+
+static int
+print_nffsV0_flash_object(struct nffs_area_desc *area, uint32_t off)
+{
+       struct nffs_disk_object ndo;
+    uint32_t magic;
+
+       file_flash_read(area->nad_offset + off, &ndo, sizeof(ndo));
+       if (nffs_hash_id_is_inode(ndo.ndo_disk_inode.ndi_id)) {
+        return print_nffsV0_flash_inode(&ndo, off);
+
+       } else if (nffs_hash_id_is_block(ndo.ndo_disk_block.ndb_id)) {
+        return print_nffsV0_flash_block(&ndo, off);
+
+       }
+       return sizeof(ndo);
+}
+#endif /* NOTYET */
+
+static int
+print_nffs_flash_inode(struct nffs_area_desc *area, uint32_t off)
+{
+    struct nffs_disk_inode ndi;
+    char filename[128];
+    int len;
+    int rc;
+
+    rc = file_flash_read(area->nad_offset + off, &ndi, sizeof(ndi));
+    assert(rc == 0);
+
+    memset(filename, 0, sizeof(filename));
+    len = min(sizeof(filename) - 1, ndi.ndi_filename_len);
+    rc = file_flash_read(area->nad_offset + off + sizeof(ndi), filename, len);
+
+    printf("  off %x %s id %x flen %d seq %d last %x prnt %x flgs %x %s\n",
+           off,
+           (nffs_hash_id_is_file(ndi.ndi_id) ? "File" :
+            (nffs_hash_id_is_dir(ndi.ndi_id) ? "Dir" : "???")),
+           ndi.ndi_id,
+           ndi.ndi_filename_len,
+           ndi.ndi_seq,
+           ndi.ndi_lastblock_id,
+           ndi.ndi_parent_id,
+           ndi.ndi_flags,
+           filename);
     return sizeof(ndi) + ndi.ndi_filename_len;
 }
 
 static int
-print_flash_block(struct nffs_area_desc *area, uint32_t off)
+print_nffs_flash_block(struct nffs_area_desc *area, uint32_t off)
 {
     struct nffs_disk_block ndb;
     int rc;
 
-       rc = file_flash_read(area->nad_offset + off, &ndb, sizeof(ndb));
+    rc = file_flash_read(area->nad_offset + off, &ndb, sizeof(ndb));
     assert(rc == 0);
 
-    printf("  off %x len %d Block id %x seq %d prev %x own ino %x\n",
-                  off, ndb.ndb_data_len, ndb.ndb_id, ndb.ndb_seq,
-                  ndb.ndb_prev_id, ndb.ndb_inode_id);
+    printf("  off %x Block id %x len %d seq %d prev %x own ino %x\n",
+           off,
+           ndb.ndb_id,
+           ndb.ndb_data_len,
+           ndb.ndb_seq,
+           ndb.ndb_prev_id,
+           ndb.ndb_inode_id);
     return sizeof(ndb) + ndb.ndb_data_len;
 }
 
 static int
-print_flash_object(struct nffs_area_desc *area, uint32_t off)
+print_nffs_flash_object(struct nffs_area_desc *area, uint32_t off)
 {
-    uint32_t magic;
+    struct nffs_disk_object ndo;
 
-       file_flash_read(area->nad_offset + off, &magic, sizeof(magic));
-    switch (magic) {
-    case NFFS_INODE_MAGIC:
-        return print_flash_inode(area, off);
+       file_flash_read(area->nad_offset + off, &ndo, sizeof(ndo));
 
-    case NFFS_BLOCK_MAGIC:
-        return print_flash_block(area, off);
-        break;
+    if (nffs_hash_id_is_inode(ndo.ndo_disk_inode.ndi_id)) {
+        return print_nffs_flash_inode(area, off);
 
-    case 0xffffffff:
-    default:
+    } else if (nffs_hash_id_is_block(ndo.ndo_disk_block.ndb_id)) {
+        return print_nffs_flash_block(area, off);
+
+    } else if (ndo.ndo_disk_block.ndb_id == 0xffffffff) {
+        return area->nad_length;
+
+    } else {
         return 1;
     }
 }
@@ -403,11 +486,75 @@ print_file_areas()
                           (i == file_scratch_idx ? "(scratch)" : ""));
                off = sizeof (struct nffs_disk_area);
                while (off < area->nad_length) {
-                       off += print_flash_object(area, off);
+#ifdef NOTYET
+                       if (nffs_version == 0) {
+                               off += print_nffsV0_flash_object(area, off);
+                       } else if (nffs_version == NFFS_AREA_VER) {
+                               off += print_nffs_flash_object(area, off);
+                       }
+#else
+                       off += print_nffs_flash_object(area, off);
+#endif /* NOTYET */
                }
     }
 }
 
+static void
+print_nffs_flash_areas(char *flash_area, size_t size)
+{
+       char *daptr;            /* Disk Area Pointer */
+       char *eoda;                     /* End Of Disk Area */
+       struct nffs_disk_area *nda;
+       int nad_cnt = 0;        /* Nffs Area Descriptor count */
+       int off;
+
+       daptr = flash_area;
+       eoda = flash_area + size;
+       while (daptr < eoda) {
+               if (nffs_area_magic_is_set((struct nffs_disk_area*)daptr)) {
+                       nda = (struct nffs_disk_area*)daptr;
+                       area_descs[nad_cnt].nad_offset = (daptr - flash_area);
+                       area_descs[nad_cnt].nad_length = nda->nda_length;
+                       area_descs[nad_cnt].nad_flash_id = nda->nda_id;
+                       nffs_version = nda->nda_ver;
+
+                       if (nda->nda_id == 0xff)
+                               file_scratch_idx = nad_cnt;
+
+                       printf("area %d: id %d %x-%x len %d flshid %x gcseq %d 
%s\n",
+                                  nad_cnt, nda->nda_id,
+                                  area_descs[nad_cnt].nad_offset,
+                                  area_descs[nad_cnt].nad_offset +
+                                      area_descs[nad_cnt].nad_length,
+                                  area_descs[nad_cnt].nad_length,
+                                  area_descs[nad_cnt].nad_flash_id,
+                                  nda->nda_gc_seq,
+                                  nffs_scratch_area_idx == nad_cnt ? 
"(scratch)" : "");
+
+            off = sizeof (struct nffs_disk_area);
+            while (off < area_descs[nad_cnt].nad_length) {
+                off += print_nffs_flash_object(&area_descs[nad_cnt], off);
+            }
+
+                       nad_cnt++;
+                       daptr = daptr + nda->nda_length;
+               } else {
+                       daptr++;
+               }
+       }
+       nffs_num_areas = nad_cnt;
+}
+
+static void
+printfs(void)
+{
+       printf("NFFS directory:\n");
+       process_inode_entry(nffs_root_dir, print_verbose);
+
+    printf("\nNFFS flash areas:\n");
+    print_nffs_flash_areas(file_flash_area, file_flash_size);
+}
+
 int
 file_area_init(char *flash_area, size_t size)
 {
@@ -424,6 +571,7 @@ file_area_init(char *flash_area, size_t size)
                        area_descs[nad_cnt].nad_offset = (daptr - flash_area);
                        area_descs[nad_cnt].nad_length = nda->nda_length;
                        area_descs[nad_cnt].nad_flash_id = nda->nda_id;
+                       nffs_version = nda->nda_ver;
                        if (nda->nda_id == 0xff)
                                file_scratch_idx = nad_cnt;
                        printf("area %d: off %d len %d flshid %x gc-seq %d id 
%x ver %d %s\n",
@@ -433,7 +581,6 @@ file_area_init(char *flash_area, size_t size)
                                   area_descs[nad_cnt].nad_flash_id,
                                   nda->nda_gc_seq, nda->nda_id, nda->nda_ver,
                                   nda->nda_id == 0xff ? "(scratch)" : "");
-
                        nad_cnt++;
                        daptr = daptr + nda->nda_length;
                } else {
@@ -447,9 +594,10 @@ file_area_init(char *flash_area, size_t size)
 static void
 usage(int rc)
 {
-    printf("%s [-c]|[-d dir][-s][-f flash_file]\n", progname);
+    printf("%s [-v][-c]|[-d dir][-s][-f flash_file]\n", progname);
     printf("  Tool for operating on simulator flash image file\n");
     printf("   -c: ...\n");
+    printf("   -v: verbose\n");
     printf("   -d: use dir as root for NFFS portion and create flash image\n");
     printf("   -f: flash_file is the name of the flash image file\n");
     printf("   -s: use flash area layout in flash image file\n");
@@ -469,7 +617,7 @@ main(int argc, char **argv)
 
     progname = argv[0];
 
-    while ((ch = getopt(argc, argv, "c:d:f:s")) != -1) {
+    while ((ch = getopt(argc, argv, "c:d:f:sv")) != -1) {
         switch (ch) {
         case 'c':
             fp = fopen(optarg, "rb");
@@ -486,6 +634,9 @@ main(int argc, char **argv)
         case 'f':
             native_flash_file = optarg;
             break;
+        case 'v':
+            print_verbose++;
+            break;
         case '?':
         default:
             usage(0);
@@ -512,14 +663,15 @@ main(int argc, char **argv)
                fd = open(native_flash_file, O_RDWR);
                if ((rc = fstat(fd, &st)))
                        perror("fstat failed");
+               file_flash_size = st.st_size;
                if ((file_flash_area = mmap(0, (size_t)8192, PROT_READ,
                                                           MAP_FILE|MAP_SHARED, 
fd, 0)) == MAP_FAILED) {
                        perror("%s mmap failed");
                }
 
-               rc = file_area_init(file_flash_area, st.st_size);
+               rc = file_area_init(file_flash_area, file_flash_size);
 
-               print_file_areas();
+               print_nffs_flash_areas(file_flash_area, file_flash_size);
 
                return 0;
        }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs.c b/fs/nffs/src/nffs.c
index c90c4ef..f3b0372 100644
--- a/fs/nffs/src/nffs.c
+++ b/fs/nffs/src/nffs.c
@@ -33,6 +33,7 @@ struct nffs_area *nffs_areas;
 uint8_t nffs_num_areas;
 uint8_t nffs_scratch_area_idx;
 uint16_t nffs_block_max_data_sz;
+struct nffs_area_desc *nffs_current_area_descs;
 
 struct os_mempool nffs_file_pool;
 struct os_mempool nffs_dir_pool;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_block.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_block.c b/fs/nffs/src/nffs_block.c
index 5d51e3b..953e4dd 100644
--- a/fs/nffs/src/nffs_block.c
+++ b/fs/nffs/src/nffs_block.c
@@ -240,6 +240,17 @@ nffs_block_delete_from_ram(struct nffs_hash_entry 
*block_entry)
     struct nffs_block block;
     int rc;
 
+    if (nffs_hash_entry_is_dummy(block_entry)) {
+        /*
+         * it's very limited to what we can do here as the block doesn't have
+         * any way to get to the inode via hash entry. Just delete the
+         * block and return FS_ECORRUPT
+         */
+        nffs_hash_remove(block_entry);
+        nffs_block_entry_free(block_entry);
+        return FS_ECORRUPT;
+    }
+
     rc = nffs_block_from_hash_entry(&block, block_entry);
     if (rc == 0 || rc == FS_ECORRUPT) {
         /* If file system corruption was detected, the resulting block is still
@@ -326,6 +337,15 @@ nffs_block_from_hash_entry_no_ptrs(struct nffs_block 
*out_block,
 
     assert(nffs_hash_id_is_block(block_entry->nhe_id));
 
+    if (nffs_hash_entry_is_dummy(block_entry)) {
+        /*
+         * We can't read this from disk so we'll be missing filling in anything
+         * not already in inode_entry (e.g., prev_id).
+         */
+        out_block->nb_hash_entry = block_entry;
+        return FS_ENOENT; /* let caller know it's a partial inode_entry */
+    }
+
     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) {
@@ -371,6 +391,20 @@ nffs_block_from_hash_entry(struct nffs_block *out_block,
 
     assert(nffs_hash_id_is_block(block_entry->nhe_id));
 
+    if (nffs_block_is_dummy(block_entry)) {
+        out_block->nb_hash_entry = block_entry;
+        out_block->nb_inode_entry = NULL;
+        out_block->nb_prev = NULL;
+        /*
+         * Dummy block added when inode was read in before real block
+         * (see nffs_restore_inode()). Return success (because there's 
+         * too many places that ned to check for this,
+         * but it's the responsibility fo the upstream code to check
+         * whether this is still a dummy entry.  XXX
+         */
+        return 0;
+        /*return FS_ENOENT;*/
+    }
     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) {
@@ -406,3 +440,9 @@ nffs_block_read_data(const struct nffs_block *block, 
uint16_t offset,
 
     return 0;
 }
+
+int
+nffs_block_is_dummy(struct nffs_hash_entry *entry)
+{
+    return (entry->nhe_flash_loc == NFFS_FLASH_LOC_NONE);
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_dir.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_dir.c b/fs/nffs/src/nffs_dir.c
index 58efa7c..09c3a0f 100644
--- a/fs/nffs/src/nffs_dir.c
+++ b/fs/nffs/src/nffs_dir.c
@@ -72,7 +72,7 @@ nffs_dir_open(const char *path, struct nffs_dir **out_dir)
     }
 
     dir->nd_parent_inode_entry = parent_inode_entry;
-    dir->nd_parent_inode_entry->nie_refcnt++;
+    nffs_inode_inc_refcnt(dir->nd_parent_inode_entry);
     memset(&dir->nd_dirent, 0, sizeof dir->nd_dirent);
 
     *out_dir = dir;
@@ -103,7 +103,7 @@ nffs_dir_read(struct nffs_dir *dir, struct nffs_dirent 
**out_dirent)
         return FS_ENOENT;
     }
 
-    child->nie_refcnt++;
+    nffs_inode_inc_refcnt(child);
     *out_dirent = &dir->nd_dirent;
 
     return 0;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_file.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_file.c b/fs/nffs/src/nffs_file.c
index 095bf21..708a493 100644
--- a/fs/nffs/src/nffs_file.c
+++ b/fs/nffs/src/nffs_file.c
@@ -93,13 +93,14 @@ nffs_file_new(struct nffs_inode_entry *parent, const char 
*filename,
         disk_inode.ndi_id = nffs_hash_next_file_id++;
     }
     disk_inode.ndi_seq = 0;
+    disk_inode.ndi_lastblock_id = NFFS_ID_NONE;
     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_lastblock_id = 0; /* will be NFFS_ID_NONE when 
implemented */
     disk_inode.ndi_filename_len = filename_len;
+    disk_inode.ndi_flags = 0;
     nffs_crc_disk_inode_fill(&disk_inode, filename);
 
     rc = nffs_inode_write_disk(&disk_inode, filename, area_idx, offset);
@@ -111,6 +112,7 @@ nffs_file_new(struct nffs_inode_entry *parent, const char 
*filename,
     inode_entry->nie_hash_entry.nhe_flash_loc =
         nffs_flash_loc(area_idx, offset);
     inode_entry->nie_refcnt = 1;
+    inode_entry->nie_last_block_entry = NULL;
 
     if (parent != NULL) {
         rc = nffs_inode_add_child(parent, inode_entry);
@@ -119,6 +121,7 @@ nffs_file_new(struct nffs_inode_entry *parent, const char 
*filename,
         }
     } else {
         assert(disk_inode.ndi_id == NFFS_ID_ROOT_DIR);
+        nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_INTREE);
     }
 
     nffs_hash_insert(&inode_entry->nie_hash_entry);
@@ -237,7 +240,7 @@ nffs_file_open(struct nffs_file **out_file, const char 
*path,
     } else {
         file->nf_offset = 0;
     }
-    file->nf_inode_entry->nie_refcnt++;
+    nffs_inode_inc_refcnt(file->nf_inode_entry);
     file->nf_access_flags = access_flags;
 
     *out_file = file;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_format.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_format.c b/fs/nffs/src/nffs_format.c
index abd237f..13d97cc 100644
--- a/fs/nffs/src/nffs_format.c
+++ b/fs/nffs/src/nffs_format.c
@@ -178,6 +178,8 @@ nffs_format_full(const struct nffs_area_desc *area_descs)
         goto err;
     }
 
+    nffs_current_area_descs = (struct nffs_area_desc*) area_descs;
+
     return 0;
 
 err:

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_gc.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_gc.c b/fs/nffs/src/nffs_gc.c
index 2f6aa20..ae92953 100644
--- a/fs/nffs/src/nffs_gc.c
+++ b/fs/nffs/src/nffs_gc.c
@@ -195,7 +195,7 @@ nffs_gc_block_chain_collate(struct nffs_hash_entry 
*last_entry,
     }
 
     memset(&last_block, 0, sizeof(last_block));
-    
+
     to_area = nffs_areas + to_area_idx;
 
     entry = last_entry;
@@ -226,7 +226,7 @@ nffs_gc_block_chain_collate(struct nffs_hash_entry 
*last_entry,
         }
         entry = block.nb_prev;
     }
-    
+
     /* we had better have found the last block */
     assert(last_block.nb_hash_entry);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_hash.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_hash.c b/fs/nffs/src/nffs_hash.c
index 6403bd1..805ed26 100644
--- a/fs/nffs/src/nffs_hash.c
+++ b/fs/nffs/src/nffs_hash.c
@@ -59,8 +59,8 @@ nffs_hash_fn(uint32_t id)
     return id % NFFS_HASH_SIZE;
 }
 
-struct nffs_hash_entry *
-nffs_hash_find(uint32_t id)
+static struct nffs_hash_entry *
+nffs_hash_find_reorder(uint32_t id)
 {
     struct nffs_hash_entry *entry;
     struct nffs_hash_entry *prev;
@@ -87,6 +87,25 @@ nffs_hash_find(uint32_t id)
     return NULL;
 }
 
+struct nffs_hash_entry *
+nffs_hash_find(uint32_t id)
+{
+    struct nffs_hash_entry *entry;
+    struct nffs_hash_list *list;
+    int idx;
+
+    idx = nffs_hash_fn(id);
+    list = nffs_hash + idx;
+
+    SLIST_FOREACH(entry, list, nhe_next) {
+        if (entry->nhe_id == id) {
+            return entry;
+        }
+    }
+
+    return NULL;
+}
+
 struct nffs_inode_entry *
 nffs_hash_find_inode(uint32_t id)
 {
@@ -94,7 +113,7 @@ nffs_hash_find_inode(uint32_t id)
 
     assert(nffs_hash_id_is_inode(id));
 
-    entry = nffs_hash_find(id);
+    entry = nffs_hash_find_reorder(id);
     return (struct nffs_inode_entry *)entry;
 }
 
@@ -105,32 +124,72 @@ nffs_hash_find_block(uint32_t id)
 
     assert(nffs_hash_id_is_block(id));
 
-    entry = nffs_hash_find(id);
+    entry = nffs_hash_find_reorder(id);
     return entry;
 }
 
+int
+nffs_hash_entry_is_dummy(struct nffs_hash_entry *he)
+{
+        return(he->nhe_flash_loc == NFFS_FLASH_LOC_NONE);
+}
+
+int
+nffs_hash_id_is_dummy(uint32_t id)
+{
+    struct nffs_hash_entry *he = nffs_hash_find(id);
+    if (he != NULL) {
+        return(he->nhe_flash_loc == NFFS_FLASH_LOC_NONE);
+    }
+    return 0;
+}
+
 void
 nffs_hash_insert(struct nffs_hash_entry *entry)
 {
     struct nffs_hash_list *list;
+    struct nffs_inode_entry *nie;
     int idx;
 
+    assert(nffs_hash_find(entry->nhe_id) == NULL);
     idx = nffs_hash_fn(entry->nhe_id);
     list = nffs_hash + idx;
 
     SLIST_INSERT_HEAD(list, entry, nhe_next);
+
+    if (nffs_hash_id_is_inode(entry->nhe_id)) {
+        nie = nffs_hash_find_inode(entry->nhe_id);
+        assert(nie);
+        nffs_inode_setflags(nie, NFFS_INODE_FLAG_INHASH);
+    } else {
+        assert(nffs_hash_find(entry->nhe_id));
+    }
 }
 
 void
 nffs_hash_remove(struct nffs_hash_entry *entry)
 {
     struct nffs_hash_list *list;
+    struct nffs_inode_entry *nie = NULL;
     int idx;
 
+    if (nffs_hash_id_is_inode(entry->nhe_id)) {
+        nie = nffs_hash_find_inode(entry->nhe_id);
+        assert(nie);
+        assert(nffs_inode_getflags(nie, NFFS_INODE_FLAG_INHASH));
+    } else {
+        assert(nffs_hash_find(entry->nhe_id));
+    }
+
     idx = nffs_hash_fn(entry->nhe_id);
     list = nffs_hash + idx;
 
     SLIST_REMOVE(list, entry, nffs_hash_entry, nhe_next);
+
+    if (nffs_hash_id_is_inode(entry->nhe_id) && nie) {
+        nffs_inode_unsetflags(nie, NFFS_INODE_FLAG_INHASH);
+    }
+    assert(nffs_hash_find(entry->nhe_id) == NULL);
 }
 
 int
@@ -151,4 +210,3 @@ nffs_hash_init(void)
 
     return 0;
 }
-

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_inode.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_inode.c b/fs/nffs/src/nffs_inode.c
index 4b5a5a5..d8b881c 100644
--- a/fs/nffs/src/nffs_inode.c
+++ b/fs/nffs/src/nffs_inode.c
@@ -99,8 +99,8 @@ nffs_inode_read_disk(uint8_t area_idx, uint32_t offset,
                          sizeof *out_disk_inode);
     if (rc != 0) {
         return rc;
-       }
-       if (!nffs_hash_id_is_inode(out_disk_inode->ndi_id)) {
+    }
+    if (!nffs_hash_id_is_inode(out_disk_inode->ndi_id)) {
         return FS_EUNEXP;
     }
 
@@ -177,6 +177,14 @@ nffs_inode_data_len(struct nffs_inode_entry *inode_entry, 
uint32_t *out_len)
     return 0;
 }
 
+static void
+nffs_inode_restore_from_dummy_entry(struct nffs_inode *out_inode,
+                                    struct nffs_inode_entry *inode_entry)
+{
+    memset(out_inode, 0, sizeof *out_inode);
+    out_inode->ni_inode_entry = inode_entry;
+}
+
 int
 nffs_inode_from_entry(struct nffs_inode *out_inode,
                       struct nffs_inode_entry *entry)
@@ -187,6 +195,11 @@ nffs_inode_from_entry(struct nffs_inode *out_inode,
     int cached_name_len;
     int rc;
 
+    if (nffs_inode_is_dummy(entry)) {
+        nffs_inode_restore_from_dummy_entry(out_inode, entry);
+        return FS_ENOENT;
+    }
+
     nffs_flash_loc_expand(entry->nie_hash_entry.nhe_flash_loc,
                           &area_idx, &area_offset);
 
@@ -197,6 +210,11 @@ nffs_inode_from_entry(struct nffs_inode *out_inode,
 
     out_inode->ni_inode_entry = entry;
     out_inode->ni_seq = disk_inode.ndi_seq;
+
+    /*
+     * Relink to parent if possible
+     * XXX does this belong here?
+     */
     if (disk_inode.ndi_parent_id == NFFS_ID_NONE) {
         out_inode->ni_parent = NULL;
     } else {
@@ -217,6 +235,11 @@ nffs_inode_from_entry(struct nffs_inode *out_inode,
         }
     }
 
+    if (disk_inode.ndi_flags & NFFS_INODE_FLAG_DELETED) {
+        nffs_inode_setflags(out_inode->ni_inode_entry, 
NFFS_INODE_FLAG_DELETED);
+        nffs_inode_setflags(entry, NFFS_INODE_FLAG_DELETED);
+    }
+
     return 0;
 }
 
@@ -246,7 +269,9 @@ nffs_inode_delete_blocks_from_ram(struct nffs_inode_entry 
*inode_entry)
 
     return 0;
 }
-            /* The block references something that does not exist in RAM.  This
+
+            /* Dead comment?? XXX
+             * The block references something that does not exist in RAM.  This
              * is likely because the pointed-to object was in an area that has
              * been garbage collected.  Terminate the delete procedure and
              * report success.
@@ -276,6 +301,11 @@ nffs_inode_delete_from_ram(struct nffs_inode_entry 
*inode_entry,
     int rc;
 
     if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
+        /*
+         * Record the intention to delete the file
+         */
+        nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED);
+
         rc = nffs_inode_delete_blocks_from_ram(inode_entry);
         if (rc == FS_ECORRUPT && ignore_corruption) {
             inode_entry->nie_last_block_entry = NULL;
@@ -285,6 +315,10 @@ nffs_inode_delete_from_ram(struct nffs_inode_entry 
*inode_entry,
     }
 
     nffs_cache_inode_delete(inode_entry);
+    /*
+     * XXX Not deleting empty inode delete records from hash could prevent
+     * a case where we could lose delete records in a gc operation
+     */
     nffs_hash_remove(&inode_entry->nie_hash_entry);
     nffs_inode_entry_free(inode_entry);
 
@@ -312,6 +346,7 @@ nffs_inode_dec_refcnt_priv(struct nffs_inode_entry 
*inode_entry,
 {
     int rc;
 
+    assert(inode_entry);
     assert(inode_entry->nie_refcnt > 0);
 
     inode_entry->nie_refcnt--;
@@ -329,6 +364,14 @@ nffs_inode_dec_refcnt_priv(struct nffs_inode_entry 
*inode_entry,
     return 0;
 }
 
+int
+nffs_inode_inc_refcnt(struct nffs_inode_entry *inode_entry)
+{
+    assert(inode_entry);
+    inode_entry->nie_refcnt++;
+    return 0;
+}
+
 /**
  * Decrements the reference count of the specified inode entry.
  *
@@ -394,6 +437,8 @@ nffs_inode_process_unlink_list(struct nffs_hash_entry 
**inout_next,
             child = child_next;
         }
 
+
+
         /* The directory is already removed from the hash table; just free its
          * memory.
          */
@@ -424,10 +469,33 @@ nffs_inode_delete_from_disk(struct nffs_inode *inode)
     disk_inode.ndi_id = inode->ni_inode_entry->nie_hash_entry.nhe_id;
     disk_inode.ndi_seq = inode->ni_seq;
     disk_inode.ndi_parent_id = NFFS_ID_NONE;
+    disk_inode.ndi_flags = NFFS_INODE_FLAG_DELETED;
+    if (inode->ni_inode_entry->nie_last_block_entry) {
+        disk_inode.ndi_lastblock_id =
+                          inode->ni_inode_entry->nie_last_block_entry->nhe_id;
+    } else {
+        disk_inode.ndi_lastblock_id = NFFS_ID_NONE;
+    }
     disk_inode.ndi_filename_len = 0;
     nffs_crc_disk_inode_fill(&disk_inode, "");
 
     rc = nffs_inode_write_disk(&disk_inode, "", area_idx, offset);
+    NFFS_LOG(DEBUG, "inode_del_disk: wrote unlinked ino %x to disk ref %d\n",
+               (unsigned int)disk_inode.ndi_id,
+               inode->ni_inode_entry->nie_refcnt);
+
+    /*
+     * Flag the incore inode as deleted to the inode won't get updated to
+     * disk. This could happen if the refcnt > 0 and there are future appends
+     * XXX only do this for files and not directories
+     */
+    if (nffs_hash_id_is_file(inode->ni_inode_entry->nie_hash_entry.nhe_id)) {
+        nffs_inode_setflags(inode->ni_inode_entry, NFFS_INODE_FLAG_DELETED);
+        NFFS_LOG(DEBUG, "inode_delete_from_disk: ino %x flag DELETE\n",
+                   (unsigned int)inode->ni_inode_entry->nie_hash_entry.nhe_id);
+
+    }
+
     if (rc != 0) {
         return rc;
     }
@@ -544,6 +612,11 @@ nffs_inode_rename(struct nffs_inode_entry *inode_entry,
     disk_inode.ndi_seq = inode.ni_seq + 1;
     disk_inode.ndi_parent_id = nffs_inode_parent_id(&inode);
     disk_inode.ndi_filename_len = filename_len;
+    if (inode_entry->nie_last_block_entry &&
+        inode_entry->nie_last_block_entry->nhe_id != NFFS_ID_NONE)
+        disk_inode.ndi_lastblock_id = 
inode_entry->nie_last_block_entry->nhe_id;
+    else 
+        disk_inode.ndi_lastblock_id = NFFS_ID_NONE;
     nffs_crc_disk_inode_fill(&disk_inode, new_filename);
 
     rc = nffs_inode_write_disk(&disk_inode, new_filename, area_idx,
@@ -558,6 +631,74 @@ nffs_inode_rename(struct nffs_inode_entry *inode_entry,
     return 0;
 }
 
+int
+nffs_inode_update(struct nffs_inode_entry *inode_entry)
+{
+    struct nffs_disk_inode disk_inode;
+    struct nffs_inode inode;
+    uint32_t area_offset;
+    uint8_t area_idx;
+    char *filename;
+    int filename_len;
+    int rc;
+
+    rc = nffs_inode_from_entry(&inode, inode_entry);
+    /*
+     * if rc == FS_ENOENT, file is dummy is unlinked and so
+     * can not be updated to disk.
+     */
+    if (rc == FS_ENOENT)
+        assert(nffs_inode_is_dummy(inode_entry));
+    if (rc != 0) {
+        return rc;
+    }
+
+    assert(inode_entry->nie_hash_entry.nhe_flash_loc != NFFS_FLASH_LOC_NONE);
+
+    filename_len = inode.ni_filename_len;
+    nffs_flash_loc_expand(inode_entry->nie_hash_entry.nhe_flash_loc,
+                          &area_idx, &area_offset);
+    rc = nffs_flash_read(area_idx,
+                         area_offset + sizeof (struct nffs_disk_inode),
+                         nffs_flash_buf, filename_len);
+    if (rc != 0) {
+        return rc;
+    }
+
+    filename = (char *)nffs_flash_buf;
+
+    rc = nffs_misc_reserve_space(sizeof disk_inode + filename_len,
+                                 &area_idx, &area_offset);
+    if (rc != 0) {
+        return rc;
+    }
+
+    disk_inode.ndi_id = inode_entry->nie_hash_entry.nhe_id;
+    disk_inode.ndi_seq = inode.ni_seq + 1;
+    disk_inode.ndi_parent_id = nffs_inode_parent_id(&inode);
+    disk_inode.ndi_flags = 0;
+    disk_inode.ndi_filename_len = filename_len;
+
+    assert(nffs_hash_id_is_block(inode_entry->nie_last_block_entry->nhe_id));
+    disk_inode.ndi_lastblock_id = inode_entry->nie_last_block_entry->nhe_id;
+
+    nffs_crc_disk_inode_fill(&disk_inode, filename);
+
+    NFFS_LOG(DEBUG, "nffs_inode_update writing inode %x last block %x\n",
+             (unsigned int)disk_inode.ndi_id,
+             (unsigned int)disk_inode.ndi_lastblock_id);
+
+    rc = nffs_inode_write_disk(&disk_inode, filename, area_idx,
+                               area_offset);
+    if (rc != 0) {
+        return rc;
+    }
+
+    inode_entry->nie_hash_entry.nhe_flash_loc =
+        nffs_flash_loc(area_idx, area_offset);
+    return 0;
+}
+
 static int
 nffs_inode_read_filename_chunk(const struct nffs_inode *inode,
                                uint8_t filename_offset, void *buf, int len)
@@ -637,6 +778,7 @@ nffs_inode_add_child(struct nffs_inode_entry *parent,
     int rc;
 
     assert(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
+    assert(!nffs_inode_getflags(child, NFFS_INODE_FLAG_INTREE));
 
     rc = nffs_inode_from_entry(&child_inode, child);
     if (rc != 0) {
@@ -668,6 +810,7 @@ nffs_inode_add_child(struct nffs_inode_entry *parent,
     } else {
         SLIST_INSERT_AFTER(prev, child, nie_sibling_next);
     }
+    nffs_inode_setflags(child, NFFS_INODE_FLAG_INTREE);
 
     return 0;
 }
@@ -677,12 +820,14 @@ nffs_inode_remove_child(struct nffs_inode *child)
 {
     struct nffs_inode_entry *parent;
 
+    assert(nffs_inode_getflags(child->ni_inode_entry, NFFS_INODE_FLAG_INTREE));
     parent = child->ni_parent;
     assert(parent != NULL);
     assert(nffs_hash_id_is_dir(parent->nie_hash_entry.nhe_id));
     SLIST_REMOVE(&parent->nie_child_list, child->ni_inode_entry,
                  nffs_inode_entry, nie_sibling_next);
     SLIST_NEXT(child->ni_inode_entry, nie_sibling_next) = NULL;
+    nffs_inode_unsetflags(child->ni_inode_entry, NFFS_INODE_FLAG_INTREE);
 }
 
 int
@@ -737,6 +882,9 @@ nffs_inode_filename_cmp_ram(const struct nffs_inode *inode,
     return 0;
 }
 
+/*
+ * Compare filenames in flash
+ */
 int
 nffs_inode_filename_cmp_flash(const struct nffs_inode *inode1,
                               const struct nffs_inode *inode2,
@@ -973,6 +1121,12 @@ nffs_inode_unlink_from_ram_priv(struct nffs_inode *inode,
         nffs_inode_remove_child(inode);
     }
 
+    /*
+     * Regardless of whether the inode is removed from hashlist, we record
+     * the intention to delete it here.
+     */
+    nffs_inode_setflags(inode->ni_inode_entry, NFFS_INODE_FLAG_DELETED);
+
     if (nffs_hash_id_is_dir(inode->ni_inode_entry->nie_hash_entry.nhe_id)) {
         nffs_inode_insert_unlink_list(inode->ni_inode_entry);
         rc = nffs_inode_process_unlink_list(out_next, ignore_corruption);
@@ -1073,3 +1227,59 @@ nffs_inode_unlink(struct nffs_inode *inode)
 
     return 0;
 }
+
+/*
+ * Return true if inode is a dummy inode, that was allocated as a
+ * place holder in the case that an inode is restored before it's parent.
+ */
+int
+nffs_inode_is_dummy(struct nffs_inode_entry *inode_entry)
+{
+    if (inode_entry->nie_flash_loc == NFFS_FLASH_LOC_NONE) {
+        /*
+         * set if not already XXX can delete after debug
+         */
+        nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DUMMY);
+        return 1;
+    }
+
+    if (inode_entry == nffs_root_dir) {
+        return 0;
+    } else {
+        return nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMY);
+    }
+}
+
+/*
+ * Return true if inode is marked as deleted.
+ */
+int
+nffs_inode_is_deleted(struct nffs_inode_entry *inode_entry)
+{
+    assert(inode_entry);
+
+    return nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DELETED);
+}
+
+int
+nffs_inode_setflags(struct nffs_inode_entry *entry, uint8_t flag)
+{
+    /*
+     * We shouldn't be setting flags to already deleted inodes
+     */
+    entry->nie_flags |= flag;
+    return (int)entry->nie_flags;
+}
+
+int
+nffs_inode_unsetflags(struct nffs_inode_entry *entry, uint8_t flag)
+{
+    entry->nie_flags &= ~flag;
+    return (int)entry->nie_flags;
+}
+
+int
+nffs_inode_getflags(struct nffs_inode_entry *entry, uint8_t flag)
+{
+    return (int)(entry->nie_flags & flag);
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_misc.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_misc.c b/fs/nffs/src/nffs_misc.c
index 7f9955d..cbf0e45 100644
--- a/fs/nffs/src/nffs_misc.c
+++ b/fs/nffs/src/nffs_misc.c
@@ -48,7 +48,10 @@ nffs_misc_validate_root_dir(void)
     }
 
     rc = nffs_inode_from_entry(&inode, nffs_root_dir);
-    if (rc != 0) {
+    /*
+     * nffs_root_dir is automatically flagged a "dummy" inode but it's special
+     */
+    if (rc != 0 && rc != FS_ENOENT) {
         return rc;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_path.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_path.c b/fs/nffs/src/nffs_path.c
index 7fae0d1..85772d4 100644
--- a/fs/nffs/src/nffs_path.c
+++ b/fs/nffs/src/nffs_path.c
@@ -98,6 +98,10 @@ nffs_path_find_child(struct nffs_inode_entry *parent,
     return FS_ENOENT;
 }
 
+/*
+ * Return the inode and optionally it's parent associated with the input path
+ * nffs_path_parser struct used to track location in hierarchy
+ */
 int
 nffs_path_find(struct nffs_path_parser *parser,
                struct nffs_inode_entry **out_inode_entry,
@@ -269,6 +273,15 @@ nffs_path_rename(const char *from, const char *to)
             return rc;
         }
 
+        /*
+         * Don't allow renames if the inode has been deleted
+         * Side-effect is that we've restored the inode as needed.
+         */
+        if (nffs_inode_is_deleted(from_inode_entry)) {
+            assert(0);
+            return FS_ENOENT;
+        }
+
         rc = nffs_inode_unlink(&inode);
         if (rc != 0) {
             return rc;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_priv.h
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_priv.h b/fs/nffs/src/nffs_priv.h
index 76c7827..b654b85 100644
--- a/fs/nffs/src/nffs_priv.h
+++ b/fs/nffs/src/nffs_priv.h
@@ -39,6 +39,7 @@
 
 #define NFFS_ID_ROOT_DIR             0
 #define NFFS_ID_NONE                 0xffffffff
+#define NFFS_HASH_ENTRY_NONE         0xffffffff
 
 #define NFFS_AREA_MAGIC0             0xb98a31e2
 #define NFFS_AREA_MAGIC1             0x7fb0428c
@@ -48,7 +49,7 @@
 #define NFFS_INODE_MAGIC             0x925f8bc0
 
 #define NFFS_AREA_ID_NONE            0xff
-#define NFFS_AREA_VER_0                                 0
+#define NFFS_AREA_VER_0                 0
 #define NFFS_AREA_VER_1              1
 #define NFFS_AREA_VER                NFFS_AREA_VER_1
 #define NFFS_AREA_OFFSET_ID          23
@@ -68,44 +69,14 @@ struct nffs_disk_area {
 };
 
 /** On-disk representation of an inode (file or directory). */
-struct nffs_disk_inodeV0 {
-    uint32_t ndi_magic;         /* NFFS_INODE_MAGIC */
-    uint32_t ndi_id;            /* Unique object ID. */
-    uint32_t ndi_seq;           /* Sequence number; greater supersedes
-                                   lesser. */
-    uint32_t ndi_parent_id;     /* Object ID of parent directory inode. */
-    uint8_t reserved8;
-    uint8_t ndi_filename_len;   /* Length of filename, in bytes. */
-    uint16_t ndi_crc16;         /* Covers rest of header and filename. */
-    /* Followed by filename. */
-};
-
-#define NFFS_DISK_INODEV0_OFFSET_CRC  18
-
-/** On-disk representation of a data block. */
-struct nffs_disk_blockV0 {
-    uint32_t ndb_magic;     /* NFFS_BLOCK_MAGIC */
-    uint32_t ndb_id;        /* Unique object ID. */
-    uint32_t ndb_seq;       /* Sequence number; greater supersedes lesser. */
-    uint32_t ndb_inode_id;  /* Object ID of owning inode. */
-    uint32_t ndb_prev_id;   /* Object ID of previous block in file;
-                               NFFS_ID_NONE if this is the first block. */
-    uint16_t ndb_data_len;  /* Length of data contents, in bytes. */
-    uint16_t ndb_crc16;     /* Covers rest of header and data. */
-    /* Followed by 'ndb_data_len' bytes of data. */
-};
-
-#define NFFS_DISK_BLOCKV0_OFFSET_CRC  22
-
-/** On-disk representation of an inode (file or directory). */
 struct nffs_disk_inode {
     uint32_t ndi_id;            /* Unique object ID. */
     uint32_t ndi_parent_id;     /* Object ID of parent directory inode. */
     uint32_t ndi_lastblock_id;     /* Object ID of parent directory inode. */
     uint16_t ndi_seq;           /* Sequence number; greater supersedes
                                    lesser. */
-       uint16_t reserved16;
-    uint8_t reserved8;
+    uint16_t reserved16;
+    uint8_t ndi_flags;            /* flags */
     uint8_t ndi_filename_len;   /* Length of filename, in bytes. */
     uint16_t ndi_crc16;         /* Covers rest of header and filename. */
     /* Followed by filename. */
@@ -149,10 +120,26 @@ struct nffs_inode_entry {
     union {
         struct nffs_inode_list nie_child_list;           /* If directory */
         struct nffs_hash_entry *nie_last_block_entry;    /* If file */
+        uint32_t nie_lastblock_id;
     };
     uint8_t nie_refcnt;
+    uint8_t nie_flags;
+    uint16_t reserved16;
 };
 
+#define    NFFS_INODE_FLAG_FREE        0x00
+#define    NFFS_INODE_FLAG_DUMMY       0x01    /* inode is a dummy */
+#define    NFFS_INODE_FLAG_DUMMYPARENT 0x02    /* parent not in cache */
+#define    NFFS_INODE_FLAG_DUMMYLSTBLK 0x04    /* lastblock not in cache */
+#define    NFFS_INODE_FLAG_DUMMYINOBLK 0x08    /* dummy inode for blk */
+#define    NFFS_INODE_FLAG_OBSOLETE    0x10    /* always replace if same ID */
+#define    NFFS_INODE_FLAG_INTREE      0x20    /* in directory structure */
+#define    NFFS_INODE_FLAG_INHASH      0x40    /* in hash table */
+#define    NFFS_INODE_FLAG_DELETED     0x80    /* inode deleted */
+
+#define nie_id            nie_hash_entry.nhe_id
+#define nie_flash_loc    nie_hash_entry.nhe_flash_loc
+
 /** Full inode representation; not stored permanently RAM. */
 struct nffs_inode {
     struct nffs_inode_entry *ni_inode_entry; /* Points to real inode entry. */
@@ -199,8 +186,8 @@ struct nffs_disk_object {
     } ndo_un_obj;
 };
 
-#define ndo_disk_inode ndo_un_obj.ndo_disk_inode
-#define ndo_disk_block ndo_un_obj.ndo_disk_block
+#define ndo_disk_inode    ndo_un_obj.ndo_disk_inode
+#define ndo_disk_block    ndo_un_obj.ndo_disk_block
 
 struct nffs_seek_info {
     struct nffs_block nsi_last_block;
@@ -269,6 +256,7 @@ extern uint8_t nffs_num_areas;
 extern uint8_t nffs_scratch_area_idx;
 extern uint16_t nffs_block_max_data_sz;
 extern unsigned int nffs_gc_count;
+extern struct nffs_area_desc *nffs_current_area_descs;
 
 #define NFFS_FLASH_BUF_SZ        256
 extern uint8_t nffs_flash_buf[NFFS_FLASH_BUF_SZ];
@@ -313,6 +301,7 @@ int nffs_block_from_hash_entry(struct nffs_block *out_block,
                                struct nffs_hash_entry *entry);
 int nffs_block_read_data(const struct nffs_block *block, uint16_t offset,
                          uint16_t length, void *dst);
+int nffs_block_is_dummy(struct nffs_hash_entry *entry);
 
 /* @cache */
 void nffs_cache_inode_delete(const struct nffs_inode_entry *inode_entry);
@@ -390,6 +379,8 @@ struct nffs_hash_entry *nffs_hash_find_block(uint32_t id);
 void nffs_hash_insert(struct nffs_hash_entry *entry);
 void nffs_hash_remove(struct nffs_hash_entry *entry);
 int nffs_hash_init(void);
+int nffs_hash_entry_is_dummy(struct nffs_hash_entry *he);
+int nffs_hash_id_is_dummy(uint32_t id);
 
 /* @inode */
 struct nffs_inode_entry *nffs_inode_entry_alloc(void);
@@ -407,6 +398,7 @@ int nffs_inode_entry_from_disk(struct nffs_inode_entry 
*out_inode,
 int nffs_inode_rename(struct nffs_inode_entry *inode_entry,
                       struct nffs_inode_entry *new_parent,
                       const char *new_filename);
+int nffs_inode_update(struct nffs_inode_entry *inode_entry);
 void nffs_inode_insert_block(struct nffs_inode *inode,
                              struct nffs_block *block);
 int nffs_inode_read_disk(uint8_t area_idx, uint32_t offset,
@@ -414,6 +406,7 @@ int nffs_inode_read_disk(uint8_t area_idx, uint32_t offset,
 int nffs_inode_write_disk(const struct nffs_disk_inode *disk_inode,
                           const char *filename, uint8_t area_idx,
                           uint32_t offset);
+int nffs_inode_inc_refcnt(struct nffs_inode_entry *inode_entry);
 int nffs_inode_dec_refcnt(struct nffs_inode_entry *inode_entry);
 int nffs_inode_add_child(struct nffs_inode_entry *parent,
                          struct nffs_inode_entry *child);
@@ -439,6 +432,11 @@ int nffs_inode_unlink_from_ram(struct nffs_inode *inode,
 int nffs_inode_unlink_from_ram_corrupt_ok(struct nffs_inode *inode,
                                           struct nffs_hash_entry **out_next);
 int nffs_inode_unlink(struct nffs_inode *inode);
+int nffs_inode_is_dummy(struct nffs_inode_entry *inode_entry);
+int nffs_inode_is_deleted(struct nffs_inode_entry *inode_entry);
+int nffs_inode_setflags(struct nffs_inode_entry *entry, uint8_t flag);
+int nffs_inode_unsetflags(struct nffs_inode_entry *entry, uint8_t flag);
+int nffs_inode_getflags(struct nffs_inode_entry *entry, uint8_t flag);
 
 /* @misc */
 int nffs_misc_gc_if_oom(void *resource, int *out_rc);
@@ -483,7 +481,7 @@ int nffs_write_to_file(struct nffs_file *file, const void 
*data, int len);
 #ifdef NFFS_DEBUG
 #include <stdio.h>
 #define NFFS_LOG(lvl, ...) \
-       printf(__VA_ARGS__)
+      printf(__VA_ARGS__)
 #else
 #define NFFS_LOG(lvl, ...) \
     LOG_ ## lvl(&nffs_log, LOG_MODULE_NFFS, __VA_ARGS__)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_restore.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_restore.c b/fs/nffs/src/nffs_restore.c
index eb16c71..83b2ef9 100644
--- a/fs/nffs/src/nffs_restore.c
+++ b/fs/nffs/src/nffs_restore.c
@@ -56,6 +56,10 @@ nffs_restore_validate_block_chain(struct nffs_hash_entry 
*last_block_entry)
     cur = last_block_entry;
 
     while (cur != NULL) {
+        if (nffs_hash_entry_is_dummy(cur)) {
+            return FS_ENOENT;
+        }
+
         nffs_flash_loc_expand(cur->nhe_flash_loc, &area_idx, &area_offset);
 
         rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
@@ -77,27 +81,27 @@ nffs_restore_validate_block_chain(struct nffs_hash_entry 
*last_block_entry)
 static void
 u32toa(char *dst, uint32_t val)
 {
-       uint8_t tmp;
-       int idx = 0;
-       int i;
-       int print = 0;
-
-       for (i = 0; i < 8; i++) {
-               tmp = val >> 28;
-               if (tmp || i == 7) {
-                       print = 1;
-               }
-               if (tmp < 10) {
-                       tmp += '0';
-               } else {
-                       tmp += 'a' - 10;
-               }
-               if (print) {
-                       dst[idx++] = tmp;
-               }
-               val <<= 4;
-       }
-       dst[idx++] = '\0';
+    uint8_t tmp;
+    int idx = 0;
+    int i;
+    int print = 0;
+
+    for (i = 0; i < 8; i++) {
+        tmp = val >> 28;
+        if (tmp || i == 7) {
+            print = 1;
+        }
+        if (tmp < 10) {
+            tmp += '0';
+        } else {
+            tmp += 'a' - 10;
+        }
+        if (print) {
+            dst[idx++] = tmp;
+        }
+        val <<= 4;
+    }
+    dst[idx++] = '\0';
 }
 
 /**
@@ -121,7 +125,7 @@ nffs_restore_migrate_orphan_children(struct 
nffs_inode_entry *inode_entry)
         return 0;
     }
 
-    if (inode_entry->nie_refcnt != 0) {
+    if (!nffs_inode_is_dummy(inode_entry)) {
         /* Not a dummy. */
         return 0;
     }
@@ -160,16 +164,44 @@ nffs_restore_should_sweep_inode_entry(struct 
nffs_inode_entry *inode_entry,
     struct nffs_inode inode;
     int rc;
 
-    /* Determine if the inode is a dummy.  Dummy inodes have a reference count
-     * of 0.  If it is a dummy, increment its reference count back to 1 so that
-     * it can be properly deleted.  The presence of a dummy inode during the
-     * final sweep step indicates file system corruption.  If the inode is a
-     * directory, all its children should have been migrated to the /lost+found
-     * directory prior to this.
+
+    /*
+     * if this inode was tagged to have a dummy block entry and the
+     * flag is still set, that means we didn't find the block and so
+     * should remove this inode and all associated blocks.
      */
-    if (inode_entry->nie_refcnt == 0) {
+    if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMYLSTBLK)) {
         *out_should_sweep = 1;
-        inode_entry->nie_refcnt++;
+        /*nffs_inode_inc_refcnt(inode_entry);*/
+        inode_entry->nie_refcnt = 1;
+        assert(inode_entry->nie_refcnt >= 1);
+        return 0;
+    }
+
+    /*
+     * This inode was originally created to hold a block that was restored
+     * before the owning inode. If the flag is still set, it means we never
+     * restored the inode from disk and so this entry should be deleted.
+     */
+    if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMYINOBLK)) {
+        *out_should_sweep = 2;
+        /*nffs_inode_inc_refcnt(inode_entry);*/
+        inode_entry->nie_refcnt = 1;
+        assert(inode_entry->nie_refcnt >= 1);
+        return 0;
+    }
+
+    /*
+     * Determine if the inode is a dummy. Dummy inodes have a flash
+     * location set to LOC_NONE and should have a flag set for the reason.
+     * The presence of a dummy inode during the final sweep step indicates
+     * file system corruption.  It's assumed that directories have
+     * previously migrated all children to /lost+found.
+     */
+    if (nffs_inode_is_dummy(inode_entry)) {
+        *out_should_sweep = 3;
+        nffs_inode_inc_refcnt(inode_entry);
+        assert(inode_entry->nie_refcnt >= 1);
         return 0;
     }
 
@@ -179,17 +211,32 @@ nffs_restore_should_sweep_inode_entry(struct 
nffs_inode_entry *inode_entry,
      */
     if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
         rc = nffs_inode_from_entry(&inode, inode_entry);
-        if (rc != 0) {
+        if (rc != 0 && rc != FS_ENOENT) {
             *out_should_sweep = 0;
             return rc;
         }
-
         if (inode.ni_parent == NULL) {
-            *out_should_sweep = 1;
+            *out_should_sweep = 4;
             return 0;
         }
     }
 
+    /*
+     * If this inode has been marked as deleted, we can unlink it here.
+     *
+     * XXX Note that the record of a deletion could be lost if garbage
+     * collection erases the delete but leaves inode updates on other
+     * partitions which can then be restored.
+     */
+    if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DELETED)) {
+        rc = nffs_inode_from_entry(&inode, inode_entry);
+        if (rc != 0) {
+            *out_should_sweep = 0;
+            return rc;
+        }
+        *out_should_sweep = 5;
+    }
+
     /* If this is a file inode, verify that all of its constituent blocks are
      * present.
      */
@@ -197,7 +244,10 @@ nffs_restore_should_sweep_inode_entry(struct 
nffs_inode_entry *inode_entry,
         rc = nffs_restore_validate_block_chain(
                 inode_entry->nie_last_block_entry);
         if (rc == FS_ECORRUPT) {
-            *out_should_sweep = 1;
+            *out_should_sweep = 6;
+            return 0;
+        } else if (rc == FS_ENOENT) {
+            *out_should_sweep = 7;
             return 0;
         } else if (rc != 0) {
             *out_should_sweep = 0;
@@ -210,85 +260,6 @@ nffs_restore_should_sweep_inode_entry(struct 
nffs_inode_entry *inode_entry,
     return 0;
 }
 
-static void
-nffs_restore_inode_from_dummy_entry(struct nffs_inode *out_inode,
-                                    struct nffs_inode_entry *inode_entry)
-{
-    memset(out_inode, 0, sizeof *out_inode);
-    out_inode->ni_inode_entry = inode_entry;
-}
-
-static int
-nffs_restore_find_file_end_block(struct nffs_hash_entry *block_entry)
-{
-    struct nffs_inode_entry *inode_entry;
-    struct nffs_block block;
-    int rc;
-
-    rc = nffs_block_from_hash_entry(&block, block_entry);
-    assert(rc == 0);
-
-    inode_entry = block.nb_inode_entry;
-
-    /* Make sure the parent inode (file) points to the latest data block
-     * restored so far.
-     *
-     * XXX: This is an O(n) operation (n = # of blocks in the file), and is
-     * horribly inefficient for large files.  MYNEWT-161 has been opened to
-     * address this.
-     */
-    if (inode_entry->nie_last_block_entry == NULL) {
-        /* This is the first data block restored for this file. */
-        inode_entry->nie_last_block_entry = block_entry;
-        NFFS_LOG(DEBUG, "setting last block: %u\n", block_entry->nhe_id);
-    } else {
-        /* Determine if this this data block comes after our current idea of
-         * the file's last data block.
-         */
-
-        rc = nffs_block_find_predecessor(
-            block_entry, inode_entry->nie_last_block_entry->nhe_id);
-        switch (rc) {
-        case 0:
-            /* The currently-last block is a predecessor of the new block; the
-             * new block comes later.
-             */
-            NFFS_LOG(DEBUG, "replacing last block: %u --> %u\n",
-                     inode_entry->nie_last_block_entry->nhe_id,
-                     block_entry->nhe_id);
-            inode_entry->nie_last_block_entry = block_entry;
-            break;
-
-        case FS_ENOENT:
-            break;
-
-        default:
-            return rc;
-        }
-    }
-
-    return 0;
-}
-
-
-static int
-nffs_restore_find_file_ends(void)
-{
-    struct nffs_hash_entry *block_entry;
-    struct nffs_hash_entry *next;
-    int rc;
-    int i;
-
-    NFFS_HASH_FOREACH(block_entry, i, next) {
-        if (!nffs_hash_id_is_inode(block_entry->nhe_id)) {
-            rc = nffs_restore_find_file_end_block(block_entry);
-            assert(rc == 0);
-        }
-    }
-
-    return 0;
-}
-
 /**
  * Performs a sweep of the RAM representation at the end of a successful
  * restore.  The sweep phase performs the following actions of each inode in
@@ -310,6 +281,7 @@ nffs_restore_sweep(void)
     struct nffs_hash_entry *next;
     struct nffs_hash_list *list;
     struct nffs_inode inode;
+    struct nffs_block block;
     int del;
     int rc;
     int i;
@@ -326,9 +298,10 @@ nffs_restore_sweep(void)
             if (nffs_hash_id_is_inode(entry->nhe_id)) {
                 inode_entry = (struct nffs_inode_entry *)entry;
 
-                /* If this is a dummy inode directory, the file system is
-                 * corrupted.  Move the directory's children inodes to the
-                 * lost+found directory.
+                /*
+                 * If this is a dummy inode directory, the file system
+                 * is corrupt.  Move the directory's children inodes to
+                 * the lost+found directory.
                  */
                 rc = nffs_restore_migrate_orphan_children(inode_entry);
                 if (rc != 0) {
@@ -341,18 +314,23 @@ nffs_restore_sweep(void)
                     return rc;
                 }
 
+                rc = nffs_inode_from_entry(&inode, inode_entry);
+                if (rc != 0 && rc != FS_ENOENT) {
+                    return rc;
+                }
+
+#if 0 /* for now, don't preserve corrupted directories */
+                /*
+                 * if this inode doesn't have a parent, move it to
+                 * the lost_found directory
+                 */
+                if (inode_entry != nffs_root_dir && inode.ni_parent == NULL) {
+                    rc = nffs_inode_rename(inode_entry,
+                                           nffs_lost_found_dir, NULL);
+                }
+#endif
+
                 if (del) {
-                    if (inode_entry->nie_hash_entry.nhe_flash_loc ==
-                        NFFS_FLASH_LOC_NONE) {
-
-                        nffs_restore_inode_from_dummy_entry(&inode,
-                                                           inode_entry);
-                    } else {
-                        rc = nffs_inode_from_entry(&inode, inode_entry);
-                        if (rc != 0) {
-                            return rc;
-                        }
-                    }
 
                     /* Remove the inode and all its children from RAM.  We
                      * expect some file system corruption; the children are
@@ -366,6 +344,15 @@ nffs_restore_sweep(void)
                     }
                     next = SLIST_FIRST(list);
                 }
+            } else if (nffs_hash_id_is_block(entry->nhe_id)) {
+                if (nffs_hash_id_is_dummy(entry->nhe_id)) {
+                    nffs_block_delete_from_ram(entry);
+                } else {
+                    rc = nffs_block_from_hash_entry(&block, entry);
+                    if (rc != 0 && rc != FS_ENOENT) {
+                        nffs_block_delete_from_ram(entry);
+                    }
+                }
             }
 
             entry = next;
@@ -380,7 +367,8 @@ nffs_restore_sweep(void)
  * a temporary placeholder for a real inode that has not been restored yet.
  * These are necessary so that the inter-object links can be maintained until
  * the absent inode is eventually restored.  Dummy inodes are identified by a
- * reference count of 0.
+ * inaccessible flash location (NFFS_FLASH_LOC_NONE). When the real inode
+ * is restored, this flash location will be udpated.
  *
  * @param id                    The ID of the dummy inode to create.
  * @param out_inode_entry       On success, the dummy inode gets written here.
@@ -400,6 +388,8 @@ nffs_restore_dummy_inode(uint32_t id,
     inode_entry->nie_hash_entry.nhe_id = id;
     inode_entry->nie_hash_entry.nhe_flash_loc = NFFS_FLASH_LOC_NONE;
     inode_entry->nie_refcnt = 0;
+    inode_entry->nie_last_block_entry = NULL; /* lastblock not available yet */
+    nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DUMMY);
 
     nffs_hash_insert(&inode_entry->nie_hash_entry);
 
@@ -431,11 +421,22 @@ nffs_restore_inode_gets_replaced(struct nffs_inode_entry 
*old_inode_entry,
 
     assert(old_inode_entry->nie_hash_entry.nhe_id == disk_inode->ndi_id);
 
-    if (old_inode_entry->nie_refcnt == 0) {
+
+    if (nffs_inode_is_dummy(old_inode_entry)) {
+        NFFS_LOG(DEBUG, "inode_gets_replaced dummy!\n");
         *out_should_replace = 1;
         return 0;
     }
 
+    /*
+     * inode is known to be obsolete and needs to be replaced no matter what
+     */
+    if (nffs_inode_getflags(old_inode_entry, NFFS_INODE_FLAG_OBSOLETE)) {
+        NFFS_LOG(DEBUG, "inode_gets_replaced obsolete\n");
+        *out_should_replace = 2;
+        return 0;
+    }
+
     rc = nffs_inode_from_entry(&old_inode, old_inode_entry);
     if (rc != 0) {
         *out_should_replace = 0;
@@ -443,7 +444,8 @@ nffs_restore_inode_gets_replaced(struct nffs_inode_entry 
*old_inode_entry,
     }
 
     if (old_inode.ni_seq < disk_inode->ndi_seq) {
-        *out_should_replace = 1;
+        NFFS_LOG(DEBUG, "inode_gets_replaced seq\n");
+        *out_should_replace = 3;
         return 0;
     }
 
@@ -452,7 +454,7 @@ nffs_restore_inode_gets_replaced(struct nffs_inode_entry 
*old_inode_entry,
          * happen.
          */
         *out_should_replace = 0;
-        return FS_ECORRUPT;
+        return FS_EEXIST;
     }
 
     *out_should_replace = 0;
@@ -476,6 +478,7 @@ nffs_restore_inode(const struct nffs_disk_inode 
*disk_inode, uint8_t area_idx,
     struct nffs_inode_entry *inode_entry;
     struct nffs_inode_entry *parent;
     struct nffs_inode inode;
+    struct nffs_hash_entry *lastblock_entry = NULL;
     int new_inode;
     int do_add;
     int rc;
@@ -489,29 +492,113 @@ nffs_restore_inode(const struct nffs_disk_inode 
*disk_inode, uint8_t area_idx,
     }
 
     inode_entry = nffs_hash_find_inode(disk_inode->ndi_id);
+
+    /*
+     * Inode has already been restored. Determine whether this version
+     * from disk should replace the previous version referenced in RAM.
+     */
     if (inode_entry != NULL) {
-        rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode,
-                                              &do_add);
+
+        if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED) {
+            /*
+             * Restore this inode even though deleted on disk
+             * so the additional restored blocks have a place to go
+             */
+            NFFS_LOG(DEBUG, "restoring deleted inode %x\n", 
disk_inode->ndi_id);
+            nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED);
+        }
+
+        /*
+         * inodes get replaced if they're dummy entries (i.e. allocated
+         * as place holders until the actual inode is restored), or this is
+         * a more recent version of the inode which supercedes the old.
+         */
+        rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode, 
&do_add);
         if (rc != 0) {
             goto err;
         }
 
-        if (do_add) {
-            if (inode_entry->nie_hash_entry.nhe_flash_loc !=
-                NFFS_FLASH_LOC_NONE) {
-
+        if (do_add) { /* replace in this case */
+            if (!nffs_inode_is_dummy(inode_entry)) {
+                /*
+                 * if it's not a dummy, read block from flash
+                 */
                 rc = nffs_inode_from_entry(&inode, inode_entry);
                 if (rc != 0) {
                     return rc;
                 }
+
+                /*
+                 * inode is known to be obsolete
+                 */
+                if (nffs_inode_getflags(inode_entry, 
+                                        NFFS_INODE_FLAG_OBSOLETE)) {
+                    nffs_inode_unsetflags(inode_entry,
+                                          NFFS_INODE_FLAG_OBSOLETE);
+                }
+
                 if (inode.ni_parent != NULL) {
                     nffs_inode_remove_child(&inode);
                 }
+
+                /*
+                 * If this is a delete record, subsequent inode and restore
+                 * records from flash may be ignored.
+                 * If the parent is NULL, this inode has been deleted. (old)
+                 * XXX if we could count on delete records for every inode,
+                 * we wouldn't need to check for the root directory looking
+                 * like a delete record because of it's parent ID.
+                 */
+                if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
+                    if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED ||
+                        disk_inode->ndi_parent_id == NFFS_ID_NONE) {
+
+                        nffs_inode_setflags(inode_entry,
+                                            NFFS_INODE_FLAG_DELETED);
+                    }
+                }
+
+            } else {
+                /*
+                 * The existing inode entry was added as dummy.
+                 * The restore operation clears that state.
+                 */
+
+                /* If it's a directory, it was added as a parent to
+                 * one of it's children who were restored first.
+                 */
+                if (nffs_inode_getflags(inode_entry, 
+                                         NFFS_INODE_FLAG_DUMMYPARENT)) {
+                    
assert(nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id));
+                    nffs_inode_unsetflags(inode_entry, 
+                                         NFFS_INODE_FLAG_DUMMYPARENT);
+                }
+
+                /*
+                 * If it's a file, it was added to store a lastblock
+                 */
+                if (nffs_inode_getflags(inode_entry, 
+                                         NFFS_INODE_FLAG_DUMMYINOBLK)) {
+                    
assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
+                    nffs_inode_unsetflags(inode_entry, 
+                                         NFFS_INODE_FLAG_DUMMYINOBLK);
+                }
+
+                /*
+                 * Also, since it's a dummy, clear this flag too
+                 */
+                if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMY)) {
+                    nffs_inode_unsetflags(inode_entry, NFFS_INODE_FLAG_DUMMY);
+                }
             }
  
+            /*
+             * Update location to reference new location in flash
+             */
             inode_entry->nie_hash_entry.nhe_flash_loc =
-                nffs_flash_loc(area_idx, area_offset);
+                                    nffs_flash_loc(area_idx, area_offset);
         }
+        
     } else {
         inode_entry = nffs_inode_entry_alloc();
         if (inode_entry == NULL) {
@@ -523,19 +610,94 @@ nffs_restore_inode(const struct nffs_disk_inode 
*disk_inode, uint8_t area_idx,
 
         inode_entry->nie_hash_entry.nhe_id = disk_inode->ndi_id;
         inode_entry->nie_hash_entry.nhe_flash_loc =
-            nffs_flash_loc(area_idx, area_offset);
+                              nffs_flash_loc(area_idx, area_offset);
+        inode_entry->nie_last_block_entry = NULL; /* for now */
 
         nffs_hash_insert(&inode_entry->nie_hash_entry);
     }
 
+    /*
+     * inode object has been restored and the entry is in the hash
+     * Check whether the lastblock and parent have also been restored
+     * and link up or allocate dummy entries as appropriate.
+     */
     if (do_add) {
         inode_entry->nie_refcnt = 1;
 
+        if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED) {
+            /*
+             * Restore this inode even though deleted on disk
+             * so the additional restored blocks have a place to go
+             */
+            NFFS_LOG(DEBUG, "restoring deleted inode %x\n", 
disk_inode->ndi_id);
+            nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED);
+        }
+
+        /*
+         * Inode has a lastblock on disk.
+         * Add reference to last block entry if in hash table
+         * otherwise add a dummy block entry for later update
+         */
+        if (disk_inode->ndi_lastblock_id != NFFS_ID_NONE &&
+                nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
+            lastblock_entry =
+              nffs_hash_find_block(disk_inode->ndi_lastblock_id);
+
+            /*
+             * Lastblock has already been restored.
+             */
+            if (lastblock_entry != NULL) {
+                if (lastblock_entry->nhe_id == disk_inode->ndi_lastblock_id) {
+                    inode_entry->nie_last_block_entry = lastblock_entry;
+                    /*
+                     * This flag should have been turned unset
+                     * when the block was restored.
+                     */
+                    assert(!nffs_inode_getflags(inode_entry,
+                                               NFFS_INODE_FLAG_DUMMYLSTBLK));
+                }
+
+            } else {
+                /*
+                 * Insert a temporary reference to a 'dummy' block entry
+                 * When block is restored, it will update this dummy and
+                 * the entry of this inode is updated to flash location
+                 */
+                rc = nffs_block_entry_reserve(&lastblock_entry);
+                if (lastblock_entry == NULL) {
+                    rc = FS_ENOMEM;
+                    goto err;
+                }
+
+                lastblock_entry->nhe_id = disk_inode->ndi_lastblock_id;
+                lastblock_entry->nhe_flash_loc = NFFS_FLASH_LOC_NONE;
+                inode_entry->nie_last_block_entry = lastblock_entry;
+                nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DUMMYLSTBLK);
+                nffs_hash_insert(lastblock_entry);
+
+                if (lastblock_entry->nhe_id >= nffs_hash_next_block_id) {
+                    nffs_hash_next_block_id = lastblock_entry->nhe_id + 1;
+                }
+            }
+        }
+
         if (disk_inode->ndi_parent_id != NFFS_ID_NONE) {
+            
             parent = nffs_hash_find_inode(disk_inode->ndi_parent_id);
+            /*
+             * The parent directory for this inode hasn't been restored yet.
+             * Add a dummy directory so it can be added as a child.
+             * When the parent inode is restored, it's hash entry will be
+             * updated with the flash location.
+             */
             if (parent == NULL) {
                 rc = nffs_restore_dummy_inode(disk_inode->ndi_parent_id,
                                              &parent);
+                /*
+                 * Set the dummy parent flag in the new parent.
+                 * It's turned off above when restored.
+                 */
+                nffs_inode_setflags(parent, NFFS_INODE_FLAG_DUMMYPARENT);
                 if (rc != 0) {
                     goto err;
                 }
@@ -547,9 +709,9 @@ nffs_restore_inode(const struct nffs_disk_inode 
*disk_inode, uint8_t area_idx,
             }
         } 
 
-
         if (inode_entry->nie_hash_entry.nhe_id == NFFS_ID_ROOT_DIR) {
             nffs_root_dir = inode_entry;
+            nffs_inode_setflags(nffs_root_dir, NFFS_INODE_FLAG_INTREE);
         }
     }
 
@@ -599,16 +761,22 @@ nffs_restore_block_gets_replaced(const struct nffs_block 
*old_block,
 {
     assert(old_block->nb_hash_entry->nhe_id == disk_block->ndb_id);
 
-    if (old_block->nb_seq < disk_block->ndb_seq) {
+    if (nffs_block_is_dummy(old_block->nb_hash_entry)) {
+        assert(0);
         *out_should_replace = 1;
         return 0;
     }
 
+    if (old_block->nb_seq < disk_block->ndb_seq) {
+        *out_should_replace = 2;
+        return 0;
+    }
+
     if (old_block->nb_seq == disk_block->ndb_seq) {
-        /* This is a duplicate of an previously-read inode.  This should never
+        /* This is a duplicate of an previously-read block.  This should never
          * happen.
          */
-        return FS_ECORRUPT;
+        return FS_EEXIST;
     }
 
     *out_should_replace = 0;
@@ -648,12 +816,39 @@ nffs_restore_block(const struct nffs_disk_block 
*disk_block, uint8_t area_idx,
 
     entry = nffs_hash_find_block(disk_block->ndb_id);
     if (entry != NULL) {
+
         rc = nffs_block_from_hash_entry_no_ptrs(&block, entry);
-        if (rc != 0) {
+        if (rc != 0 && rc != FS_ENOENT) {
             goto err;
         }
 
-        rc = nffs_restore_block_gets_replaced(&block, disk_block, &do_replace);
+        /*
+         * If the old block reference is for a 'dummy' block, it was added
+         * because the owning inode's lastblock was not yet restored.
+         * Update the block hash entry and inode to reference the entry.
+         */
+        if (nffs_block_is_dummy(entry)) {
+
+            assert(entry->nhe_id == disk_block->ndb_id);
+
+            /*
+             * Entry is no longer dummy as it references the correct location
+             */
+            entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
+
+            inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);
+
+            /*
+             * Turn off flags in previously restored inode recording the
+             * allocation of a dummy block
+             */
+            if (inode_entry) {
+                nffs_inode_unsetflags(inode_entry, 
NFFS_INODE_FLAG_DUMMYLSTBLK);
+            }
+        }
+
+        rc = nffs_restore_block_gets_replaced(&block, disk_block,
+                                              &do_replace);
         if (rc != 0) {
             goto err;
         }
@@ -663,24 +858,28 @@ nffs_restore_block(const struct nffs_disk_block 
*disk_block, uint8_t area_idx,
             return 0;
         }
 
-        nffs_block_delete_from_ram(entry);
-    }
+        /*
+         * update the existing hash entry to reference the new flash location
+         */
+        entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
 
-    entry = nffs_block_entry_alloc();
-    if (entry == NULL) {
-        rc = FS_ENOMEM;
-        goto err;
-    }
-    new_block = 1;
-    entry->nhe_id = disk_block->ndb_id;
-    entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
+    } else {
+        entry = nffs_block_entry_alloc();
+        if (entry == NULL) {
+            rc = FS_ENOMEM;
+            goto err;
+        }
+        new_block = 1;
+        entry->nhe_id = disk_block->ndb_id;
+        entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);
 
-    /* The block is ready to be inserted into the hash. */
+        /* The block is ready to be inserted into the hash. */
 
-    nffs_hash_insert(entry);
+        nffs_hash_insert(entry);
 
-    if (disk_block->ndb_id >= nffs_hash_next_block_id) {
-        nffs_hash_next_block_id = disk_block->ndb_id + 1;
+        if (disk_block->ndb_id >= nffs_hash_next_block_id) {
+            nffs_hash_next_block_id = disk_block->ndb_id + 1;
+        }
     }
 
     /* Make sure the maximum block data size is not set lower than the size of
@@ -696,14 +895,33 @@ nffs_restore_block(const struct nffs_disk_block 
*disk_block, uint8_t area_idx,
              disk_block->ndb_data_len);
 
     inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);
+
     if (inode_entry == NULL) {
+        /*
+         * Owning inode not yet restored.
+         * Allocate a dummy inode which temporarily owns this block.
+         * It is not yet linked to a parent.
+         */
         rc = nffs_restore_dummy_inode(disk_block->ndb_inode_id, &inode_entry);
         if (rc != 0) {
             goto err;
         }
+        /*
+         * Record that this inode was created because a block was restored
+         * before the inode
+         */
+        nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DUMMYINOBLK);
+        inode_entry->nie_last_block_entry = entry;
+    } else {
+        if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DELETED)) {
+            /*
+             * don't restore blocks for deleted inodes
+             */
+            rc = FS_ENOENT;
+            goto err;
+        }
     }
 
-
     return 0;
 
 err:
@@ -764,24 +982,24 @@ nffs_restore_disk_object(int area_idx, uint32_t 
area_offset,
     int rc;
 
     rc = nffs_flash_read(area_idx, area_offset,
-                                                &out_disk_object->ndo_un_obj,
-                                                
sizeof(out_disk_object->ndo_un_obj));
+                         &out_disk_object->ndo_un_obj,
+                         sizeof(out_disk_object->ndo_un_obj));
     if (rc != 0) {
         return rc;
     }
 
-       if (nffs_hash_id_is_inode(out_disk_object->ndo_disk_inode.ndi_id)) {
+    if (nffs_hash_id_is_inode(out_disk_object->ndo_disk_inode.ndi_id)) {
         out_disk_object->ndo_type = NFFS_OBJECT_TYPE_INODE;
 
-       } else if 
(nffs_hash_id_is_block(out_disk_object->ndo_disk_block.ndb_id)) {
+    } else if (nffs_hash_id_is_block(out_disk_object->ndo_disk_block.ndb_id)) {
         out_disk_object->ndo_type = NFFS_OBJECT_TYPE_BLOCK;
 
-       } else if (out_disk_object->ndo_disk_block.ndb_id == NFFS_ID_NONE) {
+    } else if (out_disk_object->ndo_disk_block.ndb_id == NFFS_ID_NONE) {
         return FS_EEMPTY;
 
-       } else {
-               return FS_ECORRUPT;
-       }
+    } else {
+        return FS_ECORRUPT;
+    }
 
     out_disk_object->ndo_area_idx = area_idx;
     out_disk_object->ndo_offset = area_offset;
@@ -834,13 +1052,28 @@ nffs_restore_area_contents(int area_idx)
         rc = nffs_restore_disk_object(area_idx, area->na_cur,  &disk_object);
         switch (rc) {
         case 0:
+
             /* Valid object; restore it into the RAM representation. */
-            nffs_restore_object(&disk_object);
-            area->na_cur += nffs_restore_disk_object_size(&disk_object);
+            rc = nffs_restore_object(&disk_object);
+
+            /*
+             * If the restore fails the CRC check, the object length field
+             * can't be trusted so just start looking for the next valid
+             * object in the flash area.
+             * XXX Deal with file system corruption
+             */
+            if (rc == FS_ECORRUPT) {
+                area->na_cur++;
+            } else {
+                area->na_cur += nffs_restore_disk_object_size(&disk_object);
+            }
             break;
 
         case FS_ECORRUPT:
-            /* Invalid object; keep scanning for a valid magic number. */
+            /*
+             * Invalid object; keep scanning for a valid object ID and CRC
+             * Can nffs_restore_disk_object return FS_ECORRUPT? XXX
+             */
             area->na_cur++;
             break;
 
@@ -941,7 +1174,7 @@ nffs_restore_corrupt_scratch(void)
                     }
                 } else {
                     inode_entry = (struct nffs_inode_entry *)entry;
-                    inode_entry->nie_refcnt = 0;
+                    nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_OBSOLETE);
                 }
             }
 
@@ -985,7 +1218,7 @@ nffs_log_contents(void)
     NFFS_HASH_FOREACH(entry, i, next) {
         if (nffs_hash_id_is_block(entry->nhe_id)) {
             rc = nffs_block_from_hash_entry(&block, entry);
-            assert(rc == 0);
+            assert(rc == 0 || rc == FS_ENOENT);
             NFFS_LOG(DEBUG, "block; id=%u inode_id=", entry->nhe_id);
             if (block.nb_inode_entry == NULL) {
                 NFFS_LOG(DEBUG, "null ");
@@ -1005,7 +1238,19 @@ nffs_log_contents(void)
         } else {
             inode_entry = (void *)entry;
             rc = nffs_inode_from_entry(&inode, inode_entry);
-            assert(rc == 0);
+            if (rc == FS_ENOENT) {
+                NFFS_LOG(DEBUG, "DUMMY file; id=%x ref=%d block_id=",
+                         (unsigned int)entry->nhe_id, inode_entry->nie_refcnt);
+                if (inode_entry->nie_last_block_entry == NULL) {
+                    NFFS_LOG(DEBUG, "null");
+                } else {
+                    NFFS_LOG(DEBUG, "%x",
+                             (unsigned 
int)inode_entry->nie_last_block_entry->nhe_id);
+                }
+            } else if (rc != 0) {
+                continue;
+            }
+            /*assert(rc == 0);*/
 
             if (nffs_hash_id_is_file(entry->nhe_id)) {
                 NFFS_LOG(DEBUG, "file; id=%u name=%.3s block_id=",
@@ -1055,6 +1300,7 @@ nffs_restore_full(const struct nffs_area_desc *area_descs)
         return rc;
     }
     nffs_restore_largest_block_data_len = 0;
+    nffs_current_area_descs = (struct nffs_area_desc*) area_descs;
 
     /* Read each area from flash. */
     for (i = 0; area_descs[i].nad_length != 0; i++) {
@@ -1071,6 +1317,7 @@ nffs_restore_full(const struct nffs_area_desc *area_descs)
             use_area = 1;
             break;
 
+        case FS_EUNEXP:    /* not formatted with current on-disk NFFS format */
         case FS_ECORRUPT:
             use_area = 0;
             break;
@@ -1147,9 +1394,6 @@ nffs_restore_full(const struct nffs_area_desc *area_descs)
         goto err;
     }
 
-    /* Find the last block in each file inode. */
-    nffs_restore_find_file_ends();
-
     /* Delete from RAM any objects that were invalidated when subsequent areas
      * were restored.
      */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f9ef5686/fs/nffs/src/nffs_write.c
----------------------------------------------------------------------
diff --git a/fs/nffs/src/nffs_write.c b/fs/nffs/src/nffs_write.c
index b888630..dd9fb0e 100644
--- a/fs/nffs/src/nffs_write.c
+++ b/fs/nffs/src/nffs_write.c
@@ -247,6 +247,18 @@ nffs_write_append(struct nffs_cache_inode *cache_inode, 
const void *data,
 
     inode_entry->nie_last_block_entry = entry;
 
+    /*
+     * When a new block is appended to a file, the inode must be written
+     * out to flash so the last block id is is stored persistently.
+     * Need to be written atomically with writing the block out so filesystem
+     * remains consistent. nffs_lock is held in nffs_write().
+     * The inode will not be updated if it's been unlinked on disk and this
+     * is signaled by setting the hash entry's flash location to NONE
+     */
+    if (inode_entry->nie_hash_entry.nhe_flash_loc != NFFS_FLASH_LOC_NONE) {
+        rc = nffs_inode_update(inode_entry);
+    }
+
     /* Update cached inode with the new file size. */
     cache_inode->nci_file_size += len;
 

Reply via email to