Annotation of src/lib/libquota/quota_proplib.c, Revision 1.6
1.6 ! dholland 1: /* $NetBSD: quota_proplib.c,v 1.5 2012/01/09 15:41:58 dholland Exp $ */
1.1 dholland 2: /*-
3: * Copyright (c) 2011 Manuel Bouyer
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. Redistributions in binary form must reproduce the above copyright
12: * notice, this list of conditions and the following disclaimer in the
13: * documentation and/or other materials provided with the distribution.
14: *
15: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25: * POSSIBILITY OF SUCH DAMAGE.
26: */
27:
28: #include <sys/cdefs.h>
1.6 ! dholland 29: __RCSID("$NetBSD: quota_proplib.c,v 1.5 2012/01/09 15:41:58 dholland Exp $");
1.1 dholland 30:
1.4 dholland 31: #include <stdlib.h>
1.1 dholland 32: #include <string.h>
1.4 dholland 33: #include <limits.h>
1.1 dholland 34: #include <errno.h>
1.3 dholland 35: #include <err.h>
1.1 dholland 36:
37: #include <quota.h>
38: #include "quotapvt.h"
39:
40: #include <quota/quotaprop.h>
41: #include <quota/quota.h>
42:
1.4 dholland 43: struct proplib_quotacursor {
44: prop_array_t users;
45: prop_array_t groups;
46:
47: unsigned numusers;
48: unsigned numgroups;
49:
50: unsigned haveusers;
51: unsigned havegroups;
52:
53: unsigned didusers;
54: unsigned pos;
55: unsigned didblocks;
56: };
57:
1.5 dholland 58: int
1.3 dholland 59: __quota_proplib_getversion(struct quotahandle *qh, int8_t *version_ret)
60: {
61: const char *idtype;
62: prop_dictionary_t dict, data, cmd;
63: prop_array_t cmds, blank, datas;
64: const char *cmdstr;
65: struct plistref pref;
66: int8_t error8;
67:
68: /* XXX does this matter? */
69: idtype = ufs_quota_class_names[QUOTA_CLASS_USER];
70:
71: /*
72: * XXX this should not crash out on error. But this is what
73: * the code this came from did... probably because it can just
74: * leak memory instead of needing the proper cleanup code.
75: */
76:
77: dict = quota_prop_create();
78: if (dict == NULL) {
79: err(1, "quota_getimplname: quota_prop_create");
80: }
81:
82: cmds = prop_array_create();
83: if (cmds == NULL) {
84: err(1, "quota_getimplname: prop_array_create");
85: }
86:
87: blank = prop_array_create();
88: if (blank == NULL) {
89: err(1, "quota_getimplname: prop_array_create");
90: }
91:
92: if (!quota_prop_add_command(cmds, "get version", idtype, blank)) {
93: err(1, "quota_getimplname: quota_prop_add_command");
94: }
95:
96: if (!prop_dictionary_set(dict, "commands", cmds)) {
97: err(1, "quota_getimplname: prop_dictionary_set");
98: }
99:
100: if (prop_dictionary_send_syscall(dict, &pref) != 0) {
101: err(1, "quota_getimplname: prop_dictionary_send_syscall");
102: }
103:
104: /* XXX don't we need prop_object_release(cmds) here too? */
105: prop_object_release(dict);
106:
107: if (quotactl(qh->qh_mountpoint, &pref) != 0)
108: err(1, "quota_getimplname: quotactl");
109:
110: if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
111: err(1, "quota_getimplname: prop_dictionary_recv_syscall");
112: }
113:
114: if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
115: err(1, "quota_getimplname: bad response (%s)",
116: "quota_get_cmds");
117: }
118:
119: cmd = prop_array_get(cmds, 0);
120: if (cmd == NULL) {
121: err(1, "quota_getimplname: bad response (%s)",
122: "prop_array_get");
123: }
124:
125: if (!prop_dictionary_get_cstring_nocopy(cmd, "command", &cmdstr)) {
126: err(1, "quota_getimplname: bad response (%s)",
127: "prop_dictionary_get_cstring_nocopy");
128: }
129:
130: if (strcmp("get version", cmdstr) != 0) {
131: errx(1, "quota_getimplname: bad response (%s)",
132: "command name did not match");
133: }
134:
135: if (!prop_dictionary_get_int8(cmd, "return", &error8)) {
136: err(1, "quota_getimplname: bad response (%s)",
137: "prop_dictionary_get_int8");
138: }
139:
140: if (error8) {
141: /* this means the RPC action failed */
142: prop_object_release(dict);
143: errno = error8;
144: return -1;
145: }
146:
147: datas = prop_dictionary_get(cmd, "data");
148: if (datas == NULL) {
149: err(1, "quota_getimplname: bad response (%s)",
150: "prop_dictionary_get");
151: }
152:
153: data = prop_array_get(datas, 0);
154: if (data == NULL) {
155: err(1, "quota_getimplname: bad response (%s)",
156: "prop_array_get");
157: }
158:
159: if (!prop_dictionary_get_int8(data, "version", version_ret)) {
160: err(1, "quota_getimplname: bad response (%s)",
161: "prop_array_get_int8");
162: }
163:
164: return 0;
165: }
166:
167: const char *
168: __quota_proplib_getimplname(struct quotahandle *qh)
169: {
170: int8_t version;
171:
172: if (__quota_proplib_getversion(qh, &version) < 0) {
173: return NULL;
174: }
175: switch (version) {
176: case 1: return "ffs quota1";
177: case 2: return "ffs quota2";
178: default: break;
179: }
180: return "unknown";
181: }
182:
1.4 dholland 183: static int
184: __quota_proplib_extractval(int objtype, prop_dictionary_t data,
185: struct quotaval *qv)
186: {
187: uint64_t vals[UFS_QUOTA_NENTRIES];
188: uint64_t *valptrs[1];
189: int limitcode;
190:
191: /*
192: * So, the way proptoquota64 works is that you pass it an
193: * array of pointers to uint64. Each of these pointers is
194: * supposed to point to 5 (UFS_QUOTA_NENTRIES) uint64s. This
195: * array of pointers is the second argument. The third and
196: * forth argument are the names of the five values to extract,
197: * and UFS_QUOTA_NENTRIES. The last two arguments are the
198: * names assocated with the pointers (QUOTATYPE_LDICT_BLOCK,
199: * QUOTADICT_LTYPE_FILE) and the number of pointers. Most of
200: * the existing code was unsafely casting struct quotaval
201: * (formerly struct ufs_quota_entry) to (uint64_t *) and using
202: * that as the block of 5 uint64s. I refuse to countenance
203: * that. Also, most of that code extracts both block and file
204: * limits at once (last arguments are ufs_quota_limit_names
205: * and UFS_QUOTA_NLIMITS) but I only need one.
206: */
207:
208: switch (objtype) {
209: case QUOTA_OBJTYPE_BLOCKS:
210: limitcode = QUOTA_LIMIT_BLOCK;
211: break;
212: case QUOTA_OBJTYPE_FILES:
213: limitcode = QUOTA_LIMIT_FILE;
214: break;
215: default:
216: errno = EINVAL;
217: return -1;
218: }
219:
220: valptrs[0] = vals;
221: errno = proptoquota64(data, valptrs,
222: ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
223: &ufs_quota_limit_names[limitcode], 1);
224: if (errno) {
225: return -1;
226: }
227:
228: /*
229: * there are no symbolic constants for these indexes! XXX
230: */
231: qv->qv_hardlimit = vals[0];
232: qv->qv_softlimit = vals[1];
233: qv->qv_usage = vals[2];
234: qv->qv_expiretime = vals[3];
235: qv->qv_grace = vals[4];
236:
237: return 0;
238: }
239:
1.1 dholland 240: int
241: __quota_proplib_get(struct quotahandle *qh, const struct quotakey *qk,
242: struct quotaval *qv)
243: {
244: prop_dictionary_t dict, data, cmd;
245: prop_array_t cmds, datas;
246: struct plistref pref;
247: int8_t error8;
248: const char *idstr;
249: const char *cmdstr;
250: int serrno;
251:
252: switch (qk->qk_idtype) {
253: case QUOTA_IDTYPE_USER:
254: idstr = QUOTADICT_CLASS_USER;
255: break;
256: case QUOTA_IDTYPE_GROUP:
257: idstr = QUOTADICT_CLASS_GROUP;
258: break;
259: default:
260: errno = EINVAL;
261: return -1;
262: }
263:
264: /*
265: * Cons up the RPC packet.
266: */
267:
268: data = prop_dictionary_create();
269: if (data == NULL) {
270: errno = ENOMEM;
271: return -1;
272: }
1.2 dholland 273: if (qk->qk_id == QUOTA_DEFAULTID) {
274: if (!prop_dictionary_set_cstring(data, "id", "default")) {
275: prop_object_release(data);
276: errno = ENOMEM;
277: return -1;
278: }
279: } else {
280: if (!prop_dictionary_set_uint32(data, "id", qk->qk_id)) {
281: prop_object_release(data);
282: errno = ENOMEM;
283: return -1;
284: }
1.1 dholland 285: }
286:
287: datas = prop_array_create();
288: if (datas == NULL) {
289: prop_object_release(data);
290: errno = ENOMEM;
291: return -1;
292: }
293: if (!prop_array_add_and_rel(datas, data)) {
294: prop_object_release(datas);
295: /* DATA is consumed if this fails! */
296: errno = ENOMEM;
297: return -1;
298: }
299:
300: cmds = prop_array_create();
301: if (cmds == NULL) {
302: prop_object_release(datas);
303: errno = ENOMEM;
304: return -1;
305: }
306: if (!quota_prop_add_command(cmds, "get", idstr, datas)) {
307: prop_object_release(cmds);
308: /* AFAICT, CMDS is consumed if this fails, too. */
309: errno = ENOMEM;
310: return -1;
311: }
312:
313: dict = quota_prop_create();
314: if (dict == NULL) {
315: prop_object_release(cmds);
316: errno = ENOMEM;
317: return -1;
318: }
319: if (!prop_dictionary_set(dict, "commands", cmds)) {
320: prop_object_release(dict);
321: /* here CMDS is *not* released on failure. yay consistency! */
322: prop_object_release(cmds);
323: errno = ENOMEM;
324: return -1;
325: }
326: /* as far as I can tell this is required here - dholland */
327: prop_object_release(cmds);
328:
329: /*
330: * Convert it to an XML turd for transfer.
331: */
332:
333: if (prop_dictionary_send_syscall(dict, &pref) != 0) {
334: serrno = errno;
335: prop_object_release(dict);
336: errno = serrno;
337: return -1;
338: }
339: prop_object_release(dict);
340:
341: /*
342: * Send it off.
343: *
344: * Note:
345: *
346: * prop_dictionary_send_syscall allocates memory in PREF,
347: * which we ought to free if quotactl fails, but there's no
348: * way (or no documented way) to do this without breaking the
349: * abstraction.
350: *
351: * Furthermore, quotactl replaces the send buffer in PREF
352: * with a receive buffer. (AFAIK at least...) This overwrites
353: * the send buffer and makes it impossible to free it. The
354: * receive buffer is consumed by prop_dictionary_recv_syscall
355: * with munmap(); however, I'm not sure what happens if the
356: * prop_dictionary_recv_syscall operation fails.
357: *
358: * So it at least looks as if the send bundle is leaked on
359: * every quotactl call.
360: *
361: * XXX.
362: *
363: * - dholland 20111125
364: */
365:
366: if (quotactl(qh->qh_mountpoint, &pref) != 0) {
367: /* XXX free PREF buffer here */
368: return -1;
369: }
370: /* XXX free now-overwritten PREF buffer here */
371:
372: /*
373: * Convert the XML response turd.
374: */
375:
376: if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
377: /* XXX do we have to free the buffer in PREF here? */
378: return -1;
379: }
380:
381: /*
382: * Now unpack the response.
383: */
384:
385: /* more consistency, returning an errno instead of setting it */
386: errno = quota_get_cmds(dict, &cmds);
387: if (errno != 0) {
388: prop_object_release(dict);
389: return -1;
390: }
391:
392: cmd = prop_array_get(cmds, 0);
393: if (cmd == NULL) {
394: prop_object_release(dict);
395: errno = EINVAL;
396: return -1;
397: }
398:
399: if (!prop_dictionary_get_cstring_nocopy(cmd, "command", &cmdstr)) {
400: /* malformed response from the kernel */
401: prop_object_release(dict);
402: errno = EINVAL;
403: return -1;
404: }
405:
406: if (strcmp("get", cmdstr) != 0) {
407: /* malformed response from the kernel */
408: prop_object_release(dict);
409: errno = EINVAL;
410: return -1;
411: }
412:
413: if (!prop_dictionary_get_int8(cmd, "return", &error8)) {
414: /* malformed response from the kernel */
415: prop_object_release(dict);
416: errno = EINVAL;
417: return -1;
418: }
419:
420: if (error8 == ENODEV) {
421: /* XXX this currently means quotas are not enabled */
422: /* XXX but there's currently no way to fail in quota_open */
423: /* XXX in that case */
424: quotaval_clear(qv);
425: prop_object_release(dict);
426: return 0;
427: }
428:
429: if (error8) {
430: /* this means the RPC action failed */
431: prop_object_release(dict);
432: errno = error8;
433: return -1;
434: }
435:
436: datas = prop_dictionary_get(cmd, "data");
437: if (datas == NULL) {
438: /* malformed response from the kernel */
439: prop_object_release(dict);
440: errno = EINVAL;
441: return -1;
442: }
443:
444: if (prop_array_count(datas) == 0) {
445: /* No quotas for this id */
446: prop_object_release(dict);
447: errno = ENOENT;
448: return -1;
449: }
450:
451: data = prop_array_get(datas, 0);
452: if (data == NULL) {
453: /* malformed response from the kernel */
454: prop_object_release(dict);
455: errno = EINVAL;
456: return -1;
457: }
458:
1.4 dholland 459: if (__quota_proplib_extractval(qk->qk_objtype, data, qv)) {
460: serrno = errno;
461: prop_object_release(dict);
462: errno = serrno;
463: return -1;
464: }
465:
466: prop_object_release(dict);
467:
468: return 0;
469: }
470:
1.6 ! dholland 471: int
! 472: __quota_proplib_put(struct quotahandle *qh, const struct quotakey *qk,
! 473: const struct quotaval *qv)
! 474: {
! 475: prop_dictionary_t dict, data, cmd;
! 476: prop_array_t cmds, datas;
! 477: struct plistref pref;
! 478: int8_t error8;
! 479: uint64_t *valuesp[QUOTA_NLIMITS];
! 480: const char *idtype;
! 481: unsigned limitcode, otherlimitcode;
! 482: unsigned otherobjtype;
! 483: struct quotakey qk2;
! 484: struct quotaval qv2;
! 485:
! 486: switch (qk->qk_idtype) {
! 487: case QUOTA_IDTYPE_USER:
! 488: idtype = ufs_quota_class_names[QUOTA_CLASS_USER];
! 489: break;
! 490: case QUOTA_IDTYPE_GROUP:
! 491: idtype = ufs_quota_class_names[QUOTA_CLASS_GROUP];
! 492: break;
! 493: default:
! 494: errno = EINVAL;
! 495: return -1;
! 496: }
! 497:
! 498: switch (qk->qk_objtype) {
! 499: case QUOTA_OBJTYPE_BLOCKS:
! 500: limitcode = QUOTA_LIMIT_BLOCK;
! 501: otherlimitcode = QUOTA_LIMIT_FILE;
! 502: otherobjtype = QUOTA_OBJTYPE_FILES;
! 503: break;
! 504: case QUOTA_OBJTYPE_FILES:
! 505: limitcode = QUOTA_LIMIT_FILE;
! 506: otherlimitcode = QUOTA_LIMIT_BLOCK;
! 507: otherobjtype = QUOTA_OBJTYPE_BLOCKS;
! 508: break;
! 509: default:
! 510: errno = EINVAL;
! 511: return -1;
! 512: }
! 513:
! 514: /* XXX in addition to being invalid/unsafe this also discards const */
! 515: valuesp[limitcode] = __UNCONST(&qv->qv_hardlimit);
! 516:
! 517: /*
! 518: * You cannot set just the block info or just the file info.
! 519: * You have to set both together, or EINVAL comes back. So we
! 520: * have to fetch the current values for the other object type,
! 521: * and stuff both into the RPC packet. Blah. XXX.
! 522: */
! 523: qk2.qk_idtype = qk->qk_idtype;
! 524: qk2.qk_id = qk->qk_id;
! 525: qk2.qk_objtype = otherobjtype;
! 526: if (__quota_proplib_get(qh, &qk2, &qv2)) {
! 527: if (errno == ENOENT) {
! 528: /* Nothing there yet, use a blank value */
! 529: quotaval_clear(&qv2);
! 530: } else {
! 531: return -1;
! 532: }
! 533: }
! 534: valuesp[otherlimitcode] = &qv2.qv_hardlimit;
! 535:
! 536: data = quota64toprop(qk->qk_id, qk->qk_id == QUOTA_DEFAULTID ? 1 : 0,
! 537: valuesp, ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
! 538: ufs_quota_limit_names, QUOTA_NLIMITS);
! 539:
! 540: if (data == NULL)
! 541: err(1, "quota64toprop(id)");
! 542:
! 543: dict = quota_prop_create();
! 544: cmds = prop_array_create();
! 545: datas = prop_array_create();
! 546:
! 547: if (dict == NULL || cmds == NULL || datas == NULL) {
! 548: errx(1, "can't allocate proplist");
! 549: }
! 550:
! 551: if (!prop_array_add_and_rel(datas, data))
! 552: err(1, "prop_array_add(data)");
! 553:
! 554: if (!quota_prop_add_command(cmds, "set", idtype, datas))
! 555: err(1, "prop_add_command");
! 556: if (!prop_dictionary_set(dict, "commands", cmds))
! 557: err(1, "prop_dictionary_set(command)");
! 558: #if 0
! 559: if (Dflag)
! 560: printf("message to kernel:\n%s\n",
! 561: prop_dictionary_externalize(dict));
! 562: #endif
! 563:
! 564: if (prop_dictionary_send_syscall(dict, &pref) != 0)
! 565: err(1, "prop_dictionary_send_syscall");
! 566: prop_object_release(dict);
! 567:
! 568: if (quotactl(qh->qh_mountpoint, &pref) != 0)
! 569: err(1, "quotactl");
! 570:
! 571: if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
! 572: err(1, "prop_dictionary_recv_syscall");
! 573: }
! 574:
! 575: #if 0
! 576: if (Dflag)
! 577: printf("reply from kernel:\n%s\n",
! 578: prop_dictionary_externalize(dict));
! 579: #endif
! 580:
! 581: if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
! 582: err(1, "quota_get_cmds");
! 583: }
! 584: /* only one command, no need to iter */
! 585: cmd = prop_array_get(cmds, 0);
! 586: if (cmd == NULL)
! 587: err(1, "prop_array_get(cmd)");
! 588:
! 589: if (!prop_dictionary_get_int8(cmd, "return", &error8))
! 590: err(1, "prop_get(return)");
! 591:
! 592: if (error8) {
! 593: prop_object_release(dict);
! 594: errno = error8;
! 595: return -1;
! 596: }
! 597: prop_object_release(dict);
! 598: return 0;
! 599: }
! 600:
! 601: int
! 602: __quota_proplib_delete(struct quotahandle *qh, const struct quotakey *qk)
! 603: {
! 604: prop_array_t cmds, datas;
! 605: prop_dictionary_t protodict, dict, data, cmd;
! 606: struct plistref pref;
! 607: int8_t error8;
! 608: bool ret;
! 609: const char *idtype;
! 610:
! 611: /*
! 612: * XXX for now we always clear quotas for all objtypes no
! 613: * matter what's passed in. This is ok (sort of) for now
! 614: * because the only caller is edquota, which only calls delete
! 615: * for both blocks and files in immediate succession. But it's
! 616: * wrong in the long run. I'm not fixing it at the moment
! 617: * because I expect all this code to be deleted in the near
! 618: * future.
! 619: */
! 620: (void)qk->qk_objtype;
! 621:
! 622: switch (qk->qk_idtype) {
! 623: case QUOTA_IDTYPE_USER:
! 624: idtype = ufs_quota_class_names[QUOTA_CLASS_USER];
! 625: break;
! 626: case QUOTA_IDTYPE_GROUP:
! 627: idtype = ufs_quota_class_names[QUOTA_CLASS_GROUP];
! 628: break;
! 629: default:
! 630: errno = EINVAL;
! 631: return -1;
! 632: }
! 633:
! 634: /* build a generic command */
! 635: protodict = quota_prop_create();
! 636: cmds = prop_array_create();
! 637: datas = prop_array_create();
! 638: if (protodict == NULL || cmds == NULL || datas == NULL) {
! 639: errx(1, "can't allocate proplist");
! 640: }
! 641:
! 642: data = prop_dictionary_create();
! 643: if (data == NULL)
! 644: errx(1, "can't allocate proplist");
! 645:
! 646: ret = prop_dictionary_set_uint32(data, "id", qk->qk_id);
! 647: if (!ret)
! 648: err(1, "prop_dictionary_set(id)");
! 649: if (!prop_array_add_and_rel(datas, data))
! 650: err(1, "prop_array_add(data)");
! 651:
! 652: if (!quota_prop_add_command(cmds, "clear", idtype, datas))
! 653: err(1, "prop_add_command");
! 654:
! 655: if (!prop_dictionary_set(protodict, "commands", cmds))
! 656: err(1, "prop_dictionary_set(command)");
! 657:
! 658: #if 0
! 659: if (Dflag) {
! 660: fprintf(stderr, "message to kernel for %s:\n%s\n",
! 661: qh->qh_mountpoint,
! 662: prop_dictionary_externalize(protodict));
! 663: }
! 664: #endif
! 665:
! 666: if (prop_dictionary_send_syscall(protodict, &pref) != 0)
! 667: err(1, "prop_dictionary_send_syscall");
! 668: if (quotactl(qh->qh_mountpoint, &pref) != 0)
! 669: err(1, "quotactl");
! 670:
! 671: if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
! 672: err(1, "prop_dictionary_recv_syscall");
! 673: }
! 674:
! 675: #if 0
! 676: if (Dflag) {
! 677: fprintf(stderr, "reply from kernel for %s:\n%s\n",
! 678: qh->qh_mountpoint,
! 679: prop_dictionary_externalize(dict));
! 680: }
! 681: #endif
! 682:
! 683: if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
! 684: err(1, "quota_get_cmds");
! 685: }
! 686: /* only one command, no need to iter */
! 687: cmd = prop_array_get(cmds, 0);
! 688: if (cmd == NULL)
! 689: err(1, "prop_array_get(cmd)");
! 690:
! 691: if (!prop_dictionary_get_int8(cmd, "return", &error8))
! 692: err(1, "prop_get(return)");
! 693: if (error8) {
! 694: prop_object_release(dict);
! 695: prop_object_release(protodict);
! 696: errno = error8;
! 697: return -1;
! 698: }
! 699: prop_object_release(dict);
! 700: prop_object_release(protodict);
! 701: return 0;
! 702: }
! 703:
1.4 dholland 704: static int
705: __quota_proplib_getall(struct quotahandle *qh, int idtype, prop_array_t *ret)
706: {
707: prop_dictionary_t dict, cmd;
708: prop_array_t cmds, datas;
709: struct plistref pref;
710: int8_t error8;
711:
1.1 dholland 712: /*
1.4 dholland 713: * XXX this should not crash out on error. But this is what
714: * the code this came from did... probably because it can just
715: * leak memory instead of needing the proper cleanup code.
1.1 dholland 716: */
717:
1.4 dholland 718: dict = quota_prop_create();
719: cmds = prop_array_create();
720: datas = prop_array_create();
721:
722: if (dict == NULL || cmds == NULL || datas == NULL)
723: errx(1, "can't allocate proplist");
724: if (!quota_prop_add_command(cmds, "getall",
725: ufs_quota_class_names[idtype], datas))
726: err(1, "prop_add_command");
727: if (!prop_dictionary_set(dict, "commands", cmds))
728: err(1, "prop_dictionary_set(command)");
729: #if 0
730: if (Dflag)
731: printf("message to kernel:\n%s\n",
732: prop_dictionary_externalize(dict));
733: #endif
734: if (prop_dictionary_send_syscall(dict, &pref) != 0)
735: err(1, "prop_dictionary_send_syscall");
736: prop_object_release(dict);
737:
738: if (quotactl(quota_getmountpoint(qh), &pref) != 0)
739: err(1, "quotactl");
740:
741: if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
742: err(1, "prop_dictionary_recv_syscall");
743: }
744: #if 0
745: if (Dflag)
746: printf("reply from kernel:\n%s\n",
747: prop_dictionary_externalize(dict));
748: #endif
749: if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
750: err(1, "quota_get_cmds");
751: }
752:
753: cmd = prop_array_get(cmds, 0);
754: if (cmd == NULL) {
755: err(1, "prop_array_get(cmds)");
756: }
757:
758: const char *cmdstr;
759: if (!prop_dictionary_get_cstring_nocopy(cmd, "command",
760: &cmdstr))
761: err(1, "prop_get(command)");
762:
763: if (!prop_dictionary_get_int8(cmd, "return", &error8))
764: err(1, "prop_get(return)");
765:
766: if (error8) {
767: prop_object_release(dict);
768: if (error8 != EOPNOTSUPP) {
769: errno = error8;
770: warn("get %s quotas",
771: ufs_quota_class_names[idtype]);
772: }
773: return -1;
774: }
775: datas = prop_dictionary_get(cmd, "data");
776: if (datas == NULL)
777: err(1, "prop_dict_get(datas)");
778:
779: prop_object_retain(datas);
780: prop_object_release(dict);
781:
782: *ret = datas;
783: return 0;
784: }
785:
786: struct proplib_quotacursor *
787: __quota_proplib_cursor_create(void)
788: {
789: struct proplib_quotacursor *pqc;
790:
791: pqc = malloc(sizeof(*pqc));
792: if (pqc == NULL) {
793: return NULL;
794: }
795:
796: pqc->users = NULL;
797: pqc->numusers = 0;
798: pqc->haveusers = 0;
799:
800: pqc->groups = NULL;
801: pqc->numgroups = 0;
802: pqc->havegroups = 0;
803:
804: pqc->didusers = 0;
805: pqc->pos = 0;
806: pqc->didblocks = 0;
807:
808: return pqc;
809: }
810:
811: /* ARGSUSED */
812: void
813: __quota_proplib_cursor_destroy(struct proplib_quotacursor *pqc)
814: {
815: prop_object_release(pqc->users);
816: prop_object_release(pqc->groups);
817: free(pqc);
818: }
819:
820: static int
821: __quota_proplib_cursor_load(struct quotahandle *qh,
822: struct proplib_quotacursor *pqc)
823: {
824: prop_array_t users, groups;
825:
826: if (pqc->haveusers == 0) {
827: if (__quota_proplib_getall(qh, QUOTA_IDTYPE_USER, &users)) {
828: return -1;
829: }
830: pqc->users = users;
831: pqc->numusers = prop_array_count(users);
832: pqc->haveusers = 1;
833: }
834:
835: if (pqc->havegroups == 0) {
836: if (__quota_proplib_getall(qh, QUOTA_IDTYPE_GROUP, &groups)) {
837: return -1;
838: }
839: pqc->groups = groups;
840: pqc->numgroups = prop_array_count(groups);
841: pqc->havegroups = 1;
842: }
843: return 0;
844: }
845:
846: int
847: __quota_proplib_cursor_skipidtype(struct proplib_quotacursor *pqc,
848: unsigned idtype)
849: {
850: switch (idtype) {
851: case QUOTA_IDTYPE_USER:
852: /* if not yet loaded, numusers will be 0 and users NULL */
853: pqc->haveusers = 1;
1.1 dholland 854: break;
1.4 dholland 855: case QUOTA_IDTYPE_GROUP:
856: /* if not yet loaded, numgroups will be 0 and groups NULL */
857: pqc->havegroups = 1;
1.1 dholland 858: break;
859: default:
860: errno = EINVAL;
861: return -1;
862: }
1.4 dholland 863: return 0;
864: }
865:
866: static int
867: __quota_proplib_cursor_subget(struct proplib_quotacursor *pqc,
868: prop_dictionary_t data,
869: struct quotakey *key, struct quotaval *val)
870: {
871: uint32_t id;
872: const char *strid;
873:
874: if (prop_dictionary_get_uint32(data, "id", &id)) {
875: key->qk_id = id;
876: } else if (prop_dictionary_get_cstring_nocopy(data,
877: "id", &strid) &&
878: !strcmp(strid, "default")) {
879: key->qk_id = QUOTA_DEFAULTID;
880: } else {
881: /* invalid bundle */
882: errno = EINVAL;
883: return -1;
884: }
885: if (__quota_proplib_extractval(key->qk_objtype, data, val)) {
886: return -1;
887: }
888: return 0;
889: }
890:
891: int
892: __quota_proplib_cursor_get(struct quotahandle *qh,
893: struct proplib_quotacursor *pqc,
894: struct quotakey *key, struct quotaval *val)
895: {
896: prop_dictionary_t data;
897:
898: if (pqc->haveusers == 0 || pqc->havegroups == 0) {
899: if (__quota_proplib_cursor_load(qh, pqc)) {
900: return -1;
901: }
902: }
903:
904: if (!pqc->didblocks) {
905: key->qk_objtype = QUOTA_OBJTYPE_BLOCKS;
906: } else {
907: key->qk_objtype = QUOTA_OBJTYPE_FILES;
908: }
909:
910: if (!pqc->didusers && pqc->pos >= pqc->numusers) {
911: /* in case there are 0 users */
912: pqc->didusers = 1;
913: }
914:
915: if (!pqc->didusers) {
916: key->qk_idtype = QUOTA_IDTYPE_USER;
917:
918: data = prop_array_get(pqc->users, pqc->pos);
919: if (data == NULL) {
920: errno = ENOENT;
921: return -1;
922: }
923:
924: /* get id and value */
925: if (__quota_proplib_cursor_subget(pqc, data, key, val)) {
926: return -1;
927: }
928:
929: /* advance */
930: if (!pqc->didblocks) {
931: pqc->didblocks = 1;
932: } else {
933: pqc->didblocks = 0;
934: pqc->pos++;
935: if (pqc->pos >= pqc->numusers) {
936: pqc->pos = 0;
937: pqc->didusers = 1;
938: }
939: }
940:
941: /* succeed */
942: return 0;
943: } else if (pqc->pos < pqc->numgroups) {
944: key->qk_idtype = QUOTA_IDTYPE_GROUP;
945:
946: data = prop_array_get(pqc->groups, pqc->pos);
947: if (data == NULL) {
948: errno = ENOENT;
949: return -1;
950: }
951:
952: /* get id and value */
953: if (__quota_proplib_cursor_subget(pqc, data, key, val)) {
954: return -1;
955: }
956:
957: /* advance */
958: if (!pqc->didblocks) {
959: pqc->didblocks = 1;
960: } else {
961: pqc->didblocks = 0;
962: pqc->pos++;
963: }
964:
965: /* succeed */
966: return 0;
967: } else {
968: /* at EOF */
969: /* XXX is there a better errno for this? */
970: errno = ENOENT;
971: return -1;
972: }
973: }
974:
975: int
976: __quota_proplib_cursor_getn(struct quotahandle *qh,
977: struct proplib_quotacursor *pqc,
978: struct quotakey *keys, struct quotaval *vals,
979: unsigned maxnum)
980: {
981: unsigned i;
1.1 dholland 982:
1.4 dholland 983: if (maxnum > INT_MAX) {
984: /* joker, eh? */
985: errno = EINVAL;
1.1 dholland 986: return -1;
987: }
988:
1.4 dholland 989: for (i=0; i<maxnum; i++) {
990: if (__quota_proplib_cursor_atend(qh, pqc)) {
991: break;
992: }
993: if (__quota_proplib_cursor_get(qh, pqc, &keys[i], &vals[i])) {
994: if (i > 0) {
995: /*
996: * Succeed witih what we have so far;
997: * the next attempt will hit the same
998: * error again.
999: */
1000: break;
1001: }
1002: return -1;
1003: }
1004: }
1005: return i;
1006: }
1007:
1008: int
1009: __quota_proplib_cursor_atend(struct quotahandle *qh,
1010: struct proplib_quotacursor *pqc)
1011: {
1012: if (!pqc->haveusers || !pqc->havegroups) {
1013: if (__quota_proplib_cursor_load(qh, pqc)) {
1014: /*
1015: * Cannot fail here - report that we are not
1016: * at EOF (lying if necessary) and let the
1017: * next get call try to load again, fail and
1018: * return the proper error.
1019: */
1020: return 0;
1021: }
1022: }
1023:
1024: if (!pqc->didusers && pqc->pos >= pqc->numusers) {
1025: pqc->didusers = 1;
1026: }
1027:
1028: if (!pqc->didusers) {
1029: return 0;
1030: }
1031: if (pqc->pos < pqc->numgroups) {
1032: return 0;
1033: }
1034: return 1;
1035: }
1.1 dholland 1036:
1.4 dholland 1037: int
1038: __quota_proplib_cursor_rewind(struct proplib_quotacursor *pqc)
1039: {
1040: pqc->didusers = 0;
1041: pqc->pos = 0;
1042: pqc->didblocks = 0;
1.1 dholland 1043: return 0;
1044: }
CVSweb <webmaster@jp.NetBSD.org>