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