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