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