ufs_quota.c revision 1.8 1 /* $NetBSD: ufs_quota.c,v 1.8 1996/02/09 22:36:09 christos Exp $ */
2
3 /*
4 * Copyright (c) 1982, 1986, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * @(#)ufs_quota.c 8.3 (Berkeley) 8/19/94
39 */
40 #include <sys/param.h>
41 #include <sys/kernel.h>
42 #include <sys/systm.h>
43 #include <sys/namei.h>
44 #include <sys/malloc.h>
45 #include <sys/file.h>
46 #include <sys/proc.h>
47 #include <sys/vnode.h>
48 #include <sys/mount.h>
49
50 #include <ufs/ufs/quota.h>
51 #include <ufs/ufs/inode.h>
52 #include <ufs/ufs/ufsmount.h>
53 #include <ufs/ufs/ufs_extern.h>
54
55 /*
56 * Quota name to error message mapping.
57 */
58 static char *quotatypes[] = INITQFNAMES;
59
60 /*
61 * Set up the quotas for an inode.
62 *
63 * This routine completely defines the semantics of quotas.
64 * If other criterion want to be used to establish quotas, the
65 * MAXQUOTAS value in quotas.h should be increased, and the
66 * additional dquots set up here.
67 */
68 int
69 getinoquota(ip)
70 register struct inode *ip;
71 {
72 struct ufsmount *ump;
73 struct vnode *vp = ITOV(ip);
74 int error;
75
76 ump = VFSTOUFS(vp->v_mount);
77 /*
78 * Set up the user quota based on file uid.
79 * EINVAL means that quotas are not enabled.
80 */
81 if (ip->i_dquot[USRQUOTA] == NODQUOT &&
82 (error =
83 dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
84 error != EINVAL)
85 return (error);
86 /*
87 * Set up the group quota based on file gid.
88 * EINVAL means that quotas are not enabled.
89 */
90 if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
91 (error =
92 dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
93 error != EINVAL)
94 return (error);
95 return (0);
96 }
97
98 /*
99 * Update disk usage, and take corrective action.
100 */
101 int
102 chkdq(ip, change, cred, flags)
103 register struct inode *ip;
104 long change;
105 struct ucred *cred;
106 int flags;
107 {
108 register struct dquot *dq;
109 register int i;
110 int ncurblocks, error;
111
112 #ifdef DIAGNOSTIC
113 if ((flags & CHOWN) == 0)
114 chkdquot(ip);
115 #endif
116 if (change == 0)
117 return (0);
118 if (change < 0) {
119 for (i = 0; i < MAXQUOTAS; i++) {
120 if ((dq = ip->i_dquot[i]) == NODQUOT)
121 continue;
122 while (dq->dq_flags & DQ_LOCK) {
123 dq->dq_flags |= DQ_WANT;
124 sleep((caddr_t)dq, PINOD+1);
125 }
126 ncurblocks = dq->dq_curblocks + change;
127 if (ncurblocks >= 0)
128 dq->dq_curblocks = ncurblocks;
129 else
130 dq->dq_curblocks = 0;
131 dq->dq_flags &= ~DQ_BLKS;
132 dq->dq_flags |= DQ_MOD;
133 }
134 return (0);
135 }
136 if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
137 for (i = 0; i < MAXQUOTAS; i++) {
138 if ((dq = ip->i_dquot[i]) == NODQUOT)
139 continue;
140 if ((error = chkdqchg(ip, change, cred, i)) != 0)
141 return (error);
142 }
143 }
144 for (i = 0; i < MAXQUOTAS; i++) {
145 if ((dq = ip->i_dquot[i]) == NODQUOT)
146 continue;
147 while (dq->dq_flags & DQ_LOCK) {
148 dq->dq_flags |= DQ_WANT;
149 sleep((caddr_t)dq, PINOD+1);
150 }
151 dq->dq_curblocks += change;
152 dq->dq_flags |= DQ_MOD;
153 }
154 return (0);
155 }
156
157 /*
158 * Check for a valid change to a users allocation.
159 * Issue an error message if appropriate.
160 */
161 int
162 chkdqchg(ip, change, cred, type)
163 struct inode *ip;
164 long change;
165 struct ucred *cred;
166 int type;
167 {
168 register struct dquot *dq = ip->i_dquot[type];
169 long ncurblocks = dq->dq_curblocks + change;
170
171 /*
172 * If user would exceed their hard limit, disallow space allocation.
173 */
174 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
175 if ((dq->dq_flags & DQ_BLKS) == 0 &&
176 ip->i_uid == cred->cr_uid) {
177 uprintf("\n%s: write failed, %s disk limit reached\n",
178 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
179 quotatypes[type]);
180 dq->dq_flags |= DQ_BLKS;
181 }
182 return (EDQUOT);
183 }
184 /*
185 * If user is over their soft limit for too long, disallow space
186 * allocation. Reset time limit as they cross their soft limit.
187 */
188 if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
189 if (dq->dq_curblocks < dq->dq_bsoftlimit) {
190 dq->dq_btime = time.tv_sec +
191 VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];
192 if (ip->i_uid == cred->cr_uid)
193 uprintf("\n%s: warning, %s %s\n",
194 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
195 quotatypes[type], "disk quota exceeded");
196 return (0);
197 }
198 if (time.tv_sec > dq->dq_btime) {
199 if ((dq->dq_flags & DQ_BLKS) == 0 &&
200 ip->i_uid == cred->cr_uid) {
201 uprintf("\n%s: write failed, %s %s\n",
202 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
203 quotatypes[type],
204 "disk quota exceeded for too long");
205 dq->dq_flags |= DQ_BLKS;
206 }
207 return (EDQUOT);
208 }
209 }
210 return (0);
211 }
212
213 /*
214 * Check the inode limit, applying corrective action.
215 */
216 int
217 chkiq(ip, change, cred, flags)
218 register struct inode *ip;
219 long change;
220 struct ucred *cred;
221 int flags;
222 {
223 register struct dquot *dq;
224 register int i;
225 int ncurinodes, error;
226
227 #ifdef DIAGNOSTIC
228 if ((flags & CHOWN) == 0)
229 chkdquot(ip);
230 #endif
231 if (change == 0)
232 return (0);
233 if (change < 0) {
234 for (i = 0; i < MAXQUOTAS; i++) {
235 if ((dq = ip->i_dquot[i]) == NODQUOT)
236 continue;
237 while (dq->dq_flags & DQ_LOCK) {
238 dq->dq_flags |= DQ_WANT;
239 sleep((caddr_t)dq, PINOD+1);
240 }
241 ncurinodes = dq->dq_curinodes + change;
242 if (ncurinodes >= 0)
243 dq->dq_curinodes = ncurinodes;
244 else
245 dq->dq_curinodes = 0;
246 dq->dq_flags &= ~DQ_INODS;
247 dq->dq_flags |= DQ_MOD;
248 }
249 return (0);
250 }
251 if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
252 for (i = 0; i < MAXQUOTAS; i++) {
253 if ((dq = ip->i_dquot[i]) == NODQUOT)
254 continue;
255 if ((error = chkiqchg(ip, change, cred, i)) != 0)
256 return (error);
257 }
258 }
259 for (i = 0; i < MAXQUOTAS; i++) {
260 if ((dq = ip->i_dquot[i]) == NODQUOT)
261 continue;
262 while (dq->dq_flags & DQ_LOCK) {
263 dq->dq_flags |= DQ_WANT;
264 sleep((caddr_t)dq, PINOD+1);
265 }
266 dq->dq_curinodes += change;
267 dq->dq_flags |= DQ_MOD;
268 }
269 return (0);
270 }
271
272 /*
273 * Check for a valid change to a users allocation.
274 * Issue an error message if appropriate.
275 */
276 int
277 chkiqchg(ip, change, cred, type)
278 struct inode *ip;
279 long change;
280 struct ucred *cred;
281 int type;
282 {
283 register struct dquot *dq = ip->i_dquot[type];
284 long ncurinodes = dq->dq_curinodes + change;
285
286 /*
287 * If user would exceed their hard limit, disallow inode allocation.
288 */
289 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
290 if ((dq->dq_flags & DQ_INODS) == 0 &&
291 ip->i_uid == cred->cr_uid) {
292 uprintf("\n%s: write failed, %s inode limit reached\n",
293 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
294 quotatypes[type]);
295 dq->dq_flags |= DQ_INODS;
296 }
297 return (EDQUOT);
298 }
299 /*
300 * If user is over their soft limit for too long, disallow inode
301 * allocation. Reset time limit as they cross their soft limit.
302 */
303 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
304 if (dq->dq_curinodes < dq->dq_isoftlimit) {
305 dq->dq_itime = time.tv_sec +
306 VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];
307 if (ip->i_uid == cred->cr_uid)
308 uprintf("\n%s: warning, %s %s\n",
309 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
310 quotatypes[type], "inode quota exceeded");
311 return (0);
312 }
313 if (time.tv_sec > dq->dq_itime) {
314 if ((dq->dq_flags & DQ_INODS) == 0 &&
315 ip->i_uid == cred->cr_uid) {
316 uprintf("\n%s: write failed, %s %s\n",
317 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
318 quotatypes[type],
319 "inode quota exceeded for too long");
320 dq->dq_flags |= DQ_INODS;
321 }
322 return (EDQUOT);
323 }
324 }
325 return (0);
326 }
327
328 #ifdef DIAGNOSTIC
329 /*
330 * On filesystems with quotas enabled, it is an error for a file to change
331 * size and not to have a dquot structure associated with it.
332 */
333 void
334 chkdquot(ip)
335 register struct inode *ip;
336 {
337 struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
338 register int i;
339
340 for (i = 0; i < MAXQUOTAS; i++) {
341 if (ump->um_quotas[i] == NULLVP ||
342 (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
343 continue;
344 if (ip->i_dquot[i] == NODQUOT) {
345 vprint("chkdquot: missing dquot", ITOV(ip));
346 panic("missing dquot");
347 }
348 }
349 }
350 #endif
351
352 /*
353 * Code to process quotactl commands.
354 */
355
356 /*
357 * Q_QUOTAON - set up a quota file for a particular file system.
358 */
359 int
360 quotaon(p, mp, type, fname)
361 struct proc *p;
362 struct mount *mp;
363 register int type;
364 caddr_t fname;
365 {
366 register struct ufsmount *ump = VFSTOUFS(mp);
367 register struct vnode *vp, **vpp;
368 struct vnode *nextvp;
369 struct dquot *dq;
370 int error;
371 struct nameidata nd;
372
373 vpp = &ump->um_quotas[type];
374 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p);
375 if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0)
376 return (error);
377 vp = nd.ni_vp;
378 VOP_UNLOCK(vp);
379 if (vp->v_type != VREG) {
380 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
381 return (EACCES);
382 }
383 if (vfs_busy(mp)) {
384 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
385 return (EBUSY);
386 }
387 if (*vpp != vp)
388 quotaoff(p, mp, type);
389 ump->um_qflags[type] |= QTF_OPENING;
390 mp->mnt_flag |= MNT_QUOTA;
391 vp->v_flag |= VSYSTEM;
392 *vpp = vp;
393 /*
394 * Save the credential of the process that turned on quotas.
395 * Set up the time limits for this quota.
396 */
397 crhold(p->p_ucred);
398 ump->um_cred[type] = p->p_ucred;
399 ump->um_btime[type] = MAX_DQ_TIME;
400 ump->um_itime[type] = MAX_IQ_TIME;
401 if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
402 if (dq->dq_btime > 0)
403 ump->um_btime[type] = dq->dq_btime;
404 if (dq->dq_itime > 0)
405 ump->um_itime[type] = dq->dq_itime;
406 dqrele(NULLVP, dq);
407 }
408 /*
409 * Search vnodes associated with this mount point,
410 * adding references to quota file being opened.
411 * NB: only need to add dquot's for inodes being modified.
412 */
413 again:
414 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
415 nextvp = vp->v_mntvnodes.le_next;
416 if (vp->v_writecount == 0)
417 continue;
418 if (vget(vp, 1))
419 goto again;
420 if ((error = getinoquota(VTOI(vp))) != 0) {
421 vput(vp);
422 break;
423 }
424 vput(vp);
425 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
426 goto again;
427 }
428 ump->um_qflags[type] &= ~QTF_OPENING;
429 if (error)
430 quotaoff(p, mp, type);
431 vfs_unbusy(mp);
432 return (error);
433 }
434
435 /*
436 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
437 */
438 int
439 quotaoff(p, mp, type)
440 struct proc *p;
441 struct mount *mp;
442 register int type;
443 {
444 register struct vnode *vp;
445 struct vnode *qvp, *nextvp;
446 struct ufsmount *ump = VFSTOUFS(mp);
447 register struct dquot *dq;
448 register struct inode *ip;
449 int error;
450
451 if ((mp->mnt_flag & MNT_MPBUSY) == 0)
452 panic("quotaoff: not busy");
453 if ((qvp = ump->um_quotas[type]) == NULLVP)
454 return (0);
455 ump->um_qflags[type] |= QTF_CLOSING;
456 /*
457 * Search vnodes associated with this mount point,
458 * deleting any references to quota file being closed.
459 */
460 again:
461 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
462 nextvp = vp->v_mntvnodes.le_next;
463 if (vget(vp, 1))
464 goto again;
465 ip = VTOI(vp);
466 dq = ip->i_dquot[type];
467 ip->i_dquot[type] = NODQUOT;
468 dqrele(vp, dq);
469 vput(vp);
470 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
471 goto again;
472 }
473 dqflush(qvp);
474 qvp->v_flag &= ~VSYSTEM;
475 error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
476 ump->um_quotas[type] = NULLVP;
477 crfree(ump->um_cred[type]);
478 ump->um_cred[type] = NOCRED;
479 ump->um_qflags[type] &= ~QTF_CLOSING;
480 for (type = 0; type < MAXQUOTAS; type++)
481 if (ump->um_quotas[type] != NULLVP)
482 break;
483 if (type == MAXQUOTAS)
484 mp->mnt_flag &= ~MNT_QUOTA;
485 return (error);
486 }
487
488 /*
489 * Q_GETQUOTA - return current values in a dqblk structure.
490 */
491 int
492 getquota(mp, id, type, addr)
493 struct mount *mp;
494 u_long id;
495 int type;
496 caddr_t addr;
497 {
498 struct dquot *dq;
499 int error;
500
501 if ((error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) != 0)
502 return (error);
503 error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
504 dqrele(NULLVP, dq);
505 return (error);
506 }
507
508 /*
509 * Q_SETQUOTA - assign an entire dqblk structure.
510 */
511 int
512 setquota(mp, id, type, addr)
513 struct mount *mp;
514 u_long id;
515 int type;
516 caddr_t addr;
517 {
518 register struct dquot *dq;
519 struct dquot *ndq;
520 struct ufsmount *ump = VFSTOUFS(mp);
521 struct dqblk newlim;
522 int error;
523
524 error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk));
525 if (error)
526 return (error);
527 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
528 return (error);
529 dq = ndq;
530 while (dq->dq_flags & DQ_LOCK) {
531 dq->dq_flags |= DQ_WANT;
532 sleep((caddr_t)dq, PINOD+1);
533 }
534 /*
535 * Copy all but the current values.
536 * Reset time limit if previously had no soft limit or were
537 * under it, but now have a soft limit and are over it.
538 */
539 newlim.dqb_curblocks = dq->dq_curblocks;
540 newlim.dqb_curinodes = dq->dq_curinodes;
541 if (dq->dq_id != 0) {
542 newlim.dqb_btime = dq->dq_btime;
543 newlim.dqb_itime = dq->dq_itime;
544 }
545 if (newlim.dqb_bsoftlimit &&
546 dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
547 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
548 newlim.dqb_btime = time.tv_sec + ump->um_btime[type];
549 if (newlim.dqb_isoftlimit &&
550 dq->dq_curinodes >= newlim.dqb_isoftlimit &&
551 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
552 newlim.dqb_itime = time.tv_sec + ump->um_itime[type];
553 dq->dq_dqb = newlim;
554 if (dq->dq_curblocks < dq->dq_bsoftlimit)
555 dq->dq_flags &= ~DQ_BLKS;
556 if (dq->dq_curinodes < dq->dq_isoftlimit)
557 dq->dq_flags &= ~DQ_INODS;
558 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
559 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
560 dq->dq_flags |= DQ_FAKE;
561 else
562 dq->dq_flags &= ~DQ_FAKE;
563 dq->dq_flags |= DQ_MOD;
564 dqrele(NULLVP, dq);
565 return (0);
566 }
567
568 /*
569 * Q_SETUSE - set current inode and block usage.
570 */
571 int
572 setuse(mp, id, type, addr)
573 struct mount *mp;
574 u_long id;
575 int type;
576 caddr_t addr;
577 {
578 register struct dquot *dq;
579 struct ufsmount *ump = VFSTOUFS(mp);
580 struct dquot *ndq;
581 struct dqblk usage;
582 int error;
583
584 error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk));
585 if (error)
586 return (error);
587 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
588 return (error);
589 dq = ndq;
590 while (dq->dq_flags & DQ_LOCK) {
591 dq->dq_flags |= DQ_WANT;
592 sleep((caddr_t)dq, PINOD+1);
593 }
594 /*
595 * Reset time limit if have a soft limit and were
596 * previously under it, but are now over it.
597 */
598 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
599 usage.dqb_curblocks >= dq->dq_bsoftlimit)
600 dq->dq_btime = time.tv_sec + ump->um_btime[type];
601 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
602 usage.dqb_curinodes >= dq->dq_isoftlimit)
603 dq->dq_itime = time.tv_sec + ump->um_itime[type];
604 dq->dq_curblocks = usage.dqb_curblocks;
605 dq->dq_curinodes = usage.dqb_curinodes;
606 if (dq->dq_curblocks < dq->dq_bsoftlimit)
607 dq->dq_flags &= ~DQ_BLKS;
608 if (dq->dq_curinodes < dq->dq_isoftlimit)
609 dq->dq_flags &= ~DQ_INODS;
610 dq->dq_flags |= DQ_MOD;
611 dqrele(NULLVP, dq);
612 return (0);
613 }
614
615 /*
616 * Q_SYNC - sync quota files to disk.
617 */
618 int
619 qsync(mp)
620 struct mount *mp;
621 {
622 struct ufsmount *ump = VFSTOUFS(mp);
623 register struct vnode *vp, *nextvp;
624 register struct dquot *dq;
625 register int i;
626
627 /*
628 * Check if the mount point has any quotas.
629 * If not, simply return.
630 */
631 if ((mp->mnt_flag & MNT_MPBUSY) == 0)
632 panic("qsync: not busy");
633 for (i = 0; i < MAXQUOTAS; i++)
634 if (ump->um_quotas[i] != NULLVP)
635 break;
636 if (i == MAXQUOTAS)
637 return (0);
638 /*
639 * Search vnodes associated with this mount point,
640 * synchronizing any modified dquot structures.
641 */
642 again:
643 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
644 nextvp = vp->v_mntvnodes.le_next;
645 if (VOP_ISLOCKED(vp))
646 continue;
647 if (vget(vp, 1))
648 goto again;
649 for (i = 0; i < MAXQUOTAS; i++) {
650 dq = VTOI(vp)->i_dquot[i];
651 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
652 dqsync(vp, dq);
653 }
654 vput(vp);
655 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
656 goto again;
657 }
658 return (0);
659 }
660
661 /*
662 * Code pertaining to management of the in-core dquot data structures.
663 */
664 #define DQHASH(dqvp, id) \
665 (&dqhashtbl[((((long)(dqvp)) >> 8) + id) & dqhash])
666 LIST_HEAD(dqhash, dquot) *dqhashtbl;
667 u_long dqhash;
668
669 /*
670 * Dquot free list.
671 */
672 #define DQUOTINC 5 /* minimum free dquots desired */
673 TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
674 long numdquot, desireddquot = DQUOTINC;
675
676 /*
677 * Initialize the quota system.
678 */
679 void
680 dqinit()
681 {
682
683 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
684 TAILQ_INIT(&dqfreelist);
685 }
686
687 /*
688 * Obtain a dquot structure for the specified identifier and quota file
689 * reading the information from the file if necessary.
690 */
691 int
692 dqget(vp, id, ump, type, dqp)
693 struct vnode *vp;
694 u_long id;
695 register struct ufsmount *ump;
696 register int type;
697 struct dquot **dqp;
698 {
699 register struct dquot *dq;
700 struct dqhash *dqh;
701 register struct vnode *dqvp;
702 struct iovec aiov;
703 struct uio auio;
704 int error;
705
706 dqvp = ump->um_quotas[type];
707 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
708 *dqp = NODQUOT;
709 return (EINVAL);
710 }
711 /*
712 * Check the cache first.
713 */
714 dqh = DQHASH(dqvp, id);
715 for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) {
716 if (dq->dq_id != id ||
717 dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
718 continue;
719 /*
720 * Cache hit with no references. Take
721 * the structure off the free list.
722 */
723 if (dq->dq_cnt == 0)
724 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
725 DQREF(dq);
726 *dqp = dq;
727 return (0);
728 }
729 /*
730 * Not in cache, allocate a new one.
731 */
732 if (dqfreelist.tqh_first == NODQUOT &&
733 numdquot < MAXQUOTAS * desiredvnodes)
734 desireddquot += DQUOTINC;
735 if (numdquot < desireddquot) {
736 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
737 bzero((char *)dq, sizeof *dq);
738 numdquot++;
739 } else {
740 if ((dq = dqfreelist.tqh_first) == NULL) {
741 tablefull("dquot");
742 *dqp = NODQUOT;
743 return (EUSERS);
744 }
745 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
746 panic("free dquot isn't");
747 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
748 LIST_REMOVE(dq, dq_hash);
749 }
750 /*
751 * Initialize the contents of the dquot structure.
752 */
753 if (vp != dqvp)
754 VOP_LOCK(dqvp);
755 LIST_INSERT_HEAD(dqh, dq, dq_hash);
756 DQREF(dq);
757 dq->dq_flags = DQ_LOCK;
758 dq->dq_id = id;
759 dq->dq_ump = ump;
760 dq->dq_type = type;
761 auio.uio_iov = &aiov;
762 auio.uio_iovcnt = 1;
763 aiov.iov_base = (caddr_t)&dq->dq_dqb;
764 aiov.iov_len = sizeof (struct dqblk);
765 auio.uio_resid = sizeof (struct dqblk);
766 auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
767 auio.uio_segflg = UIO_SYSSPACE;
768 auio.uio_rw = UIO_READ;
769 auio.uio_procp = (struct proc *)0;
770 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
771 if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
772 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
773 if (vp != dqvp)
774 VOP_UNLOCK(dqvp);
775 if (dq->dq_flags & DQ_WANT)
776 wakeup((caddr_t)dq);
777 dq->dq_flags = 0;
778 /*
779 * I/O error in reading quota file, release
780 * quota structure and reflect problem to caller.
781 */
782 if (error) {
783 LIST_REMOVE(dq, dq_hash);
784 dqrele(vp, dq);
785 *dqp = NODQUOT;
786 return (error);
787 }
788 /*
789 * Check for no limit to enforce.
790 * Initialize time values if necessary.
791 */
792 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
793 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
794 dq->dq_flags |= DQ_FAKE;
795 if (dq->dq_id != 0) {
796 if (dq->dq_btime == 0)
797 dq->dq_btime = time.tv_sec + ump->um_btime[type];
798 if (dq->dq_itime == 0)
799 dq->dq_itime = time.tv_sec + ump->um_itime[type];
800 }
801 *dqp = dq;
802 return (0);
803 }
804
805 /*
806 * Obtain a reference to a dquot.
807 */
808 void
809 dqref(dq)
810 struct dquot *dq;
811 {
812
813 dq->dq_cnt++;
814 }
815
816 /*
817 * Release a reference to a dquot.
818 */
819 void
820 dqrele(vp, dq)
821 struct vnode *vp;
822 register struct dquot *dq;
823 {
824
825 if (dq == NODQUOT)
826 return;
827 if (dq->dq_cnt > 1) {
828 dq->dq_cnt--;
829 return;
830 }
831 if (dq->dq_flags & DQ_MOD)
832 (void) dqsync(vp, dq);
833 if (--dq->dq_cnt > 0)
834 return;
835 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
836 }
837
838 /*
839 * Update the disk quota in the quota file.
840 */
841 int
842 dqsync(vp, dq)
843 struct vnode *vp;
844 register struct dquot *dq;
845 {
846 struct vnode *dqvp;
847 struct iovec aiov;
848 struct uio auio;
849 int error;
850
851 if (dq == NODQUOT)
852 panic("dqsync: dquot");
853 if ((dq->dq_flags & DQ_MOD) == 0)
854 return (0);
855 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
856 panic("dqsync: file");
857 if (vp != dqvp)
858 VOP_LOCK(dqvp);
859 while (dq->dq_flags & DQ_LOCK) {
860 dq->dq_flags |= DQ_WANT;
861 sleep((caddr_t)dq, PINOD+2);
862 if ((dq->dq_flags & DQ_MOD) == 0) {
863 if (vp != dqvp)
864 VOP_UNLOCK(dqvp);
865 return (0);
866 }
867 }
868 dq->dq_flags |= DQ_LOCK;
869 auio.uio_iov = &aiov;
870 auio.uio_iovcnt = 1;
871 aiov.iov_base = (caddr_t)&dq->dq_dqb;
872 aiov.iov_len = sizeof (struct dqblk);
873 auio.uio_resid = sizeof (struct dqblk);
874 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
875 auio.uio_segflg = UIO_SYSSPACE;
876 auio.uio_rw = UIO_WRITE;
877 auio.uio_procp = (struct proc *)0;
878 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
879 if (auio.uio_resid && error == 0)
880 error = EIO;
881 if (dq->dq_flags & DQ_WANT)
882 wakeup((caddr_t)dq);
883 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
884 if (vp != dqvp)
885 VOP_UNLOCK(dqvp);
886 return (error);
887 }
888
889 /*
890 * Flush all entries from the cache for a particular vnode.
891 */
892 void
893 dqflush(vp)
894 register struct vnode *vp;
895 {
896 register struct dquot *dq, *nextdq;
897 struct dqhash *dqh;
898
899 /*
900 * Move all dquot's that used to refer to this quota
901 * file off their hash chains (they will eventually
902 * fall off the head of the free list and be re-used).
903 */
904 for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
905 for (dq = dqh->lh_first; dq; dq = nextdq) {
906 nextdq = dq->dq_hash.le_next;
907 if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
908 continue;
909 if (dq->dq_cnt)
910 panic("dqflush: stray dquot");
911 LIST_REMOVE(dq, dq_hash);
912 dq->dq_ump = (struct ufsmount *)0;
913 }
914 }
915 }
916