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