|
@@ -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)
|