From b3d3f59363f334d185adc66163816e0bb8c34fc2 Mon Sep 17 00:00:00 2001
From: Gregory Nutt <gnutt@nuttx.org>
Date: Mon, 2 Dec 2013 17:19:22 -0600
Subject: [PATCH] NXFFS: more NAND-Releated changes; don't fail if a block read
 fails.  That might just be a NAND uncorrectable bit error.  Make scanning of
 the device on startup optional.  This might have introduced some NXFFS issure
 -- To be determined

---
 fs/nxffs/Kconfig            |  30 ++++++++++
 fs/nxffs/nxffs.h            | 110 ++++++++++++++++++++++--------------
 fs/nxffs/nxffs_block.c      |  12 ++--
 fs/nxffs/nxffs_cache.c      |   7 ++-
 fs/nxffs/nxffs_initialize.c |  40 ++++++++++++-
 fs/nxffs/nxffs_pack.c       |  37 +++++++++---
 fs/nxffs/nxffs_reformat.c   |  91 +++++++++++++++++++++--------
 fs/nxffs/nxffs_unlink.c     |   6 +-
 fs/nxffs/nxffs_write.c      |  66 +++++++++++++---------
 include/nuttx/fs/nxffs.h    |  15 ++++-
 10 files changed, 302 insertions(+), 112 deletions(-)

diff --git a/fs/nxffs/Kconfig b/fs/nxffs/Kconfig
index dc05c93df1..e9528c8ab1 100644
--- a/fs/nxffs/Kconfig
+++ b/fs/nxffs/Kconfig
@@ -14,6 +14,36 @@ config FS_NXFFS
 
 if FS_NXFFS
 
+config NXFFS_SCAN_VOLUME
+	bool "Scan volume"
+	default n
+	---help---
+		Scan the media for bad blocks on start-up.  If too many bad or
+		unformatted blocks are found, then re-format the volume.  Otherwise,
+		the volume will be reformatted only if no NXFFS file system is
+		found.
+
+		Why might you want to do this?  If too many bad blocks accumulate
+		over time, then attempting to reformat my be the only way to
+		recover.  And what if you power down the device while formatting
+		the FLASH so that you have only a partially formatted device?
+		Scanning the volume can get you out of these situations.
+
+		The down side is that scanning the volume can adversely affect
+		your start-up time.  An option is to just erase the FLASH and
+		reboot in these cases.  That can be done with
+		apps/system/flash_eraseall.
+
+config NXFFS_REFORMAT_THRESH
+	int "Reformat percentage"
+	default 20
+	range 0 100
+	depends on NXFFS_SCAN_VOLUME
+	---help---
+		This defines the threshold for re-formatting.  Is less than this
+		percentage of good blocks are found, then the volume is re-
+		formatted.
+
 config NXFFS_PREALLOCATED
 	bool "Single, preallocated volume"
 	default y
diff --git a/fs/nxffs/nxffs.h b/fs/nxffs/nxffs.h
index 38a27da389..9b7bdc4ce8 100644
--- a/fs/nxffs/nxffs.h
+++ b/fs/nxffs/nxffs.h
@@ -374,7 +374,7 @@ extern struct nxffs_volume_s g_volume;
  *
  ****************************************************************************/
 
-extern int nxffs_limits(FAR struct nxffs_volume_s *volume);
+int nxffs_limits(FAR struct nxffs_volume_s *volume);
 
 /****************************************************************************
  * Name: nxffs_rdle16
@@ -392,7 +392,7 @@ extern int nxffs_limits(FAR struct nxffs_volume_s *volume);
  *
  ****************************************************************************/
 
-extern uint16_t nxffs_rdle16(FAR const uint8_t *val);
+uint16_t nxffs_rdle16(FAR const uint8_t *val);
 
 /****************************************************************************
  * Name: nxffs_wrle16
@@ -411,7 +411,7 @@ extern uint16_t nxffs_rdle16(FAR const uint8_t *val);
  *
  ****************************************************************************/
 
-extern void nxffs_wrle16(uint8_t *dest, uint16_t val);
+void nxffs_wrle16(uint8_t *dest, uint16_t val);
 
 /****************************************************************************
  * Name: nxffs_rdle32
@@ -429,7 +429,7 @@ extern void nxffs_wrle16(uint8_t *dest, uint16_t val);
  *
  ****************************************************************************/
 
