etc/protocols - sync with NetBSD-8
[minix.git] / sys / ufs / lfs / ulfs_quota1.c
blobb6799fae0381eb8593e4d36b8cb55b4ab3a34f73
1 /* $NetBSD: ulfs_quota1.c,v 1.9 2015/07/26 08:33:53 hannken Exp $ */
2 /* from NetBSD: ufs_quota1.c,v 1.18 2012/02/02 03:00:48 matt Exp */
4 /*
5 * Copyright (c) 1982, 1986, 1990, 1993, 1995
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Robert Elz at The University of Melbourne.
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.
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.
35 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: ulfs_quota1.c,v 1.9 2015/07/26 08:33:53 hannken Exp $");
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>
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>
57 static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
58 static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
61 * Update disk usage, and take corrective action.
63 int
64 lfs_chkdq1(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
66 struct dquot *dq;
67 int i;
68 int ncurblocks, error;
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);
88 return (0);
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);
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);
112 return (0);
116 * Check for a valid change to a users allocation.
117 * Issue an error message if appropriate.
119 static int
120 chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
122 struct dquot *dq = ip->i_dquot[type];
123 long ncurblocks = dq->dq_curblocks + change;
125 KASSERT(mutex_owned(&dq->dq_interlock));
127 * If user would exceed their hard limit, disallow space allocation.
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);
137 return (EDQUOT);
140 * If user is over their soft limit for too long, disallow space
141 * allocation. Reset time limit as they cross their soft limit.
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);
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);
162 return (EDQUOT);
165 return (0);
169 * Check the inode limit, applying corrective action.
172 lfs_chkiq1(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
174 struct dquot *dq;
175 int i;
176 int ncurinodes, error;
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);
196 return (0);
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);
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);
219 return (0);
223 * Check for a valid change to a users allocation.
224 * Issue an error message if appropriate.
226 static int
227 chkiqchg(struct inode *ip, int32_t change, kauth_cred_t cred, int type)
229 struct dquot *dq = ip->i_dquot[type];
230 long ncurinodes = dq->dq_curinodes + change;
232 KASSERT(mutex_owned(&dq->dq_interlock));
234 * If user would exceed their hard limit, disallow inode allocation.
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);
244 return (EDQUOT);
247 * If user is over their soft limit for too long, disallow inode
248 * allocation. Reset time limit as they cross their soft limit.
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);
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);
269 return (EDQUOT);
272 return (0);
276 lfsquota1_umount(struct mount *mp, int flags)
278 int i, error;
279 struct ulfsmount *ump = VFSTOULFS(mp);
280 struct lfs *fs = ump->um_lfs;
281 struct lwp *l = curlwp;
283 if ((fs->um_flags & ULFS_QUOTA) == 0)
284 return 0;
286 if ((error = vflush(mp, NULLVP, SKIPSYSTEM | flags)) != 0)
287 return (error);
289 for (i = 0; i < ULFS_MAXQUOTAS; i++) {
290 if (ump->um_quotas[i] != NULLVP) {
291 lfsquota1_handle_cmd_quotaoff(l, ump, i);
294 return 0;
298 * Code to process quotactl commands.
302 * set up a quota file for a particular file system.
305 lfsquota1_handle_cmd_quotaon(struct lwp *l, struct ulfsmount *ump, int type,
306 const char *fname)
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;
317 if (fs->um_flags & ULFS_QUOTA2) {
318 uprintf("%s: quotas v2 already enabled\n",
319 mp->mnt_stat.f_mntonname);
320 return (EBUSY);
323 vpp = &ump->um_quotas[type];
325 pb = pathbuf_create(fname);
326 if (pb == NULL) {
327 return ENOMEM;
329 NDINIT(&nd, LOOKUP, FOLLOW, pb);
330 if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) {
331 pathbuf_destroy(pb);
332 return error;
334 vp = nd.ni_vp;
335 pathbuf_destroy(pb);
337 VOP_UNLOCK(vp);
338 if (vp->v_type != VREG) {
339 (void) vn_close(vp, FREAD|FWRITE, l->l_cred);
340 return (EACCES);
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;
353 * Save the credential of the process that turned on quotas.
354 * Set up the time limits for this quota.
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);
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.
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;
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;
386 mutex_exit(vp->v_interlock);
387 if ((error = lfs_getinoquota(VTOI(vp))) != 0) {
388 vput(vp);
389 break;
391 vput(vp);
393 vfs_vnode_iterator_destroy(marker);
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);
407 * turn off disk quotas for a filesystem.
410 lfsquota1_handle_cmd_quotaoff(struct lwp *l, struct ulfsmount *ump, int type)
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;
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);
429 ump->umq1_qflags[type] |= QTF_CLOSING;
430 fs->um_flags &= ~ULFS_QUOTA;
431 mutex_exit(&lfs_dqlock);
433 * Search vnodes associated with this mount point,
434 * deleting any references to quota file being closed.
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;
443 ip = VTOI(vp);
444 if (ip == NULL || vp->v_type == VNON) {
445 vput(vp);
446 continue;
448 dq = ip->i_dquot[type];
449 ip->i_dquot[type] = NODQUOT;
450 lfs_dqrele(vp, dq);
451 vput(vp);
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);
475 int
476 lfsquota1_handle_cmd_get(struct ulfsmount *ump, const struct quotakey *qk,
477 struct quotaval *qv)
479 struct dquot *dq;
480 int error;
481 struct quotaval blocks, files;
482 int idtype;
483 id_t id;
485 idtype = qk->qk_idtype;
486 id = qk->qk_id;
488 if (ump->um_quotas[idtype] == NULLVP)
489 return ENODEV;
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;
495 } else {
496 if ((error = lfs_dqget(NULLVP, id, ump, idtype, &dq)) != 0)
497 return error;
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;
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;
523 return 0;
526 static uint32_t
527 quota1_encode_limit(uint64_t lim)
529 if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) {
530 return 0;
532 return lim;
536 lfsquota1_handle_cmd_put(struct ulfsmount *ump, const struct quotakey *key,
537 const struct quotaval *val)
539 struct dquot *dq;
540 struct dqblk dqb;
541 int error;
543 switch (key->qk_idtype) {
544 case QUOTA_IDTYPE_USER:
545 case QUOTA_IDTYPE_GROUP:
546 break;
547 default:
548 return EINVAL;
551 switch (key->qk_objtype) {
552 case QUOTA_OBJTYPE_BLOCKS:
553 case QUOTA_OBJTYPE_FILES:
554 break;
555 default:
556 return EINVAL;
559 if (ump->um_quotas[key->qk_idtype] == NULLVP)
560 return ENODEV;
562 if (key->qk_id == QUOTA_DEFAULTID) {
563 /* just update grace times */
564 id_t id = 0;
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;
577 dq->dq_flags |= DQ_MOD;
578 mutex_exit(&dq->dq_interlock);
579 lfs_dqrele(NULLVP, dq);
580 return 0;
583 if ((error = lfs_dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0)
584 return (error);
585 mutex_enter(&dq->dq_interlock);
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.
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);
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;
613 if (key->qk_objtype == QUOTA_OBJTYPE_FILES) {
614 ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime =
615 val->qv_grace;
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);
643 #if 0
645 * Q_SETQUOTA - assign an entire dqblk structure.
648 setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
650 struct dquot *dq;
651 struct dquot *ndq;
652 struct ulfsmount *ump = VFSTOULFS(mp);
655 if ((error = lfs_dqget(NULLVP, id, ump, type, &ndq)) != 0)
656 return (error);
657 dq = ndq;
658 mutex_enter(&dq->dq_interlock);
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.
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;
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);
695 * Q_SETUSE - set current inode and block usage.
698 setuse(struct mount *mp, u_long id, int type, void *addr)
700 struct dquot *dq;
701 struct ulfsmount *ump = VFSTOULFS(mp);
702 struct dquot *ndq;
703 struct dqblk usage;
704 int error;
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);
714 * Reset time limit if have a soft limit and were
715 * previously under it, but are now over it.
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);
734 #endif
737 * Q_SYNC - sync quota files to disk.
740 lfs_q1sync(struct mount *mp)
742 struct ulfsmount *ump = VFSTOULFS(mp);
743 struct vnode *vp;
744 struct vnode_iterator *marker;
745 struct dquot *dq;
746 int i, error;
749 * Check if the mount point has any quotas.
750 * If not, simply return.
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);
759 * Search vnodes associated with this mount point,
760 * synchronizing any modified dquot structures.
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;
769 if (VTOI(vp) == NULL || vp->v_type == VNON) {
770 vput(vp);
771 continue;
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);
782 vput(vp);
784 vfs_vnode_iterator_destroy(marker);
785 return (0);
789 * Obtain a dquot structure for the specified identifier and quota file
790 * reading the information from the file if necessary.
793 lfs_dq1get(struct vnode *dqvp, u_long id, struct ulfsmount *ump, int type,
794 struct dquot *dq)
796 struct iovec aiov;
797 struct uio auio;
798 int error;
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);
815 * I/O error in reading quota file, release
816 * quota structure and reflect problem to caller.
818 if (error)
819 return (error);
821 * Check for no limit to enforce.
822 * Initialize time values if necessary.
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];
833 return (0);
837 * Update the disk quota in the quota file.
840 lfs_dq1sync(struct vnode *vp, struct dquot *dq)
842 struct vnode *dqvp;
843 struct iovec aiov;
844 struct uio auio;
845 int error;
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);