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