]>
git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_quota.c
11278c5f5876fae716bb5af66bf1b1d759efd15f
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1982, 1986, 1990, 1993, 1995
24 * The Regents of the University of California. All rights reserved.
26 * This code is derived from software contributed to Berkeley by
27 * Robert Elz at The University of Melbourne.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
61 #include <sys/param.h>
62 #include <sys/kernel.h>
63 #include <sys/systm.h>
64 #include <sys/mount.h>
65 #include <sys/namei.h>
66 #include <sys/malloc.h>
69 #include <sys/vnode.h>
70 #include <sys/quota.h>
73 #include <hfs/hfs_cnode.h>
74 #include <hfs/hfs_quota.h>
75 #include <hfs/hfs_mount.h>
78 * Quota name to error message mapping.
80 static char *quotatypes
[] = INITQFNAMES
;
83 * Set up the quotas for a cnode.
85 * This routine completely defines the semantics of quotas.
86 * If other criterion want to be used to establish quotas, the
87 * MAXQUOTAS value in quotas.h should be increased, and the
88 * additional dquots set up here.
92 register struct cnode
*cp
;
94 struct hfsmount
*hfsmp
;
98 vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
99 hfsmp
= VFSTOHFS(vp
->v_mount
);
101 * Set up the user quota based on file uid.
102 * EINVAL means that quotas are not enabled.
104 if (cp
->c_dquot
[USRQUOTA
] == NODQUOT
&&
106 dqget(vp
, cp
->c_uid
, &hfsmp
->hfs_qfiles
[USRQUOTA
], USRQUOTA
, &cp
->c_dquot
[USRQUOTA
])) &&
110 * Set up the group quota based on file gid.
111 * EINVAL means that quotas are not enabled.
113 if (cp
->c_dquot
[GRPQUOTA
] == NODQUOT
&&
115 dqget(vp
, cp
->c_gid
, &hfsmp
->hfs_qfiles
[GRPQUOTA
], GRPQUOTA
, &cp
->c_dquot
[GRPQUOTA
])) &&
122 * Update disk usage, and take corrective action.
125 hfs_chkdq(cp
, change
, cred
, flags
)
126 register struct cnode
*cp
;
131 register struct dquot
*dq
;
138 if ((flags
& CHOWN
) == 0)
144 for (i
= 0; i
< MAXQUOTAS
; i
++) {
145 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
147 while (dq
->dq_flags
& DQ_LOCK
) {
148 dq
->dq_flags
|= DQ_WANT
;
149 sleep((caddr_t
)dq
, PINOD
+1);
151 ncurbytes
= dq
->dq_curbytes
+ change
;
153 dq
->dq_curbytes
= ncurbytes
;
156 dq
->dq_flags
&= ~DQ_BLKS
;
157 dq
->dq_flags
|= DQ_MOD
;
163 cred
= kernproc
->p_ucred
;
164 if ((cred
->cr_uid
!= 0) || (p
->p_flag
& P_FORCEQUOTA
)) {
165 for (i
= 0; i
< MAXQUOTAS
; i
++) {
166 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
168 error
= hfs_chkdqchg(cp
, change
, cred
, i
);
174 if ((flags
& FORCE
) || error
== 0) {
175 for (i
= 0; i
< MAXQUOTAS
; i
++) {
176 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
178 while (dq
->dq_flags
& DQ_LOCK
) {
179 dq
->dq_flags
|= DQ_WANT
;
180 sleep((caddr_t
)dq
, PINOD
+1);
182 dq
->dq_curbytes
+= change
;
183 dq
->dq_flags
|= DQ_MOD
;
190 * Check for a valid change to a users allocation.
191 * Issue an error message if appropriate.
194 hfs_chkdqchg(cp
, change
, cred
, type
)
200 register struct dquot
*dq
= cp
->c_dquot
[type
];
201 u_int64_t ncurbytes
= dq
->dq_curbytes
+ change
;
202 struct vnode
*vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
205 * If user would exceed their hard limit, disallow space allocation.
207 if (ncurbytes
>= dq
->dq_bhardlimit
&& dq
->dq_bhardlimit
) {
208 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
209 cp
->c_uid
== cred
->cr_uid
) {
211 printf("\n%s: write failed, %s disk limit reached\n",
212 vp
->v_mount
->mnt_stat
.f_mntonname
,
215 dq
->dq_flags
|= DQ_BLKS
;
220 * If user is over their soft limit for too long, disallow space
221 * allocation. Reset time limit as they cross their soft limit.
223 if (ncurbytes
>= dq
->dq_bsoftlimit
&& dq
->dq_bsoftlimit
) {
224 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
) {
225 dq
->dq_btime
= time
.tv_sec
+
226 VFSTOHFS(vp
->v_mount
)->hfs_qfiles
[type
].qf_btime
;
228 if (cp
->c_uid
== cred
->cr_uid
)
229 printf("\n%s: warning, %s %s\n",
230 vp
->v_mount
->mnt_stat
.f_mntonname
,
231 quotatypes
[type
], "disk quota exceeded");
235 if (time
.tv_sec
> dq
->dq_btime
) {
236 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
237 cp
->c_uid
== cred
->cr_uid
) {
239 printf("\n%s: write failed, %s %s\n",
240 vp
->v_mount
->mnt_stat
.f_mntonname
,
242 "disk quota exceeded for too long");
244 dq
->dq_flags
|= DQ_BLKS
;
253 * Check the inode limit, applying corrective action.
256 hfs_chkiq(cp
, change
, cred
, flags
)
257 register struct cnode
*cp
;
262 register struct dquot
*dq
;
264 int ncurinodes
, error
=0;
268 if ((flags
& CHOWN
) == 0)
274 for (i
= 0; i
< MAXQUOTAS
; i
++) {
275 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
277 while (dq
->dq_flags
& DQ_LOCK
) {
278 dq
->dq_flags
|= DQ_WANT
;
279 sleep((caddr_t
)dq
, PINOD
+1);
281 ncurinodes
= dq
->dq_curinodes
+ change
;
283 dq
->dq_curinodes
= ncurinodes
;
285 dq
->dq_curinodes
= 0;
286 dq
->dq_flags
&= ~DQ_INODS
;
287 dq
->dq_flags
|= DQ_MOD
;
293 cred
= kernproc
->p_ucred
;
294 if ((cred
->cr_uid
!= 0) || (p
->p_flag
& P_FORCEQUOTA
)) {
295 for (i
= 0; i
< MAXQUOTAS
; i
++) {
296 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
298 error
= hfs_chkiqchg(cp
, change
, cred
, i
);
304 if ((flags
& FORCE
) || error
== 0) {
305 for (i
= 0; i
< MAXQUOTAS
; i
++) {
306 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
308 while (dq
->dq_flags
& DQ_LOCK
) {
309 dq
->dq_flags
|= DQ_WANT
;
310 sleep((caddr_t
)dq
, PINOD
+1);
312 dq
->dq_curinodes
+= change
;
313 dq
->dq_flags
|= DQ_MOD
;
320 * Check for a valid change to a users allocation.
321 * Issue an error message if appropriate.
324 hfs_chkiqchg(cp
, change
, cred
, type
)
330 register struct dquot
*dq
= cp
->c_dquot
[type
];
331 long ncurinodes
= dq
->dq_curinodes
+ change
;
332 struct vnode
*vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
335 * If user would exceed their hard limit, disallow cnode allocation.
337 if (ncurinodes
>= dq
->dq_ihardlimit
&& dq
->dq_ihardlimit
) {
338 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
339 cp
->c_uid
== cred
->cr_uid
) {
341 printf("\n%s: write failed, %s cnode limit reached\n",
342 vp
->v_mount
->mnt_stat
.f_mntonname
,
345 dq
->dq_flags
|= DQ_INODS
;
350 * If user is over their soft limit for too long, disallow cnode
351 * allocation. Reset time limit as they cross their soft limit.
353 if (ncurinodes
>= dq
->dq_isoftlimit
&& dq
->dq_isoftlimit
) {
354 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
) {
355 dq
->dq_itime
= time
.tv_sec
+
356 VFSTOHFS(vp
->v_mount
)->hfs_qfiles
[type
].qf_itime
;
358 if (cp
->c_uid
== cred
->cr_uid
)
359 printf("\n%s: warning, %s %s\n",
360 vp
->v_mount
->mnt_stat
.f_mntonname
,
361 quotatypes
[type
], "cnode quota exceeded");
365 if (time
.tv_sec
> dq
->dq_itime
) {
366 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
367 cp
->c_uid
== cred
->cr_uid
) {
369 printf("\n%s: write failed, %s %s\n",
370 vp
->v_mount
->mnt_stat
.f_mntonname
,
372 "cnode quota exceeded for too long");
374 dq
->dq_flags
|= DQ_INODS
;
384 * On filesystems with quotas enabled, it is an error for a file to change
385 * size and not to have a dquot structure associated with it.
389 register struct cnode
*cp
;
391 struct vnode
*vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
392 struct hfsmount
*hfsmp
= VFSTOHFS(vp
->v_mount
);
395 for (i
= 0; i
< MAXQUOTAS
; i
++) {
396 if (hfsmp
->hfs_qfiles
[i
].qf_vp
== NULLVP
||
397 (hfsmp
->hfs_qfiles
[i
].qf_qflags
& (QTF_OPENING
|QTF_CLOSING
)))
399 if (cp
->c_dquot
[i
] == NODQUOT
) {
400 vprint("chkdquot: missing dquot", vp
);
401 panic("missing dquot");
408 * Code to process quotactl commands.
412 * Q_QUOTAON - set up a quota file for a particular file system.
415 hfs_quotaon(p
, mp
, type
, fname
, segflg
)
422 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
423 struct vnode
*vp
, **vpp
;
424 struct vnode
*nextvp
;
429 vpp
= &hfsmp
->hfs_qfiles
[type
].qf_vp
;
430 NDINIT(&nd
, LOOKUP
, FOLLOW
, segflg
, fname
, p
);
431 if (error
= vn_open(&nd
, FREAD
|FWRITE
, 0))
434 VOP_UNLOCK(vp
, 0, p
);
435 if (vp
->v_type
!= VREG
) {
436 (void) vn_close(vp
, FREAD
|FWRITE
, p
->p_ucred
, p
);
440 hfs_quotaoff(p
, mp
, type
);
441 hfsmp
->hfs_qfiles
[type
].qf_qflags
|= QTF_OPENING
;
442 mp
->mnt_flag
|= MNT_QUOTA
;
443 vp
->v_flag
|= VNOFLUSH
;
446 * Save the credential of the process that turned on quotas.
449 hfsmp
->hfs_qfiles
[type
].qf_cred
= p
->p_ucred
;
450 /* Finish initializing the quota file */
451 if (error
= dqfileopen(&hfsmp
->hfs_qfiles
[type
], type
))
454 * Search vnodes associated with this mount point,
455 * adding references to quota file being opened.
456 * NB: only need to add dquot's for cnodes being modified.
459 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
460 nextvp
= vp
->v_mntvnodes
.le_next
;
461 if (vp
->v_writecount
== 0)
463 if (vget(vp
, LK_EXCLUSIVE
, p
))
465 if (error
= hfs_getinoquota(VTOC(vp
))) {
470 if (vp
->v_mntvnodes
.le_next
!= nextvp
|| vp
->v_mount
!= mp
)
474 hfsmp
->hfs_qfiles
[type
].qf_qflags
&= ~QTF_OPENING
;
476 hfs_quotaoff(p
, mp
, type
);
481 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
484 hfs_quotaoff(p
, mp
, type
)
490 struct vnode
*qvp
, *nextvp
;
491 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
497 if ((qvp
= hfsmp
->hfs_qfiles
[type
].qf_vp
) == NULLVP
)
499 hfsmp
->hfs_qfiles
[type
].qf_qflags
|= QTF_CLOSING
;
502 * Sync out any orpaned dirty dquot entries.
504 dqsync_orphans(&hfsmp
->hfs_qfiles
[type
]);
507 * Search vnodes associated with this mount point,
508 * deleting any references to quota file being closed.
511 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
512 nextvp
= vp
->v_mntvnodes
.le_next
;
513 if (vget(vp
, LK_EXCLUSIVE
, p
))
516 dq
= cp
->c_dquot
[type
];
517 cp
->c_dquot
[type
] = NODQUOT
;
520 if (vp
->v_mntvnodes
.le_next
!= nextvp
|| vp
->v_mount
!= mp
)
524 /* Finish tearing down the quota file */
525 dqfileclose(&hfsmp
->hfs_qfiles
[type
], type
);
526 qvp
->v_flag
&= ~VNOFLUSH
;
527 error
= vn_close(qvp
, FREAD
|FWRITE
, p
->p_ucred
, p
);
528 hfsmp
->hfs_qfiles
[type
].qf_vp
= NULLVP
;
529 cred
= hfsmp
->hfs_qfiles
[type
].qf_cred
;
530 if (cred
!= NOCRED
) {
531 hfsmp
->hfs_qfiles
[type
].qf_cred
= NOCRED
;
534 hfsmp
->hfs_qfiles
[type
].qf_qflags
&= ~QTF_CLOSING
;
535 for (type
= 0; type
< MAXQUOTAS
; type
++)
536 if (hfsmp
->hfs_qfiles
[type
].qf_vp
!= NULLVP
)
538 if (type
== MAXQUOTAS
)
539 mp
->mnt_flag
&= ~MNT_QUOTA
;
544 * Q_GETQUOTA - return current values in a dqblk structure.
547 hfs_getquota(mp
, id
, type
, addr
)
556 if (error
= dqget(NULLVP
, id
, &VFSTOHFS(mp
)->hfs_qfiles
[type
], type
, &dq
))
558 error
= copyout((caddr_t
)&dq
->dq_dqb
, addr
, sizeof (struct dqblk
));
564 * Q_SETQUOTA - assign an entire dqblk structure.
567 hfs_setquota(mp
, id
, type
, addr
)
573 register struct dquot
*dq
;
575 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
579 if (error
= copyin(addr
, (caddr_t
)&newlim
, sizeof (struct dqblk
)))
581 if (error
= dqget(NULLVP
, id
, &hfsmp
->hfs_qfiles
[type
], type
, &ndq
))
584 while (dq
->dq_flags
& DQ_LOCK
) {
585 dq
->dq_flags
|= DQ_WANT
;
586 sleep((caddr_t
)dq
, PINOD
+1);
589 * Copy all but the current values.
590 * Reset time limit if previously had no soft limit or were
591 * under it, but now have a soft limit and are over it.
593 newlim
.dqb_curbytes
= dq
->dq_curbytes
;
594 newlim
.dqb_curinodes
= dq
->dq_curinodes
;
595 if (dq
->dq_id
!= 0) {
596 newlim
.dqb_btime
= dq
->dq_btime
;
597 newlim
.dqb_itime
= dq
->dq_itime
;
599 if (newlim
.dqb_bsoftlimit
&&
600 dq
->dq_curbytes
>= newlim
.dqb_bsoftlimit
&&
601 (dq
->dq_bsoftlimit
== 0 || dq
->dq_curbytes
< dq
->dq_bsoftlimit
))
602 newlim
.dqb_btime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_btime
;
603 if (newlim
.dqb_isoftlimit
&&
604 dq
->dq_curinodes
>= newlim
.dqb_isoftlimit
&&
605 (dq
->dq_isoftlimit
== 0 || dq
->dq_curinodes
< dq
->dq_isoftlimit
))
606 newlim
.dqb_itime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_itime
;
608 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
)
609 dq
->dq_flags
&= ~DQ_BLKS
;
610 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
611 dq
->dq_flags
&= ~DQ_INODS
;
612 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
613 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
614 dq
->dq_flags
|= DQ_FAKE
;
616 dq
->dq_flags
&= ~DQ_FAKE
;
617 dq
->dq_flags
|= DQ_MOD
;
623 * Q_SETUSE - set current cnode and byte usage.
626 hfs_setuse(mp
, id
, type
, addr
)
632 register struct dquot
*dq
;
633 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
638 if (error
= copyin(addr
, (caddr_t
)&usage
, sizeof (struct dqblk
)))
640 if (error
= dqget(NULLVP
, id
, &hfsmp
->hfs_qfiles
[type
], type
, &ndq
))
643 while (dq
->dq_flags
& DQ_LOCK
) {
644 dq
->dq_flags
|= DQ_WANT
;
645 sleep((caddr_t
)dq
, PINOD
+1);
648 * Reset time limit if have a soft limit and were
649 * previously under it, but are now over it.
651 if (dq
->dq_bsoftlimit
&& dq
->dq_curbytes
< dq
->dq_bsoftlimit
&&
652 usage
.dqb_curbytes
>= dq
->dq_bsoftlimit
)
653 dq
->dq_btime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_btime
;
654 if (dq
->dq_isoftlimit
&& dq
->dq_curinodes
< dq
->dq_isoftlimit
&&
655 usage
.dqb_curinodes
>= dq
->dq_isoftlimit
)
656 dq
->dq_itime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_itime
;
657 dq
->dq_curbytes
= usage
.dqb_curbytes
;
658 dq
->dq_curinodes
= usage
.dqb_curinodes
;
659 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
)
660 dq
->dq_flags
&= ~DQ_BLKS
;
661 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
662 dq
->dq_flags
&= ~DQ_INODS
;
663 dq
->dq_flags
|= DQ_MOD
;
669 * Q_SYNC - sync quota files to disk.
675 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
676 struct proc
*p
= current_proc(); /* XXX */
677 struct vnode
*vp
, *nextvp
;
682 * Check if the mount point has any quotas.
683 * If not, simply return.
685 for (i
= 0; i
< MAXQUOTAS
; i
++)
686 if (hfsmp
->hfs_qfiles
[i
].qf_vp
!= NULLVP
)
692 * Sync out any orpaned dirty dquot entries.
694 for (i
= 0; i
< MAXQUOTAS
; i
++)
695 if (hfsmp
->hfs_qfiles
[i
].qf_vp
!= NULLVP
)
696 dqsync_orphans(&hfsmp
->hfs_qfiles
[i
]);
699 * Search vnodes associated with this mount point,
700 * synchronizing any modified dquot structures.
702 simple_lock(&mntvnode_slock
);
704 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
705 if (vp
->v_mount
!= mp
)
708 nextvp
= vp
->v_mntvnodes
.le_next
;
709 simple_lock(&vp
->v_interlock
);
710 simple_unlock(&mntvnode_slock
);
712 error
= vget(vp
, LK_EXCLUSIVE
| LK_NOWAIT
| LK_INTERLOCK
, p
);
714 simple_lock(&mntvnode_slock
);
720 // Make sure that this is really an hfs vnode.
722 if ( vp
->v_mount
!= mp
723 || vp
->v_type
== VNON
724 || vp
->v_tag
!= VT_HFS
725 || VTOC(vp
) == NULL
) {
728 simple_lock(&mntvnode_slock
);
732 for (i
= 0; i
< MAXQUOTAS
; i
++) {
733 dq
= VTOC(vp
)->c_dquot
[i
];
734 if (dq
!= NODQUOT
&& (dq
->dq_flags
& DQ_MOD
))
738 simple_lock(&mntvnode_slock
);
739 if (vp
->v_mntvnodes
.le_next
!= nextvp
)
742 simple_unlock(&mntvnode_slock
);
747 * Q_QUOTASTAT - get quota on/off status
750 hfs_quotastat(mp
, type
, addr
)
755 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
759 if ((mp
->mnt_flag
& MNT_QUOTA
) && (hfsmp
->hfs_qfiles
[type
].qf_vp
!= NULLVP
))
760 qstat
= 1; /* quotas are on for this type */
762 qstat
= 0; /* quotas are off for this type */
764 error
= copyout ((caddr_t
)&qstat
, addr
, sizeof(qstat
));