linux-f2fs-devel.lists.sourceforge.net archive mirror
 help / color / mirror / Atom feed
From: Chao Yu <chao@kernel.org>
To: Sheng Yong <shengyong1@huawei.com>,
	jaegeuk@kernel.org, yuchao0@huawei.com
Cc: miaoxie@huawei.com, heyunlei@huawei.com,
	linux-f2fs-devel@lists.sourceforge.net
Subject: Re: [RFC PATCH 5/5] fsck.f2fs: reconnect unreachable files to lost+found
Date: Tue, 13 Feb 2018 22:22:07 +0800	[thread overview]
Message-ID: <7c76bcc1-8d6a-b2c1-c970-19ff73846379@kernel.org> (raw)
In-Reply-To: <20180206043125.134191-6-shengyong1@huawei.com>

On 2018/2/6 12:31, Sheng Yong wrote:
> 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  | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  fsck/fsck.h  |   3 +
>  fsck/mount.c |   2 +
>  4 files changed, 397 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 fcaab14..81e1145 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);
> @@ -448,9 +457,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);
>  
> @@ -2041,6 +2052,362 @@ 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, strlen(LPF))) {
> +		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);

We don't need update those stuff, right? As all those fields are just related
to recovery.

> +	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);

free(node);

> +
> +	/* 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;
> +		}
> +	}
> +}
> +
> +/*
> + * 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;
> +
> +	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;
> +			}
> +
> +			if (sanity_check_nid(sbi, nid, node,
> +					     F2FS_FT_REG_FILE, TYPE_INODE,

As we have lost dirent which contains inode type, so we have no way to check
file type in between f2fs_dir_entry.file_type and inode.i_mode in
sanity_check_nid, here how about just passing type converted from inode.i_mode?

Need to check all places using F2FS_FT_REG_FILE.

Thanks,

> +					     &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;
> +			}
> +
> +			/* FIXME: need update quota? */
> +			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;
> @@ -2059,6 +2426,11 @@ int fsck_verify(struct f2fs_sb_info *sbi)
>  		}
>  	}
>  
> +	if (nr_unref_nid && c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND)) {
> +		i = fsck_reconnect_file(sbi);
> +		printf("\n[FSCK] Reconnect %u files to lost+found\n", i);
> +	}
> +
>  	if (fsck->hard_link_list_head != NULL) {
>  		node = fsck->hard_link_list_head;
>  		while (node) {
> 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 df53c48..9775e9e 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);
> 

------------------------------------------------------------------------------
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-13 14:22 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-02-06  4:31 [RFC PATCH 0/5] f2fs-tools: introduce lost+found feature Sheng Yong
2018-02-06  4:31 ` [RFC PATCH 1/5] mkfs.f2fs: introduce mkfs parameters in f2fs_configuration Sheng Yong
2018-02-08 13:30   ` Chao Yu
2018-02-09  3:21     ` Sheng Yong
2018-02-09 12:59       ` Chao Yu
2018-02-06  4:31 ` [RFC PATCH 2/5] f2fs-tools: init f2fs_configuration as 0 Sheng Yong
2018-02-08 13:32   ` Chao Yu
2018-02-10  2:49     ` Jaegeuk Kim
2018-02-06  4:31 ` [RFC PATCH 3/5] fsck.f2fs: integrate sanity_check_inode to __check_inode_mode Sheng Yong
2018-02-08 13:44   ` Chao Yu
2018-02-06  4:31 ` [RFC PATCH 4/5] mkfs.f2fs: create lost+found directory Sheng Yong
2018-02-08 15:08   ` Chao Yu
2018-02-09  3:21     ` Sheng Yong
2018-02-09 13:13       ` Chao Yu
2018-02-06  4:31 ` [RFC PATCH 5/5] fsck.f2fs: reconnect unreachable files to lost+found Sheng Yong
2018-02-07 10:01   ` Sheng Yong
2018-02-07 10:04     ` Sheng Yong
2018-02-13 14:22   ` Chao Yu [this message]

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=7c76bcc1-8d6a-b2c1-c970-19ff73846379@kernel.org \
    --to=chao@kernel.org \
    --cc=heyunlei@huawei.com \
    --cc=jaegeuk@kernel.org \
    --cc=linux-f2fs-devel@lists.sourceforge.net \
    --cc=miaoxie@huawei.com \
    --cc=shengyong1@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).