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 "a2_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