-extern uint32_t nxffs_rdle32(FAR const uint8_t *val);
+uint32_t nxffs_rdle32(FAR const uint8_t *val);
 
 /****************************************************************************
  * Name: nxffs_wrle32
@@ -448,7 +448,7 @@ extern uint32_t nxffs_rdle32(FAR const uint8_t *val);
  *
  ****************************************************************************/
 
-extern void nxffs_wrle32(uint8_t *dest, uint32_t val);
+void nxffs_wrle32(uint8_t *dest, uint32_t val);
 
 /****************************************************************************
  * Name: nxffs_erased
@@ -467,7 +467,7 @@ extern void nxffs_wrle32(uint8_t *dest, uint32_t val);
  *
  ****************************************************************************/
 
-extern size_t nxffs_erased(FAR const uint8_t *buffer, size_t buflen);
+size_t nxffs_erased(FAR const uint8_t *buffer, size_t buflen);
 
 /****************************************************************************
  * Name: nxffs_rdcache
@@ -487,7 +487,7 @@ extern size_t nxffs_erased(FAR const uint8_t *buffer, size_t buflen);
  *
  ****************************************************************************/
 
-extern int nxffs_rdcache(FAR struct nxffs_volume_s *volume, off_t block);
+int nxffs_rdcache(FAR struct nxffs_volume_s *volume, off_t block);
 
 /****************************************************************************
  * Name: nxffs_wrcache
@@ -505,7 +505,7 @@ extern int nxffs_rdcache(FAR struct nxffs_volume_s *volume, off_t block);
  *
  ****************************************************************************/
 
-extern int nxffs_wrcache(FAR struct nxffs_volume_s *volume);
+int nxffs_wrcache(FAR struct nxffs_volume_s *volume);
 
 /****************************************************************************
  * Name: nxffs_ioseek
@@ -524,7 +524,7 @@ extern int nxffs_wrcache(FAR struct nxffs_volume_s *volume);
  *
  ****************************************************************************/
 
-extern void nxffs_ioseek(FAR struct nxffs_volume_s *volume, off_t offset);
+void nxffs_ioseek(FAR struct nxffs_volume_s *volume, off_t offset);
 
 /****************************************************************************
  * Name: nxffs_iotell
@@ -542,7 +542,7 @@ extern void nxffs_ioseek(FAR struct nxffs_volume_s *volume, off_t offset);
  *
  ****************************************************************************/
 
-extern off_t nxffs_iotell(FAR struct nxffs_volume_s *volume);
+off_t nxffs_iotell(FAR struct nxffs_volume_s *volume);
 
 /****************************************************************************
  * Name: nxffs_getc
@@ -566,7 +566,7 @@ extern off_t nxffs_iotell(FAR struct nxffs_volume_s *volume);
  *
  ****************************************************************************/
 
-extern int nxffs_getc(FAR struct nxffs_volume_s *volume, uint16_t reserve);
+int nxffs_getc(FAR struct nxffs_volume_s *volume, uint16_t reserve);
 
 /****************************************************************************
  * Name: nxffs_freeentry
@@ -590,7 +590,7 @@ extern int nxffs_getc(FAR struct nxffs_volume_s *volume, uint16_t reserve);
  *
  ****************************************************************************/
 
-extern void nxffs_freeentry(FAR struct nxffs_entry_s *entry);
+void nxffs_freeentry(FAR struct nxffs_entry_s *entry);
 
 /****************************************************************************
  * Name: nxffs_nextentry
@@ -612,8 +612,8 @@ extern void nxffs_freeentry(FAR struct nxffs_entry_s *entry);
  *
  ****************************************************************************/
 
-extern int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset,
-                           FAR struct nxffs_entry_s *entry);
+int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset,
+                    FAR struct nxffs_entry_s *entry);
 
 /****************************************************************************
  * Name: nxffs_findinode
@@ -636,9 +636,8 @@ extern int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset,
  *
  ****************************************************************************/
 
-extern int nxffs_findinode(FAR struct nxffs_volume_s *volume,
-                           FAR const char *name,
-                           FAR struct nxffs_entry_s *entry);
+int nxffs_findinode(FAR struct nxffs_volume_s *volume, FAR const char *name,
+                    FAR struct nxffs_entry_s *entry);
 
 /****************************************************************************
  * Name: nxffs_inodeend
@@ -664,8 +663,8 @@ extern int nxffs_findinode(FAR struct nxffs_volume_s *volume,
  *
  ****************************************************************************/
 
