linux-f2fs-devel.lists.sourceforge.net archive mirror
 help / color / mirror / Atom feed
From: Sheng Yong <shengyong1@huawei.com>
To: jaegeuk@kernel.org, yuchao0@huawei.com
Cc: hyojun@google.com, miaoxie@huawei.com,
	linux-f2fs-devel@lists.sourceforge.net
Subject: [RFC PATCH v2 7/7] fsck.f2fs: reconnect unreachable files to lost+found
Date: Fri, 23 Feb 2018 11:18:01 +0800	[thread overview]
Message-ID: <20180223031801.231805-8-shengyong1@huawei.com> (raw)
In-Reply-To: <20180223031801.231805-1-shengyong1@huawei.com>

This patch introduces lost_found feature to fsck. If a file is found
unreachable by fsck. Fsck tries to reconnect the file to lost+found
directory:
  1. Scan all unreachable file inodes, ignore non-inodes ones and
     directories.
  2. Check them and fix incorrupted data to make sure filesystem
     metadata (mainly counters and main/nat bitmap) are all consistent.
  3. Reconnect these files to lost+found. If lost+found does not exist,
     create it first. During reconnecting, expand lost+found's dentry
     block automatically. Reconnected files are renamed after its ino
     number.
  4. If reconnect fails drop the node and restore filesystem metadata.

Signed-off-by: Sheng Yong <shengyong1@huawei.com>
---
 fsck/dir.c   |  19 ++-
 fsck/fsck.c  | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 fsck/fsck.h  |   3 +
 fsck/mount.c |   2 +
 4 files changed, 409 insertions(+), 3 deletions(-)

diff --git a/fsck/dir.c b/fsck/dir.c
index b2ea18f..567a4e9 100644
--- a/fsck/dir.c
+++ b/fsck/dir.c
@@ -176,6 +176,23 @@ static int f2fs_find_entry(struct f2fs_sb_info *sbi,
 	return 0;
 }
 
+/* return ino if file exists, otherwise return 0 */
+nid_t f2fs_lookup(struct f2fs_sb_info *sbi, struct f2fs_node *dir,
+				u8 *name, int len)
+{
+	int err;
+	struct dentry de = {
+		.name = name,
+		.len = len,
+	};
+
+	err = f2fs_find_entry(sbi, dir, &de);
+	if (err == 1)
+		return de.ino;
+	else
+		return 0;
+}
+
 static void f2fs_update_dentry(nid_t ino, int file_type,
 		struct f2fs_dentry_ptr *d,
 		const unsigned char *name, int len, f2fs_hash_t name_hash,
@@ -199,7 +216,7 @@ static void f2fs_update_dentry(nid_t ino, int file_type,
 /*
  * f2fs_add_link - Add a new file(dir) to parent dir.
  */
-static int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent,
+int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent,
 			const unsigned char *name, int name_len, nid_t ino,
 			int file_type, block_t p_blkaddr, int inc_link)
 {
diff --git a/fsck/fsck.c b/fsck/fsck.c
index f6391e9..9a822ef 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -10,6 +10,7 @@
  */
 #include "fsck.h"
 #include "quotaio.h"
+#include <time.h>
 
 char *tree_mark;
 uint32_t tree_mark_size = 256;
@@ -43,6 +44,14 @@ static inline int f2fs_test_main_bitmap(struct f2fs_sb_info *sbi, u32 blk)
 						fsck->main_area_bitmap);
 }
 
+static inline int f2fs_clear_main_bitmap(struct f2fs_sb_info *sbi, u32 blk)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+
+	return f2fs_clear_bit(BLKOFF_FROM_MAIN(sbi, blk),
+						fsck->main_area_bitmap);
+}
+
 static inline int f2fs_test_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk)
 {
 	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
@@ -453,9 +462,11 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid,
 
 	/* workaround to fix later */
 	if (ftype != F2FS_FT_ORPHAN ||
-			f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0)
+			f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0) {
 		f2fs_clear_bit(nid, fsck->nat_area_bitmap);
-	else
+		/* avoid reusing nid when reconnecting files */
+		f2fs_set_bit(nid, NM_I(sbi)->nid_bitmap);
+	} else
 		ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n",
 				nid);
 
