===================================================================
@@ -48,6 +48,7 @@
#include "print-tree.h"
#include "volumes.h"
#include "locking.h"
+#include "ctree.h"
/* Mask out flags that are inappropriate for the given type of inode. */
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
@@ -738,6 +739,286 @@ out:
return ret;
}
+/*
+ Search INODE_REFs to identify path name of 'dirid' directory
+ in a 'tree_id' tree. and sets path name to 'name'.
+*/
+static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
+ u64 tree_id, u64 dirid, char *name)
+{
+ struct btrfs_root *root;
+ struct btrfs_key key;
+ char *name_stack, *ptr;
+ int ret = -1;
+ int slot;
+ int len;
+ int total_len = 0;
+ struct btrfs_inode_ref *iref;
+ struct extent_buffer *l;
+ struct btrfs_path *path;
+
+ if (dirid == BTRFS_FIRST_FREE_OBJECTID) {
+ name[0]='\0';
+ ret = 0;
+ goto out_direct;
+ }
+
+ path = btrfs_alloc_path();
+ name_stack = kzalloc(BTRFS_PATH_NAME_MAX+1, GFP_NOFS);
+ ptr = &name_stack[BTRFS_PATH_NAME_MAX];
+
+ key.objectid = tree_id;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ root = btrfs_read_fs_root_no_name(info, &key);
+
+ key.objectid = dirid;
+ key.type = BTRFS_INODE_REF_KEY;
+ key.offset = 0;
+
+ while(1) {
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret<0)
+ goto out;
+
+ l = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(l, &key, slot);
+
+ if (ret>0 && (key.objectid != dirid ||
+ key.type != BTRFS_INODE_REF_KEY))
+ goto out;
+
+ iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref);
+ len = btrfs_inode_ref_name_len(l, iref);
+ ptr -= len + 1;
+ total_len += len + 1;
+ if (ptr < name_stack)
+ goto out;
+
+ *(ptr + len) = '/';
+ read_extent_buffer(l, ptr,(unsigned long)(iref + 1), len);
+
+ if (key.offset == BTRFS_FIRST_FREE_OBJECTID)
+ break;
+
+ btrfs_release_path(root, path);
+ key.objectid = key.offset;
+ key.offset = 0;
+ dirid = key.objectid;
+
+ }
+ if (ptr < name_stack)
+ goto out;
+ strncpy(name, ptr, total_len);
+ name[total_len]='\0';
+ ret = 0;
+out:
+ btrfs_release_path(root, path);
+ kfree(path);
+ kfree(name_stack);
+
+out_direct:
+ return ret;
+}
+
+static inline char *btrfs_path_ptr(struct btrfs_ioctl_subvol_leaf *l,
+ int nr)
+{
+ return ((char *)l+l->items[nr].path_offset);
+}
+
+/*
+ Helper function to search tree root directory which contains
+ specified dentry.
+ This function is used in btrfs_ioctl_snap_listing function,
+ to notify root directory(different from the directory what
+ user specified) to user.
+*/
+static noinline struct dentry *btrfs_walkup_dentry_to_root(struct dentry *d)
+{
+ u64 ino;
+ struct dentry *dent = d;
+
+ ino = dent->d_inode->i_ino;
+ while (ino != BTRFS_FIRST_FREE_OBJECTID) {
+ dent = dent->d_parent;
+ ino = dent->d_inode->i_ino;
+ }
+ return dent;
+}
+
+/*
+ Create a list of Snapshot/Subvolume in specified tree.
+ Target tree is specified by struct file.
+*/
+static noinline int btrfs_ioctl_snap_listing(struct file *file,
+ void __user *arg)
+{
+ struct btrfs_ioctl_subvol_leaf *leaf;
+ struct btrfs_ioctl_subvol_args *svol;
+ int rest, offset, idx, name_len, i;
+ struct btrfs_root *tree_root;
+ struct btrfs_root_ref *ref;
+ struct extent_buffer *l;
+ struct btrfs_path *path = NULL;
+ struct btrfs_key key;
+ u64 dirid;
+ char *work_path, *f_path, *name;
+ int err, ret = 0, slot = 0;
+ LIST_HEAD(pending_subvols);
+ struct path vfs_path;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ work_path = kzalloc(BTRFS_PATH_NAME_MAX + 1, GFP_NOFS);
+ if (!work_path) {
+ kfree(path);
+ return -ENOMEM;
+ }
+
+ svol = memdup_user(arg, sizeof(struct btrfs_ioctl_subvol_args));
+ if (IS_ERR(svol)) {
+ kfree(path);
+ kfree(work_path);
+ return PTR_ERR(svol);
+ }
+ if (svol->len < BTRFS_SUBVOL_LEAF_SIZE_MIN) {
+ kfree(path);
+ kfree(work_path);
+ kfree(svol);
+ return -EINVAL;
+ }
+
+ leaf = memdup_user(svol->leaf, svol->len);
+ if (IS_ERR(leaf)) {
+ kfree(path);
+ kfree(work_path);
+ kfree(svol);
+ return PTR_ERR(leaf);
+ }
+ if (leaf->len != svol->len)
+ goto out_inval;
+
+ tree_root =
+ BTRFS_I(fdentry(file)->d_inode)->root->fs_info->tree_root;
+ if (!leaf->parent_tree) {
+ leaf->parent_tree =
+ BTRFS_I(fdentry(file)->d_inode)->root->root_key.objectid;
+ if (svol->base_path) {
+ work_path = kzalloc(BTRFS_PATH_NAME_MAX+1, GFP_NOFS);
+ if (!work_path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ vfs_path.mnt = file->f_path.mnt;
+ vfs_path.dentry = btrfs_walkup_dentry_to_root(fdentry(file));
+ f_path = d_path(&vfs_path, work_path, BTRFS_PATH_NAME_MAX);
+ if (!IS_ERR(f_path)) {
+ strcpy(svol->base_path, f_path);
+ strcat(svol->base_path, "/");
+ if (copy_to_user(svol->base_path, f_path,
+ strlen(f_path))) {
+ ret = -EFAULT;
+ kfree(work_path);
+ goto out;
+ }
+ }
+ kfree(work_path);
+ }
+ } else {
+ if (leaf->parent_tree != BTRFS_FS_TREE_OBJECTID &&
+ leaf->parent_tree < BTRFS_FIRST_FREE_OBJECTID) {
+ goto out_inval;
+ }
+ }
+
+ /* search root tree to find subvolumes */
+ key.objectid = leaf->parent_tree;
+ key.type = BTRFS_ROOT_REF_KEY;
+ key.offset = leaf->last_tree+1;
+
+ offset = leaf->len;
+ rest = leaf->len -
+ offsetof(struct btrfs_ioctl_subvol_leaf, items);
+
+ idx = 0;
+ ret = 0;
+ while(rest >= 0) {
+ err = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0);
+ if (err < 0) {
+ printk("search tree failed: code=%d\n", err);
+ ret = -EINVAL;
+ goto outr_error;
+ }
+
+ l = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(l, &key, path->slots[0]);
+
+ if (key.type != BTRFS_ROOT_REF_KEY ||
+ key.objectid != leaf->parent_tree) {
+ ret = 0;
+ btrfs_release_path(tree_root, path);
+ break;
+ }
+
+ ref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+ name_len = btrfs_root_ref_name_len(l, ref);
+ name = kzalloc(name_len + 1, GFP_NOFS);
+ dirid = btrfs_root_ref_dirid(l, ref);
+ read_extent_buffer(l, name,
+ (unsigned long)(ref + 1), name_len);
+ btrfs_release_path(tree_root, path);
+ btrfs_search_path_in_tree(tree_root->fs_info,
+ leaf->parent_tree, dirid, work_path);
+ i = sizeof(struct btrfs_ioctl_subvol_items) +
+ name_len + strlen(work_path) + 1;
+ if (rest < sizeof(struct btrfs_ioctl_subvol_items) +
+ name_len + strlen(work_path) + 1) {
+ svol->next_len = name_len + strlen(work_path);
+ if (copy_to_user(arg, svol, sizeof(*svol))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = 1;
+ break;
+ }
+ leaf->nritems++;
+ leaf->last_tree = key.offset;
+ leaf->items[idx].tree_id = key.offset;
+ leaf->items[idx].len = strlen(work_path) + name_len;
+ *((char *)leaf+offset-1) = '\0';
+ offset -= leaf->items[idx].len + 1;
+ leaf->items[idx].path_offset = offset;
+ rest -= sizeof(struct btrfs_ioctl_subvol_items) + leaf->items[idx].len + 1;
+ strncpy(btrfs_path_ptr(leaf, idx), work_path, strlen(work_path));
+ strncpy(btrfs_path_ptr(leaf, idx) + strlen(work_path), name, name_len);
+ idx++;
+ key.offset++;
+ }
+
+ if (copy_to_user(svol->leaf, leaf, svol->len)) {
+ ret = -EFAULT;
+ }
+ goto out;
+
+outr_error:
+ btrfs_release_path(tree_root, path);
+out_inval:
+ ret = -EINVAL;
+out:
+ kfree(path);
+ kfree(leaf);
+ kfree(svol);
+ return ret;
+}
+
static noinline int btrfs_ioctl_snap_destroy(struct file *file,
void __user *arg)
{
@@ -1334,6 +1615,8 @@ long btrfs_ioctl(struct file *file, unsi
return btrfs_ioctl_trans_start(file);
case BTRFS_IOC_TRANS_END:
return btrfs_ioctl_trans_end(file);
+ case BTRFS_IOC_SNAP_LISTING:
+ return btrfs_ioctl_snap_listing(file, argp);
case BTRFS_IOC_SYNC:
btrfs_sync_fs(file->f_dentry->d_sb, 1);
return 0;
===================================================================
@@ -30,6 +30,33 @@ struct btrfs_ioctl_vol_args {
char name[BTRFS_PATH_NAME_MAX + 1];
};
+struct btrfs_ioctl_subvol_args {
+ int len;
+ int next_len;
+ char *base_path;
+ struct btrfs_ioctl_subvol_leaf *leaf;
+ struct list_head list_top;
+};
+
+struct btrfs_ioctl_subvol_items {
+ u64 tree_id;
+ struct list_head children;
+ int path_offset;
+ int len;
+};
+
+struct btrfs_ioctl_subvol_leaf {
+ int len;
+ int nritems;
+ u64 parent_tree;
+ u64 last_tree;
+ struct list_head brother;
+ struct btrfs_ioctl_subvol_items items[];
+};
+
+#define BTRFS_SUBVOL_LEAF_SIZE_MIN sizeof(struct btrfs_ioctl_subvol_leaf) + \
+ sizeof(struct btrfs_ioctl_subvol_items)
+
struct btrfs_ioctl_clone_range_args {
__s64 src_fd;
__u64 src_offset, src_length;
@@ -67,4 +94,6 @@ struct btrfs_ioctl_clone_range_args {
struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_SNAP_LISTING _IOWR(BTRFS_IOCTL_MAGIC, 16, \
+ struct btrfs_ioctl_subvol_args)
#endif