]> git.saurik.com Git - apple/xnu.git/blob - bsd/ufs/ufs/ufs_quota.c
a1704ed379916a7d227748d8cace9a5f5b667c12
[apple/xnu.git] / bsd / ufs / ufs / ufs_quota.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*
31 * Copyright (c) 1982, 1986, 1990, 1993, 1995
32 * The Regents of the University of California. All rights reserved.
33 *
34 * This code is derived from software contributed to Berkeley by
35 * Robert Elz at The University of Melbourne.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 * must display the following acknowledgement:
47 * This product includes software developed by the University of
48 * California, Berkeley and its contributors.
49 * 4. Neither the name of the University nor the names of its contributors
50 * may be used to endorse or promote products derived from this software
51 * without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
66 */
67
68 #include <sys/param.h>
69 #include <sys/kernel.h>
70 #include <sys/systm.h>
71 #include <sys/malloc.h>
72 #include <sys/file.h>
73 #include <sys/proc.h>
74 #include <sys/kauth.h>
75 #include <sys/vnode_internal.h>
76 #include <sys/mount_internal.h>
77 #include <sys/namei.h>
78 #include <sys/quota.h>
79
80 #include <ufs/ufs/quota.h>
81 #include <ufs/ufs/inode.h>
82 #include <ufs/ufs/ufsmount.h>
83 #include <ufs/ufs/ufs_extern.h>
84
85 /*
86 * Quota name to error message mapping.
87 */
88 static char *quotatypes[] = INITQFNAMES;
89
90 /*
91 * Set up the quotas for an inode.
92 *
93 * This routine completely defines the semantics of quotas.
94 * If other criterion want to be used to establish quotas, the
95 * MAXQUOTAS value in quotas.h should be increased, and the
96 * additional dquots set up here.
97 */
98 int
99 getinoquota(ip)
100 register struct inode *ip;
101 {
102 struct ufsmount *ump;
103 struct vnode *vp = ITOV(ip);
104 int error;
105
106 ump = VFSTOUFS(vp->v_mount);
107 /*
108 * Set up the user quota based on file uid.
109 * EINVAL means that quotas are not enabled.
110 */
111 if (ip->i_dquot[USRQUOTA] == NODQUOT &&
112 (error =
113 dqget(ip->i_uid, &ump->um_qfiles[USRQUOTA], USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
114 error != EINVAL)
115 return (error);
116 /*
117 * Set up the group quota based on file gid.
118 * EINVAL means that quotas are not enabled.
119 */
120 if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
121 (error =
122 dqget(ip->i_gid, &ump->um_qfiles[GRPQUOTA], GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
123 error != EINVAL)
124 return (error);
125 return (0);
126 }
127
128 /*
129 * Update disk usage, and take corrective action.
130 */
131 int
132 chkdq(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
133 {
134 register struct dquot *dq;
135 register int i;
136 int64_t ncurbytes;
137 int error;
138 struct proc *p;
139
140 #if DIAGNOSTIC
141 if ((flags & CHOWN) == 0)
142 chkdquot(ip);
143 #endif
144 if (change == 0)
145 return (0);
146 if (change < 0) {
147 for (i = 0; i < MAXQUOTAS; i++) {
148 if ((dq = ip->i_dquot[i]) == NODQUOT)
149 continue;
150 dqlock(dq);
151
152 ncurbytes = dq->dq_curbytes + change;
153 if (ncurbytes >= 0)
154 dq->dq_curbytes = ncurbytes;
155 else
156 dq->dq_curbytes = 0;
157 dq->dq_flags &= ~DQ_BLKS;
158 dq->dq_flags |= DQ_MOD;
159
160 dqunlock(dq);
161 }
162 return (0);
163 }
164 #warning "hack for no cred passed to chkdq()"
165 p = current_proc();
166 if (cred == NOCRED)
167 cred = proc_ucred(kernproc);
168 if ((flags & FORCE) == 0 && (suser(cred, NULL) || (proc_forcequota(p)))) {
169 for (i = 0; i < MAXQUOTAS; i++) {
170 if ((dq = ip->i_dquot[i]) == NODQUOT)
171 continue;
172 if ( (error = chkdqchg(ip, change, cred, i)) )
173 return (error);
174 }
175 }
176 for (i = 0; i < MAXQUOTAS; i++) {
177 if ((dq = ip->i_dquot[i]) == NODQUOT)
178 continue;
179 dqlock(dq);
180
181 dq->dq_curbytes += change;
182 dq->dq_flags |= DQ_MOD;
183
184 dqunlock(dq);
185 }
186 return (0);
187 }
188
189 /*
190 * Check for a valid change to a users allocation.
191 * Issue an error message if appropriate.
192 */
193 int
194 chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
195 {
196 register struct dquot *dq = ip->i_dquot[type];
197 u_int64_t ncurbytes;
198
199 dqlock(dq);
200
201 ncurbytes = dq->dq_curbytes + change;
202 /*
203 * If user would exceed their hard limit, disallow space allocation.
204 */
205 if (ncurbytes >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
206 if ((dq->dq_flags & DQ_BLKS) == 0 &&
207 ip->i_uid == kauth_cred_getuid(cred)) {
208 #if 1
209 printf("\n%s: write failed, %s disk limit reached\n",
210 ITOV(ip)->v_mount->mnt_vfsstat.f_mntonname,
211 quotatypes[type]);
212 #endif
213 dq->dq_flags |= DQ_BLKS;
214 }
215 dqunlock(dq);
216
217 return (EDQUOT);
218 }
219 /*
220 * If user is over their soft limit for too long, disallow space
221 * allocation. Reset time limit as they cross their soft limit.
222 */
223 if (ncurbytes >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
224 struct timeval tv;
225
226 microtime(&tv);
227 if (dq->dq_curbytes < dq->dq_bsoftlimit) {
228 dq->dq_btime = tv.tv_sec +
229 VFSTOUFS(ITOV(ip)->v_mount)->um_qfiles[type].qf_btime;
230 #if 1
231 if (ip->i_uid == kauth_cred_getuid(cred))
232 printf("\n%s: warning, %s %s\n",
233 ITOV(ip)->v_mount->mnt_vfsstat.f_mntonname,
234 quotatypes[type], "disk quota exceeded");
235 #endif
236 dqunlock(dq);
237
238 return (0);
239 }
240 if (tv.tv_sec > dq->dq_btime) {
241 if ((dq->dq_flags & DQ_BLKS) == 0 &&
242 ip->i_uid == kauth_cred_getuid(cred)) {
243 #if 1
244 printf("\n%s: write failed, %s %s\n",
245 ITOV(ip)->v_mount->mnt_vfsstat.f_mntonname,
246 quotatypes[type],
247 "disk quota exceeded for too long");
248 #endif
249 dq->dq_flags |= DQ_BLKS;
250 }
251 dqunlock(dq);
252
253 return (EDQUOT);
254 }
255 }
256 dqunlock(dq);
257
258 return (0);
259 }
260
261 /*
262 * Check the inode limit, applying corrective action.
263 */
264 int
265 chkiq(struct inode *ip, long change, kauth_cred_t cred, int flags)
266 {
267 register struct dquot *dq;
268 register int i;
269 int ncurinodes, error;
270 struct proc *p;
271
272 #if DIAGNOSTIC
273 if ((flags & CHOWN) == 0)
274 chkdquot(ip);
275 #endif
276 if (change == 0)
277 return (0);
278 if (change < 0) {
279 for (i = 0; i < MAXQUOTAS; i++) {
280 if ((dq = ip->i_dquot[i]) == NODQUOT)
281 continue;
282 dqlock(dq);
283
284 ncurinodes = dq->dq_curinodes + change;
285 if (ncurinodes >= 0)
286 dq->dq_curinodes = ncurinodes;
287 else
288 dq->dq_curinodes = 0;
289 dq->dq_flags &= ~DQ_INODS;
290 dq->dq_flags |= DQ_MOD;
291
292 dqunlock(dq);
293 }
294 return (0);
295 }
296 #warning "hack for no cred passed to chkiq()"
297 p = current_proc();
298 if (cred == NOCRED)
299 cred = proc_ucred(kernproc);
300 if ((flags & FORCE) == 0 && (suser(cred, NULL) || (proc_forcequota(p)))) {
301 for (i = 0; i < MAXQUOTAS; i++) {
302 if ((dq = ip->i_dquot[i]) == NODQUOT)
303 continue;
304 if ( (error = chkiqchg(ip, change, cred, i)) )
305 return (error);
306 }
307 }
308 for (i = 0; i < MAXQUOTAS; i++) {
309 if ((dq = ip->i_dquot[i]) == NODQUOT)
310 continue;
311 dqlock(dq);
312
313 dq->dq_curinodes += change;
314 dq->dq_flags |= DQ_MOD;
315
316 dqunlock(dq);
317 }
318 return (0);
319 }
320
321 /*
322 * Check for a valid change to a users allocation.
323 * Issue an error message if appropriate.
324 */
325 int
326 chkiqchg(struct inode *ip, long change, kauth_cred_t cred, int type)
327 {
328 register struct dquot *dq = ip->i_dquot[type];
329 long ncurinodes;
330
331 dqlock(dq);
332
333 ncurinodes = dq->dq_curinodes + change;
334 /*
335 * If user would exceed their hard limit, disallow inode allocation.
336 */
337 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
338 if ((dq->dq_flags & DQ_INODS) == 0 &&
339 ip->i_uid == kauth_cred_getuid(cred)) {
340 #if 1
341 printf("\n%s: write failed, %s inode limit reached\n",
342 ITOV(ip)->v_mount->mnt_vfsstat.f_mntonname,
343 quotatypes[type]);
344 #endif
345 dq->dq_flags |= DQ_INODS;
346 }
347 dqunlock(dq);
348
349 return (EDQUOT);
350 }
351 /*
352 * If user is over their soft limit for too long, disallow inode
353 * allocation. Reset time limit as they cross their soft limit.
354 */
355 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
356 struct timeval tv;
357
358 microtime(&tv);
359 if (dq->dq_curinodes < dq->dq_isoftlimit) {
360 dq->dq_itime = tv.tv_sec +
361 VFSTOUFS(ITOV(ip)->v_mount)->um_qfiles[type].qf_itime;
362 #if 1
363 if (ip->i_uid == kauth_cred_getuid(cred))
364 printf("\n%s: warning, %s %s\n",
365 ITOV(ip)->v_mount->mnt_vfsstat.f_mntonname,
366 quotatypes[type], "inode quota exceeded");
367 #endif
368 dqunlock(dq);
369
370 return (0);
371 }
372 if (tv.tv_sec > dq->dq_itime) {
373 if ((dq->dq_flags & DQ_INODS) == 0 &&
374 ip->i_uid == kauth_cred_getuid(cred)) {
375 #if 1
376 printf("\n%s: write failed, %s %s\n",
377 ITOV(ip)->v_mount->mnt_vfsstat.f_mntonname,
378 quotatypes[type],
379 "inode quota exceeded for too long");
380 #endif
381 dq->dq_flags |= DQ_INODS;
382 }
383 dqunlock(dq);
384
385 return (EDQUOT);
386 }
387 }
388 dqunlock(dq);
389
390 return (0);
391 }
392
393 #if DIAGNOSTIC
394 /*
395 * On filesystems with quotas enabled, it is an error for a file to change
396 * size and not to have a dquot structure associated with it.
397 */
398 void
399 chkdquot(ip)
400 register struct inode *ip;
401 {
402 struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
403 register int i;
404
405 for (i = 0; i < MAXQUOTAS; i++) {
406 if (ump->um_qfiles[i].qf_vp == NULLVP)
407 continue;
408 if (ip->i_dquot[i] == NODQUOT) {
409 vprint("chkdquot: missing dquot", ITOV(ip));
410 panic("missing dquot");
411 }
412 }
413 }
414 #endif
415
416 /*
417 * Code to process quotactl commands.
418 */
419
420
421 struct ufs_quotaon_cargs {
422 int error;
423 };
424
425
426 static int
427 ufs_quotaon_callback(struct vnode *vp, void *cargs)
428 {
429 struct ufs_quotaon_cargs *args;
430
431 args = (struct ufs_quotaon_cargs *)cargs;
432
433 if ( (args->error = getinoquota(VTOI(vp))) )
434 return (VNODE_RETURNED_DONE);
435
436 return (VNODE_RETURNED);
437 }
438
439
440 /*
441 * Q_QUOTAON - set up a quota file for a particular file system.
442 */
443 int
444 quotaon(context, mp, type, fnamep)
445 vfs_context_t context;
446 struct mount *mp;
447 register int type;
448 caddr_t fnamep;
449 {
450 struct ufsmount *ump = VFSTOUFS(mp);
451 struct quotafile *qfp;
452 struct vnode *vp;
453 int error = 0;
454 struct ufs_quotaon_cargs args;
455
456 qfp = &ump->um_qfiles[type];
457
458 if ( (qf_get(qfp, QTF_OPENING)) )
459 return (0);
460
461 error = vnode_open(fnamep, FREAD|FWRITE, 0, 0, &vp, NULL);
462 if (error) {
463 goto out;
464 }
465 if (!vnode_isreg(vp)) {
466 (void) vnode_close(vp, FREAD|FWRITE, NULL);
467 error = EACCES;
468 goto out;
469 }
470 vfs_setflags(mp, (uint64_t)((unsigned int)MNT_QUOTA));
471 vnode_setnoflush(vp);
472 /*
473 * Save the credential of the process that turned on quotas.
474 */
475 qfp->qf_vp = vp;
476 qfp->qf_cred = vfs_context_ucred(context);
477 kauth_cred_ref(qfp->qf_cred);
478
479 /*
480 * Finish initializing the quota file
481 */
482 if ( (error = dqfileopen(&ump->um_qfiles[type], type)) ) {
483 (void) vnode_close(vp, FREAD|FWRITE, NULL);
484
485 kauth_cred_rele(qfp->qf_cred);
486 qfp->qf_cred = NOCRED;
487 qfp->qf_vp = NULLVP;
488 goto out;
489 }
490 qf_put(qfp, QTF_OPENING);
491
492 /*
493 * Search vnodes associated with this mount point,
494 * adding references to quota file being opened.
495 * NB: only need to add dquot's for inodes being modified.
496 *
497 * ufs_quota_callback will be called for each vnode open for
498 * 'write' (VNODE_WRITEABLE) hung off of this mount point
499 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
500 * properly referenced and unreferenced around the callback
501 */
502 args.error = 0;
503
504 vnode_iterate(mp, VNODE_WRITEABLE | VNODE_WAIT, ufs_quotaon_callback, (void *)&args);
505
506 error = args.error;
507
508 if (error)
509 quotaoff(mp, type);
510 return (error);
511 out:
512 qf_put(qfp, QTF_OPENING);
513
514 return (error);
515 }
516
517
518
519 struct ufs_quotaoff_cargs {
520 int type;
521 };
522
523 static int
524 ufs_quotaoff_callback(struct vnode *vp, void *cargs)
525 {
526 struct ufs_quotaoff_cargs *args;
527 struct inode *ip;
528 struct dquot *dq;
529
530 args = (struct ufs_quotaoff_cargs *)cargs;
531
532 ip = VTOI(vp);
533
534 dq = ip->i_dquot[args->type];
535 ip->i_dquot[args->type] = NODQUOT;
536
537 dqrele(dq);
538
539 return (VNODE_RETURNED);
540 }
541
542 /*
543 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
544 */
545 int
546 quotaoff(struct mount *mp, register int type)
547 {
548 struct vnode *qvp;
549 struct ufsmount *ump = VFSTOUFS(mp);
550 struct quotafile *qfp;
551 int error = 0;
552 kauth_cred_t cred;
553 struct ufs_quotaoff_cargs args;
554
555 qfp = &ump->um_qfiles[type];
556
557 if ( (qf_get(qfp, QTF_CLOSING)) )
558 return (0);
559 qvp = qfp->qf_vp;
560
561 /*
562 * Sync out any orpaned dirty dquot entries.
563 */
564 dqsync_orphans(qfp);
565
566 /*
567 * Search vnodes associated with this mount point,
568 * deleting any references to quota file being closed.
569 *
570 * ufs_quotaoff_callback will be called for each vnode
571 * hung off of this mount point
572 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
573 * properly referenced and unreferenced around the callback
574 */
575 args.type = type;
576
577 vnode_iterate(mp, VNODE_WAIT, ufs_quotaoff_callback, (void *)&args);
578
579 dqflush(qvp);
580 /* Finish tearing down the quota file */
581 dqfileclose(qfp, type);
582
583 vnode_clearnoflush(qvp);
584 error = vnode_close(qvp, FREAD|FWRITE, NULL);
585
586 qfp->qf_vp = NULLVP;
587 cred = qfp->qf_cred;
588 if (cred != NOCRED) {
589 qfp->qf_cred = NOCRED;
590 kauth_cred_rele(cred);
591 }
592 for (type = 0; type < MAXQUOTAS; type++)
593 if (ump->um_qfiles[type].qf_vp != NULLVP)
594 break;
595 if (type == MAXQUOTAS)
596 mp->mnt_flag &= ~MNT_QUOTA;
597
598 qf_put(qfp, QTF_CLOSING);
599
600 return (error);
601 }
602
603 /*
604 * Q_GETQUOTA - return current values in a dqblk structure.
605 */
606 int
607 getquota(mp, id, type, datap)
608 struct mount *mp;
609 u_long id;
610 int type;
611 caddr_t datap;
612 {
613 struct dquot *dq;
614 int error;
615
616 if ( (error = dqget(id, &VFSTOUFS(mp)->um_qfiles[type], type, &dq)) )
617 return (error);
618 dqlock(dq);
619
620 bcopy(&dq->dq_dqb, datap, sizeof(dq->dq_dqb));
621
622 dqunlock(dq);
623 dqrele(dq);
624
625 return (error);
626 }
627
628 /*
629 * Q_SETQUOTA - assign an entire dqblk structure.
630 */
631 int
632 setquota(mp, id, type, datap)
633 struct mount *mp;
634 u_long id;
635 int type;
636 caddr_t datap;
637 {
638 struct dquot *dq;
639 struct ufsmount *ump = VFSTOUFS(mp);
640 struct dqblk * newlimp = (struct dqblk *) datap;
641 struct timeval tv;
642 int error;
643
644 error = dqget(id, &ump->um_qfiles[type], type, &dq);
645 if (error)
646 return (error);
647 dqlock(dq);
648
649 /*
650 * Copy all but the current values.
651 * Reset time limit if previously had no soft limit or were
652 * under it, but now have a soft limit and are over it.
653 */
654 newlimp->dqb_curbytes = dq->dq_curbytes;
655 newlimp->dqb_curinodes = dq->dq_curinodes;
656 if (dq->dq_id != 0) {
657 newlimp->dqb_btime = dq->dq_btime;
658 newlimp->dqb_itime = dq->dq_itime;
659 }
660 if (newlimp->dqb_bsoftlimit &&
661 dq->dq_curbytes >= newlimp->dqb_bsoftlimit &&
662 (dq->dq_bsoftlimit == 0 || dq->dq_curbytes < dq->dq_bsoftlimit)) {
663 microtime(&tv);
664 newlimp->dqb_btime = tv.tv_sec + ump->um_qfiles[type].qf_btime;
665 }
666 if (newlimp->dqb_isoftlimit &&
667 dq->dq_curinodes >= newlimp->dqb_isoftlimit &&
668 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) {
669 microtime(&tv);
670 newlimp->dqb_itime = tv.tv_sec + ump->um_qfiles[type].qf_itime;
671 }
672 bcopy(newlimp, &dq->dq_dqb, sizeof(dq->dq_dqb));
673 if (dq->dq_curbytes < dq->dq_bsoftlimit)
674 dq->dq_flags &= ~DQ_BLKS;
675 if (dq->dq_curinodes < dq->dq_isoftlimit)
676 dq->dq_flags &= ~DQ_INODS;
677 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
678 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
679 dq->dq_flags |= DQ_FAKE;
680 else
681 dq->dq_flags &= ~DQ_FAKE;
682 dq->dq_flags |= DQ_MOD;
683
684 dqunlock(dq);
685 dqrele(dq);
686
687 return (0);
688 }
689
690 /*
691 * Q_SETUSE - set current inode and byte usage.
692 */
693 int
694 setuse(mp, id, type, datap)
695 struct mount *mp;
696 u_long id;
697 int type;
698 caddr_t datap;
699 {
700 struct dquot *dq;
701 struct ufsmount *ump = VFSTOUFS(mp);
702 struct timeval tv;
703 int error;
704 struct dqblk *quotablkp = (struct dqblk *) datap;
705
706 error = dqget(id, &ump->um_qfiles[type], type, &dq);
707 if (error)
708 return (error);
709 dqlock(dq);
710
711 /*
712 * Reset time limit if have a soft limit and were
713 * previously under it, but are now over it.
714 */
715 if (dq->dq_bsoftlimit && dq->dq_curbytes < dq->dq_bsoftlimit &&
716 quotablkp->dqb_curbytes >= dq->dq_bsoftlimit) {
717 microtime(&tv);
718 dq->dq_btime = tv.tv_sec + ump->um_qfiles[type].qf_btime;
719 }
720 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
721 quotablkp->dqb_curinodes >= dq->dq_isoftlimit) {
722 microtime(&tv);
723 dq->dq_itime = tv.tv_sec + ump->um_qfiles[type].qf_itime;
724 }
725 dq->dq_curbytes = quotablkp->dqb_curbytes;
726 dq->dq_curinodes = quotablkp->dqb_curinodes;
727 if (dq->dq_curbytes < dq->dq_bsoftlimit)
728 dq->dq_flags &= ~DQ_BLKS;
729 if (dq->dq_curinodes < dq->dq_isoftlimit)
730 dq->dq_flags &= ~DQ_INODS;
731 dq->dq_flags |= DQ_MOD;
732
733 dqunlock(dq);
734 dqrele(dq);
735
736 return (0);
737 }
738
739
740
741 static int
742 ufs_qsync_callback(struct vnode *vp, __unused void *cargs)
743 {
744 struct inode *ip;
745 struct dquot *dq;
746 int i;
747
748 ip = VTOI(vp);
749
750 for (i = 0; i < MAXQUOTAS; i++) {
751 dq = ip->i_dquot[i];
752 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
753 dqsync(dq);
754 }
755 return (VNODE_RETURNED);
756 }
757
758
759 /*
760 * Q_SYNC - sync quota files to disk.
761 */
762 int
763 qsync(mp)
764 struct mount *mp;
765 {
766 struct ufsmount *ump = VFSTOUFS(mp);
767 int i;
768
769 /*
770 * Check if the mount point has any quotas.
771 * If not, simply return.
772 */
773 for (i = 0; i < MAXQUOTAS; i++)
774 if (ump->um_qfiles[i].qf_vp != NULLVP)
775 break;
776 if (i == MAXQUOTAS)
777 return (0);
778 /*
779 * Search vnodes associated with this mount point,
780 * synchronizing any modified dquot structures.
781 *
782 * ufs_qsync_callback will be called for each vnode
783 * hung off of this mount point
784 * the vnode will be
785 * properly referenced and unreferenced around the callback
786 */
787 vnode_iterate(mp, 0, ufs_qsync_callback, (void *)NULL);
788
789 return (0);
790 }
791
792 /*
793 * Q_QUOTASTAT - get quota on/off status
794 */
795 int
796 quotastat(mp, type, datap)
797 struct mount *mp;
798 register int type;
799 caddr_t datap;
800 {
801 struct ufsmount *ump = VFSTOUFS(mp);
802 int error = 0;
803 int qstat;
804
805 if ((mp->mnt_flag & MNT_QUOTA) && (ump->um_qfiles[type].qf_vp != NULLVP))
806 qstat = 1; /* quotas are on for this type */
807 else
808 qstat = 0; /* quotas are off for this type */
809 *((int *)datap) = qstat;
810 return (error);
811 }
812