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