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