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