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