Btrfs-progs: add function to map subvol ID to path
authorStefan Behrens <sbehrens@giantdisaster.de>
Tue, 23 Apr 2013 10:18:55 +0000 (12:18 +0200)
committerDavid Sterba <dsterba@suse.cz>
Tue, 23 Apr 2013 17:16:25 +0000 (19:16 +0200)
Several tools like btrfs-send and btrfs-receive need to map a
subvolume ID to a filesystem path. The so far existing methods
in btrfs-list.c cause a horrible effort when performing this
operation (and the effort is dependent on the number of
existing subvolumes with quadratic effort). This commit adds a
function that is able to map a subvolume ID to a filesystem path
with an effort that is independent of the number of existing
subvolumes.

In addition to this function, a command line frontend is added as well:
btrfs inspect-internal subvolid-resolve <subvolid> <path>

Signed-off-by: Stefan Behrens <sbehrens@giantdisaster.de>
cmds-inspect.c
man/btrfs.8.in
send-utils.c
send-utils.h

index 7761759..30b41fc 100644 (file)
@@ -24,6 +24,8 @@
 #include "kerncompat.h"
 #include "ioctl.h"
 #include "utils.h"
+#include "ctree.h"
+#include "send-utils.h"
 
 #include "commands.h"
 #include "btrfs-list.h"
@@ -253,12 +255,56 @@ out:
        return ret;
 }
 
+static const char * const cmd_subvolid_resolve_usage[] = {
+       "btrfs inspect-internal subvolid-resolve <subvolid> <path>",
+       "Get file system paths for the given subvolume ID.",
+       NULL
+};
+
+static int cmd_subvolid_resolve(int argc, char **argv)
+{
+       int ret;
+       int fd = -1;
+       u64 subvol_id;
+       char path[BTRFS_PATH_NAME_MAX + 1];
+
+       if (check_argc_exact(argc, 3))
+               usage(cmd_subvolid_resolve_usage);
+
+       fd = open_file_or_dir(argv[2]);
+       if (fd < 0) {
+               fprintf(stderr, "ERROR: can't access '%s'\n", argv[2]);
+               ret = -ENOENT;
+               goto out;
+       }
+
+       subvol_id = atoll(argv[1]);
+       ret = btrfs_subvolid_resolve(fd, path, sizeof(path), subvol_id);
+
+       if (ret) {
+               fprintf(stderr,
+                       "%s: btrfs_subvolid_resolve(subvol_id %llu) failed with ret=%d\n",
+                       argv[0], (unsigned long long)subvol_id, ret);
+               goto out;
+       }
+
+       path[BTRFS_PATH_NAME_MAX] = '\0';
+       printf("%s\n", path);
+
+out:
+       if (fd >= 0)
+               close(fd);
+       return ret ? 1 : 0;
+}
+
 const struct cmd_group inspect_cmd_group = {
        inspect_cmd_group_usage, NULL, {
                { "inode-resolve", cmd_inode_resolve, cmd_inode_resolve_usage,
                        NULL, 0 },
                { "logical-resolve", cmd_logical_resolve,
                        cmd_logical_resolve_usage, NULL, 0 },
+               { "subvolid-resolve", cmd_subvolid_resolve,
+                       cmd_subvolid_resolve_usage, NULL, 0 },
                { 0, 0, 0, 0, 0 }
        }
 };
index 8ec5d5a..af7df4d 100644 (file)
@@ -60,6 +60,8 @@ btrfs \- control a btrfs filesystem
 \fBbtrfs\fP \fBinspect-internal logical-resolve\fP
 [-Pv] [-s size] \fI<logical>\fP \fI<path>\fP
 .PP
+\fBbtrfs\fP \fBinspect-internal subvolid-resolve\fP \fI<subvolid>\fP \fI<path>\fP
+.PP
 \fBbtrfs\fP \fBqgroup assign\fP \fI<src>\fP \fI<dst>\fP \fI<path>\fP
 .PP
 \fBbtrfs\fP \fBqgroup remove\fP \fI<src>\fP \fI<dst>\fP \fI<path>\fP
@@ -461,6 +463,10 @@ not enough to read all the resolved results. The max value one can set is 64k.
 .RE
 .TP
 
+\fBinspect-internal subvolid-resolve\fP \fI<subvolid>\fP \fI<path>\fP
+Get file system paths for the given subvolume ID.
+.TP
+
 \fBbtrfs qgroup assign\fP \fI<src>\fP \fI<dst>\fP \fI<path>\fP
 Enable subvolume qgroup support for a filesystem.
 .TP
