xref: /minix3/sys/ufs/lfs/ulfs_quota2.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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 					 &quota2_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