-extern off_t nxffs_inodeend(FAR struct nxffs_volume_s *volume,
-                            FAR struct nxffs_entry_s *entry);
+off_t nxffs_inodeend(FAR struct nxffs_volume_s *volume,
+                     FAR struct nxffs_entry_s *entry);
 
 /****************************************************************************
  * Name: nxffs_verifyblock
@@ -680,14 +679,19 @@ extern off_t nxffs_inodeend(FAR struct nxffs_volume_s *volume,
  *   block - The (logical) block number to load and verify.
  *
  * Returned Values:
- *   Zero is returned on success.  Otherwise, a negated errno value is
- *   returned indicating the nature of the failure.
+ *   OK (zero( is returned on success.  Otherwise, a negated errno value is
+ *   returned indicating the nature of the failure:
+ *
+ *     -EIO is returned if we failed to read the block.  If we are using
+ *        NAND memory, then this probably means that the block has
+ *        uncorrectable bit errors.
+ *     -ENOENT is returned if the block is a bad block.
  *
  * Defined in nxffs_block.c
  *
  ****************************************************************************/
 
-extern int nxffs_verifyblock(FAR struct nxffs_volume_s *volume, off_t block);
+int nxffs_verifyblock(FAR struct nxffs_volume_s *volume, off_t block);
 
 /****************************************************************************
  * Name: nxffs_validblock
@@ -709,7 +713,7 @@ extern int nxffs_verifyblock(FAR struct nxffs_volume_s *volume, off_t block);
  *
  ****************************************************************************/
 
-extern int nxffs_validblock(struct nxffs_volume_s *volume, off_t *block);
+int nxffs_validblock(struct nxffs_volume_s *volume, off_t *block);
 
 /****************************************************************************
  * Name: nxffs_blockstats
@@ -732,8 +736,8 @@ extern int nxffs_validblock(struct nxffs_volume_s *volume, off_t *block);
  *
  ****************************************************************************/
 
-extern int nxffs_blockstats(FAR struct nxffs_volume_s *volume,
-                            FAR struct nxffs_blkstats_s *stats);
+int nxffs_blockstats(FAR struct nxffs_volume_s *volume,
+                     FAR struct nxffs_blkstats_s *stats);
 
 /****************************************************************************
  * Name: nxffs_reformat
@@ -754,7 +758,27 @@ extern int nxffs_blockstats(FAR struct nxffs_volume_s *volume,
  *
  ****************************************************************************/
 
-extern int nxffs_reformat(FAR struct nxffs_volume_s *volume);
+int nxffs_reformat(FAR struct nxffs_volume_s *volume);
+
+/****************************************************************************
+ * Name: nxffs_blkinit
+ *
+ * Description:
+ *   Initialize an NXFFS block to the erased state with the specified block
+ *   status.
+ *
+ * Input Parameters:
+ *   volume - Describes the NXFFS volume (needed for the blocksize).
+ *   blkptr - Pointer to the logic block to initialize.
+ *   state  - Either BLOCK_STATE_GOOD or BLOCK_STATE_BAD.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void nxffs_blkinit(FAR struct nxffs_volume_s *volume, FAR uint8_t *blkptr,
+                   uint8_t state);
 
 /****************************************************************************
  * Name: nxffs_findofile
@@ -776,8 +800,8 @@ extern int nxffs_reformat(FAR struct nxffs_volume_s *volume);
  *
  ****************************************************************************/
 
-extern FAR struct nxffs_ofile_s *nxffs_findofile(FAR struct nxffs_volume_s *volume,
-                                                 FAR const char *name);
+FAR struct nxffs_ofile_s *nxffs_findofile(FAR struct nxffs_volume_s *volume,
+                                          FAR const char *name);
 
 /****************************************************************************
  * Name: nxffs_findwriter
@@ -797,7 +821,7 @@ extern FAR struct nxffs_ofile_s *nxffs_findofile(FAR struct nxffs_volume_s *volu
  *
  ****************************************************************************/
 
-extern FAR struct nxffs_wrfile_s *nxffs_findwriter(FAR struct nxffs_volume_s *volume);
+FAR struct nxffs_wrfile_s *nxffs_findwriter(FAR struct nxffs_volume_s *volume);
 
 /****************************************************************************
  * Name: nxffs_wrinode
@@ -823,8 +847,8 @@ extern FAR struct nxffs_wrfile_s *nxffs_findwriter(FAR struct nxffs_volume_s *vo
  *
  ****************************************************************************/
 
