Btrfs-progs: fix wrong leaf when checking the trees relationship
[platform/upstream/btrfs-progs.git] / btrfs-list.c
index c922f09..c53d016 100644 (file)
@@ -16,6 +16,7 @@
  * Boston, MA 021110-1307, USA.
  */
 
+#define _GNU_SOURCE
 #ifndef __CHECKER__
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -33,7 +34,6 @@
 #include "ctree.h"
 #include "transaction.h"
 #include "utils.h"
-#include "version.h"
 
 /* we store all the roots we find in an rbtree so that we can
  * search for them later.
@@ -66,7 +66,7 @@ struct root_info {
        char name[];
 };
 
-void root_lookup_init(struct root_lookup *tree)
+static void root_lookup_init(struct root_lookup *tree)
 {
        tree->root.rb_node = NULL;
 }
@@ -154,39 +154,6 @@ static struct root_info *tree_search(struct rb_root *root, u64 root_id)
 }
 
 /*
- * helper to open either a file or directory so that
- * we can run ioctls on it.
- */
-static int open_file_or_dir(const char *fname)
-{
-       int ret;
-       struct stat st;
-       DIR *dirstream;
-       int fd;
-
-       ret = stat(fname, &st);
-       if (ret < 0) {
-               perror("stat:");
-               exit(1);
-       }
-       if (S_ISDIR(st.st_mode)) {
-               dirstream = opendir(fname);
-               if (!dirstream) {
-                       perror("opendir");
-                       exit(1);
-               }
-               fd = dirfd(dirstream);
-       } else {
-               fd = open(fname, O_RDWR);
-       }
-       if (fd < 0) {
-               perror("open");
-               exit(1);
-       }
-       return fd;
-}
-
-/*
  * this allocates a new root in the lookup tree.
  *
  * root_id should be the object id of the root
@@ -205,16 +172,19 @@ static int add_root(struct root_lookup *root_lookup,
 {
        struct root_info *ri;
        struct rb_node *ret;
-       ri = malloc(sizeof(*ri) + name_len);
+       ri = malloc(sizeof(*ri) + name_len + 1);
        if (!ri) {
                printf("memory allocation failed\n");
                exit(1);
        }
+       memset(ri, 0, sizeof(*ri) + name_len + 1);
        ri->path = NULL;
        ri->dir_id = dir_id;
        ri->root_id = root_id;
        ri->ref_tree = ref_tree;
        strncpy(ri->name, name, name_len);
+       if (name_len > 0)
+               ri->name[name_len] = 0;
 
        ret = tree_insert(&root_lookup->root, root_id, ref_tree, &ri->rb_node);
        if (ret) {
@@ -231,9 +201,9 @@ static int add_root(struct root_lookup *root_lookup,
  * This can't be called until all the root_info->path fields are filled
  * in by lookup_ino_path
  */
-static int resolve_root(struct root_lookup *rl, struct root_info *ri)
+static int resolve_root(struct root_lookup *rl, struct root_info *ri,
+                       u64 *parent_id, u64 *top_id, char **path)
 {
-       u64 top_id;
        char *full_path = NULL;
        int len = 0;
        struct root_info *found;
@@ -242,6 +212,7 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri)
         * we go backwards from the root_info object and add pathnames
         * from parent directories as we go.
         */
+       *parent_id = 0;
        found = ri;
        while (1) {
                char *tmp;
@@ -264,9 +235,13 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri)
                }
 
                next = found->ref_tree;
+               /* record the first parent */
+               if (*parent_id == 0)
+                       *parent_id = next;
+
                /* if the ref_tree refers to ourselves, we're at the top */
                if (next == found->root_id) {
-                       top_id = next;
+                       *top_id = next;
                        break;
                }
 
@@ -276,13 +251,13 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri)
                 */
                found = tree_search(&rl->root, next);
                if (!found) {
-                       top_id = next;
+                       *top_id = next;
                        break;
                }
        }
-       printf("ID %llu top level %llu path %s\n", ri->root_id, top_id,
-              full_path);
-       free(full_path);
+
+       *path = full_path;
+
        return 0;
 }
 
