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