I made Snapshot/subvolume listing feature. This feature consists of two patches, for kernel(ioctl), and for progs(btrfsctl). I send these two patches as response of this mail soon. New option ''-l'' is introduced to btrfsctl for listing. If this option is specified, btrfsctl call new ioctl. New ioctl searches root tree and enumerates subtrees. For each subtrees, ioctl searches directory path to tree root, and enumerates more descendant until no more subtree is found. MANPAGE-like option description and examples are as follows. OPTIONS -l _file_ List all snapshot/subvolume directories under a tree which _file_ belongs to. EXAMPLES # btrfsctl -l /work/btrfs Base path = /work/btrfs/ No. Tree ID Subvolume Relative Path 1 256 ss1/ 2 257 ss2/ 3 258 svs1/ss1/ 4 259 svs1/ss2/ 5 260 svs2/ss1/ 6 261 svs2/ss2/ 7 262 ss3/ 8 263 ss4/ 9 264 sv_pool/ 10 265 sv_pool/ss01/ 11 266 sv_pool/ss02/ 12 267 sv_pool/ss03/ 13 268 sv_pool/ss04/ 14 269 sv_pool/ss05/ 15 270 sv_pool/ss06/ 16 271 sv_pool/ss07/ 17 272 sv_pool/ss08/ 18 273 sv_pool/ss09/ 19 274 sv_pool/ss10/ operation complete Btrfs v0.19-9-gd67dad2 Regards, taruisi -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
New feature to list up subvolume/snapshots under specified tree of file is introduced to ioctl. --- fs/btrfs/ioctl.c | 321 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/ioctl.h | 27 ++++ 2 files changed, 348 insertions(+) Index: b/fs/btrfs/ioctl.c ==================================================================--- a/fs/btrfs/ioctl.c 2009-11-12 23:47:05.000000000 +0900 +++ b/fs/btrfs/ioctl.c 2009-11-12 23:54:21.000000000 +0900 @@ -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,323 @@ 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; +} + +/* + 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_name *svol_iname; + struct btrfs_ioctl_subvol_cache *subvol, *tmp; + struct btrfs_ioctl_subvol_args *svol; + struct btrfs_root *tree_root, *root; + struct btrfs_root_ref *ref; + struct extent_buffer *l; + struct btrfs_path *path=NULL; + struct btrfs_key key; + u64 tree_id; + char *work_path, *f_path, *name; + int err, ret = 0, slot = 0, seq = 0; + LIST_HEAD(pending_subvols); + struct list_head *cur; + struct path vfs_path; + struct inode *d_inode; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + svol_iname = kzalloc(sizeof(*svol_iname), GFP_NOFS); + name = kzalloc(BTRFS_PATH_NAME_MAX+1, GFP_NOFS); + svol = memdup_user(arg, sizeof(*svol)); + if (!svol_iname || IS_ERR(svol) || !name) + return -ENOMEM; + + /* identify tree base and set it to user parameter. */ + svol->nr = 0; + if (svol->seq == 0) { + work_path = kzalloc(BTRFS_PATH_NAME_MAX+1, GFP_NOFS); + if (!work_path) { + ret = -ENOMEM; + goto error_unrelease; + } + vfs_path.mnt = file->f_path.mnt; + vfs_path.dentry = btrfs_walkup_dentry_to_root(file->f_path.dentry); + f_path = d_path(&vfs_path, work_path, BTRFS_PATH_NAME_MAX); + if (!IS_ERR(f_path)) { + strcpy(svol_iname->name, f_path); + strcat(svol_iname->name, "/"); + svol_iname->objectid = 0; + if (copy_to_user((svol->subvols), svol_iname, + sizeof(*svol_iname))) { + ret = -EFAULT; + kfree(work_path); + goto error_unrelease; + }; + svol->nr++; + } + kfree(work_path); + } + + /* identify tree id and tree root */ + d_inode = file->f_path.dentry->d_inode; + root = BTRFS_I(d_inode)->root; + tree_root = root->fs_info->tree_root; + tree_id = root->root_key.objectid; + + /* create first tree info to search subvolume and add it to inner list */ + subvol = kzalloc(sizeof(struct btrfs_ioctl_subvol_cache), GFP_NOFS); + subvol->tree_id = tree_id; + subvol->name_len = 0; + subvol->path_len = 0; + list_add_tail(&subvol->list, &pending_subvols); + path = btrfs_alloc_path(); + + /* finalize path name info and search subvolumes until list get empty */ + while (!list_empty(&pending_subvols)) { + + /* pick up tree info */ + subvol = list_entry(pending_subvols.next, + struct btrfs_ioctl_subvol_cache, list); + tree_id = subvol->tree_id; + + /* if not first tree, identfy its path in the parent tree. + subvol->path_name holds path_name from the specified root + to the parent tree root. so, we concatenate subvol->path_name, + and path_name in the parent tree, and directry name itself. */ + if (subvol->name_len > 0) { + btrfs_search_path_in_tree(root->fs_info, + subvol->parent, subvol->dirid, name); + + if (strlen(name)+subvol->name_len>BTRFS_PATH_NAME_MAX) { + ret = -1; + goto error_unrelease; + } + strcpy(svol_iname->name, subvol->path_name); + strcpy(svol_iname->name+strlen(subvol->path_name), name); + strcpy(svol_iname->name+strlen(subvol->path_name)+strlen(name), + subvol->name); + /* this name string is used to set subvolume info as path name + of parent tree. */ + strcpy(name, svol_iname->name); + + /* if sequence is bigger than required, return subvol info to + user parameter */ + if(seq > svol->seq) { + strcat(svol_iname->name, "/"); + svol_iname->objectid = tree_id; + if (copy_to_user((svol->subvols+svol->nr), + svol_iname, sizeof(*svol_iname))) { + ret = -EFAULT; + goto error; + } + svol->nr++; + if (svol->nr >= svol->max) { + list_for_each_entry_safe(subvol, tmp, + &pending_subvols, list) { + list_del(&subvol->list); + if (subvol->name_len != 0) + kfree(subvol->name); + if (subvol->path_name) + kfree(subvol->path_name); + kfree(subvol); + } + ret = 1; + goto reach_limit; + } + } + } + + /* search root tree to find subvolumes */ + key.objectid = tree_id; + key.type = BTRFS_ROOT_REF_KEY; + key.offset = 0; + list_del(&subvol->list); + if (subvol->name_len != 0) + kfree(subvol->name); + if (subvol->path_name) + kfree(subvol->path_name); + kfree(subvol); + seq++; + + err = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); + if (err < 0) { + printk("search slot failed: code=%d\n", err); + ret = -1; + goto error_unrelease; + } + cur = &pending_subvols; + + /* traverse leafs to search subvolumes under the subvolume */ + while (1) { + l = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu( l, &key, path->slots[0]); + if (slot >= btrfs_header_nritems(l)) { + err = btrfs_next_leaf(tree_root, path); + if (err == 0) + continue; + if (err < 0) { + printk("next_leaf failed: code=%d\n", err); + ret = -1; + goto error; + } + } + if (key.type != BTRFS_ROOT_REF_KEY || key.objectid != tree_id) + break; + + subvol = kzalloc(sizeof(struct btrfs_ioctl_subvol_cache), + GFP_NOFS); + subvol->tree_id = key.offset; + subvol->parent = key.objectid; + + /* set to subvolume info its name and info about parent tree */ + ref = btrfs_item_ptr(l, slot, struct btrfs_root_ref); + subvol->name_len = btrfs_root_ref_name_len( l, ref); + subvol->name = kzalloc(subvol->name_len+1, GFP_NOFS); + read_extent_buffer(l, subvol->name, + (unsigned long)(ref + 1), subvol->name_len); + subvol->path_name = kzalloc(strlen(name)+2, GFP_NOFS); + if (strlen(name)!=0) { + strcpy(subvol->path_name, name); + strcat(subvol->path_name, "/"); + } + subvol->dirid = btrfs_root_ref_dirid(l, ref); + + list_add(&subvol->list, cur); + cur = &subvol->list; + + path->slots[0]++; + cond_resched(); + } + btrfs_release_path(tree_root, path); + } + +reach_limit: + svol->seq = seq; + if (copy_to_user(arg, svol, + sizeof(struct btrfs_ioctl_subvol_args))) { + ret = -EFAULT; + } + btrfs_release_path(tree_root, path); + kfree(svol); + kfree(svol_iname); + kfree(path); + kfree(name); + return ret; +error: + btrfs_release_path(tree_root, path); +error_unrelease: + kfree(svol); + kfree(svol_iname); + kfree(path); + kfree(name); + return ret; +} + static noinline int btrfs_ioctl_snap_destroy(struct file *file, void __user *arg) { @@ -1334,10 +1652,13 @@ 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; } + printk("cmd : %u \n", cmd); return -ENOTTY; } Index: b/fs/btrfs/ioctl.h ==================================================================--- a/fs/btrfs/ioctl.h 2009-11-12 23:47:08.000000000 +0900 +++ b/fs/btrfs/ioctl.h 2009-11-12 23:47:27.000000000 +0900 @@ -23,6 +23,7 @@ #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_VOL_NAME_MAX 255 #define BTRFS_PATH_NAME_MAX 4087 +#define BTRFS_SUBVOL_LIST_MAX 3 /* this should be 4k */ struct btrfs_ioctl_vol_args { @@ -30,6 +31,30 @@ struct btrfs_ioctl_vol_args { char name[BTRFS_PATH_NAME_MAX + 1]; }; +struct btrfs_ioctl_subvol_name { + u64 objectid; + char name[BTRFS_PATH_NAME_MAX + 1]; +}; + +/* these members are same as first three members of btrfs_ioctl_subvol_args_inner */ +struct btrfs_ioctl_subvol_args { + int max; + int nr; + u64 seq; + struct btrfs_ioctl_subvol_name *subvols; +}; + +struct btrfs_ioctl_subvol_cache { + struct list_head list; + u64 tree_id; + u64 parent; + u64 dirid; + int path_len; + char *path_name; + int name_len; + char *name; +}; + struct btrfs_ioctl_clone_range_args { __s64 src_fd; __u64 src_offset, src_length; @@ -67,4 +92,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 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
New feature to list up subvolume/snapshots under specified tree of file is introduced to btrfsctl. This patch should apply with corresponding patch for kernel. --- btrfsctl.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- ioctl.h | 17 +++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) Index: b/btrfsctl.c ==================================================================--- a/btrfsctl.c 2009-11-13 01:48:04.000000000 +0900 +++ b/btrfsctl.c 2009-11-16 09:07:29.000000000 +0900 @@ -47,6 +47,7 @@ static void print_usage(void) { printf("usage: btrfsctl [ -d file|dir] [ -s snap_name subvol|tree ]\n"); printf(" [-r size] [-A device] [-a] [-c] [-D dir .]\n"); + printf(" [-l dir]\n"); printf("\t-d filename: defragments one file\n"); printf("\t-d directory: defragments the entire Btree\n"); printf("\t-s snap_name dir: creates a new snapshot of dir\n"); @@ -56,6 +57,7 @@ static void print_usage(void) printf("\t-a: scans all devices for Btrfs filesystems\n"); printf("\t-c: forces a single FS sync\n"); printf("\t-D: delete snapshot\n"); + printf("\t-l file: listing snapshot/subvolume under a subvolume\n"); printf("%s\n", BTRFS_BUILD_VERSION); exit(1); } @@ -96,8 +98,9 @@ int main(int ac, char **av) int fd; int ret; struct btrfs_ioctl_vol_args args; + struct btrfs_ioctl_subvol_args *svargs = NULL; char *name = NULL; - int i; + int i, n; unsigned long command = 0; int len; char *fullpath; @@ -191,6 +194,30 @@ int main(int ac, char **av) command = BTRFS_IOC_RESIZE; } else if (strcmp(av[i], "-c") == 0) { command = BTRFS_IOC_SYNC; + } else if (strcmp(av[i], "-l") == 0) { + if (i >= ac - 1) { + fprintf(stderr, "-l requires an arg\n"); + print_usage(); + } + fullpath = av[i + 1]; + snap_location = strdup(fullpath); + snap_fd = open_file_or_dir(snap_location); + + name = strdup(fullpath); + name = basename(name); + len = strlen(name); + + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, + "listing subvolume name zero length or too long\n"); + exit(1); + } + if (strchr(name, ''/'')) { + fprintf(stderr, + "error: / not allowed in names\n"); + exit(1); + } + command = BTRFS_IOC_SNAP_LISTING; } } if (command == 0) { @@ -219,6 +246,35 @@ int main(int ac, char **av) if (command == BTRFS_IOC_SNAP_CREATE) { args.fd = fd; ret = ioctl(snap_fd, command, &args); + } else if (command == BTRFS_IOC_SNAP_LISTING) { + n = 0; + svargs = malloc(sizeof(*svargs)); + svargs->seq = 0ULL; + svargs->nr = 0; + svargs->max = BTRFS_SUBVOL_LIST_MAX; + svargs->subvols + malloc(sizeof(*svargs->subvols)*BTRFS_SUBVOL_LIST_MAX); + ret = ioctl(snap_fd, command, svargs); + while (ret >= 0) { + for(i = 0; i < svargs->nr; i++) { + if (i==0 && svargs->subvols->objectid == 0) { + printf(" Base path = %s\n", + (svargs->subvols + i)->name); + printf(" No.\t Tree ID\tSubvolume Relative Path\n"); + } else { + n++; + printf("%5d\t%10llu\t%s\n", + n, + (svargs->subvols + i)->objectid, + (svargs->subvols + i)->name); + } + } + if (svargs->subvols->objectid == 0 && svargs->nr == 1) + printf("\t\tNo Subvolumes\n"); + if (ret == 0) + break; + ret = ioctl(snap_fd, command, svargs); + } } else ret = ioctl(fd, command, &args); if (ret < 0) { Index: b/ioctl.h ==================================================================--- a/ioctl.h 2009-11-13 01:48:04.000000000 +0900 +++ b/ioctl.h 2009-11-16 09:07:29.000000000 +0900 @@ -20,16 +20,31 @@ #define __IOCTL_ #include <asm/types.h> #include <linux/ioctl.h> +#include "kerncompat.h" +#include "list.h" #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_VOL_NAME_MAX 255 #define BTRFS_PATH_NAME_MAX 4087 +#define BTRFS_SUBVOL_LIST_MAX 3 struct btrfs_ioctl_vol_args { __s64 fd; char name[BTRFS_PATH_NAME_MAX + 1]; }; +struct btrfs_ioctl_subvol_name { + u64 objectid; + char name[BTRFS_PATH_NAME_MAX + 1]; +}; + +struct btrfs_ioctl_subvol_args { + int max; + int nr; + u64 seq; + struct btrfs_ioctl_subvol_name *subvols; +}; + #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \ struct btrfs_ioctl_vol_args) #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ @@ -59,4 +74,6 @@ 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 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
I''m sorry, I forgot Signed-off line. Please add this line. Signed-off-by: TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com> -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
2009/11/16 TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com>:> I made Snapshot/subvolume listing feature. > > This feature consists of two patches, for kernel(ioctl), > and for progs(btrfsctl). I send these two patches as response > of this mail soon. > > New option ''-l'' is introduced to btrfsctl for listing. > > If this option is specified, btrfsctl call new ioctl. New ioctl > searches root tree and enumerates subtrees. For each subtrees, > ioctl searches directory path to tree root, and enumerates > more descendant until no more subtree is found. > > MANPAGE-like option description and examples are as follows. > > OPTIONS > -l _file_ > List all snapshot/subvolume directories under a tree > which _file_ belongs to. > > EXAMPLES > # btrfsctl -l /work/btrfs > Base path = /work/btrfs/ > No. Tree ID Subvolume Relative Path > 1 256 ss1/ > 2 257 ss2/ > 3 258 svs1/ss1/ > 4 259 svs1/ss2/ > 5 260 svs2/ss1/ > 6 261 svs2/ss2/ > 7 262 ss3/ > 8 263 ss4/ > 9 264 sv_pool/ > 10 265 sv_pool/ss01/ > 11 266 sv_pool/ss02/ > 12 267 sv_pool/ss03/ > 13 268 sv_pool/ss04/ > 14 269 sv_pool/ss05/ > 15 270 sv_pool/ss06/ > 16 271 sv_pool/ss07/ > 17 272 sv_pool/ss08/ > 18 273 sv_pool/ss09/ > 19 274 sv_pool/ss10/ > operation complete > Btrfs v0.19-9-gd67dad2 >Thank you for doing this. I have a quick look at the patches. It seems the ioctl returns full path to each subvolume and uses sequence ID to indicate the progress of listing. Every time the ioctl is called, it tries building full list of subvolume, then skip entries that already returned. I think the API is suboptimal, a getdents like API is better. (The ioctl only lists subvolumes within a given subvolume, the user program call the ioctl recursively to list all subvolumes.) Yan, Zheng -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Thank you for your advice. I''m aware of redundant search, but I didn''t think of getdents like interface. I''ll remake it without redundant search. Regards, taruisi Yan, Zheng wrote:> 2009/11/16 TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com>: >> I made Snapshot/subvolume listing feature. >> >> This feature consists of two patches, for kernel(ioctl), >> and for progs(btrfsctl). I send these two patches as response >> of this mail soon. >> >> New option ''-l'' is introduced to btrfsctl for listing. >> >> If this option is specified, btrfsctl call new ioctl. New ioctl >> searches root tree and enumerates subtrees. For each subtrees, >> ioctl searches directory path to tree root, and enumerates >> more descendant until no more subtree is found. >> >> MANPAGE-like option description and examples are as follows. >> >> OPTIONS >> -l _file_ >> List all snapshot/subvolume directories under a tree >> which _file_ belongs to. >> >> EXAMPLES >> # btrfsctl -l /work/btrfs >> Base path = /work/btrfs/ >> No. Tree ID Subvolume Relative Path >> 1 256 ss1/ >> 2 257 ss2/ >> 3 258 svs1/ss1/ >> 4 259 svs1/ss2/ >> 5 260 svs2/ss1/ >> 6 261 svs2/ss2/ >> 7 262 ss3/ >> 8 263 ss4/ >> 9 264 sv_pool/ >> 10 265 sv_pool/ss01/ >> 11 266 sv_pool/ss02/ >> 12 267 sv_pool/ss03/ >> 13 268 sv_pool/ss04/ >> 14 269 sv_pool/ss05/ >> 15 270 sv_pool/ss06/ >> 16 271 sv_pool/ss07/ >> 17 272 sv_pool/ss08/ >> 18 273 sv_pool/ss09/ >> 19 274 sv_pool/ss10/ >> operation complete >> Btrfs v0.19-9-gd67dad2 >> > > Thank you for doing this. > > I have a quick look at the patches. It seems the ioctl returns full path > to each subvolume and uses sequence ID to indicate the progress > of listing. Every time the ioctl is called, it tries building full list of > subvolume, then skip entries that already returned. I think the API is > suboptimal, a getdents like API is better. (The ioctl only lists subvolumes > within a given subvolume, the user program call the ioctl recursively > to list all subvolumes.) > > Yan, Zheng > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html-- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Just for clarity, getdents is exactly the other interface options discussed couple of weeks back (use virtual directories & standard file-system API). Regards, Andrey On Mon, Nov 16, 2009 at 11:58 AM, TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com> wrote:> Thank you for your advice. > > I''m aware of redundant search, but I didn''t think of > getdents like interface. > > I''ll remake it without redundant search. > > Regards, > taruisi > > Yan, Zheng wrote: >> 2009/11/16 TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com>: >>> I made Snapshot/subvolume listing feature. >>> >>> This feature consists of two patches, for kernel(ioctl), >>> and for progs(btrfsctl). I send these two patches as response >>> of this mail soon. >>> >>> New option ''-l'' is introduced to btrfsctl for listing. >>> >>> If this option is specified, btrfsctl call new ioctl. New ioctl >>> searches root tree and enumerates subtrees. For each subtrees, >>> ioctl searches directory path to tree root, and enumerates >>> more descendant until no more subtree is found. >>> >>> MANPAGE-like option description and examples are as follows. >>> >>> OPTIONS >>> -l _file_ >>> List all snapshot/subvolume directories under a tree >>> which _file_ belongs to. >>> >>> EXAMPLES >>> # btrfsctl -l /work/btrfs >>> Base path = /work/btrfs/ >>> No. Tree ID Subvolume Relative Path >>> 1 256 ss1/ >>> 2 257 ss2/ >>> 3 258 svs1/ss1/ >>> 4 259 svs1/ss2/ >>> 5 260 svs2/ss1/ >>> 6 261 svs2/ss2/ >>> 7 262 ss3/ >>> 8 263 ss4/ >>> 9 264 sv_pool/ >>> 10 265 sv_pool/ss01/ >>> 11 266 sv_pool/ss02/ >>> 12 267 sv_pool/ss03/ >>> 13 268 sv_pool/ss04/ >>> 14 269 sv_pool/ss05/ >>> 15 270 sv_pool/ss06/ >>> 16 271 sv_pool/ss07/ >>> 17 272 sv_pool/ss08/ >>> 18 273 sv_pool/ss09/ >>> 19 274 sv_pool/ss10/ >>> operation complete >>> Btrfs v0.19-9-gd67dad2 >>> >> >> Thank you for doing this. >> >> I have a quick look at the patches. It seems the ioctl returns full path >> to each subvolume and uses sequence ID to indicate the progress >> of listing. Every time the ioctl is called, it tries building full list of >> subvolume, then skip entries that already returned. I think the API is >> suboptimal, a getdents like API is better. (The ioctl only lists subvolumes >> within a given subvolume, the user program call the ioctl recursively >> to list all subvolumes.) >> >> Yan, Zheng >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >-- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patches for Snapshot/subvolume listing feature are modified. With these patches, ioctl searches for subvolumes under only one tree, and btrfsctl repeatedly creates parameters and calls ioctl until all subvolumes are listed. An intrenal logic was changed but user interfaces (option and contents of a list) remain same as a patch I posted before. Regards, taruisi TARUISI Hiroaki wrote:> Thank you for your advice. > > I''m aware of redundant search, but I didn''t think of > getdents like interface. > > I''ll remake it without redundant search. > > Regards, > taruisi > > Yan, Zheng wrote: >> 2009/11/16 TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com>: >>> I made Snapshot/subvolume listing feature. >>> >>> This feature consists of two patches, for kernel(ioctl), >>> and for progs(btrfsctl). I send these two patches as response >>> of this mail soon. >>> >>> New option ''-l'' is introduced to btrfsctl for listing. >>> >>> If this option is specified, btrfsctl call new ioctl. New ioctl >>> searches root tree and enumerates subtrees. For each subtrees, >>> ioctl searches directory path to tree root, and enumerates >>> more descendant until no more subtree is found. >>> >>> MANPAGE-like option description and examples are as follows. >>> >>> OPTIONS >>> -l _file_ >>> List all snapshot/subvolume directories under a tree >>> which _file_ belongs to. >>> >>> EXAMPLES >>> # btrfsctl -l /work/btrfs >>> Base path = /work/btrfs/ >>> No. Tree ID Subvolume Relative Path >>> 1 256 ss1/ >>> 2 257 ss2/ >>> 3 258 svs1/ss1/ >>> 4 259 svs1/ss2/ >>> 5 260 svs2/ss1/ >>> 6 261 svs2/ss2/ >>> 7 262 ss3/ >>> 8 263 ss4/ >>> 9 264 sv_pool/ >>> 10 265 sv_pool/ss01/ >>> 11 266 sv_pool/ss02/ >>> 12 267 sv_pool/ss03/ >>> 13 268 sv_pool/ss04/ >>> 14 269 sv_pool/ss05/ >>> 15 270 sv_pool/ss06/ >>> 16 271 sv_pool/ss07/ >>> 17 272 sv_pool/ss08/ >>> 18 273 sv_pool/ss09/ >>> 19 274 sv_pool/ss10/ >>> operation complete >>> Btrfs v0.19-9-gd67dad2 >>> >> Thank you for doing this. >> >> I have a quick look at the patches. It seems the ioctl returns full path >> to each subvolume and uses sequence ID to indicate the progress >> of listing. Every time the ioctl is called, it tries building full list of >> subvolume, then skip entries that already returned. I think the API is >> suboptimal, a getdents like API is better. (The ioctl only lists subvolumes >> within a given subvolume, the user program call the ioctl recursively >> to list all subvolumes.) >> >> Yan, Zheng >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html-- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
New feature to list up subvolume/snapshots under specified tree of file is introduced to ioctl. Signed-off-by: TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com> --- fs/btrfs/ioctl.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/ioctl.h | 29 +++++ 2 files changed, 312 insertions(+) Index: b/fs/btrfs/ioctl.c ==================================================================--- a/fs/btrfs/ioctl.c 2009-11-12 23:47:05.000000000 +0900 +++ b/fs/btrfs/ioctl.c 2009-11-18 13:51:05.000000000 +0900 @@ -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; Index: b/fs/btrfs/ioctl.h ==================================================================--- a/fs/btrfs/ioctl.h 2009-11-12 23:47:08.000000000 +0900 +++ b/fs/btrfs/ioctl.h 2009-11-18 10:21:27.000000000 +0900 @@ -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 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
New feature to list up subvolume/snapshots under specified tree of file is introduced to btrfsctl. This patch should apply with corresponding patch for kernel. Signed-off-by: TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com> --- btrfsctl.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ioctl.h | 32 ++++++++++ 2 files changed, 223 insertions(+) Index: b/btrfsctl.c ==================================================================--- a/btrfsctl.c 2009-11-16 11:18:57.000000000 +0900 +++ b/btrfsctl.c 2009-11-18 12:09:37.000000000 +0900 @@ -47,6 +47,7 @@ static void print_usage(void) { printf("usage: btrfsctl [ -d file|dir] [ -s snap_name subvol|tree ]\n"); printf(" [-r size] [-A device] [-a] [-c] [-D dir .]\n"); + printf(" [-l dir]\n"); printf("\t-d filename: defragments one file\n"); printf("\t-d directory: defragments the entire Btree\n"); printf("\t-s snap_name dir: creates a new snapshot of dir\n"); @@ -56,6 +57,7 @@ static void print_usage(void) printf("\t-a: scans all devices for Btrfs filesystems\n"); printf("\t-c: forces a single FS sync\n"); printf("\t-D: delete snapshot\n"); + printf("\t-l file: listing snapshot/subvolume under a subvolume\n"); printf("%s\n", BTRFS_BUILD_VERSION); exit(1); } @@ -88,6 +90,169 @@ static int open_file_or_dir(const char * } return fd; } + +static noinline int btrfs_gather_under_one_subvolume(int fd, + unsigned long command, + struct btrfs_ioctl_subvol_args *svargs, + u64 tree_id, + struct list_head *list, + int len) +{ + u64 last_tree = 0ULL; + int i, ret = 1, local_size; + + while (ret > 0) { + + svargs->leaf = malloc(len); + if (!svargs->leaf) + return -1; + svargs->len = len; + svargs->leaf->len = len; + svargs->leaf->nritems = 0; + svargs->leaf->last_tree = last_tree; + svargs->leaf->parent_tree = tree_id; + +again: + ret = ioctl(fd, command, svargs); + if (ret < 0) { + free(svargs->leaf); + svargs->leaf = NULL; + return -1; + } + if (svargs->leaf->nritems == 0) { + free(svargs->leaf); + if (ret > 0) { + local_size = (svargs->next_len + 1) * 2 + + offsetof(struct btrfs_ioctl_subvol_leaf, items) + + sizeof(struct btrfs_ioctl_subvol_items)*2; + svargs->leaf = malloc(local_size); + if (!svargs->leaf) + return -1; + svargs->len = local_size; + svargs->leaf->len = local_size; + svargs->leaf->last_tree = last_tree; + svargs->leaf->parent_tree = tree_id; + goto again; + } + } else { + for (i = 0; i < svargs->leaf->nritems; i++) + INIT_LIST_HEAD(&svargs->leaf->items[i].children); + list_add_tail(&svargs->leaf->brother, list); + last_tree = svargs->leaf->last_tree; + } + } + return 0; +} + +int btrfs_gather_subvolumes(int fd, unsigned long command, + struct btrfs_ioctl_subvol_args *svargs, + u64 tree_id, struct list_head *list_top, int len) +{ + struct btrfs_ioctl_subvol_leaf *cur; + int i; + + if (btrfs_gather_under_one_subvolume(fd, command, svargs, tree_id, + list_top, len)) + return -1; + list_for_each_entry(cur, list_top, brother) { + for(i = 0; i < cur->nritems; i++) { + if (btrfs_gather_subvolumes( fd, command, svargs, + cur->items[i].tree_id, &cur->items[i].children, len)) + return -1; + } + } + return 0; +} + +int btrfs_free_subvolume_info(struct list_head *list_top) +{ + struct btrfs_ioctl_subvol_leaf *cur, *tmp; + int i; + + list_for_each_entry_safe(cur, tmp, list_top, brother) { + for(i = 0; i < cur->nritems; i++) { + if (!list_empty(&cur->items[i].children)) + btrfs_free_subvolume_info(&cur->items[i].children); + } + list_del(&cur->brother); + free(cur); + } + return 0; +} + +int btrfs_show_subvolume(struct list_head *list_top, char *path, + int *seq) +{ + int nr = *seq, i; + int base_path_len, path_len; + struct btrfs_ioctl_subvol_leaf *cur; + + base_path_len = strlen(path); + list_for_each_entry(cur, list_top, brother) { + for (i = 0; i < cur->nritems; i++) { + nr++; + path_len = strlen((char *)cur + + cur->items[i].path_offset); + if (base_path_len + path_len + 1 > BTRFS_PATH_NAME_MAX) + return -1; + strcpy(path+base_path_len, + (char *)cur+cur->items[i].path_offset); + strcat(path+base_path_len, "/"); + printf("%5d\t%10llu\t%s\n", nr, + cur->items[i].tree_id, path); + if (!list_empty(&cur->items[i].children)) + if (btrfs_show_subvolume(&cur->items[i].children, + path, &nr)) + return -1; + } + } + *seq = nr; + return 0; +} + + +int btrfs_list_subvolumes(int fd, unsigned long command) +{ + struct btrfs_ioctl_subvol_args *svargs; + int len, seq = 0, ret = 0; + char *path; + + svargs = malloc(sizeof(struct btrfs_ioctl_subvol_args) + + BTRFS_PATH_NAME_MAX + 1); + if (!svargs) + return -1; + INIT_LIST_HEAD(&svargs->list_top); + svargs->base_path = (char *)(svargs+1); + + len = (BTRFS_SUBVOL_LEAF_SIZE_MIN + getpagesize() - 1) & + ~(getpagesize() - 1); + + if (btrfs_gather_subvolumes(fd, command, svargs, 0, + &svargs->list_top, len)) { + ret = -1; + goto out; + } + + printf(" Base path = %s\n", svargs->base_path); + printf(" No.\t Tree ID\tSubvolume Relative Path\n"); + + path = malloc(BTRFS_PATH_NAME_MAX + 1); + if (!path) { + ret = -1; + goto out; + } + path[0]=''\0''; + if(btrfs_show_subvolume(&svargs->list_top, path, &seq)) { + ret = -1; + goto out; + } + if (seq == 0) + printf("\t\tNo Subvolumes\n"); +out: + btrfs_free_subvolume_info(&svargs->list_top); + return ret; +} + int main(int ac, char **av) { char *fname = NULL; @@ -191,6 +356,30 @@ int main(int ac, char **av) command = BTRFS_IOC_RESIZE; } else if (strcmp(av[i], "-c") == 0) { command = BTRFS_IOC_SYNC; + } else if (strcmp(av[i], "-l") == 0) { + if (i >= ac - 1) { + fprintf(stderr, "-l requires an arg\n"); + print_usage(); + } + fullpath = av[i + 1]; + snap_location = strdup(fullpath); + snap_fd = open_file_or_dir(snap_location); + + name = strdup(fullpath); + name = basename(name); + len = strlen(name); + + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, + "listing subvolume name zero length or too long\n"); + exit(1); + } + if (strchr(name, ''/'')) { + fprintf(stderr, + "error: / not allowed in names\n"); + exit(1); + } + command = BTRFS_IOC_SNAP_LISTING; } } if (command == 0) { @@ -219,6 +408,8 @@ int main(int ac, char **av) if (command == BTRFS_IOC_SNAP_CREATE) { args.fd = fd; ret = ioctl(snap_fd, command, &args); + } else if (command == BTRFS_IOC_SNAP_LISTING) { + ret = btrfs_list_subvolumes(snap_fd, command); } else ret = ioctl(fd, command, &args); if (ret < 0) { Index: b/ioctl.h ==================================================================--- a/ioctl.h 2009-11-16 11:18:57.000000000 +0900 +++ b/ioctl.h 2009-11-18 10:21:22.000000000 +0900 @@ -20,16 +20,46 @@ #define __IOCTL_ #include <asm/types.h> #include <linux/ioctl.h> +#include "kerncompat.h" +#include "list.h" #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_VOL_NAME_MAX 255 #define BTRFS_PATH_NAME_MAX 4087 +#define BTRFS_SUBVOL_LIST_MAX 3 struct btrfs_ioctl_vol_args { __s64 fd; 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) + #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \ struct btrfs_ioctl_vol_args) #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ @@ -59,4 +89,6 @@ 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 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Nov 18, 2009 at 02:42:14PM +0900, TARUISI Hiroaki wrote:> New feature to list up subvolume/snapshots under > specified tree of file is introduced to ioctl. > > Signed-off-by: TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com> > --- > fs/btrfs/ioctl.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/btrfs/ioctl.h | 29 +++++ > 2 files changed, 312 insertions(+) >I''ve been using this patch to work with my subvolume mounting patches, and it seems to have a problem where it will panic the box. At first I thought it was something that I did, but on a freshly mkfs''ed fs I can reproduce the panic without doing any of the subvolume mounting weirdness. If I do btrfsctl -l a couple of times it will panic. This most recent time I did a btrfsctl -l on an empty fs, created a subvolume, did a list again, created another subvolume, and tried to list again and the box paniced. I don''t have time to look into this now, but I will look into it next week if you haven''t had a chance to get to it before then. Thanks, Josef -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Thank you for your report. I''m going to reprocude this panic and fix it maybe next week. Regards, taruisi (2009/12/12 5:57), Josef Bacik wrote:> On Wed, Nov 18, 2009 at 02:42:14PM +0900, TARUISI Hiroaki wrote: >> New feature to list up subvolume/snapshots under >> specified tree of file is introduced to ioctl. >> >> Signed-off-by: TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com> >> --- >> fs/btrfs/ioctl.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> fs/btrfs/ioctl.h | 29 +++++ >> 2 files changed, 312 insertions(+) >> > > I''ve been using this patch to work with my subvolume mounting patches, and it > seems to have a problem where it will panic the box. At first I thought it was > something that I did, but on a freshly mkfs''ed fs I can reproduce the panic > without doing any of the subvolume mounting weirdness. If I do btrfsctl -l a > couple of times it will panic. This most recent time I did a btrfsctl -l on an > empty fs, created a subvolume, did a list again, created another subvolume, and > tried to list again and the box paniced. I don''t have time to look into this > now, but I will look into it next week if you haven''t had a chance to get to it > before then. Thanks, > > Josef > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html-- taruisi -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html