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