@@ -2046,6 +2057,369 @@ int check_sit_types(struct f2fs_sb_info *sbi)
 	return err;
 }
 
+static struct f2fs_node *fsck_get_lpf(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_node *node;
+	struct node_info ni;
+	nid_t lpf_ino;
+	int err;
+
+	/* read root inode first */
+	node = calloc(F2FS_BLKSIZE, 1);
+	ASSERT(node);
+	get_node_info(sbi, F2FS_ROOT_INO(sbi), &ni);
+	err = dev_read_block(node, ni.blk_addr);
+	ASSERT(err >= 0);
+
+	/* lookup lost+found in root directory */
+	lpf_ino = f2fs_lookup(sbi, node, (u8 *)LPF, strlen(LPF));
+	if (lpf_ino) { /* found */
+		get_node_info(sbi, lpf_ino, &ni);
+		err = dev_read_block(node, ni.blk_addr);
+		ASSERT(err >= 0);
+		DBG(1, "Found lost+found 0x%x at blkaddr [0x%x]\n",
+		    lpf_ino, ni.blk_addr);
+		if (!S_ISDIR(le16_to_cpu(node->i.i_mode))) {
+			ASSERT_MSG("lost+found is not directory [0%o]\n",
+				   le16_to_cpu(node->i.i_mode));
+			/* FIXME: give up? */
+			goto out;
+		}
+	} else { /* not found, create it */
+		struct dentry de;
+
+		memset(&de, 0, sizeof(de));
+		de.name = (u8 *) LPF;
+		de.len = strlen(LPF);
+		de.mode = 0x41c0;
+		de.pino = F2FS_ROOT_INO(sbi),
+		de.file_type = F2FS_FT_DIR,
+		de.uid = getuid();
+		de.gid = getgid();
+		de.mtime = time(NULL);
+
+		err = f2fs_mkdir(sbi, &de);
+		if (err) {
+			ASSERT_MSG("Failed create lost+found");
+			goto out;
+		}
+
+		get_node_info(sbi, de.ino, &ni);
+		err = dev_read_block(node, ni.blk_addr);
+		ASSERT(err >= 0);
+		DBG(1, "Create lost+found 0x%x at blkaddr [0x%x]\n",
+		    de.ino, ni.blk_addr);
+	}
+
+	c.lpf_ino = le32_to_cpu(node->footer.ino);
+	return node;
+out:
+	free(node);
+	return NULL;
+}
+
+static int fsck_do_reconnect_file(struct f2fs_sb_info *sbi,
+				  struct f2fs_node *lpf,
+				  struct f2fs_node *fnode)
+{
+	char name[80];
+	size_t namelen;
+	nid_t ino = le32_to_cpu(fnode->footer.ino);
+	struct node_info ni;
+	int ret;
+
+	namelen = snprintf(name, 80, "%u", ino);
+	if (namelen >= 80)
+		/* ignore terminating '\0', should never happen */
+		namelen = 79;
+
+	if (f2fs_lookup(sbi, lpf, (u8 *)name, namelen)) {
+		ASSERT_MSG("Name %s already exist in lost+found", name);
+		return -EEXIST;
+	}
+
+	get_node_info(sbi, le32_to_cpu(lpf->footer.ino), &ni);
+	ret = f2fs_add_link(sbi, lpf, (unsigned char *)name, namelen,
+			    ino, F2FS_FT_REG_FILE, ni.blk_addr, 0);
+	if (ret) {
+		ASSERT_MSG("Failed to add inode [0x%x] to lost+found", ino);
+		return -EINVAL;
+	}
+
+	/* update fnode */
+	memcpy(fnode->i.i_name, name, namelen);
+	fnode->i.i_namelen = cpu_to_le32(namelen);
+	fnode->i.i_pino = c.lpf_ino;
+	get_node_info(sbi, le32_to_cpu(fnode->footer.ino), &ni);
+	ret = dev_write_block(fnode, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	DBG(1, "Reconnect inode [0x%x] to lost+found\n", ino);
+	return 0;
+}
+
+static void fsck_failed_reconnect_file_dnode(struct f2fs_sb_info *sbi,
+					     nid_t nid)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct f2fs_node *node;
+	struct node_info ni;
+	u32 addr;
+	int i, err;
+
+	node = calloc(F2FS_BLKSIZE, 1);
+	ASSERT(node);
+
+	get_node_info(sbi, nid, &ni);
+	err = dev_read_block(node, ni.blk_addr);
+	ASSERT(err >= 0);
+
+	fsck->chk.valid_node_cnt--;
+	fsck->chk.valid_blk_cnt--;
+	f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+	for (i = 0; i < ADDRS_PER_BLOCK; i++) {
+		addr = le32_to_cpu(node->dn.addr[i]);
+		if (!addr)
+			continue;
+		fsck->chk.valid_blk_cnt--;
+		if (addr == NEW_ADDR)
+			continue;
+		f2fs_clear_main_bitmap(sbi, addr);
+	}
+
+	free(node);
+}
+
+static void fsck_failed_reconnect_file_idnode(struct f2fs_sb_info *sbi,
+					      nid_t nid)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct f2fs_node *node;
+	struct node_info ni;
+	nid_t tmp;
+	int i, err;
+
+	node = calloc(F2FS_BLKSIZE, 1);
+	ASSERT(node);
+
+	get_node_info(sbi, nid, &ni);
+	err = dev_read_block(node, ni.blk_addr);
+	ASSERT(err >= 0);
+
+	fsck->chk.valid_node_cnt--;
+	fsck->chk.valid_blk_cnt--;
+	f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+	for (i = 0; i < NIDS_PER_BLOCK; i++) {
+		tmp = le32_to_cpu(node->in.nid[i]);
+		if (!tmp)
+			continue;
+		fsck_failed_reconnect_file_dnode(sbi, tmp);
+	}
+
+	free(node);
+}
+
+static void fsck_failed_reconnect_file_didnode(struct f2fs_sb_info *sbi,
+					       nid_t nid)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct f2fs_node *node;
+	struct node_info ni;
+	nid_t tmp;
+	int i, err;
+
+	node = calloc(F2FS_BLKSIZE, 1);
+	ASSERT(node);
+
+	get_node_info(sbi, nid, &ni);
+	err = dev_read_block(node, ni.blk_addr);
+	ASSERT(err >= 0);
+
+	fsck->chk.valid_node_cnt--;
+	fsck->chk.valid_blk_cnt--;
+	f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+	for (i = 0; i < NIDS_PER_BLOCK; i++) {
+		tmp = le32_to_cpu(node->in.nid[i]);
+		if (!tmp)
+			continue;
+		fsck_failed_reconnect_file_idnode(sbi, tmp);
+	}
+
+	free(node);
+}
+
+/*
+ * Counters and main_area_bitmap are already changed during checking
+ * inode block, so clear them. There is no need to clear new blocks
+ * allocted to lost+found.
+ */
+static void fsck_failed_reconnect_file(struct f2fs_sb_info *sbi, nid_t ino)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct f2fs_node *node;
+	struct node_info ni;
+	nid_t nid;
+	int ofs, i, err;
+
+	node = calloc(F2FS_BLKSIZE, 1);
+	ASSERT(node);
+
+	get_node_info(sbi, ino, &ni);
+	err = dev_read_block(node, ni.blk_addr);
+	ASSERT(err >= 0);
+
+	/* clear inode counters */
+	fsck->chk.valid_inode_cnt--;
+	fsck->chk.valid_node_cnt--;
+	fsck->chk.valid_blk_cnt--;
+	f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+	/* clear xnid counters */
+	if (node->i.i_xattr_nid) {
+		nid = le32_to_cpu(node->i.i_xattr_nid);
+		fsck->chk.valid_node_cnt--;
+		fsck->chk.valid_blk_cnt--;
+		get_node_info(sbi, nid, &ni);
+		f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+	}
+
+	/* clear data counters */
+	if(!(node->i.i_inline & F2FS_INLINE_DATA)) {
+		ofs = get_extra_isize(node);
+		for (i = 0; i < ADDRS_PER_INODE(&node->i); i++) {
+			block_t addr = le32_to_cpu(node->i.i_addr[ofs + i]);
+			if (!addr)
+				continue;
+			fsck->chk.valid_blk_cnt--;
+			if (addr == NEW_ADDR)
+				continue;
+			f2fs_clear_main_bitmap(sbi, addr);
+		}
+	}
+
+	for (i = 0; i < 5; i++) {
+		nid = le32_to_cpu(node->i.i_nid[i]);
+		if (!nid)
+			continue;
+
+		switch (i) {
+		case 0: /* direct node */
+		case 1:
+			fsck_failed_reconnect_file_dnode(sbi, nid);
+			break;
+		case 2: /* indirect node */
+		case 3:
+			fsck_failed_reconnect_file_idnode(sbi, nid);
+			break;
+		case 4: /* double indirect node */
+			fsck_failed_reconnect_file_didnode(sbi, nid);
+			break;
+		}
+	}
+
+	free(node);
+}
+
+/*
+ * Scan unreachable nids and find only regular file inodes. If these files
+ * are not corrupted, reconnect them to lost+found.
+ *
+ * Since all unreachable nodes are already checked, we can allocate new
+ * blocks safely.
+ *
+ * This function returns the number of files been reconnected.
+ */
+static int fsck_reconnect_file(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+	struct f2fs_node *lpf_node, *node;
+	struct node_info ni;
+	char *reconnect_bitmap;
+	u32 blk_cnt;
+	nid_t nid;
+	int err, cnt = 0, ftype;
+
+	node = calloc(F2FS_BLKSIZE, 1);
+	ASSERT(node);
+
+	reconnect_bitmap = calloc(fsck->nat_area_bitmap_sz, 1);
+	ASSERT(reconnect_bitmap);
+
+	for (nid = 0; nid < fsck->nr_nat_entries; nid++) {
+		if (f2fs_test_bit(nid, fsck->nat_area_bitmap)) {
+			if (is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) {
+				DBG(1, "Not support quota inode [0x%x]\n",
+				    nid);
+				continue;
+			}
+
+			get_node_info(sbi, nid, &ni);
+			err = dev_read_block(node, ni.blk_addr);
+			ASSERT(err >= 0);
+
+			/* reconnection will restore these nodes if needed */
+			if (node->footer.ino != node->footer.nid) {
+				DBG(1, "Not support non-inode node [0x%x]\n",
+				    nid);
+				continue;
+			}
+
+			if (S_ISDIR(le16_to_cpu(node->i.i_mode))) {
+				DBG(1, "Not support directory inode [0x%x]\n",
+				    nid);
+				continue;
+			}
+
+			ftype = map_de_type(le16_to_cpu(node->i.i_mode));
+			if (sanity_check_nid(sbi, nid, node, ftype,
+					     TYPE_INODE, &ni)) {
+				ASSERT_MSG("Invalid nid [0x%x]\n", nid);
+				continue;
+			}
+
+			DBG(1, "Check inode 0x%x\n", nid);
+			blk_cnt = 1;
+			fsck_chk_inode_blk(sbi, nid, F2FS_FT_REG_FILE,
+					   node, &blk_cnt, &ni, NULL);
+
+			f2fs_set_bit(nid, reconnect_bitmap);
+		}
+	}
+
+	lpf_node = fsck_get_lpf(sbi);
+	if (!lpf_node)
+		goto out;
+
+	for (nid = 0; nid < fsck->nr_nat_entries; nid++) {
+		if (f2fs_test_bit(nid, reconnect_bitmap)) {
+			get_node_info(sbi, nid, &ni);
+			err = dev_read_block(node, ni.blk_addr);
+			ASSERT(err >= 0);
+
+			if (fsck_do_reconnect_file(sbi, lpf_node, node)) {
+				DBG(1, "Failed to reconnect inode [0x%x]\n",
+				    nid);
+				fsck_failed_reconnect_file(sbi, nid);
+				continue;
+			}
+
+			quota_add_inode_usage(fsck->qctx, nid, &node->i);
+
+			DBG(1, "Reconnected inode [0x%x] to lost+found\n", nid);
+			cnt++;
+		}
+	}
+
+out:
+	free(node);
+	free(lpf_node);
+	free(reconnect_bitmap);
+	return cnt;
+}
+
 int fsck_verify(struct f2fs_sb_info *sbi)
 {
 	unsigned int i = 0;
@@ -2057,6 +2431,16 @@ int fsck_verify(struct f2fs_sb_info *sbi)
 
 	printf("\n");
 
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND)) {
+		for (i = 0; i < fsck->nr_nat_entries; i++)
+			if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0)
+				break;
+		if (i < fsck->nr_nat_entries) {
+			i = fsck_reconnect_file(sbi);
+			printf("[FSCK] Reconnect %u files to lost+found\n", i);
+		}
+	}
+
 	for (i = 0; i < fsck->nr_nat_entries; i++) {
 		if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) {
 			printf("NID[0x%x] is unreachable\n", i);
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 648c2db..8e133fa 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -243,6 +243,9 @@ int f2fs_mkdir(struct f2fs_sb_info *, struct dentry *);
 int f2fs_symlink(struct f2fs_sb_info *, struct dentry *);
 int inode_set_selinux(struct f2fs_sb_info *, u32, const char *);
 int f2fs_find_path(struct f2fs_sb_info *, char *, nid_t *);
+nid_t f2fs_lookup(struct f2fs_sb_info *, struct f2fs_node *, u8 *, int);
+int f2fs_add_link(struct f2fs_sb_info *, struct f2fs_node *,
+		const unsigned char *, int, nid_t, int, block_t, int);
 
 /* xattr.c */
 void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *);
