Commit afce772e authored by Lu Fengqi's avatar Lu Fengqi Committed by David Sterba

btrfs: fix check_shared for fiemap ioctl

Only in the case of different root_id or different object_id, check_shared
identified extent as the shared. However, If a extent was referred by
different offset of same file, it should also be identified as shared.
In addition, check_shared's loop scale is at least n^3, so if a extent
has too many references, even causes soft hang up.

First, add all delayed_ref to the ref_tree and calculate the unqiue_refs,
if the unique_refs is greater than one, return BACKREF_FOUND_SHARED.
Then individually add the on-disk reference(inline/keyed) to the ref_tree
and calculate the unique_refs of the ref_tree to check if the unique_refs
is greater than one.Because once there are two references to return
SHARED, so the time complexity is close to the constant.
Reported-by: default avatarTsutomu Itoh <t-itoh@jp.fujitsu.com>
Signed-off-by: default avatarLu Fengqi <lufq.fnst@cn.fujitsu.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent b0de6c4c
This diff is collapsed.
......@@ -20,6 +20,7 @@
#include "locking.h"
#include "rcu-string.h"
#include "backref.h"
#include "transaction.h"
static struct kmem_cache *extent_state_cache;
static struct kmem_cache *extent_buffer_cache;
......@@ -4487,11 +4488,24 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
flags |= (FIEMAP_EXTENT_DELALLOC |
FIEMAP_EXTENT_UNKNOWN);
} else if (fieinfo->fi_extents_max) {
struct btrfs_trans_handle *trans;
u64 bytenr = em->block_start -
(em->start - em->orig_start);
disko = em->block_start + offset_in_extent;
/*
* We need a trans handle to get delayed refs
*/
trans = btrfs_join_transaction(root);
/*
* It's OK if we can't start a trans we can still check
* from commit_root
*/
if (IS_ERR(trans))
trans = NULL;
/*
* As btrfs supports shared space, this information
* can be exported to userspace tools via
......@@ -4499,9 +4513,11 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
* then we're just getting a count and we can skip the
* lookup stuff.
*/
ret = btrfs_check_shared(NULL, root->fs_info,
ret = btrfs_check_shared(trans, root->fs_info,
root->objectid,
btrfs_ino(inode), bytenr);
if (trans)
btrfs_end_transaction(trans, root);
if (ret < 0)
goto out_free;
if (ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment