1/* 2 * Copyright (c) 2002-2008 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#include <sys/param.h> 68#include <sys/kernel.h> 69#include <sys/systm.h> 70#include <sys/mount.h> 71#include <sys/malloc.h> 72#include <sys/file.h> 73#include <sys/proc.h> 74#include <sys/kauth.h> 75#include <sys/vnode.h> 76#include <sys/vnode_internal.h> 77#include <sys/quota.h> 78#include <sys/proc_internal.h> 79#include <kern/kalloc.h> 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 86 87/* 88 * Quota name to error message mapping. 89 */ 90#if 0 91static 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 */ 102int 103hfs_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 */ 147int 148hfs_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 if appropriate. 219 */ 220int 221hfs_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 dqlock(dq); 232 233 ncurbytes = dq->dq_curbytes + change; 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 && 239 cp->c_uid == kauth_cred_getuid(cred)) { 240#if 0 241 printf("\nhfs: write failed, %s disk limit reached\n", 242 quotatypes[type]); 243#endif 244 dq->dq_flags |= DQ_BLKS; 245 } 246 dqunlock(dq); 247 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) { 255 struct timeval tv; 256 257 microuptime(&tv); 258 if (dq->dq_curbytes < dq->dq_bsoftlimit) { 259 dq->dq_btime = tv.tv_sec + 260 VTOHFS(vp)->hfs_qfiles[type].qf_btime; 261#if 0 262 if (cp->c_uid == kauth_cred_getuid(cred)) 263 printf("\nhfs: warning, %s %s\n", 264 quotatypes[type], "disk quota exceeded"); 265#endif 266 dqunlock(dq); 267 268 return (0); 269 } 270 if (tv.tv_sec > (time_t)dq->dq_btime) { 271 if ((dq->dq_flags & DQ_BLKS) == 0 && 272 cp->c_uid == kauth_cred_getuid(cred)) { 273#if 0 274 printf("\nhfs: write failed, %s %s\n", 275 quotatypes[type], 276 "disk quota exceeded for too long"); 277#endif 278 dq->dq_flags |= DQ_BLKS; 279 } 280 dqunlock(dq); 281 282 return (EDQUOT); 283 } 284 } 285 dqunlock(dq); 286 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; 296 int32_t change; 297 kauth_cred_t cred; 298 int flags; 299{ 300 register struct dquot *dq; 301 register int i; 302 int ncurinodes, error=0; 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; 315 dqlock(dq); 316 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; 324 325 dqunlock(dq); 326 } 327 return (0); 328 } 329 p = current_proc(); 330 /* 331 * This use of proc_ucred() is safe because kernproc credential never 332 * changes. 333 */ 334 if (!IS_VALID_CRED(cred)) 335 cred = proc_ucred(kernproc); 336 if (suser(cred, NULL) || proc_forcequota(p)) { 337 for (i = 0; i < MAXQUOTAS; i++) { 338 if ((dq = cp->c_dquot[i]) == NODQUOT) 339 continue; 340 error = hfs_chkiqchg(cp, change, cred, i); 341 if (error) { 342 break; 343 } 344 } 345 } 346 if ((flags & FORCE) || error == 0) { 347 for (i = 0; i < MAXQUOTAS; i++) { 348 if ((dq = cp->c_dquot[i]) == NODQUOT) 349 continue; 350 dqlock(dq); 351 352 dq->dq_curinodes += change; 353 dq->dq_flags |= DQ_MOD; 354 355 dqunlock(dq); 356 } 357 } 358 return (error); 359} 360 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 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; 428 int32_t change; 429 kauth_cred_t cred; 430 int type; 431{ 432 register struct dquot *dq = cp->c_dquot[type]; 433 u_int32_t ncurinodes; 434 struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp; 435 436 dqlock(dq); 437 438 ncurinodes = dq->dq_curinodes + change; 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 && 444 cp->c_uid == kauth_cred_getuid(cred)) { 445#if 0 446 printf("\nhfs: write failed, %s cnode limit reached\n", 447 quotatypes[type]); 448#endif 449 dq->dq_flags |= DQ_INODS; 450 } 451 dqunlock(dq); 452 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) { 460 struct timeval tv; 461 462 microuptime(&tv); 463 if (dq->dq_curinodes < dq->dq_isoftlimit) { 464 dq->dq_itime = tv.tv_sec + 465 VTOHFS(vp)->hfs_qfiles[type].qf_itime; 466#if 0 467 if (cp->c_uid == kauth_cred_getuid(cred)) 468 printf("\nhfs: warning, %s %s\n", 469 quotatypes[type], "cnode quota exceeded"); 470#endif 471 dqunlock(dq); 472 473 return (0); 474 } 475 if (tv.tv_sec > (time_t)dq->dq_itime) { 476 if ((dq->dq_flags & DQ_INODS) == 0 && 477 cp->c_uid == kauth_cred_getuid(cred)) { 478#if 0 479 printf("\nhfs: write failed, %s %s\n", 480 quotatypes[type], 481 "cnode quota exceeded for too long"); 482#endif 483 dq->dq_flags |= DQ_INODS; 484 } 485 dqunlock(dq); 486 487 return (EDQUOT); 488 } 489 } 490 dqunlock(dq); 491 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; 505 struct hfsmount *hfsmp = VTOHFS(vp); 506 register int i; 507 508 for (i = 0; i < MAXQUOTAS; i++) { 509 if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP) 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 */ 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 544int 545hfs_quotaon(p, mp, type, fnamep) 546 struct proc *p; 547 struct mount *mp; 548 register int type; 549 caddr_t fnamep; 550{ 551 struct hfsmount *hfsmp = VFSTOHFS(mp); 552 struct quotafile *qfp; 553 struct vnode *vp; 554 int error = 0; 555 struct hfs_quotaon_cargs args; 556 557 /* Finish setting up quota structures. */ 558 dqhashinit(); 559 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; 568 } 569 if (!vnode_isreg(vp)) { 570 (void) vnode_close(vp, FREAD|FWRITE, NULL); 571 error = EACCES; 572 goto out; 573 } 574 vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA)); 575 HFS_MOUNT_LOCK(hfsmp, TRUE) 576 hfsmp->hfs_flags |= HFS_QUOTAS; 577 HFS_MOUNT_UNLOCK(hfsmp, TRUE); 578 vnode_setnoflush(vp); 579 /* 580 * Save the credential of the process that turned on quotas. 581 */ 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 591 if (IS_VALID_CRED(qfp->qf_cred)) 592 kauth_cred_unref(&qfp->qf_cred); 593 qfp->qf_vp = NULLVP; 594 goto out; 595 } 596 qf_put(qfp, QTF_OPENING); 597 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. 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 607 */ 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) { 615 hfs_quotaoff(p, mp, type); 616 } 617 return (error); 618 619out: 620 qf_put(qfp, QTF_OPENING); 621 622 return (error); 623} 624 625 626/* 627 * Q_QUOTAOFF - turn off disk quotas for a filesystem. 628 */ 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 652int 653hfs_quotaoff(__unused struct proc *p, struct mount *mp, register int type) 654{ 655 struct vnode *qvp; 656 struct hfsmount *hfsmp = VFSTOHFS(mp); 657 struct quotafile *qfp; 658 int error; 659 struct hfs_quotaoff_cargs args; 660 661 /* 662 * If quotas haven't been initialized, there's no work to be done. 663 */ 664 if (!dqisinitialized()) 665 return (0); 666 667 qfp = &hfsmp->hfs_qfiles[type]; 668 669 if ( (qf_get(qfp, QTF_CLOSING)) ) 670 return (0); 671 qvp = qfp->qf_vp; 672 673 /* 674 * Sync out any orpaned dirty dquot entries. 675 */ 676 dqsync_orphans(qfp); 677 678 /* 679 * Search vnodes associated with this mount point, 680 * deleting any references to quota file being closed. 681 * 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 686 */ 687 args.type = type; 688 689 vnode_iterate(mp, VNODE_WAIT, hfs_quotaoff_callback, (void *)&args); 690 691 dqflush(qvp); 692 /* Finish tearing down the quota file */ 693 dqfileclose(qfp, type); 694 695 vnode_clearnoflush(qvp); 696 error = vnode_close(qvp, FREAD|FWRITE, NULL); 697 698 qfp->qf_vp = NULLVP; 699 700 if (IS_VALID_CRED(qfp->qf_cred)) 701 kauth_cred_unref(&qfp->qf_cred); 702 for (type = 0; type < MAXQUOTAS; type++) 703 if (hfsmp->hfs_qfiles[type].qf_vp != NULLVP) 704 break; 705 if (type == MAXQUOTAS) { 706 vfs_clearflags(mp, (u_int64_t)((unsigned int)MNT_QUOTA)); 707 HFS_MOUNT_LOCK(hfsmp, TRUE) 708 hfsmp->hfs_flags &= ~HFS_QUOTAS; 709 HFS_MOUNT_UNLOCK(hfsmp, TRUE); 710 } 711 712 qf_put(qfp, QTF_CLOSING); 713 714 return (error); 715} 716 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 779/* 780 * Q_GETQUOTA - return current values in a dqblk structure. 781 */ 782int 783hfs_getquota(mp, id, type, datap) 784 struct mount *mp; 785 u_int32_t id; 786 int type; 787 caddr_t datap; 788{ 789 struct dquot *dq; 790 int error; 791 792 error = dqget(id, &VFSTOHFS(mp)->hfs_qfiles[type], type, &dq); 793 if (error) 794 return (error); 795 dqlock(dq); 796 797 bcopy(&dq->dq_dqb, datap, sizeof(dq->dq_dqb)); 798 799 dqunlock(dq); 800 dqrele(dq); 801 802 return (error); 803} 804 805/* 806 * Q_SETQUOTA - assign an entire dqblk structure. 807 */ 808int 809hfs_setquota(mp, id, type, datap) 810 struct mount *mp; 811 u_int32_t id; 812 int type; 813 caddr_t datap; 814{ 815 struct dquot *dq; 816 struct hfsmount *hfsmp = VFSTOHFS(mp); 817 struct dqblk * newlimp = (struct dqblk *) datap; 818 struct timeval tv; 819 int error; 820 821 error = dqget(id, &hfsmp->hfs_qfiles[type], type, &dq); 822 if (error) 823 return (error); 824 dqlock(dq); 825 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 */ 831 newlimp->dqb_curbytes = dq->dq_curbytes; 832 newlimp->dqb_curinodes = dq->dq_curinodes; 833 if (dq->dq_id != 0) { 834 newlimp->dqb_btime = dq->dq_btime; 835 newlimp->dqb_itime = dq->dq_itime; 836 } 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)); 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; 860 861 dqunlock(dq); 862 dqrele(dq); 863 864 return (0); 865} 866 867/* 868 * Q_SETUSE - set current cnode and byte usage. 869 */ 870int 871hfs_setuse(mp, id, type, datap) 872 struct mount *mp; 873 u_int32_t id; 874 int type; 875 caddr_t datap; 876{ 877 struct hfsmount *hfsmp = VFSTOHFS(mp); 878 struct dquot *dq; 879 struct timeval tv; 880 int error; 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); 887 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 && 893 quotablkp->dqb_curbytes >= dq->dq_bsoftlimit) { 894 microuptime(&tv); 895 dq->dq_btime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_btime; 896 } 897 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && 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; 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; 909 910 dqunlock(dq); 911 dqrele(dq); 912 913 return (0); 914} 915 916 917/* 918 * Q_SYNC - sync quota files to disk. 919 */ 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 937int 938hfs_qsync(mp) 939 struct mount *mp; 940{ 941 struct hfsmount *hfsmp = VFSTOHFS(mp); 942 int i; 943 944 if (!dqisinitialized()) 945 return (0); 946 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); 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 964 /* 965 * Search vnodes associated with this mount point, 966 * synchronizing any modified dquot structures. 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 972 */ 973 vnode_iterate(mp, 0, hfs_qsync_callback, (void *)NULL); 974 975 return (0); 976} 977 978/* 979 * Q_QUOTASTAT - get quota on/off status 980 */ 981int 982hfs_quotastat(mp, type, datap) 983 struct mount *mp; 984 register int type; 985 caddr_t datap; 986{ 987 struct hfsmount *hfsmp = VFSTOHFS(mp); 988 int error = 0; 989 int qstat; 990 991 if ((((unsigned int)vfs_flags(mp)) & MNT_QUOTA) && (hfsmp->hfs_qfiles[type].qf_vp != NULLVP)) 992 qstat = 1; /* quotas are on for this type */ 993 else 994 qstat = 0; /* quotas are off for this type */ 995 996 *((int *)datap) = qstat; 997 return (error); 998} 999 1000