minix/sys/ufs/ufs/ufs_quota2.c
Ben Gras d65f6f7009 imported code harmonisation
. common/include/arch/i386 is not actually an imported
	  sys/arch/i386/include but leftover Minix files;
	  remove and move to include/
	. move include/ufs to sys/ufs, where it came from, now that
	  we have a sys/ hierarchy
	. move mdocml/ to external/bsd/, now we have that
	. single sys/arch/i386/stand/ import for boot stuff
2012-03-14 16:02:59 +01:00

1013 lines
24 KiB
C

/* $NetBSD: ufs_quota2.c,v 1.4 2011/06/07 14:56:13 bouyer Exp $ */
/*-
* Copyright (c) 2010 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>
__KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.4 2011/06/07 14:56:13 bouyer Exp $");
#include <sys/buf.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/fstrans.h>
#include <sys/kauth.h>
#include <sys/wapbl.h>
#include <ufs/ufs/quota2.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_bswap.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ufs/ufs_quota.h>
#include <ufs/ufs/ufs_wapbl.h>
#include <quota/quotaprop.h>
/*
* LOCKING:
* Data in the entries are protected by the associated struct dquot's
* dq_interlock (this means we can't read or change a quota entry without
* grabing a dquot for it).
* The header and lists (including pointers in the data entries, and q2e_uid)
* are protected by the global dqlock.
* the locking order is dq_interlock -> dqlock
*/
static int quota2_bwrite(struct mount *, struct buf *);
static int getinoquota2(struct inode *, bool, bool, struct buf **,
struct quota2_entry **);
static int getq2h(struct ufsmount *, int, struct buf **,
struct quota2_header **, int);
static int getq2e(struct ufsmount *, int, daddr_t, int, struct buf **,
struct quota2_entry **, int);
static int quota2_walk_list(struct ufsmount *, struct buf *, int,
uint64_t *, int, void *,
int (*func)(struct ufsmount *, uint64_t *, struct quota2_entry *,
uint64_t, void *));
static int quota2_dict_update_q2e_limits(prop_dictionary_t,
struct quota2_entry *);
static prop_dictionary_t q2etoprop(struct quota2_entry *, int);
static const char *limnames[] = INITQLNAMES;
static int
quota2_dict_update_q2e_limits(prop_dictionary_t data,
struct quota2_entry *q2e)
{
const char *val_limitsonly_names[] = INITQVNAMES_LIMITSONLY;
int i, error;
prop_dictionary_t val;
for (i = 0; i < N_QL; i++) {
if (!prop_dictionary_get_dict(data, limnames[i], &val))
return EINVAL;
error = quotaprop_dict_get_uint64(val,
&q2e->q2e_val[i].q2v_hardlimit,
val_limitsonly_names, N_QV, true);
if (error)
return error;
}
return 0;
}
static prop_dictionary_t
q2etoprop(struct quota2_entry *q2e, int def)
{
const char *val_names[] = INITQVNAMES_ALL;
prop_dictionary_t dict1 = prop_dictionary_create();
prop_dictionary_t dict2;
int i;
if (dict1 == NULL)
return NULL;
if (def) {
if (!prop_dictionary_set_cstring_nocopy(dict1, "id",
"default")) {
goto err;
}
} else {
if (!prop_dictionary_set_uint32(dict1, "id", q2e->q2e_uid)) {
goto err;
}
}
for (i = 0; i < N_QL; i++) {
dict2 = limits64toprop(&q2e->q2e_val[i].q2v_hardlimit,
val_names, N_QV);
if (dict2 == NULL)
goto err;
if (!prop_dictionary_set_and_rel(dict1, limnames[i], dict2))
goto err;
}
return dict1;
err:
prop_object_release(dict1);
return NULL;
}
static int
quota2_bwrite(struct mount *mp, struct buf *bp)
{
if (mp->mnt_flag & MNT_SYNCHRONOUS)
return bwrite(bp);
else {
bdwrite(bp);
return 0;
}
}
static int
getq2h(struct ufsmount *ump, int type,
struct buf **bpp, struct quota2_header **q2hp, int flags)
{
#ifdef FFS_EI
const int needswap = UFS_MPNEEDSWAP(ump);
#endif
int error;
struct buf *bp;
struct quota2_header *q2h;
KASSERT(mutex_owned(&dqlock));
error = bread(ump->um_quotas[type], 0, ump->umq2_bsize,
ump->um_cred[type], flags, &bp);
if (error)
return error;
if (bp->b_resid != 0)
panic("dq2get: %s quota file truncated", quotatypes[type]);
q2h = (void *)bp->b_data;
if (ufs_rw32(q2h->q2h_magic_number, needswap) != Q2_HEAD_MAGIC ||
q2h->q2h_type != type)
panic("dq2get: corrupted %s quota header", quotatypes[type]);
*bpp = bp;
*q2hp = q2h;
return 0;
}
static int
getq2e(struct ufsmount *ump, int type, daddr_t lblkno, int blkoffset,
struct buf **bpp, struct quota2_entry **q2ep, int flags)
{
int error;
struct buf *bp;
if (blkoffset & (sizeof(uint64_t) - 1)) {
panic("dq2get: %s quota file corrupted",
quotatypes[type]);
}
error = bread(ump->um_quotas[type], lblkno, ump->umq2_bsize,
ump->um_cred[type], flags, &bp);
if (error)
return error;
if (bp->b_resid != 0) {
panic("dq2get: %s quota file corrupted",
quotatypes[type]);
}
*q2ep = (void *)((char *)bp->b_data + blkoffset);
*bpp = bp;
return 0;
}
/* walk a quota entry list, calling the callback for each entry */
#define Q2WL_ABORT 0x10000000
static int
quota2_walk_list(struct ufsmount *ump, struct buf *hbp, int type,
uint64_t *offp, int flags, void *a,
int (*func)(struct ufsmount *, uint64_t *, struct quota2_entry *, uint64_t, void *))
{
#ifdef FFS_EI
const int needswap = UFS_MPNEEDSWAP(ump);
#endif
daddr_t off = ufs_rw64(*offp, needswap);
struct buf *bp, *obp = hbp;
int ret = 0, ret2 = 0;
struct quota2_entry *q2e;
daddr_t lblkno, blkoff, olblkno = 0;
KASSERT(mutex_owner(&dqlock));
while (off != 0) {
lblkno = (off >> ump->um_mountp->mnt_fs_bshift);
blkoff = (off & ump->umq2_bmask);
if (lblkno == 0) {
/* in the header block */
bp = hbp;
} else if (lblkno == olblkno) {
/* still in the same buf */
bp = obp;
} else {
ret = bread(ump->um_quotas[type], lblkno,
ump->umq2_bsize,
ump->um_cred[type], flags, &bp);
if (ret)
return ret;
if (bp->b_resid != 0) {
panic("quota2_walk_list: %s quota file corrupted",
quotatypes[type]);
}
}
q2e = (void *)((char *)(bp->b_data) + blkoff);
ret = (*func)(ump, offp, q2e, off, a);
if (off != ufs_rw64(*offp, needswap)) {
/* callback changed parent's pointer, redo */
off = ufs_rw64(*offp, needswap);
if (bp != hbp && bp != obp)
ret2 = bwrite(bp);
} else {
/* parent if now current */
if (obp != bp && obp != hbp) {
if (flags & B_MODIFY)
ret2 = bwrite(obp);
else
brelse(obp, 0);
}
obp = bp;
olblkno = lblkno;
offp = &(q2e->q2e_next);
off = ufs_rw64(*offp, needswap);
}
if (ret)
break;
if (ret2) {
ret = ret2;
break;
}
}
if (obp != hbp) {
if (flags & B_MODIFY)
ret2 = bwrite(obp);
else
brelse(obp, 0);
}
if (ret & Q2WL_ABORT)
return 0;
if (ret == 0)
return ret2;
return ret;
}
int
quota2_umount(struct mount *mp, int flags)
{
int i, error;
struct ufsmount *ump = VFSTOUFS(mp);
if ((ump->um_flags & UFS_QUOTA2) == 0)
return 0;
for (i = 0; i < MAXQUOTAS; i++) {
if (ump->um_quotas[i] != NULLVP) {
error = vn_close(ump->um_quotas[i], FREAD|FWRITE,
ump->um_cred[i]);
if (error) {
printf("quota2_umount failed: close(%p) %d\n",
ump->um_quotas[i], error);
return error;
}
}
ump->um_quotas[i] = NULLVP;
}
return 0;
}
static int
quota2_q2ealloc(struct ufsmount *ump, int type, uid_t uid, struct dquot *dq,
struct buf **bpp, struct quota2_entry **q2ep)
{
int error, error2;
struct buf *hbp, *bp;
struct quota2_header *q2h;
struct quota2_entry *q2e;
daddr_t offset;
u_long hash_mask;
const int needswap = UFS_MPNEEDSWAP(ump);
KASSERT(mutex_owned(&dq->dq_interlock));
KASSERT(mutex_owned(&dqlock));
error = getq2h(ump, type, &hbp, &q2h, B_MODIFY);
if (error)
return error;
offset = ufs_rw64(q2h->q2h_free, needswap);
if (offset == 0) {
struct vnode *vp = ump->um_quotas[type];
struct inode *ip = VTOI(vp);
uint64_t size = ip->i_size;
/* need to alocate a new disk block */
error = UFS_BALLOC(vp, size, ump->umq2_bsize,
ump->um_cred[type], B_CLRBUF | B_SYNC, &bp);
if (error) {
brelse(hbp, 0);
return error;
}
KASSERT((ip->i_size % ump->umq2_bsize) == 0);
ip->i_size += ump->umq2_bsize;
DIP_ASSIGN(ip, size, ip->i_size);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
uvm_vnp_setsize(vp, ip->i_size);
quota2_addfreeq2e(q2h, bp->b_data, size, ump->umq2_bsize,
needswap);
error = bwrite(bp);
error2 = UFS_UPDATE(vp, NULL, NULL, UPDATE_WAIT);
if (error || error2) {
brelse(hbp, 0);
if (error)
return error;
return error2;
}
offset = ufs_rw64(q2h->q2h_free, needswap);
KASSERT(offset != 0);
}
dq->dq2_lblkno = (offset >> ump->um_mountp->mnt_fs_bshift);
dq->dq2_blkoff = (offset & ump->umq2_bmask);
if (dq->dq2_lblkno == 0) {
bp = hbp;
q2e = (void *)((char *)bp->b_data + dq->dq2_blkoff);
} else {
error = getq2e(ump, type, dq->dq2_lblkno,
dq->dq2_blkoff, &bp, &q2e, B_MODIFY);
if (error) {
brelse(hbp, 0);
return error;
}
}
hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
/* remove from free list */
q2h->q2h_free = q2e->q2e_next;
memcpy(q2e, &q2h->q2h_defentry, sizeof(*q2e));
q2e->q2e_uid = ufs_rw32(uid, needswap);
/* insert in hash list */
q2e->q2e_next = q2h->q2h_entries[uid & hash_mask];
q2h->q2h_entries[uid & hash_mask] = ufs_rw64(offset, needswap);
if (hbp != bp) {
bwrite(hbp);
}
*q2ep = q2e;
*bpp = bp;
return 0;
}
static int
getinoquota2(struct inode *ip, bool alloc, bool modify, struct buf **bpp,
struct quota2_entry **q2ep)
{
int error;
int i;
struct dquot *dq;
struct ufsmount *ump = ip->i_ump;
u_int32_t ino_ids[MAXQUOTAS];
error = getinoquota(ip);
if (error)
return error;
if (alloc) {
UFS_WAPBL_JLOCK_ASSERT(ump->um_mountp);
}
ino_ids[USRQUOTA] = ip->i_uid;
ino_ids[GRPQUOTA] = ip->i_gid;
/* first get the interlock for all dquot */
for (i = 0; i < MAXQUOTAS; i++) {
dq = ip->i_dquot[i];
if (dq == NODQUOT)
continue;
mutex_enter(&dq->dq_interlock);
}
/* now get the corresponding quota entry */
for (i = 0; i < MAXQUOTAS; i++) {
bpp[i] = NULL;
q2ep[i] = NULL;
dq = ip->i_dquot[i];
if (dq == NODQUOT)
continue;
if (__predict_false(ump->um_quotas[i] == NULL)) {
/*
* quotas have been turned off. This can happen
* at umount time.
*/
mutex_exit(&dq->dq_interlock);
dqrele(NULLVP, dq);
ip->i_dquot[i] = NULL;
continue;
}
if ((dq->dq2_lblkno | dq->dq2_blkoff) == 0) {
if (!alloc) {
continue;
}
/* need to alloc a new on-disk quot */
mutex_enter(&dqlock);
error = quota2_q2ealloc(ump, i, ino_ids[i], dq,
&bpp[i], &q2ep[i]);
mutex_exit(&dqlock);
if (error)
return error;
} else {
error = getq2e(ump, i, dq->dq2_lblkno,
dq->dq2_blkoff, &bpp[i], &q2ep[i],
modify ? B_MODIFY : 0);
if (error)
return error;
}
}
return 0;
}
static int
quota2_check(struct inode *ip, int vtype, int64_t change, kauth_cred_t cred,
int flags)
{
int error;
struct buf *bp[MAXQUOTAS];
struct quota2_entry *q2e[MAXQUOTAS];
struct quota2_val *q2vp;
struct dquot *dq;
uint64_t ncurblks;
struct ufsmount *ump = ip->i_ump;
struct mount *mp = ump->um_mountp;
const int needswap = UFS_MPNEEDSWAP(ump);
int i;
if ((error = getinoquota2(ip, change > 0, change != 0, bp, q2e)) != 0)
return error;
if (change == 0) {
for (i = 0; i < MAXQUOTAS; i++) {
dq = ip->i_dquot[i];
if (dq == NODQUOT)
continue;
if (bp[i])
brelse(bp[i], 0);
mutex_exit(&dq->dq_interlock);
}
return 0;
}
if (change < 0) {
for (i = 0; i < MAXQUOTAS; i++) {
dq = ip->i_dquot[i];
if (dq == NODQUOT)
continue;
if (q2e[i] == NULL) {
mutex_exit(&dq->dq_interlock);
continue;
}
q2vp = &q2e[i]->q2e_val[vtype];
ncurblks = ufs_rw64(q2vp->q2v_cur, needswap);
if (ncurblks < -change)
ncurblks = 0;
else
ncurblks += change;
q2vp->q2v_cur = ufs_rw64(ncurblks, needswap);
quota2_bwrite(mp, bp[i]);
mutex_exit(&dq->dq_interlock);
}
return 0;
}
/* see if the allocation is allowed */
for (i = 0; i < MAXQUOTAS; i++) {
struct quota2_val q2v;
int ql_stat;
dq = ip->i_dquot[i];
if (dq == NODQUOT)
continue;
KASSERT(q2e[i] != NULL);
quota2_ufs_rwq2v(&q2e[i]->q2e_val[vtype], &q2v, needswap);
ql_stat = quota2_check_limit(&q2v, change, time_second);
if ((flags & FORCE) == 0 &&
kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
KAUTH_ARG(i), KAUTH_ARG(vtype), NULL) != 0) {
/* enforce this limit */
switch(QL_STATUS(ql_stat)) {
case QL_S_DENY_HARD:
if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
uprintf("\n%s: write failed, %s %s "
"limit reached\n",
mp->mnt_stat.f_mntonname,
quotatypes[i], limnames[vtype]);
dq->dq_flags |= DQ_WARN(vtype);
}
error = EDQUOT;
break;
case QL_S_DENY_GRACE:
if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
uprintf("\n%s: write failed, %s %s "
"limit reached\n",
mp->mnt_stat.f_mntonname,
quotatypes[i], limnames[vtype]);
dq->dq_flags |= DQ_WARN(vtype);
}
error = EDQUOT;
break;
case QL_S_ALLOW_SOFT:
if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
uprintf("\n%s: warning, %s %s "
"quota exceeded\n",
mp->mnt_stat.f_mntonname,
quotatypes[i], limnames[vtype]);
dq->dq_flags |= DQ_WARN(vtype);
}
break;
}
}
/*
* always do this; we don't know if the allocation will
* succed or not in the end. if we don't do the allocation
* q2v_time will be ignored anyway
*/
if (ql_stat & QL_F_CROSS) {
q2v.q2v_time = time_second + q2v.q2v_grace;
quota2_ufs_rwq2v(&q2v, &q2e[i]->q2e_val[vtype],
needswap);
}
}
/* now do the allocation if allowed */
for (i = 0; i < MAXQUOTAS; i++) {
dq = ip->i_dquot[i];
if (dq == NODQUOT)
continue;
KASSERT(q2e[i] != NULL);
if (error == 0) {
q2vp = &q2e[i]->q2e_val[vtype];
ncurblks = ufs_rw64(q2vp->q2v_cur, needswap);
q2vp->q2v_cur = ufs_rw64(ncurblks + change, needswap);
quota2_bwrite(mp, bp[i]);
} else
brelse(bp[i], 0);
mutex_exit(&dq->dq_interlock);
}
return error;
}
int
chkdq2(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
{
return quota2_check(ip, QL_BLOCK, change, cred, flags);
}
int
chkiq2(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
{
return quota2_check(ip, QL_FILE, change, cred, flags);
}
int
quota2_handle_cmd_set(struct ufsmount *ump, int type, int id,
int defaultq, prop_dictionary_t data)
{
int error;
struct dquot *dq;
struct quota2_header *q2h;
struct quota2_entry q2e, *q2ep;
struct buf *bp;
const int needswap = UFS_MPNEEDSWAP(ump);
if (ump->um_quotas[type] == NULLVP)
return ENODEV;
error = UFS_WAPBL_BEGIN(ump->um_mountp);
if (error)
return error;
if (defaultq) {
mutex_enter(&dqlock);
error = getq2h(ump, type, &bp, &q2h, B_MODIFY);
if (error) {
mutex_exit(&dqlock);
goto out_wapbl;
}
quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
error = quota2_dict_update_q2e_limits(data, &q2e);
if (error) {
mutex_exit(&dqlock);
brelse(bp, 0);
goto out_wapbl;
}
quota2_ufs_rwq2e(&q2e, &q2h->q2h_defentry, needswap);
mutex_exit(&dqlock);
quota2_bwrite(ump->um_mountp, bp);
goto out_wapbl;
}
error = dqget(NULLVP, id, ump, type, &dq);
if (error)
goto out_wapbl;
mutex_enter(&dq->dq_interlock);
if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
/* need to alloc a new on-disk quot */
mutex_enter(&dqlock);
error = quota2_q2ealloc(ump, type, id, dq, &bp, &q2ep);
mutex_exit(&dqlock);
} else {
error = getq2e(ump, type, dq->dq2_lblkno, dq->dq2_blkoff,
&bp, &q2ep, B_MODIFY);
}
if (error)
goto out_il;
quota2_ufs_rwq2e(q2ep, &q2e, needswap);
error = quota2_dict_update_q2e_limits(data, &q2e);
if (error) {
brelse(bp, 0);
goto out_il;
}
quota2_ufs_rwq2e(&q2e, q2ep, needswap);
quota2_bwrite(ump->um_mountp, bp);
out_il:
mutex_exit(&dq->dq_interlock);
dqrele(NULLVP, dq);
out_wapbl:
UFS_WAPBL_END(ump->um_mountp);
return error;
}
struct dq2clear_callback {
uid_t id;
struct dquot *dq;
struct quota2_header *q2h;
};
static int
dq2clear_callback(struct ufsmount *ump, uint64_t *offp, struct quota2_entry *q2e,
uint64_t off, void *v)
{
struct dq2clear_callback *c = v;
#ifdef FFS_EI
const int needswap = UFS_MPNEEDSWAP(ump);
#endif
uint64_t myoff;
if (ufs_rw32(q2e->q2e_uid, needswap) == c->id) {
KASSERT(mutex_owned(&c->dq->dq_interlock));
c->dq->dq2_lblkno = 0;
c->dq->dq2_blkoff = 0;
myoff = *offp;
/* remove from hash list */
*offp = q2e->q2e_next;
/* add to free list */
q2e->q2e_next = c->q2h->q2h_free;
c->q2h->q2h_free = myoff;
return Q2WL_ABORT;
}
return 0;
}
int
quota2_handle_cmd_clear(struct ufsmount *ump, int type, int id,
int defaultq, prop_dictionary_t data)
{
int error, i;
struct dquot *dq;
struct quota2_header *q2h;
struct quota2_entry q2e, *q2ep;
struct buf *hbp, *bp;
u_long hash_mask;
struct dq2clear_callback c;
if (ump->um_quotas[type] == NULLVP)
return ENODEV;
if (defaultq)
return EOPNOTSUPP;
/* get the default entry before locking the entry's buffer */
mutex_enter(&dqlock);
error = getq2h(ump, type, &hbp, &q2h, 0);
if (error) {
mutex_exit(&dqlock);
return error;
}
/* we'll copy to another disk entry, so no need to swap */
memcpy(&q2e, &q2h->q2h_defentry, sizeof(q2e));
mutex_exit(&dqlock);
brelse(hbp, 0);
error = dqget(NULLVP, id, ump, type, &dq);
if (error)
return error;
mutex_enter(&dq->dq_interlock);
if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
/* already clear, nothing to do */
error = ENOENT;
goto out_il;
}
error = UFS_WAPBL_BEGIN(ump->um_mountp);
if (error)
goto out_dq;
error = getq2e(ump, type, dq->dq2_lblkno, dq->dq2_blkoff,
&bp, &q2ep, B_MODIFY);
if (error)
goto out_wapbl;
if (q2ep->q2e_val[QL_BLOCK].q2v_cur != 0 ||
q2ep->q2e_val[QL_FILE].q2v_cur != 0) {
/* can't free this entry; revert to default */
for (i = 0; i < N_QL; i++) {
q2ep->q2e_val[i].q2v_softlimit =
q2e.q2e_val[i].q2v_softlimit;
q2ep->q2e_val[i].q2v_hardlimit =
q2e.q2e_val[i].q2v_hardlimit;
q2ep->q2e_val[i].q2v_grace =
q2e.q2e_val[i].q2v_grace;
q2ep->q2e_val[i].q2v_time = 0;
}
quota2_bwrite(ump->um_mountp, bp);
goto out_wapbl;
}
/* we can free it. release bp so we can walk the list */
brelse(bp, 0);
mutex_enter(&dqlock);
error = getq2h(ump, type, &hbp, &q2h, 0);
if (error)
goto out_dqlock;
hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
c.dq = dq;
c.id = id;
c.q2h = q2h;
error = quota2_walk_list(ump, hbp, type,
&q2h->q2h_entries[id & hash_mask], B_MODIFY, &c,
dq2clear_callback);
bwrite(hbp);
out_dqlock:
mutex_exit(&dqlock);
out_wapbl:
UFS_WAPBL_END(ump->um_mountp);
out_il:
mutex_exit(&dq->dq_interlock);
out_dq:
dqrele(NULLVP, dq);
return error;
}
static int
quota2_array_add_q2e(struct ufsmount *ump, int type,
int id, prop_array_t replies)
{
struct dquot *dq;
int error;
struct quota2_entry *q2ep, q2e;
struct buf *bp;
const int needswap = UFS_MPNEEDSWAP(ump);
prop_dictionary_t dict;
error = dqget(NULLVP, id, ump, type, &dq);
if (error)
return error;
mutex_enter(&dq->dq_interlock);
if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
mutex_exit(&dq->dq_interlock);
dqrele(NULLVP, dq);
return ENOENT;
}
error = getq2e(ump, type, dq->dq2_lblkno, dq->dq2_blkoff,
&bp, &q2ep, 0);
if (error) {
mutex_exit(&dq->dq_interlock);
dqrele(NULLVP, dq);
return error;
}
quota2_ufs_rwq2e(q2ep, &q2e, needswap);
brelse(bp, 0);
mutex_exit(&dq->dq_interlock);
dqrele(NULLVP, dq);
dict = q2etoprop(&q2e, 0);
if (dict == NULL)
return ENOMEM;
if (!prop_array_add_and_rel(replies, dict))
return ENOMEM;
return 0;
}
int
quota2_handle_cmd_get(struct ufsmount *ump, int type, int id,
int defaultq, prop_array_t replies)
{
int error;
struct quota2_header *q2h;
struct quota2_entry q2e;
struct buf *bp;
prop_dictionary_t dict;
const int needswap = UFS_MPNEEDSWAP(ump);
if (ump->um_quotas[type] == NULLVP)
return ENODEV;
if (defaultq) {
mutex_enter(&dqlock);
error = getq2h(ump, type, &bp, &q2h, 0);
if (error) {
mutex_exit(&dqlock);
return error;
}
quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
mutex_exit(&dqlock);
brelse(bp, 0);
dict = q2etoprop(&q2e, defaultq);
if (dict == NULL)
return ENOMEM;
if (!prop_array_add_and_rel(replies, dict))
return ENOMEM;
} else
error = quota2_array_add_q2e(ump, type, id, replies);
return error;
}
struct getuids {
long nuids; /* number of uids in array */
long size; /* size of array */
uid_t *uids; /* array of uids, dynamically allocated */
};
static int
quota2_getuids_callback(struct ufsmount *ump, uint64_t *offp,
struct quota2_entry *q2ep, uint64_t off, void *v)
{
struct getuids *gu = v;
uid_t *newuids;
#ifdef FFS_EI
const int needswap = UFS_MPNEEDSWAP(ump);
#endif
if (gu->nuids == gu->size) {
newuids = realloc(gu->uids, gu->size + PAGE_SIZE, M_TEMP,
M_WAITOK);
if (newuids == NULL) {
free(gu->uids, M_TEMP);
return ENOMEM;
}
gu->uids = newuids;
gu->size += (PAGE_SIZE / sizeof(uid_t));
}
gu->uids[gu->nuids] = ufs_rw32(q2ep->q2e_uid, needswap);
gu->nuids++;
return 0;
}
int
quota2_handle_cmd_getall(struct ufsmount *ump, int type, prop_array_t replies)
{
int error;
struct quota2_header *q2h;
struct quota2_entry q2e;
struct buf *hbp;
prop_dictionary_t dict;
uint64_t offset;
int i, j;
int quota2_hash_size;
const int needswap = UFS_MPNEEDSWAP(ump);
struct getuids gu;
if (ump->um_quotas[type] == NULLVP)
return ENODEV;
mutex_enter(&dqlock);
error = getq2h(ump, type, &hbp, &q2h, 0);
if (error) {
mutex_exit(&dqlock);
return error;
}
quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
dict = q2etoprop(&q2e, 1);
if (!prop_array_add_and_rel(replies, dict)) {
error = ENOMEM;
goto error_bp;
}
/*
* we can't directly get entries as we can't walk the list
* with qdlock and grab dq_interlock to read the entries
* at the same time. So just walk the lists to build a list of uid,
* and then read entries for these uids
*/
memset(&gu, 0, sizeof(gu));
quota2_hash_size = ufs_rw16(q2h->q2h_hash_size, needswap);
for (i = 0; i < quota2_hash_size ; i++) {
offset = q2h->q2h_entries[i];
error = quota2_walk_list(ump, hbp, type, &offset, 0, &gu,
quota2_getuids_callback);
if (error) {
if (gu.uids != NULL)
free(gu.uids, M_TEMP);
break;
}
}
error_bp:
mutex_exit(&dqlock);
brelse(hbp, 0);
if (error)
return error;
for (j = 0; j < gu.nuids; j++) {
error = quota2_array_add_q2e(ump, type,
gu.uids[j], replies);
if (error && error != ENOENT)
break;
}
free(gu.uids, M_TEMP);
return error;
}
int
q2sync(struct mount *mp)
{
return 0;
}
struct dq2get_callback {
uid_t id;
struct dquot *dq;
};
static int
dq2get_callback(struct ufsmount *ump, uint64_t *offp, struct quota2_entry *q2e,
uint64_t off, void *v)
{
struct dq2get_callback *c = v;
daddr_t lblkno;
int blkoff;
#ifdef FFS_EI
const int needswap = UFS_MPNEEDSWAP(ump);
#endif
if (ufs_rw32(q2e->q2e_uid, needswap) == c->id) {
KASSERT(mutex_owned(&c->dq->dq_interlock));
lblkno = (off >> ump->um_mountp->mnt_fs_bshift);
blkoff = (off & ump->umq2_bmask);
c->dq->dq2_lblkno = lblkno;
c->dq->dq2_blkoff = blkoff;
return Q2WL_ABORT;
}
return 0;
}
int
dq2get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type,
struct dquot *dq)
{
struct buf *bp;
struct quota2_header *q2h;
int error;
daddr_t offset;
u_long hash_mask;
struct dq2get_callback c = {
.id = id,
.dq = dq
};
KASSERT(mutex_owned(&dq->dq_interlock));
mutex_enter(&dqlock);
error = getq2h(ump, type, &bp, &q2h, 0);
if (error)
goto out_mutex;
/* look for our entry */
hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
offset = q2h->q2h_entries[id & hash_mask];
error = quota2_walk_list(ump, bp, type, &offset, 0, (void *)&c,
dq2get_callback);
brelse(bp, 0);
out_mutex:
mutex_exit(&dqlock);
return error;
}
int
dq2sync(struct vnode *vp, struct dquot *dq)
{
return 0;
}