diff --git a/fsck/mount.c b/fsck/mount.c
index b8e7643..545f862 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -1587,6 +1587,8 @@ void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino,
 	if (ino)
 		nat_block->entries[entry_off].ino = cpu_to_le32(ino);
 	nat_block->entries[entry_off].block_addr = cpu_to_le32(newaddr);
+	if (c.func == FSCK)
+		F2FS_FSCK(sbi)->entries[nid] = nat_block->entries[entry_off];
 
 	ret = dev_write_block(nat_block, block_addr);
 	ASSERT(ret >= 0);
-- 
2.14.1


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

  parent reply	other threads:[~2018-02-23  3:18 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-02-23  3:17 [RFC PATCH v2 0/7] f2fs-tools: introduce F2FS_FEATURE_LOST_FOUND feature Sheng Yong
2018-02-23  3:17 ` [RFC PATCH v2 1/7] fsck.f2fs: fix typo Sheng Yong
2018-02-23  3:17 ` [RFC PATCH v2 2/7] mkfs.f2fs: introduce mkfs parameters in f2fs_configuration Sheng Yong
2018-02-23  3:17 ` [RFC PATCH v2 3/7] f2fs-tools: init f2fs_configuration as 0 Sheng Yong
2018-02-23  3:17 ` [RFC PATCH v2 4/7] fsck.f2fs: integrate sanity_check_inode to __check_inode_mode Sheng Yong
2018-02-23  3:17 ` [RFC PATCH v2 5/7] mkfs.f2fs: create lost+found directory Sheng Yong
2018-02-28  5:25   ` Jaegeuk Kim
2018-02-23  3:18 ` [RFC PATCH v2 6/7] fsck.f2fs: read nat block if nat entry is invalid Sheng Yong
2018-02-23  3:18 ` Sheng Yong [this message]
     [not found]   ` <0c849139-fdbe-9362-770c-ad4e9da57142@huawei.com>
     [not found]     ` <8abbaedf-ae3b-741f-d36f-d65f96739065@huawei.com>
2018-02-28  5:16       ` [RFC PATCH v2 7/7] fsck.f2fs: reconnect unreachable files to lost+found Jaegeuk Kim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180223031801.231805-8-shengyong1@huawei.com \
    --to=shengyong1@huawei.com \
    --cc=hyojun@google.com \
    --cc=jaegeuk@kernel.org \
    --cc=linux-f2fs-devel@lists.sourceforge.net \
    --cc=miaoxie@huawei.com \
    --cc=yuchao0@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).