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 /fs/btrfs/ioctl.c | |
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>
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 249 |
1 files changed, 233 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; |