]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/ufs/ufs/ufs_quota.c
Instead of asserting the vnode lock before manipulating v_vflag, acquire
[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         ASSERT_VOP_LOCKED(qvp, "quotaoff");
540         qvp->v_vflag &= ~VV_SYSTEM;
541         error = vn_close(qvp, FREAD|FWRITE, td->td_ucred, td);
542         ump->um_quotas[type] = NULLVP;
543         crfree(ump->um_cred[type]);
544         ump->um_cred[type] = NOCRED;
545         ump->um_qflags[type] &= ~QTF_CLOSING;
546         for (type = 0; type < MAXQUOTAS; type++)
547                 if (ump->um_quotas[type] != NULLVP)
548                         break;
549         if (type == MAXQUOTAS)
550                 mp->mnt_flag &= ~MNT_QUOTA;
551         return (error);
552 }
553
554 /*
555  * Q_GETQUOTA - return current values in a dqblk structure.
556  */
557 int
558 getquota(td, mp, id, type, addr)
559         struct thread *td;
560         struct mount *mp;
561         u_long id;
562         int type;
563         void *addr;
564 {
565         struct dquot *dq;
566         int error;
567
568         switch (type) {
569         case USRQUOTA:
570                 if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) {
571                         error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL);
572                         if (error)
573                                 return (error);
574                 }
575                 break;  
576
577         case GRPQUOTA:
578                 if (!groupmember(id, td->td_ucred) && !unprivileged_get_quota) {
579                         error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL);
580                         if (error)
581                                 return (error);
582                 }
583                 break;
584
585         default:
586                 return (EINVAL);
587         }
588
589         error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
590         if (error)
591                 return (error);
592         error = copyout(&dq->dq_dqb, addr, sizeof (struct dqblk));
593         dqrele(NULLVP, dq);
594         return (error);
595 }
596
597 /*
598  * Q_SETQUOTA - assign an entire dqblk structure.
599  */
600 int
601 setquota(td, mp, id, type, addr)
602         struct thread *td;
603         struct mount *mp;
604         u_long id;
605         int type;
606         void *addr;
607 {
608         struct dquot *dq;
609         struct dquot *ndq;
610         struct ufsmount *ump = VFSTOUFS(mp);
611         struct dqblk newlim;
612         int error;
613
614         error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL);
615         if (error)
616                 return (error);
617
618         error = copyin(addr, &newlim, sizeof (struct dqblk));
619         if (error)
620                 return (error);
621         error = dqget(NULLVP, id, ump, type, &ndq);
622         if (error)
623                 return (error);
624         dq = ndq;
625         while (dq->dq_flags & DQ_LOCK) {
626                 dq->dq_flags |= DQ_WANT;
627                 (void) tsleep(dq, PINOD+1, "setqta", 0);
628         }
629         /*
630          * Copy all but the current values.
631          * Reset time limit if previously had no soft limit or were
632          * under it, but now have a soft limit and are over it.
633          */
634         newlim.dqb_curblocks = dq->dq_curblocks;
635         newlim.dqb_curinodes = dq->dq_curinodes;
636         if (dq->dq_id != 0) {
637                 newlim.dqb_btime = dq->dq_btime;
638                 newlim.dqb_itime = dq->dq_itime;
639         }
640         if (newlim.dqb_bsoftlimit &&
641             dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
642             (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
643                 newlim.dqb_btime = time_second + ump->um_btime[type];
644         if (newlim.dqb_isoftlimit &&
645             dq->dq_curinodes >= newlim.dqb_isoftlimit &&
646             (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
647                 newlim.dqb_itime = time_second + ump->um_itime[type];
648         dq->dq_dqb = newlim;
649         if (dq->dq_curblocks < dq->dq_bsoftlimit)
650                 dq->dq_flags &= ~DQ_BLKS;
651         if (dq->dq_curinodes < dq->dq_isoftlimit)
652                 dq->dq_flags &= ~DQ_INODS;
653         if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
654             dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
655                 dq->dq_flags |= DQ_FAKE;
656         else
657                 dq->dq_flags &= ~DQ_FAKE;
658         dq->dq_flags |= DQ_MOD;
659         dqrele(NULLVP, dq);
660         return (0);
661 }
662
663 /*
664  * Q_SETUSE - set current inode and block usage.
665  */
666 int
667 setuse(td, mp, id, type, addr)
668         struct thread *td;
669         struct mount *mp;
670         u_long id;
671         int type;
672         void *addr;
673 {
674         struct dquot *dq;
675         struct ufsmount *ump = VFSTOUFS(mp);
676         struct dquot *ndq;
677         struct dqblk usage;
678         int error;
679
680         error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL);
681         if (error)
682                 return (error);
683
684         error = copyin(addr, &usage, sizeof (struct dqblk));
685         if (error)
686                 return (error);
687         error = dqget(NULLVP, id, ump, type, &ndq);
688         if (error)
689                 return (error);
690         dq = ndq;
691         while (dq->dq_flags & DQ_LOCK) {
692                 dq->dq_flags |= DQ_WANT;
693                 (void) tsleep(dq, PINOD+1, "setuse", 0);
694         }
695         /*
696          * Reset time limit if have a soft limit and were
697          * previously under it, but are now over it.
698          */
699         if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
700             usage.dqb_curblocks >= dq->dq_bsoftlimit)
701                 dq->dq_btime = time_second + ump->um_btime[type];
702         if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
703             usage.dqb_curinodes >= dq->dq_isoftlimit)
704                 dq->dq_itime = time_second + ump->um_itime[type];
705         dq->dq_curblocks = usage.dqb_curblocks;
706         dq->dq_curinodes = usage.dqb_curinodes;
707         if (dq->dq_curblocks < dq->dq_bsoftlimit)
708                 dq->dq_flags &= ~DQ_BLKS;
709         if (dq->dq_curinodes < dq->dq_isoftlimit)
710                 dq->dq_flags &= ~DQ_INODS;
711         dq->dq_flags |= DQ_MOD;
712         dqrele(NULLVP, dq);
713         return (0);
714 }
715
716 /*
717  * Q_SYNC - sync quota files to disk.
718  */
719 int
720 qsync(mp)
721         struct mount *mp;
722 {
723         struct ufsmount *ump = VFSTOUFS(mp);
724         struct thread *td = curthread;          /* XXX */
725         struct vnode *vp, *mvp;
726         struct dquot *dq;
727         int i, error;
728
729         /*
730          * Check if the mount point has any quotas.
731          * If not, simply return.
732          */
733         for (i = 0; i < MAXQUOTAS; i++)
734                 if (ump->um_quotas[i] != NULLVP)
735                         break;
736         if (i == MAXQUOTAS)
737                 return (0);
738         /*
739          * Search vnodes associated with this mount point,
740          * synchronizing any modified dquot structures.
741          */
742         MNT_ILOCK(mp);
743 again:
744         MNT_VNODE_FOREACH(vp, mp, mvp) {
745                 VI_LOCK(vp);
746                 MNT_IUNLOCK(mp);
747                 if (vp->v_type == VNON) {
748                         VI_UNLOCK(vp);
749                         MNT_ILOCK(mp);
750                         continue;
751                 }
752                 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td);
753                 if (error) {
754                         MNT_ILOCK(mp);
755                         if (error == ENOENT) {
756                                 MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
757                                 goto again;
758                         }
759                         continue;
760                 }
761                 for (i = 0; i < MAXQUOTAS; i++) {
762                         dq = VTOI(vp)->i_dquot[i];
763                         if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
764                                 dqsync(vp, dq);
765                 }
766                 vput(vp);
767                 MNT_ILOCK(mp);
768         }
769         MNT_IUNLOCK(mp);
770         return (0);
771 }
772
773 /*
774  * Code pertaining to management of the in-core dquot data structures.
775  */
776 #define DQHASH(dqvp, id) \
777         (&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
778 static LIST_HEAD(dqhash, dquot) *dqhashtbl;
779 static u_long dqhash;
780
781 /*
782  * Dquot free list.
783  */
784 #define DQUOTINC        5       /* minimum free dquots desired */
785 static TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
786 static long numdquot, desireddquot = DQUOTINC;
787
788 /*
789  * Initialize the quota system.
790  */
791 void
792 dqinit()
793 {
794
795         dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
796         TAILQ_INIT(&dqfreelist);
797 }
798
799 /*
800  * Shut down the quota system.
801  */
802 void
803 dquninit()
804 {
805         struct dquot *dq;
806
807         hashdestroy(dqhashtbl, M_DQUOT, dqhash);
808         while ((dq = TAILQ_FIRST(&dqfreelist)) != NULL) {
809                 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
810                 free(dq, M_DQUOT);
811         }
812 }
813
814 /*
815  * Obtain a dquot structure for the specified identifier and quota file
816  * reading the information from the file if necessary.
817  */
818 static int
819 dqget(vp, id, ump, type, dqp)
820         struct vnode *vp;
821         u_long id;
822         struct ufsmount *ump;
823         int type;
824         struct dquot **dqp;
825 {
826         struct thread *td = curthread;          /* XXX */
827         struct dquot *dq;
828         struct dqhash *dqh;
829         struct vnode *dqvp;
830         struct iovec aiov;
831         struct uio auio;
832         int error;
833
834         dqvp = ump->um_quotas[type];
835         if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
836                 *dqp = NODQUOT;
837                 return (EINVAL);
838         }
839         /*
840          * Check the cache first.
841          */
842         dqh = DQHASH(dqvp, id);
843         LIST_FOREACH(dq, dqh, dq_hash) {
844                 if (dq->dq_id != id ||
845                     dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
846                         continue;
847                 /*
848                  * Cache hit with no references.  Take
849                  * the structure off the free list.
850                  */
851                 if (dq->dq_cnt == 0)
852                         TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
853                 DQREF(dq);
854                 *dqp = dq;
855                 return (0);
856         }
857         /*
858          * Not in cache, allocate a new one.
859          */
860         if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
861             numdquot < MAXQUOTAS * desiredvnodes)
862                 desireddquot += DQUOTINC;
863         if (numdquot < desireddquot) {
864                 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT,
865                     M_WAITOK | M_ZERO);
866                 numdquot++;
867         } else {
868                 if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
869                         tablefull("dquot");
870                         *dqp = NODQUOT;
871                         return (EUSERS);
872                 }
873                 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
874                         panic("dqget: free dquot isn't");
875                 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
876                 if (dq->dq_ump != NULL)
877                         LIST_REMOVE(dq, dq_hash);
878         }
879         /*
880          * Initialize the contents of the dquot structure.
881          */
882         if (vp != dqvp)
883                 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td);
884         LIST_INSERT_HEAD(dqh, dq, dq_hash);
885         DQREF(dq);
886         dq->dq_flags = DQ_LOCK;
887         dq->dq_id = id;
888         dq->dq_ump = ump;
889         dq->dq_type = type;
890         auio.uio_iov = &aiov;
891         auio.uio_iovcnt = 1;
892         aiov.iov_base = &dq->dq_dqb;
893         aiov.iov_len = sizeof (struct dqblk);
894         auio.uio_resid = sizeof (struct dqblk);
895         auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
896         auio.uio_segflg = UIO_SYSSPACE;
897         auio.uio_rw = UIO_READ;
898         auio.uio_td = (struct thread *)0;
899         error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
900         if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
901                 bzero(&dq->dq_dqb, sizeof(struct dqblk));
902         if (vp != dqvp)
903                 VOP_UNLOCK(dqvp, 0, td);
904         if (dq->dq_flags & DQ_WANT)
905                 wakeup(dq);
906         dq->dq_flags = 0;
907         /*
908          * I/O error in reading quota file, release
909          * quota structure and reflect problem to caller.
910          */
911         if (error) {
912                 LIST_REMOVE(dq, dq_hash);
913                 dqrele(vp, dq);
914                 *dqp = NODQUOT;
915                 return (error);
916         }
917         /*
918          * Check for no limit to enforce.
919          * Initialize time values if necessary.
920          */
921         if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
922             dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
923                 dq->dq_flags |= DQ_FAKE;
924         if (dq->dq_id != 0) {
925                 if (dq->dq_btime == 0)
926                         dq->dq_btime = time_second + ump->um_btime[type];
927                 if (dq->dq_itime == 0)
928                         dq->dq_itime = time_second + ump->um_itime[type];
929         }
930         *dqp = dq;
931         return (0);
932 }
933
934 #ifdef DIAGNOSTIC
935 /*
936  * Obtain a reference to a dquot.
937  */
938 static void
939 dqref(dq)
940         struct dquot *dq;
941 {
942
943         dq->dq_cnt++;
944 }
945 #endif
946
947 /*
948  * Release a reference to a dquot.
949  */
950 void
951 dqrele(vp, dq)
952         struct vnode *vp;
953         struct dquot *dq;
954 {
955
956         if (dq == NODQUOT)
957                 return;
958         if (dq->dq_cnt > 1) {
959                 dq->dq_cnt--;
960                 return;
961         }
962         if (dq->dq_flags & DQ_MOD)
963                 (void) dqsync(vp, dq);
964         if (--dq->dq_cnt > 0)
965                 return;
966         TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
967 }
968
969 /*
970  * Update the disk quota in the quota file.
971  */
972 static int
973 dqsync(vp, dq)
974         struct vnode *vp;
975         struct dquot *dq;
976 {
977         struct thread *td = curthread;          /* XXX */
978         struct vnode *dqvp;
979         struct iovec aiov;
980         struct uio auio;
981         int error;
982
983         if (dq == NODQUOT)
984                 panic("dqsync: dquot");
985         if ((dq->dq_flags & DQ_MOD) == 0)
986                 return (0);
987         if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
988                 panic("dqsync: file");
989         (void) vn_write_suspend_wait(dqvp, NULL, V_WAIT);
990         if (vp != dqvp)
991                 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td);
992         while (dq->dq_flags & DQ_LOCK) {
993                 dq->dq_flags |= DQ_WANT;
994                 (void) tsleep(dq, PINOD+2, "dqsync", 0);
995                 if ((dq->dq_flags & DQ_MOD) == 0) {
996                         if (vp != dqvp)
997                                 VOP_UNLOCK(dqvp, 0, td);
998                         return (0);
999                 }
1000         }
1001         dq->dq_flags |= DQ_LOCK;
1002         auio.uio_iov = &aiov;
1003         auio.uio_iovcnt = 1;
1004         aiov.iov_base = &dq->dq_dqb;
1005         aiov.iov_len = sizeof (struct dqblk);
1006         auio.uio_resid = sizeof (struct dqblk);
1007         auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
1008         auio.uio_segflg = UIO_SYSSPACE;
1009         auio.uio_rw = UIO_WRITE;
1010         auio.uio_td = (struct thread *)0;
1011         error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
1012         if (auio.uio_resid && error == 0)
1013                 error = EIO;
1014         if (dq->dq_flags & DQ_WANT)
1015                 wakeup(dq);
1016         dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
1017         if (vp != dqvp)
1018                 VOP_UNLOCK(dqvp, 0, td);
1019         return (error);
1020 }
1021
1022 /*
1023  * Flush all entries from the cache for a particular vnode.
1024  */
1025 static void
1026 dqflush(vp)
1027         struct vnode *vp;
1028 {
1029         struct dquot *dq, *nextdq;
1030         struct dqhash *dqh;
1031
1032         /*
1033          * Move all dquot's that used to refer to this quota
1034          * file off their hash chains (they will eventually
1035          * fall off the head of the free list and be re-used).
1036          */
1037         for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
1038                 for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
1039                         nextdq = LIST_NEXT(dq, dq_hash);
1040                         if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
1041                                 continue;
1042                         if (dq->dq_cnt)
1043                                 panic("dqflush: stray dquot");
1044                         LIST_REMOVE(dq, dq_hash);
1045                         dq->dq_ump = (struct ufsmount *)0;
1046                 }
1047         }
1048 }