Annotation of src/sys/ufs/ufs/ufs_quota1.c, Revision 1.5
1.5 ! hannken 1: /* $NetBSD: ufs_quota1.c,v 1.4 2011/06/12 03:36:02 rmind Exp $ */
1.2 bouyer 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. Neither the name of the University nor the names of its contributors
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: *
34: * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
35: */
36:
37: #include <sys/cdefs.h>
1.5 ! hannken 38: __KERNEL_RCSID(0, "$NetBSD: ufs_quota1.c,v 1.4 2011/06/12 03:36:02 rmind Exp $");
1.2 bouyer 39:
40: #include <sys/param.h>
41: #include <sys/kernel.h>
42: #include <sys/systm.h>
43: #include <sys/namei.h>
44: #include <sys/file.h>
45: #include <sys/proc.h>
46: #include <sys/vnode.h>
47: #include <sys/mount.h>
48: #include <sys/kauth.h>
49:
1.3 bouyer 50: #include <quota/quotaprop.h>
1.2 bouyer 51: #include <ufs/ufs/quota1.h>
52: #include <ufs/ufs/inode.h>
53: #include <ufs/ufs/ufsmount.h>
54: #include <ufs/ufs/ufs_extern.h>
55: #include <ufs/ufs/ufs_quota.h>
56:
57: static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
58: static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
59:
60: /*
61: * Update disk usage, and take corrective action.
62: */
63: int
64: chkdq1(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
65: {
66: struct dquot *dq;
67: int i;
68: int ncurblocks, error;
69:
70: if ((error = getinoquota(ip)) != 0)
71: return error;
72: if (change == 0)
73: return (0);
74: if (change < 0) {
75: for (i = 0; i < MAXQUOTAS; i++) {
76: if ((dq = ip->i_dquot[i]) == NODQUOT)
77: continue;
78: mutex_enter(&dq->dq_interlock);
79: ncurblocks = dq->dq_curblocks + change;
80: if (ncurblocks >= 0)
81: dq->dq_curblocks = ncurblocks;
82: else
83: dq->dq_curblocks = 0;
84: dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
85: dq->dq_flags |= DQ_MOD;
86: mutex_exit(&dq->dq_interlock);
87: }
88: return (0);
89: }
90: for (i = 0; i < MAXQUOTAS; i++) {
91: if ((dq = ip->i_dquot[i]) == NODQUOT)
92: continue;
93: if ((flags & FORCE) == 0 &&
94: kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
95: KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, KAUTH_ARG(i),
96: KAUTH_ARG(QL_BLOCK), NULL) != 0) {
97: mutex_enter(&dq->dq_interlock);
98: error = chkdqchg(ip, change, cred, i);
99: mutex_exit(&dq->dq_interlock);
100: if (error != 0)
101: return (error);
102: }
103: }
104: for (i = 0; i < MAXQUOTAS; i++) {
105: if ((dq = ip->i_dquot[i]) == NODQUOT)
106: continue;
107: mutex_enter(&dq->dq_interlock);
108: dq->dq_curblocks += change;
109: dq->dq_flags |= DQ_MOD;
110: mutex_exit(&dq->dq_interlock);
111: }
112: return (0);
113: }
114:
115: /*
116: * Check for a valid change to a users allocation.
117: * Issue an error message if appropriate.
118: */
119: static int
120: chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
121: {
122: struct dquot *dq = ip->i_dquot[type];
123: long ncurblocks = dq->dq_curblocks + change;
124:
125: KASSERT(mutex_owned(&dq->dq_interlock));
126: /*
127: * If user would exceed their hard limit, disallow space allocation.
128: */
129: if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
130: if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
131: ip->i_uid == kauth_cred_geteuid(cred)) {
132: uprintf("\n%s: write failed, %s disk limit reached\n",
133: ITOV(ip)->v_mount->mnt_stat.f_mntonname,
134: quotatypes[type]);
135: dq->dq_flags |= DQ_WARN(QL_BLOCK);
136: }
137: return (EDQUOT);
138: }
139: /*
140: * If user is over their soft limit for too long, disallow space
141: * allocation. Reset time limit as they cross their soft limit.
142: */
143: if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
144: if (dq->dq_curblocks < dq->dq_bsoftlimit) {
145: dq->dq_btime =
146: time_second + ip->i_ump->umq1_btime[type];
147: if (ip->i_uid == kauth_cred_geteuid(cred))
148: uprintf("\n%s: warning, %s %s\n",
149: ITOV(ip)->v_mount->mnt_stat.f_mntonname,
150: quotatypes[type], "disk quota exceeded");
151: return (0);
152: }
153: if (time_second > dq->dq_btime) {
154: if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
155: ip->i_uid == kauth_cred_geteuid(cred)) {
156: uprintf("\n%s: write failed, %s %s\n",
157: ITOV(ip)->v_mount->mnt_stat.f_mntonname,
158: quotatypes[type],
159: "disk quota exceeded for too long");
160: dq->dq_flags |= DQ_WARN(QL_BLOCK);
161: }
162: return (EDQUOT);
163: }
164: }
165: return (0);
166: }
167:
168: /*
169: * Check the inode limit, applying corrective action.
170: */
171: int
172: chkiq1(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
173: {
174: struct dquot *dq;
175: int i;
176: int ncurinodes, error;
177:
178: if ((error = getinoquota(ip)) != 0)
179: return error;
180: if (change == 0)
181: return (0);
182: if (change < 0) {
183: for (i = 0; i < MAXQUOTAS; i++) {
184: if ((dq = ip->i_dquot[i]) == NODQUOT)
185: continue;
186: mutex_enter(&dq->dq_interlock);
187: ncurinodes = dq->dq_curinodes + change;
188: if (ncurinodes >= 0)
189: dq->dq_curinodes = ncurinodes;
190: else
191: dq->dq_curinodes = 0;
192: dq->dq_flags &= ~DQ_WARN(QL_FILE);
193: dq->dq_flags |= DQ_MOD;
194: mutex_exit(&dq->dq_interlock);
195: }
196: return (0);
197: }
198: for (i = 0; i < MAXQUOTAS; i++) {
199: if ((dq = ip->i_dquot[i]) == NODQUOT)
200: continue;
201: if ((flags & FORCE) == 0 && kauth_authorize_system(cred,
202: KAUTH_SYSTEM_FS_QUOTA, KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
203: KAUTH_ARG(i), KAUTH_ARG(QL_FILE), NULL) != 0) {
204: mutex_enter(&dq->dq_interlock);
205: error = chkiqchg(ip, change, cred, i);
206: mutex_exit(&dq->dq_interlock);
207: if (error != 0)
208: return (error);
209: }
210: }
211: for (i = 0; i < MAXQUOTAS; i++) {
212: if ((dq = ip->i_dquot[i]) == NODQUOT)
213: continue;
214: mutex_enter(&dq->dq_interlock);
215: dq->dq_curinodes += change;
216: dq->dq_flags |= DQ_MOD;
217: mutex_exit(&dq->dq_interlock);
218: }
219: return (0);
220: }
221:
222: /*
223: * Check for a valid change to a users allocation.
224: * Issue an error message if appropriate.
225: */
226: static int
227: chkiqchg(struct inode *ip, int32_t change, kauth_cred_t cred, int type)
228: {
229: struct dquot *dq = ip->i_dquot[type];
230: long ncurinodes = dq->dq_curinodes + change;
231:
232: KASSERT(mutex_owned(&dq->dq_interlock));
233: /*
234: * If user would exceed their hard limit, disallow inode allocation.
235: */
236: if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
237: if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
238: ip->i_uid == kauth_cred_geteuid(cred)) {
239: uprintf("\n%s: write failed, %s inode limit reached\n",
240: ITOV(ip)->v_mount->mnt_stat.f_mntonname,
241: quotatypes[type]);
242: dq->dq_flags |= DQ_WARN(QL_FILE);
243: }
244: return (EDQUOT);
245: }
246: /*
247: * If user is over their soft limit for too long, disallow inode
248: * allocation. Reset time limit as they cross their soft limit.
249: */
250: if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
251: if (dq->dq_curinodes < dq->dq_isoftlimit) {
252: dq->dq_itime =
253: time_second + ip->i_ump->umq1_itime[type];
254: if (ip->i_uid == kauth_cred_geteuid(cred))
255: uprintf("\n%s: warning, %s %s\n",
256: ITOV(ip)->v_mount->mnt_stat.f_mntonname,
257: quotatypes[type], "inode quota exceeded");
258: return (0);
259: }
260: if (time_second > dq->dq_itime) {
261: if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
262: ip->i_uid == kauth_cred_geteuid(cred)) {
263: uprintf("\n%s: write failed, %s %s\n",
264: ITOV(ip)->v_mount->mnt_stat.f_mntonname,
265: quotatypes[type],
266: "inode quota exceeded for too long");
267: dq->dq_flags |= DQ_WARN(QL_FILE);
268: }
269: return (EDQUOT);
270: }
271: }
272: return (0);
273: }
274:
275: int
276: quota1_umount(struct mount *mp, int flags)
277: {
278: int i, error;
279: struct ufsmount *ump = VFSTOUFS(mp);
280: struct lwp *l = curlwp;
281:
282: if ((ump->um_flags & UFS_QUOTA) == 0)
283: return 0;
284:
285: if ((error = vflush(mp, NULLVP, SKIPSYSTEM | flags)) != 0)
286: return (error);
287:
288: for (i = 0; i < MAXQUOTAS; i++) {
289: if (ump->um_quotas[i] != NULLVP) {
290: quota1_handle_cmd_quotaoff(l, ump, i);
291: }
292: }
293: return 0;
294: }
295:
296: /*
297: * Code to process quotactl commands.
298: */
299:
300: /*
301: * set up a quota file for a particular file system.
302: */
303: int
304: quota1_handle_cmd_quotaon(struct lwp *l, struct ufsmount *ump, int type,
305: const char *fname)
306: {
307: struct mount *mp = ump->um_mountp;
308: struct vnode *vp, **vpp, *mvp;
309: struct dquot *dq;
310: int error;
311: struct pathbuf *pb;
312: struct nameidata nd;
313:
314: if (ump->um_flags & UFS_QUOTA2) {
315: uprintf("%s: quotas v2 already enabled\n",
316: mp->mnt_stat.f_mntonname);
317: return (EBUSY);
318: }
319:
320: if (mp->mnt_wapbl != NULL) {
321: printf("%s: quota v1 cannot be used with -o log\n",
322: mp->mnt_stat.f_mntonname);
323: return (EOPNOTSUPP);
324: }
325:
326: vpp = &ump->um_quotas[type];
327:
328: pb = pathbuf_create(fname);
329: if (pb == NULL) {
330: return ENOMEM;
331: }
332: NDINIT(&nd, LOOKUP, FOLLOW, pb);
333: if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) {
334: pathbuf_destroy(pb);
335: return error;
336: }
337: vp = nd.ni_vp;
338: pathbuf_destroy(pb);
339:
340: VOP_UNLOCK(vp);
341: if (vp->v_type != VREG) {
342: (void) vn_close(vp, FREAD|FWRITE, l->l_cred);
343: return (EACCES);
344: }
345: if (*vpp != vp)
346: quota1_handle_cmd_quotaoff(l, ump, type);
347: mutex_enter(&dqlock);
348: while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
349: cv_wait(&dqcv, &dqlock);
350: ump->umq1_qflags[type] |= QTF_OPENING;
351: mutex_exit(&dqlock);
352: mp->mnt_flag |= MNT_QUOTA;
353: vp->v_vflag |= VV_SYSTEM; /* XXXSMP */
354: *vpp = vp;
355: /*
356: * Save the credential of the process that turned on quotas.
357: * Set up the time limits for this quota.
358: */
359: kauth_cred_hold(l->l_cred);
360: ump->um_cred[type] = l->l_cred;
361: ump->umq1_btime[type] = MAX_DQ_TIME;
362: ump->umq1_itime[type] = MAX_IQ_TIME;
363: if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
364: if (dq->dq_btime > 0)
365: ump->umq1_btime[type] = dq->dq_btime;
366: if (dq->dq_itime > 0)
367: ump->umq1_itime[type] = dq->dq_itime;
368: dqrele(NULLVP, dq);
369: }
370: /* Allocate a marker vnode. */
1.5 ! hannken 371: mvp = vnalloc(mp);
1.2 bouyer 372: /*
373: * Search vnodes associated with this mount point,
374: * adding references to quota file being opened.
375: * NB: only need to add dquot's for inodes being modified.
376: */
377: mutex_enter(&mntvnode_lock);
378: again:
379: for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) {
380: vmark(mvp, vp);
1.4 rmind 381: mutex_enter(vp->v_interlock);
1.2 bouyer 382: if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) ||
383: vp->v_type == VNON || vp->v_writecount == 0 ||
384: (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) {
1.4 rmind 385: mutex_exit(vp->v_interlock);
1.2 bouyer 386: continue;
387: }
388: mutex_exit(&mntvnode_lock);
389: if (vget(vp, LK_EXCLUSIVE)) {
390: mutex_enter(&mntvnode_lock);
391: (void)vunmark(mvp);
392: goto again;
393: }
394: if ((error = getinoquota(VTOI(vp))) != 0) {
395: vput(vp);
396: mutex_enter(&mntvnode_lock);
397: (void)vunmark(mvp);
398: break;
399: }
400: vput(vp);
401: mutex_enter(&mntvnode_lock);
402: }
403: mutex_exit(&mntvnode_lock);
404: vnfree(mvp);
1.5 ! hannken 405:
1.2 bouyer 406: mutex_enter(&dqlock);
407: ump->umq1_qflags[type] &= ~QTF_OPENING;
408: cv_broadcast(&dqcv);
409: if (error == 0)
410: ump->um_flags |= UFS_QUOTA;
411: mutex_exit(&dqlock);
412: if (error)
413: quota1_handle_cmd_quotaoff(l, ump, type);
414: return (error);
415: }
416:
417: /*
418: * turn off disk quotas for a filesystem.
419: */
420: int
421: quota1_handle_cmd_quotaoff(struct lwp *l, struct ufsmount *ump, int type)
422: {
423: struct mount *mp = ump->um_mountp;
424: struct vnode *vp;
425: struct vnode *qvp, *mvp;
426: struct dquot *dq;
427: struct inode *ip;
428: kauth_cred_t cred;
429: int i, error;
430:
431: /* Allocate a marker vnode. */
1.5 ! hannken 432: mvp = vnalloc(mp);
1.2 bouyer 433:
434: mutex_enter(&dqlock);
435: while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
436: cv_wait(&dqcv, &dqlock);
437: if ((qvp = ump->um_quotas[type]) == NULLVP) {
438: mutex_exit(&dqlock);
439: vnfree(mvp);
440: return (0);
441: }
442: ump->umq1_qflags[type] |= QTF_CLOSING;
443: ump->um_flags &= ~UFS_QUOTA;
444: mutex_exit(&dqlock);
445: /*
446: * Search vnodes associated with this mount point,
447: * deleting any references to quota file being closed.
448: */
449: mutex_enter(&mntvnode_lock);
450: again:
451: for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) {
452: vmark(mvp, vp);
1.4 rmind 453: mutex_enter(vp->v_interlock);
1.2 bouyer 454: if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) ||
455: vp->v_type == VNON ||
456: (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) {
1.4 rmind 457: mutex_exit(vp->v_interlock);
1.2 bouyer 458: continue;
459: }
460: mutex_exit(&mntvnode_lock);
461: if (vget(vp, LK_EXCLUSIVE)) {
462: mutex_enter(&mntvnode_lock);
463: (void)vunmark(mvp);
464: goto again;
465: }
466: ip = VTOI(vp);
467: dq = ip->i_dquot[type];
468: ip->i_dquot[type] = NODQUOT;
469: dqrele(vp, dq);
470: vput(vp);
471: mutex_enter(&mntvnode_lock);
472: }
473: mutex_exit(&mntvnode_lock);
474: #ifdef DIAGNOSTIC
475: dqflush(qvp);
476: #endif
477: qvp->v_vflag &= ~VV_SYSTEM;
478: error = vn_close(qvp, FREAD|FWRITE, l->l_cred);
479: mutex_enter(&dqlock);
480: ump->um_quotas[type] = NULLVP;
481: cred = ump->um_cred[type];
482: ump->um_cred[type] = NOCRED;
483: for (i = 0; i < MAXQUOTAS; i++)
484: if (ump->um_quotas[i] != NULLVP)
485: break;
486: ump->umq1_qflags[type] &= ~QTF_CLOSING;
487: cv_broadcast(&dqcv);
488: mutex_exit(&dqlock);
489: kauth_cred_free(cred);
490: if (i == MAXQUOTAS)
491: mp->mnt_flag &= ~MNT_QUOTA;
492: return (error);
493: }
494:
495: int
496: quota1_handle_cmd_get(struct ufsmount *ump, int type, int id,
497: int defaultq, prop_array_t replies)
498: {
499: struct dquot *dq;
1.3 bouyer 500: struct ufs_quota_entry qe[QUOTA_NLIMITS];
1.2 bouyer 501: prop_dictionary_t dict;
502: int error;
1.3 bouyer 503: uint64_t *valuesp[QUOTA_NLIMITS];
504: valuesp[QUOTA_LIMIT_BLOCK] = &qe[QUOTA_LIMIT_BLOCK].ufsqe_hardlimit;
505: valuesp[QUOTA_LIMIT_FILE] = &qe[QUOTA_LIMIT_FILE].ufsqe_hardlimit;
506:
1.2 bouyer 507:
508: if (ump->um_quotas[type] == NULLVP)
509: return ENODEV;
510:
511: if (defaultq) { /* we want the grace period of id 0 */
512: if ((error = dqget(NULLVP, 0, ump, type, &dq)) != 0)
513: return error;
1.3 bouyer 514:
1.2 bouyer 515: } else {
516: if ((error = dqget(NULLVP, id, ump, type, &dq)) != 0)
517: return error;
518: }
1.3 bouyer 519: dqblk2ufsqe(&dq->dq_un.dq1_dqb, qe);
1.2 bouyer 520: dqrele(NULLVP, dq);
521: if (defaultq) {
1.3 bouyer 522: if (qe[QUOTA_LIMIT_BLOCK].ufsqe_time > 0)
523: qe[QUOTA_LIMIT_BLOCK].ufsqe_grace =
524: qe[QUOTA_LIMIT_BLOCK].ufsqe_time;
1.2 bouyer 525: else
1.3 bouyer 526: qe[QUOTA_LIMIT_BLOCK].ufsqe_grace = MAX_DQ_TIME;
527: if (qe[QUOTA_LIMIT_FILE].ufsqe_time > 0)
528: qe[QUOTA_LIMIT_FILE].ufsqe_grace =
529: qe[QUOTA_LIMIT_FILE].ufsqe_time;
1.2 bouyer 530: else
1.3 bouyer 531: qe[QUOTA_LIMIT_FILE].ufsqe_grace = MAX_DQ_TIME;
1.2 bouyer 532: }
1.3 bouyer 533: dict = quota64toprop(id, defaultq, valuesp,
534: ufs_quota_entry_names, UFS_QUOTA_NENTRIES,
535: ufs_quota_limit_names, QUOTA_NLIMITS);
1.2 bouyer 536: if (dict == NULL)
537: return ENOMEM;
538: if (!prop_array_add_and_rel(replies, dict))
539: return ENOMEM;
540: return 0;
541: }
542:
543: int
544: quota1_handle_cmd_set(struct ufsmount *ump, int type, int id,
545: int defaultq, prop_dictionary_t data)
546: {
547: struct dquot *dq;
548: struct dqblk dqb;
549: int error;
1.3 bouyer 550: uint64_t bval[2];
551: uint64_t ival[2];
552: const char *val_limitsonly_grace[] = {QUOTADICT_LIMIT_GTIME};
553: #define Q1_GTIME 0
554: const char *val_limitsonly_softhard[] =
555: {QUOTADICT_LIMIT_SOFT, QUOTADICT_LIMIT_HARD};
556: #define Q1_SOFT 0
557: #define Q1_HARD 1
558:
559: uint64_t *valuesp[QUOTA_NLIMITS];
560: valuesp[QUOTA_LIMIT_BLOCK] = bval;
561: valuesp[QUOTA_LIMIT_FILE] = ival;
1.2 bouyer 562:
563: if (ump->um_quotas[type] == NULLVP)
564: return ENODEV;
565:
566: if (defaultq) {
567: /* just update grace times */
1.3 bouyer 568: error = proptoquota64(data, valuesp, val_limitsonly_grace, 1,
569: ufs_quota_limit_names, QUOTA_NLIMITS);
570: if (error)
571: return error;
1.2 bouyer 572: if ((error = dqget(NULLVP, id, ump, type, &dq)) != 0)
573: return error;
574: mutex_enter(&dq->dq_interlock);
1.3 bouyer 575: if (bval[Q1_GTIME] > 0)
1.2 bouyer 576: ump->umq1_btime[type] = dq->dq_btime =
1.3 bouyer 577: bval[Q1_GTIME];
578: if (ival[Q1_GTIME] > 0)
1.2 bouyer 579: ump->umq1_itime[type] = dq->dq_itime =
1.3 bouyer 580: ival[Q1_GTIME];
1.2 bouyer 581: mutex_exit(&dq->dq_interlock);
582: dq->dq_flags |= DQ_MOD;
583: dqrele(NULLVP, dq);
584: return 0;
585: }
1.3 bouyer 586: error = proptoquota64(data, valuesp, val_limitsonly_softhard, 2,
587: ufs_quota_limit_names, QUOTA_NLIMITS);
588: if (error)
589: return error;
1.2 bouyer 590:
591: if ((error = dqget(NULLVP, id, ump, type, &dq)) != 0)
592: return (error);
593: mutex_enter(&dq->dq_interlock);
594: /*
595: * Copy all but the current values.
596: * Reset time limit if previously had no soft limit or were
597: * under it, but now have a soft limit and are over it.
598: */
599: dqb.dqb_curblocks = dq->dq_curblocks;
600: dqb.dqb_curinodes = dq->dq_curinodes;
1.3 bouyer 601: dqb.dqb_btime = dq->dq_btime;
602: dqb.dqb_itime = dq->dq_itime;
603: dqb.dqb_bsoftlimit = (bval[Q1_SOFT] == UQUAD_MAX) ? 0 : bval[Q1_SOFT];
604: dqb.dqb_bhardlimit = (bval[Q1_HARD] == UQUAD_MAX) ? 0 : bval[Q1_HARD];
605: dqb.dqb_isoftlimit = (ival[Q1_SOFT] == UQUAD_MAX) ? 0 : ival[Q1_SOFT];
606: dqb.dqb_ihardlimit = (ival[Q1_HARD] == UQUAD_MAX) ? 0 : ival[Q1_HARD];
607: if (dq->dq_id == 0) {
608: /* also update grace time if available */
609: if (proptoquota64(data, valuesp, val_limitsonly_grace, 1,
610: ufs_quota_limit_names, QUOTA_NLIMITS) == 0) {
611: if (bval[Q1_GTIME] > 0)
612: ump->umq1_btime[type] = dqb.dqb_btime =
613: bval[Q1_GTIME];
614: if (ival[Q1_GTIME] > 0)
615: ump->umq1_itime[type] = dqb.dqb_itime =
616: ival[Q1_GTIME];
617: }
1.2 bouyer 618: }
619: if (dqb.dqb_bsoftlimit &&
620: dq->dq_curblocks >= dqb.dqb_bsoftlimit &&
621: (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
622: dqb.dqb_btime = time_second + ump->umq1_btime[type];
623: if (dqb.dqb_isoftlimit &&
624: dq->dq_curinodes >= dqb.dqb_isoftlimit &&
625: (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
626: dqb.dqb_itime = time_second + ump->umq1_itime[type];
627: dq->dq_un.dq1_dqb = dqb;
628: if (dq->dq_curblocks < dq->dq_bsoftlimit)
629: dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
630: if (dq->dq_curinodes < dq->dq_isoftlimit)
631: dq->dq_flags &= ~DQ_WARN(QL_FILE);
632: if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
633: dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
634: dq->dq_flags |= DQ_FAKE;
635: else
636: dq->dq_flags &= ~DQ_FAKE;
637: dq->dq_flags |= DQ_MOD;
638: mutex_exit(&dq->dq_interlock);
639: dqrele(NULLVP, dq);
640: return (0);
641: }
642:
643:
644: #if 0
645: /*
646: * Q_SETQUOTA - assign an entire dqblk structure.
647: */
648: int
649: setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
650: {
651: struct dquot *dq;
652: struct dquot *ndq;
653: struct ufsmount *ump = VFSTOUFS(mp);
654:
655:
656: if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
657: return (error);
658: dq = ndq;
659: mutex_enter(&dq->dq_interlock);
660: /*
661: * Copy all but the current values.
662: * Reset time limit if previously had no soft limit or were
663: * under it, but now have a soft limit and are over it.
664: */
665: dqb->dqb_curblocks = dq->dq_curblocks;
666: dqb->dqb_curinodes = dq->dq_curinodes;
667: if (dq->dq_id != 0) {
668: dqb->dqb_btime = dq->dq_btime;
669: dqb->dqb_itime = dq->dq_itime;
670: }
671: if (dqb->dqb_bsoftlimit &&
672: dq->dq_curblocks >= dqb->dqb_bsoftlimit &&
673: (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
674: dqb->dqb_btime = time_second + ump->umq1_btime[type];
675: if (dqb->dqb_isoftlimit &&
676: dq->dq_curinodes >= dqb->dqb_isoftlimit &&
677: (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
678: dqb->dqb_itime = time_second + ump->umq1_itime[type];
679: dq->dq_un.dq1_dqb = *dqb;
680: if (dq->dq_curblocks < dq->dq_bsoftlimit)
681: dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
682: if (dq->dq_curinodes < dq->dq_isoftlimit)
683: dq->dq_flags &= ~DQ_WARN(QL_FILE);
684: if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
685: dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
686: dq->dq_flags |= DQ_FAKE;
687: else
688: dq->dq_flags &= ~DQ_FAKE;
689: dq->dq_flags |= DQ_MOD;
690: mutex_exit(&dq->dq_interlock);
691: dqrele(NULLVP, dq);
692: return (0);
693: }
694:
695: /*
696: * Q_SETUSE - set current inode and block usage.
697: */
698: int
699: setuse(struct mount *mp, u_long id, int type, void *addr)
700: {
701: struct dquot *dq;
702: struct ufsmount *ump = VFSTOUFS(mp);
703: struct dquot *ndq;
704: struct dqblk usage;
705: int error;
706:
707: error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
708: if (error)
709: return (error);
710: if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
711: return (error);
712: dq = ndq;
713: mutex_enter(&dq->dq_interlock);
714: /*
715: * Reset time limit if have a soft limit and were
716: * previously under it, but are now over it.
717: */
718: if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
719: usage.dqb_curblocks >= dq->dq_bsoftlimit)
720: dq->dq_btime = time_second + ump->umq1_btime[type];
721: if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
722: usage.dqb_curinodes >= dq->dq_isoftlimit)
723: dq->dq_itime = time_second + ump->umq1_itime[type];
724: dq->dq_curblocks = usage.dqb_curblocks;
725: dq->dq_curinodes = usage.dqb_curinodes;
726: if (dq->dq_curblocks < dq->dq_bsoftlimit)
727: dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
728: if (dq->dq_curinodes < dq->dq_isoftlimit)
729: dq->dq_flags &= ~DQ_WARN(QL_FILE);
730: dq->dq_flags |= DQ_MOD;
731: mutex_exit(&dq->dq_interlock);
732: dqrele(NULLVP, dq);
733: return (0);
734: }
735: #endif
736:
737: /*
738: * Q_SYNC - sync quota files to disk.
739: */
740: int
741: q1sync(struct mount *mp)
742: {
743: struct ufsmount *ump = VFSTOUFS(mp);
744: struct vnode *vp, *mvp;
745: struct dquot *dq;
746: int i, error;
747:
748: /*
749: * Check if the mount point has any quotas.
750: * If not, simply return.
751: */
752: for (i = 0; i < MAXQUOTAS; i++)
753: if (ump->um_quotas[i] != NULLVP)
754: break;
755: if (i == MAXQUOTAS)
756: return (0);
757:
758: /* Allocate a marker vnode. */
1.5 ! hannken 759: mvp = vnalloc(mp);
1.2 bouyer 760:
761: /*
762: * Search vnodes associated with this mount point,
763: * synchronizing any modified dquot structures.
764: */
765: mutex_enter(&mntvnode_lock);
766: again:
767: for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) {
768: vmark(mvp, vp);
1.4 rmind 769: mutex_enter(vp->v_interlock);
1.2 bouyer 770: if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) ||
771: vp->v_type == VNON ||
772: (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) {
1.4 rmind 773: mutex_exit(vp->v_interlock);
1.2 bouyer 774: continue;
775: }
776: mutex_exit(&mntvnode_lock);
777: error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT);
778: if (error) {
779: mutex_enter(&mntvnode_lock);
780: if (error == ENOENT) {
781: (void)vunmark(mvp);
782: goto again;
783: }
784: continue;
785: }
786: for (i = 0; i < MAXQUOTAS; i++) {
787: dq = VTOI(vp)->i_dquot[i];
788: if (dq == NODQUOT)
789: continue;
790: mutex_enter(&dq->dq_interlock);
791: if (dq->dq_flags & DQ_MOD)
792: dq1sync(vp, dq);
793: mutex_exit(&dq->dq_interlock);
794: }
795: vput(vp);
796: mutex_enter(&mntvnode_lock);
797: }
798: mutex_exit(&mntvnode_lock);
799: vnfree(mvp);
800: return (0);
801: }
802:
803: /*
804: * Obtain a dquot structure for the specified identifier and quota file
805: * reading the information from the file if necessary.
806: */
807: int
808: dq1get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type,
809: struct dquot *dq)
810: {
811: struct iovec aiov;
812: struct uio auio;
813: int error;
814:
815: KASSERT(mutex_owned(&dq->dq_interlock));
816: vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
817: auio.uio_iov = &aiov;
818: auio.uio_iovcnt = 1;
819: aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
820: aiov.iov_len = sizeof (struct dqblk);
821: auio.uio_resid = sizeof (struct dqblk);
822: auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
823: auio.uio_rw = UIO_READ;
824: UIO_SETUP_SYSSPACE(&auio);
825: error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
826: if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
827: memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk));
828: VOP_UNLOCK(dqvp);
829: /*
830: * I/O error in reading quota file, release
831: * quota structure and reflect problem to caller.
832: */
833: if (error)
834: return (error);
835: /*
836: * Check for no limit to enforce.
837: * Initialize time values if necessary.
838: */
839: if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
840: dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
841: dq->dq_flags |= DQ_FAKE;
842: if (dq->dq_id != 0) {
843: if (dq->dq_btime == 0)
844: dq->dq_btime = time_second + ump->umq1_btime[type];
845: if (dq->dq_itime == 0)
846: dq->dq_itime = time_second + ump->umq1_itime[type];
847: }
848: return (0);
849: }
850:
851: /*
852: * Update the disk quota in the quota file.
853: */
854: int
855: dq1sync(struct vnode *vp, struct dquot *dq)
856: {
857: struct vnode *dqvp;
858: struct iovec aiov;
859: struct uio auio;
860: int error;
861:
862: if (dq == NODQUOT)
863: panic("dq1sync: dquot");
864: KASSERT(mutex_owned(&dq->dq_interlock));
865: if ((dq->dq_flags & DQ_MOD) == 0)
866: return (0);
867: if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
868: panic("dq1sync: file");
869: KASSERT(dqvp != vp);
870: vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
871: auio.uio_iov = &aiov;
872: auio.uio_iovcnt = 1;
873: aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
874: aiov.iov_len = sizeof (struct dqblk);
875: auio.uio_resid = sizeof (struct dqblk);
876: auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
877: auio.uio_rw = UIO_WRITE;
878: UIO_SETUP_SYSSPACE(&auio);
879: error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
880: if (auio.uio_resid && error == 0)
881: error = EIO;
882: dq->dq_flags &= ~DQ_MOD;
883: VOP_UNLOCK(dqvp);
884: return (error);
885: }
CVSweb <webmaster@jp.NetBSD.org>