The iterate_irefs in backref.c is used to build path components from inode
refs. I had to add a 2nd iterate function callback to handle extended refs.

Both iterate callbacks eventually converge upon iref_to_path() which I was
able to keep as one function with some small code to abstract away
differences in the two disk structures.

Signed-off-by: Mark Fasheh <[email protected]>
---
 fs/btrfs/backref.c |  200 ++++++++++++++++++++++++++++++++++++++++++----------
 fs/btrfs/backref.h |    4 +-
 2 files changed, 165 insertions(+), 39 deletions(-)

diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 0436c12..f2b8952 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -22,6 +22,7 @@
 #include "ulist.h"
 #include "transaction.h"
 #include "delayed-ref.h"
+#include "tree-log.h"
 
 /*
  * this structure records all encountered refs on the way up to the root
@@ -858,62 +859,75 @@ static int inode_ref_info(u64 inum, u64 ioff, struct 
btrfs_root *fs_root,
 }
 
 /*
- * 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!
+ * Given the parent objectid and name/name_len pairs of an inode ref
+ * (any version) this iterates to turn that information 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!
  */
 static char *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)
+                         int 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;
        s64 bytes_left = size - 1;
        struct extent_buffer *eb = eb_in;
        struct btrfs_key found_key;
+       struct btrfs_inode_ref *iref;
+       struct btrfs_inode_extref *iref2;
 
        if (bytes_left >= 0)
                dest[bytes_left] = '\0';
 
        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)
                        free_extent_buffer(eb);
+
+               /* Ok, we have enough to find any refs to the parent inode. */
                ret = inode_ref_info(parent, 0, fs_root, path, &found_key);
-               if (ret > 0)
-                       ret = -ENOENT;
-               if (ret)
-                       break;
                next_inum = found_key.offset;
+               if (ret == 0) {
+                       slot = path->slots[0];
+                       eb = path->nodes[0];
+                       /* make sure we can use eb after releasing the path */
+                       if (eb != eb_in)
+                               atomic_inc(&eb->refs);
+                       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);
+               } else {
+                       ret = btrfs_find_one_extref(fs_root, parent, 0, path,
+                                                   &iref2, NULL);
+                       if (ret)
+                               break;
+
+                       next_inum = btrfs_inode_extref_parent(eb, iref2);
+                       name_off = (unsigned long)&iref2->name;
+                       name_len = btrfs_inode_extref_name_len(eb, iref2);
+               }
 
                /* regular exit ahead */
                if (parent == next_inum)
                        break;
 
-               slot = path->slots[0];
-               eb = path->nodes[0];
-               /* make sure we can use eb after releasing the path */
-               if (eb != eb_in)
-                       atomic_inc(&eb->refs);
-               btrfs_release_path(path);
-
-               iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref);
                parent = next_inum;
                --bytes_left;
                if (bytes_left >= 0)
@@ -1226,9 +1240,9 @@ 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)
+static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
+                             struct btrfs_path *path,
+                             iterate_irefs_t *iterate, void *ctx)
 {
        int ret;
        int slot;
@@ -1244,7 +1258,7 @@ static int iterate_irefs(u64 inum, struct btrfs_root 
*fs_root,
 
        while (1) {
                ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path,
-                                       &found_key);
+                                    &found_key);
                if (ret < 0)
                        break;
                if (ret) {
@@ -1286,6 +1300,76 @@ 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_extrefs_t *iterate, void *ctx)
+{
+       int ret;
+       int slot;
+       u64 offset = 0;
+       u64 parent;
+       int found = 0;
+       struct extent_buffer *eb;
+       struct btrfs_item *item;
+       struct btrfs_inode_extref *iref2;
+
+       while (1) {
+               ret = btrfs_find_one_extref(fs_root, inum, offset, path, &iref2,
+                                           &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_release_path(path);
+
+               item = btrfs_item_nr(eb, slot);
+               iref2 = btrfs_item_ptr(eb, slot, struct btrfs_inode_extref);
+
+               parent = btrfs_inode_extref_parent(eb, iref2);
+               ret = iterate(parent, iref2, eb, ctx);
+               if (ret) {
+                       free_extent_buffer(eb);
+                       break;
+               }
+
+               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,
+                        iterate_extrefs_t *iterate2, void *ctx)
+{
+       int ret, found_refs = 0;
+
+       ret = iterate_inode_refs(inum, fs_root, path, iterate, ctx);
+       if (ret && ret != -ENOENT)
+               return ret;
+
+       if (ret != -ENOENT)
+               ++found_refs;
+
+       ret = iterate_inode_extrefs(inum, fs_root, path, iterate2, 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
@@ -1299,13 +1383,53 @@ static int inode_to_path(u64 inum, struct 
btrfs_inode_ref *iref,
        int i = ipath->fspath->elem_cnt;
        const int s_ptr = sizeof(char *);
        u32 bytes_left;
+       u32 name_len = btrfs_inode_ref_name_len(eb, iref);
+
+       bytes_left = ipath->fspath->bytes_left > s_ptr ?
+                                       ipath->fspath->bytes_left - s_ptr : 0;
+
+       fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
+       fspath = iref_to_path(ipath->fs_root, ipath->btrfs_path, name_len,
+                             (unsigned long)(iref + 1), eb, inum, fspath_min,
+                             bytes_left);
+       if (IS_ERR(fspath))
+               return PTR_ERR(fspath);
+
+       if (fspath > fspath_min) {
+               ipath->fspath->val[i] = (u64)(unsigned long)fspath;
+               ++ipath->fspath->elem_cnt;
+               ipath->fspath->bytes_left = fspath - fspath_min;
+       } else {
+               ++ipath->fspath->elem_missed;
+               ipath->fspath->bytes_missing += fspath_min - fspath;
+               ipath->fspath->bytes_left = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * returns 0 if the path could be dumped (probably truncated)
+ * returns <0 in case of an error
+ */
+static int inode_to_path2(u64 inum, struct btrfs_inode_extref *iref2,
+                         struct extent_buffer *eb, void *ctx)
+{
+       struct inode_fs_paths *ipath = ctx;
+       char *fspath;
+       char *fspath_min;
+       int i = ipath->fspath->elem_cnt;
+       const int s_ptr = sizeof(char *);
+       u32 bytes_left;
+       u32 name_len = btrfs_inode_extref_name_len(eb, iref2);
 
        bytes_left = ipath->fspath->bytes_left > s_ptr ?
                                        ipath->fspath->bytes_left - s_ptr : 0;
 
        fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
-       fspath = iref_to_path(ipath->fs_root, ipath->btrfs_path, iref, eb,
-                               inum, fspath_min, bytes_left);
+       fspath = iref_to_path(ipath->fs_root, ipath->btrfs_path, name_len,
+                             (unsigned long)&iref2->name, eb, inum,
+                             fspath_min, bytes_left);
        if (IS_ERR(fspath))
                return PTR_ERR(fspath);
 
@@ -1339,7 +1463,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, inode_to_path2, ipath);
 }
 
 /*
diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h
index d00dfa9..4634ed7 100644
--- a/fs/btrfs/backref.h
+++ b/fs/btrfs/backref.h
@@ -31,7 +31,9 @@ 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);
+                                struct extent_buffer *eb, void *ctx);
+typedef int (iterate_extrefs_t)(u64 parent, struct btrfs_inode_extref *iref,
+                                struct extent_buffer *eb, void *ctx);
 
 int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
                        struct btrfs_path *path);
-- 
1.7.7

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to