fsck.f2fs: fix summary block
Previously, if data and node summary was corrupted, the block was deallocated.
But, this patch fixes their summary first, so that we can keep their blocks.
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
diff --git a/fsck/fsck.c b/fsck/fsck.c
index a3029ba..ecb18f2 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -129,66 +129,137 @@
static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid,
u32 blk_addr)
{
- int ret = 0;
- struct f2fs_summary sum_entry;
+ struct f2fs_summary_block *sum_blk;
+ struct f2fs_summary *sum_entry;
+ u32 segno, offset;
+ int need_fix = 0, ret = 0;
+ int type;
- ret = get_sum_entry(sbi, blk_addr, &sum_entry);
+ segno = GET_SEGNO(sbi, blk_addr);
+ offset = OFFSET_IN_SEG(sbi, blk_addr);
- if (ret != SEG_TYPE_NODE && ret != SEG_TYPE_CUR_NODE) {
- ASSERT_MSG("Summary footer is not for node segment");
- return -EINVAL;
+ sum_blk = get_sum_block(sbi, segno, &type);
+
+ if (type != SEG_TYPE_NODE && type != SEG_TYPE_CUR_NODE) {
+ /* can't fix current summary, then drop the block */
+ if (!config.fix_on || type < 0) {
+ ASSERT_MSG("Summary footer is not for node segment");
+ ret = -EINVAL;
+ goto out;
+ }
+ FIX_MSG("Summary footer indicates a node segment: 0x%x", segno);
+ sum_blk->footer.entry_type = SUM_TYPE_NODE;
+ need_fix = 1;
}
- if (le32_to_cpu(sum_entry.nid) != nid) {
- DBG(0, "nid [0x%x]\n", nid);
- DBG(0, "target blk_addr [0x%x]\n", blk_addr);
- DBG(0, "summary blk_addr [0x%x]\n",
- GET_SUM_BLKADDR(sbi,
- GET_SEGNO(sbi, blk_addr)));
- DBG(0, "seg no / offset [0x%x / 0x%x]\n",
- GET_SEGNO(sbi, blk_addr),
- OFFSET_IN_SEG(sbi, blk_addr));
- DBG(0, "summary_entry.nid [0x%x]\n",
- le32_to_cpu(sum_entry.nid));
- DBG(0, "--> node block's nid [0x%x]\n", nid);
- ASSERT_MSG("Invalid node seg summary\n");
- return -EINVAL;
+ sum_entry = &(sum_blk->entries[offset]);
+
+ if (le32_to_cpu(sum_entry->nid) != nid) {
+ if (!config.fix_on || type < 0) {
+ DBG(0, "nid [0x%x]\n", nid);
+ DBG(0, "target blk_addr [0x%x]\n", blk_addr);
+ DBG(0, "summary blk_addr [0x%x]\n",
+ GET_SUM_BLKADDR(sbi,
+ GET_SEGNO(sbi, blk_addr)));
+ DBG(0, "seg no / offset [0x%x / 0x%x]\n",
+ GET_SEGNO(sbi, blk_addr),
+ OFFSET_IN_SEG(sbi, blk_addr));
+ DBG(0, "summary_entry.nid [0x%x]\n",
+ le32_to_cpu(sum_entry->nid));
+ DBG(0, "--> node block's nid [0x%x]\n", nid);
+ ASSERT_MSG("Invalid node seg summary\n");
+ ret = -EINVAL;
+ } else {
+ FIX_MSG("Set node summary 0x%x -> [0x%x] [0x%x]",
+ segno, nid, blk_addr);
+ sum_entry->nid = cpu_to_le32(nid);
+ need_fix = 1;
+ }
}
- return 0;
+ if (need_fix) {
+ u64 ssa_blk;
+ int ret2;
+
+ ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+ ret2 = dev_write_block(sum_blk, ssa_blk);
+ ASSERT(ret2 >= 0);
+ }
+out:
+ if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+ type == SEG_TYPE_MAX)
+ free(sum_blk);
+ return ret;
}
static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
u32 parent_nid, u16 idx_in_node, u8 version)
{
- int ret = 0;
- struct f2fs_summary sum_entry;
+ struct f2fs_summary_block *sum_blk;
+ struct f2fs_summary *sum_entry;
+ u32 segno, offset;
+ int need_fix = 0, ret = 0;
+ int type;
- ret = get_sum_entry(sbi, blk_addr, &sum_entry);
+ segno = GET_SEGNO(sbi, blk_addr);
+ offset = OFFSET_IN_SEG(sbi, blk_addr);
- if (ret != SEG_TYPE_DATA && ret != SEG_TYPE_CUR_DATA) {
- ASSERT_MSG("Summary footer is not for data segment");
- return -EINVAL;
+ sum_blk = get_sum_block(sbi, segno, &type);
+
+ if (type != SEG_TYPE_DATA && type != SEG_TYPE_CUR_DATA) {
+ /* can't fix current summary, then drop the block */
+ if (!config.fix_on || type < 0) {
+ ASSERT_MSG("Summary footer is not for data segment");
+ ret = -EINVAL;
+ goto out;
+ }
+ FIX_MSG("Summary footer indicates a data segment: 0x%x", segno);
+ sum_blk->footer.entry_type = SUM_TYPE_DATA;
+ need_fix = 1;
}
- if (le32_to_cpu(sum_entry.nid) != parent_nid ||
- sum_entry.version != version ||
- le16_to_cpu(sum_entry.ofs_in_node) != idx_in_node) {
+ sum_entry = &(sum_blk->entries[offset]);
- DBG(0, "summary_entry.nid [0x%x]\n",
- le32_to_cpu(sum_entry.nid));
- DBG(0, "summary_entry.version [0x%x]\n",
- sum_entry.version);
- DBG(0, "summary_entry.ofs_in_node [0x%x]\n",
- le16_to_cpu(sum_entry.ofs_in_node));
- DBG(0, "parent nid [0x%x]\n", parent_nid);
- DBG(0, "version from nat [0x%x]\n", version);
- DBG(0, "idx in parent node [0x%x]\n", idx_in_node);
+ if (le32_to_cpu(sum_entry->nid) != parent_nid ||
+ sum_entry->version != version ||
+ le16_to_cpu(sum_entry->ofs_in_node) != idx_in_node) {
+ if (!config.fix_on || type < 0) {
+ DBG(0, "summary_entry.nid [0x%x]\n",
+ le32_to_cpu(sum_entry->nid));
+ DBG(0, "summary_entry.version [0x%x]\n",
+ sum_entry->version);
+ DBG(0, "summary_entry.ofs_in_node [0x%x]\n",
+ le16_to_cpu(sum_entry->ofs_in_node));
+ DBG(0, "parent nid [0x%x]\n",
+ parent_nid);
+ DBG(0, "version from nat [0x%x]\n", version);
+ DBG(0, "idx in parent node [0x%x]\n",
+ idx_in_node);
- DBG(0, "Target data block addr [0x%x]\n", blk_addr);
- ASSERT_MSG("Invalid data seg summary\n");
- return -EINVAL;
+ DBG(0, "Target data block addr [0x%x]\n", blk_addr);
+ ASSERT_MSG("Invalid data seg summary\n");
+ ret = -EINVAL;
+ } else {
+ FIX_MSG("Set data summary 0x%x -> [0x%x] [0x%x] [0x%x]",
+ segno, parent_nid, version, idx_in_node);
+ sum_entry->nid = cpu_to_le32(parent_nid);
+ sum_entry->version = version;
+ sum_entry->ofs_in_node = cpu_to_le16(idx_in_node);
+ need_fix = 1;
+ }
}
- return 0;
+ if (need_fix) {
+ u64 ssa_blk;
+ int ret2;
+
+ ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+ ret2 = dev_write_block(sum_blk, ssa_blk);
+ ASSERT(ret2 >= 0);
+ }
+out:
+ if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+ type == SEG_TYPE_MAX)
+ free(sum_blk);
+ return ret;
}
static int __check_inode_mode(u32 nid, enum FILE_TYPE ftype, u32 mode)
@@ -929,7 +1000,6 @@
ASSERT_MSG("Duplicated data [0x%x]. pnid[0x%x] idx[0x%x]",
blk_addr, parent_nid, idx_in_node);
-
fsck->chk.valid_blk_cnt++;
if (ftype == F2FS_FT_DIR) {