Home | History | Annotate | Line # | Download | only in kern
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