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