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