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