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