-extern int nxffs_wrinode(FAR struct nxffs_volume_s *volume,
-                         FAR struct nxffs_entry_s *entry);
+int nxffs_wrinode(FAR struct nxffs_volume_s *volume,
+                  FAR struct nxffs_entry_s *entry);
 
 /****************************************************************************
  * Name: nxffs_updateinode
@@ -843,8 +867,8 @@ extern int nxffs_wrinode(FAR struct nxffs_volume_s *volume,
  *
  ****************************************************************************/
 
-extern int nxffs_updateinode(FAR struct nxffs_volume_s *volume,
-                           FAR struct nxffs_entry_s *entry);
+int nxffs_updateinode(FAR struct nxffs_volume_s *volume,
+                      FAR struct nxffs_entry_s *entry);
 
 /****************************************************************************
  * Name: nxffs_wrreserve
@@ -884,7 +908,7 @@ extern int nxffs_updateinode(FAR struct nxffs_volume_s *volume,
  *
  ****************************************************************************/
 
-extern int nxffs_wrreserve(FAR struct nxffs_volume_s *volume, size_t size);
+int nxffs_wrreserve(FAR struct nxffs_volume_s *volume, size_t size);
 
 /****************************************************************************
  * Name: nxffs_wrverify
@@ -928,7 +952,7 @@ extern int nxffs_wrreserve(FAR struct nxffs_volume_s *volume, size_t size);
  *
  ****************************************************************************/
 
-extern int nxffs_wrverify(FAR struct nxffs_volume_s *volume, size_t size);
+int nxffs_wrverify(FAR struct nxffs_volume_s *volume, size_t size);
 
 /****************************************************************************
  * Name: nxffs_wrblkhdr
@@ -950,8 +974,8 @@ extern int nxffs_wrverify(FAR struct nxffs_volume_s *volume, size_t size);
  *
  ****************************************************************************/
 
-extern int nxffs_wrblkhdr(FAR struct nxffs_volume_s *volume,
-                          FAR struct nxffs_wrfile_s *wrfile);
+int nxffs_wrblkhdr(FAR struct nxffs_volume_s *volume,
+                   FAR struct nxffs_wrfile_s *wrfile);
 
 /****************************************************************************
  * Name: nxffs_nextblock
@@ -972,8 +996,8 @@ extern int nxffs_wrblkhdr(FAR struct nxffs_volume_s *volume,
  *
  ****************************************************************************/
 
-extern int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset,
-                           FAR struct nxffs_blkentry_s *blkentry);
+int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset,
+                    FAR struct nxffs_blkentry_s *blkentry);
 
 /****************************************************************************
  * Name: nxffs_rdblkhdr
@@ -995,8 +1019,8 @@ extern int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset,
  *
  ****************************************************************************/
 
-extern int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset,
-                          FAR uint16_t *datlen);
+int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset,
+                   FAR uint16_t *datlen);
 
 /****************************************************************************
  * Name: nxffs_rminode
@@ -1015,7 +1039,7 @@ extern int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset,
  *
  ****************************************************************************/
 
-extern int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name);
+int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name);
 
 /****************************************************************************
  * Name: nxffs_pack
@@ -1033,7 +1057,7 @@ extern int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name
  *
  ****************************************************************************/
 
-extern int nxffs_pack(FAR struct nxffs_volume_s *volume);
+int nxffs_pack(FAR struct nxffs_volume_s *volume);
 
 /****************************************************************************
  * Standard mountpoint operation methods
diff --git a/fs/nxffs/nxffs_block.c b/fs/nxffs/nxffs_block.c
index ace656ff2c..1a6a13adb3 100644
--- a/fs/nxffs/nxffs_block.c
+++ b/fs/nxffs/nxffs_block.c
@@ -83,9 +83,13 @@
  *   block - The (logical) block number to load and verify.
  *
  * Returned Values:
- *   Zero is returned on success.  Otherwise, a negated errno value is
- *   returned indicating the nature of the failure.  -ENOENT is returned
- *   if the block is a bad block.
+ *   OK (zero( is returned on success.  Otherwise, a negated errno value is
+ *   returned indicating the nature of the failure:
+ *
+ *     -EIO is returned if we failed to read the block.  If we are using
+ *        NAND memory, then this probably means that the block has
+ *        uncorrectable bit errors.
+ *     -ENOENT is returned if the block is a bad block.
  *
  ****************************************************************************/
 
@@ -102,7 +106,7 @@ int nxffs_verifyblock(FAR struct nxffs_volume_s *volume, off_t block)
       /* Perhaps we are at the end of the media */
 
       fdbg("ERROR: Failed to read data into cache: %d\n", ret);
