NOR Flash and other non-writebuffered devices write directly through
jffs2_flash_direct_writev() and jffs2_flash_direct_write() without
any write-back verification. If mtd_write() succeeds but the readable
medium differs from JFFS2's source buffer, a later node CRC failure
cannot distinguish transport/program-time corruption from post-commit
media damage.

Move jffs2_verify_write() from wbuf.c to writev.c so it can be shared
by both writebuffered and direct write paths. Add jffs2_verify_writev()
to iterate over kvec entries and verify each one individually. In both
direct write functions, add mtd_write() return value and retlen checks,
and invoke verification after a successful complete write.

In jffs2_flash_direct_writev(), move the mtd_writev() call before
jffs2_sum_add_kvec() so that *retlen is always set by the MTD layer
first. The original ordering let jffs2_sum_add_kvec() return early on
error without ever touching *retlen, leaving the caller's retlen check
to read an uninitialized value. Keep the same order in
jffs2_flash_direct_write().

Signed-off-by: zhouminqiang <[email protected]>
---
 fs/jffs2/os-linux.h |  11 ++++
 fs/jffs2/wbuf.c     |  70 -------------------------
 fs/jffs2/writev.c   | 121 +++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 131 insertions(+), 71 deletions(-)

diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 86ab014a349c..e73ef643fd97 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -192,6 +192,17 @@ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, 
const struct kvec *vecs,
 int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
                        size_t *retlen, const u_char *buf);
 
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+int jffs2_verify_write(struct jffs2_sb_info *c, const unsigned char *buf,
+                      uint32_t ofs, size_t len);
+int jffs2_verify_writev(struct jffs2_sb_info *c,
+                      const struct kvec *vecs,
+                      unsigned long count, loff_t to);
+#else
+#define jffs2_verify_write(c, b, o, l) (0)
+#define jffs2_verify_writev(c, v, cnt, t) (0)
+#endif
+
 #endif /* __JFFS2_OS_LINUX_H__ */
 
 
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index 81f3538ca258..2f4937951a0c 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -226,76 +226,6 @@ static struct jffs2_raw_node_ref 
**jffs2_incore_replace_raw(struct jffs2_sb_info
        return NULL;
 }
 
-#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
-static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
-                             uint32_t ofs, size_t len)
-{
-       int ret;
-       size_t retlen, i;
-       char *eccstr;
-       void *verify_buf;
-
-       verify_buf = kmalloc(len, GFP_NOFS);
-       if (!verify_buf) {
-               pr_warn("%s(): verify buffer allocation failed, skipping 
verification\n",
-                       __func__);
-               return 0;
-       }
-
-       ret = mtd_read(c->mtd, ofs, len, &retlen, verify_buf);
-
-       if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
-               pr_warn("%s(): Read back of page at %08x failed: %d\n",
-                       __func__, ofs, ret);
-               goto out_free;
-       } else if (retlen != len) {
-               pr_warn("%s(): Read back of page at %08x gave short read: %zu 
not %zu\n",
-                       __func__, ofs, retlen, len);
-               ret = -EIO;
-               goto out_free;
-       }
-       if (!memcmp(buf, verify_buf, len)) {
-               ret = 0;
-               goto out_free;
-       }
-
-       for (i = 0; i < len; i++) {
-               uint8_t c1 = ((uint8_t *)buf)[i];
-               uint8_t c2 = ((uint8_t *)verify_buf)[i];
-               int dump_len;
-
-               if (c1 == c2)
-                       continue;
-
-               if (ret == -EUCLEAN)
-                       eccstr = "corrected";
-               else if (ret == -EBADMSG)
-                       eccstr = "correction failed";
-               else
-                       eccstr = "OK or unused";
-
-               dump_len = min_t(int, 128, len - i);
-               pr_warn("Write verify error (ECC %s) at %08x (+%zu/%zu). 
Wrote:\n",
-                       eccstr, ofs, i, len);
-               print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
-                              buf + i, dump_len, 0);
-
-               pr_warn("Read back:\n");
-               print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
-                              verify_buf + i, dump_len, 0);
-
-               ret = -EIO;
-               goto out_free;
-       }
-
-out_free:
-       kfree(verify_buf);
-       return ret;
-}
-#else
-#define jffs2_verify_write(c,b,o,l) (0)
-#endif
-
 /* Recover from failure to write wbuf. Recover the nodes up to the
  * wbuf, not the one which we were starting to try to write. */
 
diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c
index a1bda9dab3f8..2cb8cb030ae3 100644
--- a/fs/jffs2/writev.c
+++ b/fs/jffs2/writev.c
@@ -10,12 +10,121 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/mtd/mtd.h>
 #include "nodelist.h"
 
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+int jffs2_verify_write(struct jffs2_sb_info *c, const unsigned char *buf,
+                             uint32_t ofs, size_t len)
+{
+       int ret;
+       size_t retlen, i;
+       char *eccstr;
+       void *verify_buf;
+
+       verify_buf = kmalloc(len, GFP_NOFS);
+       if (!verify_buf) {
+               pr_warn("%s(): verify buffer allocation failed, skipping 
verification\n",
+                       __func__);
+               return 0;
+       }
+
+       ret = mtd_read(c->mtd, ofs, len, &retlen, verify_buf);
+
+       if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
+               pr_warn("%s(): Read back of page at %08x failed: %d\n",
+                       __func__, ofs, ret);
+               goto out_free;
+       } else if (retlen != len) {
+               pr_warn("%s(): Read back of page at %08x gave short read: %zu 
not %zu\n",
+                       __func__, ofs, retlen, len);
+               ret = -EIO;
+               goto out_free;
+       }
+       if (!memcmp(buf, verify_buf, len)) {
+               ret = 0;
+               goto out_free;
+       }
+
+       for (i = 0; i < len; i++) {
+               uint8_t c1 = ((uint8_t *)buf)[i];
+               uint8_t c2 = ((uint8_t *)verify_buf)[i];
+               int dump_len;
+
+               if (c1 == c2)
+                       continue;
+
+               if (ret == -EUCLEAN)
+                       eccstr = "corrected";
+               else if (ret == -EBADMSG)
+                       eccstr = "correction failed";
+               else
+                       eccstr = "OK or unused";
+
+               dump_len = min_t(int, 128, len - i);
+               pr_warn("Write verify error (ECC %s) at %08x (+%zu/%zu). 
Wrote:\n",
+                       eccstr, ofs, i, len);
+               print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+                              buf + i, dump_len, 0);
+
+               pr_warn("Read back:\n");
+               print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+                              verify_buf + i, dump_len, 0);
+
+               ret = -EIO;
+               goto out_free;
+       }
+
+out_free:
+       kfree(verify_buf);
+       return ret;
+}
+
+int jffs2_verify_writev(struct jffs2_sb_info *c,
+                       const struct kvec *vecs,
+                       unsigned long count, loff_t to)
+{
+       loff_t ofs = to;
+       unsigned long i;
+       int ret;
+
+       for (i = 0; i < count; i++) {
+               if (!vecs[i].iov_len)
+                       continue;
+               ret = jffs2_verify_write(c, vecs[i].iov_base, ofs,
+                                        vecs[i].iov_len);
+               if (ret)
+                       return ret;
+               ofs += vecs[i].iov_len;
+       }
+       return 0;
+}
+#endif /* CONFIG_JFFS2_FS_WBUF_VERIFY */
+
 int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
                              unsigned long count, loff_t to, size_t *retlen)
 {
+       int ret;
+
+       ret = mtd_writev(c->mtd, vecs, count, to, retlen);
+
+       if (ret) {
+               pr_warn("%s(): Write failed with %d\n", __func__, ret);
+       } else {
+               size_t totlen = 0;
+               unsigned long i;
+
+               for (i = 0; i < count; i++)
+                       totlen += vecs[i].iov_len;
+               if (*retlen != totlen) {
+                       pr_warn("%s(): Write was short: %zu instead of %zu\n",
+                               __func__, *retlen, totlen);
+                       ret = -EIO;
+               } else
+                       ret = jffs2_verify_writev(c, vecs, count, to);
+       }
+
        if (!jffs2_is_writebuffered(c)) {
                if (jffs2_sum_active()) {
                        int res;
@@ -26,15 +135,25 @@ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, 
const struct kvec *vecs,
                }
        }
 
-       return mtd_writev(c->mtd, vecs, count, to, retlen);
+       return ret;
 }
 
 int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
                        size_t *retlen, const u_char *buf)
 {
        int ret;
+
        ret = mtd_write(c->mtd, ofs, len, retlen, buf);
 
+       if (ret) {
+               pr_warn("%s(): Write failed with %d\n", __func__, ret);
+       } else if (*retlen != len) {
+               pr_warn("%s(): Write was short: %zu instead of %zu\n",
+                       __func__, *retlen, len);
+               ret = -EIO;
+       } else
+               ret = jffs2_verify_write(c, buf, ofs, len);
+
        if (jffs2_sum_active()) {
                struct kvec vecs[1];
                int res;
-- 
2.52.0


Reply via email to