NetBSD/lib/libquota/quota_proplib.c
dholland 92d1ae00da Move some more stuff technically specific to the proplib kernel
interface into the source file for using the proplib kernel interface.
2012-01-25 01:22:56 +00:00

1086 lines
25 KiB
C

/* $NetBSD: quota_proplib.c,v 1.7 2012/01/25 01:22:56 dholland Exp $ */
/*-
* Copyright (c) 2011 Manuel Bouyer
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: quota_proplib.c,v 1.7 2012/01/25 01:22:56 dholland Exp $");
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <err.h>
#include <quota.h>
#include "quotapvt.h"
#include <quota/quotaprop.h>
#include <quota/quota.h>
struct proplib_quotacursor {
prop_array_t users;
prop_array_t groups;
unsigned numusers;
unsigned numgroups;
unsigned haveusers;
unsigned havegroups;
unsigned didusers;
unsigned pos;
unsigned didblocks;
};
int
__quota_proplib_getversion(struct quotahandle *qh, int8_t *version_ret)
{
const char *idtype;
prop_dictionary_t dict, data, cmd;
prop_array_t cmds, blank, datas;
const char *cmdstr;
struct plistref pref;
int8_t error8;
/* XXX does this matter? */
idtype = ufs_quota_class_names[QUOTA_CLASS_USER];
/*
* XXX this should not crash out on error. But this is what
* the code this came from did... probably because it can just
* leak memory instead of needing the proper cleanup code.
*/
dict = quota_prop_create();
if (dict == NULL) {
err(1, "quota_getimplname: quota_prop_create");
}
cmds = prop_array_create();
if (cmds == NULL) {
err(1, "quota_getimplname: prop_array_create");
}
blank = prop_array_create();
if (blank == NULL) {
err(1, "quota_getimplname: prop_array_create");
}
if (!quota_prop_add_command(cmds, "get version", idtype, blank)) {
err(1, "quota_getimplname: quota_prop_add_command");
}
if (!prop_dictionary_set(dict, "commands", cmds)) {
err(1, "quota_getimplname: prop_dictionary_set");
}
if (prop_dictionary_send_syscall(dict, &pref) != 0) {
err(1, "quota_getimplname: prop_dictionary_send_syscall");
}
/* XXX don't we need prop_object_release(cmds) here too? */
prop_object_release(dict);
if (quotactl(qh->qh_mountpoint, &pref) != 0)
err(1, "quota_getimplname: quotactl");
if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
err(1, "quota_getimplname: prop_dictionary_recv_syscall");
}
if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
err(1, "quota_getimplname: bad response (%s)",
"quota_get_cmds");
}
cmd = prop_array_get(cmds, 0);
if (cmd == NULL) {
err(1, "quota_getimplname: bad response (%s)",
"prop_array_get");
}
if (!prop_dictionary_get_cstring_nocopy(cmd, "command", &cmdstr)) {
err(1, "quota_getimplname: bad response (%s)",
"prop_dictionary_get_cstring_nocopy");
}
if (strcmp("get version", cmdstr) != 0) {
errx(1, "quota_getimplname: bad response (%s)",
"command name did not match");
}
if (!prop_dictionary_get_int8(cmd, "return", &error8)) {
err(1, "quota_getimplname: bad response (%s)",
"prop_dictionary_get_int8");
}
if (error8) {
/* this means the RPC action failed */
prop_object_release(dict);
errno = error8;
return -1;
}
datas = prop_dictionary_get(cmd, "data");
if (datas == NULL) {
err(1, "quota_getimplname: bad response (%s)",
"prop_dictionary_get");
}
data = prop_array_get(datas, 0);
if (data == NULL) {
err(1, "quota_getimplname: bad response (%s)",
"prop_array_get");
}
if (!prop_dictionary_get_int8(data, "version", version_ret)) {
err(1, "quota_getimplname: bad response (%s)",
"prop_array_get_int8");
}
return 0;
}
const char *
__quota_proplib_getimplname(struct quotahandle *qh)
{
int8_t version;
if (__quota_proplib_getversion(qh, &version) < 0) {
return NULL;
}
switch (version) {
case 1: return "ffs quota1";
case 2: return "ffs quota2";
default: break;
}
return "unknown";
}
unsigned
__quota_proplib_getnumidtypes(void)
{
return QUOTA_NCLASS;
}
const char *
__quota_proplib_idtype_getname(int idtype)
{
if (idtype < 0 || idtype >= QUOTA_NCLASS) {
return NULL;
}
return ufs_quota_class_names[idtype];
}
unsigned
__quota_proplib_getnumobjtypes(void)
{
return QUOTA_NLIMITS;
}
const char *
__quota_proplib_objtype_getname(int objtype)
{
if (objtype < 0 || objtype >= QUOTA_NLIMITS) {
return NULL;
}
return ufs_quota_limit_names[objtype];
}
int
__quota_proplib_objtype_isbytes(int objtype)
{
switch (objtype) {
case QUOTA_LIMIT_BLOCK: return 1;
case QUOTA_LIMIT_FILE: return 0;
default: break;
}
return 0;
}
static int
__quota_proplib_extractval(int objtype, prop_dictionary_t data,
struct quotaval *qv)
{
uint64_t vals[UFS_QUOTA_NENTRIES];
uint64_t *valptrs[1];
int limitcode;
/*
* 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. I refuse to countenance
* that. Also, most of that code extracts both block and file
* limits at once (last arguments are ufs_quota_limit_names
* and UFS_QUOTA_NLIMITS) but I only need one.
*/
switch (objtype) {
case QUOTA_OBJTYPE_BLOCKS:
limitcode = QUOTA_LIMIT_BLOCK;
break;
case QUOTA_OBJTYPE_FILES:
limitcode = QUOTA_LIMIT_FILE;
break;
default:
errno = EINVAL;
return -1;
}
valptrs[0] = vals;
errno = proptoquota64(data, valptrs,
ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
&ufs_quota_limit_names[limitcode], 1);
if (errno) {
return -1;
}
/*
* there are no symbolic constants for these indexes! XXX
*/
qv->qv_hardlimit = vals[0];
qv->qv_softlimit = vals[1];
qv->qv_usage = vals[2];
qv->qv_expiretime = vals[3];
qv->qv_grace = vals[4];
return 0;
}
int
__quota_proplib_get(struct quotahandle *qh, const struct quotakey *qk,
struct quotaval *qv)
{
prop_dictionary_t dict, data, cmd;
prop_array_t cmds, datas;
struct plistref pref;
int8_t error8;
const char *idstr;
const char *cmdstr;
int serrno;
switch (qk->qk_idtype) {
case QUOTA_IDTYPE_USER:
idstr = QUOTADICT_CLASS_USER;
break;
case QUOTA_IDTYPE_GROUP:
idstr = QUOTADICT_CLASS_GROUP;
break;
default:
errno = EINVAL;
return -1;
}
/*
* Cons up the RPC packet.
*/
data = prop_dictionary_create();
if (data == NULL) {
errno = ENOMEM;
return -1;
}
if (qk->qk_id == QUOTA_DEFAULTID) {
if (!prop_dictionary_set_cstring(data, "id", "default")) {
prop_object_release(data);
errno = ENOMEM;
return -1;
}
} else {
if (!prop_dictionary_set_uint32(data, "id", qk->qk_id)) {
prop_object_release(data);
errno = ENOMEM;
return -1;
}
}
datas = prop_array_create();
if (datas == NULL) {
prop_object_release(data);
errno = ENOMEM;
return -1;
}
if (!prop_array_add_and_rel(datas, data)) {
prop_object_release(datas);
/* DATA is consumed if this fails! */
errno = ENOMEM;
return -1;
}
cmds = prop_array_create();
if (cmds == NULL) {
prop_object_release(datas);
errno = ENOMEM;
return -1;
}
if (!quota_prop_add_command(cmds, "get", idstr, datas)) {
prop_object_release(cmds);
/* AFAICT, CMDS is consumed if this fails, too. */
errno = ENOMEM;
return -1;
}
dict = quota_prop_create();
if (dict == NULL) {
prop_object_release(cmds);
errno = ENOMEM;
return -1;
}
if (!prop_dictionary_set(dict, "commands", cmds)) {
prop_object_release(dict);
/* here CMDS is *not* released on failure. yay consistency! */
prop_object_release(cmds);
errno = ENOMEM;
return -1;
}
/* as far as I can tell this is required here - dholland */
prop_object_release(cmds);
/*
* Convert it to an XML turd for transfer.
*/
if (prop_dictionary_send_syscall(dict, &pref) != 0) {
serrno = errno;
prop_object_release(dict);
errno = serrno;
return -1;
}
prop_object_release(dict);
/*
* Send it off.
*
* Note:
*
* prop_dictionary_send_syscall allocates memory in PREF,
* which we ought to free if quotactl fails, but there's no
* way (or no documented way) to do this without breaking the
* abstraction.
*
* Furthermore, quotactl replaces the send buffer in PREF
* with a receive buffer. (AFAIK at least...) This overwrites
* the send buffer and makes it impossible to free it. The
* receive buffer is consumed by prop_dictionary_recv_syscall
* with munmap(); however, I'm not sure what happens if the
* prop_dictionary_recv_syscall operation fails.
*
* So it at least looks as if the send bundle is leaked on
* every quotactl call.
*
* XXX.
*
* - dholland 20111125
*/
if (quotactl(qh->qh_mountpoint, &pref) != 0) {
/* XXX free PREF buffer here */
return -1;
}
/* XXX free now-overwritten PREF buffer here */
/*
* Convert the XML response turd.
*/
if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
/* XXX do we have to free the buffer in PREF here? */
return -1;
}
/*
* Now unpack the response.
*/
/* more consistency, returning an errno instead of setting it */
errno = quota_get_cmds(dict, &cmds);
if (errno != 0) {
prop_object_release(dict);
return -1;
}
cmd = prop_array_get(cmds, 0);
if (cmd == NULL) {
prop_object_release(dict);
errno = EINVAL;
return -1;
}
if (!prop_dictionary_get_cstring_nocopy(cmd, "command", &cmdstr)) {
/* malformed response from the kernel */
prop_object_release(dict);
errno = EINVAL;
return -1;
}
if (strcmp("get", cmdstr) != 0) {
/* malformed response from the kernel */
prop_object_release(dict);
errno = EINVAL;
return -1;
}
if (!prop_dictionary_get_int8(cmd, "return", &error8)) {
/* malformed response from the kernel */
prop_object_release(dict);
errno = EINVAL;
return -1;
}
if (error8 == ENODEV) {
/* XXX this currently means quotas are not enabled */
/* XXX but there's currently no way to fail in quota_open */
/* XXX in that case */
quotaval_clear(qv);
prop_object_release(dict);
return 0;
}
if (error8) {
/* this means the RPC action failed */
prop_object_release(dict);
errno = error8;
return -1;
}
datas = prop_dictionary_get(cmd, "data");
if (datas == NULL) {
/* malformed response from the kernel */
prop_object_release(dict);
errno = EINVAL;
return -1;
}
if (prop_array_count(datas) == 0) {
/* No quotas for this id */
prop_object_release(dict);
errno = ENOENT;
return -1;
}
data = prop_array_get(datas, 0);
if (data == NULL) {
/* malformed response from the kernel */
prop_object_release(dict);
errno = EINVAL;
return -1;
}
if (__quota_proplib_extractval(qk->qk_objtype, data, qv)) {
serrno = errno;
prop_object_release(dict);
errno = serrno;
return -1;
}
prop_object_release(dict);
return 0;
}
int
__quota_proplib_put(struct quotahandle *qh, const struct quotakey *qk,
const struct quotaval *qv)
{
prop_dictionary_t dict, data, cmd;
prop_array_t cmds, datas;
struct plistref pref;
int8_t error8;
uint64_t *valuesp[QUOTA_NLIMITS];
const char *idtype;
unsigned limitcode, otherlimitcode;
unsigned otherobjtype;
struct quotakey qk2;
struct quotaval qv2;
switch (qk->qk_idtype) {
case QUOTA_IDTYPE_USER:
idtype = ufs_quota_class_names[QUOTA_CLASS_USER];
break;
case QUOTA_IDTYPE_GROUP:
idtype = ufs_quota_class_names[QUOTA_CLASS_GROUP];
break;
default:
errno = EINVAL;
return -1;
}
switch (qk->qk_objtype) {
case QUOTA_OBJTYPE_BLOCKS:
limitcode = QUOTA_LIMIT_BLOCK;
otherlimitcode = QUOTA_LIMIT_FILE;
otherobjtype = QUOTA_OBJTYPE_FILES;
break;
case QUOTA_OBJTYPE_FILES:
limitcode = QUOTA_LIMIT_FILE;
otherlimitcode = QUOTA_LIMIT_BLOCK;
otherobjtype = QUOTA_OBJTYPE_BLOCKS;
break;
default:
errno = EINVAL;
return -1;
}
/* XXX in addition to being invalid/unsafe this also discards const */
valuesp[limitcode] = __UNCONST(&qv->qv_hardlimit);
/*
* You cannot set just the block info or just the file info.
* You have to set both together, or EINVAL comes back. So we
* have to fetch the current values for the other object type,
* and stuff both into the RPC packet. Blah. XXX.
*/
qk2.qk_idtype = qk->qk_idtype;
qk2.qk_id = qk->qk_id;
qk2.qk_objtype = otherobjtype;
if (__quota_proplib_get(qh, &qk2, &qv2)) {
if (errno == ENOENT) {
/* Nothing there yet, use a blank value */
quotaval_clear(&qv2);
} else {
return -1;
}
}
valuesp[otherlimitcode] = &qv2.qv_hardlimit;
data = quota64toprop(qk->qk_id, qk->qk_id == QUOTA_DEFAULTID ? 1 : 0,
valuesp, ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
ufs_quota_limit_names, QUOTA_NLIMITS);
if (data == NULL)
err(1, "quota64toprop(id)");
dict = quota_prop_create();
cmds = prop_array_create();
datas = prop_array_create();
if (dict == NULL || cmds == NULL || datas == NULL) {
errx(1, "can't allocate proplist");
}
if (!prop_array_add_and_rel(datas, data))
err(1, "prop_array_add(data)");
if (!quota_prop_add_command(cmds, "set", idtype, datas))
err(1, "prop_add_command");
if (!prop_dictionary_set(dict, "commands", cmds))
err(1, "prop_dictionary_set(command)");
#if 0
if (Dflag)
printf("message to kernel:\n%s\n",
prop_dictionary_externalize(dict));
#endif
if (prop_dictionary_send_syscall(dict, &pref) != 0)
err(1, "prop_dictionary_send_syscall");
prop_object_release(dict);
if (quotactl(qh->qh_mountpoint, &pref) != 0)
err(1, "quotactl");
if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
err(1, "prop_dictionary_recv_syscall");
}
#if 0
if (Dflag)
printf("reply from kernel:\n%s\n",
prop_dictionary_externalize(dict));
#endif
if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
err(1, "quota_get_cmds");
}
/* only one command, no need to iter */
cmd = prop_array_get(cmds, 0);
if (cmd == NULL)
err(1, "prop_array_get(cmd)");
if (!prop_dictionary_get_int8(cmd, "return", &error8))
err(1, "prop_get(return)");
if (error8) {
prop_object_release(dict);
errno = error8;
return -1;
}
prop_object_release(dict);
return 0;
}
int
__quota_proplib_delete(struct quotahandle *qh, const struct quotakey *qk)
{
prop_array_t cmds, datas;
prop_dictionary_t protodict, dict, data, cmd;
struct plistref pref;
int8_t error8;
bool ret;
const char *idtype;
/*
* XXX for now we always clear quotas for all objtypes no
* matter what's passed in. This is ok (sort of) for now
* because the only caller is edquota, which only calls delete
* for both blocks and files in immediate succession. But it's
* wrong in the long run. I'm not fixing it at the moment
* because I expect all this code to be deleted in the near
* future.
*/
(void)qk->qk_objtype;
switch (qk->qk_idtype) {
case QUOTA_IDTYPE_USER:
idtype = ufs_quota_class_names[QUOTA_CLASS_USER];
break;
case QUOTA_IDTYPE_GROUP:
idtype = ufs_quota_class_names[QUOTA_CLASS_GROUP];
break;
default:
errno = EINVAL;
return -1;
}
/* build a generic command */
protodict = quota_prop_create();
cmds = prop_array_create();
datas = prop_array_create();
if (protodict == NULL || cmds == NULL || datas == NULL) {
errx(1, "can't allocate proplist");
}
data = prop_dictionary_create();
if (data == NULL)
errx(1, "can't allocate proplist");
ret = prop_dictionary_set_uint32(data, "id", qk->qk_id);
if (!ret)
err(1, "prop_dictionary_set(id)");
if (!prop_array_add_and_rel(datas, data))
err(1, "prop_array_add(data)");
if (!quota_prop_add_command(cmds, "clear", idtype, datas))
err(1, "prop_add_command");
if (!prop_dictionary_set(protodict, "commands", cmds))
err(1, "prop_dictionary_set(command)");
#if 0
if (Dflag) {
fprintf(stderr, "message to kernel for %s:\n%s\n",
qh->qh_mountpoint,
prop_dictionary_externalize(protodict));
}
#endif
if (prop_dictionary_send_syscall(protodict, &pref) != 0)
err(1, "prop_dictionary_send_syscall");
if (quotactl(qh->qh_mountpoint, &pref) != 0)
err(1, "quotactl");
if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
err(1, "prop_dictionary_recv_syscall");
}
#if 0
if (Dflag) {
fprintf(stderr, "reply from kernel for %s:\n%s\n",
qh->qh_mountpoint,
prop_dictionary_externalize(dict));
}
#endif
if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
err(1, "quota_get_cmds");
}
/* only one command, no need to iter */
cmd = prop_array_get(cmds, 0);
if (cmd == NULL)
err(1, "prop_array_get(cmd)");
if (!prop_dictionary_get_int8(cmd, "return", &error8))
err(1, "prop_get(return)");
if (error8) {
prop_object_release(dict);
prop_object_release(protodict);
errno = error8;
return -1;
}
prop_object_release(dict);
prop_object_release(protodict);
return 0;
}
static int
__quota_proplib_getall(struct quotahandle *qh, int idtype, prop_array_t *ret)
{
prop_dictionary_t dict, cmd;
prop_array_t cmds, datas;
struct plistref pref;
int8_t error8;
/*
* XXX this should not crash out on error. But this is what
* the code this came from did... probably because it can just
* leak memory instead of needing the proper cleanup code.
*/
dict = quota_prop_create();
cmds = prop_array_create();
datas = prop_array_create();
if (dict == NULL || cmds == NULL || datas == NULL)
errx(1, "can't allocate proplist");
if (!quota_prop_add_command(cmds, "getall",
ufs_quota_class_names[idtype], datas))
err(1, "prop_add_command");
if (!prop_dictionary_set(dict, "commands", cmds))
err(1, "prop_dictionary_set(command)");
#if 0
if (Dflag)
printf("message to kernel:\n%s\n",
prop_dictionary_externalize(dict));
#endif
if (prop_dictionary_send_syscall(dict, &pref) != 0)
err(1, "prop_dictionary_send_syscall");
prop_object_release(dict);
if (quotactl(quota_getmountpoint(qh), &pref) != 0)
err(1, "quotactl");
if (prop_dictionary_recv_syscall(&pref, &dict) != 0) {
err(1, "prop_dictionary_recv_syscall");
}
#if 0
if (Dflag)
printf("reply from kernel:\n%s\n",
prop_dictionary_externalize(dict));
#endif
if ((errno = quota_get_cmds(dict, &cmds)) != 0) {
err(1, "quota_get_cmds");
}
cmd = prop_array_get(cmds, 0);
if (cmd == NULL) {
err(1, "prop_array_get(cmds)");
}
const char *cmdstr;
if (!prop_dictionary_get_cstring_nocopy(cmd, "command",
&cmdstr))
err(1, "prop_get(command)");
if (!prop_dictionary_get_int8(cmd, "return", &error8))
err(1, "prop_get(return)");
if (error8) {
prop_object_release(dict);
if (error8 != EOPNOTSUPP) {
errno = error8;
warn("get %s quotas",
ufs_quota_class_names[idtype]);
}
return -1;
}
datas = prop_dictionary_get(cmd, "data");
if (datas == NULL)
err(1, "prop_dict_get(datas)");
prop_object_retain(datas);
prop_object_release(dict);
*ret = datas;
return 0;
}
struct proplib_quotacursor *
__quota_proplib_cursor_create(void)
{
struct proplib_quotacursor *pqc;
pqc = malloc(sizeof(*pqc));
if (pqc == NULL) {
return NULL;
}
pqc->users = NULL;
pqc->numusers = 0;
pqc->haveusers = 0;
pqc->groups = NULL;
pqc->numgroups = 0;
pqc->havegroups = 0;
pqc->didusers = 0;
pqc->pos = 0;
pqc->didblocks = 0;
return pqc;
}
/* ARGSUSED */
void
__quota_proplib_cursor_destroy(struct proplib_quotacursor *pqc)
{
prop_object_release(pqc->users);
prop_object_release(pqc->groups);
free(pqc);
}
static int
__quota_proplib_cursor_load(struct quotahandle *qh,
struct proplib_quotacursor *pqc)
{
prop_array_t users, groups;
if (pqc->haveusers == 0) {
if (__quota_proplib_getall(qh, QUOTA_IDTYPE_USER, &users)) {
return -1;
}
pqc->users = users;
pqc->numusers = prop_array_count(users);
pqc->haveusers = 1;
}
if (pqc->havegroups == 0) {
if (__quota_proplib_getall(qh, QUOTA_IDTYPE_GROUP, &groups)) {
return -1;
}
pqc->groups = groups;
pqc->numgroups = prop_array_count(groups);
pqc->havegroups = 1;
}
return 0;
}
int
__quota_proplib_cursor_skipidtype(struct proplib_quotacursor *pqc,
unsigned idtype)
{
switch (idtype) {
case QUOTA_IDTYPE_USER:
/* if not yet loaded, numusers will be 0 and users NULL */
pqc->haveusers = 1;
break;
case QUOTA_IDTYPE_GROUP:
/* if not yet loaded, numgroups will be 0 and groups NULL */
pqc->havegroups = 1;
break;
default:
errno = EINVAL;
return -1;
}
return 0;
}
static int
__quota_proplib_cursor_subget(struct proplib_quotacursor *pqc,
prop_dictionary_t data,
struct quotakey *key, struct quotaval *val)
{
uint32_t id;
const char *strid;
if (prop_dictionary_get_uint32(data, "id", &id)) {
key->qk_id = id;
} else if (prop_dictionary_get_cstring_nocopy(data,
"id", &strid) &&
!strcmp(strid, "default")) {
key->qk_id = QUOTA_DEFAULTID;
} else {
/* invalid bundle */
errno = EINVAL;
return -1;
}
if (__quota_proplib_extractval(key->qk_objtype, data, val)) {
return -1;
}
return 0;
}
int
__quota_proplib_cursor_get(struct quotahandle *qh,
struct proplib_quotacursor *pqc,
struct quotakey *key, struct quotaval *val)
{
prop_dictionary_t data;
if (pqc->haveusers == 0 || pqc->havegroups == 0) {
if (__quota_proplib_cursor_load(qh, pqc)) {
return -1;
}
}
if (!pqc->didblocks) {
key->qk_objtype = QUOTA_OBJTYPE_BLOCKS;
} else {
key->qk_objtype = QUOTA_OBJTYPE_FILES;
}
if (!pqc->didusers && pqc->pos >= pqc->numusers) {
/* in case there are 0 users */
pqc->didusers = 1;
}
if (!pqc->didusers) {
key->qk_idtype = QUOTA_IDTYPE_USER;
data = prop_array_get(pqc->users, pqc->pos);
if (data == NULL) {
errno = ENOENT;
return -1;
}
/* get id and value */
if (__quota_proplib_cursor_subget(pqc, data, key, val)) {
return -1;
}
/* advance */
if (!pqc->didblocks) {
pqc->didblocks = 1;
} else {
pqc->didblocks = 0;
pqc->pos++;
if (pqc->pos >= pqc->numusers) {
pqc->pos = 0;
pqc->didusers = 1;
}
}
/* succeed */
return 0;
} else if (pqc->pos < pqc->numgroups) {
key->qk_idtype = QUOTA_IDTYPE_GROUP;
data = prop_array_get(pqc->groups, pqc->pos);
if (data == NULL) {
errno = ENOENT;
return -1;
}
/* get id and value */
if (__quota_proplib_cursor_subget(pqc, data, key, val)) {
return -1;
}
/* advance */
if (!pqc->didblocks) {
pqc->didblocks = 1;
} else {
pqc->didblocks = 0;
pqc->pos++;
}
/* succeed */
return 0;
} else {
/* at EOF */
/* XXX is there a better errno for this? */
errno = ENOENT;
return -1;
}
}
int
__quota_proplib_cursor_getn(struct quotahandle *qh,
struct proplib_quotacursor *pqc,
struct quotakey *keys, struct quotaval *vals,
unsigned maxnum)
{
unsigned i;
if (maxnum > INT_MAX) {
/* joker, eh? */
errno = EINVAL;
return -1;
}
for (i=0; i<maxnum; i++) {
if (__quota_proplib_cursor_atend(qh, pqc)) {
break;
}
if (__quota_proplib_cursor_get(qh, pqc, &keys[i], &vals[i])) {
if (i > 0) {
/*
* Succeed witih what we have so far;
* the next attempt will hit the same
* error again.
*/
break;
}
return -1;
}
}
return i;
}
int
__quota_proplib_cursor_atend(struct quotahandle *qh,
struct proplib_quotacursor *pqc)
{
if (!pqc->haveusers || !pqc->havegroups) {
if (__quota_proplib_cursor_load(qh, pqc)) {
/*
* Cannot fail here - report that we are not
* at EOF (lying if necessary) and let the
* next get call try to load again, fail and
* return the proper error.
*/
return 0;
}
}
if (!pqc->didusers && pqc->pos >= pqc->numusers) {
pqc->didusers = 1;
}
if (!pqc->didusers) {
return 0;
}
if (pqc->pos < pqc->numgroups) {
return 0;
}
return 1;
}
int
__quota_proplib_cursor_rewind(struct proplib_quotacursor *pqc)
{
pqc->didusers = 0;
pqc->pos = 0;
pqc->didblocks = 0;
return 0;
}