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