diff options
Diffstat (limited to 'fs/xfs/xfs_ioctl.c')
| -rw-r--r-- | fs/xfs/xfs_ioctl.c | 294 |
1 files changed, 261 insertions, 33 deletions
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index fe29aa61293c..6f7848cd5527 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c | |||
| @@ -11,9 +11,8 @@ | |||
| 11 | #include "xfs_trans_resv.h" | 11 | #include "xfs_trans_resv.h" |
| 12 | #include "xfs_mount.h" | 12 | #include "xfs_mount.h" |
| 13 | #include "xfs_inode.h" | 13 | #include "xfs_inode.h" |
| 14 | #include "xfs_ioctl.h" | ||
| 15 | #include "xfs_alloc.h" | ||
| 16 | #include "xfs_rtalloc.h" | 14 | #include "xfs_rtalloc.h" |
| 15 | #include "xfs_iwalk.h" | ||
| 17 | #include "xfs_itable.h" | 16 | #include "xfs_itable.h" |
| 18 | #include "xfs_error.h" | 17 | #include "xfs_error.h" |
| 19 | #include "xfs_attr.h" | 18 | #include "xfs_attr.h" |
| @@ -25,7 +24,6 @@ | |||
| 25 | #include "xfs_export.h" | 24 | #include "xfs_export.h" |
| 26 | #include "xfs_trace.h" | 25 | #include "xfs_trace.h" |
| 27 | #include "xfs_icache.h" | 26 | #include "xfs_icache.h" |
| 28 | #include "xfs_symlink.h" | ||
| 29 | #include "xfs_trans.h" | 27 | #include "xfs_trans.h" |
| 30 | #include "xfs_acl.h" | 28 | #include "xfs_acl.h" |
| 31 | #include "xfs_btree.h" | 29 | #include "xfs_btree.h" |
| @@ -36,14 +34,8 @@ | |||
| 36 | #include "xfs_ag.h" | 34 | #include "xfs_ag.h" |
| 37 | #include "xfs_health.h" | 35 | #include "xfs_health.h" |
| 38 | 36 | ||
| 39 | #include <linux/capability.h> | ||
| 40 | #include <linux/cred.h> | ||
| 41 | #include <linux/dcache.h> | ||
| 42 | #include <linux/mount.h> | 37 | #include <linux/mount.h> |
| 43 | #include <linux/namei.h> | 38 | #include <linux/namei.h> |
| 44 | #include <linux/pagemap.h> | ||
| 45 | #include <linux/slab.h> | ||
| 46 | #include <linux/exportfs.h> | ||
| 47 | 39 | ||
| 48 | /* | 40 | /* |
| 49 | * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to | 41 | * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to |
| @@ -721,16 +713,45 @@ out_unlock: | |||
| 721 | return error; | 713 | return error; |
| 722 | } | 714 | } |
| 723 | 715 | ||
| 716 | /* Return 0 on success or positive error */ | ||
| 717 | int | ||
| 718 | xfs_fsbulkstat_one_fmt( | ||
| 719 | struct xfs_ibulk *breq, | ||
| 720 | const struct xfs_bulkstat *bstat) | ||
| 721 | { | ||
| 722 | struct xfs_bstat bs1; | ||
| 723 | |||
| 724 | xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat); | ||
| 725 | if (copy_to_user(breq->ubuffer, &bs1, sizeof(bs1))) | ||
| 726 | return -EFAULT; | ||
| 727 | return xfs_ibulk_advance(breq, sizeof(struct xfs_bstat)); | ||
| 728 | } | ||
| 729 | |||
| 730 | int | ||
| 731 | xfs_fsinumbers_fmt( | ||
| 732 | struct xfs_ibulk *breq, | ||
| 733 | const struct xfs_inumbers *igrp) | ||
| 734 | { | ||
| 735 | struct xfs_inogrp ig1; | ||
| 736 | |||
| 737 | xfs_inumbers_to_inogrp(&ig1, igrp); | ||
| 738 | if (copy_to_user(breq->ubuffer, &ig1, sizeof(struct xfs_inogrp))) | ||
| 739 | return -EFAULT; | ||
| 740 | return xfs_ibulk_advance(breq, sizeof(struct xfs_inogrp)); | ||
| 741 | } | ||
| 742 | |||
| 724 | STATIC int | 743 | STATIC int |
| 725 | xfs_ioc_bulkstat( | 744 | xfs_ioc_fsbulkstat( |
| 726 | xfs_mount_t *mp, | 745 | xfs_mount_t *mp, |
| 727 | unsigned int cmd, | 746 | unsigned int cmd, |
| 728 | void __user *arg) | 747 | void __user *arg) |
| 729 | { | 748 | { |
| 730 | xfs_fsop_bulkreq_t bulkreq; | 749 | struct xfs_fsop_bulkreq bulkreq; |
| 731 | int count; /* # of records returned */ | 750 | struct xfs_ibulk breq = { |
| 732 | xfs_ino_t inlast; /* last inode number */ | 751 | .mp = mp, |
| 733 | int done; | 752 | .ocount = 0, |
| 753 | }; | ||
| 754 | xfs_ino_t lastino; | ||
| 734 | int error; | 755 | int error; |
| 735 | 756 | ||
| 736 | /* done = 1 if there are more stats to get and if bulkstat */ | 757 | /* done = 1 if there are more stats to get and if bulkstat */ |
| @@ -742,41 +763,243 @@ xfs_ioc_bulkstat( | |||
| 742 | if (XFS_FORCED_SHUTDOWN(mp)) | 763 | if (XFS_FORCED_SHUTDOWN(mp)) |
| 743 | return -EIO; | 764 | return -EIO; |
| 744 | 765 | ||
| 745 | if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t))) | 766 | if (copy_from_user(&bulkreq, arg, sizeof(struct xfs_fsop_bulkreq))) |
| 746 | return -EFAULT; | 767 | return -EFAULT; |
| 747 | 768 | ||
| 748 | if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64))) | 769 | if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64))) |
| 749 | return -EFAULT; | 770 | return -EFAULT; |
| 750 | 771 | ||
| 751 | if ((count = bulkreq.icount) <= 0) | 772 | if (bulkreq.icount <= 0) |
| 752 | return -EINVAL; | 773 | return -EINVAL; |
| 753 | 774 | ||
| 754 | if (bulkreq.ubuffer == NULL) | 775 | if (bulkreq.ubuffer == NULL) |
| 755 | return -EINVAL; | 776 | return -EINVAL; |
| 756 | 777 | ||
| 757 | if (cmd == XFS_IOC_FSINUMBERS) | 778 | breq.ubuffer = bulkreq.ubuffer; |
| 758 | error = xfs_inumbers(mp, &inlast, &count, | 779 | breq.icount = bulkreq.icount; |
| 759 | bulkreq.ubuffer, xfs_inumbers_fmt); | 780 | |
| 760 | else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) | 781 | /* |
| 761 | error = xfs_bulkstat_one(mp, inlast, bulkreq.ubuffer, | 782 | * FSBULKSTAT_SINGLE expects that *lastip contains the inode number |
| 762 | sizeof(xfs_bstat_t), NULL, &done); | 783 | * that we want to stat. However, FSINUMBERS and FSBULKSTAT expect |
| 763 | else /* XFS_IOC_FSBULKSTAT */ | 784 | * that *lastip contains either zero or the number of the last inode to |
| 764 | error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one, | 785 | * be examined by the previous call and return results starting with |
| 765 | sizeof(xfs_bstat_t), bulkreq.ubuffer, | 786 | * the next inode after that. The new bulk request back end functions |
| 766 | &done); | 787 | * take the inode to start with, so we have to compute the startino |
| 788 | * parameter from lastino to maintain correct function. lastino == 0 | ||
| 789 | * is a special case because it has traditionally meant "first inode | ||
| 790 | * in filesystem". | ||
| 791 | */ | ||
| 792 | if (cmd == XFS_IOC_FSINUMBERS) { | ||
| 793 | breq.startino = lastino ? lastino + 1 : 0; | ||
| 794 | error = xfs_inumbers(&breq, xfs_fsinumbers_fmt); | ||
| 795 | lastino = breq.startino - 1; | ||
| 796 | } else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) { | ||
| 797 | breq.startino = lastino; | ||
| 798 | breq.icount = 1; | ||
| 799 | error = xfs_bulkstat_one(&breq, xfs_fsbulkstat_one_fmt); | ||
| 800 | } else { /* XFS_IOC_FSBULKSTAT */ | ||
| 801 | breq.startino = lastino ? lastino + 1 : 0; | ||
| 802 | error = xfs_bulkstat(&breq, xfs_fsbulkstat_one_fmt); | ||
| 803 | lastino = breq.startino - 1; | ||
| 804 | } | ||
| 767 | 805 | ||
| 768 | if (error) | 806 | if (error) |
| 769 | return error; | 807 | return error; |
| 770 | 808 | ||
| 771 | if (bulkreq.ocount != NULL) { | 809 | if (bulkreq.lastip != NULL && |
| 772 | if (copy_to_user(bulkreq.lastip, &inlast, | 810 | copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t))) |
| 773 | sizeof(xfs_ino_t))) | 811 | return -EFAULT; |
| 774 | return -EFAULT; | ||
| 775 | 812 | ||
| 776 | if (copy_to_user(bulkreq.ocount, &count, sizeof(count))) | 813 | if (bulkreq.ocount != NULL && |
| 777 | return -EFAULT; | 814 | copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32))) |
| 815 | return -EFAULT; | ||
| 816 | |||
| 817 | return 0; | ||
| 818 | } | ||
| 819 | |||
| 820 | /* Return 0 on success or positive error */ | ||
| 821 | static int | ||
| 822 | xfs_bulkstat_fmt( | ||
| 823 | struct xfs_ibulk *breq, | ||
| 824 | const struct xfs_bulkstat *bstat) | ||
| 825 | { | ||
| 826 | if (copy_to_user(breq->ubuffer, bstat, sizeof(struct xfs_bulkstat))) | ||
| 827 | return -EFAULT; | ||
| 828 | return xfs_ibulk_advance(breq, sizeof(struct xfs_bulkstat)); | ||
| 829 | } | ||
| 830 | |||
| 831 | /* | ||
| 832 | * Check the incoming bulk request @hdr from userspace and initialize the | ||
| 833 | * internal @breq bulk request appropriately. Returns 0 if the bulk request | ||
| 834 | * should proceed; XFS_ITER_ABORT if there's nothing to do; or the usual | ||
| 835 | * negative error code. | ||
| 836 | */ | ||
| 837 | static int | ||
| 838 | xfs_bulk_ireq_setup( | ||
| 839 | struct xfs_mount *mp, | ||
| 840 | struct xfs_bulk_ireq *hdr, | ||
| 841 | struct xfs_ibulk *breq, | ||
| 842 | void __user *ubuffer) | ||
| 843 | { | ||
| 844 | if (hdr->icount == 0 || | ||
| 845 | (hdr->flags & ~XFS_BULK_IREQ_FLAGS_ALL) || | ||
| 846 | memchr_inv(hdr->reserved, 0, sizeof(hdr->reserved))) | ||
| 847 | return -EINVAL; | ||
| 848 | |||
| 849 | breq->startino = hdr->ino; | ||
| 850 | breq->ubuffer = ubuffer; | ||
| 851 | breq->icount = hdr->icount; | ||
| 852 | breq->ocount = 0; | ||
| 853 | breq->flags = 0; | ||
| 854 | |||
| 855 | /* | ||
| 856 | * The @ino parameter is a special value, so we must look it up here. | ||
| 857 | * We're not allowed to have IREQ_AGNO, and we only return one inode | ||
| 858 | * worth of data. | ||
| 859 | */ | ||
| 860 | if (hdr->flags & XFS_BULK_IREQ_SPECIAL) { | ||
| 861 | if (hdr->flags & XFS_BULK_IREQ_AGNO) | ||
| 862 | return -EINVAL; | ||
| 863 | |||
| 864 | switch (hdr->ino) { | ||
| 865 | case XFS_BULK_IREQ_SPECIAL_ROOT: | ||
| 866 | hdr->ino = mp->m_sb.sb_rootino; | ||
| 867 | break; | ||
| 868 | default: | ||
| 869 | return -EINVAL; | ||
| 870 | } | ||
| 871 | breq->icount = 1; | ||
| 778 | } | 872 | } |
| 779 | 873 | ||
| 874 | /* | ||
| 875 | * The IREQ_AGNO flag means that we only want results from a given AG. | ||
| 876 | * If @hdr->ino is zero, we start iterating in that AG. If @hdr->ino is | ||
| 877 | * beyond the specified AG then we return no results. | ||
| 878 | */ | ||
| 879 | if (hdr->flags & XFS_BULK_IREQ_AGNO) { | ||
| 880 | if (hdr->agno >= mp->m_sb.sb_agcount) | ||
| 881 | return -EINVAL; | ||
| 882 | |||
| 883 | if (breq->startino == 0) | ||
| 884 | breq->startino = XFS_AGINO_TO_INO(mp, hdr->agno, 0); | ||
| 885 | else if (XFS_INO_TO_AGNO(mp, breq->startino) < hdr->agno) | ||
| 886 | return -EINVAL; | ||
| 887 | |||
| 888 | breq->flags |= XFS_IBULK_SAME_AG; | ||
| 889 | |||
| 890 | /* Asking for an inode past the end of the AG? We're done! */ | ||
| 891 | if (XFS_INO_TO_AGNO(mp, breq->startino) > hdr->agno) | ||
| 892 | return XFS_ITER_ABORT; | ||
| 893 | } else if (hdr->agno) | ||
| 894 | return -EINVAL; | ||
| 895 | |||
| 896 | /* Asking for an inode past the end of the FS? We're done! */ | ||
| 897 | if (XFS_INO_TO_AGNO(mp, breq->startino) >= mp->m_sb.sb_agcount) | ||
| 898 | return XFS_ITER_ABORT; | ||
| 899 | |||
| 900 | return 0; | ||
| 901 | } | ||
| 902 | |||
| 903 | /* | ||
| 904 | * Update the userspace bulk request @hdr to reflect the end state of the | ||
| 905 | * internal bulk request @breq. | ||
| 906 | */ | ||
| 907 | static void | ||
| 908 | xfs_bulk_ireq_teardown( | ||
| 909 | struct xfs_bulk_ireq *hdr, | ||
| 910 | struct xfs_ibulk *breq) | ||
| 911 | { | ||
| 912 | hdr->ino = breq->startino; | ||
| 913 | hdr->ocount = breq->ocount; | ||
| 914 | } | ||
| 915 | |||
| 916 | /* Handle the v5 bulkstat ioctl. */ | ||
| 917 | STATIC int | ||
| 918 | xfs_ioc_bulkstat( | ||
| 919 | struct xfs_mount *mp, | ||
| 920 | unsigned int cmd, | ||
| 921 | struct xfs_bulkstat_req __user *arg) | ||
| 922 | { | ||
| 923 | struct xfs_bulk_ireq hdr; | ||
| 924 | struct xfs_ibulk breq = { | ||
| 925 | .mp = mp, | ||
| 926 | }; | ||
| 927 | int error; | ||
| 928 | |||
| 929 | if (!capable(CAP_SYS_ADMIN)) | ||
| 930 | return -EPERM; | ||
| 931 | |||
| 932 | if (XFS_FORCED_SHUTDOWN(mp)) | ||
| 933 | return -EIO; | ||
| 934 | |||
| 935 | if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr))) | ||
| 936 | return -EFAULT; | ||
| 937 | |||
| 938 | error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->bulkstat); | ||
| 939 | if (error == XFS_ITER_ABORT) | ||
| 940 | goto out_teardown; | ||
| 941 | if (error < 0) | ||
| 942 | return error; | ||
| 943 | |||
| 944 | error = xfs_bulkstat(&breq, xfs_bulkstat_fmt); | ||
| 945 | if (error) | ||
| 946 | return error; | ||
| 947 | |||
| 948 | out_teardown: | ||
| 949 | xfs_bulk_ireq_teardown(&hdr, &breq); | ||
| 950 | if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr))) | ||
| 951 | return -EFAULT; | ||
| 952 | |||
| 953 | return 0; | ||
| 954 | } | ||
| 955 | |||
| 956 | STATIC int | ||
| 957 | xfs_inumbers_fmt( | ||
| 958 | struct xfs_ibulk *breq, | ||
| 959 | const struct xfs_inumbers *igrp) | ||
| 960 | { | ||
| 961 | if (copy_to_user(breq->ubuffer, igrp, sizeof(struct xfs_inumbers))) | ||
| 962 | return -EFAULT; | ||
| 963 | return xfs_ibulk_advance(breq, sizeof(struct xfs_inumbers)); | ||
| 964 | } | ||
| 965 | |||
| 966 | /* Handle the v5 inumbers ioctl. */ | ||
| 967 | STATIC int | ||
| 968 | xfs_ioc_inumbers( | ||
| 969 | struct xfs_mount *mp, | ||
| 970 | unsigned int cmd, | ||
| 971 | struct xfs_inumbers_req __user *arg) | ||
| 972 | { | ||
| 973 | struct xfs_bulk_ireq hdr; | ||
| 974 | struct xfs_ibulk breq = { | ||
| 975 | .mp = mp, | ||
| 976 | }; | ||
| 977 | int error; | ||
| 978 | |||
| 979 | if (!capable(CAP_SYS_ADMIN)) | ||
| 980 | return -EPERM; | ||
| 981 | |||
| 982 | if (XFS_FORCED_SHUTDOWN(mp)) | ||
| 983 | return -EIO; | ||
| 984 | |||
| 985 | if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr))) | ||
| 986 | return -EFAULT; | ||
| 987 | |||
| 988 | error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->inumbers); | ||
| 989 | if (error == XFS_ITER_ABORT) | ||
| 990 | goto out_teardown; | ||
| 991 | if (error < 0) | ||
| 992 | return error; | ||
| 993 | |||
| 994 | error = xfs_inumbers(&breq, xfs_inumbers_fmt); | ||
| 995 | if (error) | ||
| 996 | return error; | ||
| 997 | |||
| 998 | out_teardown: | ||
| 999 | xfs_bulk_ireq_teardown(&hdr, &breq); | ||
| 1000 | if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr))) | ||
| 1001 | return -EFAULT; | ||
| 1002 | |||
| 780 | return 0; | 1003 | return 0; |
| 781 | } | 1004 | } |
| 782 | 1005 | ||
| @@ -1926,7 +2149,12 @@ xfs_file_ioctl( | |||
| 1926 | case XFS_IOC_FSBULKSTAT_SINGLE: | 2149 | case XFS_IOC_FSBULKSTAT_SINGLE: |
| 1927 | case XFS_IOC_FSBULKSTAT: | 2150 | case XFS_IOC_FSBULKSTAT: |
| 1928 | case XFS_IOC_FSINUMBERS: | 2151 | case XFS_IOC_FSINUMBERS: |
| 2152 | return xfs_ioc_fsbulkstat(mp, cmd, arg); | ||
| 2153 | |||
| 2154 | case XFS_IOC_BULKSTAT: | ||
| 1929 | return xfs_ioc_bulkstat(mp, cmd, arg); | 2155 | return xfs_ioc_bulkstat(mp, cmd, arg); |
| 2156 | case XFS_IOC_INUMBERS: | ||
| 2157 | return xfs_ioc_inumbers(mp, cmd, arg); | ||
| 1930 | 2158 | ||
| 1931 | case XFS_IOC_FSGEOMETRY_V1: | 2159 | case XFS_IOC_FSGEOMETRY_V1: |
| 1932 | return xfs_ioc_fsgeometry(mp, arg, 3); | 2160 | return xfs_ioc_fsgeometry(mp, arg, 3); |
