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