1/*	$NetBSD: ufs_quota1.c,v 1.17 2012/02/01 05:43:54 dholland 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.17 2012/02/01 05:43:54 dholland 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
56static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
57static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
58
59/*
60 * Update disk usage, and take corrective action.
61 */
62int
63chkdq1(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 */
118static int
119chkdqchg(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 */
170int
171chkiq1(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 */
225static int
226chkiqchg(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
274int
275quota1_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 */
302int
303quota1_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);
377again:
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 */
419int
420quota1_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);
449again:
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
494int
495quota1_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
545static uint32_t
546quota1_encode_limit(uint64_t lim)
547{
548	if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) {
549		return 0;
550	}
551	return lim;
552}
553
554int
555quota1_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 */
666int
667setquota1(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 */
716int
717setuse(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 */
758int
759q1sync(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 */
825int
826dq1get(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 */
872int
873dq1sync(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