1*0a6a1f1dSLionel Sambuc /* $NetBSD: ulfs_quota2.c,v 1.21 2015/07/28 05:09:35 dholland Exp $ */
284d9c625SLionel Sambuc /* from NetBSD: ufs_quota2.c,v 1.35 2012/09/27 07:47:56 bouyer Exp */
384d9c625SLionel Sambuc /* from NetBSD: ffs_quota2.c,v 1.4 2011/06/12 03:36:00 rmind Exp */
484d9c625SLionel Sambuc
584d9c625SLionel Sambuc /*-
684d9c625SLionel Sambuc * Copyright (c) 2010 Manuel Bouyer
784d9c625SLionel Sambuc * All rights reserved.
884d9c625SLionel Sambuc *
984d9c625SLionel Sambuc * Redistribution and use in source and binary forms, with or without
1084d9c625SLionel Sambuc * modification, are permitted provided that the following conditions
1184d9c625SLionel Sambuc * are met:
1284d9c625SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
1384d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer.
1484d9c625SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
1584d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
1684d9c625SLionel Sambuc * documentation and/or other materials provided with the distribution.
1784d9c625SLionel Sambuc *
1884d9c625SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1984d9c625SLionel Sambuc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2084d9c625SLionel Sambuc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2184d9c625SLionel Sambuc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2284d9c625SLionel Sambuc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2384d9c625SLionel Sambuc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2484d9c625SLionel Sambuc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2584d9c625SLionel Sambuc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2684d9c625SLionel Sambuc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2784d9c625SLionel Sambuc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2884d9c625SLionel Sambuc * POSSIBILITY OF SUCH DAMAGE.
2984d9c625SLionel Sambuc */
3084d9c625SLionel Sambuc
3184d9c625SLionel Sambuc #include <sys/cdefs.h>
32*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: ulfs_quota2.c,v 1.21 2015/07/28 05:09:35 dholland Exp $");
3384d9c625SLionel Sambuc
3484d9c625SLionel Sambuc #include <sys/buf.h>
3584d9c625SLionel Sambuc #include <sys/param.h>
3684d9c625SLionel Sambuc #include <sys/kernel.h>
3784d9c625SLionel Sambuc #include <sys/systm.h>
3884d9c625SLionel Sambuc #include <sys/namei.h>
3984d9c625SLionel Sambuc #include <sys/file.h>
4084d9c625SLionel Sambuc #include <sys/proc.h>
4184d9c625SLionel Sambuc #include <sys/vnode.h>
4284d9c625SLionel Sambuc #include <sys/mount.h>
4384d9c625SLionel Sambuc #include <sys/fstrans.h>
4484d9c625SLionel Sambuc #include <sys/kauth.h>
4584d9c625SLionel Sambuc #include <sys/wapbl.h>
4684d9c625SLionel Sambuc #include <sys/quota.h>
4784d9c625SLionel Sambuc #include <sys/quotactl.h>
4884d9c625SLionel Sambuc
49*0a6a1f1dSLionel Sambuc #include <ufs/lfs/lfs.h>
50*0a6a1f1dSLionel Sambuc #include <ufs/lfs/lfs_accessors.h>
5184d9c625SLionel Sambuc #include <ufs/lfs/lfs_extern.h>
5284d9c625SLionel Sambuc
5384d9c625SLionel Sambuc #include <ufs/lfs/ulfs_quota2.h>
5484d9c625SLionel Sambuc #include <ufs/lfs/ulfs_inode.h>
5584d9c625SLionel Sambuc #include <ufs/lfs/ulfsmount.h>
5684d9c625SLionel Sambuc #include <ufs/lfs/ulfs_bswap.h>
5784d9c625SLionel Sambuc #include <ufs/lfs/ulfs_extern.h>
5884d9c625SLionel Sambuc #include <ufs/lfs/ulfs_quota.h>
5984d9c625SLionel Sambuc
6084d9c625SLionel Sambuc /*
6184d9c625SLionel Sambuc * LOCKING:
6284d9c625SLionel Sambuc * Data in the entries are protected by the associated struct dquot's
6384d9c625SLionel Sambuc * dq_interlock (this means we can't read or change a quota entry without
6484d9c625SLionel Sambuc * grabing a dquot for it).
6584d9c625SLionel Sambuc * The header and lists (including pointers in the data entries, and q2e_uid)
6684d9c625SLionel Sambuc * are protected by the global dqlock.
6784d9c625SLionel Sambuc * the locking order is dq_interlock -> dqlock
6884d9c625SLionel Sambuc */
6984d9c625SLionel Sambuc
7084d9c625SLionel Sambuc static int quota2_bwrite(struct mount *, struct buf *);
7184d9c625SLionel Sambuc static int getinoquota2(struct inode *, bool, bool, struct buf **,
7284d9c625SLionel Sambuc struct quota2_entry **);
7384d9c625SLionel Sambuc static int getq2h(struct ulfsmount *, int, struct buf **,
7484d9c625SLionel Sambuc struct quota2_header **, int);
7584d9c625SLionel Sambuc static int getq2e(struct ulfsmount *, int, daddr_t, int, struct buf **,
7684d9c625SLionel Sambuc struct quota2_entry **, int);
7784d9c625SLionel Sambuc static int quota2_walk_list(struct ulfsmount *, struct buf *, int,
7884d9c625SLionel Sambuc uint64_t *, int, void *,
7984d9c625SLionel Sambuc int (*func)(struct ulfsmount *, uint64_t *, struct quota2_entry *,
8084d9c625SLionel Sambuc uint64_t, void *));
8184d9c625SLionel Sambuc
8284d9c625SLionel Sambuc static const char *limnames[] = INITQLNAMES;
8384d9c625SLionel Sambuc
8484d9c625SLionel Sambuc static void
quota2_dict_update_q2e_limits(int objtype,const struct quotaval * val,struct quota2_entry * q2e)8584d9c625SLionel Sambuc quota2_dict_update_q2e_limits(int objtype, const struct quotaval *val,
8684d9c625SLionel Sambuc struct quota2_entry *q2e)
8784d9c625SLionel Sambuc {
8884d9c625SLionel Sambuc /* make sure we can index q2e_val[] by the fs-independent objtype */
8984d9c625SLionel Sambuc CTASSERT(QUOTA_OBJTYPE_BLOCKS == QL_BLOCK);
9084d9c625SLionel Sambuc CTASSERT(QUOTA_OBJTYPE_FILES == QL_FILE);
9184d9c625SLionel Sambuc
9284d9c625SLionel Sambuc q2e->q2e_val[objtype].q2v_hardlimit = val->qv_hardlimit;
9384d9c625SLionel Sambuc q2e->q2e_val[objtype].q2v_softlimit = val->qv_softlimit;
9484d9c625SLionel Sambuc q2e->q2e_val[objtype].q2v_grace = val->qv_grace;
9584d9c625SLionel Sambuc }
9684d9c625SLionel Sambuc
9784d9c625SLionel Sambuc /*
9884d9c625SLionel Sambuc * Convert internal representation to FS-independent representation.
9984d9c625SLionel Sambuc * (Note that while the two types are currently identical, the
10084d9c625SLionel Sambuc * internal representation is an on-disk struct and the FS-independent
10184d9c625SLionel Sambuc * representation is not, and they might diverge in the future.)
10284d9c625SLionel Sambuc */
10384d9c625SLionel Sambuc static void
q2val_to_quotaval(struct quota2_val * q2v,struct quotaval * qv)10484d9c625SLionel Sambuc q2val_to_quotaval(struct quota2_val *q2v, struct quotaval *qv)
10584d9c625SLionel Sambuc {
10684d9c625SLionel Sambuc qv->qv_softlimit = q2v->q2v_softlimit;
10784d9c625SLionel Sambuc qv->qv_hardlimit = q2v->q2v_hardlimit;
10884d9c625SLionel Sambuc qv->qv_usage = q2v->q2v_cur;
10984d9c625SLionel Sambuc qv->qv_expiretime = q2v->q2v_time;
11084d9c625SLionel Sambuc qv->qv_grace = q2v->q2v_grace;
11184d9c625SLionel Sambuc }
11284d9c625SLionel Sambuc
11384d9c625SLionel Sambuc /*
11484d9c625SLionel Sambuc * Convert a quota2entry and default-flag to the FS-independent
11584d9c625SLionel Sambuc * representation.
11684d9c625SLionel Sambuc */
11784d9c625SLionel Sambuc static void
q2e_to_quotaval(struct quota2_entry * q2e,int def,id_t * id,int objtype,struct quotaval * ret)11884d9c625SLionel Sambuc q2e_to_quotaval(struct quota2_entry *q2e, int def,
11984d9c625SLionel Sambuc id_t *id, int objtype, struct quotaval *ret)
12084d9c625SLionel Sambuc {
12184d9c625SLionel Sambuc if (def) {
12284d9c625SLionel Sambuc *id = QUOTA_DEFAULTID;
12384d9c625SLionel Sambuc } else {
12484d9c625SLionel Sambuc *id = q2e->q2e_uid;
12584d9c625SLionel Sambuc }
12684d9c625SLionel Sambuc
12784d9c625SLionel Sambuc KASSERT(objtype >= 0 && objtype < N_QL);
12884d9c625SLionel Sambuc q2val_to_quotaval(&q2e->q2e_val[objtype], ret);
12984d9c625SLionel Sambuc }
13084d9c625SLionel Sambuc
13184d9c625SLionel Sambuc
13284d9c625SLionel Sambuc static int
quota2_bwrite(struct mount * mp,struct buf * bp)13384d9c625SLionel Sambuc quota2_bwrite(struct mount *mp, struct buf *bp)
13484d9c625SLionel Sambuc {
13584d9c625SLionel Sambuc if (mp->mnt_flag & MNT_SYNCHRONOUS)
13684d9c625SLionel Sambuc return bwrite(bp);
13784d9c625SLionel Sambuc else {
13884d9c625SLionel Sambuc bdwrite(bp);
13984d9c625SLionel Sambuc return 0;
14084d9c625SLionel Sambuc }
14184d9c625SLionel Sambuc }
14284d9c625SLionel Sambuc
14384d9c625SLionel Sambuc static int
getq2h(struct ulfsmount * ump,int type,struct buf ** bpp,struct quota2_header ** q2hp,int flags)14484d9c625SLionel Sambuc getq2h(struct ulfsmount *ump, int type,
14584d9c625SLionel Sambuc struct buf **bpp, struct quota2_header **q2hp, int flags)
14684d9c625SLionel Sambuc {
14784d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
14884d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
14984d9c625SLionel Sambuc int error;
15084d9c625SLionel Sambuc struct buf *bp;
15184d9c625SLionel Sambuc struct quota2_header *q2h;
15284d9c625SLionel Sambuc
15384d9c625SLionel Sambuc KASSERT(mutex_owned(&lfs_dqlock));
154*0a6a1f1dSLionel Sambuc error = bread(ump->um_quotas[type], 0, ump->umq2_bsize, flags, &bp);
15584d9c625SLionel Sambuc if (error)
15684d9c625SLionel Sambuc return error;
15784d9c625SLionel Sambuc if (bp->b_resid != 0)
15884d9c625SLionel Sambuc panic("dq2get: %s quota file truncated", lfs_quotatypes[type]);
15984d9c625SLionel Sambuc
16084d9c625SLionel Sambuc q2h = (void *)bp->b_data;
16184d9c625SLionel Sambuc if (ulfs_rw32(q2h->q2h_magic_number, needswap) != Q2_HEAD_MAGIC ||
16284d9c625SLionel Sambuc q2h->q2h_type != type)
16384d9c625SLionel Sambuc panic("dq2get: corrupted %s quota header", lfs_quotatypes[type]);
16484d9c625SLionel Sambuc *bpp = bp;
16584d9c625SLionel Sambuc *q2hp = q2h;
16684d9c625SLionel Sambuc return 0;
16784d9c625SLionel Sambuc }
16884d9c625SLionel Sambuc
16984d9c625SLionel Sambuc static int
getq2e(struct ulfsmount * ump,int type,daddr_t lblkno,int blkoffset,struct buf ** bpp,struct quota2_entry ** q2ep,int flags)17084d9c625SLionel Sambuc getq2e(struct ulfsmount *ump, int type, daddr_t lblkno, int blkoffset,
17184d9c625SLionel Sambuc struct buf **bpp, struct quota2_entry **q2ep, int flags)
17284d9c625SLionel Sambuc {
17384d9c625SLionel Sambuc int error;
17484d9c625SLionel Sambuc struct buf *bp;
17584d9c625SLionel Sambuc
17684d9c625SLionel Sambuc if (blkoffset & (sizeof(uint64_t) - 1)) {
17784d9c625SLionel Sambuc panic("dq2get: %s quota file corrupted",
17884d9c625SLionel Sambuc lfs_quotatypes[type]);
17984d9c625SLionel Sambuc }
180*0a6a1f1dSLionel Sambuc error = bread(ump->um_quotas[type], lblkno, ump->umq2_bsize, flags, &bp);
18184d9c625SLionel Sambuc if (error)
18284d9c625SLionel Sambuc return error;
18384d9c625SLionel Sambuc if (bp->b_resid != 0) {
18484d9c625SLionel Sambuc panic("dq2get: %s quota file corrupted",
18584d9c625SLionel Sambuc lfs_quotatypes[type]);
18684d9c625SLionel Sambuc }
18784d9c625SLionel Sambuc *q2ep = (void *)((char *)bp->b_data + blkoffset);
18884d9c625SLionel Sambuc *bpp = bp;
18984d9c625SLionel Sambuc return 0;
19084d9c625SLionel Sambuc }
19184d9c625SLionel Sambuc
19284d9c625SLionel Sambuc /* walk a quota entry list, calling the callback for each entry */
19384d9c625SLionel Sambuc #define Q2WL_ABORT 0x10000000
19484d9c625SLionel Sambuc
19584d9c625SLionel Sambuc static int
quota2_walk_list(struct ulfsmount * ump,struct buf * hbp,int type,uint64_t * offp,int flags,void * a,int (* func)(struct ulfsmount *,uint64_t *,struct quota2_entry *,uint64_t,void *))19684d9c625SLionel Sambuc quota2_walk_list(struct ulfsmount *ump, struct buf *hbp, int type,
19784d9c625SLionel Sambuc uint64_t *offp, int flags, void *a,
19884d9c625SLionel Sambuc int (*func)(struct ulfsmount *, uint64_t *, struct quota2_entry *, uint64_t, void *))
19984d9c625SLionel Sambuc {
20084d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
20184d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
20284d9c625SLionel Sambuc daddr_t off = ulfs_rw64(*offp, needswap);
20384d9c625SLionel Sambuc struct buf *bp, *obp = hbp;
20484d9c625SLionel Sambuc int ret = 0, ret2 = 0;
20584d9c625SLionel Sambuc struct quota2_entry *q2e;
20684d9c625SLionel Sambuc daddr_t lblkno, blkoff, olblkno = 0;
20784d9c625SLionel Sambuc
20884d9c625SLionel Sambuc KASSERT(mutex_owner(&lfs_dqlock));
20984d9c625SLionel Sambuc
21084d9c625SLionel Sambuc while (off != 0) {
21184d9c625SLionel Sambuc lblkno = (off >> ump->um_mountp->mnt_fs_bshift);
21284d9c625SLionel Sambuc blkoff = (off & ump->umq2_bmask);
21384d9c625SLionel Sambuc if (lblkno == 0) {
21484d9c625SLionel Sambuc /* in the header block */
21584d9c625SLionel Sambuc bp = hbp;
21684d9c625SLionel Sambuc } else if (lblkno == olblkno) {
21784d9c625SLionel Sambuc /* still in the same buf */
21884d9c625SLionel Sambuc bp = obp;
21984d9c625SLionel Sambuc } else {
22084d9c625SLionel Sambuc ret = bread(ump->um_quotas[type], lblkno,
221*0a6a1f1dSLionel Sambuc ump->umq2_bsize, flags, &bp);
22284d9c625SLionel Sambuc if (ret)
22384d9c625SLionel Sambuc return ret;
22484d9c625SLionel Sambuc if (bp->b_resid != 0) {
22584d9c625SLionel Sambuc panic("quota2_walk_list: %s quota file corrupted",
22684d9c625SLionel Sambuc lfs_quotatypes[type]);
22784d9c625SLionel Sambuc }
22884d9c625SLionel Sambuc }
22984d9c625SLionel Sambuc q2e = (void *)((char *)(bp->b_data) + blkoff);
23084d9c625SLionel Sambuc ret = (*func)(ump, offp, q2e, off, a);
23184d9c625SLionel Sambuc if (off != ulfs_rw64(*offp, needswap)) {
23284d9c625SLionel Sambuc /* callback changed parent's pointer, redo */
23384d9c625SLionel Sambuc off = ulfs_rw64(*offp, needswap);
23484d9c625SLionel Sambuc if (bp != hbp && bp != obp)
23584d9c625SLionel Sambuc ret2 = bwrite(bp);
23684d9c625SLionel Sambuc } else {
23784d9c625SLionel Sambuc /* parent if now current */
23884d9c625SLionel Sambuc if (obp != bp && obp != hbp) {
23984d9c625SLionel Sambuc if (flags & B_MODIFY)
24084d9c625SLionel Sambuc ret2 = bwrite(obp);
24184d9c625SLionel Sambuc else
24284d9c625SLionel Sambuc brelse(obp, 0);
24384d9c625SLionel Sambuc }
24484d9c625SLionel Sambuc obp = bp;
24584d9c625SLionel Sambuc olblkno = lblkno;
24684d9c625SLionel Sambuc offp = &(q2e->q2e_next);
24784d9c625SLionel Sambuc off = ulfs_rw64(*offp, needswap);
24884d9c625SLionel Sambuc }
24984d9c625SLionel Sambuc if (ret)
25084d9c625SLionel Sambuc break;
25184d9c625SLionel Sambuc if (ret2) {
25284d9c625SLionel Sambuc ret = ret2;
25384d9c625SLionel Sambuc break;
25484d9c625SLionel Sambuc }
25584d9c625SLionel Sambuc }
25684d9c625SLionel Sambuc if (obp != hbp) {
25784d9c625SLionel Sambuc if (flags & B_MODIFY)
25884d9c625SLionel Sambuc ret2 = bwrite(obp);
25984d9c625SLionel Sambuc else
26084d9c625SLionel Sambuc brelse(obp, 0);
26184d9c625SLionel Sambuc }
26284d9c625SLionel Sambuc if (ret & Q2WL_ABORT)
26384d9c625SLionel Sambuc return 0;
26484d9c625SLionel Sambuc if (ret == 0)
26584d9c625SLionel Sambuc return ret2;
26684d9c625SLionel Sambuc return ret;
26784d9c625SLionel Sambuc }
26884d9c625SLionel Sambuc
26984d9c625SLionel Sambuc int
lfsquota2_umount(struct mount * mp,int flags)27084d9c625SLionel Sambuc lfsquota2_umount(struct mount *mp, int flags)
27184d9c625SLionel Sambuc {
27284d9c625SLionel Sambuc int i, error;
27384d9c625SLionel Sambuc struct ulfsmount *ump = VFSTOULFS(mp);
27484d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
27584d9c625SLionel Sambuc
27684d9c625SLionel Sambuc if ((fs->um_flags & ULFS_QUOTA2) == 0)
27784d9c625SLionel Sambuc return 0;
27884d9c625SLionel Sambuc
27984d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
28084d9c625SLionel Sambuc if (ump->um_quotas[i] != NULLVP) {
28184d9c625SLionel Sambuc error = vn_close(ump->um_quotas[i], FREAD|FWRITE,
28284d9c625SLionel Sambuc ump->um_cred[i]);
28384d9c625SLionel Sambuc if (error) {
28484d9c625SLionel Sambuc printf("quota2_umount failed: close(%p) %d\n",
28584d9c625SLionel Sambuc ump->um_quotas[i], error);
28684d9c625SLionel Sambuc return error;
28784d9c625SLionel Sambuc }
28884d9c625SLionel Sambuc }
28984d9c625SLionel Sambuc ump->um_quotas[i] = NULLVP;
29084d9c625SLionel Sambuc }
29184d9c625SLionel Sambuc return 0;
29284d9c625SLionel Sambuc }
29384d9c625SLionel Sambuc
29484d9c625SLionel Sambuc static int
quota2_q2ealloc(struct ulfsmount * ump,int type,uid_t uid,struct dquot * dq)29584d9c625SLionel Sambuc quota2_q2ealloc(struct ulfsmount *ump, int type, uid_t uid, struct dquot *dq)
29684d9c625SLionel Sambuc {
29784d9c625SLionel Sambuc int error, error2;
29884d9c625SLionel Sambuc struct buf *hbp, *bp;
29984d9c625SLionel Sambuc struct quota2_header *q2h;
30084d9c625SLionel Sambuc struct quota2_entry *q2e;
30184d9c625SLionel Sambuc daddr_t offset;
30284d9c625SLionel Sambuc u_long hash_mask;
30384d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
30484d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
30584d9c625SLionel Sambuc
30684d9c625SLionel Sambuc KASSERT(mutex_owned(&dq->dq_interlock));
30784d9c625SLionel Sambuc KASSERT(mutex_owned(&lfs_dqlock));
30884d9c625SLionel Sambuc error = getq2h(ump, type, &hbp, &q2h, B_MODIFY);
30984d9c625SLionel Sambuc if (error)
31084d9c625SLionel Sambuc return error;
31184d9c625SLionel Sambuc offset = ulfs_rw64(q2h->q2h_free, needswap);
31284d9c625SLionel Sambuc if (offset == 0) {
31384d9c625SLionel Sambuc struct vnode *vp = ump->um_quotas[type];
31484d9c625SLionel Sambuc struct inode *ip = VTOI(vp);
31584d9c625SLionel Sambuc uint64_t size = ip->i_size;
31684d9c625SLionel Sambuc /* need to alocate a new disk block */
31784d9c625SLionel Sambuc error = lfs_balloc(vp, size, ump->umq2_bsize,
31884d9c625SLionel Sambuc ump->um_cred[type], B_CLRBUF | B_SYNC, &bp);
31984d9c625SLionel Sambuc if (error) {
32084d9c625SLionel Sambuc brelse(hbp, 0);
32184d9c625SLionel Sambuc return error;
32284d9c625SLionel Sambuc }
32384d9c625SLionel Sambuc KASSERT((ip->i_size % ump->umq2_bsize) == 0);
32484d9c625SLionel Sambuc ip->i_size += ump->umq2_bsize;
32584d9c625SLionel Sambuc DIP_ASSIGN(ip, size, ip->i_size);
32684d9c625SLionel Sambuc ip->i_flag |= IN_CHANGE | IN_UPDATE;
32784d9c625SLionel Sambuc uvm_vnp_setsize(vp, ip->i_size);
32884d9c625SLionel Sambuc lfsquota2_addfreeq2e(q2h, bp->b_data, size, ump->umq2_bsize,
32984d9c625SLionel Sambuc needswap);
33084d9c625SLionel Sambuc error = bwrite(bp);
33184d9c625SLionel Sambuc error2 = lfs_update(vp, NULL, NULL, UPDATE_WAIT);
33284d9c625SLionel Sambuc if (error || error2) {
33384d9c625SLionel Sambuc brelse(hbp, 0);
33484d9c625SLionel Sambuc if (error)
33584d9c625SLionel Sambuc return error;
33684d9c625SLionel Sambuc return error2;
33784d9c625SLionel Sambuc }
33884d9c625SLionel Sambuc offset = ulfs_rw64(q2h->q2h_free, needswap);
33984d9c625SLionel Sambuc KASSERT(offset != 0);
34084d9c625SLionel Sambuc }
34184d9c625SLionel Sambuc dq->dq2_lblkno = (offset >> ump->um_mountp->mnt_fs_bshift);
34284d9c625SLionel Sambuc dq->dq2_blkoff = (offset & ump->umq2_bmask);
34384d9c625SLionel Sambuc if (dq->dq2_lblkno == 0) {
34484d9c625SLionel Sambuc bp = hbp;
34584d9c625SLionel Sambuc q2e = (void *)((char *)bp->b_data + dq->dq2_blkoff);
34684d9c625SLionel Sambuc } else {
34784d9c625SLionel Sambuc error = getq2e(ump, type, dq->dq2_lblkno,
34884d9c625SLionel Sambuc dq->dq2_blkoff, &bp, &q2e, B_MODIFY);
34984d9c625SLionel Sambuc if (error) {
35084d9c625SLionel Sambuc brelse(hbp, 0);
35184d9c625SLionel Sambuc return error;
35284d9c625SLionel Sambuc }
35384d9c625SLionel Sambuc }
35484d9c625SLionel Sambuc hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
35584d9c625SLionel Sambuc /* remove from free list */
35684d9c625SLionel Sambuc q2h->q2h_free = q2e->q2e_next;
35784d9c625SLionel Sambuc
35884d9c625SLionel Sambuc memcpy(q2e, &q2h->q2h_defentry, sizeof(*q2e));
35984d9c625SLionel Sambuc q2e->q2e_uid = ulfs_rw32(uid, needswap);
36084d9c625SLionel Sambuc /* insert in hash list */
36184d9c625SLionel Sambuc q2e->q2e_next = q2h->q2h_entries[uid & hash_mask];
36284d9c625SLionel Sambuc q2h->q2h_entries[uid & hash_mask] = ulfs_rw64(offset, needswap);
36384d9c625SLionel Sambuc if (hbp != bp) {
36484d9c625SLionel Sambuc bwrite(hbp);
36584d9c625SLionel Sambuc }
36684d9c625SLionel Sambuc bwrite(bp);
36784d9c625SLionel Sambuc return 0;
36884d9c625SLionel Sambuc }
36984d9c625SLionel Sambuc
37084d9c625SLionel Sambuc static int
getinoquota2(struct inode * ip,bool alloc,bool modify,struct buf ** bpp,struct quota2_entry ** q2ep)37184d9c625SLionel Sambuc getinoquota2(struct inode *ip, bool alloc, bool modify, struct buf **bpp,
37284d9c625SLionel Sambuc struct quota2_entry **q2ep)
37384d9c625SLionel Sambuc {
37484d9c625SLionel Sambuc int error;
37584d9c625SLionel Sambuc int i;
37684d9c625SLionel Sambuc struct dquot *dq;
37784d9c625SLionel Sambuc struct ulfsmount *ump = ip->i_ump;
37884d9c625SLionel Sambuc u_int32_t ino_ids[ULFS_MAXQUOTAS];
37984d9c625SLionel Sambuc
38084d9c625SLionel Sambuc error = lfs_getinoquota(ip);
38184d9c625SLionel Sambuc if (error)
38284d9c625SLionel Sambuc return error;
38384d9c625SLionel Sambuc
38484d9c625SLionel Sambuc ino_ids[ULFS_USRQUOTA] = ip->i_uid;
38584d9c625SLionel Sambuc ino_ids[ULFS_GRPQUOTA] = ip->i_gid;
38684d9c625SLionel Sambuc /* first get the interlock for all dquot */
38784d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
38884d9c625SLionel Sambuc dq = ip->i_dquot[i];
38984d9c625SLionel Sambuc if (dq == NODQUOT)
39084d9c625SLionel Sambuc continue;
39184d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
39284d9c625SLionel Sambuc }
39384d9c625SLionel Sambuc /* now get the corresponding quota entry */
39484d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
39584d9c625SLionel Sambuc bpp[i] = NULL;
39684d9c625SLionel Sambuc q2ep[i] = NULL;
39784d9c625SLionel Sambuc dq = ip->i_dquot[i];
39884d9c625SLionel Sambuc if (dq == NODQUOT)
39984d9c625SLionel Sambuc continue;
40084d9c625SLionel Sambuc if (__predict_false(ump->um_quotas[i] == NULL)) {
40184d9c625SLionel Sambuc /*
40284d9c625SLionel Sambuc * quotas have been turned off. This can happen
40384d9c625SLionel Sambuc * at umount time.
40484d9c625SLionel Sambuc */
40584d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
40684d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
40784d9c625SLionel Sambuc ip->i_dquot[i] = NULL;
40884d9c625SLionel Sambuc continue;
40984d9c625SLionel Sambuc }
41084d9c625SLionel Sambuc
41184d9c625SLionel Sambuc if ((dq->dq2_lblkno | dq->dq2_blkoff) == 0) {
41284d9c625SLionel Sambuc if (!alloc) {
41384d9c625SLionel Sambuc continue;
41484d9c625SLionel Sambuc }
41584d9c625SLionel Sambuc /* need to alloc a new on-disk quot */
41684d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
41784d9c625SLionel Sambuc error = quota2_q2ealloc(ump, i, ino_ids[i], dq);
41884d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
41984d9c625SLionel Sambuc if (error)
42084d9c625SLionel Sambuc return error;
42184d9c625SLionel Sambuc }
42284d9c625SLionel Sambuc KASSERT(dq->dq2_lblkno != 0 || dq->dq2_blkoff != 0);
42384d9c625SLionel Sambuc error = getq2e(ump, i, dq->dq2_lblkno,
42484d9c625SLionel Sambuc dq->dq2_blkoff, &bpp[i], &q2ep[i],
42584d9c625SLionel Sambuc modify ? B_MODIFY : 0);
42684d9c625SLionel Sambuc if (error)
42784d9c625SLionel Sambuc return error;
42884d9c625SLionel Sambuc }
42984d9c625SLionel Sambuc return 0;
43084d9c625SLionel Sambuc }
43184d9c625SLionel Sambuc
43284d9c625SLionel Sambuc __inline static int __unused
lfsquota2_check_limit(struct quota2_val * q2v,uint64_t change,time_t now)43384d9c625SLionel Sambuc lfsquota2_check_limit(struct quota2_val *q2v, uint64_t change, time_t now)
43484d9c625SLionel Sambuc {
43584d9c625SLionel Sambuc return lfsquota_check_limit(q2v->q2v_cur, change, q2v->q2v_softlimit,
43684d9c625SLionel Sambuc q2v->q2v_hardlimit, q2v->q2v_time, now);
43784d9c625SLionel Sambuc }
43884d9c625SLionel Sambuc
43984d9c625SLionel Sambuc static int
quota2_check(struct inode * ip,int vtype,int64_t change,kauth_cred_t cred,int flags)44084d9c625SLionel Sambuc quota2_check(struct inode *ip, int vtype, int64_t change, kauth_cred_t cred,
44184d9c625SLionel Sambuc int flags)
44284d9c625SLionel Sambuc {
44384d9c625SLionel Sambuc int error;
44484d9c625SLionel Sambuc struct buf *bp[ULFS_MAXQUOTAS];
44584d9c625SLionel Sambuc struct quota2_entry *q2e[ULFS_MAXQUOTAS];
44684d9c625SLionel Sambuc struct quota2_val *q2vp;
44784d9c625SLionel Sambuc struct dquot *dq;
44884d9c625SLionel Sambuc uint64_t ncurblks;
44984d9c625SLionel Sambuc struct ulfsmount *ump = ip->i_ump;
45084d9c625SLionel Sambuc struct lfs *fs = ip->i_lfs;
45184d9c625SLionel Sambuc struct mount *mp = ump->um_mountp;
45284d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
45384d9c625SLionel Sambuc int i;
45484d9c625SLionel Sambuc
45584d9c625SLionel Sambuc if ((error = getinoquota2(ip, change > 0, change != 0, bp, q2e)) != 0)
45684d9c625SLionel Sambuc return error;
45784d9c625SLionel Sambuc if (change == 0) {
45884d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
45984d9c625SLionel Sambuc dq = ip->i_dquot[i];
46084d9c625SLionel Sambuc if (dq == NODQUOT)
46184d9c625SLionel Sambuc continue;
46284d9c625SLionel Sambuc if (bp[i])
46384d9c625SLionel Sambuc brelse(bp[i], 0);
46484d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
46584d9c625SLionel Sambuc }
46684d9c625SLionel Sambuc return 0;
46784d9c625SLionel Sambuc }
46884d9c625SLionel Sambuc if (change < 0) {
46984d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
47084d9c625SLionel Sambuc dq = ip->i_dquot[i];
47184d9c625SLionel Sambuc if (dq == NODQUOT)
47284d9c625SLionel Sambuc continue;
47384d9c625SLionel Sambuc if (q2e[i] == NULL) {
47484d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
47584d9c625SLionel Sambuc continue;
47684d9c625SLionel Sambuc }
47784d9c625SLionel Sambuc q2vp = &q2e[i]->q2e_val[vtype];
47884d9c625SLionel Sambuc ncurblks = ulfs_rw64(q2vp->q2v_cur, needswap);
47984d9c625SLionel Sambuc if (ncurblks < -change)
48084d9c625SLionel Sambuc ncurblks = 0;
48184d9c625SLionel Sambuc else
48284d9c625SLionel Sambuc ncurblks += change;
48384d9c625SLionel Sambuc q2vp->q2v_cur = ulfs_rw64(ncurblks, needswap);
48484d9c625SLionel Sambuc quota2_bwrite(mp, bp[i]);
48584d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
48684d9c625SLionel Sambuc }
48784d9c625SLionel Sambuc return 0;
48884d9c625SLionel Sambuc }
48984d9c625SLionel Sambuc /* see if the allocation is allowed */
49084d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
49184d9c625SLionel Sambuc struct quota2_val q2v;
49284d9c625SLionel Sambuc int ql_stat;
49384d9c625SLionel Sambuc dq = ip->i_dquot[i];
49484d9c625SLionel Sambuc if (dq == NODQUOT)
49584d9c625SLionel Sambuc continue;
49684d9c625SLionel Sambuc KASSERT(q2e[i] != NULL);
49784d9c625SLionel Sambuc lfsquota2_ulfs_rwq2v(&q2e[i]->q2e_val[vtype], &q2v, needswap);
49884d9c625SLionel Sambuc ql_stat = lfsquota2_check_limit(&q2v, change, time_second);
49984d9c625SLionel Sambuc
50084d9c625SLionel Sambuc if ((flags & FORCE) == 0 &&
50184d9c625SLionel Sambuc kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
50284d9c625SLionel Sambuc KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
50384d9c625SLionel Sambuc KAUTH_ARG(i), KAUTH_ARG(vtype), NULL) != 0) {
50484d9c625SLionel Sambuc /* enforce this limit */
50584d9c625SLionel Sambuc switch(QL_STATUS(ql_stat)) {
50684d9c625SLionel Sambuc case QL_S_DENY_HARD:
50784d9c625SLionel Sambuc if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
50884d9c625SLionel Sambuc uprintf("\n%s: write failed, %s %s "
50984d9c625SLionel Sambuc "limit reached\n",
51084d9c625SLionel Sambuc mp->mnt_stat.f_mntonname,
51184d9c625SLionel Sambuc lfs_quotatypes[i], limnames[vtype]);
51284d9c625SLionel Sambuc dq->dq_flags |= DQ_WARN(vtype);
51384d9c625SLionel Sambuc }
51484d9c625SLionel Sambuc error = EDQUOT;
51584d9c625SLionel Sambuc break;
51684d9c625SLionel Sambuc case QL_S_DENY_GRACE:
51784d9c625SLionel Sambuc if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
51884d9c625SLionel Sambuc uprintf("\n%s: write failed, %s %s "
51984d9c625SLionel Sambuc "limit reached\n",
52084d9c625SLionel Sambuc mp->mnt_stat.f_mntonname,
52184d9c625SLionel Sambuc lfs_quotatypes[i], limnames[vtype]);
52284d9c625SLionel Sambuc dq->dq_flags |= DQ_WARN(vtype);
52384d9c625SLionel Sambuc }
52484d9c625SLionel Sambuc error = EDQUOT;
52584d9c625SLionel Sambuc break;
52684d9c625SLionel Sambuc case QL_S_ALLOW_SOFT:
52784d9c625SLionel Sambuc if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
52884d9c625SLionel Sambuc uprintf("\n%s: warning, %s %s "
52984d9c625SLionel Sambuc "quota exceeded\n",
53084d9c625SLionel Sambuc mp->mnt_stat.f_mntonname,
53184d9c625SLionel Sambuc lfs_quotatypes[i], limnames[vtype]);
53284d9c625SLionel Sambuc dq->dq_flags |= DQ_WARN(vtype);
53384d9c625SLionel Sambuc }
53484d9c625SLionel Sambuc break;
53584d9c625SLionel Sambuc }
53684d9c625SLionel Sambuc }
53784d9c625SLionel Sambuc /*
53884d9c625SLionel Sambuc * always do this; we don't know if the allocation will
53984d9c625SLionel Sambuc * succed or not in the end. if we don't do the allocation
54084d9c625SLionel Sambuc * q2v_time will be ignored anyway
54184d9c625SLionel Sambuc */
54284d9c625SLionel Sambuc if (ql_stat & QL_F_CROSS) {
54384d9c625SLionel Sambuc q2v.q2v_time = time_second + q2v.q2v_grace;
54484d9c625SLionel Sambuc lfsquota2_ulfs_rwq2v(&q2v, &q2e[i]->q2e_val[vtype],
54584d9c625SLionel Sambuc needswap);
54684d9c625SLionel Sambuc }
54784d9c625SLionel Sambuc }
54884d9c625SLionel Sambuc
54984d9c625SLionel Sambuc /* now do the allocation if allowed */
55084d9c625SLionel Sambuc for (i = 0; i < ULFS_MAXQUOTAS; i++) {
55184d9c625SLionel Sambuc dq = ip->i_dquot[i];
55284d9c625SLionel Sambuc if (dq == NODQUOT)
55384d9c625SLionel Sambuc continue;
55484d9c625SLionel Sambuc KASSERT(q2e[i] != NULL);
55584d9c625SLionel Sambuc if (error == 0) {
55684d9c625SLionel Sambuc q2vp = &q2e[i]->q2e_val[vtype];
55784d9c625SLionel Sambuc ncurblks = ulfs_rw64(q2vp->q2v_cur, needswap);
55884d9c625SLionel Sambuc q2vp->q2v_cur = ulfs_rw64(ncurblks + change, needswap);
55984d9c625SLionel Sambuc quota2_bwrite(mp, bp[i]);
56084d9c625SLionel Sambuc } else
56184d9c625SLionel Sambuc brelse(bp[i], 0);
56284d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
56384d9c625SLionel Sambuc }
56484d9c625SLionel Sambuc return error;
56584d9c625SLionel Sambuc }
56684d9c625SLionel Sambuc
56784d9c625SLionel Sambuc int
lfs_chkdq2(struct inode * ip,int64_t change,kauth_cred_t cred,int flags)56884d9c625SLionel Sambuc lfs_chkdq2(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
56984d9c625SLionel Sambuc {
57084d9c625SLionel Sambuc return quota2_check(ip, QL_BLOCK, change, cred, flags);
57184d9c625SLionel Sambuc }
57284d9c625SLionel Sambuc
57384d9c625SLionel Sambuc int
lfs_chkiq2(struct inode * ip,int32_t change,kauth_cred_t cred,int flags)57484d9c625SLionel Sambuc lfs_chkiq2(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
57584d9c625SLionel Sambuc {
57684d9c625SLionel Sambuc return quota2_check(ip, QL_FILE, change, cred, flags);
57784d9c625SLionel Sambuc }
57884d9c625SLionel Sambuc
57984d9c625SLionel Sambuc int
lfsquota2_handle_cmd_put(struct ulfsmount * ump,const struct quotakey * key,const struct quotaval * val)58084d9c625SLionel Sambuc lfsquota2_handle_cmd_put(struct ulfsmount *ump, const struct quotakey *key,
58184d9c625SLionel Sambuc const struct quotaval *val)
58284d9c625SLionel Sambuc {
58384d9c625SLionel Sambuc int error;
58484d9c625SLionel Sambuc struct dquot *dq;
58584d9c625SLionel Sambuc struct quota2_header *q2h;
58684d9c625SLionel Sambuc struct quota2_entry q2e, *q2ep;
58784d9c625SLionel Sambuc struct buf *bp;
58884d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
58984d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
59084d9c625SLionel Sambuc
59184d9c625SLionel Sambuc /* make sure we can index by the fs-independent idtype */
59284d9c625SLionel Sambuc CTASSERT(QUOTA_IDTYPE_USER == ULFS_USRQUOTA);
59384d9c625SLionel Sambuc CTASSERT(QUOTA_IDTYPE_GROUP == ULFS_GRPQUOTA);
59484d9c625SLionel Sambuc
59584d9c625SLionel Sambuc if (ump->um_quotas[key->qk_idtype] == NULLVP)
59684d9c625SLionel Sambuc return ENODEV;
59784d9c625SLionel Sambuc
59884d9c625SLionel Sambuc if (key->qk_id == QUOTA_DEFAULTID) {
59984d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
60084d9c625SLionel Sambuc error = getq2h(ump, key->qk_idtype, &bp, &q2h, B_MODIFY);
60184d9c625SLionel Sambuc if (error) {
60284d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
60384d9c625SLionel Sambuc goto out_wapbl;
60484d9c625SLionel Sambuc }
60584d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
60684d9c625SLionel Sambuc quota2_dict_update_q2e_limits(key->qk_objtype, val, &q2e);
60784d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(&q2e, &q2h->q2h_defentry, needswap);
60884d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
60984d9c625SLionel Sambuc quota2_bwrite(ump->um_mountp, bp);
61084d9c625SLionel Sambuc goto out_wapbl;
61184d9c625SLionel Sambuc }
61284d9c625SLionel Sambuc
61384d9c625SLionel Sambuc error = lfs_dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq);
61484d9c625SLionel Sambuc if (error)
61584d9c625SLionel Sambuc goto out_wapbl;
61684d9c625SLionel Sambuc
61784d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
61884d9c625SLionel Sambuc if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
61984d9c625SLionel Sambuc /* need to alloc a new on-disk quot */
62084d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
62184d9c625SLionel Sambuc error = quota2_q2ealloc(ump, key->qk_idtype, key->qk_id, dq);
62284d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
62384d9c625SLionel Sambuc if (error)
62484d9c625SLionel Sambuc goto out_il;
62584d9c625SLionel Sambuc }
62684d9c625SLionel Sambuc KASSERT(dq->dq2_lblkno != 0 || dq->dq2_blkoff != 0);
62784d9c625SLionel Sambuc error = getq2e(ump, key->qk_idtype, dq->dq2_lblkno,
62884d9c625SLionel Sambuc dq->dq2_blkoff, &bp, &q2ep, B_MODIFY);
62984d9c625SLionel Sambuc if (error)
63084d9c625SLionel Sambuc goto out_il;
63184d9c625SLionel Sambuc
63284d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(q2ep, &q2e, needswap);
63384d9c625SLionel Sambuc quota2_dict_update_q2e_limits(key->qk_objtype, val, &q2e);
63484d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(&q2e, q2ep, needswap);
63584d9c625SLionel Sambuc quota2_bwrite(ump->um_mountp, bp);
63684d9c625SLionel Sambuc
63784d9c625SLionel Sambuc out_il:
63884d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
63984d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
64084d9c625SLionel Sambuc out_wapbl:
64184d9c625SLionel Sambuc return error;
64284d9c625SLionel Sambuc }
64384d9c625SLionel Sambuc
64484d9c625SLionel Sambuc struct dq2clear_callback {
64584d9c625SLionel Sambuc uid_t id;
64684d9c625SLionel Sambuc struct dquot *dq;
64784d9c625SLionel Sambuc struct quota2_header *q2h;
64884d9c625SLionel Sambuc };
64984d9c625SLionel Sambuc
65084d9c625SLionel Sambuc static int
dq2clear_callback(struct ulfsmount * ump,uint64_t * offp,struct quota2_entry * q2e,uint64_t off,void * v)65184d9c625SLionel Sambuc dq2clear_callback(struct ulfsmount *ump, uint64_t *offp, struct quota2_entry *q2e,
65284d9c625SLionel Sambuc uint64_t off, void *v)
65384d9c625SLionel Sambuc {
65484d9c625SLionel Sambuc struct dq2clear_callback *c = v;
65584d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
65684d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
65784d9c625SLionel Sambuc uint64_t myoff;
65884d9c625SLionel Sambuc
65984d9c625SLionel Sambuc if (ulfs_rw32(q2e->q2e_uid, needswap) == c->id) {
66084d9c625SLionel Sambuc KASSERT(mutex_owned(&c->dq->dq_interlock));
66184d9c625SLionel Sambuc c->dq->dq2_lblkno = 0;
66284d9c625SLionel Sambuc c->dq->dq2_blkoff = 0;
66384d9c625SLionel Sambuc myoff = *offp;
66484d9c625SLionel Sambuc /* remove from hash list */
66584d9c625SLionel Sambuc *offp = q2e->q2e_next;
66684d9c625SLionel Sambuc /* add to free list */
66784d9c625SLionel Sambuc q2e->q2e_next = c->q2h->q2h_free;
66884d9c625SLionel Sambuc c->q2h->q2h_free = myoff;
66984d9c625SLionel Sambuc return Q2WL_ABORT;
67084d9c625SLionel Sambuc }
67184d9c625SLionel Sambuc return 0;
67284d9c625SLionel Sambuc }
67384d9c625SLionel Sambuc int
lfsquota2_handle_cmd_del(struct ulfsmount * ump,const struct quotakey * qk)674*0a6a1f1dSLionel Sambuc lfsquota2_handle_cmd_del(struct ulfsmount *ump, const struct quotakey *qk)
67584d9c625SLionel Sambuc {
67684d9c625SLionel Sambuc int idtype;
67784d9c625SLionel Sambuc id_t id;
67884d9c625SLionel Sambuc int objtype;
67984d9c625SLionel Sambuc int error, i, canfree;
68084d9c625SLionel Sambuc struct dquot *dq;
68184d9c625SLionel Sambuc struct quota2_header *q2h;
68284d9c625SLionel Sambuc struct quota2_entry q2e, *q2ep;
68384d9c625SLionel Sambuc struct buf *hbp, *bp;
68484d9c625SLionel Sambuc u_long hash_mask;
68584d9c625SLionel Sambuc struct dq2clear_callback c;
68684d9c625SLionel Sambuc
68784d9c625SLionel Sambuc idtype = qk->qk_idtype;
68884d9c625SLionel Sambuc id = qk->qk_id;
68984d9c625SLionel Sambuc objtype = qk->qk_objtype;
69084d9c625SLionel Sambuc
69184d9c625SLionel Sambuc if (ump->um_quotas[idtype] == NULLVP)
69284d9c625SLionel Sambuc return ENODEV;
69384d9c625SLionel Sambuc if (id == QUOTA_DEFAULTID)
69484d9c625SLionel Sambuc return EOPNOTSUPP;
69584d9c625SLionel Sambuc
69684d9c625SLionel Sambuc /* get the default entry before locking the entry's buffer */
69784d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
69884d9c625SLionel Sambuc error = getq2h(ump, idtype, &hbp, &q2h, 0);
69984d9c625SLionel Sambuc if (error) {
70084d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
70184d9c625SLionel Sambuc return error;
70284d9c625SLionel Sambuc }
70384d9c625SLionel Sambuc /* we'll copy to another disk entry, so no need to swap */
70484d9c625SLionel Sambuc memcpy(&q2e, &q2h->q2h_defentry, sizeof(q2e));
70584d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
70684d9c625SLionel Sambuc brelse(hbp, 0);
70784d9c625SLionel Sambuc
70884d9c625SLionel Sambuc error = lfs_dqget(NULLVP, id, ump, idtype, &dq);
70984d9c625SLionel Sambuc if (error)
71084d9c625SLionel Sambuc return error;
71184d9c625SLionel Sambuc
71284d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
71384d9c625SLionel Sambuc if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
71484d9c625SLionel Sambuc /* already clear, nothing to do */
71584d9c625SLionel Sambuc error = ENOENT;
71684d9c625SLionel Sambuc goto out_il;
71784d9c625SLionel Sambuc }
71884d9c625SLionel Sambuc
71984d9c625SLionel Sambuc error = getq2e(ump, idtype, dq->dq2_lblkno, dq->dq2_blkoff,
72084d9c625SLionel Sambuc &bp, &q2ep, B_MODIFY);
72184d9c625SLionel Sambuc if (error)
72284d9c625SLionel Sambuc goto out_wapbl;
72384d9c625SLionel Sambuc
72484d9c625SLionel Sambuc /* make sure we can index by the objtype passed in */
72584d9c625SLionel Sambuc CTASSERT(QUOTA_OBJTYPE_BLOCKS == QL_BLOCK);
72684d9c625SLionel Sambuc CTASSERT(QUOTA_OBJTYPE_FILES == QL_FILE);
72784d9c625SLionel Sambuc
72884d9c625SLionel Sambuc /* clear the requested objtype by copying from the default entry */
72984d9c625SLionel Sambuc q2ep->q2e_val[objtype].q2v_softlimit =
73084d9c625SLionel Sambuc q2e.q2e_val[objtype].q2v_softlimit;
73184d9c625SLionel Sambuc q2ep->q2e_val[objtype].q2v_hardlimit =
73284d9c625SLionel Sambuc q2e.q2e_val[objtype].q2v_hardlimit;
73384d9c625SLionel Sambuc q2ep->q2e_val[objtype].q2v_grace =
73484d9c625SLionel Sambuc q2e.q2e_val[objtype].q2v_grace;
73584d9c625SLionel Sambuc q2ep->q2e_val[objtype].q2v_time = 0;
73684d9c625SLionel Sambuc
73784d9c625SLionel Sambuc /* if this entry now contains no information, we can free it */
73884d9c625SLionel Sambuc canfree = 1;
73984d9c625SLionel Sambuc for (i = 0; i < N_QL; i++) {
74084d9c625SLionel Sambuc if (q2ep->q2e_val[i].q2v_cur != 0 ||
74184d9c625SLionel Sambuc (q2ep->q2e_val[i].q2v_softlimit !=
74284d9c625SLionel Sambuc q2e.q2e_val[i].q2v_softlimit) ||
74384d9c625SLionel Sambuc (q2ep->q2e_val[i].q2v_hardlimit !=
74484d9c625SLionel Sambuc q2e.q2e_val[i].q2v_hardlimit) ||
74584d9c625SLionel Sambuc (q2ep->q2e_val[i].q2v_grace !=
74684d9c625SLionel Sambuc q2e.q2e_val[i].q2v_grace)) {
74784d9c625SLionel Sambuc canfree = 0;
74884d9c625SLionel Sambuc break;
74984d9c625SLionel Sambuc }
75084d9c625SLionel Sambuc /* note: do not need to check q2v_time */
75184d9c625SLionel Sambuc }
75284d9c625SLionel Sambuc
75384d9c625SLionel Sambuc if (canfree == 0) {
75484d9c625SLionel Sambuc quota2_bwrite(ump->um_mountp, bp);
75584d9c625SLionel Sambuc goto out_wapbl;
75684d9c625SLionel Sambuc }
75784d9c625SLionel Sambuc /* we can free it. release bp so we can walk the list */
75884d9c625SLionel Sambuc brelse(bp, 0);
75984d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
76084d9c625SLionel Sambuc error = getq2h(ump, idtype, &hbp, &q2h, 0);
76184d9c625SLionel Sambuc if (error)
76284d9c625SLionel Sambuc goto out_dqlock;
76384d9c625SLionel Sambuc
76484d9c625SLionel Sambuc hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
76584d9c625SLionel Sambuc c.dq = dq;
76684d9c625SLionel Sambuc c.id = id;
76784d9c625SLionel Sambuc c.q2h = q2h;
76884d9c625SLionel Sambuc error = quota2_walk_list(ump, hbp, idtype,
76984d9c625SLionel Sambuc &q2h->q2h_entries[id & hash_mask], B_MODIFY, &c,
77084d9c625SLionel Sambuc dq2clear_callback);
77184d9c625SLionel Sambuc
77284d9c625SLionel Sambuc bwrite(hbp);
77384d9c625SLionel Sambuc
77484d9c625SLionel Sambuc out_dqlock:
77584d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
77684d9c625SLionel Sambuc out_wapbl:
77784d9c625SLionel Sambuc out_il:
77884d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
77984d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
78084d9c625SLionel Sambuc return error;
78184d9c625SLionel Sambuc }
78284d9c625SLionel Sambuc
78384d9c625SLionel Sambuc static int
quota2_fetch_q2e(struct ulfsmount * ump,const struct quotakey * qk,struct quota2_entry * ret)78484d9c625SLionel Sambuc quota2_fetch_q2e(struct ulfsmount *ump, const struct quotakey *qk,
78584d9c625SLionel Sambuc struct quota2_entry *ret)
78684d9c625SLionel Sambuc {
78784d9c625SLionel Sambuc struct dquot *dq;
78884d9c625SLionel Sambuc int error;
78984d9c625SLionel Sambuc struct quota2_entry *q2ep;
79084d9c625SLionel Sambuc struct buf *bp;
79184d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
79284d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
79384d9c625SLionel Sambuc
79484d9c625SLionel Sambuc error = lfs_dqget(NULLVP, qk->qk_id, ump, qk->qk_idtype, &dq);
79584d9c625SLionel Sambuc if (error)
79684d9c625SLionel Sambuc return error;
79784d9c625SLionel Sambuc
79884d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
79984d9c625SLionel Sambuc if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
80084d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
80184d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
80284d9c625SLionel Sambuc return ENOENT;
80384d9c625SLionel Sambuc }
80484d9c625SLionel Sambuc error = getq2e(ump, qk->qk_idtype, dq->dq2_lblkno, dq->dq2_blkoff,
80584d9c625SLionel Sambuc &bp, &q2ep, 0);
80684d9c625SLionel Sambuc if (error) {
80784d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
80884d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
80984d9c625SLionel Sambuc return error;
81084d9c625SLionel Sambuc }
81184d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(q2ep, ret, needswap);
81284d9c625SLionel Sambuc brelse(bp, 0);
81384d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
81484d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
81584d9c625SLionel Sambuc
81684d9c625SLionel Sambuc return 0;
81784d9c625SLionel Sambuc }
81884d9c625SLionel Sambuc
81984d9c625SLionel Sambuc static int
quota2_fetch_quotaval(struct ulfsmount * ump,const struct quotakey * qk,struct quotaval * ret)82084d9c625SLionel Sambuc quota2_fetch_quotaval(struct ulfsmount *ump, const struct quotakey *qk,
82184d9c625SLionel Sambuc struct quotaval *ret)
82284d9c625SLionel Sambuc {
82384d9c625SLionel Sambuc struct dquot *dq;
82484d9c625SLionel Sambuc int error;
82584d9c625SLionel Sambuc struct quota2_entry *q2ep, q2e;
82684d9c625SLionel Sambuc struct buf *bp;
82784d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
82884d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
82984d9c625SLionel Sambuc id_t id2;
83084d9c625SLionel Sambuc
83184d9c625SLionel Sambuc error = lfs_dqget(NULLVP, qk->qk_id, ump, qk->qk_idtype, &dq);
83284d9c625SLionel Sambuc if (error)
83384d9c625SLionel Sambuc return error;
83484d9c625SLionel Sambuc
83584d9c625SLionel Sambuc mutex_enter(&dq->dq_interlock);
83684d9c625SLionel Sambuc if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
83784d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
83884d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
83984d9c625SLionel Sambuc return ENOENT;
84084d9c625SLionel Sambuc }
84184d9c625SLionel Sambuc error = getq2e(ump, qk->qk_idtype, dq->dq2_lblkno, dq->dq2_blkoff,
84284d9c625SLionel Sambuc &bp, &q2ep, 0);
84384d9c625SLionel Sambuc if (error) {
84484d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
84584d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
84684d9c625SLionel Sambuc return error;
84784d9c625SLionel Sambuc }
84884d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(q2ep, &q2e, needswap);
84984d9c625SLionel Sambuc brelse(bp, 0);
85084d9c625SLionel Sambuc mutex_exit(&dq->dq_interlock);
85184d9c625SLionel Sambuc lfs_dqrele(NULLVP, dq);
85284d9c625SLionel Sambuc
85384d9c625SLionel Sambuc q2e_to_quotaval(&q2e, 0, &id2, qk->qk_objtype, ret);
85484d9c625SLionel Sambuc KASSERT(id2 == qk->qk_id);
85584d9c625SLionel Sambuc return 0;
85684d9c625SLionel Sambuc }
85784d9c625SLionel Sambuc
85884d9c625SLionel Sambuc int
lfsquota2_handle_cmd_get(struct ulfsmount * ump,const struct quotakey * qk,struct quotaval * qv)85984d9c625SLionel Sambuc lfsquota2_handle_cmd_get(struct ulfsmount *ump, const struct quotakey *qk,
86084d9c625SLionel Sambuc struct quotaval *qv)
86184d9c625SLionel Sambuc {
86284d9c625SLionel Sambuc int error;
86384d9c625SLionel Sambuc struct quota2_header *q2h;
86484d9c625SLionel Sambuc struct quota2_entry q2e;
86584d9c625SLionel Sambuc struct buf *bp;
86684d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
86784d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
86884d9c625SLionel Sambuc id_t id2;
86984d9c625SLionel Sambuc
87084d9c625SLionel Sambuc /*
87184d9c625SLionel Sambuc * Make sure the FS-independent codes match the internal ones,
87284d9c625SLionel Sambuc * so we can use the passed-in objtype without having to
87384d9c625SLionel Sambuc * convert it explicitly to QL_BLOCK/QL_FILE.
87484d9c625SLionel Sambuc */
87584d9c625SLionel Sambuc CTASSERT(QL_BLOCK == QUOTA_OBJTYPE_BLOCKS);
87684d9c625SLionel Sambuc CTASSERT(QL_FILE == QUOTA_OBJTYPE_FILES);
87784d9c625SLionel Sambuc CTASSERT(N_QL == 2);
87884d9c625SLionel Sambuc
87984d9c625SLionel Sambuc if (qk->qk_objtype < 0 || qk->qk_objtype >= N_QL) {
88084d9c625SLionel Sambuc return EINVAL;
88184d9c625SLionel Sambuc }
88284d9c625SLionel Sambuc
88384d9c625SLionel Sambuc if (ump->um_quotas[qk->qk_idtype] == NULLVP)
88484d9c625SLionel Sambuc return ENODEV;
88584d9c625SLionel Sambuc if (qk->qk_id == QUOTA_DEFAULTID) {
88684d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
88784d9c625SLionel Sambuc error = getq2h(ump, qk->qk_idtype, &bp, &q2h, 0);
88884d9c625SLionel Sambuc if (error) {
88984d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
89084d9c625SLionel Sambuc return error;
89184d9c625SLionel Sambuc }
89284d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
89384d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
89484d9c625SLionel Sambuc brelse(bp, 0);
89584d9c625SLionel Sambuc q2e_to_quotaval(&q2e, qk->qk_id == QUOTA_DEFAULTID, &id2,
89684d9c625SLionel Sambuc qk->qk_objtype, qv);
89784d9c625SLionel Sambuc (void)id2;
89884d9c625SLionel Sambuc } else
89984d9c625SLionel Sambuc error = quota2_fetch_quotaval(ump, qk, qv);
90084d9c625SLionel Sambuc
90184d9c625SLionel Sambuc return error;
90284d9c625SLionel Sambuc }
90384d9c625SLionel Sambuc
90484d9c625SLionel Sambuc /*
90584d9c625SLionel Sambuc * Cursor structure we used.
90684d9c625SLionel Sambuc *
90784d9c625SLionel Sambuc * This will get stored in userland between calls so we must not assume
90884d9c625SLionel Sambuc * it isn't arbitrarily corrupted.
90984d9c625SLionel Sambuc */
91084d9c625SLionel Sambuc struct ulfsq2_cursor {
91184d9c625SLionel Sambuc uint32_t q2c_magic; /* magic number */
91284d9c625SLionel Sambuc int q2c_hashsize; /* size of hash table at last go */
91384d9c625SLionel Sambuc
91484d9c625SLionel Sambuc int q2c_users_done; /* true if we've returned all user data */
91584d9c625SLionel Sambuc int q2c_groups_done; /* true if we've returned all group data */
91684d9c625SLionel Sambuc int q2c_defaults_done; /* true if we've returned the default values */
91784d9c625SLionel Sambuc int q2c_hashpos; /* slot to start at in hash table */
91884d9c625SLionel Sambuc int q2c_uidpos; /* number of ids we've handled */
91984d9c625SLionel Sambuc int q2c_blocks_done; /* true if we've returned the blocks value */
92084d9c625SLionel Sambuc };
92184d9c625SLionel Sambuc
92284d9c625SLionel Sambuc /*
92384d9c625SLionel Sambuc * State of a single cursorget call, or at least the part of it that
92484d9c625SLionel Sambuc * needs to be passed around.
92584d9c625SLionel Sambuc */
92684d9c625SLionel Sambuc struct q2cursor_state {
92784d9c625SLionel Sambuc /* data return pointers */
92884d9c625SLionel Sambuc struct quotakey *keys;
92984d9c625SLionel Sambuc struct quotaval *vals;
93084d9c625SLionel Sambuc
93184d9c625SLionel Sambuc /* key/value counters */
93284d9c625SLionel Sambuc unsigned maxkeyvals;
93384d9c625SLionel Sambuc unsigned numkeys; /* number of keys assigned */
93484d9c625SLionel Sambuc
93584d9c625SLionel Sambuc /* ID to key/value conversion state */
93684d9c625SLionel Sambuc int skipfirst; /* if true skip first key/value */
93784d9c625SLionel Sambuc int skiplast; /* if true skip last key/value */
93884d9c625SLionel Sambuc
93984d9c625SLionel Sambuc /* ID counters */
94084d9c625SLionel Sambuc unsigned maxids; /* maximum number of IDs to handle */
94184d9c625SLionel Sambuc unsigned numids; /* number of IDs handled */
94284d9c625SLionel Sambuc };
94384d9c625SLionel Sambuc
94484d9c625SLionel Sambuc /*
94584d9c625SLionel Sambuc * Additional structure for getids callback.
94684d9c625SLionel Sambuc */
94784d9c625SLionel Sambuc struct q2cursor_getids {
94884d9c625SLionel Sambuc struct q2cursor_state *state;
94984d9c625SLionel Sambuc int idtype;
95084d9c625SLionel Sambuc unsigned skip; /* number of ids to skip over */
95184d9c625SLionel Sambuc unsigned new_skip; /* number of ids to skip over next time */
95284d9c625SLionel Sambuc unsigned skipped; /* number skipped so far */
95384d9c625SLionel Sambuc int stopped; /* true if we stopped quota_walk_list early */
95484d9c625SLionel Sambuc };
95584d9c625SLionel Sambuc
95684d9c625SLionel Sambuc /*
95784d9c625SLionel Sambuc * Cursor-related functions
95884d9c625SLionel Sambuc */
95984d9c625SLionel Sambuc
96084d9c625SLionel Sambuc /* magic number */
96184d9c625SLionel Sambuc #define Q2C_MAGIC (0xbeebe111)
96284d9c625SLionel Sambuc
96384d9c625SLionel Sambuc /* extract cursor from caller form */
96484d9c625SLionel Sambuc #define Q2CURSOR(qkc) ((struct ulfsq2_cursor *)&qkc->u.qkc_space[0])
96584d9c625SLionel Sambuc
96684d9c625SLionel Sambuc /*
96784d9c625SLionel Sambuc * Check that a cursor we're handed is something like valid. If
96884d9c625SLionel Sambuc * someone munges it and it still passes these checks, they'll get
96984d9c625SLionel Sambuc * partial or odd results back but won't break anything.
97084d9c625SLionel Sambuc */
97184d9c625SLionel Sambuc static int
q2cursor_check(struct ulfsq2_cursor * cursor)97284d9c625SLionel Sambuc q2cursor_check(struct ulfsq2_cursor *cursor)
97384d9c625SLionel Sambuc {
97484d9c625SLionel Sambuc if (cursor->q2c_magic != Q2C_MAGIC) {
97584d9c625SLionel Sambuc return EINVAL;
97684d9c625SLionel Sambuc }
97784d9c625SLionel Sambuc if (cursor->q2c_hashsize < 0) {
97884d9c625SLionel Sambuc return EINVAL;
97984d9c625SLionel Sambuc }
98084d9c625SLionel Sambuc
98184d9c625SLionel Sambuc if (cursor->q2c_users_done != 0 && cursor->q2c_users_done != 1) {
98284d9c625SLionel Sambuc return EINVAL;
98384d9c625SLionel Sambuc }
98484d9c625SLionel Sambuc if (cursor->q2c_groups_done != 0 && cursor->q2c_groups_done != 1) {
98584d9c625SLionel Sambuc return EINVAL;
98684d9c625SLionel Sambuc }
98784d9c625SLionel Sambuc if (cursor->q2c_defaults_done != 0 && cursor->q2c_defaults_done != 1) {
98884d9c625SLionel Sambuc return EINVAL;
98984d9c625SLionel Sambuc }
99084d9c625SLionel Sambuc if (cursor->q2c_hashpos < 0 || cursor->q2c_uidpos < 0) {
99184d9c625SLionel Sambuc return EINVAL;
99284d9c625SLionel Sambuc }
99384d9c625SLionel Sambuc if (cursor->q2c_blocks_done != 0 && cursor->q2c_blocks_done != 1) {
99484d9c625SLionel Sambuc return EINVAL;
99584d9c625SLionel Sambuc }
99684d9c625SLionel Sambuc return 0;
99784d9c625SLionel Sambuc }
99884d9c625SLionel Sambuc
99984d9c625SLionel Sambuc /*
100084d9c625SLionel Sambuc * Set up the q2cursor state.
100184d9c625SLionel Sambuc */
100284d9c625SLionel Sambuc static void
q2cursor_initstate(struct q2cursor_state * state,struct quotakey * keys,struct quotaval * vals,unsigned maxkeyvals,int blocks_done)100384d9c625SLionel Sambuc q2cursor_initstate(struct q2cursor_state *state, struct quotakey *keys,
100484d9c625SLionel Sambuc struct quotaval *vals, unsigned maxkeyvals, int blocks_done)
100584d9c625SLionel Sambuc {
100684d9c625SLionel Sambuc state->keys = keys;
100784d9c625SLionel Sambuc state->vals = vals;
100884d9c625SLionel Sambuc
100984d9c625SLionel Sambuc state->maxkeyvals = maxkeyvals;
101084d9c625SLionel Sambuc state->numkeys = 0;
101184d9c625SLionel Sambuc
101284d9c625SLionel Sambuc /*
101384d9c625SLionel Sambuc * For each ID there are two quotavals to return. If the
101484d9c625SLionel Sambuc * maximum number of entries to return is odd, we might want
101584d9c625SLionel Sambuc * to skip the first quotaval of the first ID, or the last
101684d9c625SLionel Sambuc * quotaval of the last ID, but not both. So the number of IDs
101784d9c625SLionel Sambuc * we want is (up to) half the number of return slots we have,
101884d9c625SLionel Sambuc * rounded up.
101984d9c625SLionel Sambuc */
102084d9c625SLionel Sambuc
102184d9c625SLionel Sambuc state->maxids = (state->maxkeyvals + 1) / 2;
102284d9c625SLionel Sambuc state->numids = 0;
102384d9c625SLionel Sambuc if (state->maxkeyvals % 2) {
102484d9c625SLionel Sambuc if (blocks_done) {
102584d9c625SLionel Sambuc state->skipfirst = 1;
102684d9c625SLionel Sambuc state->skiplast = 0;
102784d9c625SLionel Sambuc } else {
102884d9c625SLionel Sambuc state->skipfirst = 0;
102984d9c625SLionel Sambuc state->skiplast = 1;
103084d9c625SLionel Sambuc }
103184d9c625SLionel Sambuc } else {
103284d9c625SLionel Sambuc state->skipfirst = 0;
103384d9c625SLionel Sambuc state->skiplast = 0;
103484d9c625SLionel Sambuc }
103584d9c625SLionel Sambuc }
103684d9c625SLionel Sambuc
103784d9c625SLionel Sambuc /*
103884d9c625SLionel Sambuc * Choose which idtype we're going to work on. If doing a full
103984d9c625SLionel Sambuc * iteration, we do users first, then groups, but either might be
104084d9c625SLionel Sambuc * disabled or marked to skip via cursorsetidtype(), so don't make
104184d9c625SLionel Sambuc * silly assumptions.
104284d9c625SLionel Sambuc */
104384d9c625SLionel Sambuc static int
q2cursor_pickidtype(struct ulfsq2_cursor * cursor,int * idtype_ret)104484d9c625SLionel Sambuc q2cursor_pickidtype(struct ulfsq2_cursor *cursor, int *idtype_ret)
104584d9c625SLionel Sambuc {
104684d9c625SLionel Sambuc if (cursor->q2c_users_done == 0) {
104784d9c625SLionel Sambuc *idtype_ret = QUOTA_IDTYPE_USER;
104884d9c625SLionel Sambuc } else if (cursor->q2c_groups_done == 0) {
104984d9c625SLionel Sambuc *idtype_ret = QUOTA_IDTYPE_GROUP;
105084d9c625SLionel Sambuc } else {
105184d9c625SLionel Sambuc return EAGAIN;
105284d9c625SLionel Sambuc }
105384d9c625SLionel Sambuc return 0;
105484d9c625SLionel Sambuc }
105584d9c625SLionel Sambuc
105684d9c625SLionel Sambuc /*
105784d9c625SLionel Sambuc * Add an ID to the current state. Sets up either one or two keys to
105884d9c625SLionel Sambuc * refer to it, depending on whether it's first/last and the setting
105984d9c625SLionel Sambuc * of skipfirst. (skiplast does not need to be explicitly tested)
106084d9c625SLionel Sambuc */
106184d9c625SLionel Sambuc static void
q2cursor_addid(struct q2cursor_state * state,int idtype,id_t id)106284d9c625SLionel Sambuc q2cursor_addid(struct q2cursor_state *state, int idtype, id_t id)
106384d9c625SLionel Sambuc {
106484d9c625SLionel Sambuc KASSERT(state->numids < state->maxids);
106584d9c625SLionel Sambuc KASSERT(state->numkeys < state->maxkeyvals);
106684d9c625SLionel Sambuc
106784d9c625SLionel Sambuc if (!state->skipfirst || state->numkeys > 0) {
106884d9c625SLionel Sambuc state->keys[state->numkeys].qk_idtype = idtype;
106984d9c625SLionel Sambuc state->keys[state->numkeys].qk_id = id;
107084d9c625SLionel Sambuc state->keys[state->numkeys].qk_objtype = QUOTA_OBJTYPE_BLOCKS;
107184d9c625SLionel Sambuc state->numkeys++;
107284d9c625SLionel Sambuc }
107384d9c625SLionel Sambuc if (state->numkeys < state->maxkeyvals) {
107484d9c625SLionel Sambuc state->keys[state->numkeys].qk_idtype = idtype;
107584d9c625SLionel Sambuc state->keys[state->numkeys].qk_id = id;
107684d9c625SLionel Sambuc state->keys[state->numkeys].qk_objtype = QUOTA_OBJTYPE_FILES;
107784d9c625SLionel Sambuc state->numkeys++;
107884d9c625SLionel Sambuc } else {
107984d9c625SLionel Sambuc KASSERT(state->skiplast);
108084d9c625SLionel Sambuc }
108184d9c625SLionel Sambuc state->numids++;
108284d9c625SLionel Sambuc }
108384d9c625SLionel Sambuc
108484d9c625SLionel Sambuc /*
108584d9c625SLionel Sambuc * Callback function for getting IDs. Update counting and call addid.
108684d9c625SLionel Sambuc */
108784d9c625SLionel Sambuc static int
q2cursor_getids_callback(struct ulfsmount * ump,uint64_t * offp,struct quota2_entry * q2ep,uint64_t off,void * v)108884d9c625SLionel Sambuc q2cursor_getids_callback(struct ulfsmount *ump, uint64_t *offp,
108984d9c625SLionel Sambuc struct quota2_entry *q2ep, uint64_t off, void *v)
109084d9c625SLionel Sambuc {
109184d9c625SLionel Sambuc struct q2cursor_getids *gi = v;
109284d9c625SLionel Sambuc id_t id;
109384d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
109484d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
109584d9c625SLionel Sambuc
109684d9c625SLionel Sambuc if (gi->skipped < gi->skip) {
109784d9c625SLionel Sambuc gi->skipped++;
109884d9c625SLionel Sambuc return 0;
109984d9c625SLionel Sambuc }
110084d9c625SLionel Sambuc id = ulfs_rw32(q2ep->q2e_uid, needswap);
110184d9c625SLionel Sambuc q2cursor_addid(gi->state, gi->idtype, id);
110284d9c625SLionel Sambuc gi->new_skip++;
110384d9c625SLionel Sambuc if (gi->state->numids >= gi->state->maxids) {
110484d9c625SLionel Sambuc /* got enough ids, stop now */
110584d9c625SLionel Sambuc gi->stopped = 1;
110684d9c625SLionel Sambuc return Q2WL_ABORT;
110784d9c625SLionel Sambuc }
110884d9c625SLionel Sambuc return 0;
110984d9c625SLionel Sambuc }
111084d9c625SLionel Sambuc
111184d9c625SLionel Sambuc /*
111284d9c625SLionel Sambuc * Fill in a batch of quotakeys by scanning one or more hash chains.
111384d9c625SLionel Sambuc */
111484d9c625SLionel Sambuc static int
q2cursor_getkeys(struct ulfsmount * ump,int idtype,struct ulfsq2_cursor * cursor,struct q2cursor_state * state,int * hashsize_ret,struct quota2_entry * default_q2e_ret)111584d9c625SLionel Sambuc q2cursor_getkeys(struct ulfsmount *ump, int idtype, struct ulfsq2_cursor *cursor,
111684d9c625SLionel Sambuc struct q2cursor_state *state,
111784d9c625SLionel Sambuc int *hashsize_ret, struct quota2_entry *default_q2e_ret)
111884d9c625SLionel Sambuc {
111984d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
112084d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
112184d9c625SLionel Sambuc struct buf *hbp;
112284d9c625SLionel Sambuc struct quota2_header *q2h;
112384d9c625SLionel Sambuc int quota2_hash_size;
112484d9c625SLionel Sambuc struct q2cursor_getids gi;
112584d9c625SLionel Sambuc uint64_t offset;
112684d9c625SLionel Sambuc int error;
112784d9c625SLionel Sambuc
112884d9c625SLionel Sambuc /*
112984d9c625SLionel Sambuc * Read the header block.
113084d9c625SLionel Sambuc */
113184d9c625SLionel Sambuc
113284d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
113384d9c625SLionel Sambuc error = getq2h(ump, idtype, &hbp, &q2h, 0);
113484d9c625SLionel Sambuc if (error) {
113584d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
113684d9c625SLionel Sambuc return error;
113784d9c625SLionel Sambuc }
113884d9c625SLionel Sambuc
113984d9c625SLionel Sambuc /* if the table size has changed, make the caller start over */
114084d9c625SLionel Sambuc quota2_hash_size = ulfs_rw16(q2h->q2h_hash_size, needswap);
114184d9c625SLionel Sambuc if (cursor->q2c_hashsize == 0) {
114284d9c625SLionel Sambuc cursor->q2c_hashsize = quota2_hash_size;
114384d9c625SLionel Sambuc } else if (cursor->q2c_hashsize != quota2_hash_size) {
114484d9c625SLionel Sambuc error = EDEADLK;
114584d9c625SLionel Sambuc goto scanfail;
114684d9c625SLionel Sambuc }
114784d9c625SLionel Sambuc
114884d9c625SLionel Sambuc /* grab the entry with the default values out of the header */
114984d9c625SLionel Sambuc lfsquota2_ulfs_rwq2e(&q2h->q2h_defentry, default_q2e_ret, needswap);
115084d9c625SLionel Sambuc
115184d9c625SLionel Sambuc /* If we haven't done the defaults yet, that goes first. */
115284d9c625SLionel Sambuc if (cursor->q2c_defaults_done == 0) {
115384d9c625SLionel Sambuc q2cursor_addid(state, idtype, QUOTA_DEFAULTID);
115484d9c625SLionel Sambuc /* if we read both halves, mark it done */
115584d9c625SLionel Sambuc if (state->numids < state->maxids || !state->skiplast) {
115684d9c625SLionel Sambuc cursor->q2c_defaults_done = 1;
115784d9c625SLionel Sambuc }
115884d9c625SLionel Sambuc }
115984d9c625SLionel Sambuc
116084d9c625SLionel Sambuc gi.state = state;
116184d9c625SLionel Sambuc gi.idtype = idtype;
116284d9c625SLionel Sambuc
116384d9c625SLionel Sambuc while (state->numids < state->maxids) {
116484d9c625SLionel Sambuc if (cursor->q2c_hashpos >= quota2_hash_size) {
116584d9c625SLionel Sambuc /* nothing more left */
116684d9c625SLionel Sambuc break;
116784d9c625SLionel Sambuc }
116884d9c625SLionel Sambuc
116984d9c625SLionel Sambuc /* scan this hash chain */
117084d9c625SLionel Sambuc gi.skip = cursor->q2c_uidpos;
117184d9c625SLionel Sambuc gi.new_skip = gi.skip;
117284d9c625SLionel Sambuc gi.skipped = 0;
117384d9c625SLionel Sambuc gi.stopped = 0;
117484d9c625SLionel Sambuc offset = q2h->q2h_entries[cursor->q2c_hashpos];
117584d9c625SLionel Sambuc
117684d9c625SLionel Sambuc error = quota2_walk_list(ump, hbp, idtype, &offset, 0, &gi,
117784d9c625SLionel Sambuc q2cursor_getids_callback);
117884d9c625SLionel Sambuc KASSERT(error != Q2WL_ABORT);
117984d9c625SLionel Sambuc if (error) {
118084d9c625SLionel Sambuc break;
118184d9c625SLionel Sambuc }
118284d9c625SLionel Sambuc if (gi.stopped) {
118384d9c625SLionel Sambuc /* callback stopped before reading whole chain */
118484d9c625SLionel Sambuc cursor->q2c_uidpos = gi.new_skip;
118584d9c625SLionel Sambuc /* if we didn't get both halves, back up */
118684d9c625SLionel Sambuc if (state->numids == state->maxids && state->skiplast){
118784d9c625SLionel Sambuc KASSERT(cursor->q2c_uidpos > 0);
118884d9c625SLionel Sambuc cursor->q2c_uidpos--;
118984d9c625SLionel Sambuc }
119084d9c625SLionel Sambuc } else {
119184d9c625SLionel Sambuc /* read whole chain */
119284d9c625SLionel Sambuc /* if we got both halves of the last id, advance */
119384d9c625SLionel Sambuc if (state->numids < state->maxids || !state->skiplast){
119484d9c625SLionel Sambuc cursor->q2c_uidpos = 0;
119584d9c625SLionel Sambuc cursor->q2c_hashpos++;
119684d9c625SLionel Sambuc }
119784d9c625SLionel Sambuc }
119884d9c625SLionel Sambuc }
119984d9c625SLionel Sambuc
120084d9c625SLionel Sambuc scanfail:
120184d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
120284d9c625SLionel Sambuc brelse(hbp, 0);
120384d9c625SLionel Sambuc if (error)
120484d9c625SLionel Sambuc return error;
120584d9c625SLionel Sambuc
120684d9c625SLionel Sambuc *hashsize_ret = quota2_hash_size;
120784d9c625SLionel Sambuc return 0;
120884d9c625SLionel Sambuc }
120984d9c625SLionel Sambuc
121084d9c625SLionel Sambuc /*
121184d9c625SLionel Sambuc * Fetch the quotavals for the quotakeys.
121284d9c625SLionel Sambuc */
121384d9c625SLionel Sambuc static int
q2cursor_getvals(struct ulfsmount * ump,struct q2cursor_state * state,const struct quota2_entry * default_q2e)121484d9c625SLionel Sambuc q2cursor_getvals(struct ulfsmount *ump, struct q2cursor_state *state,
121584d9c625SLionel Sambuc const struct quota2_entry *default_q2e)
121684d9c625SLionel Sambuc {
121784d9c625SLionel Sambuc int hasid;
121884d9c625SLionel Sambuc id_t loadedid, id;
121984d9c625SLionel Sambuc unsigned pos;
122084d9c625SLionel Sambuc struct quota2_entry q2e;
122184d9c625SLionel Sambuc int objtype;
122284d9c625SLionel Sambuc int error;
122384d9c625SLionel Sambuc
122484d9c625SLionel Sambuc hasid = 0;
122584d9c625SLionel Sambuc loadedid = 0;
122684d9c625SLionel Sambuc for (pos = 0; pos < state->numkeys; pos++) {
122784d9c625SLionel Sambuc id = state->keys[pos].qk_id;
122884d9c625SLionel Sambuc if (!hasid || id != loadedid) {
122984d9c625SLionel Sambuc hasid = 1;
123084d9c625SLionel Sambuc loadedid = id;
123184d9c625SLionel Sambuc if (id == QUOTA_DEFAULTID) {
123284d9c625SLionel Sambuc q2e = *default_q2e;
123384d9c625SLionel Sambuc } else {
123484d9c625SLionel Sambuc error = quota2_fetch_q2e(ump,
123584d9c625SLionel Sambuc &state->keys[pos],
123684d9c625SLionel Sambuc &q2e);
123784d9c625SLionel Sambuc if (error == ENOENT) {
123884d9c625SLionel Sambuc /* something changed - start over */
123984d9c625SLionel Sambuc error = EDEADLK;
124084d9c625SLionel Sambuc }
124184d9c625SLionel Sambuc if (error) {
124284d9c625SLionel Sambuc return error;
124384d9c625SLionel Sambuc }
124484d9c625SLionel Sambuc }
124584d9c625SLionel Sambuc }
124684d9c625SLionel Sambuc
124784d9c625SLionel Sambuc
124884d9c625SLionel Sambuc objtype = state->keys[pos].qk_objtype;
124984d9c625SLionel Sambuc KASSERT(objtype >= 0 && objtype < N_QL);
125084d9c625SLionel Sambuc q2val_to_quotaval(&q2e.q2e_val[objtype], &state->vals[pos]);
125184d9c625SLionel Sambuc }
125284d9c625SLionel Sambuc
125384d9c625SLionel Sambuc return 0;
125484d9c625SLionel Sambuc }
125584d9c625SLionel Sambuc
125684d9c625SLionel Sambuc /*
125784d9c625SLionel Sambuc * Handle cursorget.
125884d9c625SLionel Sambuc *
125984d9c625SLionel Sambuc * We can't just read keys and values directly, because we can't walk
126084d9c625SLionel Sambuc * the list with qdlock and grab dq_interlock to read the entries at
126184d9c625SLionel Sambuc * the same time. So we're going to do two passes: one to figure out
126284d9c625SLionel Sambuc * which IDs we want and fill in the keys, and then a second to use
126384d9c625SLionel Sambuc * the keys to fetch the values.
126484d9c625SLionel Sambuc */
126584d9c625SLionel Sambuc int
lfsquota2_handle_cmd_cursorget(struct ulfsmount * ump,struct quotakcursor * qkc,struct quotakey * keys,struct quotaval * vals,unsigned maxreturn,unsigned * ret)126684d9c625SLionel Sambuc lfsquota2_handle_cmd_cursorget(struct ulfsmount *ump, struct quotakcursor *qkc,
126784d9c625SLionel Sambuc struct quotakey *keys, struct quotaval *vals, unsigned maxreturn,
126884d9c625SLionel Sambuc unsigned *ret)
126984d9c625SLionel Sambuc {
127084d9c625SLionel Sambuc int error;
127184d9c625SLionel Sambuc struct ulfsq2_cursor *cursor;
127284d9c625SLionel Sambuc struct ulfsq2_cursor newcursor;
127384d9c625SLionel Sambuc struct q2cursor_state state;
127484d9c625SLionel Sambuc struct quota2_entry default_q2e;
127584d9c625SLionel Sambuc int idtype;
1276*0a6a1f1dSLionel Sambuc int quota2_hash_size = 0; /* XXXuninit */
127784d9c625SLionel Sambuc
127884d9c625SLionel Sambuc /*
127984d9c625SLionel Sambuc * Convert and validate the cursor.
128084d9c625SLionel Sambuc */
128184d9c625SLionel Sambuc cursor = Q2CURSOR(qkc);
128284d9c625SLionel Sambuc error = q2cursor_check(cursor);
128384d9c625SLionel Sambuc if (error) {
128484d9c625SLionel Sambuc return error;
128584d9c625SLionel Sambuc }
128684d9c625SLionel Sambuc
128784d9c625SLionel Sambuc /*
128884d9c625SLionel Sambuc * Make sure our on-disk codes match the values of the
128984d9c625SLionel Sambuc * FS-independent ones. This avoids the need for explicit
129084d9c625SLionel Sambuc * conversion (which would be a NOP anyway and thus easily
129184d9c625SLionel Sambuc * left out or called in the wrong places...)
129284d9c625SLionel Sambuc */
129384d9c625SLionel Sambuc CTASSERT(QUOTA_IDTYPE_USER == ULFS_USRQUOTA);
129484d9c625SLionel Sambuc CTASSERT(QUOTA_IDTYPE_GROUP == ULFS_GRPQUOTA);
129584d9c625SLionel Sambuc CTASSERT(QUOTA_OBJTYPE_BLOCKS == QL_BLOCK);
129684d9c625SLionel Sambuc CTASSERT(QUOTA_OBJTYPE_FILES == QL_FILE);
129784d9c625SLionel Sambuc
129884d9c625SLionel Sambuc /*
129984d9c625SLionel Sambuc * If some of the idtypes aren't configured/enabled, arrange
130084d9c625SLionel Sambuc * to skip over them.
130184d9c625SLionel Sambuc */
130284d9c625SLionel Sambuc if (cursor->q2c_users_done == 0 &&
130384d9c625SLionel Sambuc ump->um_quotas[ULFS_USRQUOTA] == NULLVP) {
130484d9c625SLionel Sambuc cursor->q2c_users_done = 1;
130584d9c625SLionel Sambuc }
130684d9c625SLionel Sambuc if (cursor->q2c_groups_done == 0 &&
130784d9c625SLionel Sambuc ump->um_quotas[ULFS_GRPQUOTA] == NULLVP) {
130884d9c625SLionel Sambuc cursor->q2c_groups_done = 1;
130984d9c625SLionel Sambuc }
131084d9c625SLionel Sambuc
131184d9c625SLionel Sambuc /* Loop over, potentially, both idtypes */
131284d9c625SLionel Sambuc while (1) {
131384d9c625SLionel Sambuc
131484d9c625SLionel Sambuc /* Choose id type */
131584d9c625SLionel Sambuc error = q2cursor_pickidtype(cursor, &idtype);
131684d9c625SLionel Sambuc if (error == EAGAIN) {
131784d9c625SLionel Sambuc /* nothing more to do, return 0 */
131884d9c625SLionel Sambuc *ret = 0;
131984d9c625SLionel Sambuc return 0;
132084d9c625SLionel Sambuc }
132184d9c625SLionel Sambuc KASSERT(ump->um_quotas[idtype] != NULLVP);
132284d9c625SLionel Sambuc
132384d9c625SLionel Sambuc /*
132484d9c625SLionel Sambuc * Initialize the per-call iteration state. Copy the
132584d9c625SLionel Sambuc * cursor state so we can update it in place but back
132684d9c625SLionel Sambuc * out on error.
132784d9c625SLionel Sambuc */
132884d9c625SLionel Sambuc q2cursor_initstate(&state, keys, vals, maxreturn,
132984d9c625SLionel Sambuc cursor->q2c_blocks_done);
133084d9c625SLionel Sambuc newcursor = *cursor;
133184d9c625SLionel Sambuc
133284d9c625SLionel Sambuc /* Assign keys */
133384d9c625SLionel Sambuc error = q2cursor_getkeys(ump, idtype, &newcursor, &state,
133484d9c625SLionel Sambuc "a2_hash_size, &default_q2e);
133584d9c625SLionel Sambuc if (error) {
133684d9c625SLionel Sambuc return error;
133784d9c625SLionel Sambuc }
133884d9c625SLionel Sambuc
133984d9c625SLionel Sambuc /* Now fill in the values. */
134084d9c625SLionel Sambuc error = q2cursor_getvals(ump, &state, &default_q2e);
134184d9c625SLionel Sambuc if (error) {
134284d9c625SLionel Sambuc return error;
134384d9c625SLionel Sambuc }
134484d9c625SLionel Sambuc
134584d9c625SLionel Sambuc /*
134684d9c625SLionel Sambuc * Now that we aren't going to fail and lose what we
134784d9c625SLionel Sambuc * did so far, we can update the cursor state.
134884d9c625SLionel Sambuc */
134984d9c625SLionel Sambuc
135084d9c625SLionel Sambuc if (newcursor.q2c_hashpos >= quota2_hash_size) {
135184d9c625SLionel Sambuc if (idtype == QUOTA_IDTYPE_USER)
135284d9c625SLionel Sambuc cursor->q2c_users_done = 1;
135384d9c625SLionel Sambuc else
135484d9c625SLionel Sambuc cursor->q2c_groups_done = 1;
135584d9c625SLionel Sambuc
135684d9c625SLionel Sambuc /* start over on another id type */
135784d9c625SLionel Sambuc cursor->q2c_hashsize = 0;
135884d9c625SLionel Sambuc cursor->q2c_defaults_done = 0;
135984d9c625SLionel Sambuc cursor->q2c_hashpos = 0;
136084d9c625SLionel Sambuc cursor->q2c_uidpos = 0;
136184d9c625SLionel Sambuc cursor->q2c_blocks_done = 0;
136284d9c625SLionel Sambuc } else {
136384d9c625SLionel Sambuc *cursor = newcursor;
136484d9c625SLionel Sambuc cursor->q2c_blocks_done = state.skiplast;
136584d9c625SLionel Sambuc }
136684d9c625SLionel Sambuc
136784d9c625SLionel Sambuc /*
136884d9c625SLionel Sambuc * If we have something to return, return it.
136984d9c625SLionel Sambuc * Otherwise, continue to the other idtype, if any,
137084d9c625SLionel Sambuc * and only return zero at end of iteration.
137184d9c625SLionel Sambuc */
137284d9c625SLionel Sambuc if (state.numkeys > 0) {
137384d9c625SLionel Sambuc break;
137484d9c625SLionel Sambuc }
137584d9c625SLionel Sambuc }
137684d9c625SLionel Sambuc
137784d9c625SLionel Sambuc *ret = state.numkeys;
137884d9c625SLionel Sambuc return 0;
137984d9c625SLionel Sambuc }
138084d9c625SLionel Sambuc
138184d9c625SLionel Sambuc int
lfsquota2_handle_cmd_cursoropen(struct ulfsmount * ump,struct quotakcursor * qkc)138284d9c625SLionel Sambuc lfsquota2_handle_cmd_cursoropen(struct ulfsmount *ump, struct quotakcursor *qkc)
138384d9c625SLionel Sambuc {
138484d9c625SLionel Sambuc struct ulfsq2_cursor *cursor;
138584d9c625SLionel Sambuc
138684d9c625SLionel Sambuc CTASSERT(sizeof(*cursor) <= sizeof(qkc->u.qkc_space));
138784d9c625SLionel Sambuc cursor = Q2CURSOR(qkc);
138884d9c625SLionel Sambuc
138984d9c625SLionel Sambuc cursor->q2c_magic = Q2C_MAGIC;
139084d9c625SLionel Sambuc cursor->q2c_hashsize = 0;
139184d9c625SLionel Sambuc
139284d9c625SLionel Sambuc cursor->q2c_users_done = 0;
139384d9c625SLionel Sambuc cursor->q2c_groups_done = 0;
139484d9c625SLionel Sambuc cursor->q2c_defaults_done = 0;
139584d9c625SLionel Sambuc cursor->q2c_hashpos = 0;
139684d9c625SLionel Sambuc cursor->q2c_uidpos = 0;
139784d9c625SLionel Sambuc cursor->q2c_blocks_done = 0;
139884d9c625SLionel Sambuc return 0;
139984d9c625SLionel Sambuc }
140084d9c625SLionel Sambuc
140184d9c625SLionel Sambuc int
lfsquota2_handle_cmd_cursorclose(struct ulfsmount * ump,struct quotakcursor * qkc)140284d9c625SLionel Sambuc lfsquota2_handle_cmd_cursorclose(struct ulfsmount *ump, struct quotakcursor *qkc)
140384d9c625SLionel Sambuc {
140484d9c625SLionel Sambuc struct ulfsq2_cursor *cursor;
140584d9c625SLionel Sambuc int error;
140684d9c625SLionel Sambuc
140784d9c625SLionel Sambuc cursor = Q2CURSOR(qkc);
140884d9c625SLionel Sambuc error = q2cursor_check(cursor);
140984d9c625SLionel Sambuc if (error) {
141084d9c625SLionel Sambuc return error;
141184d9c625SLionel Sambuc }
141284d9c625SLionel Sambuc
141384d9c625SLionel Sambuc /* nothing to do */
141484d9c625SLionel Sambuc
141584d9c625SLionel Sambuc return 0;
141684d9c625SLionel Sambuc }
141784d9c625SLionel Sambuc
141884d9c625SLionel Sambuc int
lfsquota2_handle_cmd_cursorskipidtype(struct ulfsmount * ump,struct quotakcursor * qkc,int idtype)141984d9c625SLionel Sambuc lfsquota2_handle_cmd_cursorskipidtype(struct ulfsmount *ump,
142084d9c625SLionel Sambuc struct quotakcursor *qkc, int idtype)
142184d9c625SLionel Sambuc {
142284d9c625SLionel Sambuc struct ulfsq2_cursor *cursor;
142384d9c625SLionel Sambuc int error;
142484d9c625SLionel Sambuc
142584d9c625SLionel Sambuc cursor = Q2CURSOR(qkc);
142684d9c625SLionel Sambuc error = q2cursor_check(cursor);
142784d9c625SLionel Sambuc if (error) {
142884d9c625SLionel Sambuc return error;
142984d9c625SLionel Sambuc }
143084d9c625SLionel Sambuc
143184d9c625SLionel Sambuc switch (idtype) {
143284d9c625SLionel Sambuc case QUOTA_IDTYPE_USER:
143384d9c625SLionel Sambuc cursor->q2c_users_done = 1;
143484d9c625SLionel Sambuc break;
143584d9c625SLionel Sambuc case QUOTA_IDTYPE_GROUP:
143684d9c625SLionel Sambuc cursor->q2c_groups_done = 1;
143784d9c625SLionel Sambuc break;
143884d9c625SLionel Sambuc default:
143984d9c625SLionel Sambuc return EINVAL;
144084d9c625SLionel Sambuc }
144184d9c625SLionel Sambuc
144284d9c625SLionel Sambuc return 0;
144384d9c625SLionel Sambuc }
144484d9c625SLionel Sambuc
144584d9c625SLionel Sambuc int
lfsquota2_handle_cmd_cursoratend(struct ulfsmount * ump,struct quotakcursor * qkc,int * ret)144684d9c625SLionel Sambuc lfsquota2_handle_cmd_cursoratend(struct ulfsmount *ump, struct quotakcursor *qkc,
144784d9c625SLionel Sambuc int *ret)
144884d9c625SLionel Sambuc {
144984d9c625SLionel Sambuc struct ulfsq2_cursor *cursor;
145084d9c625SLionel Sambuc int error;
145184d9c625SLionel Sambuc
145284d9c625SLionel Sambuc cursor = Q2CURSOR(qkc);
145384d9c625SLionel Sambuc error = q2cursor_check(cursor);
145484d9c625SLionel Sambuc if (error) {
145584d9c625SLionel Sambuc return error;
145684d9c625SLionel Sambuc }
145784d9c625SLionel Sambuc
145884d9c625SLionel Sambuc *ret = (cursor->q2c_users_done && cursor->q2c_groups_done);
145984d9c625SLionel Sambuc return 0;
146084d9c625SLionel Sambuc }
146184d9c625SLionel Sambuc
146284d9c625SLionel Sambuc int
lfsquota2_handle_cmd_cursorrewind(struct ulfsmount * ump,struct quotakcursor * qkc)146384d9c625SLionel Sambuc lfsquota2_handle_cmd_cursorrewind(struct ulfsmount *ump, struct quotakcursor *qkc)
146484d9c625SLionel Sambuc {
146584d9c625SLionel Sambuc struct ulfsq2_cursor *cursor;
146684d9c625SLionel Sambuc int error;
146784d9c625SLionel Sambuc
146884d9c625SLionel Sambuc cursor = Q2CURSOR(qkc);
146984d9c625SLionel Sambuc error = q2cursor_check(cursor);
147084d9c625SLionel Sambuc if (error) {
147184d9c625SLionel Sambuc return error;
147284d9c625SLionel Sambuc }
147384d9c625SLionel Sambuc
147484d9c625SLionel Sambuc cursor->q2c_hashsize = 0;
147584d9c625SLionel Sambuc
147684d9c625SLionel Sambuc cursor->q2c_users_done = 0;
147784d9c625SLionel Sambuc cursor->q2c_groups_done = 0;
147884d9c625SLionel Sambuc cursor->q2c_defaults_done = 0;
147984d9c625SLionel Sambuc cursor->q2c_hashpos = 0;
148084d9c625SLionel Sambuc cursor->q2c_uidpos = 0;
148184d9c625SLionel Sambuc cursor->q2c_blocks_done = 0;
148284d9c625SLionel Sambuc
148384d9c625SLionel Sambuc return 0;
148484d9c625SLionel Sambuc }
148584d9c625SLionel Sambuc
148684d9c625SLionel Sambuc int
lfs_q2sync(struct mount * mp)148784d9c625SLionel Sambuc lfs_q2sync(struct mount *mp)
148884d9c625SLionel Sambuc {
148984d9c625SLionel Sambuc return 0;
149084d9c625SLionel Sambuc }
149184d9c625SLionel Sambuc
149284d9c625SLionel Sambuc struct dq2get_callback {
149384d9c625SLionel Sambuc uid_t id;
149484d9c625SLionel Sambuc struct dquot *dq;
149584d9c625SLionel Sambuc };
149684d9c625SLionel Sambuc
149784d9c625SLionel Sambuc static int
dq2get_callback(struct ulfsmount * ump,uint64_t * offp,struct quota2_entry * q2e,uint64_t off,void * v)149884d9c625SLionel Sambuc dq2get_callback(struct ulfsmount *ump, uint64_t *offp, struct quota2_entry *q2e,
149984d9c625SLionel Sambuc uint64_t off, void *v)
150084d9c625SLionel Sambuc {
150184d9c625SLionel Sambuc struct dq2get_callback *c = v;
150284d9c625SLionel Sambuc daddr_t lblkno;
150384d9c625SLionel Sambuc int blkoff;
150484d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
150584d9c625SLionel Sambuc const int needswap = ULFS_MPNEEDSWAP(fs);
150684d9c625SLionel Sambuc
150784d9c625SLionel Sambuc if (ulfs_rw32(q2e->q2e_uid, needswap) == c->id) {
150884d9c625SLionel Sambuc KASSERT(mutex_owned(&c->dq->dq_interlock));
150984d9c625SLionel Sambuc lblkno = (off >> ump->um_mountp->mnt_fs_bshift);
151084d9c625SLionel Sambuc blkoff = (off & ump->umq2_bmask);
151184d9c625SLionel Sambuc c->dq->dq2_lblkno = lblkno;
151284d9c625SLionel Sambuc c->dq->dq2_blkoff = blkoff;
151384d9c625SLionel Sambuc return Q2WL_ABORT;
151484d9c625SLionel Sambuc }
151584d9c625SLionel Sambuc return 0;
151684d9c625SLionel Sambuc }
151784d9c625SLionel Sambuc
151884d9c625SLionel Sambuc int
lfs_dq2get(struct vnode * dqvp,u_long id,struct ulfsmount * ump,int type,struct dquot * dq)151984d9c625SLionel Sambuc lfs_dq2get(struct vnode *dqvp, u_long id, struct ulfsmount *ump, int type,
152084d9c625SLionel Sambuc struct dquot *dq)
152184d9c625SLionel Sambuc {
152284d9c625SLionel Sambuc struct buf *bp;
152384d9c625SLionel Sambuc struct quota2_header *q2h;
152484d9c625SLionel Sambuc int error;
152584d9c625SLionel Sambuc daddr_t offset;
152684d9c625SLionel Sambuc u_long hash_mask;
152784d9c625SLionel Sambuc struct dq2get_callback c = {
152884d9c625SLionel Sambuc .id = id,
152984d9c625SLionel Sambuc .dq = dq
153084d9c625SLionel Sambuc };
153184d9c625SLionel Sambuc
153284d9c625SLionel Sambuc KASSERT(mutex_owned(&dq->dq_interlock));
153384d9c625SLionel Sambuc mutex_enter(&lfs_dqlock);
153484d9c625SLionel Sambuc error = getq2h(ump, type, &bp, &q2h, 0);
153584d9c625SLionel Sambuc if (error)
153684d9c625SLionel Sambuc goto out_mutex;
153784d9c625SLionel Sambuc /* look for our entry */
153884d9c625SLionel Sambuc hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
153984d9c625SLionel Sambuc offset = q2h->q2h_entries[id & hash_mask];
154084d9c625SLionel Sambuc error = quota2_walk_list(ump, bp, type, &offset, 0, (void *)&c,
154184d9c625SLionel Sambuc dq2get_callback);
154284d9c625SLionel Sambuc brelse(bp, 0);
154384d9c625SLionel Sambuc out_mutex:
154484d9c625SLionel Sambuc mutex_exit(&lfs_dqlock);
154584d9c625SLionel Sambuc return error;
154684d9c625SLionel Sambuc }
154784d9c625SLionel Sambuc
154884d9c625SLionel Sambuc int
lfs_dq2sync(struct vnode * vp,struct dquot * dq)154984d9c625SLionel Sambuc lfs_dq2sync(struct vnode *vp, struct dquot *dq)
155084d9c625SLionel Sambuc {
155184d9c625SLionel Sambuc return 0;
155284d9c625SLionel Sambuc }
155384d9c625SLionel Sambuc
155484d9c625SLionel Sambuc int
lfs_quota2_mount(struct mount * mp)155584d9c625SLionel Sambuc lfs_quota2_mount(struct mount *mp)
155684d9c625SLionel Sambuc {
155784d9c625SLionel Sambuc struct ulfsmount *ump = VFSTOULFS(mp);
155884d9c625SLionel Sambuc struct lfs *fs = ump->um_lfs;
155984d9c625SLionel Sambuc int error = 0;
156084d9c625SLionel Sambuc struct vnode *vp;
156184d9c625SLionel Sambuc struct lwp *l = curlwp;
156284d9c625SLionel Sambuc
156384d9c625SLionel Sambuc if ((fs->lfs_use_quota2) == 0)
156484d9c625SLionel Sambuc return 0;
156584d9c625SLionel Sambuc
156684d9c625SLionel Sambuc fs->um_flags |= ULFS_QUOTA2;
1567*0a6a1f1dSLionel Sambuc ump->umq2_bsize = lfs_sb_getbsize(fs);
1568*0a6a1f1dSLionel Sambuc ump->umq2_bmask = lfs_sb_getbmask(fs);
156984d9c625SLionel Sambuc if (fs->lfs_quota_magic != Q2_HEAD_MAGIC) {
157084d9c625SLionel Sambuc printf("%s: Invalid quota magic number\n",
157184d9c625SLionel Sambuc mp->mnt_stat.f_mntonname);
157284d9c625SLionel Sambuc return EINVAL;
157384d9c625SLionel Sambuc }
157484d9c625SLionel Sambuc if ((fs->lfs_quota_flags & FS_Q2_DO_TYPE(ULFS_USRQUOTA)) &&
157584d9c625SLionel Sambuc fs->lfs_quotaino[ULFS_USRQUOTA] == 0) {
157684d9c625SLionel Sambuc printf("%s: no user quota inode\n",
157784d9c625SLionel Sambuc mp->mnt_stat.f_mntonname);
157884d9c625SLionel Sambuc error = EINVAL;
157984d9c625SLionel Sambuc }
158084d9c625SLionel Sambuc if ((fs->lfs_quota_flags & FS_Q2_DO_TYPE(ULFS_GRPQUOTA)) &&
158184d9c625SLionel Sambuc fs->lfs_quotaino[ULFS_GRPQUOTA] == 0) {
158284d9c625SLionel Sambuc printf("%s: no group quota inode\n",
158384d9c625SLionel Sambuc mp->mnt_stat.f_mntonname);
158484d9c625SLionel Sambuc error = EINVAL;
158584d9c625SLionel Sambuc }
158684d9c625SLionel Sambuc if (error)
158784d9c625SLionel Sambuc return error;
158884d9c625SLionel Sambuc
158984d9c625SLionel Sambuc if (fs->lfs_quota_flags & FS_Q2_DO_TYPE(ULFS_USRQUOTA) &&
159084d9c625SLionel Sambuc ump->um_quotas[ULFS_USRQUOTA] == NULLVP) {
159184d9c625SLionel Sambuc error = VFS_VGET(mp, fs->lfs_quotaino[ULFS_USRQUOTA], &vp);
159284d9c625SLionel Sambuc if (error) {
159384d9c625SLionel Sambuc printf("%s: can't vget() user quota inode: %d\n",
159484d9c625SLionel Sambuc mp->mnt_stat.f_mntonname, error);
159584d9c625SLionel Sambuc return error;
159684d9c625SLionel Sambuc }
159784d9c625SLionel Sambuc ump->um_quotas[ULFS_USRQUOTA] = vp;
159884d9c625SLionel Sambuc ump->um_cred[ULFS_USRQUOTA] = l->l_cred;
159984d9c625SLionel Sambuc mutex_enter(vp->v_interlock);
160084d9c625SLionel Sambuc vp->v_writecount++;
160184d9c625SLionel Sambuc mutex_exit(vp->v_interlock);
160284d9c625SLionel Sambuc VOP_UNLOCK(vp);
160384d9c625SLionel Sambuc }
160484d9c625SLionel Sambuc if (fs->lfs_quota_flags & FS_Q2_DO_TYPE(ULFS_GRPQUOTA) &&
160584d9c625SLionel Sambuc ump->um_quotas[ULFS_GRPQUOTA] == NULLVP) {
160684d9c625SLionel Sambuc error = VFS_VGET(mp, fs->lfs_quotaino[ULFS_GRPQUOTA], &vp);
160784d9c625SLionel Sambuc if (error) {
160884d9c625SLionel Sambuc vn_close(ump->um_quotas[ULFS_USRQUOTA],
160984d9c625SLionel Sambuc FREAD|FWRITE, l->l_cred);
161084d9c625SLionel Sambuc printf("%s: can't vget() group quota inode: %d\n",
161184d9c625SLionel Sambuc mp->mnt_stat.f_mntonname, error);
161284d9c625SLionel Sambuc return error;
161384d9c625SLionel Sambuc }
161484d9c625SLionel Sambuc ump->um_quotas[ULFS_GRPQUOTA] = vp;
161584d9c625SLionel Sambuc ump->um_cred[ULFS_GRPQUOTA] = l->l_cred;
161684d9c625SLionel Sambuc mutex_enter(vp->v_interlock);
161784d9c625SLionel Sambuc vp->v_vflag |= VV_SYSTEM;
161884d9c625SLionel Sambuc vp->v_writecount++;
161984d9c625SLionel Sambuc mutex_exit(vp->v_interlock);
162084d9c625SLionel Sambuc VOP_UNLOCK(vp);
162184d9c625SLionel Sambuc }
162284d9c625SLionel Sambuc mp->mnt_flag |= MNT_QUOTA;
162384d9c625SLionel Sambuc return 0;
162484d9c625SLionel Sambuc }
1625