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