ufs_quota.c revision 1.49 1 /* $NetBSD: ufs_quota.c,v 1.49 2007/07/19 09:33:04 hannken 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. 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>
38 __KERNEL_RCSID(0, "$NetBSD: ufs_quota.c,v 1.49 2007/07/19 09:33:04 hannken Exp $");
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 #include <sys/kauth.h>
50
51 #include <ufs/ufs/quota.h>
52 #include <ufs/ufs/inode.h>
53 #include <ufs/ufs/ufsmount.h>
54 #include <ufs/ufs/ufs_extern.h>
55
56 /*
57 * The following structure records disk usage for a user or group on a
58 * filesystem. There is one allocated for each quota that exists on any
59 * filesystem for the current user or group. A cache is kept of recently
60 * used entries.
61 * Field markings and the corresponding locks:
62 * h: dqhashtbl_mtx
63 * d: dq_interlock
64 *
65 * Lock order is: dq_interlock -> dqhashtbl_mtx
66 */
67 struct dquot {
68 LIST_ENTRY(dquot) dq_hash; /* h: hash list */
69 TAILQ_ENTRY(dquot) dq_freelist; /* free list */
70 u_int16_t dq_flags; /* d: flags, see below */
71 u_int16_t dq_type; /* d: quota type of this dquot */
72 u_int32_t dq_cnt; /* h: count of active references */
73 u_int32_t dq_id; /* d: identifier this applies to */
74 struct ufsmount *dq_ump; /* d: filesystem this is taken from */
75 kmutex_t dq_interlock; /* d: lock this dquot */
76 struct dqblk dq_dqb; /* d: actual usage & quotas */
77 };
78 /*
79 * Flag values.
80 */
81 #define DQ_MOD 0x04 /* this quota modified since read */
82 #define DQ_FAKE 0x08 /* no limits here, just usage */
83 #define DQ_BLKS 0x10 /* has been warned about blk limit */
84 #define DQ_INODS 0x20 /* has been warned about inode limit */
85 /*
86 * Shorthand notation.
87 */
88 #define dq_bhardlimit dq_dqb.dqb_bhardlimit
89 #define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
90 #define dq_curblocks dq_dqb.dqb_curblocks
91 #define dq_ihardlimit dq_dqb.dqb_ihardlimit
92 #define dq_isoftlimit dq_dqb.dqb_isoftlimit
93 #define dq_curinodes dq_dqb.dqb_curinodes
94 #define dq_btime dq_dqb.dqb_btime
95 #define dq_itime dq_dqb.dqb_itime
96 /*
97 * If the system has never checked for a quota for this file, then it is
98 * set to NODQUOT. Once a write attempt is made the inode pointer is set
99 * to reference a dquot structure.
100 */
101 #define NODQUOT NULL
102
103 static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
104 static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
105 static void dqflush(struct vnode *);
106 static int dqget(struct vnode *, u_long, struct ufsmount *, int,
107 struct dquot **);
108 static void dqref(struct dquot *);
109 static void dqrele(struct vnode *, struct dquot *);
110 static int dqsync(struct vnode *, struct dquot *);
111
112 /*
113 * Quota name to error message mapping.
114 */
115 static const char *quotatypes[] = INITQFNAMES;
116
117 /*
118 * Set up the quotas for an inode.
119 *
120 * This routine completely defines the semantics of quotas.
121 * If other criterion want to be used to establish quotas, the
122 * MAXQUOTAS value in quotas.h should be increased, and the
123 * additional dquots set up here.
124 */
125 int
126 getinoquota(struct inode *ip)
127 {
128 struct ufsmount *ump = ip->i_ump;
129 struct vnode *vp = ITOV(ip);
130 int i, error;
131 u_int32_t ino_ids[MAXQUOTAS];
132
133 /*
134 * To avoid deadlocks never update quotas for quota files
135 * on the same file system
136 */
137 for (i = 0; i < MAXQUOTAS; i++)
138 if (ITOV(ip) == ump->um_quotas[i])
139 return 0;
140
141 ino_ids[USRQUOTA] = ip->i_uid;
142 ino_ids[GRPQUOTA] = ip->i_gid;
143 for (i = 0; i < MAXQUOTAS; i++) {
144 /*
145 * If the file id changed the quota needs update.
146 */
147 if (ip->i_dquot[i] != NODQUOT &&
148 ip->i_dquot[i]->dq_id != ino_ids[i]) {
149 dqrele(ITOV(ip), ip->i_dquot[i]);
150 ip->i_dquot[i] = NODQUOT;
151 }
152 /*
153 * Set up the quota based on file id.
154 * EINVAL means that quotas are not enabled.
155 */
156 if (ip->i_dquot[i] == NODQUOT &&
157 (error = dqget(vp, ino_ids[i], ump, i, &ip->i_dquot[i])) &&
158 error != EINVAL)
159 return (error);
160 }
161 return 0;
162 }
163
164 /*
165 * Initialize the quota fields of an inode.
166 */
167 void
168 ufsquota_init(struct inode *ip)
169 {
170 int i;
171
172 for (i = 0; i < MAXQUOTAS; i++)
173 ip->i_dquot[i] = NODQUOT;
174 }
175
176 /*
177 * Release the quota fields from an inode.
178 */
179 void
180 ufsquota_free(struct inode *ip)
181 {
182 int i;
183
184 for (i = 0; i < MAXQUOTAS; i++) {
185 dqrele(ITOV(ip), ip->i_dquot[i]);
186 ip->i_dquot[i] = NODQUOT;
187 }
188 }
189
190 /*
191 * Update disk usage, and take corrective action.
192 */
193 int
194 chkdq(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
195 {
196 struct dquot *dq;
197 int i;
198 int ncurblocks, error;
199
200 if ((error = getinoquota(ip)) != 0)
201 return error;
202 if (change == 0)
203 return (0);
204 if (change < 0) {
205 for (i = 0; i < MAXQUOTAS; i++) {
206 if ((dq = ip->i_dquot[i]) == NODQUOT)
207 continue;
208 mutex_enter(&dq->dq_interlock);
209 ncurblocks = dq->dq_curblocks + change;
210 if (ncurblocks >= 0)
211 dq->dq_curblocks = ncurblocks;
212 else
213 dq->dq_curblocks = 0;
214 dq->dq_flags &= ~DQ_BLKS;
215 dq->dq_flags |= DQ_MOD;
216 mutex_exit(&dq->dq_interlock);
217 }
218 return (0);
219 }
220 if ((flags & FORCE) == 0 &&
221 kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) {
222 for (i = 0; i < MAXQUOTAS; i++) {
223 if ((dq = ip->i_dquot[i]) == NODQUOT)
224 continue;
225 mutex_enter(&dq->dq_interlock);
226 error = chkdqchg(ip, change, cred, i);
227 mutex_exit(&dq->dq_interlock);
228 if (error != 0)
229 return (error);
230 }
231 }
232 for (i = 0; i < MAXQUOTAS; i++) {
233 if ((dq = ip->i_dquot[i]) == NODQUOT)
234 continue;
235 mutex_enter(&dq->dq_interlock);
236 dq->dq_curblocks += change;
237 dq->dq_flags |= DQ_MOD;
238 mutex_exit(&dq->dq_interlock);
239 }
240 return (0);
241 }
242
243 /*
244 * Check for a valid change to a users allocation.
245 * Issue an error message if appropriate.
246 */
247 static int
248 chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
249 {
250 struct dquot *dq = ip->i_dquot[type];
251 long ncurblocks = dq->dq_curblocks + change;
252
253 /*
254 * If user would exceed their hard limit, disallow space allocation.
255 */
256 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
257 if ((dq->dq_flags & DQ_BLKS) == 0 &&
258 ip->i_uid == kauth_cred_geteuid(cred)) {
259 uprintf("\n%s: write failed, %s disk limit reached\n",
260 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
261 quotatypes[type]);
262 dq->dq_flags |= DQ_BLKS;
263 }
264 return (EDQUOT);
265 }
266 /*
267 * If user is over their soft limit for too long, disallow space
268 * allocation. Reset time limit as they cross their soft limit.
269 */
270 if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
271 if (dq->dq_curblocks < dq->dq_bsoftlimit) {
272 dq->dq_btime = time_second + ip->i_ump->um_btime[type];
273 if (ip->i_uid == kauth_cred_geteuid(cred))
274 uprintf("\n%s: warning, %s %s\n",
275 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
276 quotatypes[type], "disk quota exceeded");
277 return (0);
278 }
279 if (time_second > dq->dq_btime) {
280 if ((dq->dq_flags & DQ_BLKS) == 0 &&
281 ip->i_uid == kauth_cred_geteuid(cred)) {
282 uprintf("\n%s: write failed, %s %s\n",
283 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
284 quotatypes[type],
285 "disk quota exceeded for too long");
286 dq->dq_flags |= DQ_BLKS;
287 }
288 return (EDQUOT);
289 }
290 }
291 return (0);
292 }
293
294 /*
295 * Check the inode limit, applying corrective action.
296 */
297 int
298 chkiq(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
299 {
300 struct dquot *dq;
301 int i;
302 int ncurinodes, error;
303
304 if ((error = getinoquota(ip)) != 0)
305 return error;
306 if (change == 0)
307 return (0);
308 if (change < 0) {
309 for (i = 0; i < MAXQUOTAS; i++) {
310 if ((dq = ip->i_dquot[i]) == NODQUOT)
311 continue;
312 mutex_enter(&dq->dq_interlock);
313 ncurinodes = dq->dq_curinodes + change;
314 if (ncurinodes >= 0)
315 dq->dq_curinodes = ncurinodes;
316 else
317 dq->dq_curinodes = 0;
318 dq->dq_flags &= ~DQ_INODS;
319 dq->dq_flags |= DQ_MOD;
320 mutex_exit(&dq->dq_interlock);
321 }
322 return (0);
323 }
324 if ((flags & FORCE) == 0 && kauth_authorize_generic(cred,
325 KAUTH_GENERIC_ISSUSER, NULL) != 0) {
326 for (i = 0; i < MAXQUOTAS; i++) {
327 if ((dq = ip->i_dquot[i]) == NODQUOT)
328 continue;
329 mutex_enter(&dq->dq_interlock);
330 error = chkiqchg(ip, change, cred, i);
331 mutex_exit(&dq->dq_interlock);
332 if (error != 0)
333 return (error);
334 }
335 }
336 for (i = 0; i < MAXQUOTAS; i++) {
337 if ((dq = ip->i_dquot[i]) == NODQUOT)
338 continue;
339 mutex_enter(&dq->dq_interlock);
340 dq->dq_curinodes += change;
341 dq->dq_flags |= DQ_MOD;
342 mutex_exit(&dq->dq_interlock);
343 }
344 return (0);
345 }
346
347 /*
348 * Check for a valid change to a users allocation.
349 * Issue an error message if appropriate.
350 */
351 static int
352 chkiqchg(struct inode *ip, int32_t change, kauth_cred_t cred, int type)
353 {
354 struct dquot *dq = ip->i_dquot[type];
355 long ncurinodes = dq->dq_curinodes + change;
356
357 /*
358 * If user would exceed their hard limit, disallow inode allocation.
359 */
360 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
361 if ((dq->dq_flags & DQ_INODS) == 0 &&
362 ip->i_uid == kauth_cred_geteuid(cred)) {
363 uprintf("\n%s: write failed, %s inode limit reached\n",
364 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
365 quotatypes[type]);
366 dq->dq_flags |= DQ_INODS;
367 }
368 return (EDQUOT);
369 }
370 /*
371 * If user is over their soft limit for too long, disallow inode
372 * allocation. Reset time limit as they cross their soft limit.
373 */
374 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
375 if (dq->dq_curinodes < dq->dq_isoftlimit) {
376 dq->dq_itime = time_second + ip->i_ump->um_itime[type];
377 if (ip->i_uid == kauth_cred_geteuid(cred))
378 uprintf("\n%s: warning, %s %s\n",
379 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
380 quotatypes[type], "inode quota exceeded");
381 return (0);
382 }
383 if (time_second > dq->dq_itime) {
384 if ((dq->dq_flags & DQ_INODS) == 0 &&
385 ip->i_uid == kauth_cred_geteuid(cred)) {
386 uprintf("\n%s: write failed, %s %s\n",
387 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
388 quotatypes[type],
389 "inode quota exceeded for too long");
390 dq->dq_flags |= DQ_INODS;
391 }
392 return (EDQUOT);
393 }
394 }
395 return (0);
396 }
397
398 /*
399 * Code to process quotactl commands.
400 */
401
402 /*
403 * Q_QUOTAON - set up a quota file for a particular file system.
404 */
405 int
406 quotaon(struct lwp *l, struct mount *mp, int type, void *fname)
407 {
408 struct ufsmount *ump = VFSTOUFS(mp);
409 struct vnode *vp, **vpp;
410 struct vnode *nextvp;
411 struct dquot *dq;
412 int error;
413 struct nameidata nd;
414
415 vpp = &ump->um_quotas[type];
416 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, l);
417 if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0)
418 return (error);
419 vp = nd.ni_vp;
420 VOP_UNLOCK(vp, 0);
421 if (vp->v_type != VREG) {
422 (void) vn_close(vp, FREAD|FWRITE, l->l_cred, l);
423 return (EACCES);
424 }
425 if (*vpp != vp)
426 quotaoff(l, mp, type);
427 ump->um_qflags[type] |= QTF_OPENING;
428 mp->mnt_flag |= MNT_QUOTA;
429 vp->v_flag |= VSYSTEM;
430 *vpp = vp;
431 /*
432 * Save the credential of the process that turned on quotas.
433 * Set up the time limits for this quota.
434 */
435 kauth_cred_hold(l->l_cred);
436 ump->um_cred[type] = l->l_cred;
437 ump->um_btime[type] = MAX_DQ_TIME;
438 ump->um_itime[type] = MAX_IQ_TIME;
439 if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
440 if (dq->dq_btime > 0)
441 ump->um_btime[type] = dq->dq_btime;
442 if (dq->dq_itime > 0)
443 ump->um_itime[type] = dq->dq_itime;
444 dqrele(NULLVP, dq);
445 }
446 /*
447 * Search vnodes associated with this mount point,
448 * adding references to quota file being opened.
449 * NB: only need to add dquot's for inodes being modified.
450 */
451 again:
452 TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
453 nextvp = TAILQ_NEXT(vp, v_mntvnodes);
454 if (vp->v_mount != mp)
455 goto again;
456 if (vp->v_type == VNON ||vp->v_writecount == 0)
457 continue;
458 if (vget(vp, LK_EXCLUSIVE))
459 goto again;
460 if ((error = getinoquota(VTOI(vp))) != 0) {
461 vput(vp);
462 break;
463 }
464 vput(vp);
465 /* if the list changed, start again */
466 if (TAILQ_NEXT(vp, v_mntvnodes) != nextvp)
467 goto again;
468 }
469 ump->um_qflags[type] &= ~QTF_OPENING;
470 if (error)
471 quotaoff(l, mp, type);
472 return (error);
473 }
474
475 /*
476 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
477 */
478 int
479 quotaoff(struct lwp *l, struct mount *mp, int type)
480 {
481 struct vnode *vp;
482 struct vnode *qvp, *nextvp;
483 struct ufsmount *ump = VFSTOUFS(mp);
484 struct dquot *dq;
485 struct inode *ip;
486 int error;
487
488 if ((qvp = ump->um_quotas[type]) == NULLVP)
489 return (0);
490 ump->um_qflags[type] |= QTF_CLOSING;
491 /*
492 * Search vnodes associated with this mount point,
493 * deleting any references to quota file being closed.
494 */
495 again:
496 TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
497 nextvp = TAILQ_NEXT(vp, v_mntvnodes);
498 if (vp->v_mount != mp)
499 goto again;
500 if (vp->v_type == VNON)
501 continue;
502 if (vget(vp, LK_EXCLUSIVE))
503 goto again;
504 ip = VTOI(vp);
505 dq = ip->i_dquot[type];
506 ip->i_dquot[type] = NODQUOT;
507 dqrele(vp, dq);
508 vput(vp);
509 /* if the list changed, start again */
510 if (TAILQ_NEXT(vp, v_mntvnodes) != nextvp)
511 goto again;
512 }
513 dqflush(qvp);
514 qvp->v_flag &= ~VSYSTEM;
515 error = vn_close(qvp, FREAD|FWRITE, l->l_cred, l);
516 ump->um_quotas[type] = NULLVP;
517 kauth_cred_free(ump->um_cred[type]);
518 ump->um_cred[type] = NOCRED;
519 ump->um_qflags[type] &= ~QTF_CLOSING;
520 for (type = 0; type < MAXQUOTAS; type++)
521 if (ump->um_quotas[type] != NULLVP)
522 break;
523 if (type == MAXQUOTAS)
524 mp->mnt_flag &= ~MNT_QUOTA;
525 return (error);
526 }
527
528 /*
529 * Q_GETQUOTA - return current values in a dqblk structure.
530 */
531 int
532 getquota(struct mount *mp, u_long id, int type, void *addr)
533 {
534 struct dquot *dq;
535 int error;
536
537 if ((error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) != 0)
538 return (error);
539 error = copyout((void *)&dq->dq_dqb, addr, sizeof (struct dqblk));
540 dqrele(NULLVP, dq);
541 return (error);
542 }
543
544 /*
545 * Q_SETQUOTA - assign an entire dqblk structure.
546 */
547 int
548 setquota(struct mount *mp, u_long id, int type, void *addr)
549 {
550 struct dquot *dq;
551 struct dquot *ndq;
552 struct ufsmount *ump = VFSTOUFS(mp);
553 struct dqblk newlim;
554 int error;
555
556 error = copyin(addr, (void *)&newlim, sizeof (struct dqblk));
557 if (error)
558 return (error);
559 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
560 return (error);
561 dq = ndq;
562 mutex_enter(&dq->dq_interlock);
563 /*
564 * Copy all but the current values.
565 * Reset time limit if previously had no soft limit or were
566 * under it, but now have a soft limit and are over it.
567 */
568 newlim.dqb_curblocks = dq->dq_curblocks;
569 newlim.dqb_curinodes = dq->dq_curinodes;
570 if (dq->dq_id != 0) {
571 newlim.dqb_btime = dq->dq_btime;
572 newlim.dqb_itime = dq->dq_itime;
573 }
574 if (newlim.dqb_bsoftlimit &&
575 dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
576 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
577 newlim.dqb_btime = time_second + ump->um_btime[type];
578 if (newlim.dqb_isoftlimit &&
579 dq->dq_curinodes >= newlim.dqb_isoftlimit &&
580 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
581 newlim.dqb_itime = time_second + ump->um_itime[type];
582 dq->dq_dqb = newlim;
583 if (dq->dq_curblocks < dq->dq_bsoftlimit)
584 dq->dq_flags &= ~DQ_BLKS;
585 if (dq->dq_curinodes < dq->dq_isoftlimit)
586 dq->dq_flags &= ~DQ_INODS;
587 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
588 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
589 dq->dq_flags |= DQ_FAKE;
590 else
591 dq->dq_flags &= ~DQ_FAKE;
592 dq->dq_flags |= DQ_MOD;
593 mutex_exit(&dq->dq_interlock);
594 dqrele(NULLVP, dq);
595 return (0);
596 }
597
598 /*
599 * Q_SETUSE - set current inode and block usage.
600 */
601 int
602 setuse(struct mount *mp, u_long id, int type, void *addr)
603 {
604 struct dquot *dq;
605 struct ufsmount *ump = VFSTOUFS(mp);
606 struct dquot *ndq;
607 struct dqblk usage;
608 int error;
609
610 error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
611 if (error)
612 return (error);
613 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
614 return (error);
615 dq = ndq;
616 mutex_enter(&dq->dq_interlock);
617 /*
618 * Reset time limit if have a soft limit and were
619 * previously under it, but are now over it.
620 */
621 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
622 usage.dqb_curblocks >= dq->dq_bsoftlimit)
623 dq->dq_btime = time_second + ump->um_btime[type];
624 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
625 usage.dqb_curinodes >= dq->dq_isoftlimit)
626 dq->dq_itime = time_second + ump->um_itime[type];
627 dq->dq_curblocks = usage.dqb_curblocks;
628 dq->dq_curinodes = usage.dqb_curinodes;
629 if (dq->dq_curblocks < dq->dq_bsoftlimit)
630 dq->dq_flags &= ~DQ_BLKS;
631 if (dq->dq_curinodes < dq->dq_isoftlimit)
632 dq->dq_flags &= ~DQ_INODS;
633 dq->dq_flags |= DQ_MOD;
634 mutex_exit(&dq->dq_interlock);
635 dqrele(NULLVP, dq);
636 return (0);
637 }
638
639 /*
640 * Q_SYNC - sync quota files to disk.
641 */
642 int
643 qsync(struct mount *mp)
644 {
645 struct ufsmount *ump = VFSTOUFS(mp);
646 struct vnode *vp, *nextvp;
647 struct dquot *dq;
648 int i, error;
649
650 /*
651 * Check if the mount point has any quotas.
652 * If not, simply return.
653 */
654 for (i = 0; i < MAXQUOTAS; i++)
655 if (ump->um_quotas[i] != NULLVP)
656 break;
657 if (i == MAXQUOTAS)
658 return (0);
659 /*
660 * Search vnodes associated with this mount point,
661 * synchronizing any modified dquot structures.
662 */
663 simple_lock(&mntvnode_slock);
664 again:
665 TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
666 nextvp = TAILQ_NEXT(vp, v_mntvnodes);
667 if (vp->v_mount != mp)
668 goto again;
669 if (vp->v_type == VNON)
670 continue;
671 simple_lock(&vp->v_interlock);
672 simple_unlock(&mntvnode_slock);
673 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
674 if (error) {
675 simple_lock(&mntvnode_slock);
676 if (error == ENOENT)
677 goto again;
678 continue;
679 }
680 for (i = 0; i < MAXQUOTAS; i++) {
681 dq = VTOI(vp)->i_dquot[i];
682 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
683 dqsync(vp, dq);
684 }
685 vput(vp);
686 simple_lock(&mntvnode_slock);
687 /* if the list changed, start again */
688 if (TAILQ_NEXT(vp, v_mntvnodes) != nextvp)
689 goto again;
690 }
691 simple_unlock(&mntvnode_slock);
692 return (0);
693 }
694
695 /*
696 * Code pertaining to management of the in-core dquot data structures.
697 */
698 #define DQHASH(dqvp, id) \
699 (((((long)(dqvp)) >> 8) + id) & dqhash)
700 static LIST_HEAD(dqhashhead, dquot) *dqhashtbl;
701 static u_long dqhash;
702 static kmutex_t dqhashtbl_mtx;
703
704 /*
705 * Dquot free list.
706 */
707 #define DQUOTINC 5 /* minimum free dquots desired */
708 static TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
709 static long numdquot, desireddquot = DQUOTINC;
710
711 MALLOC_JUSTDEFINE(M_DQUOT, "UFS quota", "UFS quota entries");
712
713 /*
714 * Initialize the quota system.
715 */
716 void
717 dqinit(void)
718 {
719
720 mutex_init(&dqhashtbl_mtx, MUTEX_DEFAULT, IPL_NONE);
721 malloc_type_attach(M_DQUOT);
722 dqhashtbl =
723 hashinit(desiredvnodes, HASH_LIST, M_DQUOT, M_WAITOK, &dqhash);
724 TAILQ_INIT(&dqfreelist);
725 }
726
727 void
728 dqreinit(void)
729 {
730 struct dquot *dq;
731 struct dqhashhead *oldhash, *hash;
732 struct vnode *dqvp;
733 u_long oldmask, mask, hashval;
734 int i;
735
736 hash = hashinit(desiredvnodes, HASH_LIST, M_DQUOT, M_WAITOK, &mask);
737 oldhash = dqhashtbl;
738 oldmask = dqhash;
739 dqhashtbl = hash;
740 dqhash = mask;
741 for (i = 0; i <= oldmask; i++) {
742 while ((dq = LIST_FIRST(&oldhash[i])) != NULL) {
743 dqvp = dq->dq_ump->um_quotas[dq->dq_type];
744 LIST_REMOVE(dq, dq_hash);
745 hashval = DQHASH(dqvp, dq->dq_id);
746 LIST_INSERT_HEAD(&dqhashtbl[hashval], dq, dq_hash);
747 }
748 }
749 hashdone(oldhash, M_DQUOT);
750 }
751
752 /*
753 * Free resources held by quota system.
754 */
755 void
756 dqdone(void)
757 {
758
759 hashdone(dqhashtbl, M_DQUOT);
760 malloc_type_detach(M_DQUOT);
761 mutex_destroy(&dqhashtbl_mtx);
762 }
763
764 /*
765 * Obtain a dquot structure for the specified identifier and quota file
766 * reading the information from the file if necessary.
767 */
768 static int
769 dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
770 struct dquot **dqp)
771 {
772 struct dquot *dq;
773 struct dqhashhead *dqh;
774 struct vnode *dqvp;
775 struct iovec aiov;
776 struct uio auio;
777 int error;
778
779 dqvp = ump->um_quotas[type];
780 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
781 *dqp = NODQUOT;
782 return (EINVAL);
783 }
784 /*
785 * Check the cache first.
786 */
787 mutex_enter(&dqhashtbl_mtx);
788 dqh = &dqhashtbl[DQHASH(dqvp, id)];
789 LIST_FOREACH(dq, dqh, dq_hash) {
790 if (dq->dq_id != id ||
791 dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
792 continue;
793 /*
794 * Cache hit with no references. Take
795 * the structure off the free list.
796 */
797 if (dq->dq_cnt == 0)
798 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
799 dqref(dq);
800 mutex_exit(&dqhashtbl_mtx);
801 *dqp = dq;
802 return (0);
803 }
804 /*
805 * Not in cache, allocate a new one.
806 */
807 if (dqfreelist.tqh_first == NODQUOT &&
808 numdquot < MAXQUOTAS * desiredvnodes)
809 desireddquot += DQUOTINC;
810 if (numdquot < desireddquot) {
811 mutex_exit(&dqhashtbl_mtx);
812 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
813 memset((char *)dq, 0, sizeof *dq);
814 mutex_init(&dq->dq_interlock, MUTEX_DEFAULT, IPL_NONE);
815 numdquot++;
816 } else {
817 if ((dq = dqfreelist.tqh_first) == NULL) {
818 mutex_exit(&dqhashtbl_mtx);
819 tablefull("dquot",
820 "increase kern.maxvnodes or NVNODE");
821 *dqp = NODQUOT;
822 return (EUSERS);
823 }
824 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
825 panic("free dquot isn't");
826 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
827 LIST_REMOVE(dq, dq_hash);
828 mutex_exit(&dqhashtbl_mtx);
829 }
830 /*
831 * Initialize the contents of the dquot structure.
832 */
833 if (vp != dqvp)
834 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
835 mutex_enter(&dq->dq_interlock);
836 mutex_enter(&dqhashtbl_mtx);
837 dqh = &dqhashtbl[DQHASH(dqvp, id)];
838 LIST_INSERT_HEAD(dqh, dq, dq_hash);
839 dqref(dq);
840 mutex_exit(&dqhashtbl_mtx);
841 dq->dq_flags = 0;
842 dq->dq_id = id;
843 dq->dq_ump = ump;
844 dq->dq_type = type;
845 auio.uio_iov = &aiov;
846 auio.uio_iovcnt = 1;
847 aiov.iov_base = (void *)&dq->dq_dqb;
848 aiov.iov_len = sizeof (struct dqblk);
849 auio.uio_resid = sizeof (struct dqblk);
850 auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
851 auio.uio_rw = UIO_READ;
852 UIO_SETUP_SYSSPACE(&auio);
853 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
854 if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
855 memset((void *)&dq->dq_dqb, 0, sizeof(struct dqblk));
856 if (vp != dqvp)
857 VOP_UNLOCK(dqvp, 0);
858 /*
859 * I/O error in reading quota file, release
860 * quota structure and reflect problem to caller.
861 */
862 if (error) {
863 mutex_enter(&dqhashtbl_mtx);
864 LIST_REMOVE(dq, dq_hash);
865 mutex_exit(&dqhashtbl_mtx);
866 mutex_exit(&dq->dq_interlock);
867 dqrele(vp, dq);
868 *dqp = NODQUOT;
869 return (error);
870 }
871 /*
872 * Check for no limit to enforce.
873 * Initialize time values if necessary.
874 */
875 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
876 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
877 dq->dq_flags |= DQ_FAKE;
878 if (dq->dq_id != 0) {
879 if (dq->dq_btime == 0)
880 dq->dq_btime = time_second + ump->um_btime[type];
881 if (dq->dq_itime == 0)
882 dq->dq_itime = time_second + ump->um_itime[type];
883 }
884 mutex_exit(&dq->dq_interlock);
885 *dqp = dq;
886 return (0);
887 }
888
889 /*
890 * Obtain a reference to a dquot.
891 */
892 static void
893 dqref(struct dquot *dq)
894 {
895
896 dq->dq_cnt++;
897 KASSERT(dq->dq_cnt > 0);
898 }
899
900 /*
901 * Release a reference to a dquot.
902 */
903 static void
904 dqrele(struct vnode *vp, struct dquot *dq)
905 {
906
907 if (dq == NODQUOT)
908 return;
909 if (dq->dq_cnt > 1) {
910 dq->dq_cnt--;
911 return;
912 }
913 if (dq->dq_flags & DQ_MOD)
914 (void) dqsync(vp, dq);
915 if (--dq->dq_cnt > 0)
916 return;
917 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
918 }
919
920 /*
921 * Update the disk quota in the quota file.
922 */
923 static int
924 dqsync(struct vnode *vp, struct dquot *dq)
925 {
926 struct vnode *dqvp;
927 struct iovec aiov;
928 struct uio auio;
929 int error;
930
931 if (dq == NODQUOT)
932 panic("dqsync: dquot");
933 if ((dq->dq_flags & DQ_MOD) == 0)
934 return (0);
935 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
936 panic("dqsync: file");
937 if (vp != dqvp)
938 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
939 mutex_enter(&dq->dq_interlock);
940 if ((dq->dq_flags & DQ_MOD) == 0) {
941 mutex_exit(&dq->dq_interlock);
942 if (vp != dqvp)
943 VOP_UNLOCK(dqvp, 0);
944 return (0);
945 }
946 auio.uio_iov = &aiov;
947 auio.uio_iovcnt = 1;
948 aiov.iov_base = (void *)&dq->dq_dqb;
949 aiov.iov_len = sizeof (struct dqblk);
950 auio.uio_resid = sizeof (struct dqblk);
951 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
952 auio.uio_rw = UIO_WRITE;
953 UIO_SETUP_SYSSPACE(&auio);
954 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
955 if (auio.uio_resid && error == 0)
956 error = EIO;
957 dq->dq_flags &= ~DQ_MOD;
958 mutex_exit(&dq->dq_interlock);
959 if (vp != dqvp)
960 VOP_UNLOCK(dqvp, 0);
961 return (error);
962 }
963
964 /*
965 * Flush all entries from the cache for a particular vnode.
966 */
967 static void
968 dqflush(struct vnode *vp)
969 {
970 struct dquot *dq, *nextdq;
971 struct dqhashhead *dqh;
972
973 /*
974 * Move all dquot's that used to refer to this quota
975 * file off their hash chains (they will eventually
976 * fall off the head of the free list and be re-used).
977 */
978 mutex_enter(&dqhashtbl_mtx);
979 for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
980 for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
981 nextdq = LIST_NEXT(dq, dq_hash);
982 if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
983 continue;
984 if (dq->dq_cnt)
985 panic("dqflush: stray dquot");
986 LIST_REMOVE(dq, dq_hash);
987 dq->dq_ump = NULL;
988 }
989 }
990 mutex_exit(&dqhashtbl_mtx);
991 }
992