| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: ufs_quota2.c,v 1.29 2012/01/29 07:20:27 dholland Exp $ */ | | 1 | /* $NetBSD: ufs_quota2.c,v 1.30 2012/01/29 07:21:00 dholland Exp $ */ |
2 | /*- | | 2 | /*- |
3 | * Copyright (c) 2010 Manuel Bouyer | | 3 | * Copyright (c) 2010 Manuel Bouyer |
4 | * All rights reserved. | | 4 | * All rights reserved. |
5 | * | | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | | 7 | * modification, are permitted provided that the following conditions |
8 | * are met: | | 8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright | | 9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. | | 10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright | | 11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the | | 12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. | | 13 | * documentation and/or other materials provided with the distribution. |
14 | * | | 14 | * |
| @@ -16,33 +16,32 @@ | | | @@ -16,33 +16,32 @@ |
16 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 16 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
17 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 17 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
18 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 18 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
19 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 19 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 | * POSSIBILITY OF SUCH DAMAGE. | | 25 | * POSSIBILITY OF SUCH DAMAGE. |
26 | */ | | 26 | */ |
27 | | | 27 | |
28 | #include <sys/cdefs.h> | | 28 | #include <sys/cdefs.h> |
29 | __KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.29 2012/01/29 07:20:27 dholland Exp $"); | | 29 | __KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.30 2012/01/29 07:21:00 dholland Exp $"); |
30 | | | 30 | |
31 | #include <sys/buf.h> | | 31 | #include <sys/buf.h> |
32 | #include <sys/param.h> | | 32 | #include <sys/param.h> |
33 | #include <sys/kernel.h> | | 33 | #include <sys/kernel.h> |
34 | #include <sys/systm.h> | | 34 | #include <sys/systm.h> |
35 | #include <sys/malloc.h> | | | |
36 | #include <sys/namei.h> | | 35 | #include <sys/namei.h> |
37 | #include <sys/file.h> | | 36 | #include <sys/file.h> |
38 | #include <sys/proc.h> | | 37 | #include <sys/proc.h> |
39 | #include <sys/vnode.h> | | 38 | #include <sys/vnode.h> |
40 | #include <sys/mount.h> | | 39 | #include <sys/mount.h> |
41 | #include <sys/fstrans.h> | | 40 | #include <sys/fstrans.h> |
42 | #include <sys/kauth.h> | | 41 | #include <sys/kauth.h> |
43 | #include <sys/wapbl.h> | | 42 | #include <sys/wapbl.h> |
44 | #include <sys/quota.h> | | 43 | #include <sys/quota.h> |
45 | #include <sys/quotactl.h> | | 44 | #include <sys/quotactl.h> |
46 | | | 45 | |
47 | #include <ufs/ufs/quota2.h> | | 46 | #include <ufs/ufs/quota2.h> |
48 | #include <ufs/ufs/inode.h> | | 47 | #include <ufs/ufs/inode.h> |
| @@ -784,79 +783,62 @@ quota2_handle_cmd_delete(struct ufsmount | | | @@ -784,79 +783,62 @@ quota2_handle_cmd_delete(struct ufsmount |
784 | | | 783 | |
785 | out_dqlock: | | 784 | out_dqlock: |
786 | mutex_exit(&dqlock); | | 785 | mutex_exit(&dqlock); |
787 | out_wapbl: | | 786 | out_wapbl: |
788 | UFS_WAPBL_END(ump->um_mountp); | | 787 | UFS_WAPBL_END(ump->um_mountp); |
789 | out_il: | | 788 | out_il: |
790 | mutex_exit(&dq->dq_interlock); | | 789 | mutex_exit(&dq->dq_interlock); |
791 | out_dq: | | 790 | out_dq: |
792 | dqrele(NULLVP, dq); | | 791 | dqrele(NULLVP, dq); |
793 | return error; | | 792 | return error; |
794 | } | | 793 | } |
795 | | | 794 | |
796 | static int | | 795 | static int |
797 | quota2_result_add_q2e(struct ufsmount *ump, int idtype, | | 796 | quota2_fetch_q2e(struct ufsmount *ump, const struct quotakey *qk, |
798 | int id, struct quotakey *keys, struct quotaval *vals, unsigned pos, | | 797 | struct quota2_entry *ret) |
799 | int skipfirst, int skiplast) | | | |
800 | { | | 798 | { |
801 | struct dquot *dq; | | 799 | struct dquot *dq; |
802 | int error; | | 800 | int error; |
803 | struct quota2_entry *q2ep, q2e; | | 801 | struct quota2_entry *q2ep; |
804 | struct buf *bp; | | 802 | struct buf *bp; |
805 | const int needswap = UFS_MPNEEDSWAP(ump); | | 803 | const int needswap = UFS_MPNEEDSWAP(ump); |
806 | | | 804 | |
807 | error = dqget(NULLVP, id, ump, idtype, &dq); | | 805 | error = dqget(NULLVP, qk->qk_id, ump, qk->qk_idtype, &dq); |
808 | if (error) | | 806 | if (error) |
809 | return error; | | 807 | return error; |
810 | | | 808 | |
811 | mutex_enter(&dq->dq_interlock); | | 809 | mutex_enter(&dq->dq_interlock); |
812 | if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) { | | 810 | if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) { |
813 | mutex_exit(&dq->dq_interlock); | | 811 | mutex_exit(&dq->dq_interlock); |
814 | dqrele(NULLVP, dq); | | 812 | dqrele(NULLVP, dq); |
815 | return ENOENT; | | 813 | return ENOENT; |
816 | } | | 814 | } |
817 | error = getq2e(ump, idtype, dq->dq2_lblkno, dq->dq2_blkoff, | | 815 | error = getq2e(ump, qk->qk_idtype, dq->dq2_lblkno, dq->dq2_blkoff, |
818 | &bp, &q2ep, 0); | | 816 | &bp, &q2ep, 0); |
819 | if (error) { | | 817 | if (error) { |
820 | mutex_exit(&dq->dq_interlock); | | 818 | mutex_exit(&dq->dq_interlock); |
821 | dqrele(NULLVP, dq); | | 819 | dqrele(NULLVP, dq); |
822 | return error; | | 820 | return error; |
823 | } | | 821 | } |
824 | quota2_ufs_rwq2e(q2ep, &q2e, needswap); | | 822 | quota2_ufs_rwq2e(q2ep, ret, needswap); |
825 | brelse(bp, 0); | | 823 | brelse(bp, 0); |
826 | mutex_exit(&dq->dq_interlock); | | 824 | mutex_exit(&dq->dq_interlock); |
827 | dqrele(NULLVP, dq); | | 825 | dqrele(NULLVP, dq); |
828 | | | 826 | |
829 | if (skipfirst == 0) { | | | |
830 | keys[pos].qk_idtype = idtype; | | | |
831 | keys[pos].qk_objtype = QUOTA_OBJTYPE_BLOCKS; | | | |
832 | q2e_to_quotaval(&q2e, 0, &keys[pos].qk_id, | | | |
833 | QL_BLOCK, &vals[pos]); | | | |
834 | pos++; | | | |
835 | } | | | |
836 | | | | |
837 | if (skiplast == 0) { | | | |
838 | keys[pos].qk_idtype = idtype; | | | |
839 | keys[pos].qk_objtype = QUOTA_OBJTYPE_FILES; | | | |
840 | q2e_to_quotaval(&q2e, 0, &keys[pos].qk_id, | | | |
841 | QL_FILE, &vals[pos]); | | | |
842 | pos++; | | | |
843 | } | | | |
844 | | | | |
845 | return 0; | | 827 | return 0; |
846 | } | | 828 | } |
847 | | | 829 | |
848 | static int | | 830 | static int |
849 | quota2_fetch_q2e(struct ufsmount *ump, const struct quotakey *qk, | | 831 | quota2_fetch_quotaval(struct ufsmount *ump, const struct quotakey *qk, |
850 | struct quotaval *ret) | | 832 | struct quotaval *ret) |
851 | { | | 833 | { |
852 | struct dquot *dq; | | 834 | struct dquot *dq; |
853 | int error; | | 835 | int error; |
854 | struct quota2_entry *q2ep, q2e; | | 836 | struct quota2_entry *q2ep, q2e; |
855 | struct buf *bp; | | 837 | struct buf *bp; |
856 | const int needswap = UFS_MPNEEDSWAP(ump); | | 838 | const int needswap = UFS_MPNEEDSWAP(ump); |
857 | id_t id2; | | 839 | id_t id2; |
858 | | | 840 | |
859 | error = dqget(NULLVP, qk->qk_id, ump, qk->qk_idtype, &dq); | | 841 | error = dqget(NULLVP, qk->qk_id, ump, qk->qk_idtype, &dq); |
860 | if (error) | | 842 | if (error) |
861 | return error; | | 843 | return error; |
862 | | | 844 | |
| @@ -913,47 +895,97 @@ quota2_handle_cmd_get(struct ufsmount *u | | | @@ -913,47 +895,97 @@ quota2_handle_cmd_get(struct ufsmount *u |
913 | mutex_enter(&dqlock); | | 895 | mutex_enter(&dqlock); |
914 | error = getq2h(ump, qk->qk_idtype, &bp, &q2h, 0); | | 896 | error = getq2h(ump, qk->qk_idtype, &bp, &q2h, 0); |
915 | if (error) { | | 897 | if (error) { |
916 | mutex_exit(&dqlock); | | 898 | mutex_exit(&dqlock); |
917 | return error; | | 899 | return error; |
918 | } | | 900 | } |
919 | quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap); | | 901 | quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap); |
920 | mutex_exit(&dqlock); | | 902 | mutex_exit(&dqlock); |
921 | brelse(bp, 0); | | 903 | brelse(bp, 0); |
922 | q2e_to_quotaval(&q2e, qk->qk_id == QUOTA_DEFAULTID, &id2, | | 904 | q2e_to_quotaval(&q2e, qk->qk_id == QUOTA_DEFAULTID, &id2, |
923 | qk->qk_objtype, ret); | | 905 | qk->qk_objtype, ret); |
924 | (void)id2; | | 906 | (void)id2; |
925 | } else | | 907 | } else |
926 | error = quota2_fetch_q2e(ump, qk, ret); | | 908 | error = quota2_fetch_quotaval(ump, qk, ret); |
927 | | | 909 | |
928 | return error; | | 910 | return error; |
929 | } | | 911 | } |
930 | | | 912 | |
| | | 913 | /* |
| | | 914 | * Cursor structure we used. |
| | | 915 | * |
| | | 916 | * This will get stored in userland between calls so we must not assume |
| | | 917 | * it isn't arbitrarily corrupted. |
| | | 918 | */ |
931 | struct ufsq2_cursor { | | 919 | struct ufsq2_cursor { |
932 | uint32_t q2c_magic; /* magic number */ | | 920 | uint32_t q2c_magic; /* magic number */ |
933 | int q2c_hashsize; /* size of hash table at last go */ | | 921 | int q2c_hashsize; /* size of hash table at last go */ |
934 | | | 922 | |
935 | int q2c_users_done; /* true if we've returned all user data */ | | 923 | int q2c_users_done; /* true if we've returned all user data */ |
936 | int q2c_groups_done; /* true if we've returned all group data */ | | 924 | int q2c_groups_done; /* true if we've returned all group data */ |
937 | int q2c_defaults_done; /* true if we've returned the default values */ | | 925 | int q2c_defaults_done; /* true if we've returned the default values */ |
938 | int q2c_hashpos; /* slot to start at in hash table */ | | 926 | int q2c_hashpos; /* slot to start at in hash table */ |
939 | int q2c_uidpos; /* number of ids we've handled */ | | 927 | int q2c_uidpos; /* number of ids we've handled */ |
940 | int q2c_blocks_done; /* true if we've returned the blocks value */ | | 928 | int q2c_blocks_done; /* true if we've returned the blocks value */ |
941 | }; | | 929 | }; |
942 | | | 930 | |
| | | 931 | /* |
| | | 932 | * State of a single cursorget call, or at least the part of it that |
| | | 933 | * needs to be passed around. |
| | | 934 | */ |
| | | 935 | struct q2cursor_state { |
| | | 936 | /* data return pointers */ |
| | | 937 | struct quotakey *keys; |
| | | 938 | struct quotaval *vals; |
| | | 939 | |
| | | 940 | /* key/value counters */ |
| | | 941 | unsigned maxkeyvals; |
| | | 942 | unsigned numkeys; /* number of keys assigned */ |
| | | 943 | |
| | | 944 | /* ID to key/value conversion state */ |
| | | 945 | int skipfirst; /* if true skip first key/value */ |
| | | 946 | int skiplast; /* if true skip last key/value */ |
| | | 947 | |
| | | 948 | /* ID counters */ |
| | | 949 | unsigned maxids; /* maximum number of IDs to handle */ |
| | | 950 | unsigned numids; /* number of IDs handled */ |
| | | 951 | }; |
| | | 952 | |
| | | 953 | /* |
| | | 954 | * Additional structure for getids callback. |
| | | 955 | */ |
| | | 956 | struct q2cursor_getids { |
| | | 957 | struct q2cursor_state *state; |
| | | 958 | int idtype; |
| | | 959 | unsigned skip; /* number of ids to skip over */ |
| | | 960 | unsigned new_skip; /* number of ids to skip over next time */ |
| | | 961 | unsigned skipped; /* number skipped so far */ |
| | | 962 | }; |
| | | 963 | |
| | | 964 | /* |
| | | 965 | * Cursor-related functions |
| | | 966 | */ |
| | | 967 | |
| | | 968 | /* magic number */ |
943 | #define Q2C_MAGIC (0xbeebe111) | | 969 | #define Q2C_MAGIC (0xbeebe111) |
944 | | | 970 | |
| | | 971 | /* extract cursor from caller form */ |
945 | #define Q2CURSOR(qkc) ((struct ufsq2_cursor *)&qkc->u.qkc_space[0]) | | 972 | #define Q2CURSOR(qkc) ((struct ufsq2_cursor *)&qkc->u.qkc_space[0]) |
946 | | | 973 | |
| | | 974 | /* |
| | | 975 | * Check that a cursor we're handed is something like valid. If |
| | | 976 | * someone munges it and it still passes these checks, they'll get |
| | | 977 | * partial or odd results back but won't break anything. |
| | | 978 | */ |
947 | static int | | 979 | static int |
948 | q2cursor_check(struct ufsq2_cursor *cursor) | | 980 | q2cursor_check(struct ufsq2_cursor *cursor) |
949 | { | | 981 | { |
950 | if (cursor->q2c_magic != Q2C_MAGIC) { | | 982 | if (cursor->q2c_magic != Q2C_MAGIC) { |
951 | return EINVAL; | | 983 | return EINVAL; |
952 | } | | 984 | } |
953 | if (cursor->q2c_hashsize < 0) { | | 985 | if (cursor->q2c_hashsize < 0) { |
954 | return EINVAL; | | 986 | return EINVAL; |
955 | } | | 987 | } |
956 | | | 988 | |
957 | if (cursor->q2c_users_done != 0 && cursor->q2c_users_done != 1) { | | 989 | if (cursor->q2c_users_done != 0 && cursor->q2c_users_done != 1) { |
958 | return EINVAL; | | 990 | return EINVAL; |
959 | } | | 991 | } |
| @@ -962,260 +994,393 @@ q2cursor_check(struct ufsq2_cursor *curs | | | @@ -962,260 +994,393 @@ q2cursor_check(struct ufsq2_cursor *curs |
962 | } | | 994 | } |
963 | if (cursor->q2c_defaults_done != 0 && cursor->q2c_defaults_done != 1) { | | 995 | if (cursor->q2c_defaults_done != 0 && cursor->q2c_defaults_done != 1) { |
964 | return EINVAL; | | 996 | return EINVAL; |
965 | } | | 997 | } |
966 | if (cursor->q2c_hashpos < 0 || cursor->q2c_uidpos < 0) { | | 998 | if (cursor->q2c_hashpos < 0 || cursor->q2c_uidpos < 0) { |
967 | return EINVAL; | | 999 | return EINVAL; |
968 | } | | 1000 | } |
969 | if (cursor->q2c_blocks_done != 0 && cursor->q2c_blocks_done != 1) { | | 1001 | if (cursor->q2c_blocks_done != 0 && cursor->q2c_blocks_done != 1) { |
970 | return EINVAL; | | 1002 | return EINVAL; |
971 | } | | 1003 | } |
972 | return 0; | | 1004 | return 0; |
973 | } | | 1005 | } |
974 | | | 1006 | |
975 | struct getuids { | | 1007 | /* |
976 | long nuids; /* number of uids in array */ | | 1008 | * Set up the q2cursor state. |
977 | long maxuids; /* number of uids allocated */ | | 1009 | */ |
978 | uid_t *uids; /* array of uids, dynamically allocated */ | | 1010 | static void |
979 | long skip; | | 1011 | q2cursor_initstate(struct q2cursor_state *state, struct quotakey *keys, |
980 | long seen; | | 1012 | struct quotaval *vals, unsigned maxkeyvals, int blocks_done) |
981 | long limit; | | 1013 | { |
982 | }; | | 1014 | state->keys = keys; |
| | | 1015 | state->vals = vals; |
| | | 1016 | |
| | | 1017 | state->maxkeyvals = maxkeyvals; |
| | | 1018 | state->numkeys = 0; |
| | | 1019 | |
| | | 1020 | /* |
| | | 1021 | * For each ID there are two quotavals to return. If the |
| | | 1022 | * maximum number of entries to return is odd, we might want |
| | | 1023 | * to skip the first quotaval of the first ID, or the last |
| | | 1024 | * quotaval of the last ID, but not both. So the number of IDs |
| | | 1025 | * we want is (up to) half the number of return slots we have, |
| | | 1026 | * rounded up. |
| | | 1027 | */ |
| | | 1028 | |
| | | 1029 | state->maxids = (state->maxkeyvals + 1) / 2; |
| | | 1030 | state->numids = 0; |
| | | 1031 | if (state->maxkeyvals % 2) { |
| | | 1032 | if (blocks_done) { |
| | | 1033 | state->skipfirst = 1; |
| | | 1034 | state->skiplast = 0; |
| | | 1035 | } else { |
| | | 1036 | state->skipfirst = 0; |
| | | 1037 | state->skiplast = 1; |
| | | 1038 | } |
| | | 1039 | } else { |
| | | 1040 | state->skipfirst = 0; |
| | | 1041 | state->skiplast = 0; |
| | | 1042 | } |
| | | 1043 | } |
| | | 1044 | |
| | | 1045 | /* |
| | | 1046 | * Choose which idtype we're going to work on. If doing a full |
| | | 1047 | * iteration, we do users first, then groups, but either might be |
| | | 1048 | * disabled or marked to skip via cursorsetidtype(), so don't make |
| | | 1049 | * silly assumptions. |
| | | 1050 | */ |
| | | 1051 | static int |
| | | 1052 | q2cursor_pickidtype(struct ufsq2_cursor *cursor, int *idtype_ret) |
| | | 1053 | { |
| | | 1054 | if (cursor->q2c_users_done == 0) { |
| | | 1055 | *idtype_ret = QUOTA_IDTYPE_USER; |
| | | 1056 | } else if (cursor->q2c_groups_done == 0) { |
| | | 1057 | *idtype_ret = QUOTA_IDTYPE_GROUP; |
| | | 1058 | } else { |
| | | 1059 | return EAGAIN; |
| | | 1060 | } |
| | | 1061 | return 0; |
| | | 1062 | } |
| | | 1063 | |
| | | 1064 | /* |
| | | 1065 | * Add an ID to the current state. Sets up either one or two keys to |
| | | 1066 | * refer to it, depending on whether it's first/last and the setting |
| | | 1067 | * of skipfirst. (skiplast does not need to be explicitly tested) |
| | | 1068 | */ |
| | | 1069 | static void |
| | | 1070 | q2cursor_addid(struct q2cursor_state *state, int idtype, id_t id) |
| | | 1071 | { |
| | | 1072 | KASSERT(state->numids < state->maxids); |
| | | 1073 | KASSERT(state->numkeys < state->maxkeyvals); |
983 | | | 1074 | |
| | | 1075 | if (!state->skipfirst || state->numkeys > 0) { |
| | | 1076 | state->keys[state->numkeys].qk_idtype = idtype; |
| | | 1077 | state->keys[state->numkeys].qk_id = id; |
| | | 1078 | state->keys[state->numkeys].qk_objtype = QUOTA_OBJTYPE_BLOCKS; |
| | | 1079 | state->numkeys++; |
| | | 1080 | } |
| | | 1081 | if (state->numkeys < state->maxkeyvals) { |
| | | 1082 | state->keys[state->numkeys].qk_idtype = idtype; |
| | | 1083 | state->keys[state->numkeys].qk_id = id; |
| | | 1084 | state->keys[state->numkeys].qk_objtype = QUOTA_OBJTYPE_FILES; |
| | | 1085 | state->numkeys++; |
| | | 1086 | } else { |
| | | 1087 | KASSERT(state->skiplast); |
| | | 1088 | } |
| | | 1089 | state->numids++; |
| | | 1090 | } |
| | | 1091 | |
| | | 1092 | /* |
| | | 1093 | * Callback function for getting IDs. Update counting and call addid. |
| | | 1094 | */ |
984 | static int | | 1095 | static int |
985 | quota2_getuids_callback(struct ufsmount *ump, uint64_t *offp, | | 1096 | q2cursor_getids_callback(struct ufsmount *ump, uint64_t *offp, |
986 | struct quota2_entry *q2ep, uint64_t off, void *v) | | 1097 | struct quota2_entry *q2ep, uint64_t off, void *v) |
987 | { | | 1098 | { |
988 | struct getuids *gu = v; | | 1099 | struct q2cursor_getids *gi = v; |
989 | uid_t *newuids; | | 1100 | id_t id; |
990 | long newmax; | | | |
991 | #ifdef FFS_EI | | 1101 | #ifdef FFS_EI |
992 | const int needswap = UFS_MPNEEDSWAP(ump); | | 1102 | const int needswap = UFS_MPNEEDSWAP(ump); |
993 | #endif | | 1103 | #endif |
994 | | | 1104 | |
995 | if (gu->skip > 0) { | | 1105 | if (gi->skipped < gi->skip) { |
996 | gu->skip--; | | 1106 | gi->skipped++; |
997 | return 0; | | 1107 | return 0; |
998 | } | | 1108 | } |
999 | if (gu->nuids == gu->maxuids) { | | 1109 | id = ufs_rw32(q2ep->q2e_uid, needswap); |
1000 | newmax = gu->maxuids + PAGE_SIZE / sizeof(uid_t); | | 1110 | q2cursor_addid(gi->state, gi->idtype, id); |
1001 | newuids = realloc(gu->uids, newmax * sizeof(gu->uids[0]), | | 1111 | gi->new_skip++; |
1002 | M_TEMP, M_WAITOK); | | 1112 | if (gi->state->numids >= gi->state->maxids) { |
1003 | if (newuids == NULL) { | | 1113 | /* got enough ids, stop now */ |
1004 | return ENOMEM; | | | |
1005 | } | | | |
1006 | gu->uids = newuids; | | | |
1007 | gu->maxuids = newmax; | | | |
1008 | } | | | |
1009 | gu->uids[gu->nuids] = ufs_rw32(q2ep->q2e_uid, needswap); | | | |
1010 | gu->nuids++; | | | |
1011 | gu->seen++; | | | |
1012 | if (gu->nuids == gu->limit) { | | | |
1013 | return Q2WL_ABORT; | | 1114 | return Q2WL_ABORT; |
1014 | } | | 1115 | } |
1015 | return 0; | | 1116 | return 0; |
1016 | } | | 1117 | } |
1017 | | | 1118 | |
1018 | int | | 1119 | /* |
1019 | quota2_handle_cmd_cursorget(struct ufsmount *ump, struct quotakcursor *qkc, | | 1120 | * Fill in a batch of quotakeys by scanning one or more hash chains. |
1020 | struct quotakey *keys, struct quotaval *vals, unsigned maxreturn, | | 1121 | */ |
1021 | unsigned *ret) | | 1122 | static int |
| | | 1123 | q2cursor_getkeys(struct ufsmount *ump, int idtype, struct ufsq2_cursor *cursor, |
| | | 1124 | struct q2cursor_state *state, |
| | | 1125 | int *hashsize_ret, struct quota2_entry *default_q2e_ret) |
1022 | { | | 1126 | { |
1023 | int error; | | 1127 | const int needswap = UFS_MPNEEDSWAP(ump); |
1024 | struct ufsq2_cursor *cursor; | | | |
1025 | struct quota2_header *q2h; | | | |
1026 | struct quota2_entry q2e; | | | |
1027 | struct buf *hbp; | | 1128 | struct buf *hbp; |
1028 | uint64_t offset; | | 1129 | struct quota2_header *q2h; |
1029 | int idtype; | | | |
1030 | int can_switch_idtype; | | | |
1031 | int i, j; | | | |
1032 | int quota2_hash_size; | | 1130 | int quota2_hash_size; |
1033 | const int needswap = UFS_MPNEEDSWAP(ump); | | 1131 | struct q2cursor_getids gi; |
1034 | struct getuids gu; | | 1132 | uint64_t offset; |
1035 | long excess; | | 1133 | int error; |
1036 | id_t junkid; | | | |
1037 | struct quotaval qv; | | | |
1038 | unsigned num, maxnum; | | | |
1039 | int skipfirst, skiplast; | | | |
1040 | int numreturn; | | | |
1041 | | | | |
1042 | cursor = Q2CURSOR(qkc); | | | |
1043 | error = q2cursor_check(cursor); | | | |
1044 | if (error) { | | | |
1045 | return error; | | | |
1046 | } | | | |
1047 | | | | |
1048 | CTASSERT(USRQUOTA == QUOTA_IDTYPE_USER); | | | |
1049 | CTASSERT(GRPQUOTA == QUOTA_IDTYPE_GROUP); | | | |
1050 | | | | |
1051 | if (cursor->q2c_users_done == 0 && | | | |
1052 | ump->um_quotas[USRQUOTA] == NULLVP) { | | | |
1053 | cursor->q2c_users_done = 1; | | | |
1054 | } | | | |
1055 | if (cursor->q2c_groups_done == 0 && | | | |
1056 | ump->um_quotas[GRPQUOTA] == NULLVP) { | | | |
1057 | cursor->q2c_groups_done = 1; | | | |
1058 | } | | | |
1059 | | | | |
1060 | restart: | | | |
1061 | | | | |
1062 | if (cursor->q2c_users_done == 0) { | | | |
1063 | idtype = QUOTA_IDTYPE_USER; | | | |
1064 | can_switch_idtype = 1; | | | |
1065 | } else if (cursor->q2c_groups_done == 0) { | | | |
1066 | idtype = QUOTA_IDTYPE_GROUP; | | | |
1067 | can_switch_idtype = 0; | | | |
1068 | } else { | | | |
1069 | /* nothing more to do, return 0 */ | | | |
1070 | *ret = 0; | | | |
1071 | return 0; | | | |
1072 | } | | | |
1073 | | | | |
1074 | KASSERT(ump->um_quotas[idtype] != NULLVP); | | | |
1075 | | | 1134 | |
1076 | numreturn = 0; | | 1135 | /* |
| | | 1136 | * Read the header block. |
| | | 1137 | */ |
1077 | | | 1138 | |
1078 | mutex_enter(&dqlock); | | 1139 | mutex_enter(&dqlock); |
1079 | error = getq2h(ump, idtype, &hbp, &q2h, 0); | | 1140 | error = getq2h(ump, idtype, &hbp, &q2h, 0); |
1080 | if (error) { | | 1141 | if (error) { |
1081 | mutex_exit(&dqlock); | | 1142 | mutex_exit(&dqlock); |
1082 | return error; | | 1143 | return error; |
1083 | } | | 1144 | } |
1084 | | | 1145 | |
1085 | if (cursor->q2c_defaults_done == 0) { | | | |
1086 | quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap); | | | |
1087 | if (cursor->q2c_blocks_done == 0) { | | | |
1088 | q2e_to_quotaval(&q2e, 1, &junkid, QL_BLOCK, &qv); | | | |
1089 | keys[numreturn].qk_idtype = idtype; | | | |
1090 | keys[numreturn].qk_id = QUOTA_DEFAULTID; | | | |
1091 | keys[numreturn].qk_objtype = QUOTA_OBJTYPE_BLOCKS; | | | |
1092 | vals[numreturn++] = qv; | | | |
1093 | cursor->q2c_blocks_done = 1; | | | |
1094 | } | | | |
1095 | if (cursor->q2c_blocks_done == 1) { | | | |
1096 | q2e_to_quotaval(&q2e, 1, &junkid, QL_FILE, &qv); | | | |
1097 | keys[numreturn].qk_idtype = idtype; | | | |
1098 | keys[numreturn].qk_id = QUOTA_DEFAULTID; | | | |
1099 | keys[numreturn].qk_objtype = QUOTA_OBJTYPE_FILES; | | | |
1100 | vals[numreturn++] = qv; | | | |
1101 | cursor->q2c_blocks_done = 0; | | | |
1102 | cursor->q2c_defaults_done = 1; | | | |
1103 | } | | | |
1104 | } | | | |
1105 | | | | |
1106 | /* | | | |
1107 | * we can't directly get entries as we can't walk the list | | | |
1108 | * with qdlock and grab dq_interlock to read the entries | | | |
1109 | * at the same time. So just walk the lists to build a list of uid, | | | |
1110 | * and then read entries for these uids | | | |
1111 | */ | | | |
1112 | memset(&gu, 0, sizeof(gu)); | | | |
1113 | quota2_hash_size = ufs_rw16(q2h->q2h_hash_size, needswap); | | | |
1114 | | | | |
1115 | /* if the table size has changed, make the caller start over */ | | 1146 | /* if the table size has changed, make the caller start over */ |
| | | 1147 | quota2_hash_size = ufs_rw16(q2h->q2h_hash_size, needswap); |
1116 | if (cursor->q2c_hashsize == 0) { | | 1148 | if (cursor->q2c_hashsize == 0) { |
1117 | cursor->q2c_hashsize = quota2_hash_size; | | 1149 | cursor->q2c_hashsize = quota2_hash_size; |
1118 | } else if (cursor->q2c_hashsize != quota2_hash_size) { | | 1150 | } else if (cursor->q2c_hashsize != quota2_hash_size) { |
1119 | error = EDEADLK; | | 1151 | error = EDEADLK; |
1120 | goto fail; | | 1152 | goto scanfail; |
1121 | } | | 1153 | } |
1122 | | | 1154 | |
1123 | gu.skip = cursor->q2c_uidpos; | | 1155 | /* grab the entry with the default values out of the header */ |
1124 | gu.seen = 0; | | 1156 | quota2_ufs_rwq2e(&q2h->q2h_defentry, default_q2e_ret, needswap); |
1125 | gu.limit = (maxreturn - numreturn) / 2; | | 1157 | |
1126 | if (gu.limit == 0 && (maxreturn - numreturn) > 0) { | | 1158 | /* If we haven't done the defaults yet, that goes first. */ |
1127 | gu.limit = 1; | | 1159 | if (cursor->q2c_defaults_done == 0) { |
1128 | } | | 1160 | q2cursor_addid(state, idtype, QUOTA_DEFAULTID); |
1129 | for (i = cursor->q2c_hashpos; i < quota2_hash_size ; i++) { | | 1161 | cursor->q2c_defaults_done = 1; |
1130 | offset = q2h->q2h_entries[i]; | | 1162 | } |
1131 | gu.seen = 0; | | 1163 | |
1132 | error = quota2_walk_list(ump, hbp, idtype, &offset, 0, &gu, | | 1164 | gi.state = state; |
1133 | quota2_getuids_callback); | | 1165 | gi.idtype = idtype; |
1134 | if (error && error != Q2WL_ABORT) { | | 1166 | |
1135 | if (gu.uids != NULL) | | 1167 | while (state->numids < state->maxids) { |
1136 | free(gu.uids, M_TEMP); | | 1168 | if (cursor->q2c_hashpos >= quota2_hash_size) { |
| | | 1169 | /* nothing more left */ |
1137 | break; | | 1170 | break; |
1138 | } | | 1171 | } |
| | | 1172 | |
| | | 1173 | /* scan this hash chain */ |
| | | 1174 | gi.skip = cursor->q2c_uidpos; |
| | | 1175 | gi.new_skip = gi.skip; |
| | | 1176 | gi.skipped = 0; |
| | | 1177 | offset = q2h->q2h_entries[cursor->q2c_hashpos]; |
| | | 1178 | |
| | | 1179 | error = quota2_walk_list(ump, hbp, idtype, &offset, 0, &gi, |
| | | 1180 | q2cursor_getids_callback); |
1139 | if (error == Q2WL_ABORT) { | | 1181 | if (error == Q2WL_ABORT) { |
1140 | /* got enough uids for now */ | | 1182 | /* callback stopped before reading whole chain */ |
| | | 1183 | cursor->q2c_uidpos = gi.new_skip; |
| | | 1184 | /* not an error */ |
1141 | error = 0; | | 1185 | error = 0; |
1142 | } | | 1186 | } else if (error) { |
1143 | if (gu.nuids > gu.limit) { | | | |
1144 | excess = gu.nuids - gu.limit; | | | |
1145 | KASSERT(excess < gu.seen); | | | |
1146 | gu.seen -= excess; | | | |
1147 | gu.nuids -= excess; | | | |
1148 | } | | | |
1149 | if (gu.nuids == gu.limit) { | | | |
1150 | break; | | 1187 | break; |
| | | 1188 | } else { |
| | | 1189 | /* read whole chain, advance to next */ |
| | | 1190 | cursor->q2c_uidpos = 0; |
| | | 1191 | cursor->q2c_hashpos++; |
1151 | } | | 1192 | } |
1152 | } | | 1193 | } |
1153 | KASSERT(gu.nuids <= gu.limit); | | | |
1154 | cursor->q2c_hashpos = i; | | | |
1155 | cursor->q2c_uidpos = gu.seen; | | | |
1156 | | | 1194 | |
1157 | fail: | | 1195 | scanfail: |
1158 | mutex_exit(&dqlock); | | 1196 | mutex_exit(&dqlock); |
1159 | brelse(hbp, 0); | | 1197 | brelse(hbp, 0); |
1160 | if (error) | | 1198 | if (error) |
1161 | return error; | | 1199 | return error; |
1162 | | | 1200 | |
1163 | if (gu.nuids == 0) { | | 1201 | *hashsize_ret = quota2_hash_size; |
1164 | if (idtype == QUOTA_IDTYPE_USER) | | 1202 | return 0; |
1165 | cursor->q2c_users_done = 1; | | 1203 | } |
1166 | else | | 1204 | |
1167 | cursor->q2c_groups_done = 1; | | 1205 | /* |
1168 | if (can_switch_idtype) { | | 1206 | * Fetch the quotavals for the quotakeys. |
1169 | goto restart; | | 1207 | */ |
| | | 1208 | static int |
| | | 1209 | q2cursor_getvals(struct ufsmount *ump, struct q2cursor_state *state, |
| | | 1210 | const struct quota2_entry *default_q2e) |
| | | 1211 | { |
| | | 1212 | int hasid; |
| | | 1213 | id_t loadedid, id; |
| | | 1214 | unsigned pos; |
| | | 1215 | struct quota2_entry q2e; |
| | | 1216 | int objtype; |
| | | 1217 | int error; |
| | | 1218 | |
| | | 1219 | hasid = 0; |
| | | 1220 | loadedid = 0; |
| | | 1221 | for (pos = 0; pos < state->numkeys; pos++) { |
| | | 1222 | id = state->keys[pos].qk_id; |
| | | 1223 | if (!hasid || id != loadedid) { |
| | | 1224 | hasid = 1; |
| | | 1225 | loadedid = id; |
| | | 1226 | if (id == QUOTA_DEFAULTID) { |
| | | 1227 | q2e = *default_q2e; |
| | | 1228 | } else { |
| | | 1229 | error = quota2_fetch_q2e(ump, |
| | | 1230 | &state->keys[pos], |
| | | 1231 | &q2e); |
| | | 1232 | if (error == ENOENT) { |
| | | 1233 | /* something changed - start over */ |
| | | 1234 | error = EDEADLK; |
| | | 1235 | } |
| | | 1236 | if (error) { |
| | | 1237 | return error; |
| | | 1238 | } |
| | | 1239 | } |
1170 | } | | 1240 | } |
| | | 1241 | |
| | | 1242 | |
| | | 1243 | objtype = state->keys[pos].qk_objtype; |
| | | 1244 | KASSERT(objtype >= 0 && objtype < N_QL); |
| | | 1245 | q2val_to_quotaval(&q2e.q2e_val[objtype], &state->vals[pos]); |
1171 | } | | 1246 | } |
1172 | | | 1247 | |
1173 | maxnum = gu.nuids*2; | | 1248 | return 0; |
| | | 1249 | } |
| | | 1250 | |
| | | 1251 | /* |
| | | 1252 | * Handle cursorget. |
| | | 1253 | * |
| | | 1254 | * We can't just read keys and values directly, because we can't walk |
| | | 1255 | * the list with qdlock and grab dq_interlock to read the entries at |
| | | 1256 | * the same time. So we're going to do two passes: one to figure out |
| | | 1257 | * which IDs we want and fill in the keys, and then a second to use |
| | | 1258 | * the keys to fetch the values. |
| | | 1259 | */ |
| | | 1260 | int |
| | | 1261 | quota2_handle_cmd_cursorget(struct ufsmount *ump, struct quotakcursor *qkc, |
| | | 1262 | struct quotakey *keys, struct quotaval *vals, unsigned maxreturn, |
| | | 1263 | unsigned *ret) |
| | | 1264 | { |
| | | 1265 | int error; |
| | | 1266 | struct ufsq2_cursor *cursor; |
| | | 1267 | struct ufsq2_cursor newcursor; |
| | | 1268 | struct q2cursor_state state; |
| | | 1269 | struct quota2_entry default_q2e; |
| | | 1270 | int idtype; |
| | | 1271 | int quota2_hash_size; |
1174 | | | 1272 | |
1175 | /* | | 1273 | /* |
1176 | * If we've already sent back the blocks value for the first id, | | 1274 | * Convert and validate the cursor. |
1177 | * don't send it again (skipfirst). | | | |
1178 | * | | | |
1179 | * If we have an odd number of available result slots and we | | | |
1180 | * aren't going to skip the first result entry, we need to | | | |
1181 | * leave off the last result entry (skiplast). | | | |
1182 | */ | | 1275 | */ |
1183 | skipfirst = (cursor->q2c_blocks_done != 0); | | 1276 | cursor = Q2CURSOR(qkc); |
1184 | skiplast = skipfirst == 0 && (maxreturn < maxnum); | | 1277 | error = q2cursor_check(cursor); |
1185 | num = 0; | | 1278 | if (error) { |
1186 | for (j = 0; j < gu.nuids; j++) { | | 1279 | return error; |
1187 | error = quota2_result_add_q2e(ump, idtype, | | 1280 | } |
1188 | gu.uids[j], keys, vals, numreturn + j*2, | | 1281 | |
1189 | j == 0 && skipfirst, | | 1282 | /* |
1190 | j + 1 == gu.nuids && skiplast); | | 1283 | * Make sure our on-disk codes match the values of the |
1191 | if (error == ENOENT) | | 1284 | * FS-independent ones. This avoids the need for explicit |
1192 | continue; | | 1285 | * conversion (which would be a NOP anyway and thus easily |
1193 | if (error) | | 1286 | * left out or called in the wrong places...) |
1194 | break; | | 1287 | */ |
1195 | if ((j == 0 && skipfirst) || (j + 1 == gu.nuids && skiplast)) { | | 1288 | CTASSERT(QUOTA_IDTYPE_USER == USRQUOTA); |
1196 | num += 1; | | 1289 | CTASSERT(QUOTA_IDTYPE_GROUP == GRPQUOTA); |
| | | 1290 | CTASSERT(QUOTA_OBJTYPE_BLOCKS == QL_BLOCK); |
| | | 1291 | CTASSERT(QUOTA_OBJTYPE_FILES == QL_FILE); |
| | | 1292 | |
| | | 1293 | /* |
| | | 1294 | * If some of the idtypes aren't configured/enabled, arrange |
| | | 1295 | * to skip over them. |
| | | 1296 | */ |
| | | 1297 | if (cursor->q2c_users_done == 0 && |
| | | 1298 | ump->um_quotas[USRQUOTA] == NULLVP) { |
| | | 1299 | cursor->q2c_users_done = 1; |
| | | 1300 | } |
| | | 1301 | if (cursor->q2c_groups_done == 0 && |
| | | 1302 | ump->um_quotas[GRPQUOTA] == NULLVP) { |
| | | 1303 | cursor->q2c_groups_done = 1; |
| | | 1304 | } |
| | | 1305 | |
| | | 1306 | /* Loop over, potentially, both idtypes */ |
| | | 1307 | while (1) { |
| | | 1308 | |
| | | 1309 | /* Choose id type */ |
| | | 1310 | error = q2cursor_pickidtype(cursor, &idtype); |
| | | 1311 | if (error == EAGAIN) { |
| | | 1312 | /* nothing more to do, return 0 */ |
| | | 1313 | *ret = 0; |
| | | 1314 | return 0; |
| | | 1315 | } |
| | | 1316 | KASSERT(ump->um_quotas[idtype] != NULLVP); |
| | | 1317 | |
| | | 1318 | /* |
| | | 1319 | * Initialize the per-call iteration state. Copy the |
| | | 1320 | * cursor state so we can update it in place but back |
| | | 1321 | * out on error. |
| | | 1322 | */ |
| | | 1323 | q2cursor_initstate(&state, keys, vals, maxreturn, |
| | | 1324 | cursor->q2c_blocks_done); |
| | | 1325 | newcursor = *cursor; |
| | | 1326 | |
| | | 1327 | /* Assign keys */ |
| | | 1328 | error = q2cursor_getkeys(ump, idtype, &newcursor, &state, |
| | | 1329 | "a2_hash_size, &default_q2e); |
| | | 1330 | if (error) { |
| | | 1331 | return error; |
| | | 1332 | } |
| | | 1333 | |
| | | 1334 | /* Now fill in the values. */ |
| | | 1335 | error = q2cursor_getvals(ump, &state, &default_q2e); |
| | | 1336 | if (error) { |
| | | 1337 | return error; |
| | | 1338 | } |
| | | 1339 | |
| | | 1340 | /* |
| | | 1341 | * Now that we aren't going to fail and lose what we |
| | | 1342 | * did so far, we can update the cursor state. |
| | | 1343 | */ |
| | | 1344 | |
| | | 1345 | if (newcursor.q2c_hashpos >= quota2_hash_size) { |
| | | 1346 | if (idtype == QUOTA_IDTYPE_USER) |
| | | 1347 | cursor->q2c_users_done = 1; |
| | | 1348 | else |
| | | 1349 | cursor->q2c_groups_done = 1; |
| | | 1350 | |
| | | 1351 | /* start over on another id type */ |
| | | 1352 | cursor->q2c_hashsize = 0; |
| | | 1353 | cursor->q2c_defaults_done = 0; |
| | | 1354 | cursor->q2c_hashpos = 0; |
| | | 1355 | cursor->q2c_uidpos = 0; |
| | | 1356 | cursor->q2c_blocks_done = 0; |
1197 | } else { | | 1357 | } else { |
1198 | num += 2; | | 1358 | *cursor = newcursor; |
| | | 1359 | cursor->q2c_blocks_done = state.skiplast; |
1199 | } | | 1360 | } |
1200 | } | | | |
1201 | numreturn += num; | | | |
1202 | *ret = numreturn; | | | |
1203 | | | 1361 | |
1204 | cursor->q2c_blocks_done = skiplast; | | 1362 | /* |
| | | 1363 | * If we have something to return, return it. |
| | | 1364 | * Otherwise, continue to the other idtype, if any, |
| | | 1365 | * and only return zero at end of iteration. |
| | | 1366 | */ |
| | | 1367 | if (state.numkeys > 0) { |
| | | 1368 | break; |
| | | 1369 | } |
| | | 1370 | } |
1205 | | | 1371 | |
1206 | if (gu.uids != NULL) | | 1372 | *ret = state.numkeys; |
1207 | free(gu.uids, M_TEMP); | | 1373 | return 0; |
1208 | return error; | | | |
1209 | } | | 1374 | } |
1210 | | | 1375 | |
1211 | int | | 1376 | int |
1212 | quota2_handle_cmd_cursoropen(struct ufsmount *ump, struct quotakcursor *qkc) | | 1377 | quota2_handle_cmd_cursoropen(struct ufsmount *ump, struct quotakcursor *qkc) |
1213 | { | | 1378 | { |
1214 | struct ufsq2_cursor *cursor; | | 1379 | struct ufsq2_cursor *cursor; |
1215 | | | 1380 | |
1216 | CTASSERT(sizeof(*cursor) <= sizeof(qkc->u.qkc_space)); | | 1381 | CTASSERT(sizeof(*cursor) <= sizeof(qkc->u.qkc_space)); |
1217 | cursor = Q2CURSOR(qkc); | | 1382 | cursor = Q2CURSOR(qkc); |
1218 | | | 1383 | |
1219 | cursor->q2c_magic = Q2C_MAGIC; | | 1384 | cursor->q2c_magic = Q2C_MAGIC; |
1220 | cursor->q2c_hashsize = 0; | | 1385 | cursor->q2c_hashsize = 0; |
1221 | | | 1386 | |