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