Module Name: src
Committed By: dholland
Date: Mon Jan 30 19:23:49 UTC 2012
Added Files:
src/usr.sbin/quotactl: proplib-interpreter.c
Log Message:
Add a copy of the proplib interpreter here so this program will
continue to work after it's removed from the kernel. This copy is
unchanged from sys/kern/vfs_quotactl.c except that I've preserved a
cop of the old rcsid.
To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/usr.sbin/quotactl/proplib-interpreter.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Added files:
Index: src/usr.sbin/quotactl/proplib-interpreter.c
diff -u /dev/null src/usr.sbin/quotactl/proplib-interpreter.c:1.1
--- /dev/null Mon Jan 30 19:23:49 2012
+++ src/usr.sbin/quotactl/proplib-interpreter.c Mon Jan 30 19:23:49 2012
@@ -0,0 +1,908 @@
+/* $NetBSD: proplib-interpreter.c,v 1.1 2012/01/30 19:23:49 dholland Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95
+ * From NetBSD: ufs_vfsops.c,v 1.42 2011/03/24 17:05:46 bouyer Exp
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
+ * From NetBSD: ufs_quota.c,v 1.70 2011/03/24 17:05:46 bouyer Exp
+ */
+
+/*
+ * From NetBSD: vfs_quotactl.c,v 1.36 2012/01/29 07:21:59 dholland Exp
+ */
+
+/*
+ * Note that both of the copyrights above are moderately spurious;
+ * this code should almost certainly have the Copyright 2010 Manuel
+ * Bouyer notice and license found in e.g. sys/ufs/ufs/quota2_subr.c.
+ * However, they're what was on the files this code was sliced out of.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: proplib-interpreter.c,v 1.1 2012/01/30 19:23:49 dholland Exp $");
+
+#include <sys/kmem.h>
+#include <sys/mount.h>
+#include <sys/quota.h>
+#include <sys/quotactl.h>
+#include <quota/quotaprop.h>
+
+static int
+vfs_quotactl_getversion(struct mount *mp,
+ prop_dictionary_t cmddict, int q2type,
+ prop_array_t datas)
+{
+ prop_array_t replies;
+ prop_dictionary_t data;
+ struct quotastat stat;
+ int q2version;
+ struct vfs_quotactl_args args;
+ int error;
+
+ KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
+ KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
+
+ args.qc_op = QUOTACTL_STAT;
+ args.u.stat.qc_ret = &stat;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ return error;
+ }
+
+ /*
+ * Set q2version based on the stat results. Currently there
+ * are two valid values for q2version, 1 and 2, which we pick
+ * based on whether quotacheck is required.
+ */
+ if (stat.qs_restrictions & QUOTA_RESTRICT_NEEDSQUOTACHECK) {
+ q2version = 1;
+ } else {
+ q2version = 2;
+ }
+
+ data = prop_dictionary_create();
+ if (data == NULL) {
+ return ENOMEM;
+ }
+
+ if (!prop_dictionary_set_int8(data, "version", q2version)) {
+ prop_object_release(data);
+ return ENOMEM;
+ }
+
+ replies = prop_array_create();
+ if (replies == NULL) {
+ prop_object_release(data);
+ return ENOMEM;
+ }
+
+ if (!prop_array_add_and_rel(replies, data)) {
+ prop_object_release(data);
+ prop_object_release(replies);
+ return ENOMEM;
+ }
+
+ if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
+ prop_object_release(replies);
+ return ENOMEM;
+ }
+
+ return error;
+}
+
+static int
+vfs_quotactl_quotaon(struct mount *mp,
+ prop_dictionary_t cmddict, int q2type,
+ prop_array_t datas)
+{
+ prop_dictionary_t data;
+ const char *qfile;
+ struct vfs_quotactl_args args;
+
+ KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
+ KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
+
+ if (prop_array_count(datas) != 1)
+ return EINVAL;
+
+ data = prop_array_get(datas, 0);
+ if (data == NULL)
+ return ENOMEM;
+ if (!prop_dictionary_get_cstring_nocopy(data, "quotafile",
+ &qfile))
+ return EINVAL;
+
+ args.qc_op = QUOTACTL_QUOTAON;
+ args.u.quotaon.qc_idtype = q2type;
+ args.u.quotaon.qc_quotafile = qfile;
+ return VFS_QUOTACTL(mp, &args);
+}
+
+static int
+vfs_quotactl_quotaoff(struct mount *mp,
+ prop_dictionary_t cmddict, int q2type,
+ prop_array_t datas)
+{
+ struct vfs_quotactl_args args;
+
+ KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
+ KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
+
+ if (prop_array_count(datas) != 0)
+ return EINVAL;
+
+ args.qc_op = QUOTACTL_QUOTAOFF;
+ args.u.quotaoff.qc_idtype = q2type;
+ return VFS_QUOTACTL(mp, &args);
+}
+
+static int
+vfs_quotactl_get_addreply(const struct quotakey *qk,
+ const struct quotaval *blocks,
+ const struct quotaval *files,
+ prop_array_t replies)
+{
+ prop_dictionary_t dict;
+ id_t id;
+ int defaultq;
+ uint64_t *valuesp[QUOTA_NLIMITS];
+
+ /* XXX illegal casts */
+ valuesp[QUOTA_LIMIT_BLOCK] = (void *)(intptr_t)&blocks->qv_hardlimit;
+ valuesp[QUOTA_LIMIT_FILE] = (void *)(intptr_t)&files->qv_hardlimit;
+
+ if (qk->qk_id == QUOTA_DEFAULTID) {
+ id = 0;
+ defaultq = 1;
+ } else {
+ id = qk->qk_id;
+ defaultq = 0;
+ }
+
+ dict = quota64toprop(id, defaultq, valuesp,
+ ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
+ ufs_quota_limit_names, QUOTA_NLIMITS);
+ if (dict == NULL)
+ return ENOMEM;
+ if (!prop_array_add_and_rel(replies, dict)) {
+ prop_object_release(dict);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+static int
+vfs_quotactl_get(struct mount *mp,
+ prop_dictionary_t cmddict, int idtype,
+ prop_array_t datas)
+{
+ prop_object_iterator_t iter;
+ prop_dictionary_t data;
+ prop_array_t replies;
+ uint32_t id;
+ const char *idstr;
+ struct vfs_quotactl_args args;
+ struct quotakey qk;
+ struct quotaval blocks, files;
+ int error;
+
+ KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
+ KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
+
+ replies = prop_array_create();
+ if (replies == NULL) {
+ return ENOMEM;
+ }
+
+ iter = prop_array_iterator(datas);
+ if (iter == NULL) {
+ prop_object_release(replies);
+ return ENOMEM;
+ }
+
+ while ((data = prop_object_iterator_next(iter)) != NULL) {
+ qk.qk_idtype = idtype;
+
+ if (!prop_dictionary_get_uint32(data, "id", &id)) {
+ if (!prop_dictionary_get_cstring_nocopy(data, "id",
+ &idstr))
+ continue;
+ if (strcmp(idstr, "default")) {
+ error = EINVAL;
+ goto fail;
+ }
+ qk.qk_id = QUOTA_DEFAULTID;
+ } else {
+ qk.qk_id = id;
+ }
+
+ qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
+
+ args.qc_op = QUOTACTL_GET;
+ args.u.get.qc_key = &qk;
+ args.u.get.qc_ret = &blocks;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error == EPERM) {
+ /* XXX does this make sense? */
+ continue;
+ } else if (error == ENOENT) {
+ /* XXX does *this* make sense? */
+ continue;
+ } else if (error) {
+ goto fail;
+ }
+
+ qk.qk_objtype = QUOTA_OBJTYPE_FILES;
+
+ args.qc_op = QUOTACTL_GET;
+ args.u.get.qc_key = &qk;
+ args.u.get.qc_ret = &files;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error == EPERM) {
+ /* XXX does this make sense? */
+ continue;
+ } else if (error == ENOENT) {
+ /* XXX does *this* make sense? */
+ continue;
+ } else if (error) {
+ goto fail;
+ }
+
+ error = vfs_quotactl_get_addreply(&qk, &blocks, &files,
+ replies);
+ }
+
+ prop_object_iterator_release(iter);
+ if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
+ error = ENOMEM;
+ } else {
+ error = 0;
+ }
+
+ return error;
+
+ fail:
+ prop_object_iterator_release(iter);
+ prop_object_release(replies);
+ return error;
+}
+
+static int
+vfs_quotactl_put_extractinfo(prop_dictionary_t data,
+ struct quotaval *blocks, struct quotaval *files)
+{
+ /*
+ * So, the way proptoquota64 works is that you pass it an
+ * array of pointers to uint64. Each of these pointers is
+ * supposed to point to 5 (UFS_QUOTA_NENTRIES) uint64s. This
+ * array of pointers is the second argument. The third and
+ * forth argument are the names of the five values to extract,
+ * and UFS_QUOTA_NENTRIES. The last two arguments are the
+ * names assocated with the pointers (QUOTATYPE_LDICT_BLOCK,
+ * QUOTADICT_LTYPE_FILE) and the number of pointers. Most of
+ * the existing code was unsafely casting struct quotaval
+ * (formerly struct ufs_quota_entry) to (uint64_t *) and using
+ * that as the block of 5 uint64s. Or worse, pointing to
+ * subregions of that and reducing the number of uint64s to
+ * pull "adjacent" values. Demons fly out of your nose!
+ */
+
+ uint64_t bvals[UFS_QUOTA_NENTRIES];
+ uint64_t fvals[UFS_QUOTA_NENTRIES];
+ uint64_t *valptrs[QUOTA_NLIMITS];
+ int error;
+
+ valptrs[QUOTA_LIMIT_BLOCK] = bvals;
+ valptrs[QUOTA_LIMIT_FILE] = fvals;
+ error = proptoquota64(data, valptrs,
+ ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
+ ufs_quota_limit_names, QUOTA_NLIMITS);
+ if (error) {
+ return error;
+ }
+
+ /*
+ * There are no symbolic constants for these indexes!
+ */
+
+ blocks->qv_hardlimit = bvals[0];
+ blocks->qv_softlimit = bvals[1];
+ blocks->qv_usage = bvals[2];
+ blocks->qv_expiretime = bvals[3];
+ blocks->qv_grace = bvals[4];
+ files->qv_hardlimit = fvals[0];
+ files->qv_softlimit = fvals[1];
+ files->qv_usage = fvals[2];
+ files->qv_expiretime = fvals[3];
+ files->qv_grace = fvals[4];
+
+ return 0;
+}
+
+static int
+vfs_quotactl_put(struct mount *mp,
+ prop_dictionary_t cmddict, int q2type,
+ prop_array_t datas)
+{
+ prop_array_t replies;
+ prop_object_iterator_t iter;
+ prop_dictionary_t data;
+ int defaultq;
+ uint32_t id;
+ const char *idstr;
+ struct quotakey qk;
+ struct quotaval blocks, files;
+ struct vfs_quotactl_args args;
+ int error;
+
+ KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
+ KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
+
+ replies = prop_array_create();
+ if (replies == NULL)
+ return ENOMEM;
+
+ iter = prop_array_iterator(datas);
+ if (iter == NULL) {
+ prop_object_release(replies);
+ return ENOMEM;
+ }
+
+ while ((data = prop_object_iterator_next(iter)) != NULL) {
+
+ KASSERT(prop_object_type(data) == PROP_TYPE_DICTIONARY);
+
+ if (!prop_dictionary_get_uint32(data, "id", &id)) {
+ if (!prop_dictionary_get_cstring_nocopy(data, "id",
+ &idstr))
+ continue;
+ if (strcmp(idstr, "default"))
+ continue;
+ id = 0;
+ defaultq = 1;
+ } else {
+ defaultq = 0;
+ }
+
+ error = vfs_quotactl_put_extractinfo(data, &blocks, &files);
+ if (error) {
+ goto err;
+ }
+
+ qk.qk_idtype = q2type;
+ qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
+ qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
+
+ args.qc_op = QUOTACTL_PUT;
+ args.u.put.qc_key = &qk;
+ args.u.put.qc_val = &blocks;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ goto err;
+ }
+
+ qk.qk_idtype = q2type;
+ qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
+ qk.qk_objtype = QUOTA_OBJTYPE_FILES;
+
+ args.qc_op = QUOTACTL_PUT;
+ args.u.put.qc_key = &qk;
+ args.u.put.qc_val = &files;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ goto err;
+ }
+ }
+ prop_object_iterator_release(iter);
+ if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
+ error = ENOMEM;
+ } else {
+ error = 0;
+ }
+ return error;
+err:
+ prop_object_iterator_release(iter);
+ prop_object_release(replies);
+ return error;
+}
+
+static prop_dictionary_t
+vfs_quotactl_getall_makereply(const struct quotakey *key)
+{
+ prop_dictionary_t dict;
+ id_t id;
+ int defaultq;
+
+ dict = prop_dictionary_create();
+ if (dict == NULL)
+ return NULL;
+
+ id = key->qk_id;
+ if (id == QUOTA_DEFAULTID) {
+ id = 0;
+ defaultq = 1;
+ } else {
+ defaultq = 0;
+ }
+
+ if (defaultq) {
+ if (!prop_dictionary_set_cstring_nocopy(dict, "id",
+ "default")) {
+ goto err;
+ }
+ } else {
+ if (!prop_dictionary_set_uint32(dict, "id", id)) {
+ goto err;
+ }
+ }
+
+ return dict;
+
+err:
+ prop_object_release(dict);
+ return NULL;
+}
+
+static int
+vfs_quotactl_getall_addreply(prop_dictionary_t thisreply,
+ const struct quotakey *key, const struct quotaval *val)
+{
+#define INITQVNAMES_ALL { \
+ QUOTADICT_LIMIT_HARD, \
+ QUOTADICT_LIMIT_SOFT, \
+ QUOTADICT_LIMIT_USAGE, \
+ QUOTADICT_LIMIT_ETIME, \
+ QUOTADICT_LIMIT_GTIME \
+ }
+#define N_QV 5
+
+ const char *val_names[] = INITQVNAMES_ALL;
+ uint64_t vals[N_QV];
+ prop_dictionary_t dict2;
+ const char *objtypename;
+
+ switch (key->qk_objtype) {
+ case QUOTA_OBJTYPE_BLOCKS:
+ objtypename = QUOTADICT_LTYPE_BLOCK;
+ break;
+ case QUOTA_OBJTYPE_FILES:
+ objtypename = QUOTADICT_LTYPE_FILE;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ vals[0] = val->qv_hardlimit;
+ vals[1] = val->qv_softlimit;
+ vals[2] = val->qv_usage;
+ vals[3] = val->qv_expiretime;
+ vals[4] = val->qv_grace;
+ dict2 = limits64toprop(vals, val_names, N_QV);
+ if (dict2 == NULL)
+ return ENOMEM;
+
+ if (!prop_dictionary_set_and_rel(thisreply, objtypename, dict2))
+ return ENOMEM;
+
+ return 0;
+}
+
+static int
+vfs_quotactl_getall(struct mount *mp,
+ prop_dictionary_t cmddict, int q2type,
+ prop_array_t datas)
+{
+ struct quotakcursor cursor;
+ struct quotakey *keys;
+ struct quotaval *vals;
+ unsigned loopmax = 8;
+ unsigned loopnum;
+ int skipidtype;
+ struct vfs_quotactl_args args;
+ prop_array_t replies;
+ int atend, atzero;
+ struct quotakey *key;
+ struct quotaval *val;
+ id_t lastid;
+ prop_dictionary_t thisreply;
+ unsigned i;
+ int error, error2;
+
+ KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
+
+ args.qc_op = QUOTACTL_CURSOROPEN;
+ args.u.cursoropen.qc_cursor = &cursor;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ return error;
+ }
+
+ keys = kmem_alloc(loopmax * sizeof(keys[0]), KM_SLEEP);
+ vals = kmem_alloc(loopmax * sizeof(vals[0]), KM_SLEEP);
+
+ skipidtype = (q2type == QUOTA_IDTYPE_USER ?
+ QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER);
+ args.qc_op = QUOTACTL_CURSORSKIPIDTYPE;
+ args.u.cursorskipidtype.qc_cursor = &cursor;
+ args.u.cursorskipidtype.qc_idtype = skipidtype;
+ error = VFS_QUOTACTL(mp, &args);
+ /* ignore if it fails */
+ (void)error;
+
+ replies = prop_array_create();
+ if (replies == NULL) {
+ error = ENOMEM;
+ goto err;
+ }
+
+ thisreply = NULL;
+ lastid = 0; /* value not actually referenced */
+ atzero = 0;
+
+ while (1) {
+ args.qc_op = QUOTACTL_CURSORATEND;
+ args.u.cursoratend.qc_cursor = &cursor;
+ args.u.cursoratend.qc_ret = &atend;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ goto err;
+ }
+ if (atend) {
+ break;
+ }
+
+ args.qc_op = QUOTACTL_CURSORGET;
+ args.u.cursorget.qc_cursor = &cursor;
+ args.u.cursorget.qc_keys = keys;
+ args.u.cursorget.qc_vals = vals;
+ args.u.cursorget.qc_maxnum = loopmax;
+ args.u.cursorget.qc_ret = &loopnum;
+
+ error = VFS_QUOTACTL(mp, &args);
+ if (error == EDEADLK) {
+ /*
+ * transaction abort, start over
+ */
+
+ args.qc_op = QUOTACTL_CURSORREWIND;
+ args.u.cursorrewind.qc_cursor = &cursor;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ goto err;
+ }
+
+ args.qc_op = QUOTACTL_CURSORSKIPIDTYPE;
+ args.u.cursorskipidtype.qc_cursor = &cursor;
+ args.u.cursorskipidtype.qc_idtype = skipidtype;
+ error = VFS_QUOTACTL(mp, &args);
+ /* ignore if it fails */
+ (void)error;
+
+ prop_object_release(replies);
+ replies = prop_array_create();
+ if (replies == NULL) {
+ error = ENOMEM;
+ goto err;
+ }
+
+ thisreply = NULL;
+ lastid = 0;
+ atzero = 0;
+
+ continue;
+ }
+ if (error) {
+ goto err;
+ }
+
+ if (loopnum == 0) {
+ /*
+ * This is not supposed to happen. However,
+ * allow a return of zero items once as long
+ * as something happens (including an atend
+ * indication) on the next pass. If it happens
+ * twice, warn and assume end of iteration.
+ */
+ if (atzero) {
+ printf("vfs_quotactl: zero items returned\n");
+ break;
+ }
+ atzero = 1;
+ } else {
+ atzero = 0;
+ }
+
+ for (i = 0; i < loopnum; i++) {
+ key = &keys[i];
+ val = &vals[i];
+
+ if (key->qk_idtype != q2type) {
+ /* don't want this result */
+ continue;
+ }
+
+ if (thisreply == NULL || key->qk_id != lastid) {
+ lastid = key->qk_id;
+ thisreply = vfs_quotactl_getall_makereply(key);
+ if (thisreply == NULL) {
+ error = ENOMEM;
+ goto err;
+ }
+ /*
+ * Note: while we release our reference to
+ * thisreply here, we can (and do) continue to
+ * use the pointer in the loop because the
+ * copy attached to the replies array is not
+ * going away.
+ */
+ if (!prop_array_add_and_rel(replies,
+ thisreply)) {
+ error = ENOMEM;
+ goto err;
+ }
+ }
+
+ error = vfs_quotactl_getall_addreply(thisreply,
+ key, val);
+ if (error) {
+ goto err;
+ }
+ }
+ }
+
+ if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
+ replies = NULL;
+ error = ENOMEM;
+ goto err;
+ }
+ replies = NULL;
+ error = 0;
+
+ err:
+ kmem_free(keys, loopmax * sizeof(keys[0]));
+ kmem_free(vals, loopmax * sizeof(vals[0]));
+
+ if (replies != NULL) {
+ prop_object_release(replies);
+ }
+
+ args.qc_op = QUOTACTL_CURSORCLOSE;
+ args.u.cursorclose.qc_cursor = &cursor;
+ error2 = VFS_QUOTACTL(mp, &args);
+
+ if (error) {
+ return error;
+ }
+ error = error2;
+ return error;
+}
+
+static int
+vfs_quotactl_clear(struct mount *mp,
+ prop_dictionary_t cmddict, int q2type,
+ prop_array_t datas)
+{
+ prop_array_t replies;
+ prop_object_iterator_t iter;
+ prop_dictionary_t data;
+ uint32_t id;
+ int defaultq;
+ const char *idstr;
+ struct quotakey qk;
+ struct vfs_quotactl_args args;
+ int error;
+
+ KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
+ KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
+
+ replies = prop_array_create();
+ if (replies == NULL)
+ return ENOMEM;
+
+ iter = prop_array_iterator(datas);
+ if (iter == NULL) {
+ prop_object_release(replies);
+ return ENOMEM;
+ }
+
+ while ((data = prop_object_iterator_next(iter)) != NULL) {
+ if (!prop_dictionary_get_uint32(data, "id", &id)) {
+ if (!prop_dictionary_get_cstring_nocopy(data, "id",
+ &idstr))
+ continue;
+ if (strcmp(idstr, "default"))
+ continue;
+ id = 0;
+ defaultq = 1;
+ } else {
+ defaultq = 0;
+ }
+
+ qk.qk_idtype = q2type;
+ qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
+ qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
+
+ args.qc_op = QUOTACTL_DELETE;
+ args.u.delete.qc_key = &qk;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ goto err;
+ }
+
+ qk.qk_idtype = q2type;
+ qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
+ qk.qk_objtype = QUOTA_OBJTYPE_FILES;
+
+ args.qc_op = QUOTACTL_DELETE;
+ args.u.delete.qc_key = &qk;
+ error = VFS_QUOTACTL(mp, &args);
+ if (error) {
+ goto err;
+ }
+ }
+
+ prop_object_iterator_release(iter);
+ if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
+ error = ENOMEM;
+ } else {
+ error = 0;
+ }
+ return error;
+err:
+ prop_object_iterator_release(iter);
+ prop_object_release(replies);
+ return error;
+}
+
+static int
+vfs_quotactl_cmd(struct mount *mp, prop_dictionary_t cmddict)
+{
+ int error;
+ const char *cmd, *type;
+ prop_array_t datas;
+ int q2type;
+
+ if (!prop_dictionary_get_cstring_nocopy(cmddict, "command", &cmd))
+ return EINVAL;
+ if (!prop_dictionary_get_cstring_nocopy(cmddict, "type", &type))
+ return EINVAL;
+
+ if (!strcmp(type, QUOTADICT_CLASS_USER)) {
+ q2type = QUOTA_CLASS_USER;
+ } else if (!strcmp(type, QUOTADICT_CLASS_GROUP)) {
+ q2type = QUOTA_CLASS_GROUP;
+ } else {
+ /* XXX this is a bad errno for this case */
+ return EOPNOTSUPP;
+ }
+
+ datas = prop_dictionary_get(cmddict, "data");
+ if (datas == NULL || prop_object_type(datas) != PROP_TYPE_ARRAY)
+ return EINVAL;
+
+ prop_object_retain(datas);
+ prop_dictionary_remove(cmddict, "data"); /* prepare for return */
+
+ if (strcmp(cmd, "get version") == 0) {
+ error = vfs_quotactl_getversion(mp, cmddict, q2type, datas);
+ } else if (strcmp(cmd, "quotaon") == 0) {
+ error = vfs_quotactl_quotaon(mp, cmddict, q2type, datas);
+ } else if (strcmp(cmd, "quotaoff") == 0) {
+ error = vfs_quotactl_quotaoff(mp, cmddict, q2type, datas);
+ } else if (strcmp(cmd, "get") == 0) {
+ error = vfs_quotactl_get(mp, cmddict, q2type, datas);
+ } else if (strcmp(cmd, "set") == 0) {
+ error = vfs_quotactl_put(mp, cmddict, q2type, datas);
+ } else if (strcmp(cmd, "getall") == 0) {
+ error = vfs_quotactl_getall(mp, cmddict, q2type, datas);
+ } else if (strcmp(cmd, "clear") == 0) {
+ error = vfs_quotactl_clear(mp, cmddict, q2type, datas);
+ } else {
+ /* XXX this a bad errno for this case */
+ error = EOPNOTSUPP;
+ }
+
+ error = (prop_dictionary_set_int8(cmddict, "return",
+ error) ? 0 : ENOMEM);
+ prop_object_release(datas);
+
+ return error;
+}
+
+int
+vfs_quotactl(struct mount *mp, prop_dictionary_t dict)
+{
+ prop_dictionary_t cmddict;
+ prop_array_t commands;
+ prop_object_iterator_t iter;
+ int error;
+
+ error = quota_get_cmds(dict, &commands);
+ if (error) {
+ return error;
+ }
+
+ iter = prop_array_iterator(commands);
+ if (iter == NULL) {
+ return ENOMEM;
+ }
+
+ while ((cmddict = prop_object_iterator_next(iter)) != NULL) {
+ if (prop_object_type(cmddict) != PROP_TYPE_DICTIONARY) {
+ /* XXX shouldn't this be an error? */
+ continue;
+ }
+ error = vfs_quotactl_cmd(mp, cmddict);
+ if (error) {
+ break;
+ }
+ }
+ prop_object_iterator_release(iter);
+ return error;
+}