]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_quota.c
xnu-3248.40.184.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.
490019cf 218 * Issue an error message and vfs event if appropriate.
9bccf70c
A
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;
490019cf
A
230
231 fsid_t fsid;
232 fsid.val[0] = VTOHFS(vp)->hfs_raw_dev;
233 fsid.val[1] = vfs_typenum(VTOVFS(vp));
234
91447636
A
235 dqlock(dq);
236
237 ncurbytes = dq->dq_curbytes + change;
9bccf70c
A
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 &&
91447636 243 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 244#if 0
b0d623f7 245 printf("\nhfs: write failed, %s disk limit reached\n",
9bccf70c
A
246 quotatypes[type]);
247#endif
248 dq->dq_flags |= DQ_BLKS;
490019cf 249 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
9bccf70c 250 }
91447636
A
251 dqunlock(dq);
252
9bccf70c
A
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) {
91447636
A
260 struct timeval tv;
261
262 microuptime(&tv);
9bccf70c 263 if (dq->dq_curbytes < dq->dq_bsoftlimit) {
91447636
A
264 dq->dq_btime = tv.tv_sec +
265 VTOHFS(vp)->hfs_qfiles[type].qf_btime;
d7e50217 266#if 0
91447636 267 if (cp->c_uid == kauth_cred_getuid(cred))
b0d623f7 268 printf("\nhfs: warning, %s %s\n",
9bccf70c
A
269 quotatypes[type], "disk quota exceeded");
270#endif
490019cf 271 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
91447636
A
272 dqunlock(dq);
273
9bccf70c
A
274 return (0);
275 }
b0d623f7 276 if (tv.tv_sec > (time_t)dq->dq_btime) {
9bccf70c 277 if ((dq->dq_flags & DQ_BLKS) == 0 &&
91447636 278 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 279#if 0
b0d623f7 280 printf("\nhfs: write failed, %s %s\n",
9bccf70c
A
281 quotatypes[type],
282 "disk quota exceeded for too long");
283#endif
284 dq->dq_flags |= DQ_BLKS;
490019cf 285 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
9bccf70c 286 }
91447636
A
287 dqunlock(dq);
288
9bccf70c
A
289 return (EDQUOT);
290 }
291 }
91447636
A
292 dqunlock(dq);
293
9bccf70c
A
294 return (0);
295}
296
297/*
298 * Check the inode limit, applying corrective action.
299 */
300int
301hfs_chkiq(cp, change, cred, flags)
302 register struct cnode *cp;
b0d623f7 303 int32_t change;
91447636 304 kauth_cred_t cred;
9bccf70c
A
305 int flags;
306{
307 register struct dquot *dq;
308 register int i;
d7e50217 309 int ncurinodes, error=0;
9bccf70c
A
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;
91447636
A
322 dqlock(dq);
323
9bccf70c
A
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;
91447636
A
331
332 dqunlock(dq);
9bccf70c
A
333 }
334 return (0);
335 }
336 p = current_proc();
0c530ab8
A
337 /*
338 * This use of proc_ucred() is safe because kernproc credential never
339 * changes.
340 */
341 if (!IS_VALID_CRED(cred))
91447636
A
342 cred = proc_ucred(kernproc);
343 if (suser(cred, NULL) || proc_forcequota(p)) {
9bccf70c
A
344 for (i = 0; i < MAXQUOTAS; i++) {
345 if ((dq = cp->c_dquot[i]) == NODQUOT)
346 continue;
d7e50217
A
347 error = hfs_chkiqchg(cp, change, cred, i);
348 if (error) {
349 break;
350 }
9bccf70c
A
351 }
352 }
d7e50217
A
353 if ((flags & FORCE) || error == 0) {
354 for (i = 0; i < MAXQUOTAS; i++) {
355 if ((dq = cp->c_dquot[i]) == NODQUOT)
356 continue;
91447636
A
357 dqlock(dq);
358
d7e50217
A
359 dq->dq_curinodes += change;
360 dq->dq_flags |= DQ_MOD;
91447636
A
361
362 dqunlock(dq);
9bccf70c 363 }
9bccf70c 364 }
d7e50217 365 return (error);
9bccf70c
A
366}
367
b0d623f7
A
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 */
374int 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
490019cf
A
384 fsid_t fsid;
385 fsid.val[0] = hfsmp->hfs_raw_dev;
386 fsid.val[1] = vfs_typenum(HFSTOVFS(hfsmp));
387
b0d623f7
A
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;
490019cf 398 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
b0d623f7
A
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;
490019cf 414 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
b0d623f7
A
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;
490019cf 422 vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
b0d623f7
A
423 }
424 dqunlock(dq);
425
426 return (EDQUOT);
427 }
428 }
429 dqunlock(dq);
430
431 return (0);
432}
433
434
9bccf70c
A
435/*
436 * Check for a valid change to a users allocation.
437 * Issue an error message if appropriate.
438 */
439int
440hfs_chkiqchg(cp, change, cred, type)
441 struct cnode *cp;
b0d623f7 442 int32_t change;
91447636 443 kauth_cred_t cred;
9bccf70c
A
444 int type;
445{
446 register struct dquot *dq = cp->c_dquot[type];
b0d623f7 447 u_int32_t ncurinodes;
9bccf70c
A
448 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
449
91447636
A
450 dqlock(dq);
451
452 ncurinodes = dq->dq_curinodes + change;
9bccf70c
A
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 &&
91447636 458 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 459#if 0
b0d623f7 460 printf("\nhfs: write failed, %s cnode limit reached\n",
9bccf70c
A
461 quotatypes[type]);
462#endif
463 dq->dq_flags |= DQ_INODS;
464 }
91447636
A
465 dqunlock(dq);
466
9bccf70c
A
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) {
91447636
A
474 struct timeval tv;
475
476 microuptime(&tv);
9bccf70c 477 if (dq->dq_curinodes < dq->dq_isoftlimit) {
91447636
A
478 dq->dq_itime = tv.tv_sec +
479 VTOHFS(vp)->hfs_qfiles[type].qf_itime;
d7e50217 480#if 0
91447636 481 if (cp->c_uid == kauth_cred_getuid(cred))
b0d623f7 482 printf("\nhfs: warning, %s %s\n",
9bccf70c
A
483 quotatypes[type], "cnode quota exceeded");
484#endif
91447636
A
485 dqunlock(dq);
486
9bccf70c
A
487 return (0);
488 }
b0d623f7 489 if (tv.tv_sec > (time_t)dq->dq_itime) {
9bccf70c 490 if ((dq->dq_flags & DQ_INODS) == 0 &&
91447636 491 cp->c_uid == kauth_cred_getuid(cred)) {
d7e50217 492#if 0
b0d623f7 493 printf("\nhfs: write failed, %s %s\n",
9bccf70c
A
494 quotatypes[type],
495 "cnode quota exceeded for too long");
496#endif
497 dq->dq_flags |= DQ_INODS;
498 }
91447636
A
499 dqunlock(dq);
500
9bccf70c
A
501 return (EDQUOT);
502 }
503 }
91447636
A
504 dqunlock(dq);
505
9bccf70c
A
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 */
514void
515hfs_chkdquot(cp)
516 register struct cnode *cp;
517{
518 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
91447636 519 struct hfsmount *hfsmp = VTOHFS(vp);
9bccf70c
A
520 register int i;
521
522 for (i = 0; i < MAXQUOTAS; i++) {
91447636 523 if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP)
9bccf70c
A
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 */
91447636
A
540struct hfs_quotaon_cargs {
541 int error;
542};
543
544static int
545hfs_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
9bccf70c 558int
91447636 559hfs_quotaon(p, mp, type, fnamep)
9bccf70c
A
560 struct proc *p;
561 struct mount *mp;
562 register int type;
91447636 563 caddr_t fnamep;
9bccf70c
A
564{
565 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636
A
566 struct quotafile *qfp;
567 struct vnode *vp;
568 int error = 0;
569 struct hfs_quotaon_cargs args;
9bccf70c 570
2d21ac55
A
571 /* Finish setting up quota structures. */
572 dqhashinit();
573
91447636
A
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;
9bccf70c 582 }
91447636
A
583 if (!vnode_isreg(vp)) {
584 (void) vnode_close(vp, FREAD|FWRITE, NULL);
585 error = EACCES;
586 goto out;
587 }
2d21ac55 588 vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA));
39236c6e 589 hfs_lock_mount (hfsmp);
2d21ac55 590 hfsmp->hfs_flags |= HFS_QUOTAS;
39236c6e 591 hfs_unlock_mount (hfsmp);
91447636 592 vnode_setnoflush(vp);
9bccf70c
A
593 /*
594 * Save the credential of the process that turned on quotas.
595 */
91447636
A
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
0c530ab8
A
605 if (IS_VALID_CRED(qfp->qf_cred))
606 kauth_cred_unref(&qfp->qf_cred);
91447636
A
607 qfp->qf_vp = NULLVP;
608 goto out;
609 }
610 qf_put(qfp, QTF_OPENING);
611
9bccf70c
A
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.
91447636
A
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
9bccf70c 621 */
91447636
A
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) {
9bccf70c 629 hfs_quotaoff(p, mp, type);
91447636
A
630 }
631 return (error);
632
633out:
634 qf_put(qfp, QTF_OPENING);
635
9bccf70c
A
636 return (error);
637}
638
91447636 639
9bccf70c
A
640/*
641 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
642 */
91447636
A
643struct hfs_quotaoff_cargs {
644 int type;
645};
646
647static int
648hfs_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
9bccf70c 666int
91447636 667hfs_quotaoff(__unused struct proc *p, struct mount *mp, register int type)
9bccf70c 668{
91447636 669 struct vnode *qvp;
9bccf70c 670 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636 671 struct quotafile *qfp;
9bccf70c 672 int error;
91447636
A
673 struct hfs_quotaoff_cargs args;
674
2d21ac55
A
675 /*
676 * If quotas haven't been initialized, there's no work to be done.
677 */
678 if (!dqisinitialized())
679 return (0);
680
91447636 681 qfp = &hfsmp->hfs_qfiles[type];
9bccf70c 682
91447636
A
683 if ( (qf_get(qfp, QTF_CLOSING)) )
684 return (0);
685 qvp = qfp->qf_vp;
d7e50217
A
686
687 /*
688 * Sync out any orpaned dirty dquot entries.
689 */
91447636 690 dqsync_orphans(qfp);
d7e50217 691
9bccf70c
A
692 /*
693 * Search vnodes associated with this mount point,
694 * deleting any references to quota file being closed.
b0d623f7 695 *
91447636
A
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
9bccf70c 700 */
91447636
A
701 args.type = type;
702
703 vnode_iterate(mp, VNODE_WAIT, hfs_quotaoff_callback, (void *)&args);
704
9bccf70c
A
705 dqflush(qvp);
706 /* Finish tearing down the quota file */
91447636
A
707 dqfileclose(qfp, type);
708
709 vnode_clearnoflush(qvp);
710 error = vnode_close(qvp, FREAD|FWRITE, NULL);
711
712 qfp->qf_vp = NULLVP;
0c530ab8
A
713
714 if (IS_VALID_CRED(qfp->qf_cred))
715 kauth_cred_unref(&qfp->qf_cred);
9bccf70c
A
716 for (type = 0; type < MAXQUOTAS; type++)
717 if (hfsmp->hfs_qfiles[type].qf_vp != NULLVP)
718 break;
2d21ac55
A
719 if (type == MAXQUOTAS) {
720 vfs_clearflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA));
39236c6e 721 hfs_lock_mount (hfsmp);
2d21ac55 722 hfsmp->hfs_flags &= ~HFS_QUOTAS;
39236c6e 723 hfs_unlock_mount (hfsmp);
2d21ac55 724 }
91447636
A
725
726 qf_put(qfp, QTF_CLOSING);
727
9bccf70c
A
728 return (error);
729}
730
b0d623f7
A
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 */
739int 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
9bccf70c
A
793/*
794 * Q_GETQUOTA - return current values in a dqblk structure.
795 */
796int
91447636 797hfs_getquota(mp, id, type, datap)
9bccf70c 798 struct mount *mp;
b0d623f7 799 u_int32_t id;
9bccf70c 800 int type;
91447636 801 caddr_t datap;
9bccf70c
A
802{
803 struct dquot *dq;
804 int error;
805
91447636
A
806 error = dqget(id, &VFSTOHFS(mp)->hfs_qfiles[type], type, &dq);
807 if (error)
9bccf70c 808 return (error);
91447636
A
809 dqlock(dq);
810
811 bcopy(&dq->dq_dqb, datap, sizeof(dq->dq_dqb));
812
813 dqunlock(dq);
814 dqrele(dq);
815
9bccf70c
A
816 return (error);
817}
818
819/*
820 * Q_SETQUOTA - assign an entire dqblk structure.
821 */
822int
91447636 823hfs_setquota(mp, id, type, datap)
9bccf70c 824 struct mount *mp;
b0d623f7 825 u_int32_t id;
9bccf70c 826 int type;
91447636 827 caddr_t datap;
9bccf70c 828{
91447636 829 struct dquot *dq;
9bccf70c 830 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636
A
831 struct dqblk * newlimp = (struct dqblk *) datap;
832 struct timeval tv;
9bccf70c
A
833 int error;
834
91447636
A
835 error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq);
836 if (error)
9bccf70c 837 return (error);
91447636
A
838 dqlock(dq);
839
9bccf70c
A
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 */
91447636
A
845 newlimp->dqb_curbytes = dq->dq_curbytes;
846 newlimp->dqb_curinodes = dq->dq_curinodes;
9bccf70c 847 if (dq->dq_id != 0) {
91447636
A
848 newlimp->dqb_btime = dq->dq_btime;
849 newlimp->dqb_itime = dq->dq_itime;
9bccf70c 850 }
91447636
A
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));
9bccf70c
A
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;
91447636
A
874
875 dqunlock(dq);
876 dqrele(dq);
877
9bccf70c
A
878 return (0);
879}
880
881/*
882 * Q_SETUSE - set current cnode and byte usage.
883 */
884int
91447636 885hfs_setuse(mp, id, type, datap)
9bccf70c 886 struct mount *mp;
b0d623f7 887 u_int32_t id;
9bccf70c 888 int type;
91447636 889 caddr_t datap;
9bccf70c 890{
9bccf70c 891 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636
A
892 struct dquot *dq;
893 struct timeval tv;
9bccf70c 894 int error;
91447636
A
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);
9bccf70c 901
9bccf70c
A
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 &&
91447636
A
907 quotablkp->dqb_curbytes >= dq->dq_bsoftlimit) {
908 microuptime(&tv);
909 dq->dq_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime;
910 }
9bccf70c 911 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
91447636
A
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;
9bccf70c
A
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;
91447636
A
923
924 dqunlock(dq);
925 dqrele(dq);
926
9bccf70c
A
927 return (0);
928}
929
91447636 930
9bccf70c
A
931/*
932 * Q_SYNC - sync quota files to disk.
933 */
91447636
A
934static int
935hfs_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
9bccf70c
A
951int
952hfs_qsync(mp)
953 struct mount *mp;
954{
955 struct hfsmount *hfsmp = VFSTOHFS(mp);
91447636 956 int i;
9bccf70c 957
2d21ac55
A
958 if (!dqisinitialized())
959 return (0);
960
9bccf70c
A
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);
d7e50217
A
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
9bccf70c
A
978 /*
979 * Search vnodes associated with this mount point,
980 * synchronizing any modified dquot structures.
91447636
A
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
9bccf70c 986 */
91447636 987 vnode_iterate(mp, 0, hfs_qsync_callback, (void *)NULL);
d7e50217 988
9bccf70c
A
989 return (0);
990}
991
992/*
993 * Q_QUOTASTAT - get quota on/off status
994 */
995int
91447636 996hfs_quotastat(mp, type, datap)
9bccf70c
A
997 struct mount *mp;
998 register int type;
91447636 999 caddr_t datap;
9bccf70c
A
1000{
1001 struct hfsmount *hfsmp = VFSTOHFS(mp);
1002 int error = 0;
1003 int qstat;
1004
91447636 1005 if ((((unsigned int)vfs_flags(mp)) & MNT_QUOTA) && (hfsmp->hfs_qfiles[type].qf_vp != NULLVP))
9bccf70c
A
1006 qstat = 1; /* quotas are on for this type */
1007 else
1008 qstat = 0; /* quotas are off for this type */
1009
91447636 1010 *((int *)datap) = qstat;
9bccf70c
A
1011 return (error);
1012}
1013