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