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