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