ufs_quota.c revision 1.2 1 /* $NetBSD: ufs_quota.c,v 1.2 1994/06/29 06:47:30 cgd 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.2 (Berkeley) 12/30/93
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))
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))
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))
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))) {
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))
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 if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)))
525 return (error);
526 if (error = dqget(NULLVP, id, ump, type, &ndq))
527 return (error);
528 dq = ndq;
529 while (dq->dq_flags & DQ_LOCK) {
530 dq->dq_flags |= DQ_WANT;
531 sleep((caddr_t)dq, PINOD+1);
532 }
533 /*
534 * Copy all but the current values.
535 * Reset time limit if previously had no soft limit or were
536 * under it, but now have a soft limit and are over it.
537 */
538 newlim.dqb_curblocks = dq->dq_curblocks;
539 newlim.dqb_curinodes = dq->dq_curinodes;
540 if (dq->dq_id != 0) {
541 newlim.dqb_btime = dq->dq_btime;
542 newlim.dqb_itime = dq->dq_itime;
543 }
544 if (newlim.dqb_bsoftlimit &&
545 dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
546 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
547 newlim.dqb_btime = time.tv_sec + ump->um_btime[type];
548 if (newlim.dqb_isoftlimit &&
549 dq->dq_curinodes >= newlim.dqb_isoftlimit &&
550 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
551 newlim.dqb_itime = time.tv_sec + ump->um_itime[type];
552 dq->dq_dqb = newlim;
553 if (dq->dq_curblocks < dq->dq_bsoftlimit)
554 dq->dq_flags &= ~DQ_BLKS;
555 if (dq->dq_curinodes < dq->dq_isoftlimit)
556 dq->dq_flags &= ~DQ_INODS;
557 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
558 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
559 dq->dq_flags |= DQ_FAKE;
560 else
561 dq->dq_flags &= ~DQ_FAKE;
562 dq->dq_flags |= DQ_MOD;
563 dqrele(NULLVP, dq);
564 return (0);
565 }
566
567 /*
568 * Q_SETUSE - set current inode and block usage.
569 */
570 int
571 setuse(mp, id, type, addr)
572 struct mount *mp;
573 u_long id;
574 int type;
575 caddr_t addr;
576 {
577 register struct dquot *dq;
578 struct ufsmount *ump = VFSTOUFS(mp);
579 struct dquot *ndq;
580 struct dqblk usage;
581 int error;
582
583 if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)))
584 return (error);
585 if (error = dqget(NULLVP, id, ump, type, &ndq))
586 return (error);
587 dq = ndq;
588 while (dq->dq_flags & DQ_LOCK) {
589 dq->dq_flags |= DQ_WANT;
590 sleep((caddr_t)dq, PINOD+1);
591 }
592 /*
593 * Reset time limit if have a soft limit and were
594 * previously under it, but are now over it.
595 */
596 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
597 usage.dqb_curblocks >= dq->dq_bsoftlimit)
598 dq->dq_btime = time.tv_sec + ump->um_btime[type];
599 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
600 usage.dqb_curinodes >= dq->dq_isoftlimit)
601 dq->dq_itime = time.tv_sec + ump->um_itime[type];
602 dq->dq_curblocks = usage.dqb_curblocks;
603 dq->dq_curinodes = usage.dqb_curinodes;
604 if (dq->dq_curblocks < dq->dq_bsoftlimit)
605 dq->dq_flags &= ~DQ_BLKS;
606 if (dq->dq_curinodes < dq->dq_isoftlimit)
607 dq->dq_flags &= ~DQ_INODS;
608 dq->dq_flags |= DQ_MOD;
609 dqrele(NULLVP, dq);
610 return (0);
611 }
612
613 /*
614 * Q_SYNC - sync quota files to disk.
615 */
616 int
617 qsync(mp)
618 struct mount *mp;
619 {
620 struct ufsmount *ump = VFSTOUFS(mp);
621 register struct vnode *vp, *nextvp;
622 register struct dquot *dq;
623 register int i;
624
625 /*
626 * Check if the mount point has any quotas.
627 * If not, simply return.
628 */
629 if ((mp->mnt_flag & MNT_MPBUSY) == 0)
630 panic("qsync: not busy");
631 for (i = 0; i < MAXQUOTAS; i++)
632 if (ump->um_quotas[i] != NULLVP)
633 break;
634 if (i == MAXQUOTAS)
635 return (0);
636 /*
637 * Search vnodes associated with this mount point,
638 * synchronizing any modified dquot structures.
639 */
640 again:
641 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
642 nextvp = vp->v_mntvnodes.le_next;
643 if (VOP_ISLOCKED(vp))
644 continue;
645 if (vget(vp, 1))
646 goto again;
647 for (i = 0; i < MAXQUOTAS; i++) {
648 dq = VTOI(vp)->i_dquot[i];
649 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
650 dqsync(vp, dq);
651 }
652 vput(vp);
653 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
654 goto again;
655 }
656 return (0);
657 }
658
659 /*
660 * Code pertaining to management of the in-core dquot data structures.
661 */
662 struct dquot **dqhashtbl;
663 u_long dqhash;
664
665 /*
666 * Dquot free list.
667 */
668 #define DQUOTINC 5 /* minimum free dquots desired */
669 struct dquot *dqfreel, **dqback = &dqfreel;
670 long numdquot, desireddquot = DQUOTINC;
671
672 /*
673 * Initialize the quota system.
674 */
675 void
676 dqinit()
677 {
678
679 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
680 }
681
682 /*
683 * Obtain a dquot structure for the specified identifier and quota file
684 * reading the information from the file if necessary.
685 */
686 int
687 dqget(vp, id, ump, type, dqp)
688 struct vnode *vp;
689 u_long id;
690 register struct ufsmount *ump;
691 register int type;
692 struct dquot **dqp;
693 {
694 register struct dquot *dq, *dp, **dpp;
695 register struct vnode *dqvp;
696 struct iovec aiov;
697 struct uio auio;
698 int error;
699
700 dqvp = ump->um_quotas[type];
701 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
702 *dqp = NODQUOT;
703 return (EINVAL);
704 }
705 /*
706 * Check the cache first.
707 */
708 dpp = &dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash];
709 for (dq = *dpp; dq; dq = dq->dq_forw) {
710 if (dq->dq_id != id ||
711 dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
712 continue;
713 /*
714 * Cache hit with no references. Take
715 * the structure off the free list.
716 */
717 if (dq->dq_cnt == 0) {
718 if ((dp = dq->dq_freef) != NODQUOT)
719 dp->dq_freeb = dq->dq_freeb;
720 else
721 dqback = dq->dq_freeb;
722 *dq->dq_freeb = dp;
723 }
724 DQREF(dq);
725 *dqp = dq;
726 return (0);
727 }
728 /*
729 * Not in cache, allocate a new one.
730 */
731 if (dqfreel == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes)
732 desireddquot += DQUOTINC;
733 if (numdquot < desireddquot) {
734 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
735 bzero((char *)dq, sizeof *dq);
736 numdquot++;
737 } else {
738 if ((dq = dqfreel) == NULL) {
739 tablefull("dquot");
740 *dqp = NODQUOT;
741 return (EUSERS);
742 }
743 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
744 panic("free dquot isn't");
745 if ((dp = dq->dq_freef) != NODQUOT)
746 dp->dq_freeb = &dqfreel;
747 else
748 dqback = &dqfreel;
749 dqfreel = dp;
750 dq->dq_freef = NULL;
751 dq->dq_freeb = NULL;
752 if (dp = dq->dq_forw)
753 dp->dq_back = dq->dq_back;
754 *dq->dq_back = dp;
755 }
756 /*
757 * Initialize the contents of the dquot structure.
758 */
759 if (vp != dqvp)
760 VOP_LOCK(dqvp);
761 if (dp = *dpp)
762 dp->dq_back = &dq->dq_forw;
763 dq->dq_forw = dp;
764 dq->dq_back = dpp;
765 *dpp = dq;
766 DQREF(dq);
767 dq->dq_flags = DQ_LOCK;
768 dq->dq_id = id;
769 dq->dq_ump = ump;
770 dq->dq_type = type;
771 auio.uio_iov = &aiov;
772 auio.uio_iovcnt = 1;
773 aiov.iov_base = (caddr_t)&dq->dq_dqb;
774 aiov.iov_len = sizeof (struct dqblk);
775 auio.uio_resid = sizeof (struct dqblk);
776 auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
777 auio.uio_segflg = UIO_SYSSPACE;
778 auio.uio_rw = UIO_READ;
779 auio.uio_procp = (struct proc *)0;
780 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
781 if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
782 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
783 if (vp != dqvp)
784 VOP_UNLOCK(dqvp);
785 if (dq->dq_flags & DQ_WANT)
786 wakeup((caddr_t)dq);
787 dq->dq_flags = 0;
788 /*
789 * I/O error in reading quota file, release
790 * quota structure and reflect problem to caller.
791 */
792 if (error) {
793 if (dp = dq->dq_forw)
794 dp->dq_back = dq->dq_back;
795 *dq->dq_back = dp;
796 dq->dq_forw = NULL;
797 dq->dq_back = NULL;
798 dqrele(vp, dq);
799 *dqp = NODQUOT;
800 return (error);
801 }
802 /*
803 * Check for no limit to enforce.
804 * Initialize time values if necessary.
805 */
806 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
807 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
808 dq->dq_flags |= DQ_FAKE;
809 if (dq->dq_id != 0) {
810 if (dq->dq_btime == 0)
811 dq->dq_btime = time.tv_sec + ump->um_btime[type];
812 if (dq->dq_itime == 0)
813 dq->dq_itime = time.tv_sec + ump->um_itime[type];
814 }
815 *dqp = dq;
816 return (0);
817 }
818
819 /*
820 * Obtain a reference to a dquot.
821 */
822 void
823 dqref(dq)
824 struct dquot *dq;
825 {
826
827 dq->dq_cnt++;
828 }
829
830 /*
831 * Release a reference to a dquot.
832 */
833 void
834 dqrele(vp, dq)
835 struct vnode *vp;
836 register struct dquot *dq;
837 {
838
839 if (dq == NODQUOT)
840 return;
841 if (dq->dq_cnt > 1) {
842 dq->dq_cnt--;
843 return;
844 }
845 if (dq->dq_flags & DQ_MOD)
846 (void) dqsync(vp, dq);
847 if (--dq->dq_cnt > 0)
848 return;
849 if (dqfreel != NODQUOT) {
850 *dqback = dq;
851 dq->dq_freeb = dqback;
852 } else {
853 dqfreel = dq;
854 dq->dq_freeb = &dqfreel;
855 }
856 dq->dq_freef = NODQUOT;
857 dqback = &dq->dq_freef;
858 }
859
860 /*
861 * Update the disk quota in the quota file.
862 */
863 int
864 dqsync(vp, dq)
865 struct vnode *vp;
866 register struct dquot *dq;
867 {
868 struct vnode *dqvp;
869 struct iovec aiov;
870 struct uio auio;
871 int error;
872
873 if (dq == NODQUOT)
874 panic("dqsync: dquot");
875 if ((dq->dq_flags & DQ_MOD) == 0)
876 return (0);
877 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
878 panic("dqsync: file");
879 if (vp != dqvp)
880 VOP_LOCK(dqvp);
881 while (dq->dq_flags & DQ_LOCK) {
882 dq->dq_flags |= DQ_WANT;
883 sleep((caddr_t)dq, PINOD+2);
884 if ((dq->dq_flags & DQ_MOD) == 0) {
885 if (vp != dqvp)
886 VOP_UNLOCK(dqvp);
887 return (0);
888 }
889 }
890 dq->dq_flags |= DQ_LOCK;
891 auio.uio_iov = &aiov;
892 auio.uio_iovcnt = 1;
893 aiov.iov_base = (caddr_t)&dq->dq_dqb;
894 aiov.iov_len = sizeof (struct dqblk);
895 auio.uio_resid = sizeof (struct dqblk);
896 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
897 auio.uio_segflg = UIO_SYSSPACE;
898 auio.uio_rw = UIO_WRITE;
899 auio.uio_procp = (struct proc *)0;
900 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
901 if (auio.uio_resid && error == 0)
902 error = EIO;
903 if (dq->dq_flags & DQ_WANT)
904 wakeup((caddr_t)dq);
905 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
906 if (vp != dqvp)
907 VOP_UNLOCK(dqvp);
908 return (error);
909 }
910
911 /*
912 * Flush all entries from the cache for a particular vnode.
913 */
914 void
915 dqflush(vp)
916 register struct vnode *vp;
917 {
918 register struct dquot *dq, *dp, **dpp, *nextdq;
919
920 /*
921 * Move all dquot's that used to refer to this quota
922 * file off their hash chains (they will eventually
923 * fall off the head of the free list and be re-used).
924 */
925 for (dpp = &dqhashtbl[dqhash]; dpp >= dqhashtbl; dpp--) {
926 for (dq = *dpp; dq; dq = nextdq) {
927 nextdq = dq->dq_forw;
928 if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
929 continue;
930 if (dq->dq_cnt)
931 panic("dqflush: stray dquot");
932 if (dp = dq->dq_forw)
933 dp->dq_back = dq->dq_back;
934 *dq->dq_back = dp;
935 dq->dq_forw = NULL;
936 dq->dq_back = NULL;
937 dq->dq_ump = (struct ufsmount *)0;
938 }
939 }
940 }
941