-      return ret;
+      return -EIO;
     }
 
   /* Check if the block has a magic number (meaning that it is not
diff --git a/fs/nxffs/nxffs_cache.c b/fs/nxffs/nxffs_cache.c
index 71f31f892d..b5a1749db6 100644
--- a/fs/nxffs/nxffs_cache.c
+++ b/fs/nxffs/nxffs_cache.c
@@ -246,8 +246,13 @@ int nxffs_getc(FAR struct nxffs_volume_s *volume, uint16_t reserve)
       ret = nxffs_verifyblock(volume, volume->ioblock);
       if (ret < 0 && ret != -ENOENT)
         {
+          /* A read error occurred.  This probably means that we are
+           * using NAND memory this block has an uncorrectable bit error.
+           * Ignore the error (after complaining) and try the next
+           * block.
+           */
+
           fdbg("ERROR: Failed to read valid data into cache: %d\n", ret);
-          return ret;
         }
     }
   while (ret != OK);
diff --git a/fs/nxffs/nxffs_initialize.c b/fs/nxffs/nxffs_initialize.c
index 5c40b7e968..084f3254e8 100644
--- a/fs/nxffs/nxffs_initialize.c
+++ b/fs/nxffs/nxffs_initialize.c
@@ -157,7 +157,9 @@ int nxffs_initialize(FAR struct mtd_dev_s *mtd)
 {
   FAR struct nxffs_volume_s *volume;
   struct nxffs_blkstats_s stats;
+#ifdef CONFIG_NXFFS_SCAN_VOLUME
   off_t threshold;
+#endif
   int ret;
 
   /* If CONFIG_NXFFS_PREALLOCATED is defined, then this is the single, pre-
@@ -230,6 +232,7 @@ int nxffs_initialize(FAR struct mtd_dev_s *mtd)
   volume->nblocks = volume->geo.neraseblocks * volume->blkper;
   DEBUGASSERT((off_t)volume->blkper * volume->geo.blocksize == volume->geo.erasesize);
 
+#ifdef CONFIG_NXFFS_SCAN_VOLUME
   /* Check if there is a valid NXFFS file system on the flash */
 
   ret = nxffs_blockstats(volume, &stats);
@@ -243,7 +246,7 @@ int nxffs_initialize(FAR struct mtd_dev_s *mtd)
    * blocks is high, then reformat the FLASH.
    */
 
-  threshold = stats.nblocks / 5;
+  threshold = (stats.nblocks * CONFIG_NXFFS_REFORMAT_THRESH) / 100;
   if (stats.ngood < threshold || stats.nunformat > threshold)
     {
       /* Reformat the volume */
@@ -266,6 +269,7 @@ int nxffs_initialize(FAR struct mtd_dev_s *mtd)
         }
 #endif
     }
+#endif /* CONFIG_NXFFS_SCAN_VOLUME */
 
   /* Get the file system limits */
 
@@ -275,6 +279,37 @@ int nxffs_initialize(FAR struct mtd_dev_s *mtd)
       return OK;
     }
 
+  /* We may need to format the volume.  Try that before giving up. */
+
+  fdbg("WARNING: Failed to calculate file system limits: %d\n", -ret);
+  ret = nxffs_reformat(volume);
+  if (ret < 0)
+    {
+      fdbg("ERROR: Failed to reformat the volume: %d\n", -ret);
+      goto errout_with_buffer;
+    }
+
+  /* Get statistics on the re-formatted volume */
+
+#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
+  ret = nxffs_blockstats(volume, &stats);
+  if (ret < 0)
+    {
+      fdbg("ERROR: Failed to collect block statistics: %d\n", -ret);
+      goto errout_with_buffer;
+    }
+#endif
+
+  /* Now try to get the file system limits again */
+
+  ret = nxffs_limits(volume);
+  if (ret == OK)
+    {
+      return OK;
+    }
+
+  /* Now give up */
+
   fdbg("ERROR: Failed to calculate file system limits: %d\n", -ret);
 
 errout_with_buffer:
@@ -382,6 +417,7 @@ int nxffs_limits(FAR struct nxffs_volume_s *volume)
           offset = nxffs_inodeend(volume, &entry);
           nxffs_freeentry(&entry);
         }
+
       fvdbg("Last inode before offset %d\n", offset);
     }
 
@@ -412,6 +448,7 @@ int nxffs_limits(FAR struct nxffs_volume_s *volume)
                   volume->inoffset = volume->froffset;
                   fvdbg("No inodes, inoffset: %d\n", volume->inoffset);
                 }
