]>
git.saurik.com Git - apple/xnu.git/blob - bsd/ufs/ufs/ufs_quota.c
5048b1725a4f5f1341dbe596e4f6037eabfc5836
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (c) 1982, 1986, 1990, 1993, 1995
27 * The Regents of the University of California. All rights reserved.
29 * This code is derived from software contributed to Berkeley by
30 * Robert Elz at The University of Melbourne.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
63 #include <sys/param.h>
64 #include <sys/kernel.h>
65 #include <sys/systm.h>
66 #include <sys/malloc.h>
69 #include <sys/vnode.h>
70 #include <sys/mount.h>
71 #include <sys/namei.h>
72 #include <sys/quota.h>
74 #include <ufs/ufs/quota.h>
75 #include <ufs/ufs/inode.h>
76 #include <ufs/ufs/ufsmount.h>
77 #include <ufs/ufs/ufs_extern.h>
80 * Quota name to error message mapping.
82 static char *quotatypes
[] = INITQFNAMES
;
85 * Set up the quotas for an inode.
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.
94 register struct inode
*ip
;
97 struct vnode
*vp
= ITOV(ip
);
100 ump
= VFSTOUFS(vp
->v_mount
);
102 * Set up the user quota based on file uid.
103 * EINVAL means that quotas are not enabled.
105 if (ip
->i_dquot
[USRQUOTA
] == NODQUOT
&&
107 dqget(vp
, ip
->i_uid
, &ump
->um_qfiles
[USRQUOTA
], USRQUOTA
, &ip
->i_dquot
[USRQUOTA
])) &&
111 * Set up the group quota based on file gid.
112 * EINVAL means that quotas are not enabled.
114 if (ip
->i_dquot
[GRPQUOTA
] == NODQUOT
&&
116 dqget(vp
, ip
->i_gid
, &ump
->um_qfiles
[GRPQUOTA
], GRPQUOTA
, &ip
->i_dquot
[GRPQUOTA
])) &&
123 * Update disk usage, and take corrective action.
126 chkdq(ip
, change
, cred
, flags
)
127 register struct inode
*ip
;
132 register struct dquot
*dq
;
139 if ((flags
& CHOWN
) == 0)
145 for (i
= 0; i
< MAXQUOTAS
; i
++) {
146 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
148 while (dq
->dq_flags
& DQ_LOCK
) {
149 dq
->dq_flags
|= DQ_WANT
;
150 sleep((caddr_t
)dq
, PINOD
+1);
152 ncurbytes
= dq
->dq_curbytes
+ change
;
154 dq
->dq_curbytes
= ncurbytes
;
157 dq
->dq_flags
&= ~DQ_BLKS
;
158 dq
->dq_flags
|= DQ_MOD
;
164 cred
= kernproc
->p_ucred
;
165 if ((flags
& FORCE
) == 0 && ((cred
->cr_uid
!= 0) || (p
->p_flag
& P_FORCEQUOTA
))) {
166 for (i
= 0; i
< MAXQUOTAS
; i
++) {
167 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
169 if (error
= chkdqchg(ip
, change
, cred
, i
))
173 for (i
= 0; i
< MAXQUOTAS
; i
++) {
174 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
176 while (dq
->dq_flags
& DQ_LOCK
) {
177 dq
->dq_flags
|= DQ_WANT
;
178 sleep((caddr_t
)dq
, PINOD
+1);
180 dq
->dq_curbytes
+= change
;
181 dq
->dq_flags
|= DQ_MOD
;
187 * Check for a valid change to a users allocation.
188 * Issue an error message if appropriate.
191 chkdqchg(ip
, change
, cred
, type
)
197 register struct dquot
*dq
= ip
->i_dquot
[type
];
198 u_int64_t ncurbytes
= dq
->dq_curbytes
+ change
;
201 * If user would exceed their hard limit, disallow space allocation.
203 if (ncurbytes
>= dq
->dq_bhardlimit
&& dq
->dq_bhardlimit
) {
204 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
205 ip
->i_uid
== cred
->cr_uid
) {
207 printf("\n%s: write failed, %s disk limit reached\n",
208 ITOV(ip
)->v_mount
->mnt_stat
.f_mntonname
,
211 dq
->dq_flags
|= DQ_BLKS
;
216 * If user is over their soft limit for too long, disallow space
217 * allocation. Reset time limit as they cross their soft limit.
219 if (ncurbytes
>= dq
->dq_bsoftlimit
&& dq
->dq_bsoftlimit
) {
220 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
) {
221 dq
->dq_btime
= time
.tv_sec
+
222 VFSTOUFS(ITOV(ip
)->v_mount
)->um_qfiles
[type
].qf_btime
;
224 if (ip
->i_uid
== cred
->cr_uid
)
225 printf("\n%s: warning, %s %s\n",
226 ITOV(ip
)->v_mount
->mnt_stat
.f_mntonname
,
227 quotatypes
[type
], "disk quota exceeded");
231 if (time
.tv_sec
> dq
->dq_btime
) {
232 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
233 ip
->i_uid
== cred
->cr_uid
) {
235 printf("\n%s: write failed, %s %s\n",
236 ITOV(ip
)->v_mount
->mnt_stat
.f_mntonname
,
238 "disk quota exceeded for too long");
240 dq
->dq_flags
|= DQ_BLKS
;
249 * Check the inode limit, applying corrective action.
252 chkiq(ip
, change
, cred
, flags
)
253 register struct inode
*ip
;
258 register struct dquot
*dq
;
260 int ncurinodes
, error
;
264 if ((flags
& CHOWN
) == 0)
270 for (i
= 0; i
< MAXQUOTAS
; i
++) {
271 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
273 while (dq
->dq_flags
& DQ_LOCK
) {
274 dq
->dq_flags
|= DQ_WANT
;
275 sleep((caddr_t
)dq
, PINOD
+1);
277 ncurinodes
= dq
->dq_curinodes
+ change
;
279 dq
->dq_curinodes
= ncurinodes
;
281 dq
->dq_curinodes
= 0;
282 dq
->dq_flags
&= ~DQ_INODS
;
283 dq
->dq_flags
|= DQ_MOD
;
289 cred
= kernproc
->p_ucred
;
290 if ((flags
& FORCE
) == 0 && ((cred
->cr_uid
!= 0) || (p
->p_flag
& P_FORCEQUOTA
))) {
291 for (i
= 0; i
< MAXQUOTAS
; i
++) {
292 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
294 if (error
= chkiqchg(ip
, change
, cred
, i
))
298 for (i
= 0; i
< MAXQUOTAS
; i
++) {
299 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
301 while (dq
->dq_flags
& DQ_LOCK
) {
302 dq
->dq_flags
|= DQ_WANT
;
303 sleep((caddr_t
)dq
, PINOD
+1);
305 dq
->dq_curinodes
+= change
;
306 dq
->dq_flags
|= DQ_MOD
;
312 * Check for a valid change to a users allocation.
313 * Issue an error message if appropriate.
316 chkiqchg(ip
, change
, cred
, type
)
322 register struct dquot
*dq
= ip
->i_dquot
[type
];
323 long ncurinodes
= dq
->dq_curinodes
+ change
;
326 * If user would exceed their hard limit, disallow inode allocation.
328 if (ncurinodes
>= dq
->dq_ihardlimit
&& dq
->dq_ihardlimit
) {
329 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
330 ip
->i_uid
== cred
->cr_uid
) {
332 printf("\n%s: write failed, %s inode limit reached\n",
333 ITOV(ip
)->v_mount
->mnt_stat
.f_mntonname
,
336 dq
->dq_flags
|= DQ_INODS
;
341 * If user is over their soft limit for too long, disallow inode
342 * allocation. Reset time limit as they cross their soft limit.
344 if (ncurinodes
>= dq
->dq_isoftlimit
&& dq
->dq_isoftlimit
) {
345 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
) {
346 dq
->dq_itime
= time
.tv_sec
+
347 VFSTOUFS(ITOV(ip
)->v_mount
)->um_qfiles
[type
].qf_itime
;
349 if (ip
->i_uid
== cred
->cr_uid
)
350 printf("\n%s: warning, %s %s\n",
351 ITOV(ip
)->v_mount
->mnt_stat
.f_mntonname
,
352 quotatypes
[type
], "inode quota exceeded");
356 if (time
.tv_sec
> dq
->dq_itime
) {
357 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
358 ip
->i_uid
== cred
->cr_uid
) {
360 printf("\n%s: write failed, %s %s\n",
361 ITOV(ip
)->v_mount
->mnt_stat
.f_mntonname
,
363 "inode quota exceeded for too long");
365 dq
->dq_flags
|= DQ_INODS
;
375 * On filesystems with quotas enabled, it is an error for a file to change
376 * size and not to have a dquot structure associated with it.
380 register struct inode
*ip
;
382 struct ufsmount
*ump
= VFSTOUFS(ITOV(ip
)->v_mount
);
385 for (i
= 0; i
< MAXQUOTAS
; i
++) {
386 if (ump
->um_qfiles
[i
].qf_vp
== NULLVP
||
387 (ump
->um_qfiles
[i
].qf_qflags
& (QTF_OPENING
|QTF_CLOSING
)))
389 if (ip
->i_dquot
[i
] == NODQUOT
) {
390 vprint("chkdquot: missing dquot", ITOV(ip
));
391 panic("missing dquot");
398 * Code to process quotactl commands.
402 * Q_QUOTAON - set up a quota file for a particular file system.
405 quotaon(p
, mp
, type
, fname
, segflg
)
412 struct ufsmount
*ump
= VFSTOUFS(mp
);
413 struct vnode
*vp
, **vpp
;
414 struct vnode
*nextvp
;
419 vpp
= &ump
->um_qfiles
[type
].qf_vp
;
420 NDINIT(&nd
, LOOKUP
, FOLLOW
, segflg
, fname
, p
);
421 if (error
= vn_open(&nd
, FREAD
|FWRITE
, 0))
424 VOP_UNLOCK(vp
, 0, p
);
425 if (vp
->v_type
!= VREG
) {
426 (void) vn_close(vp
, FREAD
|FWRITE
, p
->p_ucred
, p
);
430 quotaoff(p
, mp
, type
);
431 ump
->um_qfiles
[type
].qf_qflags
|= QTF_OPENING
;
432 mp
->mnt_flag
|= MNT_QUOTA
;
433 vp
->v_flag
|= VNOFLUSH
;
436 * Save the credential of the process that turned on quotas.
439 ump
->um_qfiles
[type
].qf_cred
= p
->p_ucred
;
440 /* Finish initializing the quota file */
441 if (error
= dqfileopen(&ump
->um_qfiles
[type
], type
))
444 ump
->um_qfiles
[type
].qf_btime
= MAX_DQ_TIME
;
445 ump
->um_qfiles
[type
].qf_itime
= MAX_IQ_TIME
;
446 if (dqget(NULLVP
, 0, &ump
->um_qfiles
[type
], type
, &dq
) == 0) {
447 if (dq
->dq_btime
> 0)
448 ump
->um_qfiles
[type
].qf_btime
= dq
->dq_btime
;
449 if (dq
->dq_itime
> 0)
450 ump
->um_qfiles
[type
].qf_itime
= dq
->dq_itime
;
455 * Search vnodes associated with this mount point,
456 * adding references to quota file being opened.
457 * NB: only need to add dquot's for inodes being modified.
460 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
461 nextvp
= vp
->v_mntvnodes
.le_next
;
462 if (vp
->v_writecount
== 0)
464 if (vget(vp
, LK_EXCLUSIVE
, p
))
466 if (error
= getinoquota(VTOI(vp
))) {
471 if (vp
->v_mntvnodes
.le_next
!= nextvp
|| vp
->v_mount
!= mp
)
475 ump
->um_qfiles
[type
].qf_qflags
&= ~QTF_OPENING
;
477 quotaoff(p
, mp
, type
);
482 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
485 quotaoff(p
, mp
, type
)
491 struct vnode
*qvp
, *nextvp
;
492 struct ufsmount
*ump
= VFSTOUFS(mp
);
498 if ((qvp
= ump
->um_qfiles
[type
].qf_vp
) == NULLVP
)
500 ump
->um_qfiles
[type
].qf_qflags
|= QTF_CLOSING
;
502 * Search vnodes associated with this mount point,
503 * deleting any references to quota file being closed.
506 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
507 nextvp
= vp
->v_mntvnodes
.le_next
;
508 if (vget(vp
, LK_EXCLUSIVE
, p
))
511 dq
= ip
->i_dquot
[type
];
512 ip
->i_dquot
[type
] = NODQUOT
;
515 if (vp
->v_mntvnodes
.le_next
!= nextvp
|| vp
->v_mount
!= mp
)
519 /* Finish tearing down the quota file */
520 dqfileclose(&ump
->um_qfiles
[type
], type
);
521 qvp
->v_flag
&= ~VNOFLUSH
;
522 error
= vn_close(qvp
, FREAD
|FWRITE
, p
->p_ucred
, p
);
523 ump
->um_qfiles
[type
].qf_vp
= NULLVP
;
524 cred
= ump
->um_qfiles
[type
].qf_cred
;
525 if (cred
!= NOCRED
) {
526 ump
->um_qfiles
[type
].qf_cred
= NOCRED
;
529 ump
->um_qfiles
[type
].qf_qflags
&= ~QTF_CLOSING
;
530 for (type
= 0; type
< MAXQUOTAS
; type
++)
531 if (ump
->um_qfiles
[type
].qf_vp
!= NULLVP
)
533 if (type
== MAXQUOTAS
)
534 mp
->mnt_flag
&= ~MNT_QUOTA
;
539 * Q_GETQUOTA - return current values in a dqblk structure.
542 getquota(mp
, id
, type
, addr
)
551 if (error
= dqget(NULLVP
, id
, &VFSTOUFS(mp
)->um_qfiles
[type
], type
, &dq
))
553 error
= copyout((caddr_t
)&dq
->dq_dqb
, addr
, sizeof (struct dqblk
));
559 * Q_SETQUOTA - assign an entire dqblk structure.
562 setquota(mp
, id
, type
, addr
)
568 register struct dquot
*dq
;
570 struct ufsmount
*ump
= VFSTOUFS(mp
);
574 if (error
= copyin(addr
, (caddr_t
)&newlim
, sizeof (struct dqblk
)))
576 if (error
= dqget(NULLVP
, id
, &ump
->um_qfiles
[type
], type
, &ndq
))
579 while (dq
->dq_flags
& DQ_LOCK
) {
580 dq
->dq_flags
|= DQ_WANT
;
581 sleep((caddr_t
)dq
, PINOD
+1);
584 * Copy all but the current values.
585 * Reset time limit if previously had no soft limit or were
586 * under it, but now have a soft limit and are over it.
588 newlim
.dqb_curbytes
= dq
->dq_curbytes
;
589 newlim
.dqb_curinodes
= dq
->dq_curinodes
;
590 if (dq
->dq_id
!= 0) {
591 newlim
.dqb_btime
= dq
->dq_btime
;
592 newlim
.dqb_itime
= dq
->dq_itime
;
594 if (newlim
.dqb_bsoftlimit
&&
595 dq
->dq_curbytes
>= newlim
.dqb_bsoftlimit
&&
596 (dq
->dq_bsoftlimit
== 0 || dq
->dq_curbytes
< dq
->dq_bsoftlimit
))
597 newlim
.dqb_btime
= time
.tv_sec
+ ump
->um_qfiles
[type
].qf_btime
;
598 if (newlim
.dqb_isoftlimit
&&
599 dq
->dq_curinodes
>= newlim
.dqb_isoftlimit
&&
600 (dq
->dq_isoftlimit
== 0 || dq
->dq_curinodes
< dq
->dq_isoftlimit
))
601 newlim
.dqb_itime
= time
.tv_sec
+ ump
->um_qfiles
[type
].qf_itime
;
603 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
)
604 dq
->dq_flags
&= ~DQ_BLKS
;
605 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
606 dq
->dq_flags
&= ~DQ_INODS
;
607 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
608 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
609 dq
->dq_flags
|= DQ_FAKE
;
611 dq
->dq_flags
&= ~DQ_FAKE
;
612 dq
->dq_flags
|= DQ_MOD
;
618 * Q_SETUSE - set current inode and byte usage.
621 setuse(mp
, id
, type
, addr
)
627 register struct dquot
*dq
;
628 struct ufsmount
*ump
= VFSTOUFS(mp
);
633 if (error
= copyin(addr
, (caddr_t
)&usage
, sizeof (struct dqblk
)))
635 if (error
= dqget(NULLVP
, id
, &ump
->um_qfiles
[type
], type
, &ndq
))
638 while (dq
->dq_flags
& DQ_LOCK
) {
639 dq
->dq_flags
|= DQ_WANT
;
640 sleep((caddr_t
)dq
, PINOD
+1);
643 * Reset time limit if have a soft limit and were
644 * previously under it, but are now over it.
646 if (dq
->dq_bsoftlimit
&& dq
->dq_curbytes
< dq
->dq_bsoftlimit
&&
647 usage
.dqb_curbytes
>= dq
->dq_bsoftlimit
)
648 dq
->dq_btime
= time
.tv_sec
+ ump
->um_qfiles
[type
].qf_btime
;
649 if (dq
->dq_isoftlimit
&& dq
->dq_curinodes
< dq
->dq_isoftlimit
&&
650 usage
.dqb_curinodes
>= dq
->dq_isoftlimit
)
651 dq
->dq_itime
= time
.tv_sec
+ ump
->um_qfiles
[type
].qf_itime
;
652 dq
->dq_curbytes
= usage
.dqb_curbytes
;
653 dq
->dq_curinodes
= usage
.dqb_curinodes
;
654 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
)
655 dq
->dq_flags
&= ~DQ_BLKS
;
656 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
657 dq
->dq_flags
&= ~DQ_INODS
;
658 dq
->dq_flags
|= DQ_MOD
;
664 * Q_SYNC - sync quota files to disk.
670 struct ufsmount
*ump
= VFSTOUFS(mp
);
671 struct proc
*p
= current_proc(); /* XXX */
672 struct vnode
*vp
, *nextvp
;
677 * Check if the mount point has any quotas.
678 * If not, simply return.
680 for (i
= 0; i
< MAXQUOTAS
; i
++)
681 if (ump
->um_qfiles
[i
].qf_vp
!= NULLVP
)
686 * Search vnodes associated with this mount point,
687 * synchronizing any modified dquot structures.
689 simple_lock(&mntvnode_slock
);
691 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
692 if (vp
->v_mount
!= mp
)
694 nextvp
= vp
->v_mntvnodes
.le_next
;
695 simple_lock(&vp
->v_interlock
);
696 simple_unlock(&mntvnode_slock
);
697 error
= vget(vp
, LK_EXCLUSIVE
| LK_NOWAIT
| LK_INTERLOCK
, p
);
699 simple_lock(&mntvnode_slock
);
704 for (i
= 0; i
< MAXQUOTAS
; i
++) {
705 dq
= VTOI(vp
)->i_dquot
[i
];
706 if (dq
!= NODQUOT
&& (dq
->dq_flags
& DQ_MOD
))
710 simple_lock(&mntvnode_slock
);
711 if (vp
->v_mntvnodes
.le_next
!= nextvp
)
714 simple_unlock(&mntvnode_slock
);
719 * Q_QUOTASTAT - get quota on/off status
722 quotastat(mp
, type
, addr
)
727 struct ufsmount
*ump
= VFSTOUFS(mp
);
731 if ((mp
->mnt_flag
& MNT_QUOTA
) && (ump
->um_qfiles
[type
].qf_vp
!= NULLVP
))
732 qstat
= 1; /* quotas are on for this type */
734 qstat
= 0; /* quotas are off for this type */
736 error
= copyout ((caddr_t
)&qstat
, addr
, sizeof(qstat
));