index bc0feb8..bacd47e 100644 (file)
 #include "ioctl.h"
 #include "btrfs-list.h"
 
+static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
+                                     u64 subvol_id);
+
+int btrfs_subvolid_resolve(int fd, char *path, size_t path_len, u64 subvol_id)
+{
+       if (path_len < 1)
+               return -EOVERFLOW;
+       path[0] = '\0';
+       path_len--;
+       path[path_len] = '\0';
+       return btrfs_subvolid_resolve_sub(fd, path, &path_len, subvol_id);
+}
+
+static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
+                                     u64 subvol_id)
+{
+       int ret;
+       struct btrfs_ioctl_search_args search_arg;
+       struct btrfs_ioctl_ino_lookup_args ino_lookup_arg;
+       struct btrfs_ioctl_search_header *search_header;
+       struct btrfs_root_ref *backref_item;
+
+       if (subvol_id == BTRFS_FS_TREE_OBJECTID) {
+               if (*path_len < 1)
+                       return -EOVERFLOW;
+               *path = '\0';
+               (*path_len)--;
+               return 0;
+       }
+
+       memset(&search_arg, 0, sizeof(search_arg));
+       search_arg.key.tree_id = BTRFS_ROOT_TREE_OBJECTID;
+       search_arg.key.min_objectid = subvol_id;
+       search_arg.key.max_objectid = subvol_id;
+       search_arg.key.min_type = BTRFS_ROOT_BACKREF_KEY;
+       search_arg.key.max_type = BTRFS_ROOT_BACKREF_KEY;
+       search_arg.key.max_offset = (u64)-1;
+       search_arg.key.max_transid = (u64)-1;
+       search_arg.key.nr_items = 1;
+       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_arg);
+       if (ret) {
+               fprintf(stderr,
+                       "ioctl(BTRFS_IOC_TREE_SEARCH, subvol_id %llu) ret=%d, error: %s\n",
+                       (unsigned long long)subvol_id, ret, strerror(errno));
+               return ret;
+       }
+
+       if (search_arg.key.nr_items < 1) {
+               fprintf(stderr,
+                       "failed to lookup subvol_id %llu!\n",
+                       (unsigned long long)subvol_id);
+               return -ENOENT;
+       }
+       search_header = (struct btrfs_ioctl_search_header *)search_arg.buf;
+       backref_item = (struct btrfs_root_ref *)(search_header + 1);
+       if (search_header->offset != BTRFS_FS_TREE_OBJECTID) {
+               int sub_ret;
+
+               sub_ret = btrfs_subvolid_resolve_sub(fd, path, path_len,
+                                                    search_header->offset);
+               if (sub_ret)
+                       return sub_ret;
+               if (*path_len < 1)
+                       return -EOVERFLOW;
+               strcat(path, "/");
+               (*path_len)--;
+       }
+
+       if (btrfs_stack_root_ref_dirid(backref_item) !=
+           BTRFS_FIRST_FREE_OBJECTID) {
+               int len;
+
+               memset(&ino_lookup_arg, 0, sizeof(ino_lookup_arg));
+               ino_lookup_arg.treeid = search_header->offset;
+               ino_lookup_arg.objectid =
+                       btrfs_stack_root_ref_dirid(backref_item);
+               ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_lookup_arg);
+               if (ret) {
+                       fprintf(stderr,
+                               "ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %s\n",
+                               ret, strerror(errno));
+                       return ret;
+               }
+
+               len = strlen(ino_lookup_arg.name);
+               if (*path_len < len)
+                       return -EOVERFLOW;
+               strcat(path, ino_lookup_arg.name);
+               (*path_len) -= len;
+       }
+
+       if (*path_len < btrfs_stack_root_ref_name_len(backref_item))
+               return -EOVERFLOW;
+       strncat(path, (char *)(backref_item + 1),
+               btrfs_stack_root_ref_name_len(backref_item));
+       (*path_len) -= btrfs_stack_root_ref_name_len(backref_item);
+       return 0;
+}
+
 static struct rb_node *tree_insert(struct rb_root *root,
                                   struct subvol_info *si,
                                   enum subvol_search_type type)
index 78abf94..06af75f 100644 (file)
@@ -70,7 +70,7 @@ struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
 void subvol_uuid_search_add(struct subvol_uuid_search *s,
                            struct subvol_info *si);
 
-
+int btrfs_subvolid_resolve(int fd, char *path, size_t path_len, u64 subvol_id);
 
 char *path_cat(const char *p1, const char *p2);
 char *path_cat3(const char *p1, const char *p2, const char *p3);