| @@ -1,816 +1,817 @@ | | | @@ -1,816 +1,817 @@ |
1 | /* $NetBSD: ufs_quota.c,v 1.86 2012/01/29 06:50:15 dholland Exp $ */ | | 1 | /* $NetBSD: ufs_quota.c,v 1.87 2012/01/29 06:50:53 dholland Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1982, 1986, 1990, 1993, 1995 | | 4 | * Copyright (c) 1982, 1986, 1990, 1993, 1995 |
5 | * The Regents of the University of California. All rights reserved. | | 5 | * The Regents of the University of California. All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to Berkeley by | | 7 | * This code is derived from software contributed to Berkeley by |
8 | * Robert Elz at The University of Melbourne. | | 8 | * Robert Elz at The University of Melbourne. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors | | 18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software | | 19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. | | 20 | * without specific prior written permission. |
21 | * | | 21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. | | 32 | * SUCH DAMAGE. |
33 | * | | 33 | * |
34 | * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 | | 34 | * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 |
35 | */ | | 35 | */ |
36 | | | 36 | |
37 | #include <sys/cdefs.h> | | 37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: ufs_quota.c,v 1.86 2012/01/29 06:50:15 dholland Exp $"); | | 38 | __KERNEL_RCSID(0, "$NetBSD: ufs_quota.c,v 1.87 2012/01/29 06:50:53 dholland Exp $"); |
39 | | | 39 | |
40 | #if defined(_KERNEL_OPT) | | 40 | #if defined(_KERNEL_OPT) |
41 | #include "opt_quota.h" | | 41 | #include "opt_quota.h" |
42 | #endif | | 42 | #endif |
43 | #include <sys/param.h> | | 43 | #include <sys/param.h> |
44 | #include <sys/kernel.h> | | 44 | #include <sys/kernel.h> |
45 | #include <sys/systm.h> | | 45 | #include <sys/systm.h> |
46 | #include <sys/namei.h> | | 46 | #include <sys/namei.h> |
47 | #include <sys/file.h> | | 47 | #include <sys/file.h> |
48 | #include <sys/proc.h> | | 48 | #include <sys/proc.h> |
49 | #include <sys/vnode.h> | | 49 | #include <sys/vnode.h> |
50 | #include <sys/mount.h> | | 50 | #include <sys/mount.h> |
51 | #include <sys/kauth.h> | | 51 | #include <sys/kauth.h> |
52 | | | 52 | |
53 | #include <sys/quotactl.h> | | 53 | #include <sys/quotactl.h> |
54 | #include <ufs/ufs/quota.h> | | 54 | #include <ufs/ufs/quota.h> |
55 | #include <ufs/ufs/inode.h> | | 55 | #include <ufs/ufs/inode.h> |
56 | #include <ufs/ufs/ufsmount.h> | | 56 | #include <ufs/ufs/ufsmount.h> |
57 | #include <ufs/ufs/ufs_extern.h> | | 57 | #include <ufs/ufs/ufs_extern.h> |
58 | #include <ufs/ufs/ufs_quota.h> | | 58 | #include <ufs/ufs/ufs_quota.h> |
59 | #include <quota/quotaprop.h> | | 59 | #include <quota/quotaprop.h> |
60 | | | 60 | |
61 | kmutex_t dqlock; | | 61 | kmutex_t dqlock; |
62 | kcondvar_t dqcv; | | 62 | kcondvar_t dqcv; |
63 | | | 63 | |
64 | /* | | 64 | /* |
65 | * Code pertaining to management of the in-core dquot data structures. | | 65 | * Code pertaining to management of the in-core dquot data structures. |
66 | */ | | 66 | */ |
67 | #define DQHASH(dqvp, id) \ | | 67 | #define DQHASH(dqvp, id) \ |
68 | (((((long)(dqvp)) >> 8) + id) & dqhash) | | 68 | (((((long)(dqvp)) >> 8) + id) & dqhash) |
69 | static LIST_HEAD(dqhashhead, dquot) *dqhashtbl; | | 69 | static LIST_HEAD(dqhashhead, dquot) *dqhashtbl; |
70 | static u_long dqhash; | | 70 | static u_long dqhash; |
71 | static pool_cache_t dquot_cache; | | 71 | static pool_cache_t dquot_cache; |
72 | | | 72 | |
73 | | | 73 | |
74 | static int quota_handle_cmd_get_version(struct mount *, struct lwp *, | | 74 | static int quota_handle_cmd_get_version(struct mount *, struct lwp *, |
75 | struct vfs_quotactl_args *args); | | 75 | struct vfs_quotactl_args *args); |
76 | static int quota_handle_cmd_get(struct mount *, struct lwp *, | | 76 | static int quota_handle_cmd_get(struct mount *, struct lwp *, |
77 | struct vfs_quotactl_args *args); | | 77 | struct vfs_quotactl_args *args); |
78 | static int quota_handle_cmd_put(struct mount *, struct lwp *, | | 78 | static int quota_handle_cmd_put(struct mount *, struct lwp *, |
79 | struct vfs_quotactl_args *args); | | 79 | struct vfs_quotactl_args *args); |
80 | static int quota_handle_cmd_getall(struct mount *, struct lwp *, | | 80 | static int quota_handle_cmd_getall(struct mount *, struct lwp *, |
81 | struct vfs_quotactl_args *args); | | 81 | struct vfs_quotactl_args *args); |
82 | static int quota_handle_cmd_clear(struct mount *, struct lwp *, | | 82 | static int quota_handle_cmd_clear(struct mount *, struct lwp *, |
83 | struct vfs_quotactl_args *args); | | 83 | struct vfs_quotactl_args *args); |
84 | static int quota_handle_cmd_quotaon(struct mount *, struct lwp *, | | 84 | static int quota_handle_cmd_quotaon(struct mount *, struct lwp *, |
85 | struct vfs_quotactl_args *args); | | 85 | struct vfs_quotactl_args *args); |
86 | static int quota_handle_cmd_quotaoff(struct mount *, struct lwp *, | | 86 | static int quota_handle_cmd_quotaoff(struct mount *, struct lwp *, |
87 | struct vfs_quotactl_args *args); | | 87 | struct vfs_quotactl_args *args); |
88 | | | 88 | |
89 | /* | | 89 | /* |
90 | * Initialize the quota fields of an inode. | | 90 | * Initialize the quota fields of an inode. |
91 | */ | | 91 | */ |
92 | void | | 92 | void |
93 | ufsquota_init(struct inode *ip) | | 93 | ufsquota_init(struct inode *ip) |
94 | { | | 94 | { |
95 | int i; | | 95 | int i; |
96 | | | 96 | |
97 | for (i = 0; i < MAXQUOTAS; i++) | | 97 | for (i = 0; i < MAXQUOTAS; i++) |
98 | ip->i_dquot[i] = NODQUOT; | | 98 | ip->i_dquot[i] = NODQUOT; |
99 | } | | 99 | } |
100 | | | 100 | |
101 | /* | | 101 | /* |
102 | * Release the quota fields from an inode. | | 102 | * Release the quota fields from an inode. |
103 | */ | | 103 | */ |
104 | void | | 104 | void |
105 | ufsquota_free(struct inode *ip) | | 105 | ufsquota_free(struct inode *ip) |
106 | { | | 106 | { |
107 | int i; | | 107 | int i; |
108 | | | 108 | |
109 | for (i = 0; i < MAXQUOTAS; i++) { | | 109 | for (i = 0; i < MAXQUOTAS; i++) { |
110 | dqrele(ITOV(ip), ip->i_dquot[i]); | | 110 | dqrele(ITOV(ip), ip->i_dquot[i]); |
111 | ip->i_dquot[i] = NODQUOT; | | 111 | ip->i_dquot[i] = NODQUOT; |
112 | } | | 112 | } |
113 | } | | 113 | } |
114 | | | 114 | |
115 | /* | | 115 | /* |
116 | * Update disk usage, and take corrective action. | | 116 | * Update disk usage, and take corrective action. |
117 | */ | | 117 | */ |
118 | int | | 118 | int |
119 | chkdq(struct inode *ip, int64_t change, kauth_cred_t cred, int flags) | | 119 | chkdq(struct inode *ip, int64_t change, kauth_cred_t cred, int flags) |
120 | { | | 120 | { |
121 | /* do not track snapshot usage, or we will deadlock */ | | 121 | /* do not track snapshot usage, or we will deadlock */ |
122 | if ((ip->i_flags & SF_SNAPSHOT) != 0) | | 122 | if ((ip->i_flags & SF_SNAPSHOT) != 0) |
123 | return 0; | | 123 | return 0; |
124 | | | 124 | |
125 | #ifdef QUOTA | | 125 | #ifdef QUOTA |
126 | if (ip->i_ump->um_flags & UFS_QUOTA) | | 126 | if (ip->i_ump->um_flags & UFS_QUOTA) |
127 | return chkdq1(ip, change, cred, flags); | | 127 | return chkdq1(ip, change, cred, flags); |
128 | #endif | | 128 | #endif |
129 | #ifdef QUOTA2 | | 129 | #ifdef QUOTA2 |
130 | if (ip->i_ump->um_flags & UFS_QUOTA2) | | 130 | if (ip->i_ump->um_flags & UFS_QUOTA2) |
131 | return chkdq2(ip, change, cred, flags); | | 131 | return chkdq2(ip, change, cred, flags); |
132 | #endif | | 132 | #endif |
133 | return 0; | | 133 | return 0; |
134 | } | | 134 | } |
135 | | | 135 | |
136 | /* | | 136 | /* |
137 | * Check the inode limit, applying corrective action. | | 137 | * Check the inode limit, applying corrective action. |
138 | */ | | 138 | */ |
139 | int | | 139 | int |
140 | chkiq(struct inode *ip, int32_t change, kauth_cred_t cred, int flags) | | 140 | chkiq(struct inode *ip, int32_t change, kauth_cred_t cred, int flags) |
141 | { | | 141 | { |
142 | /* do not track snapshot usage, or we will deadlock */ | | 142 | /* do not track snapshot usage, or we will deadlock */ |
143 | if ((ip->i_flags & SF_SNAPSHOT) != 0) | | 143 | if ((ip->i_flags & SF_SNAPSHOT) != 0) |
144 | return 0; | | 144 | return 0; |
145 | #ifdef QUOTA | | 145 | #ifdef QUOTA |
146 | if (ip->i_ump->um_flags & UFS_QUOTA) | | 146 | if (ip->i_ump->um_flags & UFS_QUOTA) |
147 | return chkiq1(ip, change, cred, flags); | | 147 | return chkiq1(ip, change, cred, flags); |
148 | #endif | | 148 | #endif |
149 | #ifdef QUOTA2 | | 149 | #ifdef QUOTA2 |
150 | if (ip->i_ump->um_flags & UFS_QUOTA2) | | 150 | if (ip->i_ump->um_flags & UFS_QUOTA2) |
151 | return chkiq2(ip, change, cred, flags); | | 151 | return chkiq2(ip, change, cred, flags); |
152 | #endif | | 152 | #endif |
153 | return 0; | | 153 | return 0; |
154 | } | | 154 | } |
155 | | | 155 | |
156 | int | | 156 | int |
157 | quota_handle_cmd(struct mount *mp, struct lwp *l, int op, | | 157 | quota_handle_cmd(struct mount *mp, struct lwp *l, int op, |
158 | struct vfs_quotactl_args *args) | | 158 | struct vfs_quotactl_args *args) |
159 | { | | 159 | { |
160 | int error = 0; | | 160 | int error = 0; |
161 | | | 161 | |
162 | switch (op) { | | 162 | switch (op) { |
163 | case QUOTACTL_GETVERSION: | | 163 | case QUOTACTL_GETVERSION: |
164 | error = quota_handle_cmd_get_version(mp, l, args); | | 164 | error = quota_handle_cmd_get_version(mp, l, args); |
165 | break; | | 165 | break; |
166 | case QUOTACTL_QUOTAON: | | 166 | case QUOTACTL_QUOTAON: |
167 | error = quota_handle_cmd_quotaon(mp, l, args); | | 167 | error = quota_handle_cmd_quotaon(mp, l, args); |
168 | break; | | 168 | break; |
169 | case QUOTACTL_QUOTAOFF: | | 169 | case QUOTACTL_QUOTAOFF: |
170 | error = quota_handle_cmd_quotaoff(mp, l, args); | | 170 | error = quota_handle_cmd_quotaoff(mp, l, args); |
171 | break; | | 171 | break; |
172 | case QUOTACTL_GET: | | 172 | case QUOTACTL_GET: |
173 | error = quota_handle_cmd_get(mp, l, args); | | 173 | error = quota_handle_cmd_get(mp, l, args); |
174 | break; | | 174 | break; |
175 | case QUOTACTL_PUT: | | 175 | case QUOTACTL_PUT: |
176 | error = quota_handle_cmd_put(mp, l, args); | | 176 | error = quota_handle_cmd_put(mp, l, args); |
177 | break; | | 177 | break; |
178 | case QUOTACTL_GETALL: | | 178 | case QUOTACTL_GETALL: |
179 | error = quota_handle_cmd_getall(mp, l, args); | | 179 | error = quota_handle_cmd_getall(mp, l, args); |
180 | break; | | 180 | break; |
181 | case QUOTACTL_CLEAR: | | 181 | case QUOTACTL_CLEAR: |
182 | error = quota_handle_cmd_clear(mp, l, args); | | 182 | error = quota_handle_cmd_clear(mp, l, args); |
183 | break; | | 183 | break; |
184 | default: | | 184 | default: |
185 | panic("Invalid quotactl operation %d\n", op); | | 185 | panic("Invalid quotactl operation %d\n", op); |
186 | } | | 186 | } |
187 | | | 187 | |
188 | return error; | | 188 | return error; |
189 | } | | 189 | } |
190 | | | 190 | |
191 | static int | | 191 | static int |
192 | quota_handle_cmd_get_version(struct mount *mp, struct lwp *l, | | 192 | quota_handle_cmd_get_version(struct mount *mp, struct lwp *l, |
193 | struct vfs_quotactl_args *args) | | 193 | struct vfs_quotactl_args *args) |
194 | { | | 194 | { |
195 | struct ufsmount *ump = VFSTOUFS(mp); | | 195 | struct ufsmount *ump = VFSTOUFS(mp); |
196 | int *version_ret; | | 196 | int *version_ret; |
197 | | | 197 | |
198 | KASSERT(args->qc_type == QCT_GETVERSION); | | 198 | KASSERT(args->qc_type == QCT_GETVERSION); |
199 | version_ret = args->u.getversion.qc_version_ret; | | 199 | version_ret = args->u.getversion.qc_version_ret; |
200 | | | 200 | |
201 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) | | 201 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) |
202 | return EOPNOTSUPP; | | 202 | return EOPNOTSUPP; |
203 | | | 203 | |
204 | #ifdef QUOTA | | 204 | #ifdef QUOTA |
205 | if (ump->um_flags & UFS_QUOTA) { | | 205 | if (ump->um_flags & UFS_QUOTA) { |
206 | *version_ret = 1; | | 206 | *version_ret = 1; |
207 | } else | | 207 | } else |
208 | #endif | | 208 | #endif |
209 | #ifdef QUOTA2 | | 209 | #ifdef QUOTA2 |
210 | if (ump->um_flags & UFS_QUOTA2) { | | 210 | if (ump->um_flags & UFS_QUOTA2) { |
211 | *version_ret = 2; | | 211 | *version_ret = 2; |
212 | } else | | 212 | } else |
213 | #endif | | 213 | #endif |
214 | return EOPNOTSUPP; | | 214 | return EOPNOTSUPP; |
215 | | | 215 | |
216 | return 0; | | 216 | return 0; |
217 | } | | 217 | } |
218 | | | 218 | |
219 | /* XXX shouldn't all this be in kauth ? */ | | 219 | /* XXX shouldn't all this be in kauth ? */ |
220 | static int | | 220 | static int |
221 | quota_get_auth(struct mount *mp, struct lwp *l, uid_t id) { | | 221 | quota_get_auth(struct mount *mp, struct lwp *l, uid_t id) { |
222 | /* The user can always query about his own quota. */ | | 222 | /* The user can always query about his own quota. */ |
223 | if (id == kauth_cred_getuid(l->l_cred)) | | 223 | if (id == kauth_cred_getuid(l->l_cred)) |
224 | return 0; | | 224 | return 0; |
225 | return kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, | | 225 | return kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, |
226 | KAUTH_REQ_SYSTEM_FS_QUOTA_GET, mp, KAUTH_ARG(id), NULL); | | 226 | KAUTH_REQ_SYSTEM_FS_QUOTA_GET, mp, KAUTH_ARG(id), NULL); |
227 | } | | 227 | } |
228 | | | 228 | |
229 | static int | | 229 | static int |
230 | quota_handle_cmd_get(struct mount *mp, struct lwp *l, | | 230 | quota_handle_cmd_get(struct mount *mp, struct lwp *l, |
231 | struct vfs_quotactl_args *args) | | 231 | struct vfs_quotactl_args *args) |
232 | { | | 232 | { |
233 | struct ufsmount *ump = VFSTOUFS(mp); | | 233 | struct ufsmount *ump = VFSTOUFS(mp); |
234 | int error; | | 234 | int error; |
235 | const struct quotakey *qk; | | 235 | const struct quotakey *qk; |
236 | struct quotaval *ret; | | 236 | struct quotaval *ret; |
237 | | | 237 | |
238 | KASSERT(args->qc_type == QCT_GET); | | 238 | KASSERT(args->qc_type == QCT_GET); |
239 | qk = args->u.get.qc_key; | | 239 | qk = args->u.get.qc_key; |
240 | ret = args->u.get.qc_ret; | | 240 | ret = args->u.get.qc_ret; |
241 | | | 241 | |
242 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) | | 242 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) |
243 | return EOPNOTSUPP; | | 243 | return EOPNOTSUPP; |
244 | | | 244 | |
245 | error = quota_get_auth(mp, l, qk->qk_id); | | 245 | error = quota_get_auth(mp, l, qk->qk_id); |
246 | if (error != 0) | | 246 | if (error != 0) |
247 | return error; | | 247 | return error; |
248 | #ifdef QUOTA | | 248 | #ifdef QUOTA |
249 | if (ump->um_flags & UFS_QUOTA) { | | 249 | if (ump->um_flags & UFS_QUOTA) { |
250 | error = quota1_handle_cmd_get(ump, qk, ret); | | 250 | error = quota1_handle_cmd_get(ump, qk, ret); |
251 | } else | | 251 | } else |
252 | #endif | | 252 | #endif |
253 | #ifdef QUOTA2 | | 253 | #ifdef QUOTA2 |
254 | if (ump->um_flags & UFS_QUOTA2) { | | 254 | if (ump->um_flags & UFS_QUOTA2) { |
255 | error = quota2_handle_cmd_get(ump, qk, ret); | | 255 | error = quota2_handle_cmd_get(ump, qk, ret); |
256 | } else | | 256 | } else |
257 | #endif | | 257 | #endif |
258 | panic("quota_handle_cmd_get: no support ?"); | | 258 | panic("quota_handle_cmd_get: no support ?"); |
259 | | | 259 | |
260 | if (error != 0) | | 260 | if (error != 0) |
261 | return error; | | 261 | return error; |
262 | | | 262 | |
263 | return error; | | 263 | return error; |
264 | } | | 264 | } |
265 | | | 265 | |
266 | static int | | 266 | static int |
267 | quota_handle_cmd_put(struct mount *mp, struct lwp *l, | | 267 | quota_handle_cmd_put(struct mount *mp, struct lwp *l, |
268 | struct vfs_quotactl_args *args) | | 268 | struct vfs_quotactl_args *args) |
269 | { | | 269 | { |
270 | struct ufsmount *ump = VFSTOUFS(mp); | | 270 | struct ufsmount *ump = VFSTOUFS(mp); |
271 | const struct quotakey *qk; | | 271 | const struct quotakey *qk; |
272 | const struct quotaval *qv; | | 272 | const struct quotaval *qv; |
273 | id_t kauth_id; | | 273 | id_t kauth_id; |
274 | int error; | | 274 | int error; |
275 | | | 275 | |
276 | KASSERT(args->qc_type == QCT_PUT); | | 276 | KASSERT(args->qc_type == QCT_PUT); |
277 | qk = args->u.put.qc_key; | | 277 | qk = args->u.put.qc_key; |
278 | qv = args->u.put.qc_val; | | 278 | qv = args->u.put.qc_val; |
279 | | | 279 | |
280 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) | | 280 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) |
281 | return EOPNOTSUPP; | | 281 | return EOPNOTSUPP; |
282 | | | 282 | |
283 | kauth_id = qk->qk_id; | | 283 | kauth_id = qk->qk_id; |
284 | if (kauth_id == QUOTA_DEFAULTID) { | | 284 | if (kauth_id == QUOTA_DEFAULTID) { |
285 | kauth_id = 0; | | 285 | kauth_id = 0; |
286 | } | | 286 | } |
287 | | | 287 | |
288 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, | | 288 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, |
289 | KAUTH_REQ_SYSTEM_FS_QUOTA_MANAGE, mp, KAUTH_ARG(kauth_id), | | 289 | KAUTH_REQ_SYSTEM_FS_QUOTA_MANAGE, mp, KAUTH_ARG(kauth_id), |
290 | NULL); | | 290 | NULL); |
291 | if (error != 0) | | 291 | if (error != 0) { |
292 | goto err; | | 292 | return error; |
| | | 293 | } |
| | | 294 | |
293 | #ifdef QUOTA | | 295 | #ifdef QUOTA |
294 | if (ump->um_flags & UFS_QUOTA) | | 296 | if (ump->um_flags & UFS_QUOTA) |
295 | error = quota1_handle_cmd_put(ump, qk, qv); | | 297 | error = quota1_handle_cmd_put(ump, qk, qv); |
296 | else | | 298 | else |
297 | #endif | | 299 | #endif |
298 | #ifdef QUOTA2 | | 300 | #ifdef QUOTA2 |
299 | if (ump->um_flags & UFS_QUOTA2) { | | 301 | if (ump->um_flags & UFS_QUOTA2) { |
300 | error = quota2_handle_cmd_put(ump, qk, qv); | | 302 | error = quota2_handle_cmd_put(ump, qk, qv); |
301 | } else | | 303 | } else |
302 | #endif | | 304 | #endif |
303 | panic("quota_handle_cmd_get: no support ?"); | | 305 | panic("quota_handle_cmd_get: no support ?"); |
304 | | | 306 | |
305 | if (error && error != ENOENT) | | 307 | if (error == ENOENT) { |
306 | goto err; | | 308 | error = 0; |
| | | 309 | } |
307 | | | 310 | |
308 | return 0; | | | |
309 | err: | | | |
310 | return error; | | 311 | return error; |
311 | } | | 312 | } |
312 | | | 313 | |
313 | static int | | 314 | static int |
314 | quota_handle_cmd_clear(struct mount *mp, struct lwp *l, | | 315 | quota_handle_cmd_clear(struct mount *mp, struct lwp *l, |
315 | struct vfs_quotactl_args *args) | | 316 | struct vfs_quotactl_args *args) |
316 | { | | 317 | { |
317 | prop_array_t replies; | | 318 | prop_array_t replies; |
318 | prop_object_iterator_t iter; | | 319 | prop_object_iterator_t iter; |
319 | prop_dictionary_t data; | | 320 | prop_dictionary_t data; |
320 | uint32_t id; | | 321 | uint32_t id; |
321 | struct ufsmount *ump = VFSTOUFS(mp); | | 322 | struct ufsmount *ump = VFSTOUFS(mp); |
322 | int error, defaultq = 0; | | 323 | int error, defaultq = 0; |
323 | const char *idstr; | | 324 | const char *idstr; |
324 | prop_dictionary_t cmddict; | | 325 | prop_dictionary_t cmddict; |
325 | int q2type; | | 326 | int q2type; |
326 | prop_array_t datas; | | 327 | prop_array_t datas; |
327 | | | 328 | |
328 | KASSERT(args->qc_type == QCT_PROPLIB); | | 329 | KASSERT(args->qc_type == QCT_PROPLIB); |
329 | cmddict = args->u.proplib.qc_cmddict; | | 330 | cmddict = args->u.proplib.qc_cmddict; |
330 | q2type = args->u.proplib.qc_q2type; | | 331 | q2type = args->u.proplib.qc_q2type; |
331 | datas = args->u.proplib.qc_datas; | | 332 | datas = args->u.proplib.qc_datas; |
332 | | | 333 | |
333 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); | | 334 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); |
334 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); | | 335 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); |
335 | | | 336 | |
336 | if ((ump->um_flags & UFS_QUOTA2) == 0) | | 337 | if ((ump->um_flags & UFS_QUOTA2) == 0) |
337 | return EOPNOTSUPP; | | 338 | return EOPNOTSUPP; |
338 | | | 339 | |
339 | replies = prop_array_create(); | | 340 | replies = prop_array_create(); |
340 | if (replies == NULL) | | 341 | if (replies == NULL) |
341 | return ENOMEM; | | 342 | return ENOMEM; |
342 | | | 343 | |
343 | iter = prop_array_iterator(datas); | | 344 | iter = prop_array_iterator(datas); |
344 | if (iter == NULL) { | | 345 | if (iter == NULL) { |
345 | prop_object_release(replies); | | 346 | prop_object_release(replies); |
346 | return ENOMEM; | | 347 | return ENOMEM; |
347 | } | | 348 | } |
348 | while ((data = prop_object_iterator_next(iter)) != NULL) { | | 349 | while ((data = prop_object_iterator_next(iter)) != NULL) { |
349 | if (!prop_dictionary_get_uint32(data, "id", &id)) { | | 350 | if (!prop_dictionary_get_uint32(data, "id", &id)) { |
350 | if (!prop_dictionary_get_cstring_nocopy(data, "id", | | 351 | if (!prop_dictionary_get_cstring_nocopy(data, "id", |
351 | &idstr)) | | 352 | &idstr)) |
352 | continue; | | 353 | continue; |
353 | if (strcmp(idstr, "default")) | | 354 | if (strcmp(idstr, "default")) |
354 | continue; | | 355 | continue; |
355 | id = 0; | | 356 | id = 0; |
356 | defaultq = 1; | | 357 | defaultq = 1; |
357 | } else { | | 358 | } else { |
358 | defaultq = 0; | | 359 | defaultq = 0; |
359 | } | | 360 | } |
360 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, | | 361 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, |
361 | KAUTH_REQ_SYSTEM_FS_QUOTA_MANAGE, mp, KAUTH_ARG(id), NULL); | | 362 | KAUTH_REQ_SYSTEM_FS_QUOTA_MANAGE, mp, KAUTH_ARG(id), NULL); |
362 | if (error != 0) | | 363 | if (error != 0) |
363 | goto err; | | 364 | goto err; |
364 | #ifdef QUOTA2 | | 365 | #ifdef QUOTA2 |
365 | if (ump->um_flags & UFS_QUOTA2) { | | 366 | if (ump->um_flags & UFS_QUOTA2) { |
366 | error = quota2_handle_cmd_clear(ump, q2type, id, defaultq, | | 367 | error = quota2_handle_cmd_clear(ump, q2type, id, defaultq, |
367 | data); | | 368 | data); |
368 | } else | | 369 | } else |
369 | #endif | | 370 | #endif |
370 | panic("quota_handle_cmd_get: no support ?"); | | 371 | panic("quota_handle_cmd_get: no support ?"); |
371 | | | 372 | |
372 | if (error && error != ENOENT) | | 373 | if (error && error != ENOENT) |
373 | goto err; | | 374 | goto err; |
374 | } | | 375 | } |
375 | prop_object_iterator_release(iter); | | 376 | prop_object_iterator_release(iter); |
376 | if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) { | | 377 | if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) { |
377 | error = ENOMEM; | | 378 | error = ENOMEM; |
378 | } else { | | 379 | } else { |
379 | error = 0; | | 380 | error = 0; |
380 | } | | 381 | } |
381 | return error; | | 382 | return error; |
382 | err: | | 383 | err: |
383 | prop_object_iterator_release(iter); | | 384 | prop_object_iterator_release(iter); |
384 | prop_object_release(replies); | | 385 | prop_object_release(replies); |
385 | return error; | | 386 | return error; |
386 | } | | 387 | } |
387 | | | 388 | |
388 | static int | | 389 | static int |
389 | quota_handle_cmd_getall(struct mount *mp, struct lwp *l, | | 390 | quota_handle_cmd_getall(struct mount *mp, struct lwp *l, |
390 | struct vfs_quotactl_args *args) | | 391 | struct vfs_quotactl_args *args) |
391 | { | | 392 | { |
392 | prop_array_t replies; | | 393 | prop_array_t replies; |
393 | struct ufsmount *ump = VFSTOUFS(mp); | | 394 | struct ufsmount *ump = VFSTOUFS(mp); |
394 | int error; | | 395 | int error; |
395 | prop_dictionary_t cmddict; | | 396 | prop_dictionary_t cmddict; |
396 | int q2type; | | 397 | int q2type; |
397 | prop_array_t datas; | | 398 | prop_array_t datas; |
398 | | | 399 | |
399 | KASSERT(args->qc_type == QCT_PROPLIB); | | 400 | KASSERT(args->qc_type == QCT_PROPLIB); |
400 | cmddict = args->u.proplib.qc_cmddict; | | 401 | cmddict = args->u.proplib.qc_cmddict; |
401 | q2type = args->u.proplib.qc_q2type; | | 402 | q2type = args->u.proplib.qc_q2type; |
402 | datas = args->u.proplib.qc_datas; | | 403 | datas = args->u.proplib.qc_datas; |
403 | | | 404 | |
404 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); | | 405 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); |
405 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); | | 406 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); |
406 | | | 407 | |
407 | if ((ump->um_flags & UFS_QUOTA2) == 0) | | 408 | if ((ump->um_flags & UFS_QUOTA2) == 0) |
408 | return EOPNOTSUPP; | | 409 | return EOPNOTSUPP; |
409 | | | 410 | |
410 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, | | 411 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, |
411 | KAUTH_REQ_SYSTEM_FS_QUOTA_GET, mp, NULL, NULL); | | 412 | KAUTH_REQ_SYSTEM_FS_QUOTA_GET, mp, NULL, NULL); |
412 | if (error) | | 413 | if (error) |
413 | return error; | | 414 | return error; |
414 | | | 415 | |
415 | replies = prop_array_create(); | | 416 | replies = prop_array_create(); |
416 | if (replies == NULL) | | 417 | if (replies == NULL) |
417 | return ENOMEM; | | 418 | return ENOMEM; |
418 | | | 419 | |
419 | #ifdef QUOTA2 | | 420 | #ifdef QUOTA2 |
420 | if (ump->um_flags & UFS_QUOTA2) { | | 421 | if (ump->um_flags & UFS_QUOTA2) { |
421 | error = quota2_handle_cmd_getall(ump, q2type, replies); | | 422 | error = quota2_handle_cmd_getall(ump, q2type, replies); |
422 | } else | | 423 | } else |
423 | #endif | | 424 | #endif |
424 | panic("quota_handle_cmd_getall: no support ?"); | | 425 | panic("quota_handle_cmd_getall: no support ?"); |
425 | if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) { | | 426 | if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) { |
426 | error = ENOMEM; | | 427 | error = ENOMEM; |
427 | } else { | | 428 | } else { |
428 | error = 0; | | 429 | error = 0; |
429 | } | | 430 | } |
430 | return error; | | 431 | return error; |
431 | } | | 432 | } |
432 | | | 433 | |
433 | static int | | 434 | static int |
434 | quota_handle_cmd_quotaon(struct mount *mp, struct lwp *l, | | 435 | quota_handle_cmd_quotaon(struct mount *mp, struct lwp *l, |
435 | struct vfs_quotactl_args *args) | | 436 | struct vfs_quotactl_args *args) |
436 | { | | 437 | { |
437 | prop_dictionary_t data; | | 438 | prop_dictionary_t data; |
438 | struct ufsmount *ump = VFSTOUFS(mp); | | 439 | struct ufsmount *ump = VFSTOUFS(mp); |
439 | int error; | | 440 | int error; |
440 | const char *qfile; | | 441 | const char *qfile; |
441 | prop_dictionary_t cmddict; | | 442 | prop_dictionary_t cmddict; |
442 | int q2type; | | 443 | int q2type; |
443 | prop_array_t datas; | | 444 | prop_array_t datas; |
444 | | | 445 | |
445 | KASSERT(args->qc_type == QCT_PROPLIB); | | 446 | KASSERT(args->qc_type == QCT_PROPLIB); |
446 | cmddict = args->u.proplib.qc_cmddict; | | 447 | cmddict = args->u.proplib.qc_cmddict; |
447 | q2type = args->u.proplib.qc_q2type; | | 448 | q2type = args->u.proplib.qc_q2type; |
448 | datas = args->u.proplib.qc_datas; | | 449 | datas = args->u.proplib.qc_datas; |
449 | | | 450 | |
450 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); | | 451 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); |
451 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); | | 452 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); |
452 | | | 453 | |
453 | if ((ump->um_flags & UFS_QUOTA2) != 0) | | 454 | if ((ump->um_flags & UFS_QUOTA2) != 0) |
454 | return EBUSY; | | 455 | return EBUSY; |
455 | | | 456 | |
456 | if (prop_array_count(datas) != 1) | | 457 | if (prop_array_count(datas) != 1) |
457 | return EINVAL; | | 458 | return EINVAL; |
458 | | | 459 | |
459 | data = prop_array_get(datas, 0); | | 460 | data = prop_array_get(datas, 0); |
460 | if (data == NULL) | | 461 | if (data == NULL) |
461 | return ENOMEM; | | 462 | return ENOMEM; |
462 | if (!prop_dictionary_get_cstring_nocopy(data, "quotafile", | | 463 | if (!prop_dictionary_get_cstring_nocopy(data, "quotafile", |
463 | &qfile)) | | 464 | &qfile)) |
464 | return EINVAL; | | 465 | return EINVAL; |
465 | | | 466 | |
466 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, | | 467 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, |
467 | KAUTH_REQ_SYSTEM_FS_QUOTA_ONOFF, mp, NULL, NULL); | | 468 | KAUTH_REQ_SYSTEM_FS_QUOTA_ONOFF, mp, NULL, NULL); |
468 | if (error != 0) { | | 469 | if (error != 0) { |
469 | return error; | | 470 | return error; |
470 | } | | 471 | } |
471 | #ifdef QUOTA | | 472 | #ifdef QUOTA |
472 | error = quota1_handle_cmd_quotaon(l, ump, q2type, qfile); | | 473 | error = quota1_handle_cmd_quotaon(l, ump, q2type, qfile); |
473 | #else | | 474 | #else |
474 | error = EOPNOTSUPP; | | 475 | error = EOPNOTSUPP; |
475 | #endif | | 476 | #endif |
476 | | | 477 | |
477 | return error; | | 478 | return error; |
478 | } | | 479 | } |
479 | | | 480 | |
480 | static int | | 481 | static int |
481 | quota_handle_cmd_quotaoff(struct mount *mp, struct lwp *l, | | 482 | quota_handle_cmd_quotaoff(struct mount *mp, struct lwp *l, |
482 | struct vfs_quotactl_args *args) | | 483 | struct vfs_quotactl_args *args) |
483 | { | | 484 | { |
484 | struct ufsmount *ump = VFSTOUFS(mp); | | 485 | struct ufsmount *ump = VFSTOUFS(mp); |
485 | int error; | | 486 | int error; |
486 | prop_dictionary_t cmddict; | | 487 | prop_dictionary_t cmddict; |
487 | int q2type; | | 488 | int q2type; |
488 | prop_array_t datas; | | 489 | prop_array_t datas; |
489 | | | 490 | |
490 | KASSERT(args->qc_type == QCT_PROPLIB); | | 491 | KASSERT(args->qc_type == QCT_PROPLIB); |
491 | cmddict = args->u.proplib.qc_cmddict; | | 492 | cmddict = args->u.proplib.qc_cmddict; |
492 | q2type = args->u.proplib.qc_q2type; | | 493 | q2type = args->u.proplib.qc_q2type; |
493 | datas = args->u.proplib.qc_datas; | | 494 | datas = args->u.proplib.qc_datas; |
494 | | | 495 | |
495 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); | | 496 | KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY); |
496 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); | | 497 | KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY); |
497 | | | 498 | |
498 | if ((ump->um_flags & UFS_QUOTA2) != 0) | | 499 | if ((ump->um_flags & UFS_QUOTA2) != 0) |
499 | return EOPNOTSUPP; | | 500 | return EOPNOTSUPP; |
500 | | | 501 | |
501 | if (prop_array_count(datas) != 0) | | 502 | if (prop_array_count(datas) != 0) |
502 | return EINVAL; | | 503 | return EINVAL; |
503 | | | 504 | |
504 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, | | 505 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_QUOTA, |
505 | KAUTH_REQ_SYSTEM_FS_QUOTA_ONOFF, mp, NULL, NULL); | | 506 | KAUTH_REQ_SYSTEM_FS_QUOTA_ONOFF, mp, NULL, NULL); |
506 | if (error != 0) { | | 507 | if (error != 0) { |
507 | return error; | | 508 | return error; |
508 | } | | 509 | } |
509 | #ifdef QUOTA | | 510 | #ifdef QUOTA |
510 | error = quota1_handle_cmd_quotaoff(l, ump, q2type); | | 511 | error = quota1_handle_cmd_quotaoff(l, ump, q2type); |
511 | #else | | 512 | #else |
512 | error = EOPNOTSUPP; | | 513 | error = EOPNOTSUPP; |
513 | #endif | | 514 | #endif |
514 | | | 515 | |
515 | return error; | | 516 | return error; |
516 | } | | 517 | } |
517 | | | 518 | |
518 | /* | | 519 | /* |
519 | * Initialize the quota system. | | 520 | * Initialize the quota system. |
520 | */ | | 521 | */ |
521 | void | | 522 | void |
522 | dqinit(void) | | 523 | dqinit(void) |
523 | { | | 524 | { |
524 | | | 525 | |
525 | mutex_init(&dqlock, MUTEX_DEFAULT, IPL_NONE); | | 526 | mutex_init(&dqlock, MUTEX_DEFAULT, IPL_NONE); |
526 | cv_init(&dqcv, "quota"); | | 527 | cv_init(&dqcv, "quota"); |
527 | dqhashtbl = hashinit(desiredvnodes, HASH_LIST, true, &dqhash); | | 528 | dqhashtbl = hashinit(desiredvnodes, HASH_LIST, true, &dqhash); |
528 | dquot_cache = pool_cache_init(sizeof(struct dquot), 0, 0, 0, "ufsdq", | | 529 | dquot_cache = pool_cache_init(sizeof(struct dquot), 0, 0, 0, "ufsdq", |
529 | NULL, IPL_NONE, NULL, NULL, NULL); | | 530 | NULL, IPL_NONE, NULL, NULL, NULL); |
530 | } | | 531 | } |
531 | | | 532 | |
532 | void | | 533 | void |
533 | dqreinit(void) | | 534 | dqreinit(void) |
534 | { | | 535 | { |
535 | struct dquot *dq; | | 536 | struct dquot *dq; |
536 | struct dqhashhead *oldhash, *hash; | | 537 | struct dqhashhead *oldhash, *hash; |
537 | struct vnode *dqvp; | | 538 | struct vnode *dqvp; |
538 | u_long oldmask, mask, hashval; | | 539 | u_long oldmask, mask, hashval; |
539 | int i; | | 540 | int i; |
540 | | | 541 | |
541 | hash = hashinit(desiredvnodes, HASH_LIST, true, &mask); | | 542 | hash = hashinit(desiredvnodes, HASH_LIST, true, &mask); |
542 | mutex_enter(&dqlock); | | 543 | mutex_enter(&dqlock); |
543 | oldhash = dqhashtbl; | | 544 | oldhash = dqhashtbl; |
544 | oldmask = dqhash; | | 545 | oldmask = dqhash; |
545 | dqhashtbl = hash; | | 546 | dqhashtbl = hash; |
546 | dqhash = mask; | | 547 | dqhash = mask; |
547 | for (i = 0; i <= oldmask; i++) { | | 548 | for (i = 0; i <= oldmask; i++) { |
548 | while ((dq = LIST_FIRST(&oldhash[i])) != NULL) { | | 549 | while ((dq = LIST_FIRST(&oldhash[i])) != NULL) { |
549 | dqvp = dq->dq_ump->um_quotas[dq->dq_type]; | | 550 | dqvp = dq->dq_ump->um_quotas[dq->dq_type]; |
550 | LIST_REMOVE(dq, dq_hash); | | 551 | LIST_REMOVE(dq, dq_hash); |
551 | hashval = DQHASH(dqvp, dq->dq_id); | | 552 | hashval = DQHASH(dqvp, dq->dq_id); |
552 | LIST_INSERT_HEAD(&dqhashtbl[hashval], dq, dq_hash); | | 553 | LIST_INSERT_HEAD(&dqhashtbl[hashval], dq, dq_hash); |
553 | } | | 554 | } |
554 | } | | 555 | } |
555 | mutex_exit(&dqlock); | | 556 | mutex_exit(&dqlock); |
556 | hashdone(oldhash, HASH_LIST, oldmask); | | 557 | hashdone(oldhash, HASH_LIST, oldmask); |
557 | } | | 558 | } |
558 | | | 559 | |
559 | /* | | 560 | /* |
560 | * Free resources held by quota system. | | 561 | * Free resources held by quota system. |
561 | */ | | 562 | */ |
562 | void | | 563 | void |
563 | dqdone(void) | | 564 | dqdone(void) |
564 | { | | 565 | { |
565 | | | 566 | |
566 | pool_cache_destroy(dquot_cache); | | 567 | pool_cache_destroy(dquot_cache); |
567 | hashdone(dqhashtbl, HASH_LIST, dqhash); | | 568 | hashdone(dqhashtbl, HASH_LIST, dqhash); |
568 | cv_destroy(&dqcv); | | 569 | cv_destroy(&dqcv); |
569 | mutex_destroy(&dqlock); | | 570 | mutex_destroy(&dqlock); |
570 | } | | 571 | } |
571 | | | 572 | |
572 | /* | | 573 | /* |
573 | * Set up the quotas for an inode. | | 574 | * Set up the quotas for an inode. |
574 | * | | 575 | * |
575 | * This routine completely defines the semantics of quotas. | | 576 | * This routine completely defines the semantics of quotas. |
576 | * If other criterion want to be used to establish quotas, the | | 577 | * If other criterion want to be used to establish quotas, the |
577 | * MAXQUOTAS value in quotas.h should be increased, and the | | 578 | * MAXQUOTAS value in quotas.h should be increased, and the |
578 | * additional dquots set up here. | | 579 | * additional dquots set up here. |
579 | */ | | 580 | */ |
580 | int | | 581 | int |
581 | getinoquota(struct inode *ip) | | 582 | getinoquota(struct inode *ip) |
582 | { | | 583 | { |
583 | struct ufsmount *ump = ip->i_ump; | | 584 | struct ufsmount *ump = ip->i_ump; |
584 | struct vnode *vp = ITOV(ip); | | 585 | struct vnode *vp = ITOV(ip); |
585 | int i, error; | | 586 | int i, error; |
586 | u_int32_t ino_ids[MAXQUOTAS]; | | 587 | u_int32_t ino_ids[MAXQUOTAS]; |
587 | | | 588 | |
588 | /* | | 589 | /* |
589 | * To avoid deadlocks never update quotas for quota files | | 590 | * To avoid deadlocks never update quotas for quota files |
590 | * on the same file system | | 591 | * on the same file system |
591 | */ | | 592 | */ |
592 | for (i = 0; i < MAXQUOTAS; i++) | | 593 | for (i = 0; i < MAXQUOTAS; i++) |
593 | if (vp == ump->um_quotas[i]) | | 594 | if (vp == ump->um_quotas[i]) |
594 | return 0; | | 595 | return 0; |
595 | | | 596 | |
596 | ino_ids[USRQUOTA] = ip->i_uid; | | 597 | ino_ids[USRQUOTA] = ip->i_uid; |
597 | ino_ids[GRPQUOTA] = ip->i_gid; | | 598 | ino_ids[GRPQUOTA] = ip->i_gid; |
598 | for (i = 0; i < MAXQUOTAS; i++) { | | 599 | for (i = 0; i < MAXQUOTAS; i++) { |
599 | /* | | 600 | /* |
600 | * If the file id changed the quota needs update. | | 601 | * If the file id changed the quota needs update. |
601 | */ | | 602 | */ |
602 | if (ip->i_dquot[i] != NODQUOT && | | 603 | if (ip->i_dquot[i] != NODQUOT && |
603 | ip->i_dquot[i]->dq_id != ino_ids[i]) { | | 604 | ip->i_dquot[i]->dq_id != ino_ids[i]) { |
604 | dqrele(ITOV(ip), ip->i_dquot[i]); | | 605 | dqrele(ITOV(ip), ip->i_dquot[i]); |
605 | ip->i_dquot[i] = NODQUOT; | | 606 | ip->i_dquot[i] = NODQUOT; |
606 | } | | 607 | } |
607 | /* | | 608 | /* |
608 | * Set up the quota based on file id. | | 609 | * Set up the quota based on file id. |
609 | * ENODEV means that quotas are not enabled. | | 610 | * ENODEV means that quotas are not enabled. |
610 | */ | | 611 | */ |
611 | if (ip->i_dquot[i] == NODQUOT && | | 612 | if (ip->i_dquot[i] == NODQUOT && |
612 | (error = dqget(vp, ino_ids[i], ump, i, &ip->i_dquot[i])) && | | 613 | (error = dqget(vp, ino_ids[i], ump, i, &ip->i_dquot[i])) && |
613 | error != ENODEV) | | 614 | error != ENODEV) |
614 | return (error); | | 615 | return (error); |
615 | } | | 616 | } |
616 | return 0; | | 617 | return 0; |
617 | } | | 618 | } |
618 | | | 619 | |
619 | /* | | 620 | /* |
620 | * Obtain a dquot structure for the specified identifier and quota file | | 621 | * Obtain a dquot structure for the specified identifier and quota file |
621 | * reading the information from the file if necessary. | | 622 | * reading the information from the file if necessary. |
622 | */ | | 623 | */ |
623 | int | | 624 | int |
624 | dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type, | | 625 | dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type, |
625 | struct dquot **dqp) | | 626 | struct dquot **dqp) |
626 | { | | 627 | { |
627 | struct dquot *dq, *ndq; | | 628 | struct dquot *dq, *ndq; |
628 | struct dqhashhead *dqh; | | 629 | struct dqhashhead *dqh; |
629 | struct vnode *dqvp; | | 630 | struct vnode *dqvp; |
630 | int error = 0; /* XXX gcc */ | | 631 | int error = 0; /* XXX gcc */ |
631 | | | 632 | |
632 | /* Lock to see an up to date value for QTF_CLOSING. */ | | 633 | /* Lock to see an up to date value for QTF_CLOSING. */ |
633 | mutex_enter(&dqlock); | | 634 | mutex_enter(&dqlock); |
634 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) { | | 635 | if ((ump->um_flags & (UFS_QUOTA|UFS_QUOTA2)) == 0) { |
635 | mutex_exit(&dqlock); | | 636 | mutex_exit(&dqlock); |
636 | *dqp = NODQUOT; | | 637 | *dqp = NODQUOT; |
637 | return (ENODEV); | | 638 | return (ENODEV); |
638 | } | | 639 | } |
639 | dqvp = ump->um_quotas[type]; | | 640 | dqvp = ump->um_quotas[type]; |
640 | #ifdef QUOTA | | 641 | #ifdef QUOTA |
641 | if (ump->um_flags & UFS_QUOTA) { | | 642 | if (ump->um_flags & UFS_QUOTA) { |
642 | if (dqvp == NULLVP || (ump->umq1_qflags[type] & QTF_CLOSING)) { | | 643 | if (dqvp == NULLVP || (ump->umq1_qflags[type] & QTF_CLOSING)) { |
643 | mutex_exit(&dqlock); | | 644 | mutex_exit(&dqlock); |
644 | *dqp = NODQUOT; | | 645 | *dqp = NODQUOT; |
645 | return (ENODEV); | | 646 | return (ENODEV); |
646 | } | | 647 | } |
647 | } | | 648 | } |
648 | #endif | | 649 | #endif |
649 | #ifdef QUOTA2 | | 650 | #ifdef QUOTA2 |
650 | if (ump->um_flags & UFS_QUOTA2) { | | 651 | if (ump->um_flags & UFS_QUOTA2) { |
651 | if (dqvp == NULLVP) { | | 652 | if (dqvp == NULLVP) { |
652 | mutex_exit(&dqlock); | | 653 | mutex_exit(&dqlock); |
653 | *dqp = NODQUOT; | | 654 | *dqp = NODQUOT; |
654 | return (ENODEV); | | 655 | return (ENODEV); |
655 | } | | 656 | } |
656 | } | | 657 | } |
657 | #endif | | 658 | #endif |
658 | KASSERT(dqvp != vp); | | 659 | KASSERT(dqvp != vp); |
659 | /* | | 660 | /* |
660 | * Check the cache first. | | 661 | * Check the cache first. |
661 | */ | | 662 | */ |
662 | dqh = &dqhashtbl[DQHASH(dqvp, id)]; | | 663 | dqh = &dqhashtbl[DQHASH(dqvp, id)]; |
663 | LIST_FOREACH(dq, dqh, dq_hash) { | | 664 | LIST_FOREACH(dq, dqh, dq_hash) { |
664 | if (dq->dq_id != id || | | 665 | if (dq->dq_id != id || |
665 | dq->dq_ump->um_quotas[dq->dq_type] != dqvp) | | 666 | dq->dq_ump->um_quotas[dq->dq_type] != dqvp) |
666 | continue; | | 667 | continue; |
667 | KASSERT(dq->dq_cnt > 0); | | 668 | KASSERT(dq->dq_cnt > 0); |
668 | dqref(dq); | | 669 | dqref(dq); |
669 | mutex_exit(&dqlock); | | 670 | mutex_exit(&dqlock); |
670 | *dqp = dq; | | 671 | *dqp = dq; |
671 | return (0); | | 672 | return (0); |
672 | } | | 673 | } |
673 | /* | | 674 | /* |
674 | * Not in cache, allocate a new one. | | 675 | * Not in cache, allocate a new one. |
675 | */ | | 676 | */ |
676 | mutex_exit(&dqlock); | | 677 | mutex_exit(&dqlock); |
677 | ndq = pool_cache_get(dquot_cache, PR_WAITOK); | | 678 | ndq = pool_cache_get(dquot_cache, PR_WAITOK); |
678 | /* | | 679 | /* |
679 | * Initialize the contents of the dquot structure. | | 680 | * Initialize the contents of the dquot structure. |
680 | */ | | 681 | */ |
681 | memset((char *)ndq, 0, sizeof *ndq); | | 682 | memset((char *)ndq, 0, sizeof *ndq); |
682 | ndq->dq_flags = 0; | | 683 | ndq->dq_flags = 0; |
683 | ndq->dq_id = id; | | 684 | ndq->dq_id = id; |
684 | ndq->dq_ump = ump; | | 685 | ndq->dq_ump = ump; |
685 | ndq->dq_type = type; | | 686 | ndq->dq_type = type; |
686 | mutex_init(&ndq->dq_interlock, MUTEX_DEFAULT, IPL_NONE); | | 687 | mutex_init(&ndq->dq_interlock, MUTEX_DEFAULT, IPL_NONE); |
687 | mutex_enter(&dqlock); | | 688 | mutex_enter(&dqlock); |
688 | dqh = &dqhashtbl[DQHASH(dqvp, id)]; | | 689 | dqh = &dqhashtbl[DQHASH(dqvp, id)]; |
689 | LIST_FOREACH(dq, dqh, dq_hash) { | | 690 | LIST_FOREACH(dq, dqh, dq_hash) { |
690 | if (dq->dq_id != id || | | 691 | if (dq->dq_id != id || |
691 | dq->dq_ump->um_quotas[dq->dq_type] != dqvp) | | 692 | dq->dq_ump->um_quotas[dq->dq_type] != dqvp) |
692 | continue; | | 693 | continue; |
693 | /* | | 694 | /* |
694 | * Another thread beat us allocating this dquot. | | 695 | * Another thread beat us allocating this dquot. |
695 | */ | | 696 | */ |
696 | KASSERT(dq->dq_cnt > 0); | | 697 | KASSERT(dq->dq_cnt > 0); |
697 | dqref(dq); | | 698 | dqref(dq); |
698 | mutex_exit(&dqlock); | | 699 | mutex_exit(&dqlock); |
699 | mutex_destroy(&ndq->dq_interlock); | | 700 | mutex_destroy(&ndq->dq_interlock); |
700 | pool_cache_put(dquot_cache, ndq); | | 701 | pool_cache_put(dquot_cache, ndq); |
701 | *dqp = dq; | | 702 | *dqp = dq; |
702 | return 0; | | 703 | return 0; |
703 | } | | 704 | } |
704 | dq = ndq; | | 705 | dq = ndq; |
705 | LIST_INSERT_HEAD(dqh, dq, dq_hash); | | 706 | LIST_INSERT_HEAD(dqh, dq, dq_hash); |
706 | dqref(dq); | | 707 | dqref(dq); |
707 | mutex_enter(&dq->dq_interlock); | | 708 | mutex_enter(&dq->dq_interlock); |
708 | mutex_exit(&dqlock); | | 709 | mutex_exit(&dqlock); |
709 | #ifdef QUOTA | | 710 | #ifdef QUOTA |
710 | if (ump->um_flags & UFS_QUOTA) | | 711 | if (ump->um_flags & UFS_QUOTA) |
711 | error = dq1get(dqvp, id, ump, type, dq); | | 712 | error = dq1get(dqvp, id, ump, type, dq); |
712 | #endif | | 713 | #endif |
713 | #ifdef QUOTA2 | | 714 | #ifdef QUOTA2 |
714 | if (ump->um_flags & UFS_QUOTA2) | | 715 | if (ump->um_flags & UFS_QUOTA2) |
715 | error = dq2get(dqvp, id, ump, type, dq); | | 716 | error = dq2get(dqvp, id, ump, type, dq); |
716 | #endif | | 717 | #endif |
717 | /* | | 718 | /* |
718 | * I/O error in reading quota file, release | | 719 | * I/O error in reading quota file, release |
719 | * quota structure and reflect problem to caller. | | 720 | * quota structure and reflect problem to caller. |
720 | */ | | 721 | */ |
721 | if (error) { | | 722 | if (error) { |
722 | mutex_enter(&dqlock); | | 723 | mutex_enter(&dqlock); |
723 | LIST_REMOVE(dq, dq_hash); | | 724 | LIST_REMOVE(dq, dq_hash); |
724 | mutex_exit(&dqlock); | | 725 | mutex_exit(&dqlock); |
725 | mutex_exit(&dq->dq_interlock); | | 726 | mutex_exit(&dq->dq_interlock); |
726 | dqrele(vp, dq); | | 727 | dqrele(vp, dq); |
727 | *dqp = NODQUOT; | | 728 | *dqp = NODQUOT; |
728 | return (error); | | 729 | return (error); |
729 | } | | 730 | } |
730 | mutex_exit(&dq->dq_interlock); | | 731 | mutex_exit(&dq->dq_interlock); |
731 | *dqp = dq; | | 732 | *dqp = dq; |
732 | return (0); | | 733 | return (0); |
733 | } | | 734 | } |
734 | | | 735 | |
735 | /* | | 736 | /* |
736 | * Obtain a reference to a dquot. | | 737 | * Obtain a reference to a dquot. |
737 | */ | | 738 | */ |
738 | void | | 739 | void |
739 | dqref(struct dquot *dq) | | 740 | dqref(struct dquot *dq) |
740 | { | | 741 | { |
741 | | | 742 | |
742 | KASSERT(mutex_owned(&dqlock)); | | 743 | KASSERT(mutex_owned(&dqlock)); |
743 | dq->dq_cnt++; | | 744 | dq->dq_cnt++; |
744 | KASSERT(dq->dq_cnt > 0); | | 745 | KASSERT(dq->dq_cnt > 0); |
745 | } | | 746 | } |
746 | | | 747 | |
747 | /* | | 748 | /* |
748 | * Release a reference to a dquot. | | 749 | * Release a reference to a dquot. |
749 | */ | | 750 | */ |
750 | void | | 751 | void |
751 | dqrele(struct vnode *vp, struct dquot *dq) | | 752 | dqrele(struct vnode *vp, struct dquot *dq) |
752 | { | | 753 | { |
753 | | | 754 | |
754 | if (dq == NODQUOT) | | 755 | if (dq == NODQUOT) |
755 | return; | | 756 | return; |
756 | mutex_enter(&dq->dq_interlock); | | 757 | mutex_enter(&dq->dq_interlock); |
757 | for (;;) { | | 758 | for (;;) { |
758 | mutex_enter(&dqlock); | | 759 | mutex_enter(&dqlock); |
759 | if (dq->dq_cnt > 1) { | | 760 | if (dq->dq_cnt > 1) { |
760 | dq->dq_cnt--; | | 761 | dq->dq_cnt--; |
761 | mutex_exit(&dqlock); | | 762 | mutex_exit(&dqlock); |
762 | mutex_exit(&dq->dq_interlock); | | 763 | mutex_exit(&dq->dq_interlock); |
763 | return; | | 764 | return; |
764 | } | | 765 | } |
765 | if ((dq->dq_flags & DQ_MOD) == 0) | | 766 | if ((dq->dq_flags & DQ_MOD) == 0) |
766 | break; | | 767 | break; |
767 | mutex_exit(&dqlock); | | 768 | mutex_exit(&dqlock); |
768 | #ifdef QUOTA | | 769 | #ifdef QUOTA |
769 | if (dq->dq_ump->um_flags & UFS_QUOTA) | | 770 | if (dq->dq_ump->um_flags & UFS_QUOTA) |
770 | (void) dq1sync(vp, dq); | | 771 | (void) dq1sync(vp, dq); |
771 | #endif | | 772 | #endif |
772 | #ifdef QUOTA2 | | 773 | #ifdef QUOTA2 |
773 | if (dq->dq_ump->um_flags & UFS_QUOTA2) | | 774 | if (dq->dq_ump->um_flags & UFS_QUOTA2) |
774 | (void) dq2sync(vp, dq); | | 775 | (void) dq2sync(vp, dq); |
775 | #endif | | 776 | #endif |
776 | } | | 777 | } |
777 | KASSERT(dq->dq_cnt == 1 && (dq->dq_flags & DQ_MOD) == 0); | | 778 | KASSERT(dq->dq_cnt == 1 && (dq->dq_flags & DQ_MOD) == 0); |
778 | LIST_REMOVE(dq, dq_hash); | | 779 | LIST_REMOVE(dq, dq_hash); |
779 | mutex_exit(&dqlock); | | 780 | mutex_exit(&dqlock); |
780 | mutex_exit(&dq->dq_interlock); | | 781 | mutex_exit(&dq->dq_interlock); |
781 | mutex_destroy(&dq->dq_interlock); | | 782 | mutex_destroy(&dq->dq_interlock); |
782 | pool_cache_put(dquot_cache, dq); | | 783 | pool_cache_put(dquot_cache, dq); |
783 | } | | 784 | } |
784 | | | 785 | |
785 | int | | 786 | int |
786 | qsync(struct mount *mp) | | 787 | qsync(struct mount *mp) |
787 | { | | 788 | { |
788 | struct ufsmount *ump = VFSTOUFS(mp); | | 789 | struct ufsmount *ump = VFSTOUFS(mp); |
789 | #ifdef QUOTA | | 790 | #ifdef QUOTA |
790 | if (ump->um_flags & UFS_QUOTA) | | 791 | if (ump->um_flags & UFS_QUOTA) |
791 | return q1sync(mp); | | 792 | return q1sync(mp); |
792 | #endif | | 793 | #endif |
793 | #ifdef QUOTA2 | | 794 | #ifdef QUOTA2 |
794 | if (ump->um_flags & UFS_QUOTA2) | | 795 | if (ump->um_flags & UFS_QUOTA2) |
795 | return q2sync(mp); | | 796 | return q2sync(mp); |
796 | #endif | | 797 | #endif |
797 | return 0; | | 798 | return 0; |
798 | } | | 799 | } |
799 | | | 800 | |
800 | #ifdef DIAGNOSTIC | | 801 | #ifdef DIAGNOSTIC |
801 | /* | | 802 | /* |
802 | * Check the hash chains for stray dquot's. | | 803 | * Check the hash chains for stray dquot's. |
803 | */ | | 804 | */ |
804 | void | | 805 | void |
805 | dqflush(struct vnode *vp) | | 806 | dqflush(struct vnode *vp) |
806 | { | | 807 | { |
807 | struct dquot *dq; | | 808 | struct dquot *dq; |
808 | int i; | | 809 | int i; |
809 | | | 810 | |
810 | mutex_enter(&dqlock); | | 811 | mutex_enter(&dqlock); |
811 | for (i = 0; i <= dqhash; i++) | | 812 | for (i = 0; i <= dqhash; i++) |
812 | LIST_FOREACH(dq, &dqhashtbl[i], dq_hash) | | 813 | LIST_FOREACH(dq, &dqhashtbl[i], dq_hash) |
813 | KASSERT(dq->dq_ump->um_quotas[dq->dq_type] != vp); | | 814 | KASSERT(dq->dq_ump->um_quotas[dq->dq_type] != vp); |
814 | mutex_exit(&dqlock); | | 815 | mutex_exit(&dqlock); |
815 | } | | 816 | } |
816 | #endif | | 817 | #endif |