vfs_quotactl.c revision 1.35 1 /* $NetBSD: vfs_quotactl.c,v 1.35 2012/01/29 07:14:38 dholland Exp $ */
2
3 /*
4 * Copyright (c) 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * (c) UNIX System Laboratories, Inc.
7 * All or some portions of this file are derived from material licensed
8 * to the University of California by American Telephone and Telegraph
9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10 * the permission of UNIX System Laboratories, Inc.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95
37 * From NetBSD: ufs_vfsops.c,v 1.42 2011/03/24 17:05:46 bouyer Exp
38 */
39
40 /*
41 * Copyright (c) 1982, 1986, 1990, 1993, 1995
42 * The Regents of the University of California. All rights reserved.
43 *
44 * This code is derived from software contributed to Berkeley by
45 * Robert Elz at The University of Melbourne.
46 *
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
49 * are met:
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 * 3. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 *
71 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
72 * From NetBSD: ufs_quota.c,v 1.70 2011/03/24 17:05:46 bouyer Exp
73 */
74
75 /*
76 * Note that both of the copyrights above are moderately spurious;
77 * this code should almost certainly have the Copyright 2010 Manuel
78 * Bouyer notice and license found in e.g. sys/ufs/ufs/quota2_subr.c.
79 * However, they're what was on the files this code was sliced out of.
80 */
81
82 #include <sys/cdefs.h>
83 __KERNEL_RCSID(0, "$NetBSD: vfs_quotactl.c,v 1.35 2012/01/29 07:14:38 dholland Exp $");
84
85 #include <sys/malloc.h> /* XXX: temporary */
86 #include <sys/mount.h>
87 #include <sys/quota.h>
88 #include <sys/quotactl.h>
89 #include <quota/quotaprop.h>
90
91 static int
92 vfs_quotactl_getversion(struct mount *mp,
93 prop_dictionary_t cmddict, int q2type,
94 prop_array_t datas)
95 {
96 prop_array_t replies;
97 prop_dictionary_t data;
98 struct quotastat stat;
99 int q2version;
100 struct vfs_quotactl_args args;
101 int error;
102
103 KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
104 KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
105
106 args.qc_op = QUOTACTL_STAT;
107 args.u.stat.qc_ret = &stat;
108 error = VFS_QUOTACTL(mp, &args);
109 if (error) {
110 return error;
111 }
112
113 /*
114 * Set q2version based on the stat results. Currently there
115 * are two valid values for q2version, 1 and 2, which we pick
116 * based on whether quotacheck is required.
117 */
118 if (stat.qs_restrictions & QUOTA_RESTRICT_NEEDSQUOTACHECK) {
119 q2version = 1;
120 } else {
121 q2version = 2;
122 }
123
124 data = prop_dictionary_create();
125 if (data == NULL) {
126 return ENOMEM;
127 }
128
129 if (!prop_dictionary_set_int8(data, "version", q2version)) {
130 prop_object_release(data);
131 return ENOMEM;
132 }
133
134 replies = prop_array_create();
135 if (replies == NULL) {
136 prop_object_release(data);
137 return ENOMEM;
138 }
139
140 if (!prop_array_add_and_rel(replies, data)) {
141 prop_object_release(data);
142 prop_object_release(replies);
143 return ENOMEM;
144 }
145
146 if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
147 prop_object_release(replies);
148 return ENOMEM;
149 }
150
151 return error;
152 }
153
154 static int
155 vfs_quotactl_quotaon(struct mount *mp,
156 prop_dictionary_t cmddict, int q2type,
157 prop_array_t datas)
158 {
159 prop_dictionary_t data;
160 const char *qfile;
161 struct vfs_quotactl_args args;
162
163 KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
164 KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
165
166 if (prop_array_count(datas) != 1)
167 return EINVAL;
168
169 data = prop_array_get(datas, 0);
170 if (data == NULL)
171 return ENOMEM;
172 if (!prop_dictionary_get_cstring_nocopy(data, "quotafile",
173 &qfile))
174 return EINVAL;
175
176 args.qc_op = QUOTACTL_QUOTAON;
177 args.u.quotaon.qc_idtype = q2type;
178 args.u.quotaon.qc_quotafile = qfile;
179 return VFS_QUOTACTL(mp, &args);
180 }
181
182 static int
183 vfs_quotactl_quotaoff(struct mount *mp,
184 prop_dictionary_t cmddict, int q2type,
185 prop_array_t datas)
186 {
187 struct vfs_quotactl_args args;
188
189 KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
190 KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
191
192 if (prop_array_count(datas) != 0)
193 return EINVAL;
194
195 args.qc_op = QUOTACTL_QUOTAOFF;
196 args.u.quotaoff.qc_idtype = q2type;
197 return VFS_QUOTACTL(mp, &args);
198 }
199
200 static int
201 vfs_quotactl_get_addreply(const struct quotakey *qk,
202 const struct quotaval *blocks,
203 const struct quotaval *files,
204 prop_array_t replies)
205 {
206 prop_dictionary_t dict;
207 id_t id;
208 int defaultq;
209 uint64_t *valuesp[QUOTA_NLIMITS];
210
211 /* XXX illegal casts */
212 valuesp[QUOTA_LIMIT_BLOCK] = (void *)(intptr_t)&blocks->qv_hardlimit;
213 valuesp[QUOTA_LIMIT_FILE] = (void *)(intptr_t)&files->qv_hardlimit;
214
215 if (qk->qk_id == QUOTA_DEFAULTID) {
216 id = 0;
217 defaultq = 1;
218 } else {
219 id = qk->qk_id;
220 defaultq = 0;
221 }
222
223 dict = quota64toprop(id, defaultq, valuesp,
224 ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
225 ufs_quota_limit_names, QUOTA_NLIMITS);
226 if (dict == NULL)
227 return ENOMEM;
228 if (!prop_array_add_and_rel(replies, dict)) {
229 prop_object_release(dict);
230 return ENOMEM;
231 }
232
233 return 0;
234 }
235
236 static int
237 vfs_quotactl_get(struct mount *mp,
238 prop_dictionary_t cmddict, int idtype,
239 prop_array_t datas)
240 {
241 prop_object_iterator_t iter;
242 prop_dictionary_t data;
243 prop_array_t replies;
244 uint32_t id;
245 const char *idstr;
246 struct vfs_quotactl_args args;
247 struct quotakey qk;
248 struct quotaval blocks, files;
249 int error;
250
251 KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
252 KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
253
254 replies = prop_array_create();
255 if (replies == NULL) {
256 return ENOMEM;
257 }
258
259 iter = prop_array_iterator(datas);
260 if (iter == NULL) {
261 prop_object_release(replies);
262 return ENOMEM;
263 }
264
265 while ((data = prop_object_iterator_next(iter)) != NULL) {
266 qk.qk_idtype = idtype;
267
268 if (!prop_dictionary_get_uint32(data, "id", &id)) {
269 if (!prop_dictionary_get_cstring_nocopy(data, "id",
270 &idstr))
271 continue;
272 if (strcmp(idstr, "default")) {
273 error = EINVAL;
274 goto fail;
275 }
276 qk.qk_id = QUOTA_DEFAULTID;
277 } else {
278 qk.qk_id = id;
279 }
280
281 qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
282
283 args.qc_op = QUOTACTL_GET;
284 args.u.get.qc_key = &qk;
285 args.u.get.qc_ret = &blocks;
286 error = VFS_QUOTACTL(mp, &args);
287 if (error == EPERM) {
288 /* XXX does this make sense? */
289 continue;
290 } else if (error == ENOENT) {
291 /* XXX does *this* make sense? */
292 continue;
293 } else if (error) {
294 goto fail;
295 }
296
297 qk.qk_objtype = QUOTA_OBJTYPE_FILES;
298
299 args.qc_op = QUOTACTL_GET;
300 args.u.get.qc_key = &qk;
301 args.u.get.qc_ret = &files;
302 error = VFS_QUOTACTL(mp, &args);
303 if (error == EPERM) {
304 /* XXX does this make sense? */
305 continue;
306 } else if (error == ENOENT) {
307 /* XXX does *this* make sense? */
308 continue;
309 } else if (error) {
310 goto fail;
311 }
312
313 error = vfs_quotactl_get_addreply(&qk, &blocks, &files,
314 replies);
315 }
316
317 prop_object_iterator_release(iter);
318 if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
319 error = ENOMEM;
320 } else {
321 error = 0;
322 }
323
324 return error;
325
326 fail:
327 prop_object_iterator_release(iter);
328 prop_object_release(replies);
329 return error;
330 }
331
332 static int
333 vfs_quotactl_put_extractinfo(prop_dictionary_t data,
334 struct quotaval *blocks, struct quotaval *files)
335 {
336 /*
337 * So, the way proptoquota64 works is that you pass it an
338 * array of pointers to uint64. Each of these pointers is
339 * supposed to point to 5 (UFS_QUOTA_NENTRIES) uint64s. This
340 * array of pointers is the second argument. The third and
341 * forth argument are the names of the five values to extract,
342 * and UFS_QUOTA_NENTRIES. The last two arguments are the
343 * names assocated with the pointers (QUOTATYPE_LDICT_BLOCK,
344 * QUOTADICT_LTYPE_FILE) and the number of pointers. Most of
345 * the existing code was unsafely casting struct quotaval
346 * (formerly struct ufs_quota_entry) to (uint64_t *) and using
347 * that as the block of 5 uint64s. Or worse, pointing to
348 * subregions of that and reducing the number of uint64s to
349 * pull "adjacent" values. Demons fly out of your nose!
350 */
351
352 uint64_t bvals[UFS_QUOTA_NENTRIES];
353 uint64_t fvals[UFS_QUOTA_NENTRIES];
354 uint64_t *valptrs[QUOTA_NLIMITS];
355 int error;
356
357 valptrs[QUOTA_LIMIT_BLOCK] = bvals;
358 valptrs[QUOTA_LIMIT_FILE] = fvals;
359 error = proptoquota64(data, valptrs,
360 ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
361 ufs_quota_limit_names, QUOTA_NLIMITS);
362 if (error) {
363 return error;
364 }
365
366 /*
367 * There are no symbolic constants for these indexes!
368 */
369
370 blocks->qv_hardlimit = bvals[0];
371 blocks->qv_softlimit = bvals[1];
372 blocks->qv_usage = bvals[2];
373 blocks->qv_expiretime = bvals[3];
374 blocks->qv_grace = bvals[4];
375 files->qv_hardlimit = fvals[0];
376 files->qv_softlimit = fvals[1];
377 files->qv_usage = fvals[2];
378 files->qv_expiretime = fvals[3];
379 files->qv_grace = fvals[4];
380
381 return 0;
382 }
383
384 static int
385 vfs_quotactl_put(struct mount *mp,
386 prop_dictionary_t cmddict, int q2type,
387 prop_array_t datas)
388 {
389 prop_array_t replies;
390 prop_object_iterator_t iter;
391 prop_dictionary_t data;
392 int defaultq;
393 uint32_t id;
394 const char *idstr;
395 struct quotakey qk;
396 struct quotaval blocks, files;
397 struct vfs_quotactl_args args;
398 int error;
399
400 KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
401 KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
402
403 replies = prop_array_create();
404 if (replies == NULL)
405 return ENOMEM;
406
407 iter = prop_array_iterator(datas);
408 if (iter == NULL) {
409 prop_object_release(replies);
410 return ENOMEM;
411 }
412
413 while ((data = prop_object_iterator_next(iter)) != NULL) {
414
415 KASSERT(prop_object_type(data) == PROP_TYPE_DICTIONARY);
416
417 if (!prop_dictionary_get_uint32(data, "id", &id)) {
418 if (!prop_dictionary_get_cstring_nocopy(data, "id",
419 &idstr))
420 continue;
421 if (strcmp(idstr, "default"))
422 continue;
423 id = 0;
424 defaultq = 1;
425 } else {
426 defaultq = 0;
427 }
428
429 error = vfs_quotactl_put_extractinfo(data, &blocks, &files);
430 if (error) {
431 goto err;
432 }
433
434 qk.qk_idtype = q2type;
435 qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
436 qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
437
438 args.qc_op = QUOTACTL_PUT;
439 args.u.put.qc_key = &qk;
440 args.u.put.qc_val = &blocks;
441 error = VFS_QUOTACTL(mp, &args);
442 if (error) {
443 goto err;
444 }
445
446 qk.qk_idtype = q2type;
447 qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
448 qk.qk_objtype = QUOTA_OBJTYPE_FILES;
449
450 args.qc_op = QUOTACTL_PUT;
451 args.u.put.qc_key = &qk;
452 args.u.put.qc_val = &files;
453 error = VFS_QUOTACTL(mp, &args);
454 if (error) {
455 goto err;
456 }
457 }
458 prop_object_iterator_release(iter);
459 if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
460 error = ENOMEM;
461 } else {
462 error = 0;
463 }
464 return error;
465 err:
466 prop_object_iterator_release(iter);
467 prop_object_release(replies);
468 return error;
469 }
470
471 static prop_dictionary_t
472 vfs_quotactl_getall_makereply(const struct quotakey *key)
473 {
474 prop_dictionary_t dict;
475 id_t id;
476 int defaultq;
477
478 dict = prop_dictionary_create();
479 if (dict == NULL)
480 return NULL;
481
482 id = key->qk_id;
483 if (id == QUOTA_DEFAULTID) {
484 id = 0;
485 defaultq = 1;
486 } else {
487 defaultq = 0;
488 }
489
490 if (defaultq) {
491 if (!prop_dictionary_set_cstring_nocopy(dict, "id",
492 "default")) {
493 goto err;
494 }
495 } else {
496 if (!prop_dictionary_set_uint32(dict, "id", id)) {
497 goto err;
498 }
499 }
500
501 return dict;
502
503 err:
504 prop_object_release(dict);
505 return NULL;
506 }
507
508 static int
509 vfs_quotactl_getall_addreply(prop_dictionary_t thisreply,
510 const struct quotakey *key, const struct quotaval *val)
511 {
512 #define INITQVNAMES_ALL { \
513 QUOTADICT_LIMIT_HARD, \
514 QUOTADICT_LIMIT_SOFT, \
515 QUOTADICT_LIMIT_USAGE, \
516 QUOTADICT_LIMIT_ETIME, \
517 QUOTADICT_LIMIT_GTIME \
518 }
519 #define N_QV 5
520
521 const char *val_names[] = INITQVNAMES_ALL;
522 uint64_t vals[N_QV];
523 prop_dictionary_t dict2;
524 const char *objtypename;
525
526 switch (key->qk_objtype) {
527 case QUOTA_OBJTYPE_BLOCKS:
528 objtypename = QUOTADICT_LTYPE_BLOCK;
529 break;
530 case QUOTA_OBJTYPE_FILES:
531 objtypename = QUOTADICT_LTYPE_FILE;
532 break;
533 default:
534 return EINVAL;
535 }
536
537 vals[0] = val->qv_hardlimit;
538 vals[1] = val->qv_softlimit;
539 vals[2] = val->qv_usage;
540 vals[3] = val->qv_expiretime;
541 vals[4] = val->qv_grace;
542 dict2 = limits64toprop(vals, val_names, N_QV);
543 if (dict2 == NULL)
544 return ENOMEM;
545
546 if (!prop_dictionary_set_and_rel(thisreply, objtypename, dict2))
547 return ENOMEM;
548
549 return 0;
550 }
551
552 static int
553 vfs_quotactl_getall(struct mount *mp,
554 prop_dictionary_t cmddict, int q2type,
555 prop_array_t datas)
556 {
557 struct quotakcursor cursor;
558 struct quotakey *keys;
559 struct quotaval *vals;
560 unsigned loopmax = 8;
561 unsigned loopnum;
562 int skipidtype;
563 struct vfs_quotactl_args args;
564 prop_array_t replies;
565 int atend, atzero;
566 struct quotakey *key;
567 struct quotaval *val;
568 id_t lastid;
569 prop_dictionary_t thisreply;
570 unsigned i;
571 int error, error2;
572
573 KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
574
575 args.qc_op = QUOTACTL_CURSOROPEN;
576 args.u.cursoropen.qc_cursor = &cursor;
577 error = VFS_QUOTACTL(mp, &args);
578 if (error) {
579 return error;
580 }
581
582 keys = malloc(loopmax * sizeof(keys[0]), M_TEMP, M_WAITOK);
583 vals = malloc(loopmax * sizeof(vals[0]), M_TEMP, M_WAITOK);
584
585 skipidtype = (q2type == QUOTA_IDTYPE_USER ?
586 QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER);
587 args.qc_op = QUOTACTL_CURSORSKIPIDTYPE;
588 args.u.cursorskipidtype.qc_cursor = &cursor;
589 args.u.cursorskipidtype.qc_idtype = skipidtype;
590 error = VFS_QUOTACTL(mp, &args);
591 /* ignore if it fails */
592 (void)error;
593
594 replies = prop_array_create();
595 if (replies == NULL) {
596 error = ENOMEM;
597 goto err;
598 }
599
600 thisreply = NULL;
601 lastid = 0; /* value not actually referenced */
602 atzero = 0;
603
604 while (1) {
605 args.qc_op = QUOTACTL_CURSORATEND;
606 args.u.cursoratend.qc_cursor = &cursor;
607 args.u.cursoratend.qc_ret = &atend;
608 error = VFS_QUOTACTL(mp, &args);
609 if (error) {
610 goto err;
611 }
612 if (atend) {
613 break;
614 }
615
616 args.qc_op = QUOTACTL_CURSORGET;
617 args.u.cursorget.qc_cursor = &cursor;
618 args.u.cursorget.qc_keys = keys;
619 args.u.cursorget.qc_vals = vals;
620 args.u.cursorget.qc_maxnum = loopmax;
621 args.u.cursorget.qc_ret = &loopnum;
622
623 error = VFS_QUOTACTL(mp, &args);
624 if (error == EDEADLK) {
625 /*
626 * transaction abort, start over
627 */
628
629 args.qc_op = QUOTACTL_CURSORREWIND;
630 args.u.cursorrewind.qc_cursor = &cursor;
631 error = VFS_QUOTACTL(mp, &args);
632 if (error) {
633 goto err;
634 }
635
636 args.qc_op = QUOTACTL_CURSORSKIPIDTYPE;
637 args.u.cursorskipidtype.qc_cursor = &cursor;
638 args.u.cursorskipidtype.qc_idtype = skipidtype;
639 error = VFS_QUOTACTL(mp, &args);
640 /* ignore if it fails */
641 (void)error;
642
643 prop_object_release(replies);
644 replies = prop_array_create();
645 if (replies == NULL) {
646 error = ENOMEM;
647 goto err;
648 }
649
650 thisreply = NULL;
651 lastid = 0;
652 atzero = 0;
653
654 continue;
655 }
656 if (error) {
657 goto err;
658 }
659
660 if (loopnum == 0) {
661 /*
662 * This is not supposed to happen. However,
663 * allow a return of zero items once as long
664 * as something happens (including an atend
665 * indication) on the next pass. If it happens
666 * twice, warn and assume end of iteration.
667 */
668 if (atzero) {
669 printf("vfs_quotactl: zero items returned\n");
670 break;
671 }
672 atzero = 1;
673 } else {
674 atzero = 0;
675 }
676
677 for (i = 0; i < loopnum; i++) {
678 key = &keys[i];
679 val = &vals[i];
680
681 if (key->qk_idtype != q2type) {
682 /* don't want this result */
683 continue;
684 }
685
686 if (thisreply == NULL || key->qk_id != lastid) {
687 lastid = key->qk_id;
688 thisreply = vfs_quotactl_getall_makereply(key);
689 if (thisreply == NULL) {
690 error = ENOMEM;
691 goto err;
692 }
693 /*
694 * Note: while we release our reference to
695 * thisreply here, we can (and do) continue to
696 * use the pointer in the loop because the
697 * copy attached to the replies array is not
698 * going away.
699 */
700 if (!prop_array_add_and_rel(replies,
701 thisreply)) {
702 error = ENOMEM;
703 goto err;
704 }
705 }
706
707 error = vfs_quotactl_getall_addreply(thisreply,
708 key, val);
709 if (error) {
710 goto err;
711 }
712 }
713 }
714
715 if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
716 replies = NULL;
717 error = ENOMEM;
718 goto err;
719 }
720 replies = NULL;
721 error = 0;
722
723 err:
724 free(keys, M_TEMP);
725 free(vals, M_TEMP);
726
727 if (replies != NULL) {
728 prop_object_release(replies);
729 }
730
731 args.qc_op = QUOTACTL_CURSORCLOSE;
732 args.u.cursorclose.qc_cursor = &cursor;
733 error2 = VFS_QUOTACTL(mp, &args);
734
735 if (error) {
736 return error;
737 }
738 error = error2;
739 return error;
740 }
741
742 static int
743 vfs_quotactl_clear(struct mount *mp,
744 prop_dictionary_t cmddict, int q2type,
745 prop_array_t datas)
746 {
747 prop_array_t replies;
748 prop_object_iterator_t iter;
749 prop_dictionary_t data;
750 uint32_t id;
751 int defaultq;
752 const char *idstr;
753 struct quotakey qk;
754 struct vfs_quotactl_args args;
755 int error;
756
757 KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
758 KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
759
760 replies = prop_array_create();
761 if (replies == NULL)
762 return ENOMEM;
763
764 iter = prop_array_iterator(datas);
765 if (iter == NULL) {
766 prop_object_release(replies);
767 return ENOMEM;
768 }
769
770 while ((data = prop_object_iterator_next(iter)) != NULL) {
771 if (!prop_dictionary_get_uint32(data, "id", &id)) {
772 if (!prop_dictionary_get_cstring_nocopy(data, "id",
773 &idstr))
774 continue;
775 if (strcmp(idstr, "default"))
776 continue;
777 id = 0;
778 defaultq = 1;
779 } else {
780 defaultq = 0;
781 }
782
783 qk.qk_idtype = q2type;
784 qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
785 qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
786
787 args.qc_op = QUOTACTL_DELETE;
788 args.u.delete.qc_key = &qk;
789 error = VFS_QUOTACTL(mp, &args);
790 if (error) {
791 goto err;
792 }
793
794 qk.qk_idtype = q2type;
795 qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
796 qk.qk_objtype = QUOTA_OBJTYPE_FILES;
797
798 args.qc_op = QUOTACTL_DELETE;
799 args.u.delete.qc_key = &qk;
800 error = VFS_QUOTACTL(mp, &args);
801 if (error) {
802 goto err;
803 }
804 }
805
806 prop_object_iterator_release(iter);
807 if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
808 error = ENOMEM;
809 } else {
810 error = 0;
811 }
812 return error;
813 err:
814 prop_object_iterator_release(iter);
815 prop_object_release(replies);
816 return error;
817 }
818
819 static int
820 vfs_quotactl_cmd(struct mount *mp, prop_dictionary_t cmddict)
821 {
822 int error;
823 const char *cmd, *type;
824 prop_array_t datas;
825 int q2type;
826
827 if (!prop_dictionary_get_cstring_nocopy(cmddict, "command", &cmd))
828 return EINVAL;
829 if (!prop_dictionary_get_cstring_nocopy(cmddict, "type", &type))
830 return EINVAL;
831
832 if (!strcmp(type, QUOTADICT_CLASS_USER)) {
833 q2type = QUOTA_CLASS_USER;
834 } else if (!strcmp(type, QUOTADICT_CLASS_GROUP)) {
835 q2type = QUOTA_CLASS_GROUP;
836 } else {
837 /* XXX this is a bad errno for this case */
838 return EOPNOTSUPP;
839 }
840
841 datas = prop_dictionary_get(cmddict, "data");
842 if (datas == NULL || prop_object_type(datas) != PROP_TYPE_ARRAY)
843 return EINVAL;
844
845 prop_object_retain(datas);
846 prop_dictionary_remove(cmddict, "data"); /* prepare for return */
847
848 if (strcmp(cmd, "get version") == 0) {
849 error = vfs_quotactl_getversion(mp, cmddict, q2type, datas);
850 } else if (strcmp(cmd, "quotaon") == 0) {
851 error = vfs_quotactl_quotaon(mp, cmddict, q2type, datas);
852 } else if (strcmp(cmd, "quotaoff") == 0) {
853 error = vfs_quotactl_quotaoff(mp, cmddict, q2type, datas);
854 } else if (strcmp(cmd, "get") == 0) {
855 error = vfs_quotactl_get(mp, cmddict, q2type, datas);
856 } else if (strcmp(cmd, "set") == 0) {
857 error = vfs_quotactl_put(mp, cmddict, q2type, datas);
858 } else if (strcmp(cmd, "getall") == 0) {
859 error = vfs_quotactl_getall(mp, cmddict, q2type, datas);
860 } else if (strcmp(cmd, "clear") == 0) {
861 error = vfs_quotactl_clear(mp, cmddict, q2type, datas);
862 } else {
863 /* XXX this a bad errno for this case */
864 error = EOPNOTSUPP;
865 }
866
867 error = (prop_dictionary_set_int8(cmddict, "return",
868 error) ? 0 : ENOMEM);
869 prop_object_release(datas);
870
871 return error;
872 }
873
874 int
875 vfs_quotactl(struct mount *mp, prop_dictionary_t dict)
876 {
877 prop_dictionary_t cmddict;
878 prop_array_t commands;
879 prop_object_iterator_t iter;
880 int error;
881
882 error = quota_get_cmds(dict, &commands);
883 if (error) {
884 return error;
885 }
886
887 iter = prop_array_iterator(commands);
888 if (iter == NULL) {
889 return ENOMEM;
890 }
891
892 while ((cmddict = prop_object_iterator_next(iter)) != NULL) {
893 if (prop_object_type(cmddict) != PROP_TYPE_DICTIONARY) {
894 /* XXX shouldn't this be an error? */
895 continue;
896 }
897 error = vfs_quotactl_cmd(mp, cmddict);
898 if (error) {
899 break;
900 }
901 }
902 prop_object_iterator_release(iter);
903 return error;
904 }
905