collect lock holder when locking inodes
[unix-history] / usr / src / sys / ufs / ffs / 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 *
76d72062 10 * @(#)ufs_quota.c 7.17 (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{
338 register struct ufsmount *ump = VFSTOUFS(mp);
339 register struct vnode *vp, **vpp;
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;
5c6762f5 350 VOP_UNLOCK(vp);
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 }
355 if (vfs_busy(mp)) {
f7fccf38 356 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
c9ba0248
KM
357 return (EBUSY);
358 }
359 if (*vpp != vp)
f7fccf38 360 quotaoff(p, mp, type);
c9ba0248 361 ump->um_qflags[type] |= QTF_OPENING;
82161bc8 362 mp->mnt_flag |= MNT_QUOTA;
c9ba0248
KM
363 vp->v_flag |= VSYSTEM;
364 *vpp = vp;
365 /*
366 * Save the credential of the process that turned on quotas.
367 * Set up the time limits for this quota.
368 */
c6f5111d
MK
369 crhold(p->p_ucred);
370 ump->um_cred[type] = p->p_ucred;
c9ba0248
KM
371 ump->um_btime[type] = MAX_DQ_TIME;
372 ump->um_itime[type] = MAX_IQ_TIME;
373 if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
374 if (dq->dq_btime > 0)
375 ump->um_btime[type] = dq->dq_btime;
376 if (dq->dq_itime > 0)
377 ump->um_itime[type] = dq->dq_itime;
378 dqrele(NULLVP, dq);
379 }
380 /*
381 * Search vnodes associated with this mount point,
382 * adding references to quota file being opened.
f7fccf38 383 * NB: only need to add dquot's for inodes being modified.
c9ba0248
KM
384 */
385again:
82161bc8 386 for (vp = mp->mnt_mounth; vp; vp = nextvp) {
c9ba0248 387 nextvp = vp->v_mountf;
f7fccf38 388 if (vp->v_writecount == 0)
c9ba0248
KM
389 continue;
390 if (vget(vp))
391 goto again;
7460dc90
KM
392 if (error = getinoquota(VTOI(vp))) {
393 vput(vp);
c9ba0248 394 break;
7460dc90 395 }
c9ba0248
KM
396 vput(vp);
397 if (vp->v_mountf != nextvp || vp->v_mount != mp)
398 goto again;
399 }
400 ump->um_qflags[type] &= ~QTF_OPENING;
401 if (error)
f7fccf38 402 quotaoff(p, mp, type);
c9ba0248
KM
403 vfs_unbusy(mp);
404 return (error);
405}
406
407/*
408 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
409 */
4e69d106 410int
f7fccf38
KM
411quotaoff(p, mp, type)
412 struct proc *p;
c9ba0248
KM
413 struct mount *mp;
414 register int type;
415{
416 register struct vnode *vp;
417 struct vnode *qvp, *nextvp;
418 struct ufsmount *ump = VFSTOUFS(mp);
419 register struct dquot *dq;
420 register struct inode *ip;
f7fccf38 421 int error;
c9ba0248 422
82161bc8 423 if ((mp->mnt_flag & MNT_MPBUSY) == 0)
c9ba0248
KM
424 panic("quotaoff: not busy");
425 if ((qvp = ump->um_quotas[type]) == NULLVP)
426 return (0);
427 ump->um_qflags[type] |= QTF_CLOSING;
428 /*
429 * Search vnodes associated with this mount point,
430 * deleting any references to quota file being closed.
431 */
432again:
82161bc8 433 for (vp = mp->mnt_mounth; vp; vp = nextvp) {
c9ba0248
KM
434 nextvp = vp->v_mountf;
435 if (vget(vp))
436 goto again;
437 ip = VTOI(vp);
438 dq = ip->i_dquot[type];
439 ip->i_dquot[type] = NODQUOT;
440 dqrele(vp, dq);
441 vput(vp);
442 if (vp->v_mountf != nextvp || vp->v_mount != mp)
443 goto again;
444 }
445 dqflush(qvp);
446 qvp->v_flag &= ~VSYSTEM;
f7fccf38 447 error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
c9ba0248
KM
448 ump->um_quotas[type] = NULLVP;
449 crfree(ump->um_cred[type]);
450 ump->um_cred[type] = NOCRED;
451 ump->um_qflags[type] &= ~QTF_CLOSING;
452 for (type = 0; type < MAXQUOTAS; type++)
453 if (ump->um_quotas[type] != NULLVP)
454 break;
455 if (type == MAXQUOTAS)
82161bc8 456 mp->mnt_flag &= ~MNT_QUOTA;
f7fccf38 457 return (error);
c9ba0248
KM
458}
459
460/*
461 * Q_GETQUOTA - return current values in a dqblk structure.
462 */
4e69d106 463int
c9ba0248
KM
464getquota(mp, id, type, addr)
465 struct mount *mp;
466 u_long id;
467 int type;
468 caddr_t addr;
469{
470 struct dquot *dq;
471 int error;
472
473 if (error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq))
474 return (error);
475 error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
476 dqrele(NULLVP, dq);
477 return (error);
478}
479
480/*
481 * Q_SETQUOTA - assign an entire dqblk structure.
482 */
4e69d106 483int
c9ba0248
KM
484setquota(mp, id, type, addr)
485 struct mount *mp;
486 u_long id;
487 int type;
488 caddr_t addr;
489{
490 register struct dquot *dq;
491 struct dquot *ndq;
492 struct ufsmount *ump = VFSTOUFS(mp);
493 struct dqblk newlim;
494 int error;
495
496 if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)))
497 return (error);
498 if (error = dqget(NULLVP, id, ump, type, &ndq))
499 return (error);
500 dq = ndq;
501 while (dq->dq_flags & DQ_LOCK) {
502 dq->dq_flags |= DQ_WANT;
503 sleep((caddr_t)dq, PINOD+1);
504 }
505 /*
506 * Copy all but the current values.
507 * Reset time limit if previously had no soft limit or were
508 * under it, but now have a soft limit and are over it.
509 */
510 newlim.dqb_curblocks = dq->dq_curblocks;
511 newlim.dqb_curinodes = dq->dq_curinodes;
512 if (dq->dq_id != 0) {
513 newlim.dqb_btime = dq->dq_btime;
514 newlim.dqb_itime = dq->dq_itime;
515 }
516 if (newlim.dqb_bsoftlimit &&
517 dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
518 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
519 newlim.dqb_btime = time.tv_sec + ump->um_btime[type];
520 if (newlim.dqb_isoftlimit &&
521 dq->dq_curinodes >= newlim.dqb_isoftlimit &&
522 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
523 newlim.dqb_itime = time.tv_sec + ump->um_itime[type];
524 dq->dq_dqb = newlim;
525 if (dq->dq_curblocks < dq->dq_bsoftlimit)
526 dq->dq_flags &= ~DQ_BLKS;
527 if (dq->dq_curinodes < dq->dq_isoftlimit)
528 dq->dq_flags &= ~DQ_INODS;
529 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
530 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
531 dq->dq_flags |= DQ_FAKE;
532 else
533 dq->dq_flags &= ~DQ_FAKE;
534 dq->dq_flags |= DQ_MOD;
535 dqrele(NULLVP, dq);
536 return (0);
537}
538
539/*
540 * Q_SETUSE - set current inode and block usage.
541 */
4e69d106 542int
c9ba0248
KM
543setuse(mp, id, type, addr)
544 struct mount *mp;
545 u_long id;
546 int type;
547 caddr_t addr;
548{
549 register struct dquot *dq;
550 struct ufsmount *ump = VFSTOUFS(mp);
551 struct dquot *ndq;
552 struct dqblk usage;
553 int error;
554
555 if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)))
556 return (error);
557 if (error = dqget(NULLVP, id, ump, type, &ndq))
558 return (error);
559 dq = ndq;
560 while (dq->dq_flags & DQ_LOCK) {
561 dq->dq_flags |= DQ_WANT;
562 sleep((caddr_t)dq, PINOD+1);
563 }
564 /*
565 * Reset time limit if have a soft limit and were
566 * previously under it, but are now over it.
567 */
568 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
569 usage.dqb_curblocks >= dq->dq_bsoftlimit)
570 dq->dq_btime = time.tv_sec + ump->um_btime[type];
571 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
572 usage.dqb_curinodes >= dq->dq_isoftlimit)
573 dq->dq_itime = time.tv_sec + ump->um_itime[type];
574 dq->dq_curblocks = usage.dqb_curblocks;
575 dq->dq_curinodes = usage.dqb_curinodes;
576 if (dq->dq_curblocks < dq->dq_bsoftlimit)
577 dq->dq_flags &= ~DQ_BLKS;
578 if (dq->dq_curinodes < dq->dq_isoftlimit)
579 dq->dq_flags &= ~DQ_INODS;
580 dq->dq_flags |= DQ_MOD;
581 dqrele(NULLVP, dq);
582 return (0);
583}
584
585/*
586 * Q_SYNC - sync quota files to disk.
587 */
4e69d106 588int
c9ba0248
KM
589qsync(mp)
590 struct mount *mp;
591{
592 struct ufsmount *ump = VFSTOUFS(mp);
593 register struct vnode *vp, *nextvp;
594 register struct dquot *dq;
595 register int i;
596
597 /*
eb1cc0cd
KM
598 * Check if the mount point has any quotas.
599 * If not, simply return.
c9ba0248 600 */
82161bc8 601 if ((mp->mnt_flag & MNT_MPBUSY) == 0)
c9ba0248 602 panic("qsync: not busy");
eb1cc0cd
KM
603 for (i = 0; i < MAXQUOTAS; i++)
604 if (ump->um_quotas[i] != NULLVP)
605 break;
606 if (i == MAXQUOTAS)
607 return (0);
608 /*
609 * Search vnodes associated with this mount point,
610 * synchronizing any modified dquot structures.
611 */
c9ba0248 612again:
82161bc8 613 for (vp = mp->mnt_mounth; vp; vp = nextvp) {
c9ba0248 614 nextvp = vp->v_mountf;
eb1cc0cd
KM
615 if (VOP_ISLOCKED(vp))
616 continue;
c9ba0248
KM
617 if (vget(vp))
618 goto again;
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);
625 if (vp->v_mountf != nextvp || vp->v_mount != mp)
626 goto again;
627 }
628 return (0);
629}
630
631/*
632 * Code pertaining to management of the in-core dquot data structures.
633 */
76d72062
KM
634struct dquot **dqhashtbl;
635u_long dqhash;
c9ba0248
KM
636
637/*
638 * Dquot free list.
639 */
640#define DQUOTINC 5 /* minimum free dquots desired */
641struct dquot *dqfreel, **dqback = &dqfreel;
642long numdquot, desireddquot = DQUOTINC;
643
644/*
645 * Initialize the quota system.
646 */
4e69d106 647void
c9ba0248
KM
648dqinit()
649{
76d72062
KM
650
651 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
c9ba0248
KM
652}
653
654/*
655 * Obtain a dquot structure for the specified identifier and quota file
656 * reading the information from the file if necessary.
657 */
4e69d106 658int
c9ba0248
KM
659dqget(vp, id, ump, type, dqp)
660 struct vnode *vp;
661 u_long id;
662 register struct ufsmount *ump;
663 register int type;
664 struct dquot **dqp;
665{
76d72062 666 register struct dquot *dq, *dp, **dpp;
c9ba0248
KM
667 register struct vnode *dqvp;
668 struct iovec aiov;
669 struct uio auio;
670 int error;
671
672 dqvp = ump->um_quotas[type];
673 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
674 *dqp = NODQUOT;
675 return (EINVAL);
676 }
677 /*
678 * Check the cache first.
679 */
76d72062
KM
680 dpp = &dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash];
681 for (dq = *dpp; dq; dq = dq->dq_forw) {
c9ba0248
KM
682 if (dq->dq_id != id ||
683 dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
684 continue;
685 /*
686 * Cache hit with no references. Take
687 * the structure off the free list.
688 */
689 if (dq->dq_cnt == 0) {
76d72062 690 if ((dp = dq->dq_freef) != NODQUOT)
c9ba0248
KM
691 dp->dq_freeb = dq->dq_freeb;
692 else
693 dqback = dq->dq_freeb;
694 *dq->dq_freeb = dp;
695 }
696 DQREF(dq);
697 *dqp = dq;
698 return (0);
699 }
700 /*
701 * Not in cache, allocate a new one.
702 */
703 if (dqfreel == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes)
704 desireddquot += DQUOTINC;
705 if (numdquot < desireddquot) {
706 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
707 bzero((char *)dq, sizeof *dq);
708 numdquot++;
709 } else {
710 if ((dq = dqfreel) == NULL) {
711 tablefull("dquot");
712 *dqp = NODQUOT;
713 return (EUSERS);
714 }
715 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
716 panic("free dquot isn't");
717 if ((dp = dq->dq_freef) != NODQUOT)
718 dp->dq_freeb = &dqfreel;
719 else
720 dqback = &dqfreel;
721 dqfreel = dp;
722 dq->dq_freef = NULL;
723 dq->dq_freeb = NULL;
76d72062
KM
724 if (dp = dq->dq_forw)
725 dp->dq_back = dq->dq_back;
726 *dq->dq_back = dp;
c9ba0248
KM
727 }
728 /*
729 * Initialize the contents of the dquot structure.
730 */
731 if (vp != dqvp)
732 VOP_LOCK(dqvp);
76d72062
KM
733 if (dp = *dpp)
734 dp->dq_back = &dq->dq_forw;
735 dq->dq_forw = dp;
736 dq->dq_back = dpp;
737 *dpp = dq;
c9ba0248
KM
738 DQREF(dq);
739 dq->dq_flags = DQ_LOCK;
740 dq->dq_id = id;
741 dq->dq_ump = ump;
742 dq->dq_type = type;
743 auio.uio_iov = &aiov;
744 auio.uio_iovcnt = 1;
745 aiov.iov_base = (caddr_t)&dq->dq_dqb;
746 aiov.iov_len = sizeof (struct dqblk);
747 auio.uio_resid = sizeof (struct dqblk);
748 auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
749 auio.uio_segflg = UIO_SYSSPACE;
750 auio.uio_rw = UIO_READ;
7bad0643 751 auio.uio_procp = (struct proc *)0;
c9ba0248 752 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
2842f018
KM
753 if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
754 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
c9ba0248
KM
755 if (vp != dqvp)
756 VOP_UNLOCK(dqvp);
757 if (dq->dq_flags & DQ_WANT)
758 wakeup((caddr_t)dq);
759 dq->dq_flags = 0;
760 /*
761 * I/O error in reading quota file, release
762 * quota structure and reflect problem to caller.
763 */
764 if (error) {
76d72062
KM
765 if (dp = dq->dq_forw)
766 dp->dq_back = dq->dq_back;
767 *dq->dq_back = dp;
768 dq->dq_forw = NULL;
769 dq->dq_back = NULL;
c9ba0248
KM
770 dqrele(vp, dq);
771 *dqp = NODQUOT;
772 return (error);
773 }
774 /*
775 * Check for no limit to enforce.
776 * Initialize time values if necessary.
777 */
778 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
779 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
780 dq->dq_flags |= DQ_FAKE;
781 if (dq->dq_id != 0) {
782 if (dq->dq_btime == 0)
783 dq->dq_btime = time.tv_sec + ump->um_btime[type];
784 if (dq->dq_itime == 0)
785 dq->dq_itime = time.tv_sec + ump->um_itime[type];
786 }
787 *dqp = dq;
788 return (0);
789}
790
791/*
792 * Obtain a reference to a dquot.
793 */
4e69d106 794void
c9ba0248
KM
795dqref(dq)
796 struct dquot *dq;
797{
798
799 dq->dq_cnt++;
800}
801
802/*
803 * Release a reference to a dquot.
804 */
4e69d106 805void
c9ba0248
KM
806dqrele(vp, dq)
807 struct vnode *vp;
808 register struct dquot *dq;
809{
810
811 if (dq == NODQUOT)
812 return;
813 if (dq->dq_cnt > 1) {
814 dq->dq_cnt--;
815 return;
816 }
817 if (dq->dq_flags & DQ_MOD)
818 (void) dqsync(vp, dq);
819 if (--dq->dq_cnt > 0)
820 return;
821 if (dqfreel != NODQUOT) {
822 *dqback = dq;
823 dq->dq_freeb = dqback;
824 } else {
825 dqfreel = dq;
826 dq->dq_freeb = &dqfreel;
827 }
828 dq->dq_freef = NODQUOT;
829 dqback = &dq->dq_freef;
830}
831
832/*
833 * Update the disk quota in the quota file.
834 */
4e69d106 835int
c9ba0248
KM
836dqsync(vp, dq)
837 struct vnode *vp;
838 register struct dquot *dq;
839{
840 struct vnode *dqvp;
841 struct iovec aiov;
842 struct uio auio;
843 int error;
844
845 if (dq == NODQUOT)
846 panic("dqsync: dquot");
847 if ((dq->dq_flags & DQ_MOD) == 0)
848 return (0);
849 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
850 panic("dqsync: file");
851 if (vp != dqvp)
852 VOP_LOCK(dqvp);
853 while (dq->dq_flags & DQ_LOCK) {
854 dq->dq_flags |= DQ_WANT;
855 sleep((caddr_t)dq, PINOD+2);
c0486eb9
KM
856 if ((dq->dq_flags & DQ_MOD) == 0) {
857 if (vp != dqvp)
858 VOP_UNLOCK(dqvp);
c9ba0248 859 return (0);
c0486eb9 860 }
c9ba0248
KM
861 }
862 dq->dq_flags |= DQ_LOCK;
863 auio.uio_iov = &aiov;
864 auio.uio_iovcnt = 1;
865 aiov.iov_base = (caddr_t)&dq->dq_dqb;
866 aiov.iov_len = sizeof (struct dqblk);
867 auio.uio_resid = sizeof (struct dqblk);
868 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
869 auio.uio_segflg = UIO_SYSSPACE;
870 auio.uio_rw = UIO_WRITE;
7bad0643 871 auio.uio_procp = (struct proc *)0;
c9ba0248
KM
872 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
873 if (auio.uio_resid && error == 0)
874 error = EIO;
875 if (dq->dq_flags & DQ_WANT)
876 wakeup((caddr_t)dq);
877 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
878 if (vp != dqvp)
879 VOP_UNLOCK(dqvp);
880 return (error);
881}
882
883/*
884 * Flush all entries from the cache for a particular vnode.
885 */
4e69d106 886void
c9ba0248
KM
887dqflush(vp)
888 register struct vnode *vp;
889{
76d72062 890 register struct dquot *dq, *dp, **dpp, *nextdq;
c9ba0248
KM
891
892 /*
893 * Move all dquot's that used to refer to this quota
894 * file off their hash chains (they will eventually
895 * fall off the head of the free list and be re-used).
896 */
76d72062
KM
897 for (dpp = &dqhashtbl[dqhash]; dpp >= dqhashtbl; dpp--) {
898 for (dq = *dpp; dq; dq = nextdq) {
c9ba0248
KM
899 nextdq = dq->dq_forw;
900 if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
901 continue;
902 if (dq->dq_cnt)
903 panic("dqflush: stray dquot");
76d72062
KM
904 if (dp = dq->dq_forw)
905 dp->dq_back = dq->dq_back;
906 *dq->dq_back = dp;
907 dq->dq_forw = NULL;
908 dq->dq_back = NULL;
c9ba0248
KM
909 dq->dq_ump = (struct ufsmount *)0;
910 }
911 }
912}