xref: /netbsd/sys/ufs/ufs/ufs_quota2.c (revision d51593aa)
1*d51593aaSriastradh /* $NetBSD: ufs_quota2.c,v 1.46 2023/02/22 21:49:45 riastradh Exp $ */
20367ea04Sbouyer /*-
30367ea04Sbouyer   * Copyright (c) 2010 Manuel Bouyer
40367ea04Sbouyer   * All rights reserved.
50367ea04Sbouyer   *
60367ea04Sbouyer   * Redistribution and use in source and binary forms, with or without
70367ea04Sbouyer   * modification, are permitted provided that the following conditions
80367ea04Sbouyer   * are met:
90367ea04Sbouyer   * 1. Redistributions of source code must retain the above copyright
100367ea04Sbouyer   *    notice, this list of conditions and the following disclaimer.
110367ea04Sbouyer   * 2. Redistributions in binary form must reproduce the above copyright
120367ea04Sbouyer   *    notice, this list of conditions and the following disclaimer in the
130367ea04Sbouyer   *    documentation and/or other materials provided with the distribution.
140367ea04Sbouyer   *
150367ea04Sbouyer   * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
160367ea04Sbouyer   * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
170367ea04Sbouyer   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
180367ea04Sbouyer   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
190367ea04Sbouyer   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
200367ea04Sbouyer   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
210367ea04Sbouyer   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
220367ea04Sbouyer   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
230367ea04Sbouyer   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
240367ea04Sbouyer   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
250367ea04Sbouyer   * POSSIBILITY OF SUCH DAMAGE.
260367ea04Sbouyer   */
270367ea04Sbouyer 
280367ea04Sbouyer #include <sys/cdefs.h>
29*d51593aaSriastradh __KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.46 2023/02/22 21:49:45 riastradh Exp $");
300367ea04Sbouyer 
310367ea04Sbouyer #include <sys/buf.h>
320367ea04Sbouyer #include <sys/param.h>
330367ea04Sbouyer #include <sys/kernel.h>
340367ea04Sbouyer #include <sys/systm.h>
350367ea04Sbouyer #include <sys/namei.h>
360367ea04Sbouyer #include <sys/file.h>
370367ea04Sbouyer #include <sys/proc.h>
380367ea04Sbouyer #include <sys/vnode.h>
390367ea04Sbouyer #include <sys/mount.h>
400367ea04Sbouyer #include <sys/kauth.h>
410367ea04Sbouyer #include <sys/wapbl.h>
42fb787cb1Sdholland #include <sys/quota.h>
4306f1e02eSdholland #include <sys/quotactl.h>
440367ea04Sbouyer 
450367ea04Sbouyer #include <ufs/ufs/quota2.h>
460367ea04Sbouyer #include <ufs/ufs/inode.h>
470367ea04Sbouyer #include <ufs/ufs/ufsmount.h>
480367ea04Sbouyer #include <ufs/ufs/ufs_bswap.h>
490367ea04Sbouyer #include <ufs/ufs/ufs_extern.h>
500367ea04Sbouyer #include <ufs/ufs/ufs_quota.h>
510367ea04Sbouyer #include <ufs/ufs/ufs_wapbl.h>
520367ea04Sbouyer 
530367ea04Sbouyer /*
540367ea04Sbouyer  * LOCKING:
550367ea04Sbouyer  * Data in the entries are protected by the associated struct dquot's
560367ea04Sbouyer  * dq_interlock (this means we can't read or change a quota entry without
57051f0471Sandvar  * grabbing a dquot for it).
580367ea04Sbouyer  * The header and lists (including pointers in the data entries, and q2e_uid)
590367ea04Sbouyer  * are protected by the global dqlock.
600367ea04Sbouyer  * the locking order is dq_interlock -> dqlock
610367ea04Sbouyer  */
620367ea04Sbouyer 
630367ea04Sbouyer static int quota2_bwrite(struct mount *, struct buf *);
640367ea04Sbouyer static int getinoquota2(struct inode *, bool, bool, struct buf **,
650367ea04Sbouyer     struct quota2_entry **);
660367ea04Sbouyer static int getq2h(struct ufsmount *, int, struct buf **,
670367ea04Sbouyer     struct quota2_header **, int);
680367ea04Sbouyer static int getq2e(struct ufsmount *, int, daddr_t, int, struct buf **,
690367ea04Sbouyer     struct quota2_entry **, int);
700367ea04Sbouyer static int quota2_walk_list(struct ufsmount *, struct buf *, int,
710367ea04Sbouyer     uint64_t *, int, void *,
720367ea04Sbouyer     int (*func)(struct ufsmount *, uint64_t *, struct quota2_entry *,
730367ea04Sbouyer       uint64_t, void *));
740367ea04Sbouyer 
75bec3b862Sbouyer static const char *limnames[] = INITQLNAMES;
76bec3b862Sbouyer 
77e4696545Sdholland static void
quota2_dict_update_q2e_limits(int objtype,const struct quotaval * val,struct quota2_entry * q2e)786ce81b0dSdholland quota2_dict_update_q2e_limits(int objtype, const struct quotaval *val,
79bec3b862Sbouyer     struct quota2_entry *q2e)
80bec3b862Sbouyer {
816ce81b0dSdholland 	/* make sure we can index q2e_val[] by the fs-independent objtype */
826ce81b0dSdholland 	CTASSERT(QUOTA_OBJTYPE_BLOCKS == QL_BLOCK);
836ce81b0dSdholland 	CTASSERT(QUOTA_OBJTYPE_FILES == QL_FILE);
84bec3b862Sbouyer 
856ce81b0dSdholland 	q2e->q2e_val[objtype].q2v_hardlimit = val->qv_hardlimit;
866ce81b0dSdholland 	q2e->q2e_val[objtype].q2v_softlimit = val->qv_softlimit;
876ce81b0dSdholland 	q2e->q2e_val[objtype].q2v_grace = val->qv_grace;
88e4696545Sdholland }
89bec3b862Sbouyer 
90fb787cb1Sdholland /*
91fb787cb1Sdholland  * Convert internal representation to FS-independent representation.
92fb787cb1Sdholland  * (Note that while the two types are currently identical, the
93fb787cb1Sdholland  * internal representation is an on-disk struct and the FS-independent
94fb787cb1Sdholland  * representation is not, and they might diverge in the future.)
95fb787cb1Sdholland  */
96fb787cb1Sdholland static void
q2val_to_quotaval(struct quota2_val * q2v,struct quotaval * qv)97fb787cb1Sdholland q2val_to_quotaval(struct quota2_val *q2v, struct quotaval *qv)
98fb787cb1Sdholland {
99fb787cb1Sdholland 	qv->qv_softlimit = q2v->q2v_softlimit;
100fb787cb1Sdholland 	qv->qv_hardlimit = q2v->q2v_hardlimit;
101fb787cb1Sdholland 	qv->qv_usage = q2v->q2v_cur;
102fb787cb1Sdholland 	qv->qv_expiretime = q2v->q2v_time;
103fb787cb1Sdholland 	qv->qv_grace = q2v->q2v_grace;
104fb787cb1Sdholland }
105fb787cb1Sdholland 
106fb787cb1Sdholland /*
107fb787cb1Sdholland  * Convert a quota2entry and default-flag to the FS-independent
108fb787cb1Sdholland  * representation.
109fb787cb1Sdholland  */
110fb787cb1Sdholland static void
q2e_to_quotaval(struct quota2_entry * q2e,int def,id_t * id,int objtype,struct quotaval * ret)11107baaa7aSdholland q2e_to_quotaval(struct quota2_entry *q2e, int def,
11207baaa7aSdholland     id_t *id, int objtype, struct quotaval *ret)
113fb787cb1Sdholland {
114fb787cb1Sdholland 	if (def) {
115fb787cb1Sdholland 		*id = QUOTA_DEFAULTID;
116fb787cb1Sdholland 	} else {
117fb787cb1Sdholland 		*id = q2e->q2e_uid;
118fb787cb1Sdholland 	}
119fb787cb1Sdholland 
12007baaa7aSdholland 	KASSERT(objtype >= 0 && objtype < N_QL);
12107baaa7aSdholland 	q2val_to_quotaval(&q2e->q2e_val[objtype], ret);
122fb787cb1Sdholland }
123fb787cb1Sdholland 
1240367ea04Sbouyer 
1250367ea04Sbouyer static int
quota2_bwrite(struct mount * mp,struct buf * bp)1260367ea04Sbouyer quota2_bwrite(struct mount *mp, struct buf *bp)
1270367ea04Sbouyer {
1280367ea04Sbouyer 	if (mp->mnt_flag & MNT_SYNCHRONOUS)
1290367ea04Sbouyer 		return bwrite(bp);
1300367ea04Sbouyer 	else {
1310367ea04Sbouyer 		bdwrite(bp);
1320367ea04Sbouyer 		return 0;
1330367ea04Sbouyer 	}
1340367ea04Sbouyer }
1350367ea04Sbouyer 
1360367ea04Sbouyer static int
getq2h(struct ufsmount * ump,int type,struct buf ** bpp,struct quota2_header ** q2hp,int flags)1370367ea04Sbouyer getq2h(struct ufsmount *ump, int type,
1380367ea04Sbouyer     struct buf **bpp, struct quota2_header **q2hp, int flags)
1390367ea04Sbouyer {
1400367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
1410367ea04Sbouyer 	int error;
1420367ea04Sbouyer 	struct buf *bp;
1430367ea04Sbouyer 	struct quota2_header *q2h;
1440367ea04Sbouyer 
1450367ea04Sbouyer 	KASSERT(mutex_owned(&dqlock));
1460367ea04Sbouyer 	error = bread(ump->um_quotas[type], 0, ump->umq2_bsize,
147f61b6169Smaxv 	    flags, &bp);
1480367ea04Sbouyer 	if (error)
1490367ea04Sbouyer 		return error;
1500367ea04Sbouyer 	if (bp->b_resid != 0)
1510367ea04Sbouyer 		panic("dq2get: %s quota file truncated", quotatypes[type]);
1520367ea04Sbouyer 
1530367ea04Sbouyer 	q2h = (void *)bp->b_data;
1540367ea04Sbouyer 	if (ufs_rw32(q2h->q2h_magic_number, needswap) != Q2_HEAD_MAGIC ||
1550367ea04Sbouyer 	    q2h->q2h_type != type)
1560367ea04Sbouyer 		panic("dq2get: corrupted %s quota header", quotatypes[type]);
1570367ea04Sbouyer 	*bpp = bp;
1580367ea04Sbouyer 	*q2hp = q2h;
1590367ea04Sbouyer 	return 0;
1600367ea04Sbouyer }
1610367ea04Sbouyer 
1620367ea04Sbouyer static int
getq2e(struct ufsmount * ump,int type,daddr_t lblkno,int blkoffset,struct buf ** bpp,struct quota2_entry ** q2ep,int flags)1630367ea04Sbouyer getq2e(struct ufsmount *ump, int type, daddr_t lblkno, int blkoffset,
1640367ea04Sbouyer     struct buf **bpp, struct quota2_entry **q2ep, int flags)
1650367ea04Sbouyer {
1660367ea04Sbouyer 	int error;
1670367ea04Sbouyer 	struct buf *bp;
1680367ea04Sbouyer 
1690367ea04Sbouyer 	if (blkoffset & (sizeof(uint64_t) - 1)) {
1700367ea04Sbouyer 		panic("dq2get: %s quota file corrupted",
1710367ea04Sbouyer 		    quotatypes[type]);
1720367ea04Sbouyer 	}
1730367ea04Sbouyer 	error = bread(ump->um_quotas[type], lblkno, ump->umq2_bsize,
174f61b6169Smaxv 	    flags, &bp);
1750367ea04Sbouyer 	if (error)
1760367ea04Sbouyer 		return error;
1770367ea04Sbouyer 	if (bp->b_resid != 0) {
1780367ea04Sbouyer 		panic("dq2get: %s quota file corrupted",
1790367ea04Sbouyer 		    quotatypes[type]);
1800367ea04Sbouyer 	}
1810367ea04Sbouyer 	*q2ep = (void *)((char *)bp->b_data + blkoffset);
1820367ea04Sbouyer 	*bpp = bp;
1830367ea04Sbouyer 	return 0;
1840367ea04Sbouyer }
1850367ea04Sbouyer 
1860367ea04Sbouyer /* walk a quota entry list, calling the callback for each entry */
1870367ea04Sbouyer #define Q2WL_ABORT 0x10000000
1880367ea04Sbouyer 
1890367ea04Sbouyer static int
quota2_walk_list(struct ufsmount * ump,struct buf * hbp,int type,uint64_t * offp,int flags,void * a,int (* func)(struct ufsmount *,uint64_t *,struct quota2_entry *,uint64_t,void *))1900367ea04Sbouyer quota2_walk_list(struct ufsmount *ump, struct buf *hbp, int type,
1910367ea04Sbouyer     uint64_t *offp, int flags, void *a,
192*d51593aaSriastradh     int (*func)(struct ufsmount *, uint64_t *, struct quota2_entry *, uint64_t,
193*d51593aaSriastradh 	void *))
1940367ea04Sbouyer {
1950367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
1960367ea04Sbouyer 	daddr_t off = ufs_rw64(*offp, needswap);
1970367ea04Sbouyer 	struct buf *bp, *obp = hbp;
1980367ea04Sbouyer 	int ret = 0, ret2 = 0;
1990367ea04Sbouyer 	struct quota2_entry *q2e;
2000367ea04Sbouyer 	daddr_t lblkno, blkoff, olblkno = 0;
2010367ea04Sbouyer 
202613afde7Sriastradh 	KASSERT(mutex_owned(&dqlock));
2030367ea04Sbouyer 
2040367ea04Sbouyer 	while (off != 0) {
2050367ea04Sbouyer 		lblkno = (off >> ump->um_mountp->mnt_fs_bshift);
2060367ea04Sbouyer 		blkoff = (off & ump->umq2_bmask);
2070367ea04Sbouyer 		if (lblkno == 0) {
2080367ea04Sbouyer 			/* in the header block */
2090367ea04Sbouyer 			bp = hbp;
2100367ea04Sbouyer 		} else if (lblkno == olblkno) {
2110367ea04Sbouyer 			/* still in the same buf */
2120367ea04Sbouyer 			bp = obp;
2130367ea04Sbouyer 		} else {
2140367ea04Sbouyer 			ret = bread(ump->um_quotas[type], lblkno,
215f61b6169Smaxv 			    ump->umq2_bsize, flags, &bp);
2160367ea04Sbouyer 			if (ret)
2170367ea04Sbouyer 				return ret;
2180367ea04Sbouyer 			if (bp->b_resid != 0) {
219*d51593aaSriastradh 				panic("%s: %s quota file corrupted",
220*d51593aaSriastradh 				    __func__, quotatypes[type]);
2210367ea04Sbouyer 			}
2220367ea04Sbouyer 		}
2230367ea04Sbouyer 		q2e = (void *)((char *)(bp->b_data) + blkoff);
2240367ea04Sbouyer 		ret = (*func)(ump, offp, q2e, off, a);
2250367ea04Sbouyer 		if (off != ufs_rw64(*offp, needswap)) {
2260367ea04Sbouyer 			/* callback changed parent's pointer, redo */
2270367ea04Sbouyer 			off = ufs_rw64(*offp, needswap);
2280367ea04Sbouyer 			if (bp != hbp && bp != obp)
2290367ea04Sbouyer 				ret2 = bwrite(bp);
2300367ea04Sbouyer 		} else {
2310367ea04Sbouyer 			/* parent if now current */
2320367ea04Sbouyer 			if (obp != bp && obp != hbp) {
2330367ea04Sbouyer 				if (flags & B_MODIFY)
2340367ea04Sbouyer 					ret2 = bwrite(obp);
2350367ea04Sbouyer 				else
2360367ea04Sbouyer 					brelse(obp, 0);
2370367ea04Sbouyer 			}
2380367ea04Sbouyer 			obp = bp;
2390367ea04Sbouyer 			olblkno = lblkno;
2400367ea04Sbouyer 			offp = &(q2e->q2e_next);
2410367ea04Sbouyer 			off = ufs_rw64(*offp, needswap);
2420367ea04Sbouyer 		}
2430367ea04Sbouyer 		if (ret)
2440367ea04Sbouyer 			break;
2450367ea04Sbouyer 		if (ret2) {
2460367ea04Sbouyer 			ret = ret2;
2470367ea04Sbouyer 			break;
2480367ea04Sbouyer 		}
2490367ea04Sbouyer 	}
2500367ea04Sbouyer 	if (obp != hbp) {
2510367ea04Sbouyer 		if (flags & B_MODIFY)
2520367ea04Sbouyer 			ret2 = bwrite(obp);
2530367ea04Sbouyer 		else
2540367ea04Sbouyer 			brelse(obp, 0);
2550367ea04Sbouyer 	}
2560367ea04Sbouyer 	if (ret & Q2WL_ABORT)
2570367ea04Sbouyer 		return 0;
2580367ea04Sbouyer 	if (ret == 0)
2590367ea04Sbouyer 		return ret2;
2600367ea04Sbouyer 	return ret;
2610367ea04Sbouyer }
2620367ea04Sbouyer 
2630367ea04Sbouyer int
quota2_umount(struct mount * mp,int flags)2640367ea04Sbouyer quota2_umount(struct mount *mp, int flags)
2650367ea04Sbouyer {
2660367ea04Sbouyer 	int i, error;
2670367ea04Sbouyer 	struct ufsmount *ump = VFSTOUFS(mp);
2680367ea04Sbouyer 
2690367ea04Sbouyer 	if ((ump->um_flags & UFS_QUOTA2) == 0)
2700367ea04Sbouyer 		return 0;
2710367ea04Sbouyer 
2720367ea04Sbouyer 	for (i = 0; i < MAXQUOTAS; i++) {
2730367ea04Sbouyer 		if (ump->um_quotas[i] != NULLVP) {
2740367ea04Sbouyer 			error = vn_close(ump->um_quotas[i], FREAD|FWRITE,
2750367ea04Sbouyer 			    ump->um_cred[i]);
2760367ea04Sbouyer 			if (error) {
2770367ea04Sbouyer 				printf("quota2_umount failed: close(%p) %d\n",
2780367ea04Sbouyer 				    ump->um_quotas[i], error);
2790367ea04Sbouyer 				return error;
2800367ea04Sbouyer 			}
2810367ea04Sbouyer 		}
2820367ea04Sbouyer 		ump->um_quotas[i] = NULLVP;
2830367ea04Sbouyer 	}
2840367ea04Sbouyer 	return 0;
2850367ea04Sbouyer }
2860367ea04Sbouyer 
2870367ea04Sbouyer static int
quota2_q2ealloc(struct ufsmount * ump,int type,uid_t uid,struct dquot * dq)288cc2f3cd5Sbouyer quota2_q2ealloc(struct ufsmount *ump, int type, uid_t uid, struct dquot *dq)
2890367ea04Sbouyer {
2900367ea04Sbouyer 	int error, error2;
2910367ea04Sbouyer 	struct buf *hbp, *bp;
2920367ea04Sbouyer 	struct quota2_header *q2h;
2930367ea04Sbouyer 	struct quota2_entry *q2e;
2940367ea04Sbouyer 	daddr_t offset;
2950367ea04Sbouyer 	u_long hash_mask;
2960367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
2970367ea04Sbouyer 
2980367ea04Sbouyer 	KASSERT(mutex_owned(&dq->dq_interlock));
2990367ea04Sbouyer 	KASSERT(mutex_owned(&dqlock));
3000367ea04Sbouyer 	error = getq2h(ump, type, &hbp, &q2h, B_MODIFY);
3010367ea04Sbouyer 	if (error)
3020367ea04Sbouyer 		return error;
3030367ea04Sbouyer 	offset = ufs_rw64(q2h->q2h_free, needswap);
3040367ea04Sbouyer 	if (offset == 0) {
3050367ea04Sbouyer 		struct vnode *vp = ump->um_quotas[type];
3060367ea04Sbouyer 		struct inode *ip = VTOI(vp);
3070367ea04Sbouyer 		uint64_t size = ip->i_size;
3087a06cadaSandvar 		/* need to allocate a new disk block */
3090367ea04Sbouyer 		error = UFS_BALLOC(vp, size, ump->umq2_bsize,
3100367ea04Sbouyer 		    ump->um_cred[type], B_CLRBUF | B_SYNC, &bp);
3110367ea04Sbouyer 		if (error) {
3120367ea04Sbouyer 			brelse(hbp, 0);
3130367ea04Sbouyer 			return error;
3140367ea04Sbouyer 		}
3150367ea04Sbouyer 		KASSERT((ip->i_size % ump->umq2_bsize) == 0);
3160367ea04Sbouyer 		ip->i_size += ump->umq2_bsize;
3170367ea04Sbouyer 		DIP_ASSIGN(ip, size, ip->i_size);
3180367ea04Sbouyer 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
3190367ea04Sbouyer 		uvm_vnp_setsize(vp, ip->i_size);
3200367ea04Sbouyer 		quota2_addfreeq2e(q2h, bp->b_data, size, ump->umq2_bsize,
3210367ea04Sbouyer 		    needswap);
3220367ea04Sbouyer 		error = bwrite(bp);
3230367ea04Sbouyer 		error2 = UFS_UPDATE(vp, NULL, NULL, UPDATE_WAIT);
3240367ea04Sbouyer 		if (error || error2) {
3250367ea04Sbouyer 			brelse(hbp, 0);
3260367ea04Sbouyer 			if (error)
3270367ea04Sbouyer 				return error;
3280367ea04Sbouyer 			return error2;
3290367ea04Sbouyer 		}
3300367ea04Sbouyer 		offset = ufs_rw64(q2h->q2h_free, needswap);
3310367ea04Sbouyer 		KASSERT(offset != 0);
3320367ea04Sbouyer 	}
3330367ea04Sbouyer 	dq->dq2_lblkno = (offset >> ump->um_mountp->mnt_fs_bshift);
3340367ea04Sbouyer 	dq->dq2_blkoff = (offset & ump->umq2_bmask);
3350367ea04Sbouyer 	if (dq->dq2_lblkno == 0) {
3360367ea04Sbouyer 		bp = hbp;
3370367ea04Sbouyer 		q2e = (void *)((char *)bp->b_data + dq->dq2_blkoff);
3380367ea04Sbouyer 	} else {
3390367ea04Sbouyer 		error = getq2e(ump, type, dq->dq2_lblkno,
3400367ea04Sbouyer 		    dq->dq2_blkoff, &bp, &q2e, B_MODIFY);
3410367ea04Sbouyer 		if (error) {
3420367ea04Sbouyer 			brelse(hbp, 0);
3430367ea04Sbouyer 			return error;
3440367ea04Sbouyer 		}
3450367ea04Sbouyer 	}
3460367ea04Sbouyer 	hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
3470367ea04Sbouyer 	/* remove from free list */
3480367ea04Sbouyer 	q2h->q2h_free = q2e->q2e_next;
3490367ea04Sbouyer 
3500367ea04Sbouyer 	memcpy(q2e, &q2h->q2h_defentry, sizeof(*q2e));
3510367ea04Sbouyer 	q2e->q2e_uid = ufs_rw32(uid, needswap);
3520367ea04Sbouyer 	/* insert in hash list */
3530367ea04Sbouyer 	q2e->q2e_next = q2h->q2h_entries[uid & hash_mask];
3540367ea04Sbouyer 	q2h->q2h_entries[uid & hash_mask] = ufs_rw64(offset, needswap);
3550367ea04Sbouyer 	if (hbp != bp) {
3560367ea04Sbouyer 		bwrite(hbp);
3570367ea04Sbouyer 	}
358cc2f3cd5Sbouyer 	bwrite(bp);
3590367ea04Sbouyer 	return 0;
3600367ea04Sbouyer }
3610367ea04Sbouyer 
3620367ea04Sbouyer static int
getinoquota2(struct inode * ip,bool alloc,bool modify,struct buf ** bpp,struct quota2_entry ** q2ep)3630367ea04Sbouyer getinoquota2(struct inode *ip, bool alloc, bool modify, struct buf **bpp,
3640367ea04Sbouyer     struct quota2_entry **q2ep)
3650367ea04Sbouyer {
3660367ea04Sbouyer 	int error;
3670367ea04Sbouyer 	int i;
3680367ea04Sbouyer 	struct dquot *dq;
3690367ea04Sbouyer 	struct ufsmount *ump = ip->i_ump;
3700367ea04Sbouyer 	u_int32_t ino_ids[MAXQUOTAS];
3710367ea04Sbouyer 
3720367ea04Sbouyer 	error = getinoquota(ip);
3730367ea04Sbouyer 	if (error)
3740367ea04Sbouyer 		return error;
3750367ea04Sbouyer 
3760367ea04Sbouyer 	if (alloc) {
3770367ea04Sbouyer 		UFS_WAPBL_JLOCK_ASSERT(ump->um_mountp);
3780367ea04Sbouyer 	}
3790367ea04Sbouyer         ino_ids[USRQUOTA] = ip->i_uid;
3800367ea04Sbouyer         ino_ids[GRPQUOTA] = ip->i_gid;
3810367ea04Sbouyer 	/* first get the interlock for all dquot */
3820367ea04Sbouyer 	for (i = 0; i < MAXQUOTAS; i++) {
3830367ea04Sbouyer 		dq = ip->i_dquot[i];
3840367ea04Sbouyer 		if (dq == NODQUOT)
3850367ea04Sbouyer 			continue;
3860367ea04Sbouyer 		mutex_enter(&dq->dq_interlock);
3870367ea04Sbouyer 	}
3880367ea04Sbouyer 	/* now get the corresponding quota entry */
3890367ea04Sbouyer 	for (i = 0; i < MAXQUOTAS; i++) {
3900367ea04Sbouyer 		bpp[i] = NULL;
3910367ea04Sbouyer 		q2ep[i] = NULL;
3920367ea04Sbouyer 		dq = ip->i_dquot[i];
3930367ea04Sbouyer 		if (dq == NODQUOT)
3940367ea04Sbouyer 			continue;
3950367ea04Sbouyer 		if (__predict_false(ump->um_quotas[i] == NULL)) {
3960367ea04Sbouyer 			/*
3970367ea04Sbouyer 			 * quotas have been turned off. This can happen
3980367ea04Sbouyer 			 * at umount time.
3990367ea04Sbouyer 			 */
4000367ea04Sbouyer 			mutex_exit(&dq->dq_interlock);
4010367ea04Sbouyer 			dqrele(NULLVP, dq);
4020367ea04Sbouyer 			ip->i_dquot[i] = NULL;
4030367ea04Sbouyer 			continue;
4040367ea04Sbouyer 		}
4050367ea04Sbouyer 
4060367ea04Sbouyer 		if ((dq->dq2_lblkno | dq->dq2_blkoff) == 0) {
4070367ea04Sbouyer 			if (!alloc) {
4080367ea04Sbouyer 				continue;
4090367ea04Sbouyer 			}
4100367ea04Sbouyer 			/* need to alloc a new on-disk quot */
4110367ea04Sbouyer 			mutex_enter(&dqlock);
412cc2f3cd5Sbouyer 			error = quota2_q2ealloc(ump, i, ino_ids[i], dq);
4130367ea04Sbouyer 			mutex_exit(&dqlock);
4140367ea04Sbouyer 			if (error)
4150367ea04Sbouyer 				return error;
416cc2f3cd5Sbouyer 		}
417cc2f3cd5Sbouyer 		KASSERT(dq->dq2_lblkno != 0 || dq->dq2_blkoff != 0);
4180367ea04Sbouyer 		error = getq2e(ump, i, dq->dq2_lblkno,
4190367ea04Sbouyer 		    dq->dq2_blkoff, &bpp[i], &q2ep[i],
4200367ea04Sbouyer 		    modify ? B_MODIFY : 0);
4210367ea04Sbouyer 		if (error)
4220367ea04Sbouyer 			return error;
4230367ea04Sbouyer 	}
4240367ea04Sbouyer 	return 0;
4250367ea04Sbouyer }
4260367ea04Sbouyer 
427f55aa32fSdholland __inline static int __unused
quota2_check_limit(struct quota2_val * q2v,uint64_t change,time_t now)428f55aa32fSdholland quota2_check_limit(struct quota2_val *q2v, uint64_t change, time_t now)
429f55aa32fSdholland {
430f55aa32fSdholland 	return quota_check_limit(q2v->q2v_cur, change, q2v->q2v_softlimit,
431f55aa32fSdholland 	    q2v->q2v_hardlimit, q2v->q2v_time, now);
432f55aa32fSdholland }
433f55aa32fSdholland 
4340367ea04Sbouyer static int
quota2_check(struct inode * ip,int vtype,int64_t change,kauth_cred_t cred,int flags)4350367ea04Sbouyer quota2_check(struct inode *ip, int vtype, int64_t change, kauth_cred_t cred,
4360367ea04Sbouyer     int flags)
4370367ea04Sbouyer {
4380367ea04Sbouyer 	int error;
4390367ea04Sbouyer 	struct buf *bp[MAXQUOTAS];
4400367ea04Sbouyer 	struct quota2_entry *q2e[MAXQUOTAS];
4410367ea04Sbouyer 	struct quota2_val *q2vp;
4420367ea04Sbouyer 	struct dquot *dq;
4430367ea04Sbouyer 	uint64_t ncurblks;
4440367ea04Sbouyer 	struct ufsmount *ump = ip->i_ump;
4450367ea04Sbouyer 	struct mount *mp = ump->um_mountp;
4460367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
4470367ea04Sbouyer 	int i;
4480367ea04Sbouyer 
4490367ea04Sbouyer 	if ((error = getinoquota2(ip, change > 0, change != 0, bp, q2e)) != 0)
4500367ea04Sbouyer 		return error;
4510367ea04Sbouyer 	if (change == 0) {
4520367ea04Sbouyer 		for (i = 0; i < MAXQUOTAS; i++) {
4530367ea04Sbouyer 			dq = ip->i_dquot[i];
4540367ea04Sbouyer 			if (dq == NODQUOT)
4550367ea04Sbouyer 				continue;
4560367ea04Sbouyer 			if (bp[i])
4570367ea04Sbouyer 				brelse(bp[i], 0);
4580367ea04Sbouyer 			mutex_exit(&dq->dq_interlock);
4590367ea04Sbouyer 		}
4600367ea04Sbouyer 		return 0;
4610367ea04Sbouyer 	}
4620367ea04Sbouyer 	if (change < 0) {
4630367ea04Sbouyer 		for (i = 0; i < MAXQUOTAS; i++) {
4640367ea04Sbouyer 			dq = ip->i_dquot[i];
4650367ea04Sbouyer 			if (dq == NODQUOT)
4660367ea04Sbouyer 				continue;
4670367ea04Sbouyer 			if (q2e[i] == NULL) {
4680367ea04Sbouyer 				mutex_exit(&dq->dq_interlock);
4690367ea04Sbouyer 				continue;
4700367ea04Sbouyer 			}
4710367ea04Sbouyer 			q2vp = &q2e[i]->q2e_val[vtype];
4720367ea04Sbouyer 			ncurblks = ufs_rw64(q2vp->q2v_cur, needswap);
4730367ea04Sbouyer 			if (ncurblks < -change)
4740367ea04Sbouyer 				ncurblks = 0;
4750367ea04Sbouyer 			else
4760367ea04Sbouyer 				ncurblks += change;
4770367ea04Sbouyer 			q2vp->q2v_cur = ufs_rw64(ncurblks, needswap);
4780367ea04Sbouyer 			quota2_bwrite(mp, bp[i]);
4790367ea04Sbouyer 			mutex_exit(&dq->dq_interlock);
4800367ea04Sbouyer 		}
4810367ea04Sbouyer 		return 0;
4820367ea04Sbouyer 	}
4830367ea04Sbouyer 	/* see if the allocation is allowed */
4840367ea04Sbouyer 	for (i = 0; i < MAXQUOTAS; i++) {
4850367ea04Sbouyer 		struct quota2_val q2v;
4860367ea04Sbouyer 		int ql_stat;
4870367ea04Sbouyer 		dq = ip->i_dquot[i];
4880367ea04Sbouyer 		if (dq == NODQUOT)
4890367ea04Sbouyer 			continue;
4900367ea04Sbouyer 		KASSERT(q2e[i] != NULL);
4910367ea04Sbouyer 		quota2_ufs_rwq2v(&q2e[i]->q2e_val[vtype], &q2v, needswap);
4920367ea04Sbouyer 		ql_stat = quota2_check_limit(&q2v, change, time_second);
4930367ea04Sbouyer 
4940367ea04Sbouyer 		if ((flags & FORCE) == 0 &&
4950367ea04Sbouyer 		    kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
4960367ea04Sbouyer 			KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
4970367ea04Sbouyer 			KAUTH_ARG(i), KAUTH_ARG(vtype), NULL) != 0) {
4980367ea04Sbouyer 			/* enforce this limit */
4990367ea04Sbouyer 			switch(QL_STATUS(ql_stat)) {
5000367ea04Sbouyer 			case QL_S_DENY_HARD:
5010367ea04Sbouyer 				if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
5020367ea04Sbouyer 					uprintf("\n%s: write failed, %s %s "
5030367ea04Sbouyer 					    "limit reached\n",
5040367ea04Sbouyer 					    mp->mnt_stat.f_mntonname,
505bec3b862Sbouyer 					    quotatypes[i], limnames[vtype]);
5060367ea04Sbouyer 					dq->dq_flags |= DQ_WARN(vtype);
5070367ea04Sbouyer 				}
5080367ea04Sbouyer 				error = EDQUOT;
5090367ea04Sbouyer 				break;
5100367ea04Sbouyer 			case QL_S_DENY_GRACE:
5110367ea04Sbouyer 				if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
5120367ea04Sbouyer 					uprintf("\n%s: write failed, %s %s "
5130367ea04Sbouyer 					    "limit reached\n",
5140367ea04Sbouyer 					    mp->mnt_stat.f_mntonname,
515bec3b862Sbouyer 					    quotatypes[i], limnames[vtype]);
5160367ea04Sbouyer 					dq->dq_flags |= DQ_WARN(vtype);
5170367ea04Sbouyer 				}
5180367ea04Sbouyer 				error = EDQUOT;
5190367ea04Sbouyer 				break;
5200367ea04Sbouyer 			case QL_S_ALLOW_SOFT:
5210367ea04Sbouyer 				if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
5220367ea04Sbouyer 					uprintf("\n%s: warning, %s %s "
5230367ea04Sbouyer 					    "quota exceeded\n",
5240367ea04Sbouyer 					    mp->mnt_stat.f_mntonname,
525bec3b862Sbouyer 					    quotatypes[i], limnames[vtype]);
5260367ea04Sbouyer 					dq->dq_flags |= DQ_WARN(vtype);
5270367ea04Sbouyer 				}
5280367ea04Sbouyer 				break;
5290367ea04Sbouyer 			}
5300367ea04Sbouyer 		}
5310367ea04Sbouyer 		/*
5320367ea04Sbouyer 		 * always do this; we don't know if the allocation will
5330367ea04Sbouyer 		 * succed or not in the end. if we don't do the allocation
5340367ea04Sbouyer 		 * q2v_time will be ignored anyway
5350367ea04Sbouyer 		 */
5360367ea04Sbouyer 		if (ql_stat & QL_F_CROSS) {
5370367ea04Sbouyer 			q2v.q2v_time = time_second + q2v.q2v_grace;
5380367ea04Sbouyer 			quota2_ufs_rwq2v(&q2v, &q2e[i]->q2e_val[vtype],
5390367ea04Sbouyer 			    needswap);
5400367ea04Sbouyer 		}
5410367ea04Sbouyer 	}
5420367ea04Sbouyer 
5430367ea04Sbouyer 	/* now do the allocation if allowed */
5440367ea04Sbouyer 	for (i = 0; i < MAXQUOTAS; i++) {
5450367ea04Sbouyer 		dq = ip->i_dquot[i];
5460367ea04Sbouyer 		if (dq == NODQUOT)
5470367ea04Sbouyer 			continue;
5480367ea04Sbouyer 		KASSERT(q2e[i] != NULL);
5490367ea04Sbouyer 		if (error == 0) {
5500367ea04Sbouyer 			q2vp = &q2e[i]->q2e_val[vtype];
5510367ea04Sbouyer 			ncurblks = ufs_rw64(q2vp->q2v_cur, needswap);
5520367ea04Sbouyer 			q2vp->q2v_cur = ufs_rw64(ncurblks + change, needswap);
5530367ea04Sbouyer 			quota2_bwrite(mp, bp[i]);
5540367ea04Sbouyer 		} else
5550367ea04Sbouyer 			brelse(bp[i], 0);
5560367ea04Sbouyer 		mutex_exit(&dq->dq_interlock);
5570367ea04Sbouyer 	}
5580367ea04Sbouyer 	return error;
5590367ea04Sbouyer }
5600367ea04Sbouyer 
5610367ea04Sbouyer int
chkdq2(struct inode * ip,int64_t change,kauth_cred_t cred,int flags)5620367ea04Sbouyer chkdq2(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
5630367ea04Sbouyer {
5640367ea04Sbouyer 	return quota2_check(ip, QL_BLOCK, change, cred, flags);
5650367ea04Sbouyer }
5660367ea04Sbouyer 
5670367ea04Sbouyer int
chkiq2(struct inode * ip,int32_t change,kauth_cred_t cred,int flags)5680367ea04Sbouyer chkiq2(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
5690367ea04Sbouyer {
5700367ea04Sbouyer 	return quota2_check(ip, QL_FILE, change, cred, flags);
5710367ea04Sbouyer }
5720367ea04Sbouyer 
5730367ea04Sbouyer int
quota2_handle_cmd_put(struct ufsmount * ump,const struct quotakey * key,const struct quotaval * val)57494c0844cSdholland quota2_handle_cmd_put(struct ufsmount *ump, const struct quotakey *key,
575fb0b74c8Sdholland     const struct quotaval *val)
5760367ea04Sbouyer {
5770367ea04Sbouyer 	int error;
5780367ea04Sbouyer 	struct dquot *dq;
5790367ea04Sbouyer 	struct quota2_header *q2h;
5800367ea04Sbouyer 	struct quota2_entry q2e, *q2ep;
5810367ea04Sbouyer 	struct buf *bp;
5820367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
5830367ea04Sbouyer 
584fb0b74c8Sdholland 	/* make sure we can index by the fs-independent idtype */
585fb0b74c8Sdholland 	CTASSERT(QUOTA_IDTYPE_USER == USRQUOTA);
586fb0b74c8Sdholland 	CTASSERT(QUOTA_IDTYPE_GROUP == GRPQUOTA);
587fb0b74c8Sdholland 
588fb0b74c8Sdholland 	if (ump->um_quotas[key->qk_idtype] == NULLVP)
5890367ea04Sbouyer 		return ENODEV;
5900367ea04Sbouyer 	error = UFS_WAPBL_BEGIN(ump->um_mountp);
5910367ea04Sbouyer 	if (error)
5920367ea04Sbouyer 		return error;
5930367ea04Sbouyer 
594fb0b74c8Sdholland 	if (key->qk_id == QUOTA_DEFAULTID) {
5950367ea04Sbouyer 		mutex_enter(&dqlock);
596fb0b74c8Sdholland 		error = getq2h(ump, key->qk_idtype, &bp, &q2h, B_MODIFY);
5970367ea04Sbouyer 		if (error) {
5980367ea04Sbouyer 			mutex_exit(&dqlock);
5990367ea04Sbouyer 			goto out_wapbl;
6000367ea04Sbouyer 		}
6010367ea04Sbouyer 		quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
602fb0b74c8Sdholland 		quota2_dict_update_q2e_limits(key->qk_objtype, val, &q2e);
6030367ea04Sbouyer 		quota2_ufs_rwq2e(&q2e, &q2h->q2h_defentry, needswap);
6040367ea04Sbouyer 		mutex_exit(&dqlock);
6050367ea04Sbouyer 		quota2_bwrite(ump->um_mountp, bp);
6060367ea04Sbouyer 		goto out_wapbl;
6070367ea04Sbouyer 	}
6080367ea04Sbouyer 
609fb0b74c8Sdholland 	error = dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq);
6100367ea04Sbouyer 	if (error)
6110367ea04Sbouyer 		goto out_wapbl;
6120367ea04Sbouyer 
6130367ea04Sbouyer 	mutex_enter(&dq->dq_interlock);
6140367ea04Sbouyer 	if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
6150367ea04Sbouyer 		/* need to alloc a new on-disk quot */
6160367ea04Sbouyer 		mutex_enter(&dqlock);
617cc2f3cd5Sbouyer 		error = quota2_q2ealloc(ump, key->qk_idtype, key->qk_id, dq);
6180367ea04Sbouyer 		mutex_exit(&dqlock);
619cc2f3cd5Sbouyer 		if (error)
620cc2f3cd5Sbouyer 			goto out_il;
621cc2f3cd5Sbouyer 	}
622cc2f3cd5Sbouyer 	KASSERT(dq->dq2_lblkno != 0 || dq->dq2_blkoff != 0);
623fb0b74c8Sdholland 	error = getq2e(ump, key->qk_idtype, dq->dq2_lblkno,
624fb0b74c8Sdholland 	    dq->dq2_blkoff, &bp, &q2ep, B_MODIFY);
6250367ea04Sbouyer 	if (error)
6260367ea04Sbouyer 		goto out_il;
6270367ea04Sbouyer 
6280367ea04Sbouyer 	quota2_ufs_rwq2e(q2ep, &q2e, needswap);
629d73ca53aSbouyer 	/*
630d73ca53aSbouyer 	 * Reset time limit if previously had no soft limit or were
631d73ca53aSbouyer 	 * under it, but now have a soft limit and are over it.
632d73ca53aSbouyer 	 */
633d73ca53aSbouyer 	if (val->qv_softlimit &&
634d73ca53aSbouyer 	    q2e.q2e_val[key->qk_objtype].q2v_cur >= val->qv_softlimit &&
635d73ca53aSbouyer 	    (q2e.q2e_val[key->qk_objtype].q2v_softlimit == 0 ||
636*d51593aaSriastradh 		(q2e.q2e_val[key->qk_objtype].q2v_cur <
637*d51593aaSriastradh 		    q2e.q2e_val[key->qk_objtype].q2v_softlimit))) {
638*d51593aaSriastradh 		q2e.q2e_val[key->qk_objtype].q2v_time =
639*d51593aaSriastradh 		    time_second + val->qv_grace;
640*d51593aaSriastradh 	}
641fb0b74c8Sdholland 	quota2_dict_update_q2e_limits(key->qk_objtype, val, &q2e);
6420367ea04Sbouyer 	quota2_ufs_rwq2e(&q2e, q2ep, needswap);
6430367ea04Sbouyer 	quota2_bwrite(ump->um_mountp, bp);
6440367ea04Sbouyer 
6450367ea04Sbouyer out_il:
6460367ea04Sbouyer 	mutex_exit(&dq->dq_interlock);
6470367ea04Sbouyer 	dqrele(NULLVP, dq);
6480367ea04Sbouyer out_wapbl:
6490367ea04Sbouyer 	UFS_WAPBL_END(ump->um_mountp);
6500367ea04Sbouyer 	return error;
6510367ea04Sbouyer }
6520367ea04Sbouyer 
6530367ea04Sbouyer struct dq2clear_callback {
6540367ea04Sbouyer 	uid_t id;
6550367ea04Sbouyer 	struct dquot *dq;
6560367ea04Sbouyer 	struct quota2_header *q2h;
6570367ea04Sbouyer };
6580367ea04Sbouyer 
6590367ea04Sbouyer static int
dq2clear_callback(struct ufsmount * ump,uint64_t * offp,struct quota2_entry * q2e,uint64_t off,void * v)660*d51593aaSriastradh dq2clear_callback(struct ufsmount *ump, uint64_t *offp,
661*d51593aaSriastradh     struct quota2_entry *q2e,
6620367ea04Sbouyer     uint64_t off, void *v)
6630367ea04Sbouyer {
6640367ea04Sbouyer 	struct dq2clear_callback *c = v;
6650367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
6660367ea04Sbouyer 	uint64_t myoff;
6670367ea04Sbouyer 
6680367ea04Sbouyer 	if (ufs_rw32(q2e->q2e_uid, needswap) == c->id) {
6690367ea04Sbouyer 		KASSERT(mutex_owned(&c->dq->dq_interlock));
6700367ea04Sbouyer 		c->dq->dq2_lblkno = 0;
6710367ea04Sbouyer 		c->dq->dq2_blkoff = 0;
6720367ea04Sbouyer 		myoff = *offp;
6730367ea04Sbouyer 		/* remove from hash list */
6740367ea04Sbouyer 		*offp = q2e->q2e_next;
6750367ea04Sbouyer 		/* add to free list */
6760367ea04Sbouyer 		q2e->q2e_next = c->q2h->q2h_free;
6770367ea04Sbouyer 		c->q2h->q2h_free = myoff;
6780367ea04Sbouyer 		return Q2WL_ABORT;
6790367ea04Sbouyer 	}
6800367ea04Sbouyer 	return 0;
6810367ea04Sbouyer }
6820367ea04Sbouyer int
quota2_handle_cmd_del(struct ufsmount * ump,const struct quotakey * qk)68330cc8b98Sdholland quota2_handle_cmd_del(struct ufsmount *ump, const struct quotakey *qk)
6840367ea04Sbouyer {
685974936fbSdholland 	int idtype;
686974936fbSdholland 	id_t id;
687974936fbSdholland 	int objtype;
688d12c9faaSdholland 	int error, i, canfree;
6890367ea04Sbouyer 	struct dquot *dq;
6900367ea04Sbouyer 	struct quota2_header *q2h;
6910367ea04Sbouyer 	struct quota2_entry q2e, *q2ep;
6920367ea04Sbouyer 	struct buf *hbp, *bp;
6930367ea04Sbouyer 	u_long hash_mask;
6940367ea04Sbouyer 	struct dq2clear_callback c;
6950367ea04Sbouyer 
696974936fbSdholland 	idtype = qk->qk_idtype;
697974936fbSdholland 	id = qk->qk_id;
698974936fbSdholland 	objtype = qk->qk_objtype;
699974936fbSdholland 
700611884a2Sdholland 	if (ump->um_quotas[idtype] == NULLVP)
7010367ea04Sbouyer 		return ENODEV;
702974936fbSdholland 	if (id == QUOTA_DEFAULTID)
7030367ea04Sbouyer 		return EOPNOTSUPP;
7040367ea04Sbouyer 
7050367ea04Sbouyer 	/* get the default entry before locking the entry's buffer */
7060367ea04Sbouyer 	mutex_enter(&dqlock);
707611884a2Sdholland 	error = getq2h(ump, idtype, &hbp, &q2h, 0);
7080367ea04Sbouyer 	if (error) {
7090367ea04Sbouyer 		mutex_exit(&dqlock);
7100367ea04Sbouyer 		return error;
7110367ea04Sbouyer 	}
7120367ea04Sbouyer 	/* we'll copy to another disk entry, so no need to swap */
7130367ea04Sbouyer 	memcpy(&q2e, &q2h->q2h_defentry, sizeof(q2e));
7140367ea04Sbouyer 	mutex_exit(&dqlock);
7150367ea04Sbouyer 	brelse(hbp, 0);
7160367ea04Sbouyer 
717611884a2Sdholland 	error = dqget(NULLVP, id, ump, idtype, &dq);
7180367ea04Sbouyer 	if (error)
7190367ea04Sbouyer 		return error;
7200367ea04Sbouyer 
7210367ea04Sbouyer 	mutex_enter(&dq->dq_interlock);
7220367ea04Sbouyer 	if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
7230367ea04Sbouyer 		/* already clear, nothing to do */
7240367ea04Sbouyer 		error = ENOENT;
7250367ea04Sbouyer 		goto out_il;
7260367ea04Sbouyer 	}
7270367ea04Sbouyer 	error = UFS_WAPBL_BEGIN(ump->um_mountp);
7280367ea04Sbouyer 	if (error)
7290367ea04Sbouyer 		goto out_dq;
7300367ea04Sbouyer 
731611884a2Sdholland 	error = getq2e(ump, idtype, dq->dq2_lblkno, dq->dq2_blkoff,
7320367ea04Sbouyer 	    &bp, &q2ep, B_MODIFY);
7330367ea04Sbouyer 	if (error)
7340367ea04Sbouyer 		goto out_wapbl;
7350367ea04Sbouyer 
736d12c9faaSdholland 	/* make sure we can index by the objtype passed in */
737d12c9faaSdholland 	CTASSERT(QUOTA_OBJTYPE_BLOCKS == QL_BLOCK);
738d12c9faaSdholland 	CTASSERT(QUOTA_OBJTYPE_FILES == QL_FILE);
739d12c9faaSdholland 
740d12c9faaSdholland 	/* clear the requested objtype by copying from the default entry */
741d12c9faaSdholland 	q2ep->q2e_val[objtype].q2v_softlimit =
742d12c9faaSdholland 		q2e.q2e_val[objtype].q2v_softlimit;
743d12c9faaSdholland 	q2ep->q2e_val[objtype].q2v_hardlimit =
744d12c9faaSdholland 		q2e.q2e_val[objtype].q2v_hardlimit;
745d12c9faaSdholland 	q2ep->q2e_val[objtype].q2v_grace =
746d12c9faaSdholland 		q2e.q2e_val[objtype].q2v_grace;
747d12c9faaSdholland 	q2ep->q2e_val[objtype].q2v_time = 0;
748d12c9faaSdholland 
749d12c9faaSdholland 	/* if this entry now contains no information, we can free it */
750d12c9faaSdholland 	canfree = 1;
7510367ea04Sbouyer 	for (i = 0; i < N_QL; i++) {
752d12c9faaSdholland 		if (q2ep->q2e_val[i].q2v_cur != 0 ||
753d12c9faaSdholland 		    (q2ep->q2e_val[i].q2v_softlimit !=
754d12c9faaSdholland 		     q2e.q2e_val[i].q2v_softlimit) ||
755d12c9faaSdholland 		    (q2ep->q2e_val[i].q2v_hardlimit !=
756d12c9faaSdholland 		     q2e.q2e_val[i].q2v_hardlimit) ||
757d12c9faaSdholland 		    (q2ep->q2e_val[i].q2v_grace !=
758d12c9faaSdholland 		     q2e.q2e_val[i].q2v_grace)) {
759d12c9faaSdholland 			canfree = 0;
760d12c9faaSdholland 			break;
7610367ea04Sbouyer 		}
762d12c9faaSdholland 		/* note: do not need to check q2v_time */
763d12c9faaSdholland 	}
764d12c9faaSdholland 
765d12c9faaSdholland 	if (canfree == 0) {
7660367ea04Sbouyer 		quota2_bwrite(ump->um_mountp, bp);
7670367ea04Sbouyer 		goto out_wapbl;
7680367ea04Sbouyer 	}
7690367ea04Sbouyer 	/* we can free it. release bp so we can walk the list */
7700367ea04Sbouyer 	brelse(bp, 0);
7710367ea04Sbouyer 	mutex_enter(&dqlock);
772611884a2Sdholland 	error = getq2h(ump, idtype, &hbp, &q2h, 0);
7730367ea04Sbouyer 	if (error)
7740367ea04Sbouyer 		goto out_dqlock;
7750367ea04Sbouyer 
7760367ea04Sbouyer 	hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
7770367ea04Sbouyer 	c.dq = dq;
7780367ea04Sbouyer 	c.id = id;
7790367ea04Sbouyer 	c.q2h = q2h;
780611884a2Sdholland 	error = quota2_walk_list(ump, hbp, idtype,
7810367ea04Sbouyer 	    &q2h->q2h_entries[id & hash_mask], B_MODIFY, &c,
7820367ea04Sbouyer 	    dq2clear_callback);
7830367ea04Sbouyer 
7840367ea04Sbouyer 	bwrite(hbp);
7850367ea04Sbouyer 
7860367ea04Sbouyer out_dqlock:
7870367ea04Sbouyer 	mutex_exit(&dqlock);
7880367ea04Sbouyer out_wapbl:
7890367ea04Sbouyer 	UFS_WAPBL_END(ump->um_mountp);
7900367ea04Sbouyer out_il:
7910367ea04Sbouyer 	mutex_exit(&dq->dq_interlock);
7920367ea04Sbouyer out_dq:
7930367ea04Sbouyer 	dqrele(NULLVP, dq);
7940367ea04Sbouyer 	return error;
7950367ea04Sbouyer }
7960367ea04Sbouyer 
7970367ea04Sbouyer static int
quota2_fetch_q2e(struct ufsmount * ump,const struct quotakey * qk,struct quota2_entry * ret)7985acd5a8bSdholland quota2_fetch_q2e(struct ufsmount *ump, const struct quotakey *qk,
7995acd5a8bSdholland     struct quota2_entry *ret)
8000367ea04Sbouyer {
8010367ea04Sbouyer 	struct dquot *dq;
8020367ea04Sbouyer 	int error;
8035acd5a8bSdholland 	struct quota2_entry *q2ep;
8040367ea04Sbouyer 	struct buf *bp;
8050367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
8060367ea04Sbouyer 
8075acd5a8bSdholland 	error = dqget(NULLVP, qk->qk_id, ump, qk->qk_idtype, &dq);
8080367ea04Sbouyer 	if (error)
8090367ea04Sbouyer 		return error;
8100367ea04Sbouyer 
8110367ea04Sbouyer 	mutex_enter(&dq->dq_interlock);
8120367ea04Sbouyer 	if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
8130367ea04Sbouyer 		mutex_exit(&dq->dq_interlock);
8140367ea04Sbouyer 		dqrele(NULLVP, dq);
8150367ea04Sbouyer 		return ENOENT;
8160367ea04Sbouyer 	}
8175acd5a8bSdholland 	error = getq2e(ump, qk->qk_idtype, dq->dq2_lblkno, dq->dq2_blkoff,
8180367ea04Sbouyer 	    &bp, &q2ep, 0);
8190367ea04Sbouyer 	if (error) {
8200367ea04Sbouyer 		mutex_exit(&dq->dq_interlock);
8210367ea04Sbouyer 		dqrele(NULLVP, dq);
8220367ea04Sbouyer 		return error;
8230367ea04Sbouyer 	}
8245acd5a8bSdholland 	quota2_ufs_rwq2e(q2ep, ret, needswap);
8250367ea04Sbouyer 	brelse(bp, 0);
8260367ea04Sbouyer 	mutex_exit(&dq->dq_interlock);
8270367ea04Sbouyer 	dqrele(NULLVP, dq);
828fb787cb1Sdholland 
8290367ea04Sbouyer 	return 0;
8300367ea04Sbouyer }
8310367ea04Sbouyer 
832fb787cb1Sdholland static int
quota2_fetch_quotaval(struct ufsmount * ump,const struct quotakey * qk,struct quotaval * ret)8335acd5a8bSdholland quota2_fetch_quotaval(struct ufsmount *ump, const struct quotakey *qk,
83461ed0e3fSdholland     struct quotaval *ret)
835fb787cb1Sdholland {
836fb787cb1Sdholland 	struct dquot *dq;
837fb787cb1Sdholland 	int error;
838fb787cb1Sdholland 	struct quota2_entry *q2ep, q2e;
839fb787cb1Sdholland 	struct buf  *bp;
840fb787cb1Sdholland 	const int needswap = UFS_MPNEEDSWAP(ump);
841fb787cb1Sdholland 	id_t id2;
842fb787cb1Sdholland 
84361ed0e3fSdholland 	error = dqget(NULLVP, qk->qk_id, ump, qk->qk_idtype, &dq);
844fb787cb1Sdholland 	if (error)
845fb787cb1Sdholland 		return error;
846fb787cb1Sdholland 
847fb787cb1Sdholland 	mutex_enter(&dq->dq_interlock);
848fb787cb1Sdholland 	if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
849fb787cb1Sdholland 		mutex_exit(&dq->dq_interlock);
850fb787cb1Sdholland 		dqrele(NULLVP, dq);
851fb787cb1Sdholland 		return ENOENT;
852fb787cb1Sdholland 	}
85361ed0e3fSdholland 	error = getq2e(ump, qk->qk_idtype, dq->dq2_lblkno, dq->dq2_blkoff,
854fb787cb1Sdholland 	    &bp, &q2ep, 0);
855fb787cb1Sdholland 	if (error) {
856fb787cb1Sdholland 		mutex_exit(&dq->dq_interlock);
857fb787cb1Sdholland 		dqrele(NULLVP, dq);
858fb787cb1Sdholland 		return error;
859fb787cb1Sdholland 	}
860fb787cb1Sdholland 	quota2_ufs_rwq2e(q2ep, &q2e, needswap);
861fb787cb1Sdholland 	brelse(bp, 0);
862fb787cb1Sdholland 	mutex_exit(&dq->dq_interlock);
863fb787cb1Sdholland 	dqrele(NULLVP, dq);
864fb787cb1Sdholland 
86561ed0e3fSdholland 	q2e_to_quotaval(&q2e, 0, &id2, qk->qk_objtype, ret);
86661ed0e3fSdholland 	KASSERT(id2 == qk->qk_id);
867fb787cb1Sdholland 	return 0;
868fb787cb1Sdholland }
869fb787cb1Sdholland 
8700367ea04Sbouyer int
quota2_handle_cmd_get(struct ufsmount * ump,const struct quotakey * qk,struct quotaval * qv)87161ed0e3fSdholland quota2_handle_cmd_get(struct ufsmount *ump, const struct quotakey *qk,
872bd7570fcSdholland     struct quotaval *qv)
8730367ea04Sbouyer {
8740367ea04Sbouyer 	int error;
8750367ea04Sbouyer 	struct quota2_header *q2h;
8760367ea04Sbouyer 	struct quota2_entry q2e;
8770367ea04Sbouyer 	struct buf *bp;
8780367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
879fb787cb1Sdholland 	id_t id2;
8800367ea04Sbouyer 
88107baaa7aSdholland 	/*
88207baaa7aSdholland 	 * Make sure the FS-independent codes match the internal ones,
88307baaa7aSdholland 	 * so we can use the passed-in objtype without having to
88407baaa7aSdholland 	 * convert it explicitly to QL_BLOCK/QL_FILE.
88507baaa7aSdholland 	 */
88607baaa7aSdholland 	CTASSERT(QL_BLOCK == QUOTA_OBJTYPE_BLOCKS);
88707baaa7aSdholland 	CTASSERT(QL_FILE == QUOTA_OBJTYPE_FILES);
88807baaa7aSdholland 	CTASSERT(N_QL == 2);
88907baaa7aSdholland 
89061ed0e3fSdholland 	if (qk->qk_objtype < 0 || qk->qk_objtype >= N_QL) {
89107baaa7aSdholland 		return EINVAL;
89207baaa7aSdholland 	}
89307baaa7aSdholland 
89461ed0e3fSdholland 	if (ump->um_quotas[qk->qk_idtype] == NULLVP)
8950367ea04Sbouyer 		return ENODEV;
89661ed0e3fSdholland 	if (qk->qk_id == QUOTA_DEFAULTID) {
8970367ea04Sbouyer 		mutex_enter(&dqlock);
89861ed0e3fSdholland 		error = getq2h(ump, qk->qk_idtype, &bp, &q2h, 0);
8990367ea04Sbouyer 		if (error) {
9000367ea04Sbouyer 			mutex_exit(&dqlock);
9010367ea04Sbouyer 			return error;
9020367ea04Sbouyer 		}
9030367ea04Sbouyer 		quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
9040367ea04Sbouyer 		mutex_exit(&dqlock);
9050367ea04Sbouyer 		brelse(bp, 0);
90661ed0e3fSdholland 		q2e_to_quotaval(&q2e, qk->qk_id == QUOTA_DEFAULTID, &id2,
907bd7570fcSdholland 		    qk->qk_objtype, qv);
908fb787cb1Sdholland 		(void)id2;
9090367ea04Sbouyer 	} else
910bd7570fcSdholland 		error = quota2_fetch_quotaval(ump, qk, qv);
9110367ea04Sbouyer 
9120367ea04Sbouyer 	return error;
9130367ea04Sbouyer }
9140367ea04Sbouyer 
9155acd5a8bSdholland /*
9165acd5a8bSdholland  * Cursor structure we used.
9175acd5a8bSdholland  *
9185acd5a8bSdholland  * This will get stored in userland between calls so we must not assume
9195acd5a8bSdholland  * it isn't arbitrarily corrupted.
9205acd5a8bSdholland  */
9214f9db7d3Sdholland struct ufsq2_cursor {
922ce9684d0Sdholland 	uint32_t q2c_magic;	/* magic number */
923ce9684d0Sdholland 	int q2c_hashsize;	/* size of hash table at last go */
924ce9684d0Sdholland 
9259ff2a109Sdholland 	int q2c_users_done;	/* true if we've returned all user data */
9269ff2a109Sdholland 	int q2c_groups_done;	/* true if we've returned all group data */
927ce9684d0Sdholland 	int q2c_defaults_done;	/* true if we've returned the default values */
928ce9684d0Sdholland 	int q2c_hashpos;	/* slot to start at in hash table */
9291f503971Sdholland 	int q2c_uidpos;		/* number of ids we've handled */
930ce9684d0Sdholland 	int q2c_blocks_done;	/* true if we've returned the blocks value */
9314f9db7d3Sdholland };
9324f9db7d3Sdholland 
9335acd5a8bSdholland /*
9345acd5a8bSdholland  * State of a single cursorget call, or at least the part of it that
9355acd5a8bSdholland  * needs to be passed around.
9365acd5a8bSdholland  */
9375acd5a8bSdholland struct q2cursor_state {
9385acd5a8bSdholland 	/* data return pointers */
9395acd5a8bSdholland 	struct quotakey *keys;
9405acd5a8bSdholland 	struct quotaval *vals;
9415acd5a8bSdholland 
9425acd5a8bSdholland 	/* key/value counters */
9435acd5a8bSdholland 	unsigned maxkeyvals;
9445acd5a8bSdholland 	unsigned numkeys;	/* number of keys assigned */
9455acd5a8bSdholland 
9465acd5a8bSdholland 	/* ID to key/value conversion state */
9475acd5a8bSdholland 	int skipfirst;		/* if true skip first key/value */
9485acd5a8bSdholland 	int skiplast;		/* if true skip last key/value */
9495acd5a8bSdholland 
9505acd5a8bSdholland 	/* ID counters */
9515acd5a8bSdholland 	unsigned maxids;	/* maximum number of IDs to handle */
9525acd5a8bSdholland 	unsigned numids;	/* number of IDs handled */
9535acd5a8bSdholland };
9545acd5a8bSdholland 
9555acd5a8bSdholland /*
9565acd5a8bSdholland  * Additional structure for getids callback.
9575acd5a8bSdholland  */
9585acd5a8bSdholland struct q2cursor_getids {
9595acd5a8bSdholland 	struct q2cursor_state *state;
9605acd5a8bSdholland 	int idtype;
9615acd5a8bSdholland 	unsigned skip;		/* number of ids to skip over */
9625acd5a8bSdholland 	unsigned new_skip;	/* number of ids to skip over next time */
9635acd5a8bSdholland 	unsigned skipped;	/* number skipped so far */
964e5409b53Sdholland 	int stopped;		/* true if we stopped quota_walk_list early */
9655acd5a8bSdholland };
9665acd5a8bSdholland 
9675acd5a8bSdholland /*
9685acd5a8bSdholland  * Cursor-related functions
9695acd5a8bSdholland  */
9705acd5a8bSdholland 
9715acd5a8bSdholland /* magic number */
9724f9db7d3Sdholland #define Q2C_MAGIC (0xbeebe111)
9734f9db7d3Sdholland 
9745acd5a8bSdholland /* extract cursor from caller form */
9754f9db7d3Sdholland #define Q2CURSOR(qkc) ((struct ufsq2_cursor *)&qkc->u.qkc_space[0])
9764f9db7d3Sdholland 
9775acd5a8bSdholland /*
9785acd5a8bSdholland  * Check that a cursor we're handed is something like valid. If
9795acd5a8bSdholland  * someone munges it and it still passes these checks, they'll get
9805acd5a8bSdholland  * partial or odd results back but won't break anything.
9815acd5a8bSdholland  */
9821f503971Sdholland static int
q2cursor_check(struct ufsq2_cursor * cursor)9831f503971Sdholland q2cursor_check(struct ufsq2_cursor *cursor)
9841f503971Sdholland {
9851f503971Sdholland 	if (cursor->q2c_magic != Q2C_MAGIC) {
9861f503971Sdholland 		return EINVAL;
9871f503971Sdholland 	}
9881f503971Sdholland 	if (cursor->q2c_hashsize < 0) {
9891f503971Sdholland 		return EINVAL;
9901f503971Sdholland 	}
9911f503971Sdholland 
9929ff2a109Sdholland 	if (cursor->q2c_users_done != 0 && cursor->q2c_users_done != 1) {
9939ff2a109Sdholland 		return EINVAL;
9949ff2a109Sdholland 	}
9959ff2a109Sdholland 	if (cursor->q2c_groups_done != 0 && cursor->q2c_groups_done != 1) {
9969ff2a109Sdholland 		return EINVAL;
9979ff2a109Sdholland 	}
9981f503971Sdholland 	if (cursor->q2c_defaults_done != 0 && cursor->q2c_defaults_done != 1) {
9991f503971Sdholland 		return EINVAL;
10001f503971Sdholland 	}
10011f503971Sdholland 	if (cursor->q2c_hashpos < 0 || cursor->q2c_uidpos < 0) {
10021f503971Sdholland 		return EINVAL;
10031f503971Sdholland 	}
10041f503971Sdholland 	if (cursor->q2c_blocks_done != 0 && cursor->q2c_blocks_done != 1) {
10051f503971Sdholland 		return EINVAL;
10061f503971Sdholland 	}
10071f503971Sdholland 	return 0;
10081f503971Sdholland }
10094f9db7d3Sdholland 
10105acd5a8bSdholland /*
10115acd5a8bSdholland  * Set up the q2cursor state.
10125acd5a8bSdholland  */
10135acd5a8bSdholland static void
q2cursor_initstate(struct q2cursor_state * state,struct quotakey * keys,struct quotaval * vals,unsigned maxkeyvals,int blocks_done)10145acd5a8bSdholland q2cursor_initstate(struct q2cursor_state *state, struct quotakey *keys,
10155acd5a8bSdholland     struct quotaval *vals, unsigned maxkeyvals, int blocks_done)
10165acd5a8bSdholland {
10175acd5a8bSdholland 	state->keys = keys;
10185acd5a8bSdholland 	state->vals = vals;
10190367ea04Sbouyer 
10205acd5a8bSdholland 	state->maxkeyvals = maxkeyvals;
10215acd5a8bSdholland 	state->numkeys = 0;
10225acd5a8bSdholland 
10235acd5a8bSdholland 	/*
10245acd5a8bSdholland 	 * For each ID there are two quotavals to return. If the
10255acd5a8bSdholland 	 * maximum number of entries to return is odd, we might want
10265acd5a8bSdholland 	 * to skip the first quotaval of the first ID, or the last
10275acd5a8bSdholland 	 * quotaval of the last ID, but not both. So the number of IDs
10285acd5a8bSdholland 	 * we want is (up to) half the number of return slots we have,
10295acd5a8bSdholland 	 * rounded up.
10305acd5a8bSdholland 	 */
10315acd5a8bSdholland 
10325acd5a8bSdholland 	state->maxids = (state->maxkeyvals + 1) / 2;
10335acd5a8bSdholland 	state->numids = 0;
10345acd5a8bSdholland 	if (state->maxkeyvals % 2) {
10355acd5a8bSdholland 		if (blocks_done) {
10365acd5a8bSdholland 			state->skipfirst = 1;
10375acd5a8bSdholland 			state->skiplast = 0;
10385acd5a8bSdholland 		} else {
10395acd5a8bSdholland 			state->skipfirst = 0;
10405acd5a8bSdholland 			state->skiplast = 1;
10415acd5a8bSdholland 		}
10425acd5a8bSdholland 	} else {
10435acd5a8bSdholland 		state->skipfirst = 0;
10445acd5a8bSdholland 		state->skiplast = 0;
10455acd5a8bSdholland 	}
10465acd5a8bSdholland }
10475acd5a8bSdholland 
10485acd5a8bSdholland /*
10495acd5a8bSdholland  * Choose which idtype we're going to work on. If doing a full
10505acd5a8bSdholland  * iteration, we do users first, then groups, but either might be
10515acd5a8bSdholland  * disabled or marked to skip via cursorsetidtype(), so don't make
10525acd5a8bSdholland  * silly assumptions.
10535acd5a8bSdholland  */
10540367ea04Sbouyer static int
q2cursor_pickidtype(struct ufsq2_cursor * cursor,int * idtype_ret)10555acd5a8bSdholland q2cursor_pickidtype(struct ufsq2_cursor *cursor, int *idtype_ret)
10565acd5a8bSdholland {
10575acd5a8bSdholland 	if (cursor->q2c_users_done == 0) {
10585acd5a8bSdholland 		*idtype_ret = QUOTA_IDTYPE_USER;
10595acd5a8bSdholland 	} else if (cursor->q2c_groups_done == 0) {
10605acd5a8bSdholland 		*idtype_ret = QUOTA_IDTYPE_GROUP;
10615acd5a8bSdholland 	} else {
10625acd5a8bSdholland 		return EAGAIN;
10635acd5a8bSdholland 	}
10645acd5a8bSdholland 	return 0;
10655acd5a8bSdholland }
10665acd5a8bSdholland 
10675acd5a8bSdholland /*
10685acd5a8bSdholland  * Add an ID to the current state. Sets up either one or two keys to
10695acd5a8bSdholland  * refer to it, depending on whether it's first/last and the setting
10705acd5a8bSdholland  * of skipfirst. (skiplast does not need to be explicitly tested)
10715acd5a8bSdholland  */
10725acd5a8bSdholland static void
q2cursor_addid(struct q2cursor_state * state,int idtype,id_t id)10735acd5a8bSdholland q2cursor_addid(struct q2cursor_state *state, int idtype, id_t id)
10745acd5a8bSdholland {
10755acd5a8bSdholland 	KASSERT(state->numids < state->maxids);
10765acd5a8bSdholland 	KASSERT(state->numkeys < state->maxkeyvals);
10775acd5a8bSdholland 
10785acd5a8bSdholland 	if (!state->skipfirst || state->numkeys > 0) {
10795acd5a8bSdholland 		state->keys[state->numkeys].qk_idtype = idtype;
10805acd5a8bSdholland 		state->keys[state->numkeys].qk_id = id;
10815acd5a8bSdholland 		state->keys[state->numkeys].qk_objtype = QUOTA_OBJTYPE_BLOCKS;
10825acd5a8bSdholland 		state->numkeys++;
10835acd5a8bSdholland 	}
10845acd5a8bSdholland 	if (state->numkeys < state->maxkeyvals) {
10855acd5a8bSdholland 		state->keys[state->numkeys].qk_idtype = idtype;
10865acd5a8bSdholland 		state->keys[state->numkeys].qk_id = id;
10875acd5a8bSdholland 		state->keys[state->numkeys].qk_objtype = QUOTA_OBJTYPE_FILES;
10885acd5a8bSdholland 		state->numkeys++;
10895acd5a8bSdholland 	} else {
10905acd5a8bSdholland 		KASSERT(state->skiplast);
10915acd5a8bSdholland 	}
10925acd5a8bSdholland 	state->numids++;
10935acd5a8bSdholland }
10945acd5a8bSdholland 
10955acd5a8bSdholland /*
10965acd5a8bSdholland  * Callback function for getting IDs. Update counting and call addid.
10975acd5a8bSdholland  */
10985acd5a8bSdholland static int
q2cursor_getids_callback(struct ufsmount * ump,uint64_t * offp,struct quota2_entry * q2ep,uint64_t off,void * v)10995acd5a8bSdholland q2cursor_getids_callback(struct ufsmount *ump, uint64_t *offp,
11000367ea04Sbouyer     struct quota2_entry *q2ep, uint64_t off, void *v)
11010367ea04Sbouyer {
11025acd5a8bSdholland 	struct q2cursor_getids *gi = v;
11035acd5a8bSdholland 	id_t id;
11040367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
11050367ea04Sbouyer 
11065acd5a8bSdholland 	if (gi->skipped < gi->skip) {
11075acd5a8bSdholland 		gi->skipped++;
11081f503971Sdholland 		return 0;
11091f503971Sdholland 	}
11105acd5a8bSdholland 	id = ufs_rw32(q2ep->q2e_uid, needswap);
11115acd5a8bSdholland 	q2cursor_addid(gi->state, gi->idtype, id);
11125acd5a8bSdholland 	gi->new_skip++;
11135acd5a8bSdholland 	if (gi->state->numids >= gi->state->maxids) {
11145acd5a8bSdholland 		/* got enough ids, stop now */
1115e5409b53Sdholland 		gi->stopped = 1;
1116aeec81c6Sdholland 		return Q2WL_ABORT;
1117aeec81c6Sdholland 	}
11180367ea04Sbouyer 	return 0;
11190367ea04Sbouyer }
11200367ea04Sbouyer 
11215acd5a8bSdholland /*
11225acd5a8bSdholland  * Fill in a batch of quotakeys by scanning one or more hash chains.
11235acd5a8bSdholland  */
11245acd5a8bSdholland static int
q2cursor_getkeys(struct ufsmount * ump,int idtype,struct ufsq2_cursor * cursor,struct q2cursor_state * state,int * hashsize_ret,struct quota2_entry * default_q2e_ret)11255acd5a8bSdholland q2cursor_getkeys(struct ufsmount *ump, int idtype, struct ufsq2_cursor *cursor,
11265acd5a8bSdholland     struct q2cursor_state *state,
11275acd5a8bSdholland     int *hashsize_ret, struct quota2_entry *default_q2e_ret)
11285acd5a8bSdholland {
11295acd5a8bSdholland 	const int needswap = UFS_MPNEEDSWAP(ump);
11305acd5a8bSdholland 	struct buf *hbp;
11315acd5a8bSdholland 	struct quota2_header *q2h;
11325acd5a8bSdholland 	int quota2_hash_size;
11335acd5a8bSdholland 	struct q2cursor_getids gi;
11345acd5a8bSdholland 	uint64_t offset;
11355acd5a8bSdholland 	int error;
11365acd5a8bSdholland 
11375acd5a8bSdholland 	/*
11385acd5a8bSdholland 	 * Read the header block.
11395acd5a8bSdholland 	 */
11405acd5a8bSdholland 
11415acd5a8bSdholland 	mutex_enter(&dqlock);
11425acd5a8bSdholland 	error = getq2h(ump, idtype, &hbp, &q2h, 0);
11435acd5a8bSdholland 	if (error) {
11445acd5a8bSdholland 		mutex_exit(&dqlock);
11455acd5a8bSdholland 		return error;
11465acd5a8bSdholland 	}
11475acd5a8bSdholland 
11485acd5a8bSdholland 	/* if the table size has changed, make the caller start over */
11495acd5a8bSdholland 	quota2_hash_size = ufs_rw16(q2h->q2h_hash_size, needswap);
11505acd5a8bSdholland 	if (cursor->q2c_hashsize == 0) {
11515acd5a8bSdholland 		cursor->q2c_hashsize = quota2_hash_size;
11525acd5a8bSdholland 	} else if (cursor->q2c_hashsize != quota2_hash_size) {
11535acd5a8bSdholland 		error = EDEADLK;
11545acd5a8bSdholland 		goto scanfail;
11555acd5a8bSdholland 	}
11565acd5a8bSdholland 
11575acd5a8bSdholland 	/* grab the entry with the default values out of the header */
11585acd5a8bSdholland 	quota2_ufs_rwq2e(&q2h->q2h_defentry, default_q2e_ret, needswap);
11595acd5a8bSdholland 
11605acd5a8bSdholland 	/* If we haven't done the defaults yet, that goes first. */
11615acd5a8bSdholland 	if (cursor->q2c_defaults_done == 0) {
11625acd5a8bSdholland 		q2cursor_addid(state, idtype, QUOTA_DEFAULTID);
116399640241Sdholland 		/* if we read both halves, mark it done */
116499640241Sdholland 		if (state->numids < state->maxids || !state->skiplast) {
11655acd5a8bSdholland 			cursor->q2c_defaults_done = 1;
11665acd5a8bSdholland 		}
116799640241Sdholland 	}
11685acd5a8bSdholland 
11695acd5a8bSdholland 	gi.state = state;
11705acd5a8bSdholland 	gi.idtype = idtype;
11715acd5a8bSdholland 
11725acd5a8bSdholland 	while (state->numids < state->maxids) {
11735acd5a8bSdholland 		if (cursor->q2c_hashpos >= quota2_hash_size) {
11745acd5a8bSdholland 			/* nothing more left */
11755acd5a8bSdholland 			break;
11765acd5a8bSdholland 		}
11775acd5a8bSdholland 
11785acd5a8bSdholland 		/* scan this hash chain */
11795acd5a8bSdholland 		gi.skip = cursor->q2c_uidpos;
11805acd5a8bSdholland 		gi.new_skip = gi.skip;
11815acd5a8bSdholland 		gi.skipped = 0;
1182e5409b53Sdholland 		gi.stopped = 0;
11835acd5a8bSdholland 		offset = q2h->q2h_entries[cursor->q2c_hashpos];
11845acd5a8bSdholland 
11855acd5a8bSdholland 		error = quota2_walk_list(ump, hbp, idtype, &offset, 0, &gi,
11865acd5a8bSdholland 		    q2cursor_getids_callback);
1187e5409b53Sdholland 		KASSERT(error != Q2WL_ABORT);
1188e5409b53Sdholland 		if (error) {
1189e5409b53Sdholland 			break;
1190e5409b53Sdholland 		}
1191e5409b53Sdholland 		if (gi.stopped) {
11925acd5a8bSdholland 			/* callback stopped before reading whole chain */
11935acd5a8bSdholland 			cursor->q2c_uidpos = gi.new_skip;
119499640241Sdholland 			/* if we didn't get both halves, back up */
119599640241Sdholland 			if (state->numids == state->maxids && state->skiplast){
119699640241Sdholland 				KASSERT(cursor->q2c_uidpos > 0);
119799640241Sdholland 				cursor->q2c_uidpos--;
119899640241Sdholland 			}
11995acd5a8bSdholland 		} else {
120099640241Sdholland 			/* read whole chain */
120199640241Sdholland 			/* if we got both halves of the last id, advance */
120299640241Sdholland 			if (state->numids < state->maxids || !state->skiplast){
12035acd5a8bSdholland 				cursor->q2c_uidpos = 0;
12045acd5a8bSdholland 				cursor->q2c_hashpos++;
12055acd5a8bSdholland 			}
12065acd5a8bSdholland 		}
120799640241Sdholland 	}
12085acd5a8bSdholland 
12095acd5a8bSdholland scanfail:
12105acd5a8bSdholland 	mutex_exit(&dqlock);
12115acd5a8bSdholland 	brelse(hbp, 0);
12125acd5a8bSdholland 	if (error)
12135acd5a8bSdholland 		return error;
12145acd5a8bSdholland 
12155acd5a8bSdholland 	*hashsize_ret = quota2_hash_size;
12165acd5a8bSdholland 	return 0;
12175acd5a8bSdholland }
12185acd5a8bSdholland 
12195acd5a8bSdholland /*
12205acd5a8bSdholland  * Fetch the quotavals for the quotakeys.
12215acd5a8bSdholland  */
12225acd5a8bSdholland static int
q2cursor_getvals(struct ufsmount * ump,struct q2cursor_state * state,const struct quota2_entry * default_q2e)12235acd5a8bSdholland q2cursor_getvals(struct ufsmount *ump, struct q2cursor_state *state,
12245acd5a8bSdholland     const struct quota2_entry *default_q2e)
12255acd5a8bSdholland {
12265acd5a8bSdholland 	int hasid;
12275acd5a8bSdholland 	id_t loadedid, id;
12285acd5a8bSdholland 	unsigned pos;
12295acd5a8bSdholland 	struct quota2_entry q2e;
12305acd5a8bSdholland 	int objtype;
12315acd5a8bSdholland 	int error;
12325acd5a8bSdholland 
12335acd5a8bSdholland 	hasid = 0;
12345acd5a8bSdholland 	loadedid = 0;
12355acd5a8bSdholland 	for (pos = 0; pos < state->numkeys; pos++) {
12365acd5a8bSdholland 		id = state->keys[pos].qk_id;
12375acd5a8bSdholland 		if (!hasid || id != loadedid) {
12385acd5a8bSdholland 			hasid = 1;
12395acd5a8bSdholland 			loadedid = id;
12405acd5a8bSdholland 			if (id == QUOTA_DEFAULTID) {
12415acd5a8bSdholland 				q2e = *default_q2e;
12425acd5a8bSdholland 			} else {
12435acd5a8bSdholland 				error = quota2_fetch_q2e(ump,
12445acd5a8bSdholland 				    &state->keys[pos],
12455acd5a8bSdholland 				    &q2e);
12465acd5a8bSdholland 				if (error == ENOENT) {
12475acd5a8bSdholland 					/* something changed - start over */
12485acd5a8bSdholland 					error = EDEADLK;
12495acd5a8bSdholland 				}
12505acd5a8bSdholland 				if (error) {
12515acd5a8bSdholland 					return error;
12525acd5a8bSdholland 				}
12535acd5a8bSdholland  			}
12545acd5a8bSdholland 		}
12555acd5a8bSdholland 
12565acd5a8bSdholland 
12575acd5a8bSdholland 		objtype = state->keys[pos].qk_objtype;
12585acd5a8bSdholland 		KASSERT(objtype >= 0 && objtype < N_QL);
12595acd5a8bSdholland 		q2val_to_quotaval(&q2e.q2e_val[objtype], &state->vals[pos]);
12605acd5a8bSdholland 	}
12615acd5a8bSdholland 
12625acd5a8bSdholland 	return 0;
12635acd5a8bSdholland }
12645acd5a8bSdholland 
12655acd5a8bSdholland /*
12665acd5a8bSdholland  * Handle cursorget.
12675acd5a8bSdholland  *
12685acd5a8bSdholland  * We can't just read keys and values directly, because we can't walk
12695acd5a8bSdholland  * the list with qdlock and grab dq_interlock to read the entries at
12705acd5a8bSdholland  * the same time. So we're going to do two passes: one to figure out
12715acd5a8bSdholland  * which IDs we want and fill in the keys, and then a second to use
12725acd5a8bSdholland  * the keys to fetch the values.
12735acd5a8bSdholland  */
12740367ea04Sbouyer int
quota2_handle_cmd_cursorget(struct ufsmount * ump,struct quotakcursor * qkc,struct quotakey * keys,struct quotaval * vals,unsigned maxreturn,unsigned * ret)12756c788ef1Sdholland quota2_handle_cmd_cursorget(struct ufsmount *ump, struct quotakcursor *qkc,
12769ff2a109Sdholland     struct quotakey *keys, struct quotaval *vals, unsigned maxreturn,
12779ff2a109Sdholland     unsigned *ret)
12780367ea04Sbouyer {
12790367ea04Sbouyer 	int error;
12804f9db7d3Sdholland 	struct ufsq2_cursor *cursor;
12815acd5a8bSdholland 	struct ufsq2_cursor newcursor;
12825acd5a8bSdholland 	struct q2cursor_state state;
12835acd5a8bSdholland 	struct quota2_entry default_q2e;
12849ff2a109Sdholland 	int idtype;
1285b4a0d227Suwe 	int quota2_hash_size = 0; /* XXX: sh3 gcc 4.8 -Wuninitialized */
12860367ea04Sbouyer 
12875acd5a8bSdholland 	/*
12885acd5a8bSdholland 	 * Convert and validate the cursor.
12895acd5a8bSdholland 	 */
12904f9db7d3Sdholland 	cursor = Q2CURSOR(qkc);
12911f503971Sdholland 	error = q2cursor_check(cursor);
12921f503971Sdholland 	if (error) {
12931f503971Sdholland 		return error;
12944f9db7d3Sdholland 	}
12954f9db7d3Sdholland 
12965acd5a8bSdholland 	/*
12975acd5a8bSdholland 	 * Make sure our on-disk codes match the values of the
12985acd5a8bSdholland 	 * FS-independent ones. This avoids the need for explicit
12995acd5a8bSdholland 	 * conversion (which would be a NOP anyway and thus easily
13005acd5a8bSdholland 	 * left out or called in the wrong places...)
13015acd5a8bSdholland 	 */
13025acd5a8bSdholland 	CTASSERT(QUOTA_IDTYPE_USER == USRQUOTA);
13035acd5a8bSdholland 	CTASSERT(QUOTA_IDTYPE_GROUP == GRPQUOTA);
13045acd5a8bSdholland 	CTASSERT(QUOTA_OBJTYPE_BLOCKS == QL_BLOCK);
13055acd5a8bSdholland 	CTASSERT(QUOTA_OBJTYPE_FILES == QL_FILE);
13069ff2a109Sdholland 
13075acd5a8bSdholland 	/*
13085acd5a8bSdholland 	 * If some of the idtypes aren't configured/enabled, arrange
13095acd5a8bSdholland 	 * to skip over them.
13105acd5a8bSdholland 	 */
13119ff2a109Sdholland 	if (cursor->q2c_users_done == 0 &&
13129ff2a109Sdholland 	    ump->um_quotas[USRQUOTA] == NULLVP) {
13139ff2a109Sdholland 		cursor->q2c_users_done = 1;
13141f503971Sdholland 	}
13159ff2a109Sdholland 	if (cursor->q2c_groups_done == 0 &&
13169ff2a109Sdholland 	    ump->um_quotas[GRPQUOTA] == NULLVP) {
13179ff2a109Sdholland 		cursor->q2c_groups_done = 1;
13189ff2a109Sdholland 	}
13199ff2a109Sdholland 
13205acd5a8bSdholland 	/* Loop over, potentially, both idtypes */
13215acd5a8bSdholland 	while (1) {
13229ff2a109Sdholland 
13235acd5a8bSdholland 		/* Choose id type */
13245acd5a8bSdholland 		error = q2cursor_pickidtype(cursor, &idtype);
13255acd5a8bSdholland 		if (error == EAGAIN) {
13269ff2a109Sdholland 			/* nothing more to do, return 0 */
13279ff2a109Sdholland 			*ret = 0;
13289ff2a109Sdholland 			return 0;
13299ff2a109Sdholland 		}
13309ff2a109Sdholland 		KASSERT(ump->um_quotas[idtype] != NULLVP);
1331e8d2fd50Sdholland 
13325acd5a8bSdholland 		/*
13335acd5a8bSdholland 		 * Initialize the per-call iteration state. Copy the
13345acd5a8bSdholland 		 * cursor state so we can update it in place but back
13355acd5a8bSdholland 		 * out on error.
13365acd5a8bSdholland 		 */
13375acd5a8bSdholland 		q2cursor_initstate(&state, keys, vals, maxreturn,
13385acd5a8bSdholland 		    cursor->q2c_blocks_done);
13395acd5a8bSdholland 		newcursor = *cursor;
1340dcf0a81fSdholland 
13415acd5a8bSdholland 		/* Assign keys */
13425acd5a8bSdholland 		error = q2cursor_getkeys(ump, idtype, &newcursor, &state,
13435acd5a8bSdholland 		    &quota2_hash_size, &default_q2e);
13440367ea04Sbouyer 		if (error) {
13450367ea04Sbouyer 			return error;
13460367ea04Sbouyer 		}
1347ce9684d0Sdholland 
13485acd5a8bSdholland 		/* Now fill in the values. */
13495acd5a8bSdholland 		error = q2cursor_getvals(ump, &state, &default_q2e);
13505acd5a8bSdholland 		if (error) {
13515acd5a8bSdholland 			return error;
1352ce9684d0Sdholland 		}
1353e8d2fd50Sdholland 
13540367ea04Sbouyer 		/*
13555acd5a8bSdholland 		 * Now that we aren't going to fail and lose what we
13565acd5a8bSdholland 		 * did so far, we can update the cursor state.
13570367ea04Sbouyer 		 */
1358ce9684d0Sdholland 
13595acd5a8bSdholland 		if (newcursor.q2c_hashpos >= quota2_hash_size) {
13609ff2a109Sdholland 			if (idtype == QUOTA_IDTYPE_USER)
13619ff2a109Sdholland 				cursor->q2c_users_done = 1;
13629ff2a109Sdholland 			else
13639ff2a109Sdholland 				cursor->q2c_groups_done = 1;
13649ff2a109Sdholland 
13655acd5a8bSdholland 			/* start over on another id type */
13665acd5a8bSdholland 			cursor->q2c_hashsize = 0;
13675acd5a8bSdholland 			cursor->q2c_defaults_done = 0;
13685acd5a8bSdholland 			cursor->q2c_hashpos = 0;
13695acd5a8bSdholland 			cursor->q2c_uidpos = 0;
13705acd5a8bSdholland 			cursor->q2c_blocks_done = 0;
13715acd5a8bSdholland 		} else {
13725acd5a8bSdholland 			*cursor = newcursor;
13735acd5a8bSdholland 			cursor->q2c_blocks_done = state.skiplast;
13745acd5a8bSdholland 		}
1375dcf0a81fSdholland 
1376aeec81c6Sdholland 		/*
13775acd5a8bSdholland 		 * If we have something to return, return it.
13785acd5a8bSdholland 		 * Otherwise, continue to the other idtype, if any,
13795acd5a8bSdholland 		 * and only return zero at end of iteration.
1380aeec81c6Sdholland 		 */
13815acd5a8bSdholland 		if (state.numkeys > 0) {
13820367ea04Sbouyer 			break;
13830367ea04Sbouyer 		}
1384dcf0a81fSdholland 	}
1385e8d2fd50Sdholland 
13865acd5a8bSdholland 	*ret = state.numkeys;
13875acd5a8bSdholland 	return 0;
13880367ea04Sbouyer }
13890367ea04Sbouyer 
139006f1e02eSdholland int
quota2_handle_cmd_cursoropen(struct ufsmount * ump,struct quotakcursor * qkc)139106f1e02eSdholland quota2_handle_cmd_cursoropen(struct ufsmount *ump, struct quotakcursor *qkc)
139206f1e02eSdholland {
139306f1e02eSdholland 	struct ufsq2_cursor *cursor;
139406f1e02eSdholland 
139506f1e02eSdholland 	CTASSERT(sizeof(*cursor) <= sizeof(qkc->u.qkc_space));
139606f1e02eSdholland 	cursor = Q2CURSOR(qkc);
139706f1e02eSdholland 
139806f1e02eSdholland 	cursor->q2c_magic = Q2C_MAGIC;
1399ce9684d0Sdholland 	cursor->q2c_hashsize = 0;
14001f503971Sdholland 
14019ff2a109Sdholland 	cursor->q2c_users_done = 0;
14029ff2a109Sdholland 	cursor->q2c_groups_done = 0;
1403ce9684d0Sdholland 	cursor->q2c_defaults_done = 0;
14041f503971Sdholland 	cursor->q2c_hashpos = 0;
14051f503971Sdholland 	cursor->q2c_uidpos = 0;
1406ce9684d0Sdholland 	cursor->q2c_blocks_done = 0;
140706f1e02eSdholland 	return 0;
140806f1e02eSdholland }
140906f1e02eSdholland 
141006f1e02eSdholland int
quota2_handle_cmd_cursorclose(struct ufsmount * ump,struct quotakcursor * qkc)141106f1e02eSdholland quota2_handle_cmd_cursorclose(struct ufsmount *ump, struct quotakcursor *qkc)
141206f1e02eSdholland {
141306f1e02eSdholland 	struct ufsq2_cursor *cursor;
14141f503971Sdholland 	int error;
141506f1e02eSdholland 
141606f1e02eSdholland 	cursor = Q2CURSOR(qkc);
14171f503971Sdholland 	error = q2cursor_check(cursor);
14181f503971Sdholland 	if (error) {
14191f503971Sdholland 		return error;
142006f1e02eSdholland 	}
142106f1e02eSdholland 
1422ce9684d0Sdholland 	/* nothing to do */
1423ce9684d0Sdholland 
142406f1e02eSdholland 	return 0;
142506f1e02eSdholland }
142606f1e02eSdholland 
14270367ea04Sbouyer int
quota2_handle_cmd_cursorskipidtype(struct ufsmount * ump,struct quotakcursor * qkc,int idtype)14286c788ef1Sdholland quota2_handle_cmd_cursorskipidtype(struct ufsmount *ump,
14296c788ef1Sdholland     struct quotakcursor *qkc, int idtype)
14306c788ef1Sdholland {
14316c788ef1Sdholland 	struct ufsq2_cursor *cursor;
14326c788ef1Sdholland 	int error;
14336c788ef1Sdholland 
14346c788ef1Sdholland 	cursor = Q2CURSOR(qkc);
14356c788ef1Sdholland 	error = q2cursor_check(cursor);
14366c788ef1Sdholland 	if (error) {
14376c788ef1Sdholland 		return error;
14386c788ef1Sdholland 	}
14396c788ef1Sdholland 
14406c788ef1Sdholland 	switch (idtype) {
14416c788ef1Sdholland 	case QUOTA_IDTYPE_USER:
14426c788ef1Sdholland 		cursor->q2c_users_done = 1;
14436c788ef1Sdholland 		break;
14446c788ef1Sdholland 	case QUOTA_IDTYPE_GROUP:
14456c788ef1Sdholland 		cursor->q2c_groups_done = 1;
14466c788ef1Sdholland 		break;
14476c788ef1Sdholland 	default:
14486c788ef1Sdholland 		return EINVAL;
14496c788ef1Sdholland 	}
14506c788ef1Sdholland 
14516c788ef1Sdholland 	return 0;
14526c788ef1Sdholland }
14536c788ef1Sdholland 
14546c788ef1Sdholland int
quota2_handle_cmd_cursoratend(struct ufsmount * ump,struct quotakcursor * qkc,int * ret)14556c788ef1Sdholland quota2_handle_cmd_cursoratend(struct ufsmount *ump, struct quotakcursor *qkc,
14566c788ef1Sdholland     int *ret)
14576c788ef1Sdholland {
14586c788ef1Sdholland 	struct ufsq2_cursor *cursor;
14596c788ef1Sdholland 	int error;
14606c788ef1Sdholland 
14616c788ef1Sdholland 	cursor = Q2CURSOR(qkc);
14626c788ef1Sdholland 	error = q2cursor_check(cursor);
14636c788ef1Sdholland 	if (error) {
14646c788ef1Sdholland 		return error;
14656c788ef1Sdholland 	}
14666c788ef1Sdholland 
14676c788ef1Sdholland 	*ret = (cursor->q2c_users_done && cursor->q2c_groups_done);
14686c788ef1Sdholland 	return 0;
14696c788ef1Sdholland }
14706c788ef1Sdholland 
14716c788ef1Sdholland int
quota2_handle_cmd_cursorrewind(struct ufsmount * ump,struct quotakcursor * qkc)14726c788ef1Sdholland quota2_handle_cmd_cursorrewind(struct ufsmount *ump, struct quotakcursor *qkc)
14736c788ef1Sdholland {
14746c788ef1Sdholland 	struct ufsq2_cursor *cursor;
14756c788ef1Sdholland 	int error;
14766c788ef1Sdholland 
14776c788ef1Sdholland 	cursor = Q2CURSOR(qkc);
14786c788ef1Sdholland 	error = q2cursor_check(cursor);
14796c788ef1Sdholland 	if (error) {
14806c788ef1Sdholland 		return error;
14816c788ef1Sdholland 	}
14826c788ef1Sdholland 
14836c788ef1Sdholland 	cursor->q2c_hashsize = 0;
14846c788ef1Sdholland 
14856c788ef1Sdholland 	cursor->q2c_users_done = 0;
14866c788ef1Sdholland 	cursor->q2c_groups_done = 0;
14876c788ef1Sdholland 	cursor->q2c_defaults_done = 0;
14886c788ef1Sdholland 	cursor->q2c_hashpos = 0;
14896c788ef1Sdholland 	cursor->q2c_uidpos = 0;
14906c788ef1Sdholland 	cursor->q2c_blocks_done = 0;
14916c788ef1Sdholland 
14926c788ef1Sdholland 	return 0;
14936c788ef1Sdholland }
14946c788ef1Sdholland 
14956c788ef1Sdholland int
q2sync(struct mount * mp)14960367ea04Sbouyer q2sync(struct mount *mp)
14970367ea04Sbouyer {
14980367ea04Sbouyer 	return 0;
14990367ea04Sbouyer }
15000367ea04Sbouyer 
15010367ea04Sbouyer struct dq2get_callback {
15020367ea04Sbouyer 	uid_t id;
15030367ea04Sbouyer 	struct dquot *dq;
15040367ea04Sbouyer };
15050367ea04Sbouyer 
15060367ea04Sbouyer static int
dq2get_callback(struct ufsmount * ump,uint64_t * offp,struct quota2_entry * q2e,uint64_t off,void * v)15070367ea04Sbouyer dq2get_callback(struct ufsmount *ump, uint64_t *offp, struct quota2_entry *q2e,
15080367ea04Sbouyer     uint64_t off, void *v)
15090367ea04Sbouyer {
15100367ea04Sbouyer 	struct dq2get_callback *c = v;
15110367ea04Sbouyer 	daddr_t lblkno;
15120367ea04Sbouyer 	int blkoff;
15130367ea04Sbouyer 	const int needswap = UFS_MPNEEDSWAP(ump);
15140367ea04Sbouyer 
15150367ea04Sbouyer 	if (ufs_rw32(q2e->q2e_uid, needswap) == c->id) {
15160367ea04Sbouyer 		KASSERT(mutex_owned(&c->dq->dq_interlock));
15170367ea04Sbouyer 		lblkno = (off >> ump->um_mountp->mnt_fs_bshift);
15180367ea04Sbouyer 		blkoff = (off & ump->umq2_bmask);
15190367ea04Sbouyer 		c->dq->dq2_lblkno = lblkno;
15200367ea04Sbouyer 		c->dq->dq2_blkoff = blkoff;
15210367ea04Sbouyer 		return Q2WL_ABORT;
15220367ea04Sbouyer 	}
15230367ea04Sbouyer 	return 0;
15240367ea04Sbouyer }
15250367ea04Sbouyer 
15260367ea04Sbouyer int
dq2get(struct vnode * dqvp,u_long id,struct ufsmount * ump,int type,struct dquot * dq)15270367ea04Sbouyer dq2get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type,
15280367ea04Sbouyer     struct dquot *dq)
15290367ea04Sbouyer {
15300367ea04Sbouyer 	struct buf *bp;
15310367ea04Sbouyer 	struct quota2_header *q2h;
15320367ea04Sbouyer 	int error;
15330367ea04Sbouyer 	daddr_t offset;
15340367ea04Sbouyer 	u_long hash_mask;
15350367ea04Sbouyer 	struct dq2get_callback c = {
15360367ea04Sbouyer 		.id = id,
15370367ea04Sbouyer 		.dq = dq
15380367ea04Sbouyer 	};
15390367ea04Sbouyer 
15400367ea04Sbouyer 	KASSERT(mutex_owned(&dq->dq_interlock));
15410367ea04Sbouyer 	mutex_enter(&dqlock);
15420367ea04Sbouyer 	error = getq2h(ump, type, &bp, &q2h, 0);
15430367ea04Sbouyer 	if (error)
15440367ea04Sbouyer 		goto out_mutex;
15450367ea04Sbouyer 	/* look for our entry */
15460367ea04Sbouyer 	hash_mask = ((1 << q2h->q2h_hash_shift) - 1);
15470367ea04Sbouyer 	offset = q2h->q2h_entries[id & hash_mask];
15480367ea04Sbouyer 	error = quota2_walk_list(ump, bp, type, &offset, 0, (void *)&c,
15490367ea04Sbouyer 	    dq2get_callback);
15500367ea04Sbouyer 	brelse(bp, 0);
15510367ea04Sbouyer out_mutex:
15520367ea04Sbouyer 	mutex_exit(&dqlock);
15530367ea04Sbouyer 	return error;
15540367ea04Sbouyer }
15550367ea04Sbouyer 
15560367ea04Sbouyer int
dq2sync(struct vnode * vp,struct dquot * dq)15570367ea04Sbouyer dq2sync(struct vnode *vp, struct dquot *dq)
15580367ea04Sbouyer {
15590367ea04Sbouyer 	return 0;
15600367ea04Sbouyer }
1561