1 /*	$NetBSD: ufs_quota1.c,v 1.22 2016/06/20 00:52:04 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.22 2016/06/20 00:52:04 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 
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
chkdq1(struct inode * ip,int64_t change,kauth_cred_t cred,int flags)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
chkdqchg(struct inode * ip,int64_t change,kauth_cred_t cred,int type)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
chkiq1(struct inode * ip,int32_t change,kauth_cred_t cred,int flags)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
chkiqchg(struct inode * ip,int32_t change,kauth_cred_t cred,int type)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
quota1_umount(struct mount * mp,int flags)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
quota1_handle_cmd_quotaon(struct lwp * l,struct ufsmount * ump,int type,const char * fname)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;
308 	struct vnode_iterator *marker;
309 	struct dquot *dq;
310 	int error;
311 	struct pathbuf *pb;
312 	struct nameidata nd;
313 
314 	if (ump->um_flags & UFS_QUOTA2) {
315 		uprintf("%s: quotas v2 already enabled\n",
316 		    mp->mnt_stat.f_mntonname);
317 		return (EBUSY);
318 	}
319 
320 	if (mp->mnt_wapbl != NULL) {
321 		printf("%s: quota v1 cannot be used with -o log\n",
322 		    mp->mnt_stat.f_mntonname);
323 		return (EOPNOTSUPP);
324 	}
325 
326 	vpp = &ump->um_quotas[type];
327 
328 	pb = pathbuf_create(fname);
329 	if (pb == NULL) {
330 		return ENOMEM;
331 	}
332 	NDINIT(&nd, LOOKUP, FOLLOW, pb);
333 	if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) {
334 		pathbuf_destroy(pb);
335 		return error;
336 	}
337 	vp = nd.ni_vp;
338 	pathbuf_destroy(pb);
339 
340 	VOP_UNLOCK(vp);
341 	if (vp->v_type != VREG) {
342 		(void) vn_close(vp, FREAD|FWRITE, l->l_cred);
343 		return (EACCES);
344 	}
345 	if (*vpp != vp)
346 		quota1_handle_cmd_quotaoff(l, ump, type);
347 	mutex_enter(&dqlock);
348 	while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
349 		cv_wait(&dqcv, &dqlock);
350 	ump->umq1_qflags[type] |= QTF_OPENING;
351 	mutex_exit(&dqlock);
352 	mp->mnt_flag |= MNT_QUOTA;
353 	vp->v_vflag |= VV_SYSTEM;	/* XXXSMP */
354 	*vpp = vp;
355 	/*
356 	 * Save the credential of the process that turned on quotas.
357 	 * Set up the time limits for this quota.
358 	 */
359 	kauth_cred_hold(l->l_cred);
360 	ump->um_cred[type] = l->l_cred;
361 	ump->umq1_btime[type] = MAX_DQ_TIME;
362 	ump->umq1_itime[type] = MAX_IQ_TIME;
363 	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
364 		if (dq->dq_btime > 0)
365 			ump->umq1_btime[type] = dq->dq_btime;
366 		if (dq->dq_itime > 0)
367 			ump->umq1_itime[type] = dq->dq_itime;
368 		dqrele(NULLVP, dq);
369 	}
370 	/*
371 	 * Search vnodes associated with this mount point,
372 	 * adding references to quota file being opened.
373 	 * NB: only need to add dquot's for inodes being modified.
374 	 */
375 	vfs_vnode_iterator_init(mp, &marker);
376 	while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
377 		error = vn_lock(vp, LK_EXCLUSIVE);
378 		if (error) {
379 			vrele(vp);
380 			continue;
381 		}
382 		mutex_enter(vp->v_interlock);
383 		if (VTOI(vp) == NULL || vp->v_type == VNON ||
384 		    vp->v_writecount == 0) {
385 			mutex_exit(vp->v_interlock);
386 			vput(vp);
387 			continue;
388 		}
389 		mutex_exit(vp->v_interlock);
390 		if ((error = getinoquota(VTOI(vp))) != 0) {
391 			vput(vp);
392 			break;
393 		}
394 		vput(vp);
395 	}
396 	vfs_vnode_iterator_destroy(marker);
397 
398 	mutex_enter(&dqlock);
399 	ump->umq1_qflags[type] &= ~QTF_OPENING;
400 	cv_broadcast(&dqcv);
401 	if (error == 0)
402 		ump->um_flags |= UFS_QUOTA;
403 	mutex_exit(&dqlock);
404 	if (error)
405 		quota1_handle_cmd_quotaoff(l, ump, type);
406 	return (error);
407 }
408 
409 /*
410  * turn off disk quotas for a filesystem.
411  */
412 int
quota1_handle_cmd_quotaoff(struct lwp * l,struct ufsmount * ump,int type)413 quota1_handle_cmd_quotaoff(struct lwp *l, struct ufsmount *ump, int type)
414 {
415 	struct mount *mp = ump->um_mountp;
416 	struct vnode *vp;
417 	struct vnode *qvp;
418 	struct vnode_iterator *marker;
419 	struct dquot *dq;
420 	struct inode *ip;
421 	kauth_cred_t cred;
422 	int i, error;
423 
424 	mutex_enter(&dqlock);
425 	while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
426 		cv_wait(&dqcv, &dqlock);
427 	if ((qvp = ump->um_quotas[type]) == NULLVP) {
428 		mutex_exit(&dqlock);
429 		return (0);
430 	}
431 	ump->umq1_qflags[type] |= QTF_CLOSING;
432 	ump->um_flags &= ~UFS_QUOTA;
433 	mutex_exit(&dqlock);
434 	/*
435 	 * Search vnodes associated with this mount point,
436 	 * deleting any references to quota file being closed.
437 	 */
438 	vfs_vnode_iterator_init(mp, &marker);
439 	while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
440 		error = vn_lock(vp, LK_EXCLUSIVE);
441 		if (error) {
442 			vrele(vp);
443 			continue;
444 		}
445 		ip = VTOI(vp);
446 		if (ip == NULL || vp->v_type == VNON) {
447 			vput(vp);
448 			continue;
449 		}
450 		dq = ip->i_dquot[type];
451 		ip->i_dquot[type] = NODQUOT;
452 		dqrele(vp, dq);
453 		vput(vp);
454 	}
455 	vfs_vnode_iterator_destroy(marker);
456 #ifdef DIAGNOSTIC
457 	dqflush(qvp);
458 #endif
459 	qvp->v_vflag &= ~VV_SYSTEM;
460 	error = vn_close(qvp, FREAD|FWRITE, l->l_cred);
461 	mutex_enter(&dqlock);
462 	ump->um_quotas[type] = NULLVP;
463 	cred = ump->um_cred[type];
464 	ump->um_cred[type] = NOCRED;
465 	for (i = 0; i < MAXQUOTAS; i++)
466 		if (ump->um_quotas[i] != NULLVP)
467 			break;
468 	ump->umq1_qflags[type] &= ~QTF_CLOSING;
469 	cv_broadcast(&dqcv);
470 	mutex_exit(&dqlock);
471 	kauth_cred_free(cred);
472 	if (i == MAXQUOTAS)
473 		mp->mnt_flag &= ~MNT_QUOTA;
474 	return (error);
475 }
476 
477 int
quota1_handle_cmd_get(struct ufsmount * ump,const struct quotakey * qk,struct quotaval * qv)478 quota1_handle_cmd_get(struct ufsmount *ump, const struct quotakey *qk,
479     struct quotaval *qv)
480 {
481 	struct dquot *dq;
482 	int error;
483 	struct quotaval blocks, files;
484 	int idtype;
485 	id_t id;
486 
487 	idtype = qk->qk_idtype;
488 	id = qk->qk_id;
489 
490 	if (ump->um_quotas[idtype] == NULLVP)
491 		return ENODEV;
492 
493 	if (id == QUOTA_DEFAULTID) { /* we want the grace period of id 0 */
494 		if ((error = dqget(NULLVP, 0, ump, idtype, &dq)) != 0)
495 			return error;
496 
497 	} else {
498 		if ((error = dqget(NULLVP, id, ump, idtype, &dq)) != 0)
499 			return error;
500 	}
501 	dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files);
502 	dqrele(NULLVP, dq);
503 	if (id == QUOTA_DEFAULTID) {
504 		if (blocks.qv_expiretime > 0)
505 			blocks.qv_grace = blocks.qv_expiretime;
506 		else
507 			blocks.qv_grace = MAX_DQ_TIME;
508 		if (files.qv_expiretime > 0)
509 			files.qv_grace = files.qv_expiretime;
510 		else
511 			files.qv_grace = MAX_DQ_TIME;
512 	}
513 
514 	switch (qk->qk_objtype) {
515 	    case QUOTA_OBJTYPE_BLOCKS:
516 		*qv = blocks;
517 		break;
518 	    case QUOTA_OBJTYPE_FILES:
519 		*qv = files;
520 		break;
521 	    default:
522 		return EINVAL;
523 	}
524 
525 	return 0;
526 }
527 
528 static uint32_t
quota1_encode_limit(uint64_t lim)529 quota1_encode_limit(uint64_t lim)
530 {
531 	if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) {
532 		return 0;
533 	}
534 	return lim;
535 }
536 
537 int
quota1_handle_cmd_put(struct ufsmount * ump,const struct quotakey * key,const struct quotaval * val)538 quota1_handle_cmd_put(struct ufsmount *ump, const struct quotakey *key,
539     const struct quotaval *val)
540 {
541 	struct dquot *dq;
542 	struct dqblk dqb;
543 	int error;
544 
545 	switch (key->qk_idtype) {
546 	    case QUOTA_IDTYPE_USER:
547 	    case QUOTA_IDTYPE_GROUP:
548 		break;
549 	    default:
550 		return EINVAL;
551 	}
552 
553 	switch (key->qk_objtype) {
554 	    case QUOTA_OBJTYPE_BLOCKS:
555 	    case QUOTA_OBJTYPE_FILES:
556 		break;
557 	    default:
558 		return EINVAL;
559 	}
560 
561 	if (ump->um_quotas[key->qk_idtype] == NULLVP)
562 		return ENODEV;
563 
564 	if (key->qk_id == QUOTA_DEFAULTID) {
565 		/* just update grace times */
566 		id_t id = 0;
567 
568 		if ((error = dqget(NULLVP, id, ump, key->qk_idtype, &dq)) != 0)
569 			return error;
570 		mutex_enter(&dq->dq_interlock);
571 		if (val->qv_grace != QUOTA_NOTIME) {
572 			if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS)
573 				ump->umq1_btime[key->qk_idtype] = dq->dq_btime =
574 					val->qv_grace;
575 			if (key->qk_objtype == QUOTA_OBJTYPE_FILES)
576 				ump->umq1_itime[key->qk_idtype] = dq->dq_itime =
577 					val->qv_grace;
578 		}
579 		dq->dq_flags |= DQ_MOD;
580 		mutex_exit(&dq->dq_interlock);
581 		dqrele(NULLVP, dq);
582 		return 0;
583 	}
584 
585 	if ((error = dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0)
586 		return (error);
587 	mutex_enter(&dq->dq_interlock);
588 	/*
589 	 * Copy all but the current values.
590 	 * Reset time limit if previously had no soft limit or were
591 	 * under it, but now have a soft limit and are over it.
592 	 */
593 	dqb.dqb_curblocks = dq->dq_curblocks;
594 	dqb.dqb_curinodes = dq->dq_curinodes;
595 	dqb.dqb_btime = dq->dq_btime;
596 	dqb.dqb_itime = dq->dq_itime;
597 	if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
598 		dqb.dqb_bsoftlimit = quota1_encode_limit(val->qv_softlimit);
599 		dqb.dqb_bhardlimit = quota1_encode_limit(val->qv_hardlimit);
600 		dqb.dqb_isoftlimit = dq->dq_isoftlimit;
601 		dqb.dqb_ihardlimit = dq->dq_ihardlimit;
602 	} else {
603 		KASSERT(key->qk_objtype == QUOTA_OBJTYPE_FILES);
604 		dqb.dqb_bsoftlimit = dq->dq_bsoftlimit;
605 		dqb.dqb_bhardlimit = dq->dq_bhardlimit;
606 		dqb.dqb_isoftlimit = quota1_encode_limit(val->qv_softlimit);
607 		dqb.dqb_ihardlimit = quota1_encode_limit(val->qv_hardlimit);
608 	}
609 	if (dq->dq_id == 0 && val->qv_grace != QUOTA_NOTIME) {
610 		/* also update grace time if available */
611 		if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
612 			ump->umq1_btime[key->qk_idtype] = dqb.dqb_btime =
613 				val->qv_grace;
614 		}
615 		if (key->qk_objtype == QUOTA_OBJTYPE_FILES) {
616 			ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime =
617 				val->qv_grace;
618 		}
619 	}
620 	if (dqb.dqb_bsoftlimit &&
621 	    dq->dq_curblocks >= dqb.dqb_bsoftlimit &&
622 	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
623 		dqb.dqb_btime = time_second + ump->umq1_btime[key->qk_idtype];
624 	if (dqb.dqb_isoftlimit &&
625 	    dq->dq_curinodes >= dqb.dqb_isoftlimit &&
626 	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
627 		dqb.dqb_itime = time_second + ump->umq1_itime[key->qk_idtype];
628 	dq->dq_un.dq1_dqb = dqb;
629 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
630 		dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
631 	if (dq->dq_curinodes < dq->dq_isoftlimit)
632 		dq->dq_flags &= ~DQ_WARN(QL_FILE);
633 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
634 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
635 		dq->dq_flags |= DQ_FAKE;
636 	else
637 		dq->dq_flags &= ~DQ_FAKE;
638 	dq->dq_flags |= DQ_MOD;
639 	mutex_exit(&dq->dq_interlock);
640 	dqrele(NULLVP, dq);
641 	return (0);
642 }
643 
644 
645 #if 0
646 /*
647  * Q_SETQUOTA - assign an entire dqblk structure.
648  */
649 int
650 setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
651 {
652 	struct dquot *dq;
653 	struct dquot *ndq;
654 	struct ufsmount *ump = VFSTOUFS(mp);
655 
656 
657 	if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
658 		return (error);
659 	dq = ndq;
660 	mutex_enter(&dq->dq_interlock);
661 	/*
662 	 * Copy all but the current values.
663 	 * Reset time limit if previously had no soft limit or were
664 	 * under it, but now have a soft limit and are over it.
665 	 */
666 	dqb->dqb_curblocks = dq->dq_curblocks;
667 	dqb->dqb_curinodes = dq->dq_curinodes;
668 	if (dq->dq_id != 0) {
669 		dqb->dqb_btime = dq->dq_btime;
670 		dqb->dqb_itime = dq->dq_itime;
671 	}
672 	if (dqb->dqb_bsoftlimit &&
673 	    dq->dq_curblocks >= dqb->dqb_bsoftlimit &&
674 	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
675 		dqb->dqb_btime = time_second + ump->umq1_btime[type];
676 	if (dqb->dqb_isoftlimit &&
677 	    dq->dq_curinodes >= dqb->dqb_isoftlimit &&
678 	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
679 		dqb->dqb_itime = time_second + ump->umq1_itime[type];
680 	dq->dq_un.dq1_dqb = *dqb;
681 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
682 		dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
683 	if (dq->dq_curinodes < dq->dq_isoftlimit)
684 		dq->dq_flags &= ~DQ_WARN(QL_FILE);
685 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
686 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
687 		dq->dq_flags |= DQ_FAKE;
688 	else
689 		dq->dq_flags &= ~DQ_FAKE;
690 	dq->dq_flags |= DQ_MOD;
691 	mutex_exit(&dq->dq_interlock);
692 	dqrele(NULLVP, dq);
693 	return (0);
694 }
695 
696 /*
697  * Q_SETUSE - set current inode and block usage.
698  */
699 int
700 setuse(struct mount *mp, u_long id, int type, void *addr)
701 {
702 	struct dquot *dq;
703 	struct ufsmount *ump = VFSTOUFS(mp);
704 	struct dquot *ndq;
705 	struct dqblk usage;
706 	int error;
707 
708 	error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
709 	if (error)
710 		return (error);
711 	if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
712 		return (error);
713 	dq = ndq;
714 	mutex_enter(&dq->dq_interlock);
715 	/*
716 	 * Reset time limit if have a soft limit and were
717 	 * previously under it, but are now over it.
718 	 */
719 	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
720 	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
721 		dq->dq_btime = time_second + ump->umq1_btime[type];
722 	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
723 	    usage.dqb_curinodes >= dq->dq_isoftlimit)
724 		dq->dq_itime = time_second + ump->umq1_itime[type];
725 	dq->dq_curblocks = usage.dqb_curblocks;
726 	dq->dq_curinodes = usage.dqb_curinodes;
727 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
728 		dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
729 	if (dq->dq_curinodes < dq->dq_isoftlimit)
730 		dq->dq_flags &= ~DQ_WARN(QL_FILE);
731 	dq->dq_flags |= DQ_MOD;
732 	mutex_exit(&dq->dq_interlock);
733 	dqrele(NULLVP, dq);
734 	return (0);
735 }
736 #endif
737 
738 /*
739  * Q_SYNC - sync quota files to disk.
740  */
741 int
q1sync(struct mount * mp)742 q1sync(struct mount *mp)
743 {
744 	struct ufsmount *ump = VFSTOUFS(mp);
745 	struct vnode *vp;
746 	struct vnode_iterator *marker;
747 	struct dquot *dq;
748 	int i, error;
749 
750 	/*
751 	 * Check if the mount point has any quotas.
752 	 * If not, simply return.
753 	 */
754 	for (i = 0; i < MAXQUOTAS; i++)
755 		if (ump->um_quotas[i] != NULLVP)
756 			break;
757 	if (i == MAXQUOTAS)
758 		return (0);
759 
760 	/*
761 	 * Search vnodes associated with this mount point,
762 	 * synchronizing any modified dquot structures.
763 	 */
764 	vfs_vnode_iterator_init(mp, &marker);
765 	while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
766 		error = vn_lock(vp, LK_EXCLUSIVE);
767 		if (error) {
768 			vrele(vp);
769 			continue;
770 		}
771 		if (VTOI(vp) == NULL || vp->v_type == VNON) {
772 			vput(vp);
773 			continue;
774 		}
775 		for (i = 0; i < MAXQUOTAS; i++) {
776 			dq = VTOI(vp)->i_dquot[i];
777 			if (dq == NODQUOT)
778 				continue;
779 			mutex_enter(&dq->dq_interlock);
780 			if (dq->dq_flags & DQ_MOD)
781 				dq1sync(vp, dq);
782 			mutex_exit(&dq->dq_interlock);
783 		}
784 		vput(vp);
785 	}
786 	vfs_vnode_iterator_destroy(marker);
787 	return (0);
788 }
789 
790 /*
791  * Obtain a dquot structure for the specified identifier and quota file
792  * reading the information from the file if necessary.
793  */
794 int
dq1get(struct vnode * dqvp,u_long id,struct ufsmount * ump,int type,struct dquot * dq)795 dq1get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type,
796     struct dquot *dq)
797 {
798 	struct iovec aiov;
799 	struct uio auio;
800 	int error;
801 
802 	KASSERT(mutex_owned(&dq->dq_interlock));
803 	vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
804 	auio.uio_iov = &aiov;
805 	auio.uio_iovcnt = 1;
806 	aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
807 	aiov.iov_len = sizeof (struct dqblk);
808 	auio.uio_resid = sizeof (struct dqblk);
809 	auio.uio_offset = (off_t)id * sizeof (struct dqblk);
810 	auio.uio_rw = UIO_READ;
811 	UIO_SETUP_SYSSPACE(&auio);
812 	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
813 	if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
814 		memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk));
815 	VOP_UNLOCK(dqvp);
816 	/*
817 	 * I/O error in reading quota file, release
818 	 * quota structure and reflect problem to caller.
819 	 */
820 	if (error)
821 		return (error);
822 	/*
823 	 * Check for no limit to enforce.
824 	 * Initialize time values if necessary.
825 	 */
826 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
827 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
828 		dq->dq_flags |= DQ_FAKE;
829 	if (dq->dq_id != 0) {
830 		if (dq->dq_btime == 0)
831 			dq->dq_btime = time_second + ump->umq1_btime[type];
832 		if (dq->dq_itime == 0)
833 			dq->dq_itime = time_second + ump->umq1_itime[type];
834 	}
835 	return (0);
836 }
837 
838 /*
839  * Update the disk quota in the quota file.
840  */
841 int
dq1sync(struct vnode * vp,struct dquot * dq)842 dq1sync(struct vnode *vp, struct dquot *dq)
843 {
844 	struct vnode *dqvp;
845 	struct iovec aiov;
846 	struct uio auio;
847 	int error;
848 
849 	if (dq == NODQUOT)
850 		panic("dq1sync: dquot");
851 	KASSERT(mutex_owned(&dq->dq_interlock));
852 	if ((dq->dq_flags & DQ_MOD) == 0)
853 		return (0);
854 	if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
855 		panic("dq1sync: file");
856 	KASSERT(dqvp != vp);
857 	vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
858 	auio.uio_iov = &aiov;
859 	auio.uio_iovcnt = 1;
860 	aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
861 	aiov.iov_len = sizeof (struct dqblk);
862 	auio.uio_resid = sizeof (struct dqblk);
863 	auio.uio_offset = (off_t)dq->dq_id * sizeof (struct dqblk);
864 	auio.uio_rw = UIO_WRITE;
865 	UIO_SETUP_SYSSPACE(&auio);
866 	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
867 	if (auio.uio_resid && error == 0)
868 		error = EIO;
869 	dq->dq_flags &= ~DQ_MOD;
870 	VOP_UNLOCK(dqvp);
871 	return (error);
872 }
873