@@ -296,20 +271,22 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri)
 static int lookup_ino_path(int fd, struct root_info *ri)
 {
        struct btrfs_ioctl_ino_lookup_args args;
-       int ret;
+       int ret, e;
 
        if (ri->path)
                return 0;
 
+       memset(&args, 0, sizeof(args));
        args.treeid = ri->ref_tree;
        args.objectid = ri->dir_id;
-       args.name[0] = '\0';
 
        ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+       e = errno;
        if (ret) {
-               fprintf(stderr, "Failed to lookup path for root %llu\n",
-                       (unsigned long long)ri->ref_tree);
-               exit(1);
+               fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n",
+                       (unsigned long long)ri->ref_tree,
+                       strerror(e));
+               return ret;
        }
 
        if (args.name[0]) {
@@ -335,10 +312,304 @@ static int lookup_ino_path(int fd, struct root_info *ri)
        return 0;
 }
 
-static int list_subvols(int fd)
+/* finding the generation for a given path is a two step process.
+ * First we use the inode loookup routine to find out the root id
+ *
+ * Then we use the tree search ioctl to scan all the root items for a
+ * given root id and spit out the latest generation we can find
+ */
+static u64 find_root_gen(int fd)
+{
+       struct btrfs_ioctl_ino_lookup_args ino_args;
+       int ret;
+       struct btrfs_ioctl_search_args args;
+       struct btrfs_ioctl_search_key *sk = &args.key;
+       struct btrfs_ioctl_search_header *sh;
+       unsigned long off = 0;
+       u64 max_found = 0;
+       int i;
+       int e;
+
+       memset(&ino_args, 0, sizeof(ino_args));
+       ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID;
+
+       /* this ioctl fills in ino_args->treeid */
+       ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args);
+       e = errno;
+       if (ret) {
+               fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
+                       (unsigned long long)BTRFS_FIRST_FREE_OBJECTID,
+                       strerror(e));
+               return 0;
+       }
+
+       memset(&args, 0, sizeof(args));
+
+       sk->tree_id = 1;
+
+       /*
+        * there may be more than one ROOT_ITEM key if there are
+        * snapshots pending deletion, we have to loop through
+        * them.
+        */
+       sk->min_objectid = ino_args.treeid;
+       sk->max_objectid = ino_args.treeid;
+       sk->max_type = BTRFS_ROOT_ITEM_KEY;
+       sk->min_type = BTRFS_ROOT_ITEM_KEY;
+       sk->max_offset = (u64)-1;
+       sk->max_transid = (u64)-1;
+       sk->nr_items = 4096;
+
+       while (1) {
+               ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+               e = errno;
+               if (ret < 0) {
+                       fprintf(stderr, "ERROR: can't perform the search - %s\n",
+                               strerror(e));
+                       return 0;
+               }
+               /* the ioctl returns the number of item it found in nr_items */
+               if (sk->nr_items == 0)
+                       break;
+
+               off = 0;
+               for (i = 0; i < sk->nr_items; i++) {
+                       struct btrfs_root_item *item;
+                       sh = (struct btrfs_ioctl_search_header *)(args.buf +
+                                                                 off);
+
+                       off += sizeof(*sh);
+                       item = (struct btrfs_root_item *)(args.buf + off);
+                       off += sh->len;
+
+                       sk->min_objectid = sh->objectid;
+                       sk->min_type = sh->type;
+                       sk->min_offset = sh->offset;
+
+                       if (sh->objectid > ino_args.treeid)
+                               break;
+
+                       if (sh->objectid == ino_args.treeid &&
+                           sh->type == BTRFS_ROOT_ITEM_KEY) {
+                               max_found = max(max_found,
+                                               btrfs_root_generation(item));
+                       }
+               }
+               if (sk->min_offset < (u64)-1)
+                       sk->min_offset++;
+               else
+                       break;
+
+               if (sk->min_type != BTRFS_ROOT_ITEM_KEY)
+                       break;
+               if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY)
+                       break;
+       }
+       return max_found;
+}
+
+/* pass in a directory id and this will return
+ * the full path of the parent directory inside its
+ * subvolume root.
+ *
+ * It may return NULL if it is in the root, or an ERR_PTR if things
+ * go badly.
+ */
+static char *__ino_resolve(int fd, u64 dirid)
+{
+       struct btrfs_ioctl_ino_lookup_args args;
+       int ret;
+       char *full;
+       int e;
+
+       memset(&args, 0, sizeof(args));
+       args.objectid = dirid;
+
+       ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+       e = errno;
+       if (ret) {
+               fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
+                       (unsigned long long)dirid, strerror(e) );
+               return ERR_PTR(ret);
+       }
+
+       if (args.name[0]) {
+               /*
+                * we're in a subdirectory of ref_tree, the kernel ioctl
+                * puts a / in there for us
+                */
+               full = strdup(args.name);
+               if (!full) {
+                       perror("malloc failed");
+                       return ERR_PTR(-ENOMEM);
+               }
+       } else {
+               /* we're at the root of ref_tree */
+               full = NULL;
+       }
+       return full;
+}
+
+/*
+ * simple string builder, returning a new string with both
+ * dirid and name
+ */
+char *build_name(char *dirid, char *name)
+{
+       char *full;
+       if (!dirid)
+               return strdup(name);
+
+       full = malloc(strlen(dirid) + strlen(name) + 1);
+       if (!full)
+               return NULL;
+       strcpy(full, dirid);
+       strcat(full, name);
+       return full;
+}
+
+/*
+ * given an inode number, this returns the full path name inside the subvolume
+ * to that file/directory.  cache_dirid and cache_name are used to
+ * cache the results so we can avoid tree searches if a later call goes
+ * to the same directory or file name
+ */
+static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name)
+
+{
+       u64 dirid;
+       char *dirname;
+       char *name;
+       char *full;
+       int ret;
+       struct btrfs_ioctl_search_args args;
+       struct btrfs_ioctl_search_key *sk = &args.key;
+       struct btrfs_ioctl_search_header *sh;
+       unsigned long off = 0;
+       int namelen;
+       int e;
+
+       memset(&args, 0, sizeof(args));
+
+       sk->tree_id = 0;
+
+       /*
+        * step one, we search for the inode back ref.  We just use the first
+        * one
+        */
+       sk->min_objectid = ino;
+       sk->max_objectid = ino;
+       sk->max_type = BTRFS_INODE_REF_KEY;
+       sk->max_offset = (u64)-1;
+       sk->min_type = BTRFS_INODE_REF_KEY;
+       sk->max_transid = (u64)-1;
+       sk->nr_items = 1;
+
+       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+       e = errno;
+       if (ret < 0) {
+               fprintf(stderr, "ERROR: can't perform the search - %s\n",
+                       strerror(e));
+               return NULL;
+       }
+       /* the ioctl returns the number of item it found in nr_items */
+       if (sk->nr_items == 0)
+               return NULL;
+
+       off = 0;
+       sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
+
+       if (sh->type == BTRFS_INODE_REF_KEY) {
+               struct btrfs_inode_ref *ref;
+               dirid = sh->offset;
+
+               ref = (struct btrfs_inode_ref *)(sh + 1);
+               namelen = btrfs_stack_inode_ref_name_len(ref);
+
+               name = (char *)(ref + 1);
+               name = strndup(name, namelen);
+
+               /* use our cached value */
+               if (dirid == *cache_dirid && *cache_name) {
+                       dirname = *cache_name;
+                       goto build;
+               }
+       } else {
+               return NULL;
+       }
+       /*
+        * the inode backref gives us the file name and the parent directory id.
+        * From here we use __ino_resolve to get the path to the parent
+        */
+       dirname = __ino_resolve(fd, dirid);
+build:
+       full = build_name(dirname, name);
+       if (*cache_name && dirname != *cache_name)
+               free(*cache_name);
+
+       *cache_name = dirname;
+       *cache_dirid = dirid;
+       free(name);
+
+       return full;
+}
+
+static int get_default_subvolid(int fd, u64 *default_id)
+{
+       struct btrfs_ioctl_search_args args;
+       struct btrfs_ioctl_search_key *sk = &args.key;
+       struct btrfs_ioctl_search_header *sh;
+       u64 found = 0;
+       int ret;
+
+       memset(&args, 0, sizeof(args));
+
+       /*
+        * search for a dir item with a name 'default' in the tree of
+        * tree roots, it should point us to a default root
+        */
+       sk->tree_id = 1;
+
+       /* don't worry about ancient format and request only one item */
+       sk->nr_items = 1;
+
+       sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
+       sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
+       sk->max_type = BTRFS_DIR_ITEM_KEY;
+       sk->min_type = BTRFS_DIR_ITEM_KEY;
+       sk->max_offset = (u64)-1;
+       sk->max_transid = (u64)-1;
+
+       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+       if (ret < 0)
+               return ret;
+
+       /* the ioctl returns the number of items it found in nr_items */
+       if (sk->nr_items == 0)
+               goto out;
+
+       sh = (struct btrfs_ioctl_search_header *)args.buf;
+
+       if (sh->type == BTRFS_DIR_ITEM_KEY) {
+               struct btrfs_dir_item *di;
+               int name_len;
+               char *name;
+
+               di = (struct btrfs_dir_item *)(sh + 1);
+               name_len = btrfs_stack_dir_name_len(di);
+               name = (char *)(di + 1);
+
+               if (!strncmp("default", name, name_len))
+                       found = btrfs_disk_key_objectid(&di->location);
+       }
+
+out:
+       *default_id = found;
+       return 0;
+}
+
+static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
 {
-       struct root_lookup root_lookup;
-       struct rb_node *n;
        int ret;
        struct btrfs_ioctl_search_args args;
        struct btrfs_ioctl_search_key *sk = &args.key;
@@ -350,8 +621,7 @@ static int list_subvols(int fd)
        u64 dir_id;
        int i;
 
-       root_lookup_init(&root_lookup);
-
+       root_lookup_init(root_lookup);
        memset(&args, 0, sizeof(args));
 
        /* search in the tree of tree roots */
@@ -377,10 +647,8 @@ static int list_subvols(int fd)
 
        while(1) {
                ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-               if (ret < 0) {
-                       perror("ioctl:");
-                       break;
-               }
+               if (ret < 0)
+                       return ret;
                /* the ioctl returns the number of item it found in nr_items */
                if (sk->nr_items == 0)
                        break;
@@ -395,14 +663,15 @@ static int list_subvols(int fd)
                        sh = (struct btrfs_ioctl_search_header *)(args.buf +
                                                                  off);
                        off += sizeof(*sh);
-
-                       ref = (struct btrfs_root_ref *)(args.buf + off);
-                       name_len = btrfs_stack_root_ref_name_len(ref);
-                       name = (char *)(ref + 1);
-                       dir_id = btrfs_stack_root_ref_dirid(ref);
-
-                       add_root(&root_lookup, sh->objectid, sh->offset,
-                                dir_id, name, name_len);
+                       if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
+                               ref = (struct btrfs_root_ref *)(args.buf + off);
+                               name_len = btrfs_stack_root_ref_name_len(ref);
+                               name = (char *)(ref + 1);
+                               dir_id = btrfs_stack_root_ref_dirid(ref);
+
+                               add_root(root_lookup, sh->objectid, sh->offset,
+                                        dir_id, name, name_len);
+                       }
 
                        off += sh->len;
 
@@ -418,23 +687,79 @@ static int list_subvols(int fd)
                /* this iteration is done, step forward one root for the next
                 * ioctl
                 */
-               if (sk->min_objectid < (u64)-1)
+               if (sk->min_type < BTRFS_ROOT_BACKREF_KEY) {
+                       sk->min_type = BTRFS_ROOT_BACKREF_KEY;
+                       sk->min_offset = 0;
+               } else  if (sk->min_objectid < (u64)-1) {
                        sk->min_objectid++;
-               else
+                       sk->min_type = BTRFS_ROOT_BACKREF_KEY;
+                       sk->min_offset = 0;
+               } else
                        break;
        }
