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