+
               return OK;
             }
 
@@ -444,6 +481,7 @@ int nxffs_limits(FAR struct nxffs_volume_s *volume)
                   volume->inoffset = offset;
                   fvdbg("First inode at offset %d\n", volume->inoffset);
                 }
+
               return OK;
             }
         }
diff --git a/fs/nxffs/nxffs_pack.c b/fs/nxffs/nxffs_pack.c
index afbb55307e..d6e31896ec 100644
--- a/fs/nxffs/nxffs_pack.c
+++ b/fs/nxffs/nxffs_pack.c
@@ -1409,20 +1409,39 @@ start_pack:
        eblock < volume->geo.neraseblocks;
        eblock++)
     {
-      /* Read the erase block into the pack buffer.  We need to do this even
-       * if we are overwriting the entire block so that we skip over
-       * previously marked bad blocks.
-       */
+      /* Get the starting block number of the erase block */
 
       pack.block0 = eblock * volume->blkper;
-      ret = MTD_BREAD(volume->mtd, pack.block0, volume->blkper, volume->pack);
-      if (ret < 0)
+
+      /* Read the entire erase block into the pack buffer, one-block-at-a-
+       * time.  We need to do this even if we are overwriting the entire
+       * block so that (1) we skip over previously marked bad blocks, and
+       * (2) we can handle individual block read failures.
+       *
+       * For most FLASH, a read failure indicates a fatal hardware failure.
+       * But for NAND FLASH, the read failure probably indicates a block
+       * with uncorrectable bit errors.
+       */
+
+      /* Pack each I/O block */
+
+      for (i = 0, block = pack.block0, pack.iobuffer = volume->pack;
+           i < volume->blkper;
+           i++, block++, pack.iobuffer += volume->geo.blocksize)
         {
-          fdbg("ERROR: Failed to read erase block %d: %d\n", eblock, -ret);
-          goto errout_with_pack;
+          /* Read the next block in the erase block */
+
+          ret = MTD_BREAD(volume->mtd, block, 1, pack.iobuffer);
+          if (ret < 0)
+            {
+              /* Force a the block to be an NXFFS bad block */
+
+              fdbg("ERROR: Failed to read block %d: %d\n", block, ret);
+              nxffs_blkinit(volume, pack.iobuffer, BLOCK_STATE_BAD);
+            }
         }
 
-      /* Pack each I/O block */
+      /* Now ack each I/O block */
 
       for (i = 0, block = pack.block0, pack.iobuffer = volume->pack;
            i < volume->blkper;
diff --git a/fs/nxffs/nxffs_reformat.c b/fs/nxffs/nxffs_reformat.c
index 294414d406..fd33dd3b23 100644
--- a/fs/nxffs/nxffs_reformat.c
+++ b/fs/nxffs/nxffs_reformat.c
@@ -92,14 +92,11 @@ static int nxffs_format(FAR struct nxffs_volume_s *volume)
 
   /* Create an image of one properly formatted erase sector */
 
-  memset(volume->pack, CONFIG_NXFFS_ERASEDSTATE, volume->geo.erasesize);
   for (blkptr = volume->pack, i = 0;
        i < volume->blkper;
        blkptr += volume->geo.blocksize, i++)
     {
-      FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr;
-      memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE);
-      blkhdr->state = BLOCK_STATE_GOOD;
+      nxffs_blkinit(volume, blkptr, BLOCK_STATE_GOOD);
     }
 
   /* Erase and format each erase block */
@@ -149,7 +146,8 @@ static int nxffs_badblocks(FAR struct nxffs_volume_s *volume)
 {
   FAR uint8_t *blkptr;   /* Pointer to next block data */
   off_t eblock;          /* Erase block number */
-  off_t lblock;          /* Logical block number */
+  off_t lblock;          /* Logical block number of the erase block */
+  off_t block;           /* Working block number */
   ssize_t nxfrd;         /* Number of blocks transferred */
   bool good;             /* TRUE: block is good */
   bool modified;         /* TRUE: The erase block has been modified */
@@ -159,31 +157,50 @@ static int nxffs_badblocks(FAR struct nxffs_volume_s *volume)
 
   for (eblock = 0; eblock < volume->geo.neraseblocks; eblock++)
     {
-      /* Read the entire erase block */
+      /* Get the logical block number of the erase block */
 
       lblock = eblock * volume->blkper;
-      nxfrd  = MTD_BREAD(volume->mtd, lblock, volume->blkper, volume->pack);
-      if (nxfrd != volume->blkper)
-        {
-          fdbg("ERROR: Read erase block %d failed: %d\n", lblock, nxfrd);
-          return -EIO;
-        }
 
-      /* Process each logical block */
+      /* Read the entire erase block into memory, processing each block one-
+       * at-a-time.  We could read the entire erase block at once.  That
+       * would be more efficient.  However, for the case of NAND FLASH, each
+       * individual block can fail independently due to uncorrectable bit
+       * errors in that block.
+       */
 
       modified = false;
-      for (blkptr = volume->pack, i = 0;
+      for (i = 0, block = lblock, blkptr = volume->pack;
            i < volume->blkper;
-           blkptr += volume->geo.blocksize, i++)
+           i++, block++, blkptr += volume->geo.blocksize)
         {
           FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr;
 
-          /* Check block header */
+          /* Read the next block in the erase block */
 
-          good = true;
-          if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0 ||
-              blkhdr->state != BLOCK_STATE_GOOD)
+          good  = true;
+          nxfrd = MTD_BREAD(volume->mtd, block, i, blkptr);
+          if (nxfrd < 0)
             {
+              /* Failed to read the block.  This should never happen with
+               * most FLASH.  However, for NAND this probably means that we
+               * read a block with uncorrectable bit errors.
+               */
+
+              fdbg("ERROR: Failed to read block %d: %d\n",
+                   block, (int)nxfrd);
+
+              good = false;
+            }
+
+          /* Check the block header */
+
+          else if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0 ||
+                          blkhdr->state != BLOCK_STATE_GOOD)
+            {
+              /* The block is not formated with the NXFFS magic bytes or else
+               * the block is specifically marked bad.
+               */
+
               good = false;
             }
 
@@ -196,15 +213,14 @@ static int nxffs_badblocks(FAR struct nxffs_volume_s *volume)
               good = (blocksize == erasesize);
             }
 
-          /* If the block is bad, attempt to re-write the block header indicating
-           * a bad block (of course, if the block has failed, this may not be
-           * possible, depending upon failure modes.
+          /* If the block is bad, attempt to re-write the block header
+           * indicating a bad block (of course, if the block has failed,
+           * this may not be possible, depending upon failure modes.
            */
 
           if (!good)
             {
-              memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE);
-              blkhdr->state = BLOCK_STATE_BAD;
+              nxffs_blkinit(volume, blkptr, BLOCK_STATE_BAD);
               modified = true;
             }
         }
