mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
btrfs: extended inode ref iteration
The iterate_irefs in backref.c is used to build path components from inode refs. This patch adds code to iterate extended refs as well. I had modify the callback function signature to abstract out some of the differences between ref structures. iref_to_path() also needed similar changes. Signed-off-by: Mark Fasheh <mfasheh@suse.de>
This commit is contained in:
parent
f186373fef
commit
d24bec3ae5
@ -1177,26 +1177,12 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* this iterates to turn a btrfs_inode_ref into a full filesystem path. elements
|
||||
* of the path are separated by '/' and the path is guaranteed to be
|
||||
* 0-terminated. the path is only given within the current file system.
|
||||
* Therefore, it never starts with a '/'. the caller is responsible to provide
|
||||
* "size" bytes in "dest". the dest buffer will be filled backwards. finally,
|
||||
* the start point of the resulting string is returned. this pointer is within
|
||||
* dest, normally.
|
||||
* in case the path buffer would overflow, the pointer is decremented further
|
||||
* as if output was written to the buffer, though no more output is actually
|
||||
* generated. that way, the caller can determine how much space would be
|
||||
* required for the path to fit into the buffer. in that case, the returned
|
||||
* value will be smaller than dest. callers must check this!
|
||||
*/
|
||||
char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
struct btrfs_inode_ref *iref,
|
||||
static char *ref_to_path(struct btrfs_root *fs_root,
|
||||
struct btrfs_path *path,
|
||||
u32 name_len, unsigned long name_off,
|
||||
struct extent_buffer *eb_in, u64 parent,
|
||||
char *dest, u32 size)
|
||||
{
|
||||
u32 len;
|
||||
int slot;
|
||||
u64 next_inum;
|
||||
int ret;
|
||||
@ -1204,17 +1190,17 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
struct extent_buffer *eb = eb_in;
|
||||
struct btrfs_key found_key;
|
||||
int leave_spinning = path->leave_spinning;
|
||||
struct btrfs_inode_ref *iref;
|
||||
|
||||
if (bytes_left >= 0)
|
||||
dest[bytes_left] = '\0';
|
||||
|
||||
path->leave_spinning = 1;
|
||||
while (1) {
|
||||
len = btrfs_inode_ref_name_len(eb, iref);
|
||||
bytes_left -= len;
|
||||
bytes_left -= name_len;
|
||||
if (bytes_left >= 0)
|
||||
read_extent_buffer(eb, dest + bytes_left,
|
||||
(unsigned long)(iref + 1), len);
|
||||
name_off, name_len);
|
||||
if (eb != eb_in) {
|
||||
btrfs_tree_read_unlock_blocking(eb);
|
||||
free_extent_buffer(eb);
|
||||
@ -1224,6 +1210,7 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
ret = -ENOENT;
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
next_inum = found_key.offset;
|
||||
|
||||
/* regular exit ahead */
|
||||
@ -1239,8 +1226,11 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
|
||||
}
|
||||
btrfs_release_path(path);
|
||||
|
||||
iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref);
|
||||
|
||||
name_len = btrfs_inode_ref_name_len(eb, iref);
|
||||
name_off = (unsigned long)(iref + 1);
|
||||
|
||||
parent = next_inum;
|
||||
--bytes_left;
|
||||
if (bytes_left >= 0)
|
||||
@ -1256,6 +1246,32 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
return dest + bytes_left;
|
||||
}
|
||||
|
||||
/*
|
||||
* this iterates to turn a btrfs_inode_ref into a full filesystem path. elements
|
||||
* of the path are separated by '/' and the path is guaranteed to be
|
||||
* 0-terminated. the path is only given within the current file system.
|
||||
* Therefore, it never starts with a '/'. the caller is responsible to provide
|
||||
* "size" bytes in "dest". the dest buffer will be filled backwards. finally,
|
||||
* the start point of the resulting string is returned. this pointer is within
|
||||
* dest, normally.
|
||||
* in case the path buffer would overflow, the pointer is decremented further
|
||||
* as if output was written to the buffer, though no more output is actually
|
||||
* generated. that way, the caller can determine how much space would be
|
||||
* required for the path to fit into the buffer. in that case, the returned
|
||||
* value will be smaller than dest. callers must check this!
|
||||
*/
|
||||
char *btrfs_iref_to_path(struct btrfs_root *fs_root,
|
||||
struct btrfs_path *path,
|
||||
struct btrfs_inode_ref *iref,
|
||||
struct extent_buffer *eb_in, u64 parent,
|
||||
char *dest, u32 size)
|
||||
{
|
||||
return ref_to_path(fs_root, path,
|
||||
btrfs_inode_ref_name_len(eb_in, iref),
|
||||
(unsigned long)(iref + 1),
|
||||
eb_in, parent, dest, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* this makes the path point to (logical EXTENT_ITEM *)
|
||||
* returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for
|
||||
@ -1529,9 +1545,12 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
|
||||
struct btrfs_path *path,
|
||||
iterate_irefs_t *iterate, void *ctx)
|
||||
typedef int (iterate_irefs_t)(u64 parent, u32 name_len, unsigned long name_off,
|
||||
struct extent_buffer *eb, void *ctx);
|
||||
|
||||
static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
|
||||
struct btrfs_path *path,
|
||||
iterate_irefs_t *iterate, void *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
int slot;
|
||||
@ -1548,7 +1567,7 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
|
||||
while (!ret) {
|
||||
path->leave_spinning = 1;
|
||||
ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path,
|
||||
&found_key);
|
||||
&found_key);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (ret) {
|
||||
@ -1576,7 +1595,8 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
|
||||
"tree %llu\n", cur,
|
||||
(unsigned long long)found_key.objectid,
|
||||
(unsigned long long)fs_root->objectid);
|
||||
ret = iterate(parent, iref, eb, ctx);
|
||||
ret = iterate(parent, name_len,
|
||||
(unsigned long)(iref + 1), eb, ctx);
|
||||
if (ret)
|
||||
break;
|
||||
len = sizeof(*iref) + name_len;
|
||||
@ -1591,12 +1611,98 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root,
|
||||
struct btrfs_path *path,
|
||||
iterate_irefs_t *iterate, void *ctx)
|
||||
{
|
||||
int ret;
|
||||
int slot;
|
||||
u64 offset = 0;
|
||||
u64 parent;
|
||||
int found = 0;
|
||||
struct extent_buffer *eb;
|
||||
struct btrfs_inode_extref *extref;
|
||||
struct extent_buffer *leaf;
|
||||
u32 item_size;
|
||||
u32 cur_offset;
|
||||
unsigned long ptr;
|
||||
|
||||
while (1) {
|
||||
ret = btrfs_find_one_extref(fs_root, inum, offset, path, &extref,
|
||||
&offset);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (ret) {
|
||||
ret = found ? 0 : -ENOENT;
|
||||
break;
|
||||
}
|
||||
++found;
|
||||
|
||||
slot = path->slots[0];
|
||||
eb = path->nodes[0];
|
||||
/* make sure we can use eb after releasing the path */
|
||||
atomic_inc(&eb->refs);
|
||||
|
||||
btrfs_tree_read_lock(eb);
|
||||
btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
|
||||
btrfs_release_path(path);
|
||||
|
||||
leaf = path->nodes[0];
|
||||
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
|
||||
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
|
||||
cur_offset = 0;
|
||||
|
||||
while (cur_offset < item_size) {
|
||||
u32 name_len;
|
||||
|
||||
extref = (struct btrfs_inode_extref *)(ptr + cur_offset);
|
||||
parent = btrfs_inode_extref_parent(eb, extref);
|
||||
name_len = btrfs_inode_extref_name_len(eb, extref);
|
||||
ret = iterate(parent, name_len,
|
||||
(unsigned long)&extref->name, eb, ctx);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
cur_offset += btrfs_inode_extref_name_len(leaf, extref);
|
||||
cur_offset += sizeof(*extref);
|
||||
}
|
||||
btrfs_tree_read_unlock_blocking(eb);
|
||||
free_extent_buffer(eb);
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
btrfs_release_path(path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
|
||||
struct btrfs_path *path, iterate_irefs_t *iterate,
|
||||
void *ctx)
|
||||
{
|
||||
int ret;
|
||||
int found_refs = 0;
|
||||
|
||||
ret = iterate_inode_refs(inum, fs_root, path, iterate, ctx);
|
||||
if (!ret)
|
||||
++found_refs;
|
||||
else if (ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
ret = iterate_inode_extrefs(inum, fs_root, path, iterate, ctx);
|
||||
if (ret == -ENOENT && found_refs)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns 0 if the path could be dumped (probably truncated)
|
||||
* returns <0 in case of an error
|
||||
*/
|
||||
static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
|
||||
struct extent_buffer *eb, void *ctx)
|
||||
static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,
|
||||
struct extent_buffer *eb, void *ctx)
|
||||
{
|
||||
struct inode_fs_paths *ipath = ctx;
|
||||
char *fspath;
|
||||
@ -1609,20 +1715,17 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
|
||||
ipath->fspath->bytes_left - s_ptr : 0;
|
||||
|
||||
fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
|
||||
fspath = btrfs_iref_to_path(ipath->fs_root, ipath->btrfs_path, iref, eb,
|
||||
inum, fspath_min, bytes_left);
|
||||
fspath = ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len,
|
||||
name_off, eb, inum, fspath_min,
|
||||
bytes_left);
|
||||
if (IS_ERR(fspath))
|
||||
return PTR_ERR(fspath);
|
||||
|
||||
if (fspath > fspath_min) {
|
||||
pr_debug("path resolved: %s\n", fspath);
|
||||
ipath->fspath->val[i] = (u64)(unsigned long)fspath;
|
||||
++ipath->fspath->elem_cnt;
|
||||
ipath->fspath->bytes_left = fspath - fspath_min;
|
||||
} else {
|
||||
pr_debug("missed path, not enough space. missing bytes: %lu, "
|
||||
"constructed so far: %s\n",
|
||||
(unsigned long)(fspath_min - fspath), fspath_min);
|
||||
++ipath->fspath->elem_missed;
|
||||
ipath->fspath->bytes_missing += fspath_min - fspath;
|
||||
ipath->fspath->bytes_left = 0;
|
||||
@ -1644,7 +1747,7 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
|
||||
int paths_from_inode(u64 inum, struct inode_fs_paths *ipath)
|
||||
{
|
||||
return iterate_irefs(inum, ipath->fs_root, ipath->btrfs_path,
|
||||
inode_to_path, ipath);
|
||||
inode_to_path, ipath);
|
||||
}
|
||||
|
||||
struct btrfs_data_container *init_data_container(u32 total_bytes)
|
||||
|
@ -33,8 +33,6 @@ struct inode_fs_paths {
|
||||
|
||||
typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, u64 root,
|
||||
void *ctx);
|
||||
typedef int (iterate_irefs_t)(u64 parent, struct btrfs_inode_ref *iref,
|
||||
struct extent_buffer *eb, void *ctx);
|
||||
|
||||
int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
|
||||
struct btrfs_path *path);
|
||||
|
Loading…
Reference in New Issue
Block a user