Annotation of src/sys/ufs/ufs/ufs_quota1.c, Revision 1.9
1.9 ! dholland 1: /* $NetBSD: ufs_quota1.c,v 1.8 2012/01/29 06:38:24 dholland 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.9 ! dholland 38: __KERNEL_RCSID(0, "$NetBSD: ufs_quota1.c,v 1.8 2012/01/29 06:38:24 dholland 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
1.8 dholland 496: quota1_handle_cmd_get(struct ufsmount *ump, int idtype, int id,
1.9 ! dholland 497: int defaultq, int objtype, struct quotaval *ret)
1.2 bouyer 498: {
499: struct dquot *dq;
500: int error;
1.9 ! dholland 501: struct quotaval blocks, files;
1.2 bouyer 502:
1.8 dholland 503: if (ump->um_quotas[idtype] == NULLVP)
1.2 bouyer 504: return ENODEV;
505:
506: if (defaultq) { /* we want the grace period of id 0 */
1.8 dholland 507: if ((error = dqget(NULLVP, 0, ump, idtype, &dq)) != 0)
1.2 bouyer 508: return error;
1.3 bouyer 509:
1.2 bouyer 510: } else {
1.8 dholland 511: if ((error = dqget(NULLVP, id, ump, idtype, &dq)) != 0)
1.2 bouyer 512: return error;
513: }
1.9 ! dholland 514: dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files);
1.2 bouyer 515: dqrele(NULLVP, dq);
516: if (defaultq) {
1.9 ! dholland 517: if (blocks.qv_expiretime > 0)
! 518: blocks.qv_grace = blocks.qv_expiretime;
1.2 bouyer 519: else
1.9 ! dholland 520: blocks.qv_grace = MAX_DQ_TIME;
! 521: if (files.qv_expiretime > 0)
! 522: files.qv_grace = files.qv_expiretime;
1.2 bouyer 523: else
1.9 ! dholland 524: files.qv_grace = MAX_DQ_TIME;
1.2 bouyer 525: }
1.9 ! dholland 526:
! 527: switch (objtype) {
! 528: case QUOTA_OBJTYPE_BLOCKS:
! 529: *ret = blocks;
! 530: break;
! 531: case QUOTA_OBJTYPE_FILES:
! 532: *ret = files;
! 533: break;
! 534: default:
! 535: return EINVAL;
! 536: }
! 537:
1.2 bouyer 538: return 0;
539: }
540:
541: int
542: quota1_handle_cmd_set(struct ufsmount *ump, int type, int id,
543: int defaultq, prop_dictionary_t data)
544: {
545: struct dquot *dq;
546: struct dqblk dqb;
547: int error;
1.3 bouyer 548: uint64_t bval[2];
549: uint64_t ival[2];
550: const char *val_limitsonly_grace[] = {QUOTADICT_LIMIT_GTIME};
551: #define Q1_GTIME 0
552: const char *val_limitsonly_softhard[] =
553: {QUOTADICT_LIMIT_SOFT, QUOTADICT_LIMIT_HARD};
554: #define Q1_SOFT 0
555: #define Q1_HARD 1
556:
557: uint64_t *valuesp[QUOTA_NLIMITS];
558: valuesp[QUOTA_LIMIT_BLOCK] = bval;
559: valuesp[QUOTA_LIMIT_FILE] = ival;
1.2 bouyer 560:
561: if (ump->um_quotas[type] == NULLVP)
562: return ENODEV;
563:
564: if (defaultq) {
565: /* just update grace times */
1.3 bouyer 566: error = proptoquota64(data, valuesp, val_limitsonly_grace, 1,
567: ufs_quota_limit_names, QUOTA_NLIMITS);
568: if (error)
569: return error;
1.2 bouyer 570: if ((error = dqget(NULLVP, id, ump, type, &dq)) != 0)
571: return error;
572: mutex_enter(&dq->dq_interlock);
1.3 bouyer 573: if (bval[Q1_GTIME] > 0)
1.2 bouyer 574: ump->umq1_btime[type] = dq->dq_btime =
1.3 bouyer 575: bval[Q1_GTIME];
576: if (ival[Q1_GTIME] > 0)
1.2 bouyer 577: ump->umq1_itime[type] = dq->dq_itime =
1.3 bouyer 578: ival[Q1_GTIME];
1.2 bouyer 579: mutex_exit(&dq->dq_interlock);
580: dq->dq_flags |= DQ_MOD;
581: dqrele(NULLVP, dq);
582: return 0;
583: }
1.3 bouyer 584: error = proptoquota64(data, valuesp, val_limitsonly_softhard, 2,
585: ufs_quota_limit_names, QUOTA_NLIMITS);
586: if (error)
587: return error;
1.2 bouyer 588:
589: if ((error = dqget(NULLVP, id, ump, type, &dq)) != 0)
590: return (error);
591: mutex_enter(&dq->dq_interlock);
592: /*
593: * Copy all but the current values.
594: * Reset time limit if previously had no soft limit or were
595: * under it, but now have a soft limit and are over it.
596: */
597: dqb.dqb_curblocks = dq->dq_curblocks;
598: dqb.dqb_curinodes = dq->dq_curinodes;
1.3 bouyer 599: dqb.dqb_btime = dq->dq_btime;
600: dqb.dqb_itime = dq->dq_itime;
601: dqb.dqb_bsoftlimit = (bval[Q1_SOFT] == UQUAD_MAX) ? 0 : bval[Q1_SOFT];
602: dqb.dqb_bhardlimit = (bval[Q1_HARD] == UQUAD_MAX) ? 0 : bval[Q1_HARD];
603: dqb.dqb_isoftlimit = (ival[Q1_SOFT] == UQUAD_MAX) ? 0 : ival[Q1_SOFT];
604: dqb.dqb_ihardlimit = (ival[Q1_HARD] == UQUAD_MAX) ? 0 : ival[Q1_HARD];
605: if (dq->dq_id == 0) {
606: /* also update grace time if available */
607: if (proptoquota64(data, valuesp, val_limitsonly_grace, 1,
608: ufs_quota_limit_names, QUOTA_NLIMITS) == 0) {
609: if (bval[Q1_GTIME] > 0)
610: ump->umq1_btime[type] = dqb.dqb_btime =
611: bval[Q1_GTIME];
612: if (ival[Q1_GTIME] > 0)
613: ump->umq1_itime[type] = dqb.dqb_itime =
614: ival[Q1_GTIME];
615: }
1.2 bouyer 616: }
617: if (dqb.dqb_bsoftlimit &&
618: dq->dq_curblocks >= dqb.dqb_bsoftlimit &&
619: (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
620: dqb.dqb_btime = time_second + ump->umq1_btime[type];
621: if (dqb.dqb_isoftlimit &&
622: dq->dq_curinodes >= dqb.dqb_isoftlimit &&
623: (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
624: dqb.dqb_itime = time_second + ump->umq1_itime[type];
625: dq->dq_un.dq1_dqb = dqb;
626: if (dq->dq_curblocks < dq->dq_bsoftlimit)
627: dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
628: if (dq->dq_curinodes < dq->dq_isoftlimit)
629: dq->dq_flags &= ~DQ_WARN(QL_FILE);
630: if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
631: dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
632: dq->dq_flags |= DQ_FAKE;
633: else
634: dq->dq_flags &= ~DQ_FAKE;
635: dq->dq_flags |= DQ_MOD;
636: mutex_exit(&dq->dq_interlock);
637: dqrele(NULLVP, dq);
638: return (0);
639: }
640:
641:
642: #if 0
643: /*
644: * Q_SETQUOTA - assign an entire dqblk structure.
645: */
646: int
647: setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
648: {
649: struct dquot *dq;
650: struct dquot *ndq;
651: struct ufsmount *ump = VFSTOUFS(mp);
652:
653:
654: if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
655: return (error);
656: dq = ndq;
657: mutex_enter(&dq->dq_interlock);
658: /*
659: * Copy all but the current values.
660: * Reset time limit if previously had no soft limit or were
661: * under it, but now have a soft limit and are over it.
662: */
663: dqb->dqb_curblocks = dq->dq_curblocks;
664: dqb->dqb_curinodes = dq->dq_curinodes;
665: if (dq->dq_id != 0) {
666: dqb->dqb_btime = dq->dq_btime;
667: dqb->dqb_itime = dq->dq_itime;
668: }
669: if (dqb->dqb_bsoftlimit &&
670: dq->dq_curblocks >= dqb->dqb_bsoftlimit &&
671: (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
672: dqb->dqb_btime = time_second + ump->umq1_btime[type];
673: if (dqb->dqb_isoftlimit &&
674: dq->dq_curinodes >= dqb->dqb_isoftlimit &&
675: (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
676: dqb->dqb_itime = time_second + ump->umq1_itime[type];
677: dq->dq_un.dq1_dqb = *dqb;
678: if (dq->dq_curblocks < dq->dq_bsoftlimit)
679: dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
680: if (dq->dq_curinodes < dq->dq_isoftlimit)
681: dq->dq_flags &= ~DQ_WARN(QL_FILE);
682: if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
683: dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
684: dq->dq_flags |= DQ_FAKE;
685: else
686: dq->dq_flags &= ~DQ_FAKE;
687: dq->dq_flags |= DQ_MOD;
688: mutex_exit(&dq->dq_interlock);
689: dqrele(NULLVP, dq);
690: return (0);
691: }
692:
693: /*
694: * Q_SETUSE - set current inode and block usage.
695: */
696: int
697: setuse(struct mount *mp, u_long id, int type, void *addr)
698: {
699: struct dquot *dq;
700: struct ufsmount *ump = VFSTOUFS(mp);
701: struct dquot *ndq;
702: struct dqblk usage;
703: int error;
704:
705: error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
706: if (error)
707: return (error);
708: if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
709: return (error);
710: dq = ndq;
711: mutex_enter(&dq->dq_interlock);
712: /*
713: * Reset time limit if have a soft limit and were
714: * previously under it, but are now over it.
715: */
716: if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
717: usage.dqb_curblocks >= dq->dq_bsoftlimit)
718: dq->dq_btime = time_second + ump->umq1_btime[type];
719: if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
720: usage.dqb_curinodes >= dq->dq_isoftlimit)
721: dq->dq_itime = time_second + ump->umq1_itime[type];
722: dq->dq_curblocks = usage.dqb_curblocks;
723: dq->dq_curinodes = usage.dqb_curinodes;
724: if (dq->dq_curblocks < dq->dq_bsoftlimit)
725: dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
726: if (dq->dq_curinodes < dq->dq_isoftlimit)
727: dq->dq_flags &= ~DQ_WARN(QL_FILE);
728: dq->dq_flags |= DQ_MOD;
729: mutex_exit(&dq->dq_interlock);
730: dqrele(NULLVP, dq);
731: return (0);
732: }
733: #endif
734:
735: /*
736: * Q_SYNC - sync quota files to disk.
737: */
738: int
739: q1sync(struct mount *mp)
740: {
741: struct ufsmount *ump = VFSTOUFS(mp);
742: struct vnode *vp, *mvp;
743: struct dquot *dq;
744: int i, error;
745:
746: /*
747: * Check if the mount point has any quotas.
748: * If not, simply return.
749: */
750: for (i = 0; i < MAXQUOTAS; i++)
751: if (ump->um_quotas[i] != NULLVP)
752: break;
753: if (i == MAXQUOTAS)
754: return (0);
755:
756: /* Allocate a marker vnode. */
1.5 hannken 757: mvp = vnalloc(mp);
1.2 bouyer 758:
759: /*
760: * Search vnodes associated with this mount point,
761: * synchronizing any modified dquot structures.
762: */
763: mutex_enter(&mntvnode_lock);
764: again:
765: for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) {
766: vmark(mvp, vp);
1.4 rmind 767: mutex_enter(vp->v_interlock);
1.2 bouyer 768: if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) ||
769: vp->v_type == VNON ||
770: (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) {
1.4 rmind 771: mutex_exit(vp->v_interlock);
1.2 bouyer 772: continue;
773: }
774: mutex_exit(&mntvnode_lock);
775: error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT);
776: if (error) {
777: mutex_enter(&mntvnode_lock);
778: if (error == ENOENT) {
779: (void)vunmark(mvp);
780: goto again;
781: }
782: continue;
783: }
784: for (i = 0; i < MAXQUOTAS; i++) {
785: dq = VTOI(vp)->i_dquot[i];
786: if (dq == NODQUOT)
787: continue;
788: mutex_enter(&dq->dq_interlock);
789: if (dq->dq_flags & DQ_MOD)
790: dq1sync(vp, dq);
791: mutex_exit(&dq->dq_interlock);
792: }
793: vput(vp);
794: mutex_enter(&mntvnode_lock);
795: }
796: mutex_exit(&mntvnode_lock);
797: vnfree(mvp);
798: return (0);
799: }
800:
801: /*
802: * Obtain a dquot structure for the specified identifier and quota file
803: * reading the information from the file if necessary.
804: */
805: int
806: dq1get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type,
807: struct dquot *dq)
808: {
809: struct iovec aiov;
810: struct uio auio;
811: int error;
812:
813: KASSERT(mutex_owned(&dq->dq_interlock));
814: vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
815: auio.uio_iov = &aiov;
816: auio.uio_iovcnt = 1;
817: aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
818: aiov.iov_len = sizeof (struct dqblk);
819: auio.uio_resid = sizeof (struct dqblk);
820: auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
821: auio.uio_rw = UIO_READ;
822: UIO_SETUP_SYSSPACE(&auio);
823: error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
824: if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
825: memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk));
826: VOP_UNLOCK(dqvp);
827: /*
828: * I/O error in reading quota file, release
829: * quota structure and reflect problem to caller.
830: */
831: if (error)
832: return (error);
833: /*
834: * Check for no limit to enforce.
835: * Initialize time values if necessary.
836: */
837: if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
838: dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
839: dq->dq_flags |= DQ_FAKE;
840: if (dq->dq_id != 0) {
841: if (dq->dq_btime == 0)
842: dq->dq_btime = time_second + ump->umq1_btime[type];
843: if (dq->dq_itime == 0)
844: dq->dq_itime = time_second + ump->umq1_itime[type];
845: }
846: return (0);
847: }
848:
849: /*
850: * Update the disk quota in the quota file.
851: */
852: int
853: dq1sync(struct vnode *vp, struct dquot *dq)
854: {
855: struct vnode *dqvp;
856: struct iovec aiov;
857: struct uio auio;
858: int error;
859:
860: if (dq == NODQUOT)
861: panic("dq1sync: dquot");
862: KASSERT(mutex_owned(&dq->dq_interlock));
863: if ((dq->dq_flags & DQ_MOD) == 0)
864: return (0);
865: if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
866: panic("dq1sync: file");
867: KASSERT(dqvp != vp);
868: vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
869: auio.uio_iov = &aiov;
870: auio.uio_iovcnt = 1;
871: aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
872: aiov.iov_len = sizeof (struct dqblk);
873: auio.uio_resid = sizeof (struct dqblk);
874: auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
875: auio.uio_rw = UIO_WRITE;
876: UIO_SETUP_SYSSPACE(&auio);
877: error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
878: if (auio.uio_resid && error == 0)
879: error = EIO;
880: dq->dq_flags &= ~DQ_MOD;
881: VOP_UNLOCK(dqvp);
882: return (error);
883: }
CVSweb <webmaster@jp.NetBSD.org>