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 |