@@ -269,3 +285,30 @@ int nxffs_reformat(FAR struct nxffs_volume_s *volume)
 
   return ret;
 }
+
+/****************************************************************************
+ * Name: nxffs_blkinit
+ *
+ * Description:
+ *   Initialize an NXFFS block to the erased state with the specified block
+ *   status.
+ *
+ * Input Parameters:
+ *   volume - Describes the NXFFS volume (needed for the blocksize).
+ *   blkptr - Pointer to the logic block to initialize.
+ *   state  - Either BLOCK_STATE_GOOD or BLOCK_STATE_BAD.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void nxffs_blkinit(FAR struct nxffs_volume_s *volume, FAR uint8_t *blkptr,
+                   uint8_t state)
+{
+  FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr;
+
+  memset(blkptr, CONFIG_NXFFS_ERASEDSTATE, volume->geo.blocksize);
+  memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE);
+  blkhdr->state = state;
+}
\ No newline at end of file
diff --git a/fs/nxffs/nxffs_unlink.c b/fs/nxffs/nxffs_unlink.c
index 6970d67a08..65838846d2 100644
--- a/fs/nxffs/nxffs_unlink.c
+++ b/fs/nxffs/nxffs_unlink.c
@@ -127,7 +127,8 @@ int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name)
   ret = nxffs_rdcache(volume, volume->ioblock);
   if (ret < 0)
     {
-      fdbg("ERROR: Failed to read data into cache: %d\n", ret);
+      fdbg("ERROR: Failed to read block %d into cache: %d\n",
+           volume->ioblock, ret);
       goto errout_with_entry;
     }
 
@@ -141,7 +142,8 @@ int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name)
   ret = nxffs_wrcache(volume);
   if (ret < 0)
     {
-      fdbg("ERROR: Failed to read data into cache: %d\n", ret);
+      fdbg("ERROR: Failed to write block %d: %d\n",
+           volume->ioblock, ret);
     }
 
 errout_with_entry:
diff --git a/fs/nxffs/nxffs_write.c b/fs/nxffs/nxffs_write.c
index 0b1e27ccd4..29f9726974 100644
--- a/fs/nxffs/nxffs_write.c
+++ b/fs/nxffs/nxffs_write.c
@@ -705,52 +705,64 @@ int nxffs_wrverify(FAR struct nxffs_volume_s *volume, size_t size)
       ret = nxffs_rdcache(volume, volume->ioblock);
       if (ret < 0)
         {
-          fdbg("ERROR: Failed to read block %d: %d\n", volume->ioblock, -ret);
-          return ret;
+          /* Ignore the error... just skip to the next block.  This should
+           * never happen with normal FLASH, but could occur with NAND if
+           * the block has uncorrectable bit errors.
+           */
+
+          fdbg("ERROR: Failed to read block %d: %d\n",
+               volume->ioblock, -ret);
         }
 
       /* Search to the very end of this block if we have to */
 