-       /*
-        * now we have an rbtree full of root_info objects, but we need to fill
-        * in their path names within the subvol that is referencing each one.
-        */
-       n = rb_first(&root_lookup.root);
+
+       return 0;
+}
+
+static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
+{
+       struct rb_node *n;
+
+       n = rb_first(&root_lookup->root);
        while (n) {
                struct root_info *entry;
+               int ret;
                entry = rb_entry(n, struct root_info, rb_node);
-               lookup_ino_path(fd, entry);
+               ret = lookup_ino_path(fd, entry);
+               if(ret < 0)
+                       return ret;
                n = rb_next(n);
        }
 
+       return 0;
+}
+
+int list_subvols(int fd, int print_parent, int get_default)
+{
+       struct root_lookup root_lookup;
+       struct rb_node *n;
+       u64 default_id;
+       int ret;
+
+       if (get_default) {
+               ret = get_default_subvolid(fd, &default_id);
+               if (ret) {
+                       fprintf(stderr, "ERROR: can't perform the search - %s\n",
+                               strerror(errno));
+                       return ret;
+               }
+               if (default_id == 0) {
+                       fprintf(stderr, "ERROR: 'default' dir item not found\n");
+                       return ret;
+               }
+
+               /* no need to resolve roots if FS_TREE is default */
+               if (default_id == BTRFS_FS_TREE_OBJECTID) {
+                       printf("ID 5 (FS_TREE)\n");
+                       return ret;
+               }
+       }
+
+       ret = __list_subvol_search(fd, &root_lookup);
+       if (ret) {
+               fprintf(stderr, "ERROR: can't perform the search - %s\n",
+                               strerror(errno));
+               return ret;
+       }
+
+       /*
+        * now we have an rbtree full of root_info objects, but we need to fill
+        * in their path names within the subvol that is referencing each one.
+        */
+       ret = __list_subvol_fill_paths(fd, &root_lookup);
+       if (ret < 0)
+               return ret;
+
        /* now that we have all the subvol-relative paths filled in,
         * we have to string the subvols together so that we can get
         * a path all the way back to the FS root
@@ -442,25 +767,246 @@ static int list_subvols(int fd)
        n = rb_last(&root_lookup.root);
        while (n) {
                struct root_info *entry;
+               u64 level;
+               u64 parent_id;
+               char *path;
+
                entry = rb_entry(n, struct root_info, rb_node);
-               resolve_root(&root_lookup, entry);
+               if (get_default && entry->root_id != default_id) {
+                       n = rb_prev(n);
+                       continue;
+               }
+
+               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
+               if (print_parent) {
+                       printf("ID %llu parent %llu top level %llu path %s\n",
+                               (unsigned long long)entry->root_id,
+                               (unsigned long long)parent_id,
+                               (unsigned long long)level, path);
+               } else {
+                       printf("ID %llu top level %llu path %s\n",
+                               (unsigned long long)entry->root_id,
+                               (unsigned long long)level, path);
+               }
+
+               free(path);
                n = rb_prev(n);
        }
 
-       printf("%s\n", BTRFS_BUILD_VERSION);
        return ret;
 }
 
-int main(int ac, char **av)
+static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
+                           struct btrfs_file_extent_item *item,
+                           u64 found_gen, u64 *cache_dirid,
+                           char **cache_dir_name, u64 *cache_ino,
+                           char **cache_full_name)
 {
-       int fd;
-
-       if (ac != 2) {
-               fprintf(stderr, "usage: btrfs-list mount_point\n");
-               exit(1);
+       u64 len = 0;
+       u64 disk_start = 0;
+       u64 disk_offset = 0;
+       u8 type;
+       int compressed = 0;
+       int flags = 0;
+       char *name = NULL;
+
+       if (sh->objectid == *cache_ino) {
+               name = *cache_full_name;
+       } else if (*cache_full_name) {
+               free(*cache_full_name);
+               *cache_full_name = NULL;
        }
-       fd = open_file_or_dir(av[1]);
+       if (!name) {
+               name = ino_resolve(fd, sh->objectid, cache_dirid,
+                                  cache_dir_name);
+               *cache_full_name = name;
+               *cache_ino = sh->objectid;
+       }
+       if (!name)
+               return -EIO;
+
+       type = btrfs_stack_file_extent_type(item);
+       compressed = btrfs_stack_file_extent_compression(item);
+
+       if (type == BTRFS_FILE_EXTENT_REG ||
+           type == BTRFS_FILE_EXTENT_PREALLOC) {
+               disk_start = btrfs_stack_file_extent_disk_bytenr(item);
+               disk_offset = btrfs_stack_file_extent_offset(item);
+               len = btrfs_stack_file_extent_num_bytes(item);
+       } else if (type == BTRFS_FILE_EXTENT_INLINE) {
+               disk_start = 0;
+               disk_offset = 0;
+               len = btrfs_stack_file_extent_ram_bytes(item);
+       } else {
+               printf("unhandled extent type %d for inode %llu "
+                      "file offset %llu gen %llu\n",
+                       type,
+                       (unsigned long long)sh->objectid,
+                       (unsigned long long)sh->offset,
+                       (unsigned long long)found_gen);
+
+               return -EIO;
+       }
+       printf("inode %llu file offset %llu len %llu disk start %llu "
+              "offset %llu gen %llu flags ",
+              (unsigned long long)sh->objectid,
+              (unsigned long long)sh->offset,
+              (unsigned long long)len,
+              (unsigned long long)disk_start,
+              (unsigned long long)disk_offset,
+              (unsigned long long)found_gen);
+
+       if (compressed) {
+               printf("COMPRESS");
+               flags++;
+       }
+       if (type == BTRFS_FILE_EXTENT_PREALLOC) {
+               printf("%sPREALLOC", flags ? "|" : "");
+               flags++;
+       }
+       if (type == BTRFS_FILE_EXTENT_INLINE) {
+               printf("%sINLINE", flags ? "|" : "");
+               flags++;
+       }
+       if (!flags)
+               printf("NONE");
 
-       return list_subvols(fd);
+       printf(" %s\n", name);
+       return 0;
 }
 
+int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
+{
+       int ret;
+       struct btrfs_ioctl_search_args args;
+       struct btrfs_ioctl_search_key *sk = &args.key;
+       struct btrfs_ioctl_search_header *sh;
+       struct btrfs_file_extent_item *item;
+       unsigned long off = 0;
+       u64 found_gen;
+       u64 max_found = 0;
+       int i;
+       int e;
+       u64 cache_dirid = 0;
+       u64 cache_ino = 0;
+       char *cache_dir_name = NULL;
+       char *cache_full_name = NULL;
+       struct btrfs_file_extent_item backup;
+
+       memset(&backup, 0, sizeof(backup));
+       memset(&args, 0, sizeof(args));
+
+       sk->tree_id = root_id;
+
+       /*
+        * set all the other params to the max, we'll take any objectid
+        * and any trans
+        */
+       sk->max_objectid = (u64)-1;
+       sk->max_offset = (u64)-1;
+       sk->max_transid = (u64)-1;
+       sk->max_type = BTRFS_EXTENT_DATA_KEY;
+       sk->min_transid = oldest_gen;
+       /* just a big number, doesn't matter much */
+       sk->nr_items = 4096;
+
+       max_found = find_root_gen(fd);
+       while(1) {
+               ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+               e = errno;
+               if (ret < 0) {
+                       fprintf(stderr, "ERROR: can't perform the search- %s\n",
+                               strerror(e));
+                       return ret;
+               }
+               /* the ioctl returns the number of item it found in nr_items */
+               if (sk->nr_items == 0)
+                       break;
+
+               off = 0;
+
+               /*
+                * for each item, pull the key out of the header and then
+                * read the root_ref item it contains
+                */
+               for (i = 0; i < sk->nr_items; i++) {
+                       sh = (struct btrfs_ioctl_search_header *)(args.buf +
+                                                                 off);
+                       off += sizeof(*sh);
+
+                       /*
+                        * just in case the item was too big, pass something other
+                        * than garbage
+                        */
+                       if (sh->len == 0)
+                               item = &backup;
+                       else
+                               item = (struct btrfs_file_extent_item *)(args.buf +
+                                                                off);
+                       found_gen = btrfs_stack_file_extent_generation(item);
+                       if (sh->type == BTRFS_EXTENT_DATA_KEY &&
+                           found_gen >= oldest_gen) {
+                               print_one_extent(fd, sh, item, found_gen,
+                                                &cache_dirid, &cache_dir_name,
+                                                &cache_ino, &cache_full_name);
+                       }
+                       off += sh->len;
+
+                       /*
+                        * record the mins in sk so we can make sure the
+                        * next search doesn't repeat this root
+                        */
+                       sk->min_objectid = sh->objectid;
+                       sk->min_offset = sh->offset;
+                       sk->min_type = sh->type;
+               }
+               sk->nr_items = 4096;
+               if (sk->min_offset < (u64)-1)
+                       sk->min_offset++;
+               else if (sk->min_objectid < (u64)-1) {
+                       sk->min_objectid++;
+                       sk->min_offset = 0;
+                       sk->min_type = 0;
+               } else
+                       break;
+       }
+       free(cache_dir_name);
+       free(cache_full_name);
+       printf("transid marker was %llu\n", (unsigned long long)max_found);
+       return ret;
+}
+
+char *path_for_root(int fd, u64 root)
+{
+       struct root_lookup root_lookup;
+       struct rb_node *n;
+       char *ret_path = NULL;
+       int ret;
+
+       ret = __list_subvol_search(fd, &root_lookup);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       ret = __list_subvol_fill_paths(fd, &root_lookup);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       n = rb_last(&root_lookup.root);
+       while (n) {
+               struct root_info *entry;
+               u64 parent_id;
+               u64 level;
+               char *path;
+
+               entry = rb_entry(n, struct root_info, rb_node);
+               resolve_root(&root_lookup, entry, &parent_id, &level, &path);
+               if (entry->root_id == root)
+                       ret_path = path;
+               else
+                       free(path);
+
+               n = rb_prev(n);
+       }
+
+       return ret_path;
+}