]> git.saurik.com Git - apple/hfs.git/blob - core/hfs_quota.c
da47b9c4187a8270a6b629a7434f0618e993b4f3
[apple/hfs.git] / core / hfs_quota.c
1 /*
2 * Copyright (c) 2002-2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
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
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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 #if QUOTA
68
69 #include <sys/param.h>
70 #include <sys/kernel.h>
71 #include <sys/systm.h>
72 #include <sys/mount.h>
73 #include <sys/malloc.h>
74 #include <sys/file.h>
75 #include <sys/proc.h>
76 #include <sys/kauth.h>
77 #include <sys/vnode.h>
78 #include <sys/quota.h>
79 #include <sys/vm.h>
80
81 #include "hfs.h"
82 #include "hfs_cnode.h"
83 #include "hfs_quota.h"
84 #include "hfs_mount.h"
85
86
87 /*
88 * Quota name to error message mapping.
89 */
90 #if 0
91 static char *quotatypes[] = INITQFNAMES;
92 #endif
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 */
102 int
103 hfs_getinoquota(cp)
104 register struct cnode *cp;
105 {
106 struct hfsmount *hfsmp;
107 struct vnode *vp;
108 int error;
109 int drop_usrquota = false;
110
111 vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
112 hfsmp = VTOHFS(vp);
113 /*
114 * Set up the user quota based on file uid.
115 * EINVAL means that quotas are not enabled.
116 */
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
126 /*
127 * Set up the group quota based on file gid.
128 * EINVAL means that quotas are not enabled.
129 */
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
141 return (0);
142 }
143
144 /*
145 * Update disk usage, and take corrective action.
146 */
147 int
148 hfs_chkdq(cp, change, cred, flags)
149 register struct cnode *cp;
150 int64_t change;
151 kauth_cred_t cred;
152 int flags;
153 {
154 register struct dquot *dq;
155 register int i;
156 int64_t ncurbytes;
157 int error=0;
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;
170 dqlock(dq);
171
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;
179
180 dqunlock(dq);
181 }
182 return (0);
183 }
184 p = current_proc();
185 /*
186 * This use of proc_ucred() is safe because kernproc credential never
187 * changes.
188 */
189 if (!IS_VALID_CRED(cred))
190 cred = proc_ucred(kernproc);
191 if (suser(cred, NULL) || proc_forcequota(p)) {
192 for (i = 0; i < MAXQUOTAS; i++) {
193 if ((dq = cp->c_dquot[i]) == NODQUOT)
194 continue;
195 error = hfs_chkdqchg(cp, change, cred, i);
196 if (error) {
197 break;
198 }
199 }
200 }
201 if ((flags & FORCE) || error == 0) {
202 for (i = 0; i < MAXQUOTAS; i++) {
203 if ((dq = cp->c_dquot[i]) == NODQUOT)
204 continue;
205 dqlock(dq);
206
207 dq->dq_curbytes += change;
208 dq->dq_flags |= DQ_MOD;
209
210 dqunlock(dq);
211 }
212 }
213 return (error);
214 }
215
216 /*
217 * Check for a valid change to a users allocation.
218 * Issue an error message and vfs event if appropriate.
219 */
220 int
221 hfs_chkdqchg(cp, change, cred, type)
222 struct cnode *cp;
223 int64_t change;
224 kauth_cred_t cred;
225 int type;
226 {
227 register struct dquot *dq = cp->c_dquot[type];
228 u_int64_t ncurbytes;
229 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
230
231 fsid_t fsid;
232 fsid.val[0] = VTOHFS(vp)->hfs_raw_dev;
233 fsid.val[1] = vfs_typenum(VTOVFS(vp));
234
235 dqlock(dq);
236
237 ncurbytes = dq->dq_curbytes + change;
238 /*
239 * If user would exceed their hard limit, disallow space allocation.
240 */
241 if (ncurbytes >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
242 if ((dq->dq_flags & DQ_BLKS) == 0 &&
243 cp->c_uid == kauth_cred_getuid(cred)) {
244 #if 0
245 printf("\nhfs: write failed, %s disk limit reached\n",
246 quotatypes[type]);
247 #endif
248 dq->dq_flags |= DQ_BLKS;
249 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
250 }
251 dqunlock(dq);
252
253 return (EDQUOT);
254 }
255 /*
256 * If user is over their soft limit for too long, disallow space
257 * allocation. Reset time limit as they cross their soft limit.
258 */
259 if (ncurbytes >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
260 struct timeval tv;
261
262 microuptime(&tv);
263 if (dq->dq_curbytes < dq->dq_bsoftlimit) {
264 dq->dq_btime = tv.tv_sec +
265 VTOHFS(vp)->hfs_qfiles[type].qf_btime;
266 #if 0
267 if (cp->c_uid == kauth_cred_getuid(cred))
268 printf("\nhfs: warning, %s %s\n",
269 quotatypes[type], "disk quota exceeded");
270 #endif
271 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
272 dqunlock(dq);
273
274 return (0);
275 }
276 if (tv.tv_sec > (time_t)dq->dq_btime) {
277 if ((dq->dq_flags & DQ_BLKS) == 0 &&
278 cp->c_uid == kauth_cred_getuid(cred)) {
279 #if 0
280 printf("\nhfs: write failed, %s %s\n",
281 quotatypes[type],
282 "disk quota exceeded for too long");
283 #endif
284 dq->dq_flags |= DQ_BLKS;
285 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
286 }
287 dqunlock(dq);
288
289 return (EDQUOT);
290 }
291 }
292 dqunlock(dq);
293
294 return (0);
295 }
296
297 /*
298 * Check the inode limit, applying corrective action.
299 */
300 int
301 hfs_chkiq(cp, change, cred, flags)
302 register struct cnode *cp;
303 int32_t change;
304 kauth_cred_t cred;
305 int flags;
306 {
307 register struct dquot *dq;
308 register int i;
309 int ncurinodes, error=0;
310 struct proc *p;
311
312 #if DIAGNOSTIC
313 if ((flags & CHOWN) == 0)
314 hfs_chkdquot(cp);
315 #endif
316 if (change == 0)
317 return (0);
318 if (change < 0) {
319 for (i = 0; i < MAXQUOTAS; i++) {
320 if ((dq = cp->c_dquot[i]) == NODQUOT)
321 continue;
322 dqlock(dq);
323
324 ncurinodes = dq->dq_curinodes + change;
325 if (ncurinodes >= 0)
326 dq->dq_curinodes = ncurinodes;
327 else
328 dq->dq_curinodes = 0;
329 dq->dq_flags &= ~DQ_INODS;
330 dq->dq_flags |= DQ_MOD;
331
332 dqunlock(dq);
333 }
334 return (0);
335 }
336 p = current_proc();
337 /*
338 * This use of proc_ucred() is safe because kernproc credential never
339 * changes.
340 */
341 if (!IS_VALID_CRED(cred))
342 cred = proc_ucred(kernproc);
343 if (suser(cred, NULL) || proc_forcequota(p)) {
344 for (i = 0; i < MAXQUOTAS; i++) {
345 if ((dq = cp->c_dquot[i]) == NODQUOT)
346 continue;
347 error = hfs_chkiqchg(cp, change, cred, i);
348 if (error) {
349 break;
350 }
351 }
352 }
353 if ((flags & FORCE) || error == 0) {
354 for (i = 0; i < MAXQUOTAS; i++) {
355 if ((dq = cp->c_dquot[i]) == NODQUOT)
356 continue;
357 dqlock(dq);
358
359 dq->dq_curinodes += change;
360 dq->dq_flags |= DQ_MOD;
361
362 dqunlock(dq);
363 }
364 }
365 return (error);
366 }
367
368
369 /*
370 * Check to see if a change to a user's allocation should be permitted or not.
371 * Issue an error message if it should not be permitted. Return 0 if
372 * it should be allowed.
373 */
374 int hfs_isiqchg_allowed(dq, hfsmp, change, cred, type, uid)
375 struct dquot* dq;
376 struct hfsmount* hfsmp;
377 int32_t change;
378 kauth_cred_t cred;
379 int type;
380 uid_t uid;
381 {
382 u_int32_t ncurinodes;
383
384 fsid_t fsid;
385 fsid.val[0] = hfsmp->hfs_raw_dev;
386 fsid.val[1] = vfs_typenum(HFSTOVFS(hfsmp));
387
388 dqlock(dq);
389
390 ncurinodes = dq->dq_curinodes + change;
391 /*
392 * If user would exceed their hard limit, disallow cnode allocation.
393 */
394 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
395 if ((dq->dq_flags & DQ_INODS) == 0 &&
396 uid == kauth_cred_getuid(cred)) {
397 dq->dq_flags |= DQ_INODS;
398 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
399 }
400 dqunlock(dq);
401
402 return (EDQUOT);
403 }
404 /*
405 * If user is over their soft limit for too long, disallow cnode
406 * allocation. Reset time limit as they cross their soft limit.
407 */
408 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
409 struct timeval tv;
410
411 microuptime(&tv);
412 if (dq->dq_curinodes < dq->dq_isoftlimit) {
413 dq->dq_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime;
414 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
415 dqunlock(dq);
416 return (0);
417 }
418 if (tv.tv_sec > (time_t)dq->dq_itime) {
419 if (((dq->dq_flags & DQ_INODS) == 0) &&
420 (uid == kauth_cred_getuid(cred))) {
421 dq->dq_flags |= DQ_INODS;
422 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
423 }
424 dqunlock(dq);
425
426 return (EDQUOT);
427 }
428 }
429 dqunlock(dq);
430
431 return (0);
432 }
433
434
435 /*
436 * Check for a valid change to a users allocation.
437 * Issue an error message if appropriate.
438 */
439 int
440 hfs_chkiqchg(cp, change, cred, type)
441 struct cnode *cp;
442 int32_t change;
443 kauth_cred_t cred;
444 int type;
445 {
446 register struct dquot *dq = cp->c_dquot[type];
447 u_int32_t ncurinodes;
448 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
449
450 dqlock(dq);
451
452 ncurinodes = dq->dq_curinodes + change;
453 /*
454 * If user would exceed their hard limit, disallow cnode allocation.
455 */
456 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
457 if ((dq->dq_flags & DQ_INODS) == 0 &&
458 cp->c_uid == kauth_cred_getuid(cred)) {
459 #if 0
460 printf("\nhfs: write failed, %s cnode limit reached\n",
461 quotatypes[type]);
462 #endif
463 dq->dq_flags |= DQ_INODS;
464 }
465 dqunlock(dq);
466
467 return (EDQUOT);
468 }
469 /*
470 * If user is over their soft limit for too long, disallow cnode
471 * allocation. Reset time limit as they cross their soft limit.
472 */
473 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
474 struct timeval tv;
475
476 microuptime(&tv);
477 if (dq->dq_curinodes < dq->dq_isoftlimit) {
478 dq->dq_itime = tv.tv_sec +
479 VTOHFS(vp)->hfs_qfiles[type].qf_itime;
480 #if 0
481 if (cp->c_uid == kauth_cred_getuid(cred))
482 printf("\nhfs: warning, %s %s\n",
483 quotatypes[type], "cnode quota exceeded");
484 #endif
485 dqunlock(dq);
486
487 return (0);
488 }
489 if (tv.tv_sec > (time_t)dq->dq_itime) {
490 if ((dq->dq_flags & DQ_INODS) == 0 &&
491 cp->c_uid == kauth_cred_getuid(cred)) {
492 #if 0
493 printf("\nhfs: write failed, %s %s\n",
494 quotatypes[type],
495 "cnode quota exceeded for too long");
496 #endif
497 dq->dq_flags |= DQ_INODS;
498 }
499 dqunlock(dq);
500
501 return (EDQUOT);
502 }
503 }
504 dqunlock(dq);
505
506 return (0);
507 }
508
509 #if DIAGNOSTIC
510 /*
511 * On filesystems with quotas enabled, it is an error for a file to change
512 * size and not to have a dquot structure associated with it.
513 */
514 void
515 hfs_chkdquot(cp)
516 register struct cnode *cp;
517 {
518 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
519 struct hfsmount *hfsmp = VTOHFS(vp);
520 register int i;
521
522 for (i = 0; i < MAXQUOTAS; i++) {
523 if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP)
524 continue;
525 if (cp->c_dquot[i] == NODQUOT) {
526 vprint("chkdquot: missing dquot", vp);
527 panic("missing dquot");
528 }
529 }
530 }
531 #endif
532
533 /*
534 * Code to process quotactl commands.
535 */
536
537 /*
538 * Q_QUOTAON - set up a quota file for a particular file system.
539 */
540 struct hfs_quotaon_cargs {
541 int error;
542 };
543
544 static int
545 hfs_quotaon_callback(struct vnode *vp, void *cargs)
546 {
547 struct hfs_quotaon_cargs *args;
548
549 args = (struct hfs_quotaon_cargs *)cargs;
550
551 args->error = hfs_getinoquota(VTOC(vp));
552 if (args->error)
553 return (VNODE_RETURNED_DONE);
554
555 return (VNODE_RETURNED);
556 }
557
558 int
559 hfs_quotaon(p, mp, type, fnamep)
560 struct proc *p;
561 struct mount *mp;
562 register int type;
563 caddr_t fnamep;
564 {
565 struct hfsmount *hfsmp = VFSTOHFS(mp);
566 struct quotafile *qfp;
567 struct vnode *vp;
568 int error = 0;
569 struct hfs_quotaon_cargs args;
570
571 /* Finish setting up quota structures. */
572 dqhashinit();
573
574 qfp = &hfsmp->hfs_qfiles[type];
575
576 if ( (qf_get(qfp, QTF_OPENING)) )
577 return (0);
578
579 error = vnode_open(fnamep, FREAD|FWRITE, 0, 0, &vp, NULL);
580 if (error) {
581 goto out;
582 }
583 if (!vnode_isreg(vp)) {
584 (void) vnode_close(vp, FREAD|FWRITE, NULL);
585 error = EACCES;
586 goto out;
587 }
588 vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA));
589 hfs_lock_mount (hfsmp);
590 hfsmp->hfs_flags |= HFS_QUOTAS;
591 hfs_unlock_mount (hfsmp);
592 vnode_setnoflush(vp);
593 /*
594 * Save the credential of the process that turned on quotas.
595 */
596 qfp->qf_cred = kauth_cred_proc_ref(p);
597 qfp->qf_vp = vp;
598 /*
599 * Finish initializing the quota file
600 */
601 error = dqfileopen(qfp, type);
602 if (error) {
603 (void) vnode_close(vp, FREAD|FWRITE, NULL);
604
605 if (IS_VALID_CRED(qfp->qf_cred))
606 kauth_cred_unref(&qfp->qf_cred);
607 qfp->qf_vp = NULLVP;
608 goto out;
609 }
610 qf_put(qfp, QTF_OPENING);
611
612 /*
613 * Search vnodes associated with this mount point,
614 * adding references to quota file being opened.
615 * NB: only need to add dquot's for cnodes being modified.
616 *
617 * hfs_quota_callback will be called for each vnode open for
618 * 'write' (VNODE_WRITEABLE) hung off of this mount point
619 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
620 * properly referenced and unreferenced around the callback
621 */
622 args.error = 0;
623
624 vnode_iterate(mp, VNODE_WRITEABLE | VNODE_WAIT, hfs_quotaon_callback, (void *)&args);
625
626 error = args.error;
627
628 if (error) {
629 hfs_quotaoff(p, mp, type);
630 }
631 return (error);
632
633 out:
634 qf_put(qfp, QTF_OPENING);
635
636 return (error);
637 }
638
639
640 /*
641 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
642 */
643 struct hfs_quotaoff_cargs {
644 int type;
645 };
646
647 static int
648 hfs_quotaoff_callback(struct vnode *vp, void *cargs)
649 {
650 struct hfs_quotaoff_cargs *args;
651 struct cnode *cp;
652 struct dquot *dq;
653
654 args = (struct hfs_quotaoff_cargs *)cargs;
655
656 cp = VTOC(vp);
657
658 dq = cp->c_dquot[args->type];
659 cp->c_dquot[args->type] = NODQUOT;
660
661 dqrele(dq);
662
663 return (VNODE_RETURNED);
664 }
665
666 int
667 hfs_quotaoff(__unused struct proc *p, struct mount *mp, register int type)
668 {
669 struct vnode *qvp;
670 struct hfsmount *hfsmp = VFSTOHFS(mp);
671 struct quotafile *qfp;
672 int error;
673 struct hfs_quotaoff_cargs args;
674
675 /*
676 * If quotas haven't been initialized, there's no work to be done.
677 */
678 if (!dqisinitialized())
679 return (0);
680
681 qfp = &hfsmp->hfs_qfiles[type];
682
683 if ( (qf_get(qfp, QTF_CLOSING)) )
684 return (0);
685 qvp = qfp->qf_vp;
686
687 /*
688 * Sync out any orpaned dirty dquot entries.
689 */
690 dqsync_orphans(qfp);
691
692 /*
693 * Search vnodes associated with this mount point,
694 * deleting any references to quota file being closed.
695 *
696 * hfs_quotaoff_callback will be called for each vnode
697 * hung off of this mount point
698 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
699 * properly referenced and unreferenced around the callback
700 */
701 args.type = type;
702
703 vnode_iterate(mp, VNODE_WAIT, hfs_quotaoff_callback, (void *)&args);
704
705 dqflush(qvp);
706 /* Finish tearing down the quota file */
707 dqfileclose(qfp, type);
708
709 vnode_clearnoflush(qvp);
710 error = vnode_close(qvp, FREAD|FWRITE, NULL);
711
712 qfp->qf_vp = NULLVP;
713
714 if (IS_VALID_CRED(qfp->qf_cred))
715 kauth_cred_unref(&qfp->qf_cred);
716 for (type = 0; type < MAXQUOTAS; type++)
717 if (hfsmp->hfs_qfiles[type].qf_vp != NULLVP)
718 break;
719 if (type == MAXQUOTAS) {
720 vfs_clearflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA));
721 hfs_lock_mount (hfsmp);
722 hfsmp->hfs_flags &= ~HFS_QUOTAS;
723 hfs_unlock_mount (hfsmp);
724 }
725
726 qf_put(qfp, QTF_CLOSING);
727
728 return (error);
729 }
730
731 /*
732 * hfs_quotacheck - checks quotas mountwide for a hypothetical situation. It probes
733 * the quota data structures to see if adding an inode would be allowed or not. If it
734 * will be allowed, the change is made. Otherwise, it reports an error back out so the
735 * caller will know not to proceed with inode allocation in the HFS Catalog.
736 *
737 * Note that this function ONLY tests for addition of inodes, not subtraction.
738 */
739 int hfs_quotacheck(hfsmp, change, uid, gid, cred)
740 struct hfsmount *hfsmp;
741 int change;
742 uid_t uid;
743 gid_t gid;
744 kauth_cred_t cred;
745 {
746 struct dquot *dq = NULL;
747 struct proc *p;
748 int error = 0;
749 int i;
750 id_t id = uid;
751
752 p = current_proc();
753 if (!IS_VALID_CRED(cred)) {
754 /* This use of proc_ucred() is safe because kernproc credential never changes */
755 cred = proc_ucred(kernproc);
756 }
757
758 if (suser(cred, NULL) || proc_forcequota(p)) {
759 for (i = 0; i < MAXQUOTAS; i++) {
760 /* Select if user or group id should be used */
761 if (i == USRQUOTA)
762 id = uid;
763 else if (i == GRPQUOTA)
764 id = gid;
765
766 error = dqget(id, &hfsmp->hfs_qfiles[i], i, &dq);
767 if (error && (error != EINVAL))
768 break;
769
770 error = 0;
771 if (dq == NODQUOT)
772 continue;
773
774 /* Check quota information */
775 error = hfs_isiqchg_allowed(dq, hfsmp, change, cred, i, id);
776 if (error) {
777 dqrele(dq);
778 break;
779 }
780
781 dqlock(dq);
782 /* Update quota information */
783 dq->dq_curinodes += change;
784 dqunlock(dq);
785 dqrele(dq);
786 }
787 }
788
789 return error;
790 }
791
792
793 /*
794 * Q_GETQUOTA - return current values in a dqblk structure.
795 */
796 int
797 hfs_getquota(mp, id, type, datap)
798 struct mount *mp;
799 u_int32_t id;
800 int type;
801 caddr_t datap;
802 {
803 struct dquot *dq;
804 int error;
805
806 error = dqget(id, &VFSTOHFS(mp)->hfs_qfiles[type], type, &dq);
807 if (error)
808 return (error);
809 dqlock(dq);
810
811 bcopy(&dq->dq_dqb, datap, sizeof(dq->dq_dqb));
812
813 dqunlock(dq);
814 dqrele(dq);
815
816 return (error);
817 }
818
819 /*
820 * Q_SETQUOTA - assign an entire dqblk structure.
821 */
822 int
823 hfs_setquota(mp, id, type, datap)
824 struct mount *mp;
825 u_int32_t id;
826 int type;
827 caddr_t datap;
828 {
829 struct dquot *dq;
830 struct hfsmount *hfsmp = VFSTOHFS(mp);
831 struct dqblk * newlimp = (struct dqblk *) datap;
832 struct timeval tv;
833 int error;
834
835 error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq);
836 if (error)
837 return (error);
838 dqlock(dq);
839
840 /*
841 * Copy all but the current values.
842 * Reset time limit if previously had no soft limit or were
843 * under it, but now have a soft limit and are over it.
844 */
845 newlimp->dqb_curbytes = dq->dq_curbytes;
846 newlimp->dqb_curinodes = dq->dq_curinodes;
847 if (dq->dq_id != 0) {
848 newlimp->dqb_btime = dq->dq_btime;
849 newlimp->dqb_itime = dq->dq_itime;
850 }
851 if (newlimp->dqb_bsoftlimit &&
852 dq->dq_curbytes >= newlimp->dqb_bsoftlimit &&
853 (dq->dq_bsoftlimit == 0 || dq->dq_curbytes < dq->dq_bsoftlimit)) {
854 microuptime(&tv);
855 newlimp->dqb_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime;
856 }
857 if (newlimp->dqb_isoftlimit &&
858 dq->dq_curinodes >= newlimp->dqb_isoftlimit &&
859 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) {
860 microuptime(&tv);
861 newlimp->dqb_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime;
862 }
863 bcopy(newlimp, &dq->dq_dqb, sizeof(dq->dq_dqb));
864 if (dq->dq_curbytes < dq->dq_bsoftlimit)
865 dq->dq_flags &= ~DQ_BLKS;
866 if (dq->dq_curinodes < dq->dq_isoftlimit)
867 dq->dq_flags &= ~DQ_INODS;
868 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
869 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
870 dq->dq_flags |= DQ_FAKE;
871 else
872 dq->dq_flags &= ~DQ_FAKE;
873 dq->dq_flags |= DQ_MOD;
874
875 dqunlock(dq);
876 dqrele(dq);
877
878 return (0);
879 }
880
881 /*
882 * Q_SETUSE - set current cnode and byte usage.
883 */
884 int
885 hfs_setuse(mp, id, type, datap)
886 struct mount *mp;
887 u_int32_t id;
888 int type;
889 caddr_t datap;
890 {
891 struct hfsmount *hfsmp = VFSTOHFS(mp);
892 struct dquot *dq;
893 struct timeval tv;
894 int error;
895 struct dqblk *quotablkp = (struct dqblk *) datap;
896
897 error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq);
898 if (error)
899 return (error);
900 dqlock(dq);
901
902 /*
903 * Reset time limit if have a soft limit and were
904 * previously under it, but are now over it.
905 */
906 if (dq->dq_bsoftlimit && dq->dq_curbytes < dq->dq_bsoftlimit &&
907 quotablkp->dqb_curbytes >= dq->dq_bsoftlimit) {
908 microuptime(&tv);
909 dq->dq_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime;
910 }
911 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
912 quotablkp->dqb_curinodes >= dq->dq_isoftlimit) {
913 microuptime(&tv);
914 dq->dq_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime;
915 }
916 dq->dq_curbytes = quotablkp->dqb_curbytes;
917 dq->dq_curinodes = quotablkp->dqb_curinodes;
918 if (dq->dq_curbytes < dq->dq_bsoftlimit)
919 dq->dq_flags &= ~DQ_BLKS;
920 if (dq->dq_curinodes < dq->dq_isoftlimit)
921 dq->dq_flags &= ~DQ_INODS;
922 dq->dq_flags |= DQ_MOD;
923
924 dqunlock(dq);
925 dqrele(dq);
926
927 return (0);
928 }
929
930
931 /*
932 * Q_SYNC - sync quota files to disk.
933 */
934 static int
935 hfs_qsync_callback(struct vnode *vp, __unused void *cargs)
936 {
937 struct cnode *cp;
938 struct dquot *dq;
939 int i;
940
941 cp = VTOC(vp);
942
943 for (i = 0; i < MAXQUOTAS; i++) {
944 dq = cp->c_dquot[i];
945 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
946 dqsync(dq);
947 }
948 return (VNODE_RETURNED);
949 }
950
951 int
952 hfs_qsync(mp)
953 struct mount *mp;
954 {
955 struct hfsmount *hfsmp = VFSTOHFS(mp);
956 int i;
957
958 if (!dqisinitialized())
959 return (0);
960
961 /*
962 * Check if the mount point has any quotas.
963 * If not, simply return.
964 */
965 for (i = 0; i < MAXQUOTAS; i++)
966 if (hfsmp->hfs_qfiles[i].qf_vp != NULLVP)
967 break;
968 if (i == MAXQUOTAS)
969 return (0);
970
971 /*
972 * Sync out any orpaned dirty dquot entries.
973 */
974 for (i = 0; i < MAXQUOTAS; i++)
975 if (hfsmp->hfs_qfiles[i].qf_vp != NULLVP)
976 dqsync_orphans(&hfsmp->hfs_qfiles[i]);
977
978 /*
979 * Search vnodes associated with this mount point,
980 * synchronizing any modified dquot structures.
981 *
982 * hfs_qsync_callback will be called for each vnode
983 * hung off of this mount point
984 * the vnode will be
985 * properly referenced and unreferenced around the callback
986 */
987 vnode_iterate(mp, 0, hfs_qsync_callback, (void *)NULL);
988
989 return (0);
990 }
991
992 /*
993 * Q_QUOTASTAT - get quota on/off status
994 */
995 int
996 hfs_quotastat(mp, type, datap)
997 struct mount *mp;
998 register int type;
999 caddr_t datap;
1000 {
1001 struct hfsmount *hfsmp = VFSTOHFS(mp);
1002 int error = 0;
1003 int qstat;
1004
1005 if ((((unsigned int)vfs_flags(mp)) & MNT_QUOTA) && (hfsmp->hfs_qfiles[type].qf_vp != NULLVP))
1006 qstat = 1; /* quotas are on for this type */
1007 else
1008 qstat = 0; /* quotas are off for this type */
1009
1010 *((int *)datap) = qstat;
1011 return (error);
1012 }
1013
1014 #endif // QUOTA