Home | History | Annotate | Line # | Download | only in ufs
      1 /*	$NetBSD: ufs_quota1.c,v 1.28 2026/01/22 03:24:19 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1982, 1986, 1990, 1993, 1995
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Robert Elz at The University of Melbourne.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  *
     34  *	@(#)ufs_quota.c	8.5 (Berkeley) 5/20/95
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: ufs_quota1.c,v 1.28 2026/01/22 03:24:19 riastradh Exp $");
     39 
     40 #include <sys/param.h>
     41 #include <sys/types.h>
     42 
     43 #include <sys/file.h>
     44 #include <sys/kauth.h>
     45 #include <sys/kernel.h>
     46 #include <sys/mount.h>
     47 #include <sys/namei.h>
     48 #include <sys/proc.h>
     49 #include <sys/sdt.h>
     50 #include <sys/systm.h>
     51 #include <sys/vnode.h>
     52 
     53 #include <ufs/ufs/inode.h>
     54 #include <ufs/ufs/quota1.h>
     55 #include <ufs/ufs/ufs_extern.h>
     56 #include <ufs/ufs/ufs_quota.h>
     57 #include <ufs/ufs/ufsmount.h>
     58 
     59 static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
     60 static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
     61 
     62 /*
     63  * Update disk usage, and take corrective action.
     64  */
     65 int
     66 chkdq1(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
     67 {
     68 	struct dquot *dq;
     69 	int i;
     70 	int ncurblocks, error;
     71 
     72 	if ((error = getinoquota(ip)) != 0)
     73 		return error;
     74 	if (change == 0)
     75 		return 0;
     76 	if (change < 0) {
     77 		for (i = 0; i < MAXQUOTAS; i++) {
     78 			if ((dq = ip->i_dquot[i]) == NODQUOT)
     79 				continue;
     80 			mutex_enter(&dq->dq_interlock);
     81 			ncurblocks = dq->dq_curblocks + change;
     82 			if (ncurblocks >= 0)
     83 				dq->dq_curblocks = ncurblocks;
     84 			else
     85 				dq->dq_curblocks = 0;
     86 			dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
     87 			dq->dq_flags |= DQ_MOD;
     88 			mutex_exit(&dq->dq_interlock);
     89 		}
     90 		return 0;
     91 	}
     92 	for (i = 0; i < MAXQUOTAS; i++) {
     93 		if ((dq = ip->i_dquot[i]) == NODQUOT)
     94 			continue;
     95 		if ((flags & FORCE) == 0 &&
     96 		    kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
     97 			KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, KAUTH_ARG(i),
     98 			KAUTH_ARG(QL_BLOCK), NULL) != 0) {
     99 			mutex_enter(&dq->dq_interlock);
    100 			error = chkdqchg(ip, change, cred, i);
    101 			mutex_exit(&dq->dq_interlock);
    102 			if (error != 0)
    103 				return error;
    104 		}
    105 	}
    106 	for (i = 0; i < MAXQUOTAS; i++) {
    107 		if ((dq = ip->i_dquot[i]) == NODQUOT)
    108 			continue;
    109 		mutex_enter(&dq->dq_interlock);
    110 		dq->dq_curblocks += change;
    111 		dq->dq_flags |= DQ_MOD;
    112 		mutex_exit(&dq->dq_interlock);
    113 	}
    114 	return 0;
    115 }
    116 
    117 /*
    118  * Check for a valid change to a users allocation.
    119  * Issue an error message if appropriate.
    120  */
    121 static int
    122 chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
    123 {
    124 	struct dquot *dq = ip->i_dquot[type];
    125 	long ncurblocks = dq->dq_curblocks + change;
    126 
    127 	KASSERT(mutex_owned(&dq->dq_interlock));
    128 	/*
    129 	 * If user would exceed their hard limit, disallow space allocation.
    130 	 */
    131 	if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
    132 		if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
    133 		    ip->i_uid == kauth_cred_geteuid(cred)) {
    134 			uprintf("\n%s: write failed, %s disk limit reached\n",
    135 			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
    136 			    quotatypes[type]);
    137 			dq->dq_flags |= DQ_WARN(QL_BLOCK);
    138 		}
    139 		return SET_ERROR(EDQUOT);
    140 	}
    141 	/*
    142 	 * If user is over their soft limit for too long, disallow space
    143 	 * allocation. Reset time limit as they cross their soft limit.
    144 	 */
    145 	if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
    146 		if (dq->dq_curblocks < dq->dq_bsoftlimit) {
    147 			dq->dq_btime =
    148 			    time_second + ip->i_ump->umq1_btime[type];
    149 			if (ip->i_uid == kauth_cred_geteuid(cred))
    150 				uprintf("\n%s: warning, %s %s\n",
    151 				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
    152 				    quotatypes[type], "disk quota exceeded");
    153 			return 0;
    154 		}
    155 		if (time_second > dq->dq_btime) {
    156 			if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
    157 			    ip->i_uid == kauth_cred_geteuid(cred)) {
    158 				uprintf("\n%s: write failed, %s %s\n",
    159 				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
    160 				    quotatypes[type],
    161 				    "disk quota exceeded for too long");
    162 				dq->dq_flags |= DQ_WARN(QL_BLOCK);
    163 			}
    164 			return SET_ERROR(EDQUOT);
    165 		}
    166 	}
    167 	return 0;
    168 }
    169 
    170 /*
    171  * Check the inode limit, applying corrective action.
    172  */
    173 int
    174 chkiq1(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
    175 {
    176 	struct dquot *dq;
    177 	int i;
    178 	int ncurinodes, error;
    179 
    180 	if ((error = getinoquota(ip)) != 0)
    181 		return error;
    182 	if (change == 0)
    183 		return 0;
    184 	if (change < 0) {
    185 		for (i = 0; i < MAXQUOTAS; i++) {
    186 			if ((dq = ip->i_dquot[i]) == NODQUOT)
    187 				continue;
    188 			mutex_enter(&dq->dq_interlock);
    189 			ncurinodes = dq->dq_curinodes + change;
    190 			if (ncurinodes >= 0)
    191 				dq->dq_curinodes = ncurinodes;
    192 			else
    193 				dq->dq_curinodes = 0;
    194 			dq->dq_flags &= ~DQ_WARN(QL_FILE);
    195 			dq->dq_flags |= DQ_MOD;
    196 			mutex_exit(&dq->dq_interlock);
    197 		}
    198 		return 0;
    199 	}
    200 	for (i = 0; i < MAXQUOTAS; i++) {
    201 		if ((dq = ip->i_dquot[i]) == NODQUOT)
    202 			continue;
    203 		if ((flags & FORCE) == 0 &&
    204 		    kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
    205 			KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
    206 			KAUTH_ARG(i), KAUTH_ARG(QL_FILE), NULL) != 0) {
    207 			mutex_enter(&dq->dq_interlock);
    208 			error = chkiqchg(ip, change, cred, i);
    209 			mutex_exit(&dq->dq_interlock);
    210 			if (error != 0)
    211 				return error;
    212 		}
    213 	}
    214 	for (i = 0; i < MAXQUOTAS; i++) {
    215 		if ((dq = ip->i_dquot[i]) == NODQUOT)
    216 			continue;
    217 		mutex_enter(&dq->dq_interlock);
    218 		dq->dq_curinodes += change;
    219 		dq->dq_flags |= DQ_MOD;
    220 		mutex_exit(&dq->dq_interlock);
    221 	}
    222 	return 0;
    223 }
    224 
    225 /*
    226  * Check for a valid change to a users allocation.
    227  * Issue an error message if appropriate.
    228  */
    229 static int
    230 chkiqchg(struct inode *ip, int32_t change, kauth_cred_t cred, int type)
    231 {
    232 	struct dquot *dq = ip->i_dquot[type];
    233 	long ncurinodes = dq->dq_curinodes + change;
    234 
    235 	KASSERT(mutex_owned(&dq->dq_interlock));
    236 	/*
    237 	 * If user would exceed their hard limit, disallow inode allocation.
    238 	 */
    239 	if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
    240 		if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
    241 		    ip->i_uid == kauth_cred_geteuid(cred)) {
    242 			uprintf("\n%s: write failed, %s inode limit reached\n",
    243 			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
    244 			    quotatypes[type]);
    245 			dq->dq_flags |= DQ_WARN(QL_FILE);
    246 		}
    247 		return SET_ERROR(EDQUOT);
    248 	}
    249 	/*
    250 	 * If user is over their soft limit for too long, disallow inode
    251 	 * allocation. Reset time limit as they cross their soft limit.
    252 	 */
    253 	if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
    254 		if (dq->dq_curinodes < dq->dq_isoftlimit) {
    255 			dq->dq_itime =
    256 			    time_second + ip->i_ump->umq1_itime[type];
    257 			if (ip->i_uid == kauth_cred_geteuid(cred))
    258 				uprintf("\n%s: warning, %s %s\n",
    259 				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
    260 				    quotatypes[type], "inode quota exceeded");
    261 			return 0;
    262 		}
    263 		if (time_second > dq->dq_itime) {
    264 			if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
    265 			    ip->i_uid == kauth_cred_geteuid(cred)) {
    266 				uprintf("\n%s: write failed, %s %s\n",
    267 				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
    268 				    quotatypes[type],
    269 				    "inode quota exceeded for too long");
    270 				dq->dq_flags |= DQ_WARN(QL_FILE);
    271 			}
    272 			return SET_ERROR(EDQUOT);
    273 		}
    274 	}
    275 	return 0;
    276 }
    277 
    278 int
    279 quota1_umount(struct mount *mp, int flags)
    280 {
    281 	int i, error;
    282 	struct ufsmount *ump = VFSTOUFS(mp);
    283 	struct lwp *l = curlwp;
    284 
    285 	if ((ump->um_flags & UFS_QUOTA) == 0)
    286 		return 0;
    287 
    288 	if ((error = vflush(mp, NULLVP, SKIPSYSTEM | flags)) != 0)
    289 		return error;
    290 
    291 	for (i = 0; i < MAXQUOTAS; i++) {
    292 		if (ump->um_quotas[i] != NULLVP) {
    293 			quota1_handle_cmd_quotaoff(l, ump, i);
    294 		}
    295 	}
    296 	return 0;
    297 }
    298 
    299 /*
    300  * Code to process quotactl commands.
    301  */
    302 
    303 /*
    304  * set up a quota file for a particular file system.
    305  */
    306 int
    307 quota1_handle_cmd_quotaon(struct lwp *l, struct ufsmount *ump, int type,
    308     const char *fname)
    309 {
    310 	struct mount *mp = ump->um_mountp;
    311 	struct vnode *vp, **vpp;
    312 	struct vnode_iterator *marker;
    313 	struct dquot *dq;
    314 	int error;
    315 	struct pathbuf *pb;
    316 
    317 	if (type < 0 || type >= MAXQUOTAS)
    318 		return SET_ERROR(EINVAL);
    319 
    320 	if (ump->um_flags & UFS_QUOTA2) {
    321 		uprintf("%s: quotas v2 already enabled\n",
    322 		    mp->mnt_stat.f_mntonname);
    323 		return SET_ERROR(EBUSY);
    324 	}
    325 
    326 	if (mp->mnt_wapbl != NULL) {
    327 		printf("%s: quota v1 cannot be used with -o log\n",
    328 		    mp->mnt_stat.f_mntonname);
    329 		return SET_ERROR(EOPNOTSUPP);
    330 	}
    331 
    332 	vpp = &ump->um_quotas[type];
    333 
    334 	pb = pathbuf_create(fname);
    335 	if (pb == NULL) {
    336 		return SET_ERROR(ENOMEM);
    337 	}
    338 	error = vn_open(NULL, pb, 0, FREAD|FWRITE, 0, &vp, NULL, NULL);
    339 	if (error != 0) {
    340 		pathbuf_destroy(pb);
    341 		return error;
    342 	}
    343 	pathbuf_destroy(pb);
    344 
    345 	VOP_UNLOCK(vp);
    346 	if (vp->v_type != VREG) {
    347 		(void) vn_close(vp, FREAD|FWRITE, l->l_cred);
    348 		return SET_ERROR(EACCES);
    349 	}
    350 	if (*vpp != vp)
    351 		quota1_handle_cmd_quotaoff(l, ump, type);
    352 	mutex_enter(&dqlock);
    353 	while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
    354 		cv_wait(&dqcv, &dqlock);
    355 	ump->umq1_qflags[type] |= QTF_OPENING;
    356 	mutex_exit(&dqlock);
    357 	mp->mnt_flag |= MNT_QUOTA;
    358 	vp->v_vflag |= VV_SYSTEM;	/* XXXSMP */
    359 	*vpp = vp;
    360 	/*
    361 	 * Save the credential of the process that turned on quotas.
    362 	 * Set up the time limits for this quota.
    363 	 */
    364 	kauth_cred_hold(l->l_cred);
    365 	ump->um_cred[type] = l->l_cred;
    366 	ump->umq1_btime[type] = MAX_DQ_TIME;
    367 	ump->umq1_itime[type] = MAX_IQ_TIME;
    368 	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
    369 		if (dq->dq_btime > 0)
    370 			ump->umq1_btime[type] = dq->dq_btime;
    371 		if (dq->dq_itime > 0)
    372 			ump->umq1_itime[type] = dq->dq_itime;
    373 		dqrele(NULLVP, dq);
    374 	}
    375 	/*
    376 	 * Search vnodes associated with this mount point,
    377 	 * adding references to quota file being opened.
    378 	 * NB: only need to add dquot's for inodes being modified.
    379 	 */
    380 	vfs_vnode_iterator_init(mp, &marker);
    381 	while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
    382 		error = vn_lock(vp, LK_EXCLUSIVE);
    383 		if (error) {
    384 			vrele(vp);
    385 			continue;
    386 		}
    387 		mutex_enter(vp->v_interlock);
    388 		if (VTOI(vp) == NULL || vp->v_type == VNON ||
    389 		    vp->v_writecount == 0) {
    390 			mutex_exit(vp->v_interlock);
    391 			vput(vp);
    392 			continue;
    393 		}
    394 		mutex_exit(vp->v_interlock);
    395 		if ((error = getinoquota(VTOI(vp))) != 0) {
    396 			vput(vp);
    397 			break;
    398 		}
    399 		vput(vp);
    400 	}
    401 	vfs_vnode_iterator_destroy(marker);
    402 
    403 	mutex_enter(&dqlock);
    404 	ump->umq1_qflags[type] &= ~QTF_OPENING;
    405 	cv_broadcast(&dqcv);
    406 	if (error == 0)
    407 		ump->um_flags |= UFS_QUOTA;
    408 	mutex_exit(&dqlock);
    409 	if (error)
    410 		quota1_handle_cmd_quotaoff(l, ump, type);
    411 	return error;
    412 }
    413 
    414 /*
    415  * turn off disk quotas for a filesystem.
    416  */
    417 int
    418 quota1_handle_cmd_quotaoff(struct lwp *l, struct ufsmount *ump, int type)
    419 {
    420 	struct mount *mp = ump->um_mountp;
    421 	struct vnode *vp;
    422 	struct vnode *qvp;
    423 	struct vnode_iterator *marker;
    424 	struct dquot *dq;
    425 	struct inode *ip;
    426 	kauth_cred_t cred;
    427 	int i, error;
    428 
    429 	if (type < 0 || type >= MAXQUOTAS)
    430 		return SET_ERROR(EINVAL);
    431 
    432 	mutex_enter(&dqlock);
    433 	while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
    434 		cv_wait(&dqcv, &dqlock);
    435 	if ((qvp = ump->um_quotas[type]) == NULLVP) {
    436 		mutex_exit(&dqlock);
    437 		return 0;
    438 	}
    439 	ump->umq1_qflags[type] |= QTF_CLOSING;
    440 	mutex_exit(&dqlock);
    441 	/*
    442 	 * Search vnodes associated with this mount point,
    443 	 * deleting any references to quota file being closed.
    444 	 */
    445 	vfs_vnode_iterator_init(mp, &marker);
    446 	while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
    447 		error = vn_lock(vp, LK_EXCLUSIVE);
    448 		if (error) {
    449 			vrele(vp);
    450 			continue;
    451 		}
    452 		ip = VTOI(vp);
    453 		if (ip == NULL || vp->v_type == VNON) {
    454 			vput(vp);
    455 			continue;
    456 		}
    457 		dq = ip->i_dquot[type];
    458 		ip->i_dquot[type] = NODQUOT;
    459 		dqrele(vp, dq);
    460 		vput(vp);
    461 	}
    462 	vfs_vnode_iterator_destroy(marker);
    463 #ifdef DIAGNOSTIC
    464 	dqflush(qvp);
    465 #endif
    466 	qvp->v_vflag &= ~VV_SYSTEM;
    467 	error = vn_close(qvp, FREAD|FWRITE, l->l_cred);
    468 	mutex_enter(&dqlock);
    469 	ump->um_quotas[type] = NULLVP;
    470 	cred = ump->um_cred[type];
    471 	ump->um_cred[type] = NOCRED;
    472 	for (i = 0; i < MAXQUOTAS; i++)
    473 		if (ump->um_quotas[i] != NULLVP)
    474 			break;
    475 	ump->umq1_qflags[type] &= ~QTF_CLOSING;
    476 	if (i == MAXQUOTAS)
    477 		ump->um_flags &= ~UFS_QUOTA;
    478 	cv_broadcast(&dqcv);
    479 	mutex_exit(&dqlock);
    480 	kauth_cred_free(cred);
    481 	if (i == MAXQUOTAS)
    482 		mp->mnt_flag &= ~MNT_QUOTA;
    483 	return error;
    484 }
    485 
    486 int
    487 quota1_handle_cmd_get(struct ufsmount *ump, const struct quotakey *qk,
    488     struct quotaval *qv)
    489 {
    490 	struct dquot *dq;
    491 	int error;
    492 	struct quotaval blocks, files;
    493 	int idtype;
    494 	id_t id;
    495 
    496 	idtype = qk->qk_idtype;
    497 	id = qk->qk_id;
    498 
    499 	if (ump->um_quotas[idtype] == NULLVP)
    500 		return SET_ERROR(ENODEV);
    501 
    502 	if (id == QUOTA_DEFAULTID) { /* we want the grace period of id 0 */
    503 		if ((error = dqget(NULLVP, 0, ump, idtype, &dq)) != 0)
    504 			return error;
    505 
    506 	} else {
    507 		if ((error = dqget(NULLVP, id, ump, idtype, &dq)) != 0)
    508 			return error;
    509 	}
    510 	dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files);
    511 	dqrele(NULLVP, dq);
    512 	if (id == QUOTA_DEFAULTID) {
    513 		if (blocks.qv_expiretime > 0)
    514 			blocks.qv_grace = blocks.qv_expiretime;
    515 		else
    516 			blocks.qv_grace = MAX_DQ_TIME;
    517 		if (files.qv_expiretime > 0)
    518 			files.qv_grace = files.qv_expiretime;
    519 		else
    520 			files.qv_grace = MAX_DQ_TIME;
    521 	}
    522 
    523 	switch (qk->qk_objtype) {
    524 	case QUOTA_OBJTYPE_BLOCKS:
    525 		*qv = blocks;
    526 		break;
    527 	case QUOTA_OBJTYPE_FILES:
    528 		*qv = files;
    529 		break;
    530 	default:
    531 		return SET_ERROR(EINVAL);
    532 	}
    533 
    534 	return 0;
    535 }
    536 
    537 static uint32_t
    538 quota1_encode_limit(uint64_t lim)
    539 {
    540 	if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) {
    541 		return 0;
    542 	}
    543 	return lim;
    544 }
    545 
    546 int
    547 quota1_handle_cmd_put(struct ufsmount *ump, const struct quotakey *key,
    548     const struct quotaval *val)
    549 {
    550 	struct dquot *dq;
    551 	struct dqblk dqb;
    552 	int error;
    553 
    554 	switch (key->qk_idtype) {
    555 	case QUOTA_IDTYPE_USER:
    556 	case QUOTA_IDTYPE_GROUP:
    557 		break;
    558 	default:
    559 		return SET_ERROR(EINVAL);
    560 	}
    561 
    562 	switch (key->qk_objtype) {
    563 	case QUOTA_OBJTYPE_BLOCKS:
    564 	case QUOTA_OBJTYPE_FILES:
    565 		break;
    566 	default:
    567 		return SET_ERROR(EINVAL);
    568 	}
    569 
    570 	if (ump->um_quotas[key->qk_idtype] == NULLVP)
    571 		return SET_ERROR(ENODEV);
    572 
    573 	if (key->qk_id == QUOTA_DEFAULTID) {
    574 		/* just update grace times */
    575 		id_t id = 0;
    576 
    577 		if ((error = dqget(NULLVP, id, ump, key->qk_idtype, &dq)) != 0)
    578 			return error;
    579 		mutex_enter(&dq->dq_interlock);
    580 		if (val->qv_grace != QUOTA_NOTIME) {
    581 			if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS)
    582 				ump->umq1_btime[key->qk_idtype] = dq->dq_btime =
    583 					val->qv_grace;
    584 			if (key->qk_objtype == QUOTA_OBJTYPE_FILES)
    585 				ump->umq1_itime[key->qk_idtype] = dq->dq_itime =
    586 					val->qv_grace;
    587 		}
    588 		dq->dq_flags |= DQ_MOD;
    589 		mutex_exit(&dq->dq_interlock);
    590 		dqrele(NULLVP, dq);
    591 		return 0;
    592 	}
    593 
    594 	if ((error = dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0)
    595 		return error;
    596 	mutex_enter(&dq->dq_interlock);
    597 	/*
    598 	 * Copy all but the current values.
    599 	 * Reset time limit if previously had no soft limit or were
    600 	 * under it, but now have a soft limit and are over it.
    601 	 */
    602 	dqb.dqb_curblocks = dq->dq_curblocks;
    603 	dqb.dqb_curinodes = dq->dq_curinodes;
    604 	dqb.dqb_btime = dq->dq_btime;
    605 	dqb.dqb_itime = dq->dq_itime;
    606 	if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
    607 		dqb.dqb_bsoftlimit = quota1_encode_limit(val->qv_softlimit);
    608 		dqb.dqb_bhardlimit = quota1_encode_limit(val->qv_hardlimit);
    609 		dqb.dqb_isoftlimit = dq->dq_isoftlimit;
    610 		dqb.dqb_ihardlimit = dq->dq_ihardlimit;
    611 	} else {
    612 		KASSERT(key->qk_objtype == QUOTA_OBJTYPE_FILES);
    613 		dqb.dqb_bsoftlimit = dq->dq_bsoftlimit;
    614 		dqb.dqb_bhardlimit = dq->dq_bhardlimit;
    615 		dqb.dqb_isoftlimit = quota1_encode_limit(val->qv_softlimit);
    616 		dqb.dqb_ihardlimit = quota1_encode_limit(val->qv_hardlimit);
    617 	}
    618 	if (dq->dq_id == 0 && val->qv_grace != QUOTA_NOTIME) {
    619 		/* also update grace time if available */
    620 		if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
    621 			ump->umq1_btime[key->qk_idtype] = dqb.dqb_btime =
    622 				val->qv_grace;
    623 		}
    624 		if (key->qk_objtype == QUOTA_OBJTYPE_FILES) {
    625 			ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime =
    626 				val->qv_grace;
    627 		}
    628 	}
    629 	if (dqb.dqb_bsoftlimit &&
    630 	    dq->dq_curblocks >= dqb.dqb_bsoftlimit &&
    631 	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
    632 		dqb.dqb_btime = time_second + ump->umq1_btime[key->qk_idtype];
    633 	if (dqb.dqb_isoftlimit &&
    634 	    dq->dq_curinodes >= dqb.dqb_isoftlimit &&
    635 	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
    636 		dqb.dqb_itime = time_second + ump->umq1_itime[key->qk_idtype];
    637 	dq->dq_un.dq1_dqb = dqb;
    638 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
    639 		dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
    640 	if (dq->dq_curinodes < dq->dq_isoftlimit)
    641 		dq->dq_flags &= ~DQ_WARN(QL_FILE);
    642 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
    643 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
    644 		dq->dq_flags |= DQ_FAKE;
    645 	else
    646 		dq->dq_flags &= ~DQ_FAKE;
    647 	dq->dq_flags |= DQ_MOD;
    648 	mutex_exit(&dq->dq_interlock);
    649 	dqrele(NULLVP, dq);
    650 	return 0;
    651 }
    652 
    653 
    654 #if 0
    655 /*
    656  * Q_SETQUOTA - assign an entire dqblk structure.
    657  */
    658 int
    659 setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
    660 {
    661 	struct dquot *dq;
    662 	struct dquot *ndq;
    663 	struct ufsmount *ump = VFSTOUFS(mp);
    664 
    665 
    666 	if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
    667 		return error;
    668 	dq = ndq;
    669 	mutex_enter(&dq->dq_interlock);
    670 	/*
    671 	 * Copy all but the current values.
    672 	 * Reset time limit if previously had no soft limit or were
    673 	 * under it, but now have a soft limit and are over it.
    674 	 */
    675 	dqb->dqb_curblocks = dq->dq_curblocks;
    676 	dqb->dqb_curinodes = dq->dq_curinodes;
    677 	if (dq->dq_id != 0) {
    678 		dqb->dqb_btime = dq->dq_btime;
    679 		dqb->dqb_itime = dq->dq_itime;
    680 	}
    681 	if (dqb->dqb_bsoftlimit &&
    682 	    dq->dq_curblocks >= dqb->dqb_bsoftlimit &&
    683 	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
    684 		dqb->dqb_btime = time_second + ump->umq1_btime[type];
    685 	if (dqb->dqb_isoftlimit &&
    686 	    dq->dq_curinodes >= dqb->dqb_isoftlimit &&
    687 	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
    688 		dqb->dqb_itime = time_second + ump->umq1_itime[type];
    689 	dq->dq_un.dq1_dqb = *dqb;
    690 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
    691 		dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
    692 	if (dq->dq_curinodes < dq->dq_isoftlimit)
    693 		dq->dq_flags &= ~DQ_WARN(QL_FILE);
    694 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
    695 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
    696 		dq->dq_flags |= DQ_FAKE;
    697 	else
    698 		dq->dq_flags &= ~DQ_FAKE;
    699 	dq->dq_flags |= DQ_MOD;
    700 	mutex_exit(&dq->dq_interlock);
    701 	dqrele(NULLVP, dq);
    702 	return 0;
    703 }
    704 
    705 /*
    706  * Q_SETUSE - set current inode and block usage.
    707  */
    708 int
    709 setuse(struct mount *mp, u_long id, int type, void *addr)
    710 {
    711 	struct dquot *dq;
    712 	struct ufsmount *ump = VFSTOUFS(mp);
    713 	struct dquot *ndq;
    714 	struct dqblk usage;
    715 	int error;
    716 
    717 	error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
    718 	if (error)
    719 		return error;
    720 	if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
    721 		return error;
    722 	dq = ndq;
    723 	mutex_enter(&dq->dq_interlock);
    724 	/*
    725 	 * Reset time limit if have a soft limit and were
    726 	 * previously under it, but are now over it.
    727 	 */
    728 	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
    729 	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
    730 		dq->dq_btime = time_second + ump->umq1_btime[type];
    731 	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
    732 	    usage.dqb_curinodes >= dq->dq_isoftlimit)
    733 		dq->dq_itime = time_second + ump->umq1_itime[type];
    734 	dq->dq_curblocks = usage.dqb_curblocks;
    735 	dq->dq_curinodes = usage.dqb_curinodes;
    736 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
    737 		dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
    738 	if (dq->dq_curinodes < dq->dq_isoftlimit)
    739 		dq->dq_flags &= ~DQ_WARN(QL_FILE);
    740 	dq->dq_flags |= DQ_MOD;
    741 	mutex_exit(&dq->dq_interlock);
    742 	dqrele(NULLVP, dq);
    743 	return 0;
    744 }
    745 #endif
    746 
    747 /*
    748  * Q_SYNC - sync quota files to disk.
    749  */
    750 int
    751 q1sync(struct mount *mp)
    752 {
    753 	struct ufsmount *ump = VFSTOUFS(mp);
    754 	struct vnode *vp;
    755 	struct vnode_iterator *marker;
    756 	struct dquot *dq;
    757 	int i, error;
    758 
    759 	/*
    760 	 * Check if the mount point has any quotas.
    761 	 * If not, simply return.
    762 	 */
    763 	for (i = 0; i < MAXQUOTAS; i++)
    764 		if (ump->um_quotas[i] != NULLVP)
    765 			break;
    766 	if (i == MAXQUOTAS)
    767 		return 0;
    768 
    769 	/*
    770 	 * Search vnodes associated with this mount point,
    771 	 * synchronizing any modified dquot structures.
    772 	 */
    773 	vfs_vnode_iterator_init(mp, &marker);
    774 	while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
    775 		error = vn_lock(vp, LK_EXCLUSIVE);
    776 		if (error) {
    777 			vrele(vp);
    778 			continue;
    779 		}
    780 		if (VTOI(vp) == NULL || vp->v_type == VNON) {
    781 			vput(vp);
    782 			continue;
    783 		}
    784 		for (i = 0; i < MAXQUOTAS; i++) {
    785 			dq = VTOI(vp)->i_dquot[i];
    786 			if (dq == NODQUOT)
    787 				continue;
    788 			mutex_enter(&dq->dq_interlock);
    789 			if (dq->dq_flags & DQ_MOD)
    790 				dq1sync(vp, dq);
    791 			mutex_exit(&dq->dq_interlock);
    792 		}
    793 		vput(vp);
    794 	}
    795 	vfs_vnode_iterator_destroy(marker);
    796 	return 0;
    797 }
    798 
    799 /*
    800  * Obtain a dquot structure for the specified identifier and quota file
    801  * reading the information from the file if necessary.
    802  */
    803 int
    804 dq1get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type,
    805     struct dquot *dq)
    806 {
    807 	struct iovec aiov;
    808 	struct uio auio;
    809 	int error;
    810 
    811 	KASSERT(mutex_owned(&dq->dq_interlock));
    812 	vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
    813 	auio.uio_iov = &aiov;
    814 	auio.uio_iovcnt = 1;
    815 	aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
    816 	aiov.iov_len = sizeof (struct dqblk);
    817 	auio.uio_resid = sizeof (struct dqblk);
    818 	auio.uio_offset = (off_t)id * sizeof (struct dqblk);
    819 	auio.uio_rw = UIO_READ;
    820 	UIO_SETUP_SYSSPACE(&auio);
    821 	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
    822 	if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
    823 		memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk));
    824 	VOP_UNLOCK(dqvp);
    825 	/*
    826 	 * I/O error in reading quota file, release
    827 	 * quota structure and reflect problem to caller.
    828 	 */
    829 	if (error)
    830 		return error;
    831 	/*
    832 	 * Check for no limit to enforce.
    833 	 * Initialize time values if necessary.
    834 	 */
    835 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
    836 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
    837 		dq->dq_flags |= DQ_FAKE;
    838 	if (dq->dq_id != 0) {
    839 		if (dq->dq_btime == 0)
    840 			dq->dq_btime = time_second + ump->umq1_btime[type];
    841 		if (dq->dq_itime == 0)
    842 			dq->dq_itime = time_second + ump->umq1_itime[type];
    843 	}
    844 	return 0;
    845 }
    846 
    847 /*
    848  * Update the disk quota in the quota file.
    849  */
    850 int
    851 dq1sync(struct vnode *vp, struct dquot *dq)
    852 {
    853 	struct vnode *dqvp;
    854 	struct iovec aiov;
    855 	struct uio auio;
    856 	int error;
    857 
    858 	if (dq == NODQUOT)
    859 		panic("dq1sync: dquot");
    860 	KASSERT(mutex_owned(&dq->dq_interlock));
    861 	if ((dq->dq_flags & DQ_MOD) == 0)
    862 		return 0;
    863 	if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
    864 		panic("dq1sync: file");
    865 	KASSERT(dqvp != vp);
    866 	vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
    867 	auio.uio_iov = &aiov;
    868 	auio.uio_iovcnt = 1;
    869 	aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
    870 	aiov.iov_len = sizeof (struct dqblk);
    871 	auio.uio_resid = sizeof (struct dqblk);
    872 	auio.uio_offset = (off_t)dq->dq_id * sizeof (struct dqblk);
    873 	auio.uio_rw = UIO_WRITE;
    874 	UIO_SETUP_SYSSPACE(&auio);
    875 	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
    876 	if (auio.uio_resid && error == 0)
    877 		error = SET_ERROR(EIO);
    878 	dq->dq_flags &= ~DQ_MOD;
    879 	VOP_UNLOCK(dqvp);
    880 	return error;
    881 }
    882