diff options
author | Chris Mason <chris.mason@oracle.com> | 2010-02-28 15:39:26 -0500 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2010-03-15 10:55:10 -0400 |
commit | ac8e9819d71f907a0532b01b22c26b56bbbcbd21 (patch) | |
tree | 5bdebf68182139e664b59286f6f7071e3ada2b18 | |
parent | 98d377a0894e6bcca44eafd4d2eee74e8af4db83 (diff) |
Btrfs: add search and inode lookup ioctls
The search ioctl is a generic tool for doing btree searches from
userland applications. The first user of the search ioctl is a
subvolume listing feature, but we'll also use it to find new
files in a subvolume.
The search ioctl allows you to specify min and max keys to search for,
along with min and max transid. It returns the items along with a
header that includes the item key.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/ioctl.c | 249 | ||||
-rw-r--r-- | fs/btrfs/ioctl.h | 66 |
2 files changed, 299 insertions, 16 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ac2a28f4fa1a..c6044733198d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -744,16 +744,206 @@ out: | |||
744 | return ret; | 744 | return ret; |
745 | } | 745 | } |
746 | 746 | ||
747 | static noinline int key_in_sk(struct btrfs_key *key, | ||
748 | struct btrfs_ioctl_search_key *sk) | ||
749 | { | ||
750 | if (key->objectid < sk->min_objectid) | ||
751 | return 0; | ||
752 | if (key->offset < sk->min_offset) | ||
753 | return 0; | ||
754 | if (key->type < sk->min_type) | ||
755 | return 0; | ||
756 | if (key->objectid > sk->max_objectid) | ||
757 | return 0; | ||
758 | if (key->type > sk->max_type) | ||
759 | return 0; | ||
760 | if (key->offset > sk->max_offset) | ||
761 | return 0; | ||
762 | return 1; | ||
763 | } | ||
764 | |||
765 | static noinline int copy_to_sk(struct btrfs_root *root, | ||
766 | struct btrfs_path *path, | ||
767 | struct btrfs_key *key, | ||
768 | struct btrfs_ioctl_search_key *sk, | ||
769 | char *buf, | ||
770 | unsigned long *sk_offset, | ||
771 | int *num_found) | ||
772 | { | ||
773 | u64 found_transid; | ||
774 | struct extent_buffer *leaf; | ||
775 | struct btrfs_ioctl_search_header sh; | ||
776 | unsigned long item_off; | ||
777 | unsigned long item_len; | ||
778 | int nritems; | ||
779 | int i; | ||
780 | int slot; | ||
781 | int found = 0; | ||
782 | int ret = 0; | ||
783 | |||
784 | leaf = path->nodes[0]; | ||
785 | slot = path->slots[0]; | ||
786 | nritems = btrfs_header_nritems(leaf); | ||
787 | |||
788 | if (btrfs_header_generation(leaf) > sk->max_transid) { | ||
789 | i = nritems; | ||
790 | goto advance_key; | ||
791 | } | ||
792 | found_transid = btrfs_header_generation(leaf); | ||
793 | |||
794 | for (i = slot; i < nritems; i++) { | ||
795 | item_off = btrfs_item_ptr_offset(leaf, i); | ||
796 | item_len = btrfs_item_size_nr(leaf, i); | ||
797 | |||
798 | if (item_len > BTRFS_SEARCH_ARGS_BUFSIZE) | ||
799 | item_len = 0; | ||
800 | |||
801 | if (sizeof(sh) + item_len + *sk_offset > | ||
802 | BTRFS_SEARCH_ARGS_BUFSIZE) { | ||
803 | ret = 1; | ||
804 | goto overflow; | ||
805 | } | ||
806 | |||
807 | btrfs_item_key_to_cpu(leaf, key, i); | ||
808 | if (!key_in_sk(key, sk)) | ||
809 | continue; | ||
810 | |||
811 | sh.objectid = key->objectid; | ||
812 | sh.offset = key->offset; | ||
813 | sh.type = key->type; | ||
814 | sh.len = item_len; | ||
815 | sh.transid = found_transid; | ||
816 | |||
817 | /* copy search result header */ | ||
818 | memcpy(buf + *sk_offset, &sh, sizeof(sh)); | ||
819 | *sk_offset += sizeof(sh); | ||
820 | |||
821 | if (item_len) { | ||
822 | char *p = buf + *sk_offset; | ||
823 | /* copy the item */ | ||
824 | read_extent_buffer(leaf, p, | ||
825 | item_off, item_len); | ||
826 | *sk_offset += item_len; | ||
827 | found++; | ||
828 | } | ||
829 | |||
830 | if (*num_found >= sk->nr_items) | ||
831 | break; | ||
832 | } | ||
833 | advance_key: | ||
834 | if (key->offset < (u64)-1) | ||
835 | key->offset++; | ||
836 | else if (key->type < (u64)-1) | ||
837 | key->type++; | ||
838 | else if (key->objectid < (u64)-1) | ||
839 | key->objectid++; | ||
840 | ret = 0; | ||
841 | overflow: | ||
842 | *num_found += found; | ||
843 | return ret; | ||
844 | } | ||
845 | |||
846 | static noinline int search_ioctl(struct inode *inode, | ||
847 | struct btrfs_ioctl_search_args *args) | ||
848 | { | ||
849 | struct btrfs_root *root; | ||
850 | struct btrfs_key key; | ||
851 | struct btrfs_key max_key; | ||
852 | struct btrfs_path *path; | ||
853 | struct btrfs_ioctl_search_key *sk = &args->key; | ||
854 | struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info; | ||
855 | int ret; | ||
856 | int num_found = 0; | ||
857 | unsigned long sk_offset = 0; | ||
858 | |||
859 | path = btrfs_alloc_path(); | ||
860 | if (!path) | ||
861 | return -ENOMEM; | ||
862 | |||
863 | if (sk->tree_id == 0) { | ||
864 | /* search the root of the inode that was passed */ | ||
865 | root = BTRFS_I(inode)->root; | ||
866 | } else { | ||
867 | key.objectid = sk->tree_id; | ||
868 | key.type = BTRFS_ROOT_ITEM_KEY; | ||
869 | key.offset = (u64)-1; | ||
870 | root = btrfs_read_fs_root_no_name(info, &key); | ||
871 | if (IS_ERR(root)) { | ||
872 | printk(KERN_ERR "could not find root %llu\n", | ||
873 | sk->tree_id); | ||
874 | btrfs_free_path(path); | ||
875 | return -ENOENT; | ||
876 | } | ||
877 | } | ||
878 | |||
879 | key.objectid = sk->min_objectid; | ||
880 | key.type = sk->min_type; | ||
881 | key.offset = sk->min_offset; | ||
882 | |||
883 | max_key.objectid = sk->max_objectid; | ||
884 | max_key.type = sk->max_type; | ||
885 | max_key.offset = sk->max_offset; | ||
886 | |||
887 | path->keep_locks = 1; | ||
888 | |||
889 | while(1) { | ||
890 | ret = btrfs_search_forward(root, &key, &max_key, path, 0, | ||
891 | sk->min_transid); | ||
892 | if (ret != 0) { | ||
893 | if (ret > 0) | ||
894 | ret = 0; | ||
895 | goto err; | ||
896 | } | ||
897 | ret = copy_to_sk(root, path, &key, sk, args->buf, | ||
898 | &sk_offset, &num_found); | ||
899 | btrfs_release_path(root, path); | ||
900 | if (ret || num_found >= sk->nr_items) | ||
901 | break; | ||
902 | |||
903 | } | ||
904 | ret = 0; | ||
905 | err: | ||
906 | sk->nr_items = num_found; | ||
907 | btrfs_free_path(path); | ||
908 | return ret; | ||
909 | } | ||
910 | |||
911 | static noinline int btrfs_ioctl_tree_search(struct file *file, | ||
912 | void __user *argp) | ||
913 | { | ||
914 | struct btrfs_ioctl_search_args *args; | ||
915 | struct inode *inode; | ||
916 | int ret; | ||
917 | |||
918 | if (!capable(CAP_SYS_ADMIN)) | ||
919 | return -EPERM; | ||
920 | |||
921 | args = kmalloc(sizeof(*args), GFP_KERNEL); | ||
922 | if (!args) | ||
923 | return -ENOMEM; | ||
924 | |||
925 | if (copy_from_user(args, argp, sizeof(*args))) { | ||
926 | kfree(args); | ||
927 | return -EFAULT; | ||
928 | } | ||
929 | inode = fdentry(file)->d_inode; | ||
930 | ret = search_ioctl(inode, args); | ||
931 | if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) | ||
932 | ret = -EFAULT; | ||
933 | kfree(args); | ||
934 | return ret; | ||
935 | } | ||
936 | |||
747 | /* | 937 | /* |
748 | Search INODE_REFs to identify path name of 'dirid' directory | 938 | * Search INODE_REFs to identify path name of 'dirid' directory |
749 | in a 'tree_id' tree. and sets path name to 'name'. | 939 | * in a 'tree_id' tree. and sets path name to 'name'. |
750 | */ | 940 | */ |
751 | static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, | 941 | static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, |
752 | u64 tree_id, u64 dirid, char *name) | 942 | u64 tree_id, u64 dirid, char *name) |
753 | { | 943 | { |
754 | struct btrfs_root *root; | 944 | struct btrfs_root *root; |
755 | struct btrfs_key key; | 945 | struct btrfs_key key; |
756 | char *name_stack, *ptr; | 946 | char *ptr; |
757 | int ret = -1; | 947 | int ret = -1; |
758 | int slot; | 948 | int slot; |
759 | int len; | 949 | int len; |
@@ -771,13 +961,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, | |||
771 | if (!path) | 961 | if (!path) |
772 | return -ENOMEM; | 962 | return -ENOMEM; |
773 | 963 | ||
774 | name_stack = kzalloc(BTRFS_PATH_NAME_MAX+1, GFP_NOFS); | 964 | ptr = &name[BTRFS_INO_LOOKUP_PATH_MAX]; |
775 | if (!name_stack) { | ||
776 | btrfs_free_path(path); | ||
777 | return -ENOMEM; | ||
778 | } | ||
779 | |||
780 | ptr = &name_stack[BTRFS_PATH_NAME_MAX]; | ||
781 | 965 | ||
782 | key.objectid = tree_id; | 966 | key.objectid = tree_id; |
783 | key.type = BTRFS_ROOT_ITEM_KEY; | 967 | key.type = BTRFS_ROOT_ITEM_KEY; |
@@ -802,14 +986,16 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, | |||
802 | btrfs_item_key_to_cpu(l, &key, slot); | 986 | btrfs_item_key_to_cpu(l, &key, slot); |
803 | 987 | ||
804 | if (ret > 0 && (key.objectid != dirid || | 988 | if (ret > 0 && (key.objectid != dirid || |
805 | key.type != BTRFS_INODE_REF_KEY)) | 989 | key.type != BTRFS_INODE_REF_KEY)) { |
990 | ret = -ENOENT; | ||
806 | goto out; | 991 | goto out; |
992 | } | ||
807 | 993 | ||
808 | iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref); | 994 | iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref); |
809 | len = btrfs_inode_ref_name_len(l, iref); | 995 | len = btrfs_inode_ref_name_len(l, iref); |
810 | ptr -= len + 1; | 996 | ptr -= len + 1; |
811 | total_len += len + 1; | 997 | total_len += len + 1; |
812 | if (ptr < name_stack) | 998 | if (ptr < name) |
813 | goto out; | 999 | goto out; |
814 | 1000 | ||
815 | *(ptr + len) = '/'; | 1001 | *(ptr + len) = '/'; |
@@ -824,14 +1010,41 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, | |||
824 | dirid = key.objectid; | 1010 | dirid = key.objectid; |
825 | 1011 | ||
826 | } | 1012 | } |
827 | if (ptr < name_stack) | 1013 | if (ptr < name) |
828 | goto out; | 1014 | goto out; |
829 | strncpy(name, ptr, total_len); | 1015 | memcpy(name, ptr, total_len); |
830 | name[total_len]='\0'; | 1016 | name[total_len]='\0'; |
831 | ret = 0; | 1017 | ret = 0; |
832 | out: | 1018 | out: |
833 | btrfs_free_path(path); | 1019 | btrfs_free_path(path); |
834 | kfree(name_stack); | 1020 | return ret; |
1021 | } | ||
1022 | |||
1023 | static noinline int btrfs_ioctl_ino_lookup(struct file *file, | ||
1024 | void __user *argp) | ||
1025 | { | ||
1026 | struct btrfs_ioctl_ino_lookup_args *args; | ||
1027 | struct inode *inode; | ||
1028 | int ret; | ||
1029 | |||
1030 | if (!capable(CAP_SYS_ADMIN)) | ||
1031 | return -EPERM; | ||
1032 | |||
1033 | args = kmalloc(sizeof(*args), GFP_KERNEL); | ||
1034 | if (copy_from_user(args, argp, sizeof(*args))) { | ||
1035 | kfree(args); | ||
1036 | return -EFAULT; | ||
1037 | } | ||
1038 | inode = fdentry(file)->d_inode; | ||
1039 | |||
1040 | ret = btrfs_search_path_in_tree(BTRFS_I(inode)->root->fs_info, | ||
1041 | args->treeid, args->objectid, | ||
1042 | args->name); | ||
1043 | |||
1044 | if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) | ||
1045 | ret = -EFAULT; | ||
1046 | |||
1047 | kfree(args); | ||
835 | return ret; | 1048 | return ret; |
836 | } | 1049 | } |
837 | 1050 | ||
@@ -1430,6 +1643,10 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
1430 | return btrfs_ioctl_trans_start(file); | 1643 | return btrfs_ioctl_trans_start(file); |
1431 | case BTRFS_IOC_TRANS_END: | 1644 | case BTRFS_IOC_TRANS_END: |
1432 | return btrfs_ioctl_trans_end(file); | 1645 | return btrfs_ioctl_trans_end(file); |
1646 | case BTRFS_IOC_TREE_SEARCH: | ||
1647 | return btrfs_ioctl_tree_search(file, argp); | ||
1648 | case BTRFS_IOC_INO_LOOKUP: | ||
1649 | return btrfs_ioctl_ino_lookup(file, argp); | ||
1433 | case BTRFS_IOC_SYNC: | 1650 | case BTRFS_IOC_SYNC: |
1434 | btrfs_sync_fs(file->f_dentry->d_sb, 1); | 1651 | btrfs_sync_fs(file->f_dentry->d_sb, 1); |
1435 | return 0; | 1652 | return 0; |
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index bc49914475eb..79c07b104f91 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h | |||
@@ -30,6 +30,68 @@ struct btrfs_ioctl_vol_args { | |||
30 | char name[BTRFS_PATH_NAME_MAX + 1]; | 30 | char name[BTRFS_PATH_NAME_MAX + 1]; |
31 | }; | 31 | }; |
32 | 32 | ||
33 | #define BTRFS_INO_LOOKUP_PATH_MAX 4080 | ||
34 | struct btrfs_ioctl_ino_lookup_args { | ||
35 | __u64 treeid; | ||
36 | __u64 objectid; | ||
37 | char name[BTRFS_INO_LOOKUP_PATH_MAX]; | ||
38 | }; | ||
39 | |||
40 | struct btrfs_ioctl_search_key { | ||
41 | /* which root are we searching. 0 is the tree of tree roots */ | ||
42 | __u64 tree_id; | ||
43 | |||
44 | /* keys returned will be >= min and <= max */ | ||
45 | __u64 min_objectid; | ||
46 | __u64 max_objectid; | ||
47 | |||
48 | /* keys returned will be >= min and <= max */ | ||
49 | __u64 min_offset; | ||
50 | __u64 max_offset; | ||
51 | |||
52 | /* max and min transids to search for */ | ||
53 | __u64 min_transid; | ||
54 | __u64 max_transid; | ||
55 | |||
56 | /* keys returned will be >= min and <= max */ | ||
57 | __u32 min_type; | ||
58 | __u32 max_type; | ||
59 | |||
60 | /* | ||
61 | * how many items did userland ask for, and how many are we | ||
62 | * returning | ||
63 | */ | ||
64 | __u32 nr_items; | ||
65 | |||
66 | /* align to 64 bits */ | ||
67 | __u32 unused; | ||
68 | |||
69 | /* some extra for later */ | ||
70 | __u64 unused1; | ||
71 | __u64 unused2; | ||
72 | __u64 unused3; | ||
73 | __u64 unused4; | ||
74 | }; | ||
75 | |||
76 | struct btrfs_ioctl_search_header { | ||
77 | __u64 transid; | ||
78 | __u64 objectid; | ||
79 | __u64 offset; | ||
80 | __u32 type; | ||
81 | __u32 len; | ||
82 | }; | ||
83 | |||
84 | #define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) | ||
85 | /* | ||
86 | * the buf is an array of search headers where | ||
87 | * each header is followed by the actual item | ||
88 | * the type field is expanded to 32 bits for alignment | ||
89 | */ | ||
90 | struct btrfs_ioctl_search_args { | ||
91 | struct btrfs_ioctl_search_key key; | ||
92 | char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; | ||
93 | }; | ||
94 | |||
33 | struct btrfs_ioctl_clone_range_args { | 95 | struct btrfs_ioctl_clone_range_args { |
34 | __s64 src_fd; | 96 | __s64 src_fd; |
35 | __u64 src_offset, src_length; | 97 | __u64 src_offset, src_length; |
@@ -67,4 +129,8 @@ struct btrfs_ioctl_clone_range_args { | |||
67 | struct btrfs_ioctl_vol_args) | 129 | struct btrfs_ioctl_vol_args) |
68 | #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ | 130 | #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ |
69 | struct btrfs_ioctl_vol_args) | 131 | struct btrfs_ioctl_vol_args) |
132 | #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ | ||
133 | struct btrfs_ioctl_search_args) | ||
134 | #define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ | ||
135 | struct btrfs_ioctl_ino_lookup_args) | ||
70 | #endif | 136 | #endif |