-      iooffset = volume->iooffset;
-      nerased  = 0;
-
-      for (i = volume->iooffset; i < volume->geo.blocksize; i++)
+      else
         {
-          /* Is this byte erased? */
+          iooffset = volume->iooffset;
+          nerased  = 0;
 
-          if (volume->cache[i] == CONFIG_NXFFS_ERASEDSTATE)
+          for (i = volume->iooffset; i < volume->geo.blocksize; i++)
             {
-              /* Yes.. increment the count of contiguous, erased bytes */
+              /* Is this byte erased? */
 
-              nerased++;
+              if (volume->cache[i] == CONFIG_NXFFS_ERASEDSTATE)
+                {
+                  /* Yes.. increment the count of contiguous, erased bytes */
 
-              /* Is the whole header memory erased? */
+                  nerased++;
 
-              if (nerased >= size)
-                {
-                   /* Yes.. this this is where we will put the object */
+                  /* Is the whole header memory erased? */
 
-                   off_t offset = volume->ioblock * volume->geo.blocksize + iooffset;
+                  if (nerased >= size)
+                    {
+                       /* Yes.. this this is where we will put the object */
 
-                   /* Update the free flash offset and return success */
+                       off_t offset =
+                         volume->ioblock * volume->geo.blocksize + iooffset;
 
-                   volume->froffset = offset + size;
-                   return OK;
+                       /* Update the free flash offset and return success */
+
+                       volume->froffset = offset + size;
+                       return OK;
+                    }
                 }
-            }
 
-          /* This byte is not erased!  (It should be unless the block is bad) */
+              /* This byte is not erased!  (It should be unless the block is
+               * bad)
+               */
 
-          else
-            {
-              nerased  = 0;
-              iooffset = i + 1;
+              else
+                {
+                  nerased  = 0;
+                  iooffset = i + 1;
+                }
             }
         }
 
-      /* If we get here, then we have looked at every byte in the block
-       * and did not find any sequence of erased bytes long enough to hold
-       * the object.  Skip to the next, valid block.
+      /* If we get here, then either (1) this block is not read-able, or
+       * (2) we have looked at every byte in the block and did not find
+       * any sequence of erased bytes long enough to hold the object.
+       * Skip to the next, valid block.
        */
 
       volume->ioblock++;
diff --git a/include/nuttx/fs/nxffs.h b/include/nuttx/fs/nxffs.h
index 79c5b1d945..60c3af1e93 100644
--- a/include/nuttx/fs/nxffs.h
+++ b/include/nuttx/fs/nxffs.h
@@ -1,7 +1,7 @@
 /****************************************************************************
  * include/nuttx/fs/nxffs.h
  *
- *   Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ *   Copyright (C) 2011-2013 Gregory Nutt. All rights reserved.
  *   Author: Gregory Nutt <gnutt@nuttx.org>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -94,6 +94,19 @@
 #undef CONFIG_NXFSS_PREALLOCATED
 #define CONFIG_NXFSS_PREALLOCATED 1
 
+/* If we were asked to scan the volume, then a re-formatting threshold must
+ * also be provided.
+ */
+
+#ifdef CONFIG_NXFFS_SCAN_VOLUME
+#  ifndef CONFIG_NXFFS_REFORMAT_THRESH
+#    define CONFIG_NXFFS_REFORMAT_THRESH 20
+#  endif
+#  if CONFIG_NXFFS_REFORMAT_THRESH < 0 || CONFIG_NXFFS_REFORMAT_THRESH > 100
+#    error CONFIG_NXFFS_REFORMAT_THRESH is not a valid percentage
+#  endif
+#endif
+
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/
-- 
GitLab