]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_quota.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_quota.c
CommitLineData
9bccf70c 1/*
b0d623f7 2 * Copyright (c) 2002-2008 Apple Inc. All rights reserved.
9bccf70c 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
9bccf70c 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
9bccf70c
A
27 */
28/*
29 * Copyright (c) 1982, 1986, 1990, 1993, 1995
30 * The Regents of the University of California. All rights reserved.
31 *
32 * This code is derived from software contributed to Berkeley by
33 * Robert Elz at The University of Melbourne.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 *
63 * @(#)hfs_quota.c
64 * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
65 */
66
67#include <sys/param.h>
68#include <sys/kernel.h>
69#include <sys/systm.h>
70#include <sys/mount.h>
9bccf70c
A
71#include <sys/malloc.h>
72#include <sys/file.h>
73#include <sys/proc.h>
91447636 74#include <sys/kauth.h>
9bccf70c 75#include <sys/vnode.h>
2d21ac55 76#include <sys/vnode_internal.h>
9bccf70c 77#include <sys/quota.h>
91447636
A
78#include <sys/proc_internal.h>
79#include <kern/kalloc.h>
9bccf70c
A
80
81#include <hfs/hfs.h>
82#include <hfs/hfs_cnode.h>
83#include <hfs/hfs_quota.h>
84#include <hfs/hfs_mount.h>
85
2d21ac55 86
9bccf70c
A
87/*
88 * Quota name to error message mapping.
89 */
91447636 90#if 0
9bccf70c 91static char *quotatypes[] = INITQFNAMES;
91447636 92#endif
9bccf70c
A
93
94/*
95 * Set up the quotas for a cnode.
96 *
97 * This routine completely defines the semantics of quotas.
98 * If other criterion want to be used to establish quotas, the
99 * MAXQUOTAS value in quotas.h should be increased, and the
100 * additional dquots set up here.
101 */
102int
103hfs_getinoquota(cp)
104 register struct cnode *cp;
105{
106 struct hfsmount *hfsmp;
107 struct vnode *vp;
108 int error;
ebb1b9f4 109 int drop_usrquota = false;
9bccf70c
A
110
111 vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
91447636 112 hfsmp = VTOHFS(vp);
9bccf70c
A
113 /*
114 * Set up the user quota based on file uid.
115 * EINVAL means that quotas are not enabled.
116 */
ebb1b9f4
A
117 if (cp->c_dquot[USRQUOTA] == NODQUOT) {
118 error = dqget(cp->c_uid, &hfsmp->hfs_qfiles[USRQUOTA], USRQUOTA, &cp->c_dquot[USRQUOTA]);
119 if ((error != 0) && (error != EINVAL)) {
120 return error;
121 } else if (error == 0) {
122 drop_usrquota = true;
123 }
124 }
125
9bccf70c
A
126 /*
127 * Set up the group quota based on file gid.
128 * EINVAL means that quotas are not enabled.
129 */
ebb1b9f4
A
130 if (cp->c_dquot[GRPQUOTA] == NODQUOT) {
131 error = dqget(cp->c_gid, &hfsmp->hfs_qfiles[GRPQUOTA], GRPQUOTA, &cp->c_dquot[GRPQUOTA]);
132 if ((error != 0) && (error != EINVAL)) {
133 if (drop_usrquota == true) {
134 dqrele(cp->c_dquot[USRQUOTA]);
135 cp->c_dquot[USRQUOTA] = NODQUOT;
136 }
137 return error;
138 }
139 }
140
9bccf70c
A
141 return (0);
142}
143
144/*
145 * Update disk usage, and take corrective action.
146 */
147int
148hfs_chkdq(cp, change, cred, flags)
149 register struct cnode *cp;
150 int64_t change;
91447636 151 kauth_cred_t cred;
9bccf70c
A
152 int flags;
153{
154 register struct dquot *dq;
155 register int i;
156 int64_t ncurbytes;
d7e50217 157 int error=0;
9bccf70c
A
158 struct proc *p;
159
160#if DIAGNOSTIC
161 if ((flags & CHOWN) == 0)
162 hfs_chkdquot(cp);
163#endif
164 if (change == 0)
165 return (0);
166 if (change < 0) {
167 for (i = 0; i < MAXQUOTAS; i++) {
168 if ((dq = cp->c_dquot[i]) == NODQUOT)
169 continue;
91447636
A
170 dqlock(dq);
171
9bccf70c
A
172 ncurbytes = dq->dq_curbytes + change;
173 if (ncurbytes >= 0)
174 dq->dq_curbytes = ncurbytes;
175 else
176 dq->dq_curbytes = 0;
177 dq->dq_flags &= ~DQ_BLKS;
178 dq->dq_flags |= DQ_MOD;
91447636
A
179
180 dqunlock(dq);
9bccf70c
A
181 }
182 return (0);
183 }
184 p = current_proc();
0c530ab8
A
185 /*
186 * This use of proc_ucred() is safe because kernproc credential never
187 * changes.
188 */
189 if (!IS_VALID_CRED(cred))
91447636
A
190 cred = proc_ucred(kernproc);
191 if (suser(cred, NULL) || proc_forcequota(p)) {
9bccf70c
A
192 for (i = 0; i < MAXQUOTAS; i++) {
193 if ((dq = cp->c_dquot[i]) == NODQUOT)
194 continue;
d7e50217
A
195 error = hfs_chkdqchg(cp, change, cred, i);
196 if (error) {
197 break;
198 }
9bccf70c
A
199 }
200 }
d7e50217
A
201 if ((flags & FORCE) || error == 0) {
202 for (i = 0; i < MAXQUOTAS; i++) {
203 if ((dq = cp->c_dquot[i]) == NODQUOT)
204 continue;
91447636
A
205 dqlock(dq);
206
d7e50217
A
207 dq->dq_curbytes += change;
208 dq->dq_flags |= DQ_MOD;
91447636
A
209
210 dqunlock(dq);
9bccf70c 211 }
9bccf70c 212 }
d7e50217 213 return (error);
9bccf70c
A
214}
215
216/*
217 * Check for a valid change to a users allocation.
218 * Issue an error message if appropriate.
219 */
220int
221hfs_chkdqchg(cp, change, cred, type)
222 struct cnode *cp;
223 int64_t change;
91447636 224 kauth_cred_t cred;
9bccf70c
A
225 int type;
226{
227 register struct dquot *dq = cp->c_dquot[type];
91447636 228 u_int64_t ncurbytes;
9bccf70c 229 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
91447636
A
230
231 dqlock(dq);
232
233 ncurbytes = dq->dq_curbytes + change;
9bccf70c
A
234 /*
235 * If user would exceed their hard limit, disallow space allocation.
236 */
237 if (ncurbytes >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
238 if ((dq->dq_flags & DQ_BLKS) == 0 &&
91447636 239 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 240#if 0
b0d623f7 241 printf("\nhfs: write failed, %s disk limit reached\n",
9bccf70c
A
242 quotatypes[type]);
243#endif
244 dq->dq_flags |= DQ_BLKS;
245 }
91447636
A
246 dqunlock(dq);
247
9bccf70c
A
248 return (EDQUOT);
249 }
250 /*
251 * If user is over their soft limit for too long, disallow space
252 * allocation. Reset time limit as they cross their soft limit.
253 */
254 if (ncurbytes >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
91447636
A
255 struct timeval tv;
256
257 microuptime(&tv);
9bccf70c 258 if (dq->dq_curbytes < dq->dq_bsoftlimit) {
91447636
A
259 dq->dq_btime = tv.tv_sec +
260 VTOHFS(vp)->hfs_qfiles[type].qf_btime;
d7e50217 261#if 0
91447636 262 if (cp->c_uid == kauth_cred_getuid(cred))
b0d623f7 263 printf("\nhfs: warning, %s %s\n",
9bccf70c
A
264 quotatypes[type], "disk quota exceeded");
265#endif
91447636
A
266 dqunlock(dq);
267
9bccf70c
A
268 return (0);
269 }
b0d623f7 270 if (tv.tv_sec > (time_t)dq->dq_btime) {
9bccf70c 271 if ((dq->dq_flags & DQ_BLKS) == 0 &&
91447636 272 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 273#if 0
b0d623f7 274 printf("\nhfs: write failed, %s %s\n",
9bccf70c
A
275 quotatypes[type],
276 "disk quota exceeded for too long");
277#endif
278 dq->dq_flags |= DQ_BLKS;
279 }
91447636
A
280 dqunlock(dq);
281
9bccf70c
A
282 return (EDQUOT);
283 }
284 }
91447636
A
285 dqunlock(dq);
286
9bccf70c
A
287 return (0);
288}
289
290/*
291 * Check the inode limit, applying corrective action.
292 */
293int
294hfs_chkiq(cp, change, cred, flags)
295 register struct cnode *cp;
b0d623f7 296 int32_t change;
91447636 297 kauth_cred_t cred;
9bccf70c
A
298 int flags;
299{
300 register struct dquot *dq;
301 register int i;
d7e50217 302 int ncurinodes, error=0;
9bccf70c
A
303 struct proc *p;
304
305#if DIAGNOSTIC
306 if ((flags & CHOWN) == 0)
307 hfs_chkdquot(cp);
308#endif
309 if (change == 0)
310 return (0);
311 if (change < 0) {
312 for (i = 0; i < MAXQUOTAS; i++) {
313 if ((dq = cp->c_dquot[i]) == NODQUOT)
314 continue;
91447636
A
315 dqlock(dq);
316
9bccf70c
A
317 ncurinodes = dq->dq_curinodes + change;
318 if (ncurinodes >= 0)
319 dq->dq_curinodes = ncurinodes;
320 else
321 dq->dq_curinodes = 0;
322 dq->dq_flags &= ~DQ_INODS;
323 dq->dq_flags |= DQ_MOD;
91447636
A
324
325 dqunlock(dq);
9bccf70c
A
326 }
327 return (0);
328 }
329 p = current_proc();
0c530ab8
A
330 /*
331 * This use of proc_ucred() is safe because kernproc credential never
332 * changes.
333 */
334 if (!IS_VALID_CRED(cred))
91447636
A
335 cred = proc_ucred(kernproc);
336 if (suser(cred, NULL) || proc_forcequota(p)) {
9bccf70c
A
337 for (i = 0; i < MAXQUOTAS; i++) {
338 if ((dq = cp->c_dquot[i]) == NODQUOT)
339 continue;
d7e50217
A
340 error = hfs_chkiqchg(cp, change, cred, i);
341 if (error) {
342 break;
343 }
9bccf70c
A
344 }
345 }
d7e50217
A
346 if ((flags & FORCE) || error == 0) {
347 for (i = 0; i < MAXQUOTAS; i++) {
348 if ((dq = cp->c_dquot[i]) == NODQUOT)
349 continue;
91447636
A
350 dqlock(dq);
351
d7e50217
A
352 dq->dq_curinodes += change;
353 dq->dq_flags |= DQ_MOD;
91447636
A
354
355 dqunlock(dq);
9bccf70c 356 }
9bccf70c 357 }
d7e50217 358 return (error);
9bccf70c
A
359}
360
b0d623f7
A
361
362/*
363 * Check to see if a change to a user's allocation should be permitted or not.
364 * Issue an error message if it should not be permitted. Return 0 if
365 * it should be allowed.
366 */
367int hfs_isiqchg_allowed(dq, hfsmp, change, cred, type, uid)
368 struct dquot* dq;
369 struct hfsmount* hfsmp;
370 int32_t change;
371 kauth_cred_t cred;
372 int type;
373 uid_t uid;
374{
375 u_int32_t ncurinodes;
376
377 dqlock(dq);
378
379 ncurinodes = dq->dq_curinodes + change;
380 /*
381 * If user would exceed their hard limit, disallow cnode allocation.
382 */
383 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
384 if ((dq->dq_flags & DQ_INODS) == 0 &&
385 uid == kauth_cred_getuid(cred)) {
386 dq->dq_flags |= DQ_INODS;
387 }
388 dqunlock(dq);
389
390 return (EDQUOT);
391 }
392 /*
393 * If user is over their soft limit for too long, disallow cnode
394 * allocation. Reset time limit as they cross their soft limit.
395 */
396 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
397 struct timeval tv;
398
399 microuptime(&tv);
400 if (dq->dq_curinodes < dq->dq_isoftlimit) {
401 dq->dq_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime;
402 dqunlock(dq);
403 return (0);
404 }
405 if (tv.tv_sec > (time_t)dq->dq_itime) {
406 if (((dq->dq_flags & DQ_INODS) == 0) &&
407 (uid == kauth_cred_getuid(cred))) {
408 dq->dq_flags |= DQ_INODS;
409 }
410 dqunlock(dq);
411
412 return (EDQUOT);
413 }
414 }
415 dqunlock(dq);
416
417 return (0);
418}
419
420
9bccf70c
A
421/*
422 * Check for a valid change to a users allocation.
423 * Issue an error message if appropriate.
424 */
425int
426hfs_chkiqchg(cp, change, cred, type)
427 struct cnode *cp;
b0d623f7 428 int32_t change;
91447636 429 kauth_cred_t cred;
9bccf70c
A
430 int type;
431{
432 register struct dquot *dq = cp->c_dquot[type];
b0d623f7 433 u_int32_t ncurinodes;
9bccf70c
A
434 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
435
91447636
A
436 dqlock(dq);
437
438 ncurinodes = dq->dq_curinodes + change;
9bccf70c
A
439 /*
440 * If user would exceed their hard limit, disallow cnode allocation.
441 */
442 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
443 if ((dq->dq_flags & DQ_INODS) == 0 &&
91447636 444 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 445#if 0
b0d623f7 446 printf("\nhfs: write failed, %s cnode limit reached\n",
9bccf70c
A
447 quotatypes[type]);
448#endif
449 dq->dq_flags |= DQ_INODS;
450 }
91447636
A
451 dqunlock(dq);
452
9bccf70c
A
453 return (EDQUOT);
454 }
455 /*
456 * If user is over their soft limit for too long, disallow cnode
457 * allocation. Reset time limit as they cross their soft limit.
458 */
459 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
91447636
A
460 struct timeval tv;
461
462 microuptime(&tv);
9bccf70c 463 if (dq->dq_curinodes < dq->dq_isoftlimit) {
91447636
A
464 dq->dq_itime = tv.tv_sec +
465 VTOHFS(vp)->hfs_qfiles[type].qf_itime;
d7e50217 466#if 0
91447636 467 if (cp->c_uid == kauth_cred_getuid(cred))
b0d623f7 468 printf("\nhfs: warning, %s %s\n",
9bccf70c
A
469 quotatypes[type], "cnode quota exceeded");
470#endif
91447636
A
471 dqunlock(dq);
472
9bccf70c
A
473 return (0);
474 }
b0d623f7 475 if (tv.tv_sec > (time_t)dq->dq_itime) {
9bccf70c 476 if ((dq->dq_flags & DQ_INODS) == 0 &&
91447636 477 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 478#if 0
b0d623f7 479 printf("\nhfs: write failed, %s %s\n",
9bccf70c
A
480 quotatypes[type],
481 "cnode quota exceeded for too long");
482#endif
483 dq->dq_flags |= DQ_INODS;
484 }
91447636
A
485 dqunlock(dq);
486
9bccf70c
A
487 return (EDQUOT);
488 }
489 }
91447636
A
490 dqunlock(dq);
491
9bccf70c
A
492 return (0);
493}
494
495#if DIAGNOSTIC
496/*
497 * On filesystems with quotas enabled, it is an error for a file to change
498 * size and not to have a dquot structure associated with it.
499 */
500void
501hfs_chkdquot(cp)
502 register struct cnode *cp;
503{
504 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
91447636 505 struct hfsmount *hfsmp = VTOHFS(vp);
9bccf70c
A
506 register int i;
507
508 for (i = 0; i < MAXQUOTAS; i++) {
91447636 509 if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP)
9bccf70c
A
510 continue;
511 if (cp->c_dquot[i] == NODQUOT) {
512 vprint("chkdquot: missing dquot", vp);
513 panic("missing dquot");
514 }
515 }
516}
517#endif
518
519/*
520 * Code to process quotactl commands.
521 */
522
523/*
524 * Q_QUOTAON - set up a quota file for a particular file system.
525 */
91447636
A
526struct hfs_quotaon_cargs {
527 int error;
528};
529
530static int
531hfs_quotaon_callback(struct vnode *vp, void *cargs)
532{
533 struct hfs_quotaon_cargs *args;
534
535 args = (struct hfs_quotaon_cargs *)cargs;
536
537 args->error = hfs_getinoquota(VTOC(vp));
538 if (args->error)
539 return (VNODE_RETURNED_DONE);
540
541 return (VNODE_RETURNED);
542}
543
9bccf70c 544int
91447636 545hfs_quotaon(p, mp, type, fnamep)
9bccf70c
A
546 struct proc *p;
547 struct mount *mp;
548 register int type;
91447636 549 caddr_t fnamep;
9bccf70c
A
550{
551 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636
A
552 struct quotafile *qfp;
553 struct vnode *vp;
554 int error = 0;
555 struct hfs_quotaon_cargs args;
9bccf70c 556
2d21ac55
A
557 /* Finish setting up quota structures. */
558 dqhashinit();
559
91447636
A
560 qfp = &hfsmp->hfs_qfiles[type];
561
562 if ( (qf_get(qfp, QTF_OPENING)) )
563 return (0);
564
565 error = vnode_open(fnamep, FREAD|FWRITE, 0, 0, &vp, NULL);
566 if (error) {
567 goto out;
9bccf70c 568 }
91447636
A
569 if (!vnode_isreg(vp)) {
570 (void) vnode_close(vp, FREAD|FWRITE, NULL);
571 error = EACCES;
572 goto out;
573 }
2d21ac55 574 vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA));
39236c6e 575 hfs_lock_mount (hfsmp);
2d21ac55 576 hfsmp->hfs_flags |= HFS_QUOTAS;
39236c6e 577 hfs_unlock_mount (hfsmp);
91447636 578 vnode_setnoflush(vp);
9bccf70c
A
579 /*
580 * Save the credential of the process that turned on quotas.
581 */
91447636
A
582 qfp->qf_cred = kauth_cred_proc_ref(p);
583 qfp->qf_vp = vp;
584 /*
585 * Finish initializing the quota file
586 */
587 error = dqfileopen(qfp, type);
588 if (error) {
589 (void) vnode_close(vp, FREAD|FWRITE, NULL);
590
0c530ab8
A
591 if (IS_VALID_CRED(qfp->qf_cred))
592 kauth_cred_unref(&qfp->qf_cred);
91447636
A
593 qfp->qf_vp = NULLVP;
594 goto out;
595 }
596 qf_put(qfp, QTF_OPENING);
597
9bccf70c
A
598 /*
599 * Search vnodes associated with this mount point,
600 * adding references to quota file being opened.
601 * NB: only need to add dquot's for cnodes being modified.
91447636
A
602 *
603 * hfs_quota_callback will be called for each vnode open for
604 * 'write' (VNODE_WRITEABLE) hung off of this mount point
605 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
606 * properly referenced and unreferenced around the callback
9bccf70c 607 */
91447636
A
608 args.error = 0;
609
610 vnode_iterate(mp, VNODE_WRITEABLE | VNODE_WAIT, hfs_quotaon_callback, (void *)&args);
611
612 error = args.error;
613
614 if (error) {
9bccf70c 615 hfs_quotaoff(p, mp, type);
91447636
A
616 }
617 return (error);
618
619out:
620 qf_put(qfp, QTF_OPENING);
621
9bccf70c
A
622 return (error);
623}
624
91447636 625
9bccf70c
A
626/*
627 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
628 */
91447636
A
629struct hfs_quotaoff_cargs {
630 int type;
631};
632
633static int
634hfs_quotaoff_callback(struct vnode *vp, void *cargs)
635{
636 struct hfs_quotaoff_cargs *args;
637 struct cnode *cp;
638 struct dquot *dq;
639
640 args = (struct hfs_quotaoff_cargs *)cargs;
641
642 cp = VTOC(vp);
643
644 dq = cp->c_dquot[args->type];
645 cp->c_dquot[args->type] = NODQUOT;
646
647 dqrele(dq);
648
649 return (VNODE_RETURNED);
650}
651
9bccf70c 652int
91447636 653hfs_quotaoff(__unused struct proc *p, struct mount *mp, register int type)
9bccf70c 654{
91447636 655 struct vnode *qvp;
9bccf70c 656 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636 657 struct quotafile *qfp;
9bccf70c 658 int error;
91447636
A
659 struct hfs_quotaoff_cargs args;
660
2d21ac55
A
661 /*
662 * If quotas haven't been initialized, there's no work to be done.
663 */
664 if (!dqisinitialized())
665 return (0);
666
91447636 667 qfp = &hfsmp->hfs_qfiles[type];
9bccf70c 668
91447636
A
669 if ( (qf_get(qfp, QTF_CLOSING)) )
670 return (0);
671 qvp = qfp->qf_vp;
d7e50217
A
672
673 /*
674 * Sync out any orpaned dirty dquot entries.
675 */
91447636 676 dqsync_orphans(qfp);
d7e50217 677
9bccf70c
A
678 /*
679 * Search vnodes associated with this mount point,
680 * deleting any references to quota file being closed.
b0d623f7 681 *
91447636
A
682 * hfs_quotaoff_callback will be called for each vnode
683 * hung off of this mount point
684 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
685 * properly referenced and unreferenced around the callback
9bccf70c 686 */
91447636
A
687 args.type = type;
688
689 vnode_iterate(mp, VNODE_WAIT, hfs_quotaoff_callback, (void *)&args);
690
9bccf70c
A
691 dqflush(qvp);
692 /* Finish tearing down the quota file */
91447636
A
693 dqfileclose(qfp, type);
694
695 vnode_clearnoflush(qvp);
696 error = vnode_close(qvp, FREAD|FWRITE, NULL);
697
698 qfp->qf_vp = NULLVP;
0c530ab8
A
699
700 if (IS_VALID_CRED(qfp->qf_cred))
701 kauth_cred_unref(&qfp->qf_cred);
9bccf70c
A
702 for (type = 0; type < MAXQUOTAS; type++)
703 if (hfsmp->hfs_qfiles[type].qf_vp != NULLVP)
704 break;
2d21ac55
A
705 if (type == MAXQUOTAS) {
706 vfs_clearflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA));
39236c6e 707 hfs_lock_mount (hfsmp);
2d21ac55 708 hfsmp->hfs_flags &= ~HFS_QUOTAS;
39236c6e 709 hfs_unlock_mount (hfsmp);
2d21ac55 710 }
91447636
A
711
712 qf_put(qfp, QTF_CLOSING);
713
9bccf70c
A
714 return (error);
715}
716
b0d623f7
A
717/*
718 * hfs_quotacheck - checks quotas mountwide for a hypothetical situation. It probes
719 * the quota data structures to see if adding an inode would be allowed or not. If it
720 * will be allowed, the change is made. Otherwise, it reports an error back out so the
721 * caller will know not to proceed with inode allocation in the HFS Catalog.
722 *
723 * Note that this function ONLY tests for addition of inodes, not subtraction.
724 */
725int hfs_quotacheck(hfsmp, change, uid, gid, cred)
726 struct hfsmount *hfsmp;
727 int change;
728 uid_t uid;
729 gid_t gid;
730 kauth_cred_t cred;
731{
732 struct dquot *dq = NULL;
733 struct proc *p;
734 int error = 0;
735 int i;
736 id_t id = uid;
737
738 p = current_proc();
739 if (!IS_VALID_CRED(cred)) {
740 /* This use of proc_ucred() is safe because kernproc credential never changes */
741 cred = proc_ucred(kernproc);
742 }
743
744 if (suser(cred, NULL) || proc_forcequota(p)) {
745 for (i = 0; i < MAXQUOTAS; i++) {
746 /* Select if user or group id should be used */
747 if (i == USRQUOTA)
748 id = uid;
749 else if (i == GRPQUOTA)
750 id = gid;
751
752 error = dqget(id, &hfsmp->hfs_qfiles[i], i, &dq);
753 if (error && (error != EINVAL))
754 break;
755
756 error = 0;
757 if (dq == NODQUOT)
758 continue;
759
760 /* Check quota information */
761 error = hfs_isiqchg_allowed(dq, hfsmp, change, cred, i, id);
762 if (error) {
763 dqrele(dq);
764 break;
765 }
766
767 dqlock(dq);
768 /* Update quota information */
769 dq->dq_curinodes += change;
770 dqunlock(dq);
771 dqrele(dq);
772 }
773 }
774
775 return error;
776}
777
778
9bccf70c
A
779/*
780 * Q_GETQUOTA - return current values in a dqblk structure.
781 */
782int
91447636 783hfs_getquota(mp, id, type, datap)
9bccf70c 784 struct mount *mp;
b0d623f7 785 u_int32_t id;
9bccf70c 786 int type;
91447636 787 caddr_t datap;
9bccf70c
A
788{
789 struct dquot *dq;
790 int error;
791
91447636
A
792 error = dqget(id, &VFSTOHFS(mp)->hfs_qfiles[type], type, &dq);
793 if (error)
9bccf70c 794 return (error);
91447636
A
795 dqlock(dq);
796
797 bcopy(&dq->dq_dqb, datap, sizeof(dq->dq_dqb));
798
799 dqunlock(dq);
800 dqrele(dq);
801
9bccf70c
A
802 return (error);
803}
804
805/*
806 * Q_SETQUOTA - assign an entire dqblk structure.
807 */
808int
91447636 809hfs_setquota(mp, id, type, datap)
9bccf70c 810 struct mount *mp;
b0d623f7 811 u_int32_t id;
9bccf70c 812 int type;
91447636 813 caddr_t datap;
9bccf70c 814{
91447636 815 struct dquot *dq;
9bccf70c 816 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636
A
817 struct dqblk * newlimp = (struct dqblk *) datap;
818 struct timeval tv;
9bccf70c
A
819 int error;
820
91447636
A
821 error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq);
822 if (error)
9bccf70c 823 return (error);
91447636
A
824 dqlock(dq);
825
9bccf70c
A
826 /*
827 * Copy all but the current values.
828 * Reset time limit if previously had no soft limit or were
829 * under it, but now have a soft limit and are over it.
830 */
91447636
A
831 newlimp->dqb_curbytes = dq->dq_curbytes;
832 newlimp->dqb_curinodes = dq->dq_curinodes;
9bccf70c 833 if (dq->dq_id != 0) {
91447636
A
834 newlimp->dqb_btime = dq->dq_btime;
835 newlimp->dqb_itime = dq->dq_itime;
9bccf70c 836 }
91447636
A
837 if (newlimp->dqb_bsoftlimit &&
838 dq->dq_curbytes >= newlimp->dqb_bsoftlimit &&
839 (dq->dq_bsoftlimit == 0 || dq->dq_curbytes < dq->dq_bsoftlimit)) {
840 microuptime(&tv);
841 newlimp->dqb_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime;
842 }
843 if (newlimp->dqb_isoftlimit &&
844 dq->dq_curinodes >= newlimp->dqb_isoftlimit &&
845 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) {
846 microuptime(&tv);
847 newlimp->dqb_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime;
848 }
849 bcopy(newlimp, &dq->dq_dqb, sizeof(dq->dq_dqb));
9bccf70c
A
850 if (dq->dq_curbytes < dq->dq_bsoftlimit)
851 dq->dq_flags &= ~DQ_BLKS;
852 if (dq->dq_curinodes < dq->dq_isoftlimit)
853 dq->dq_flags &= ~DQ_INODS;
854 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
855 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
856 dq->dq_flags |= DQ_FAKE;
857 else
858 dq->dq_flags &= ~DQ_FAKE;
859 dq->dq_flags |= DQ_MOD;
91447636
A
860
861 dqunlock(dq);
862 dqrele(dq);
863
9bccf70c
A
864 return (0);
865}
866
867/*
868 * Q_SETUSE - set current cnode and byte usage.
869 */
870int
91447636 871hfs_setuse(mp, id, type, datap)
9bccf70c 872 struct mount *mp;
b0d623f7 873 u_int32_t id;
9bccf70c 874 int type;
91447636 875 caddr_t datap;
9bccf70c 876{
9bccf70c 877 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636
A
878 struct dquot *dq;
879 struct timeval tv;
9bccf70c 880 int error;
91447636
A
881 struct dqblk *quotablkp = (struct dqblk *) datap;
882
883 error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq);
884 if (error)
885 return (error);
886 dqlock(dq);
9bccf70c 887
9bccf70c
A
888 /*
889 * Reset time limit if have a soft limit and were
890 * previously under it, but are now over it.
891 */
892 if (dq->dq_bsoftlimit && dq->dq_curbytes < dq->dq_bsoftlimit &&
91447636
A
893 quotablkp->dqb_curbytes >= dq->dq_bsoftlimit) {
894 microuptime(&tv);
895 dq->dq_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime;
896 }
9bccf70c 897 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
91447636
A
898 quotablkp->dqb_curinodes >= dq->dq_isoftlimit) {
899 microuptime(&tv);
900 dq->dq_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime;
901 }
902 dq->dq_curbytes = quotablkp->dqb_curbytes;
903 dq->dq_curinodes = quotablkp->dqb_curinodes;
9bccf70c
A
904 if (dq->dq_curbytes < dq->dq_bsoftlimit)
905 dq->dq_flags &= ~DQ_BLKS;
906 if (dq->dq_curinodes < dq->dq_isoftlimit)
907 dq->dq_flags &= ~DQ_INODS;
908 dq->dq_flags |= DQ_MOD;
91447636
A
909
910 dqunlock(dq);
911 dqrele(dq);
912
9bccf70c
A
913 return (0);
914}
915
91447636 916
9bccf70c
A
917/*
918 * Q_SYNC - sync quota files to disk.
919 */
91447636
A
920static int
921hfs_qsync_callback(struct vnode *vp, __unused void *cargs)
922{
923 struct cnode *cp;
924 struct dquot *dq;
925 int i;
926
927 cp = VTOC(vp);
928
929 for (i = 0; i < MAXQUOTAS; i++) {
930 dq = cp->c_dquot[i];
931 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
932 dqsync(dq);
933 }
934 return (VNODE_RETURNED);
935}
936
9bccf70c
A
937int
938hfs_qsync(mp)
939 struct mount *mp;
940{
941 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636 942 int i;
9bccf70c 943
2d21ac55
A
944 if (!dqisinitialized())
945 return (0);
946
9bccf70c
A
947 /*
948 * Check if the mount point has any quotas.
949 * If not, simply return.
950 */
951 for (i = 0; i < MAXQUOTAS; i++)
952 if (hfsmp->hfs_qfiles[i].qf_vp != NULLVP)
953 break;
954 if (i == MAXQUOTAS)
955 return (0);
d7e50217
A
956
957 /*
958 * Sync out any orpaned dirty dquot entries.
959 */
960 for (i = 0; i < MAXQUOTAS; i++)
961 if (hfsmp->hfs_qfiles[i].qf_vp != NULLVP)
962 dqsync_orphans(&hfsmp->hfs_qfiles[i]);
963
9bccf70c
A
964 /*
965 * Search vnodes associated with this mount point,
966 * synchronizing any modified dquot structures.
91447636
A
967 *
968 * hfs_qsync_callback will be called for each vnode
969 * hung off of this mount point
970 * the vnode will be
971 * properly referenced and unreferenced around the callback
9bccf70c 972 */
91447636 973 vnode_iterate(mp, 0, hfs_qsync_callback, (void *)NULL);
d7e50217 974
9bccf70c
A
975 return (0);
976}
977
978/*
979 * Q_QUOTASTAT - get quota on/off status
980 */
981int
91447636 982hfs_quotastat(mp, type, datap)
9bccf70c
A
983 struct mount *mp;
984 register int type;
91447636 985 caddr_t datap;
9bccf70c
A
986{
987 struct hfsmount *hfsmp = VFSTOHFS(mp);
988 int error = 0;
989 int qstat;
990
91447636 991 if ((((unsigned int)vfs_flags(mp)) & MNT_QUOTA) && (hfsmp->hfs_qfiles[type].qf_vp != NULLVP))
9bccf70c
A
992 qstat = 1; /* quotas are on for this type */
993 else
994 qstat = 0; /* quotas are off for this type */
995
91447636 996 *((int *)datap) = qstat;
9bccf70c
A
997 return (error);
998}
999