2012-01-29 11:21:59 +04:00
|
|
|
/* $NetBSD: vfs_quotactl.c,v 1.36 2012/01/29 07:21:59 dholland Exp $ */
|
2012-01-29 10:32:43 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2012-01-29 10:26:54 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2012-01-29 10:32:43 +04:00
|
|
|
* From NetBSD: ufs_quota.c,v 1.70 2011/03/24 17:05:46 bouyer 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.
|
2012-01-29 10:26:54 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2012-01-29 11:21:59 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: vfs_quotactl.c,v 1.36 2012/01/29 07:21:59 dholland Exp $");
|
2012-01-29 10:26:54 +04:00
|
|
|
|
2012-01-29 11:21:59 +04:00
|
|
|
#include <sys/kmem.h>
|
2012-01-29 10:29:04 +04:00
|
|
|
#include <sys/mount.h>
|
2012-01-29 10:39:36 +04:00
|
|
|
#include <sys/quota.h>
|
2012-01-29 10:34:57 +04:00
|
|
|
#include <sys/quotactl.h>
|
2012-01-29 10:32:43 +04:00
|
|
|
#include <quota/quotaprop.h>
|
2012-01-29 10:29:04 +04:00
|
|
|
|
2012-01-29 10:36:06 +04:00
|
|
|
static int
|
|
|
|
vfs_quotactl_getversion(struct mount *mp,
|
|
|
|
prop_dictionary_t cmddict, int q2type,
|
|
|
|
prop_array_t datas)
|
|
|
|
{
|
2012-01-29 10:36:50 +04:00
|
|
|
prop_array_t replies;
|
|
|
|
prop_dictionary_t data;
|
2012-01-29 11:12:40 +04:00
|
|
|
struct quotastat stat;
|
2012-01-29 10:36:50 +04:00
|
|
|
int q2version;
|
2012-01-29 10:36:06 +04:00
|
|
|
struct vfs_quotactl_args args;
|
2012-01-29 10:36:50 +04:00
|
|
|
int error;
|
2012-01-29 10:36:06 +04:00
|
|
|
|
2012-01-29 10:36:50 +04:00
|
|
|
KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
|
|
|
|
KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_STAT;
|
2012-01-29 11:12:40 +04:00
|
|
|
args.u.stat.qc_ret = &stat;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:36:50 +04:00
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2012-01-29 11:12:40 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:36:50 +04:00
|
|
|
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;
|
2012-01-29 10:36:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vfs_quotactl_quotaon(struct mount *mp,
|
|
|
|
prop_dictionary_t cmddict, int q2type,
|
|
|
|
prop_array_t datas)
|
|
|
|
{
|
2012-01-29 11:11:12 +04:00
|
|
|
prop_dictionary_t data;
|
|
|
|
const char *qfile;
|
2012-01-29 10:36:06 +04:00
|
|
|
struct vfs_quotactl_args args;
|
|
|
|
|
2012-01-29 11:11:12 +04:00
|
|
|
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;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_QUOTAON;
|
2012-01-29 11:11:12 +04:00
|
|
|
args.u.quotaon.qc_idtype = q2type;
|
|
|
|
args.u.quotaon.qc_quotafile = qfile;
|
2012-01-29 11:14:38 +04:00
|
|
|
return VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:36:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vfs_quotactl_quotaoff(struct mount *mp,
|
|
|
|
prop_dictionary_t cmddict, int q2type,
|
|
|
|
prop_array_t datas)
|
|
|
|
{
|
|
|
|
struct vfs_quotactl_args args;
|
|
|
|
|
2012-01-29 11:11:55 +04:00
|
|
|
KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
|
|
|
|
KASSERT(prop_object_type(datas) == PROP_TYPE_ARRAY);
|
|
|
|
|
|
|
|
if (prop_array_count(datas) != 0)
|
|
|
|
return EINVAL;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_QUOTAOFF;
|
2012-01-29 11:11:55 +04:00
|
|
|
args.u.quotaoff.qc_idtype = q2type;
|
2012-01-29 11:14:38 +04:00
|
|
|
return VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:36:06 +04:00
|
|
|
}
|
|
|
|
|
2012-01-29 10:39:36 +04:00
|
|
|
static int
|
2012-01-29 10:41:41 +04:00
|
|
|
vfs_quotactl_get_addreply(const struct quotakey *qk,
|
2012-01-29 10:39:36 +04:00
|
|
|
const struct quotaval *blocks,
|
|
|
|
const struct quotaval *files,
|
|
|
|
prop_array_t replies)
|
|
|
|
{
|
|
|
|
prop_dictionary_t dict;
|
2012-01-29 10:41:41 +04:00
|
|
|
id_t id;
|
|
|
|
int defaultq;
|
|
|
|
uint64_t *valuesp[QUOTA_NLIMITS];
|
2012-01-29 10:39:36 +04:00
|
|
|
|
|
|
|
/* XXX illegal casts */
|
|
|
|
valuesp[QUOTA_LIMIT_BLOCK] = (void *)(intptr_t)&blocks->qv_hardlimit;
|
|
|
|
valuesp[QUOTA_LIMIT_FILE] = (void *)(intptr_t)&files->qv_hardlimit;
|
|
|
|
|
2012-01-29 10:41:41 +04:00
|
|
|
if (qk->qk_id == QUOTA_DEFAULTID) {
|
|
|
|
id = 0;
|
|
|
|
defaultq = 1;
|
|
|
|
} else {
|
|
|
|
id = qk->qk_id;
|
|
|
|
defaultq = 0;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:39:36 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:36:06 +04:00
|
|
|
static int
|
|
|
|
vfs_quotactl_get(struct mount *mp,
|
2012-01-29 10:41:41 +04:00
|
|
|
prop_dictionary_t cmddict, int idtype,
|
2012-01-29 10:36:06 +04:00
|
|
|
prop_array_t datas)
|
|
|
|
{
|
2012-01-29 10:37:30 +04:00
|
|
|
prop_object_iterator_t iter;
|
|
|
|
prop_dictionary_t data;
|
2012-01-29 10:39:36 +04:00
|
|
|
prop_array_t replies;
|
2012-01-29 10:37:30 +04:00
|
|
|
uint32_t id;
|
|
|
|
const char *idstr;
|
2012-01-29 10:36:06 +04:00
|
|
|
struct vfs_quotactl_args args;
|
2012-01-29 10:41:41 +04:00
|
|
|
struct quotakey qk;
|
2012-01-29 10:39:36 +04:00
|
|
|
struct quotaval blocks, files;
|
2012-01-29 10:37:30 +04:00
|
|
|
int error;
|
2012-01-29 10:36:06 +04:00
|
|
|
|
2012-01-29 10:37:30 +04:00
|
|
|
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) {
|
2012-01-29 10:41:41 +04:00
|
|
|
qk.qk_idtype = idtype;
|
|
|
|
|
2012-01-29 10:37:30 +04:00
|
|
|
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;
|
|
|
|
}
|
2012-01-29 10:41:41 +04:00
|
|
|
qk.qk_id = QUOTA_DEFAULTID;
|
2012-01-29 10:37:30 +04:00
|
|
|
} else {
|
2012-01-29 10:41:41 +04:00
|
|
|
qk.qk_id = id;
|
2012-01-29 10:37:30 +04:00
|
|
|
}
|
|
|
|
|
2012-01-29 10:41:41 +04:00
|
|
|
qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_GET;
|
2012-01-29 10:41:41 +04:00
|
|
|
args.u.get.qc_key = &qk;
|
2012-01-29 10:40:57 +04:00
|
|
|
args.u.get.qc_ret = &blocks;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:40:57 +04:00
|
|
|
if (error == EPERM) {
|
|
|
|
/* XXX does this make sense? */
|
|
|
|
continue;
|
|
|
|
} else if (error == ENOENT) {
|
|
|
|
/* XXX does *this* make sense? */
|
|
|
|
continue;
|
|
|
|
} else if (error) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:41:41 +04:00
|
|
|
qk.qk_objtype = QUOTA_OBJTYPE_FILES;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_GET;
|
2012-01-29 10:41:41 +04:00
|
|
|
args.u.get.qc_key = &qk;
|
2012-01-29 10:40:57 +04:00
|
|
|
args.u.get.qc_ret = &files;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:37:30 +04:00
|
|
|
if (error == EPERM) {
|
|
|
|
/* XXX does this make sense? */
|
|
|
|
continue;
|
|
|
|
} else if (error == ENOENT) {
|
|
|
|
/* XXX does *this* make sense? */
|
|
|
|
continue;
|
|
|
|
} else if (error) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2012-01-29 10:39:36 +04:00
|
|
|
|
2012-01-29 10:41:41 +04:00
|
|
|
error = vfs_quotactl_get_addreply(&qk, &blocks, &files,
|
2012-01-29 10:39:36 +04:00
|
|
|
replies);
|
2012-01-29 10:37:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2012-01-29 10:36:06 +04:00
|
|
|
}
|
|
|
|
|
2012-01-29 10:45:25 +04:00
|
|
|
static int
|
2012-01-29 10:49:43 +04:00
|
|
|
vfs_quotactl_put_extractinfo(prop_dictionary_t data,
|
2012-01-29 10:45:25 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:36:06 +04:00
|
|
|
static int
|
2012-01-29 10:49:43 +04:00
|
|
|
vfs_quotactl_put(struct mount *mp,
|
2012-01-29 10:36:06 +04:00
|
|
|
prop_dictionary_t cmddict, int q2type,
|
|
|
|
prop_array_t datas)
|
|
|
|
{
|
2012-01-29 10:44:33 +04:00
|
|
|
prop_array_t replies;
|
|
|
|
prop_object_iterator_t iter;
|
|
|
|
prop_dictionary_t data;
|
|
|
|
int defaultq;
|
|
|
|
uint32_t id;
|
|
|
|
const char *idstr;
|
2012-01-29 10:48:50 +04:00
|
|
|
struct quotakey qk;
|
2012-01-29 10:45:25 +04:00
|
|
|
struct quotaval blocks, files;
|
2012-01-29 10:36:06 +04:00
|
|
|
struct vfs_quotactl_args args;
|
2012-01-29 10:44:33 +04:00
|
|
|
int error;
|
2012-01-29 10:36:06 +04:00
|
|
|
|
2012-01-29 10:44:33 +04:00
|
|
|
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) {
|
2012-01-29 10:47:38 +04:00
|
|
|
|
|
|
|
KASSERT(prop_object_type(data) == PROP_TYPE_DICTIONARY);
|
|
|
|
|
2012-01-29 10:44:33 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:49:43 +04:00
|
|
|
error = vfs_quotactl_put_extractinfo(data, &blocks, &files);
|
2012-01-29 10:45:25 +04:00
|
|
|
if (error) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:48:50 +04:00
|
|
|
qk.qk_idtype = q2type;
|
|
|
|
qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
|
|
|
|
qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_PUT;
|
2012-01-29 10:49:43 +04:00
|
|
|
args.u.put.qc_key = &qk;
|
|
|
|
args.u.put.qc_val = &blocks;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:47:38 +04:00
|
|
|
if (error) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:48:50 +04:00
|
|
|
qk.qk_idtype = q2type;
|
|
|
|
qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
|
|
|
|
qk.qk_objtype = QUOTA_OBJTYPE_FILES;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_PUT;
|
2012-01-29 10:49:43 +04:00
|
|
|
args.u.put.qc_key = &qk;
|
|
|
|
args.u.put.qc_val = &files;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:44:33 +04:00
|
|
|
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;
|
2012-01-29 10:36:06 +04:00
|
|
|
}
|
|
|
|
|
2012-01-29 11:02:06 +04:00
|
|
|
static prop_dictionary_t
|
2012-01-29 11:06:37 +04:00
|
|
|
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)
|
2012-01-29 11:02:06 +04:00
|
|
|
{
|
|
|
|
#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;
|
2012-01-29 11:06:37 +04:00
|
|
|
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;
|
2012-01-29 11:02:06 +04:00
|
|
|
}
|
|
|
|
|
2012-01-29 11:06:37 +04:00
|
|
|
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;
|
2012-01-29 11:02:06 +04:00
|
|
|
dict2 = limits64toprop(vals, val_names, N_QV);
|
|
|
|
if (dict2 == NULL)
|
2012-01-29 11:06:37 +04:00
|
|
|
return ENOMEM;
|
2012-01-29 11:02:06 +04:00
|
|
|
|
2012-01-29 11:06:37 +04:00
|
|
|
if (!prop_dictionary_set_and_rel(thisreply, objtypename, dict2))
|
|
|
|
return ENOMEM;
|
2012-01-29 11:02:06 +04:00
|
|
|
|
2012-01-29 11:06:37 +04:00
|
|
|
return 0;
|
2012-01-29 11:02:06 +04:00
|
|
|
}
|
|
|
|
|
2012-01-29 10:36:06 +04:00
|
|
|
static int
|
|
|
|
vfs_quotactl_getall(struct mount *mp,
|
|
|
|
prop_dictionary_t cmddict, int q2type,
|
|
|
|
prop_array_t datas)
|
|
|
|
{
|
2012-01-29 10:57:15 +04:00
|
|
|
struct quotakcursor cursor;
|
2012-01-29 11:07:22 +04:00
|
|
|
struct quotakey *keys;
|
|
|
|
struct quotaval *vals;
|
|
|
|
unsigned loopmax = 8;
|
|
|
|
unsigned loopnum;
|
2012-01-29 11:10:24 +04:00
|
|
|
int skipidtype;
|
2012-01-29 10:36:06 +04:00
|
|
|
struct vfs_quotactl_args args;
|
2012-01-29 11:02:06 +04:00
|
|
|
prop_array_t replies;
|
2012-01-29 11:10:24 +04:00
|
|
|
int atend, atzero;
|
2012-01-29 11:06:37 +04:00
|
|
|
struct quotakey *key;
|
|
|
|
struct quotaval *val;
|
|
|
|
id_t lastid;
|
2012-01-29 11:07:22 +04:00
|
|
|
prop_dictionary_t thisreply;
|
2012-01-29 11:02:06 +04:00
|
|
|
unsigned i;
|
2012-01-29 10:57:15 +04:00
|
|
|
int error, error2;
|
2012-01-29 11:02:06 +04:00
|
|
|
|
|
|
|
KASSERT(prop_object_type(cmddict) == PROP_TYPE_DICTIONARY);
|
2012-01-29 10:57:15 +04:00
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_CURSOROPEN;
|
2012-01-29 10:57:15 +04:00
|
|
|
args.u.cursoropen.qc_cursor = &cursor;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:57:15 +04:00
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2012-01-29 10:36:06 +04:00
|
|
|
|
2012-01-29 11:21:59 +04:00
|
|
|
keys = kmem_alloc(loopmax * sizeof(keys[0]), KM_SLEEP);
|
|
|
|
vals = kmem_alloc(loopmax * sizeof(vals[0]), KM_SLEEP);
|
2012-01-29 11:02:06 +04:00
|
|
|
|
2012-01-29 11:10:24 +04:00
|
|
|
skipidtype = (q2type == QUOTA_IDTYPE_USER ?
|
|
|
|
QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER);
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_CURSORSKIPIDTYPE;
|
2012-01-29 11:10:24 +04:00
|
|
|
args.u.cursorskipidtype.qc_cursor = &cursor;
|
|
|
|
args.u.cursorskipidtype.qc_idtype = skipidtype;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 11:10:24 +04:00
|
|
|
/* ignore if it fails */
|
|
|
|
(void)error;
|
|
|
|
|
2012-01-29 11:02:06 +04:00
|
|
|
replies = prop_array_create();
|
|
|
|
if (replies == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-01-29 11:06:37 +04:00
|
|
|
thisreply = NULL;
|
|
|
|
lastid = 0; /* value not actually referenced */
|
2012-01-29 11:10:24 +04:00
|
|
|
atzero = 0;
|
2012-01-29 11:07:22 +04:00
|
|
|
|
|
|
|
while (1) {
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_CURSORATEND;
|
2012-01-29 11:10:24 +04:00
|
|
|
args.u.cursoratend.qc_cursor = &cursor;
|
|
|
|
args.u.cursoratend.qc_ret = &atend;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 11:10:24 +04:00
|
|
|
if (error) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (atend) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_CURSORGET;
|
2012-01-29 11:09:52 +04:00
|
|
|
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;
|
|
|
|
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 11:10:24 +04:00
|
|
|
if (error == EDEADLK) {
|
|
|
|
/*
|
|
|
|
* transaction abort, start over
|
|
|
|
*/
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_CURSORREWIND;
|
2012-01-29 11:10:24 +04:00
|
|
|
args.u.cursorrewind.qc_cursor = &cursor;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 11:10:24 +04:00
|
|
|
if (error) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_CURSORSKIPIDTYPE;
|
2012-01-29 11:10:24 +04:00
|
|
|
args.u.cursorskipidtype.qc_cursor = &cursor;
|
|
|
|
args.u.cursorskipidtype.qc_idtype = skipidtype;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 11:10:24 +04:00
|
|
|
/* 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;
|
|
|
|
}
|
2012-01-29 11:06:37 +04:00
|
|
|
if (error) {
|
2012-01-29 11:02:06 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-01-29 11:07:22 +04:00
|
|
|
if (loopnum == 0) {
|
2012-01-29 11:10:24 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
2012-01-29 11:07:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < loopnum; i++) {
|
|
|
|
key = &keys[i];
|
|
|
|
val = &vals[i];
|
|
|
|
|
2012-01-29 11:08:58 +04:00
|
|
|
if (key->qk_idtype != q2type) {
|
|
|
|
/* don't want this result */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-01-29 11:07:22 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-29 11:02:06 +04:00
|
|
|
|
|
|
|
if (!prop_dictionary_set_and_rel(cmddict, "data", replies)) {
|
2012-01-29 11:10:24 +04:00
|
|
|
replies = NULL;
|
2012-01-29 11:02:06 +04:00
|
|
|
error = ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
2012-01-29 11:10:24 +04:00
|
|
|
replies = NULL;
|
2012-01-29 11:02:06 +04:00
|
|
|
error = 0;
|
2012-01-29 11:10:24 +04:00
|
|
|
|
2012-01-29 11:02:06 +04:00
|
|
|
err:
|
2012-01-29 11:21:59 +04:00
|
|
|
kmem_free(keys, loopmax * sizeof(keys[0]));
|
|
|
|
kmem_free(vals, loopmax * sizeof(vals[0]));
|
2012-01-29 10:57:15 +04:00
|
|
|
|
2012-01-29 11:10:24 +04:00
|
|
|
if (replies != NULL) {
|
|
|
|
prop_object_release(replies);
|
|
|
|
}
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_CURSORCLOSE;
|
2012-01-29 10:57:15 +04:00
|
|
|
args.u.cursorclose.qc_cursor = &cursor;
|
2012-01-29 11:14:38 +04:00
|
|
|
error2 = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:57:15 +04:00
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
error = error2;
|
|
|
|
return error;
|
2012-01-29 10:36:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vfs_quotactl_clear(struct mount *mp,
|
|
|
|
prop_dictionary_t cmddict, int q2type,
|
|
|
|
prop_array_t datas)
|
|
|
|
{
|
2012-01-29 10:51:42 +04:00
|
|
|
prop_array_t replies;
|
|
|
|
prop_object_iterator_t iter;
|
|
|
|
prop_dictionary_t data;
|
|
|
|
uint32_t id;
|
|
|
|
int defaultq;
|
|
|
|
const char *idstr;
|
2012-01-29 10:55:44 +04:00
|
|
|
struct quotakey qk;
|
2012-01-29 10:36:06 +04:00
|
|
|
struct vfs_quotactl_args args;
|
2012-01-29 10:51:42 +04:00
|
|
|
int error;
|
2012-01-29 10:36:06 +04:00
|
|
|
|
2012-01-29 10:51:42 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:55:44 +04:00
|
|
|
qk.qk_idtype = q2type;
|
|
|
|
qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
|
|
|
|
qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_DELETE;
|
2012-01-29 10:55:44 +04:00
|
|
|
args.u.delete.qc_key = &qk;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:53:35 +04:00
|
|
|
if (error) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:55:44 +04:00
|
|
|
qk.qk_idtype = q2type;
|
|
|
|
qk.qk_id = defaultq ? QUOTA_DEFAULTID : id;
|
|
|
|
qk.qk_objtype = QUOTA_OBJTYPE_FILES;
|
|
|
|
|
2012-01-29 11:13:42 +04:00
|
|
|
args.qc_op = QUOTACTL_DELETE;
|
2012-01-29 10:55:44 +04:00
|
|
|
args.u.delete.qc_key = &qk;
|
2012-01-29 11:14:38 +04:00
|
|
|
error = VFS_QUOTACTL(mp, &args);
|
2012-01-29 10:51:42 +04:00
|
|
|
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;
|
2012-01-29 10:36:06 +04:00
|
|
|
}
|
|
|
|
|
2012-01-29 10:34:57 +04:00
|
|
|
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) {
|
2012-01-29 10:36:06 +04:00
|
|
|
error = vfs_quotactl_getversion(mp, cmddict, q2type, datas);
|
2012-01-29 10:34:57 +04:00
|
|
|
} else if (strcmp(cmd, "quotaon") == 0) {
|
2012-01-29 10:36:06 +04:00
|
|
|
error = vfs_quotactl_quotaon(mp, cmddict, q2type, datas);
|
2012-01-29 10:34:57 +04:00
|
|
|
} else if (strcmp(cmd, "quotaoff") == 0) {
|
2012-01-29 10:36:06 +04:00
|
|
|
error = vfs_quotactl_quotaoff(mp, cmddict, q2type, datas);
|
2012-01-29 10:34:57 +04:00
|
|
|
} else if (strcmp(cmd, "get") == 0) {
|
2012-01-29 10:36:06 +04:00
|
|
|
error = vfs_quotactl_get(mp, cmddict, q2type, datas);
|
2012-01-29 10:34:57 +04:00
|
|
|
} else if (strcmp(cmd, "set") == 0) {
|
2012-01-29 10:49:43 +04:00
|
|
|
error = vfs_quotactl_put(mp, cmddict, q2type, datas);
|
2012-01-29 10:34:57 +04:00
|
|
|
} else if (strcmp(cmd, "getall") == 0) {
|
2012-01-29 10:36:06 +04:00
|
|
|
error = vfs_quotactl_getall(mp, cmddict, q2type, datas);
|
2012-01-29 10:34:57 +04:00
|
|
|
} else if (strcmp(cmd, "clear") == 0) {
|
2012-01-29 10:36:06 +04:00
|
|
|
error = vfs_quotactl_clear(mp, cmddict, q2type, datas);
|
2012-01-29 10:34:57 +04:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2012-01-29 10:29:04 +04:00
|
|
|
int
|
|
|
|
vfs_quotactl(struct mount *mp, prop_dictionary_t dict)
|
|
|
|
{
|
2012-01-29 10:32:43 +04:00
|
|
|
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;
|
|
|
|
}
|
2012-01-29 10:34:57 +04:00
|
|
|
error = vfs_quotactl_cmd(mp, cmddict);
|
2012-01-29 10:32:43 +04:00
|
|
|
if (error) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prop_object_iterator_release(iter);
|
|
|
|
return error;
|
2012-01-29 10:29:04 +04:00
|
|
|
}
|