summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhe Li <lizhe67@huawei.com>2020-05-29 11:37:11 +0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-12-29 13:42:44 +0100
commitb644402f029bf69d8741310bc5558c2c68db4696 (patch)
treefd536c4371e71725bdfe41c7823e0f95f9d4b08e
parent89374c84459e05522fe2058b8235f7c21ec0ba84 (diff)
downloadlinux-stable-b644402f029bf69d8741310bc5558c2c68db4696.tar.gz
linux-stable-b644402f029bf69d8741310bc5558c2c68db4696.tar.bz2
linux-stable-b644402f029bf69d8741310bc5558c2c68db4696.zip
jffs2: Fix GC exit abnormally
commit 9afc9a8a4909fece0e911e72b1060614ba2f7969 upstream. The log of this problem is: jffs2: Error garbage collecting node at 0x***! jffs2: No space for garbage collection. Aborting GC thread This is because GC believe that it do nothing, so it abort. After going over the image of jffs2, I find a scene that can trigger this problem stably. The scene is: there is a normal dirent node at summary-area, but abnormal at corresponding not-summary-area with error name_crc. The reason that GC exit abnormally is because it find that abnormal dirent node to GC, but when it goes to function jffs2_add_fd_to_list, it cannot meet the condition listed below: if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) So no node is marked obsolete, statistical information of erase_block do not change, which cause GC exit abnormally. The root cause of this problem is: we do not check the name_crc of the abnormal dirent node with summary is enabled. Noticed that in function jffs2_scan_dirent_node, we use function jffs2_scan_dirty_space to deal with the dirent node with error name_crc. So this patch add a checking code in function read_direntry to ensure the correctness of dirent node. If checked failed, the dirent node will be marked obsolete so GC will pass this node and this problem will be fixed. Cc: <stable@vger.kernel.org> Signed-off-by: Zhe Li <lizhe67@huawei.com> Signed-off-by: Richard Weinberger <richard@nod.at> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/jffs2/readinode.c16
1 files changed, 16 insertions, 0 deletions
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index 5b52ea41b84f..bee8964682f8 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -672,6 +672,22 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r
jffs2_free_full_dirent(fd);
return -EIO;
}
+
+#ifdef CONFIG_JFFS2_SUMMARY
+ /*
+ * we use CONFIG_JFFS2_SUMMARY because without it, we
+ * have checked it while mounting
+ */
+ crc = crc32(0, fd->name, rd->nsize);
+ if (unlikely(crc != je32_to_cpu(rd->name_crc))) {
+ JFFS2_NOTICE("name CRC failed on dirent node at"
+ "%#08x: read %#08x,calculated %#08x\n",
+ ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
+ jffs2_mark_node_obsolete(c, ref);
+ jffs2_free_full_dirent(fd);
+ return 0;
+ }
+#endif
}
fd->nhash = full_name_hash(fd->name, rd->nsize);