1/* 2 * Copyright (c) 2002-2006 Apple Computer, 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 * @(#)vfs_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/malloc.h> 71#include <sys/file_internal.h> 72#include <sys/proc_internal.h> 73#include <sys/vnode_internal.h> 74#include <sys/mount_internal.h> 75#include <sys/quota.h> 76#include <sys/uio_internal.h> 77 78#include <libkern/OSByteOrder.h> 79 80 81/* vars for quota file lock */ 82lck_grp_t * qf_lck_grp; 83lck_grp_attr_t * qf_lck_grp_attr; 84lck_attr_t * qf_lck_attr; 85 86/* vars for quota list lock */ 87lck_grp_t * quota_list_lck_grp; 88lck_grp_attr_t * quota_list_lck_grp_attr; 89lck_attr_t * quota_list_lck_attr; 90lck_mtx_t * quota_list_mtx_lock; 91 92/* Routines to lock and unlock the quota global data */ 93static int dq_list_lock(void); 94static void dq_list_unlock(void); 95 96static void dq_lock_internal(struct dquot *dq); 97static void dq_unlock_internal(struct dquot *dq); 98 99static u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS; 100 101 102/* 103 * Code pertaining to management of the in-core dquot data structures. 104 */ 105#define DQHASH(dqvp, id) \ 106 (&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash]) 107LIST_HEAD(dqhash, dquot) *dqhashtbl; 108u_long dqhash; 109 110#define DQUOTINC 5 /* minimum free dquots desired */ 111long numdquot, desireddquot = DQUOTINC; 112 113/* 114 * Dquot free list. 115 */ 116TAILQ_HEAD(dqfreelist, dquot) dqfreelist; 117/* 118 * Dquot dirty orphans list 119 */ 120TAILQ_HEAD(dqdirtylist, dquot) dqdirtylist; 121 122 123static int dqlookup(struct quotafile *, u_int32_t, struct dqblk *, u_int32_t *); 124static int dqsync_locked(struct dquot *dq); 125 126static void qf_lock(struct quotafile *); 127static void qf_unlock(struct quotafile *); 128static int qf_ref(struct quotafile *); 129static void qf_rele(struct quotafile *); 130 131 132/* 133 * Initialize locks for the quota system. 134 */ 135void 136dqinit(void) 137{ 138 /* 139 * Allocate quota list lock group attribute and group 140 */ 141 quota_list_lck_grp_attr= lck_grp_attr_alloc_init(); 142 quota_list_lck_grp = lck_grp_alloc_init("quota list", quota_list_lck_grp_attr); 143 144 /* 145 * Allocate qouta list lock attribute 146 */ 147 quota_list_lck_attr = lck_attr_alloc_init(); 148 149 /* 150 * Allocate quota list lock 151 */ 152 quota_list_mtx_lock = lck_mtx_alloc_init(quota_list_lck_grp, quota_list_lck_attr); 153 154 155 /* 156 * allocate quota file lock group attribute and group 157 */ 158 qf_lck_grp_attr= lck_grp_attr_alloc_init(); 159 qf_lck_grp = lck_grp_alloc_init("quota file", qf_lck_grp_attr); 160 161 /* 162 * Allocate quota file lock attribute 163 */ 164 qf_lck_attr = lck_attr_alloc_init(); 165} 166 167/* 168 * Report whether dqhashinit has been run. 169 */ 170int 171dqisinitialized(void) 172{ 173 return (dqhashtbl != NULL); 174} 175 176/* 177 * Initialize hash table for dquot structures. 178 */ 179void 180dqhashinit(void) 181{ 182 dq_list_lock(); 183 if (dqisinitialized()) 184 goto out; 185 186 TAILQ_INIT(&dqfreelist); 187 TAILQ_INIT(&dqdirtylist); 188 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); 189out: 190 dq_list_unlock(); 191} 192 193 194static volatile int dq_list_lock_cnt = 0; 195 196static int 197dq_list_lock(void) 198{ 199 lck_mtx_lock(quota_list_mtx_lock); 200 return ++dq_list_lock_cnt; 201} 202 203static int 204dq_list_lock_changed(int oldval) { 205 return (dq_list_lock_cnt != oldval); 206} 207 208static int 209dq_list_lock_val(void) { 210 return dq_list_lock_cnt; 211} 212 213void 214dq_list_unlock(void) 215{ 216 lck_mtx_unlock(quota_list_mtx_lock); 217} 218 219 220/* 221 * must be called with the quota_list_lock held 222 */ 223void 224dq_lock_internal(struct dquot *dq) 225{ 226 while (dq->dq_lflags & DQ_LLOCK) { 227 dq->dq_lflags |= DQ_LWANT; 228 msleep(&dq->dq_lflags, quota_list_mtx_lock, PVFS, "dq_lock_internal", NULL); 229 } 230 dq->dq_lflags |= DQ_LLOCK; 231} 232 233/* 234 * must be called with the quota_list_lock held 235 */ 236void 237dq_unlock_internal(struct dquot *dq) 238{ 239 int wanted = dq->dq_lflags & DQ_LWANT; 240 241 dq->dq_lflags &= ~(DQ_LLOCK | DQ_LWANT); 242 243 if (wanted) 244 wakeup(&dq->dq_lflags); 245} 246 247void 248dqlock(struct dquot *dq) { 249 250 lck_mtx_lock(quota_list_mtx_lock); 251 252 dq_lock_internal(dq); 253 254 lck_mtx_unlock(quota_list_mtx_lock); 255} 256 257void 258dqunlock(struct dquot *dq) { 259 260 lck_mtx_lock(quota_list_mtx_lock); 261 262 dq_unlock_internal(dq); 263 264 lck_mtx_unlock(quota_list_mtx_lock); 265} 266 267 268 269int 270qf_get(struct quotafile *qfp, int type) 271{ 272 int error = 0; 273 274 dq_list_lock(); 275 276 switch (type) { 277 278 case QTF_OPENING: 279 while ( (qfp->qf_qflags & (QTF_OPENING | QTF_CLOSING)) ) { 280 if ( (qfp->qf_qflags & QTF_OPENING) ) { 281 error = EBUSY; 282 break; 283 } 284 if ( (qfp->qf_qflags & QTF_CLOSING) ) { 285 qfp->qf_qflags |= QTF_WANTED; 286 msleep(&qfp->qf_qflags, quota_list_mtx_lock, PVFS, "qf_get", NULL); 287 } 288 } 289 if (qfp->qf_vp != NULLVP) 290 error = EBUSY; 291 if (error == 0) 292 qfp->qf_qflags |= QTF_OPENING; 293 break; 294 295 case QTF_CLOSING: 296 if ( (qfp->qf_qflags & QTF_CLOSING) ) { 297 error = EBUSY; 298 break; 299 } 300 qfp->qf_qflags |= QTF_CLOSING; 301 302 while ( (qfp->qf_qflags & QTF_OPENING) || qfp->qf_refcnt ) { 303 qfp->qf_qflags |= QTF_WANTED; 304 msleep(&qfp->qf_qflags, quota_list_mtx_lock, PVFS, "qf_get", NULL); 305 } 306 if (qfp->qf_vp == NULLVP) { 307 qfp->qf_qflags &= ~QTF_CLOSING; 308 error = EBUSY; 309 } 310 break; 311 } 312 dq_list_unlock(); 313 314 return (error); 315} 316 317void 318qf_put(struct quotafile *qfp, int type) 319{ 320 321 dq_list_lock(); 322 323 switch (type) { 324 325 case QTF_OPENING: 326 case QTF_CLOSING: 327 qfp->qf_qflags &= ~type; 328 break; 329 } 330 if ( (qfp->qf_qflags & QTF_WANTED) ) { 331 qfp->qf_qflags &= ~QTF_WANTED; 332 wakeup(&qfp->qf_qflags); 333 } 334 dq_list_unlock(); 335} 336 337 338static void 339qf_lock(struct quotafile *qfp) 340{ 341 lck_mtx_lock(&qfp->qf_lock); 342} 343 344static void 345qf_unlock(struct quotafile *qfp) 346{ 347 lck_mtx_unlock(&qfp->qf_lock); 348} 349 350 351/* 352 * take a reference on the quota file while we're 353 * in dqget... this will prevent a quota_off from 354 * occurring while we're potentially playing with 355 * the quota file... the quota_off will stall until 356 * all the current references 'die'... once we start 357 * into quoto_off, all new references will be rejected 358 * we also don't want any dqgets being processed while 359 * we're in the middle of the quota_on... once we've 360 * actually got the quota file open and the associated 361 * struct quotafile inited, we can let them come through 362 * 363 * quota list lock must be held on entry 364 */ 365static int 366qf_ref(struct quotafile *qfp) 367{ 368 int error = 0; 369 370 if ( (qfp->qf_qflags & (QTF_OPENING | QTF_CLOSING)) || (qfp->qf_vp == NULLVP) ) 371 error = EINVAL; 372 else 373 qfp->qf_refcnt++; 374 375 return (error); 376} 377 378/* 379 * drop our reference and wakeup any waiters if 380 * we were the last one holding a ref 381 * 382 * quota list lock must be held on entry 383 */ 384static void 385qf_rele(struct quotafile *qfp) 386{ 387 qfp->qf_refcnt--; 388 389 if ( (qfp->qf_qflags & QTF_WANTED) && qfp->qf_refcnt == 0) { 390 qfp->qf_qflags &= ~QTF_WANTED; 391 wakeup(&qfp->qf_qflags); 392 } 393} 394 395 396void 397dqfileinit(struct quotafile *qfp) 398{ 399 qfp->qf_vp = NULLVP; 400 qfp->qf_qflags = 0; 401 402 lck_mtx_init(&qfp->qf_lock, qf_lck_grp, qf_lck_attr); 403} 404 405 406/* 407 * Initialize a quota file 408 * 409 * must be called with the quota file lock held 410 */ 411int 412dqfileopen(struct quotafile *qfp, int type) 413{ 414 struct dqfilehdr header; 415 struct vfs_context context; 416 off_t file_size; 417 uio_t auio; 418 int error = 0; 419 char uio_buf[ UIO_SIZEOF(1) ]; 420 421 context.vc_thread = current_thread(); 422 context.vc_ucred = qfp->qf_cred; 423 424 /* Obtain the file size */ 425 if ((error = vnode_size(qfp->qf_vp, &file_size, &context)) != 0) 426 goto out; 427 428 /* Read the file header */ 429 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, 430 &uio_buf[0], sizeof(uio_buf)); 431 uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header)); 432 error = VNOP_READ(qfp->qf_vp, auio, 0, &context); 433 if (error) 434 goto out; 435 else if (uio_resid(auio)) { 436 error = EINVAL; 437 goto out; 438 } 439 /* Sanity check the quota file header. */ 440 if ((OSSwapBigToHostInt32(header.dqh_magic) != quotamagic[type]) || 441 (OSSwapBigToHostInt32(header.dqh_version) > QF_VERSION) || 442 (!powerof2(OSSwapBigToHostInt32(header.dqh_maxentries))) || 443 (OSSwapBigToHostInt32(header.dqh_maxentries) > (file_size / sizeof(struct dqblk)))) { 444 error = EINVAL; 445 goto out; 446 } 447 /* Set up the time limits for this quota. */ 448 if (header.dqh_btime != 0) 449 qfp->qf_btime = OSSwapBigToHostInt32(header.dqh_btime); 450 else 451 qfp->qf_btime = MAX_DQ_TIME; 452 if (header.dqh_itime != 0) 453 qfp->qf_itime = OSSwapBigToHostInt32(header.dqh_itime); 454 else 455 qfp->qf_itime = MAX_IQ_TIME; 456 457 /* Calculate the hash table constants. */ 458 qfp->qf_maxentries = OSSwapBigToHostInt32(header.dqh_maxentries); 459 qfp->qf_entrycnt = OSSwapBigToHostInt32(header.dqh_entrycnt); 460 qfp->qf_shift = dqhashshift(qfp->qf_maxentries); 461out: 462 return (error); 463} 464 465/* 466 * Close down a quota file 467 */ 468void 469dqfileclose(struct quotafile *qfp, __unused int type) 470{ 471 struct dqfilehdr header; 472 struct vfs_context context; 473 uio_t auio; 474 char uio_buf[ UIO_SIZEOF(1) ]; 475 476 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, 477 &uio_buf[0], sizeof(uio_buf)); 478 uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header)); 479 480 context.vc_thread = current_thread(); 481 context.vc_ucred = qfp->qf_cred; 482 483 if (VNOP_READ(qfp->qf_vp, auio, 0, &context) == 0) { 484 header.dqh_entrycnt = OSSwapHostToBigInt32(qfp->qf_entrycnt); 485 uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE); 486 uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header)); 487 (void) VNOP_WRITE(qfp->qf_vp, auio, 0, &context); 488 } 489} 490 491 492/* 493 * Obtain a dquot structure for the specified identifier and quota file 494 * reading the information from the file if necessary. 495 */ 496int 497dqget(u_int32_t id, struct quotafile *qfp, int type, struct dquot **dqp) 498{ 499 struct dquot *dq; 500 struct dquot *ndq = NULL; 501 struct dquot *fdq = NULL; 502 struct dqhash *dqh; 503 struct vnode *dqvp; 504 int error = 0; 505 int listlockval = 0; 506 507 if (!dqisinitialized()) { 508 *dqp = NODQUOT; 509 return (EINVAL); 510 } 511 512 if ( id == 0 || qfp->qf_vp == NULLVP ) { 513 *dqp = NODQUOT; 514 return (EINVAL); 515 } 516 dq_list_lock(); 517 518 if ( (qf_ref(qfp)) ) { 519 dq_list_unlock(); 520 521 *dqp = NODQUOT; 522 return (EINVAL); 523 } 524 if ( (dqvp = qfp->qf_vp) == NULLVP ) { 525 qf_rele(qfp); 526 dq_list_unlock(); 527 528 *dqp = NODQUOT; 529 return (EINVAL); 530 } 531 dqh = DQHASH(dqvp, id); 532 533relookup: 534 listlockval = dq_list_lock_val(); 535 536 /* 537 * Check the cache first. 538 */ 539 for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) { 540 if (dq->dq_id != id || 541 dq->dq_qfile->qf_vp != dqvp) 542 continue; 543 544 dq_lock_internal(dq); 545 if (dq_list_lock_changed(listlockval)) { 546 dq_unlock_internal(dq); 547 goto relookup; 548 } 549 550 /* 551 * dq_lock_internal may drop the quota_list_lock to msleep, so 552 * we need to re-evaluate the identity of this dq 553 */ 554 if (dq->dq_id != id || dq->dq_qfile == NULL || 555 dq->dq_qfile->qf_vp != dqvp) { 556 dq_unlock_internal(dq); 557 goto relookup; 558 } 559 /* 560 * Cache hit with no references. Take 561 * the structure off the free list. 562 */ 563 if (dq->dq_cnt++ == 0) { 564 if (dq->dq_flags & DQ_MOD) 565 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist); 566 else 567 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 568 } 569 dq_unlock_internal(dq); 570 571 if (fdq != NULL) { 572 /* 573 * we grabbed this from the free list in the first pass 574 * but we found the dq we were looking for in 575 * the cache the 2nd time through 576 * so stick it back on the free list and return the cached entry 577 */ 578 TAILQ_INSERT_HEAD(&dqfreelist, fdq, dq_freelist); 579 } 580 qf_rele(qfp); 581 dq_list_unlock(); 582 583 if (ndq != NULL) { 584 /* 585 * we allocated this in the first pass 586 * but we found the dq we were looking for in 587 * the cache the 2nd time through so free it 588 */ 589 _FREE(ndq, M_DQUOT); 590 } 591 *dqp = dq; 592 593 return (0); 594 } 595 /* 596 * Not in cache, allocate a new one. 597 */ 598 if (TAILQ_EMPTY(&dqfreelist) && 599 numdquot < MAXQUOTAS * desiredvnodes) 600 desireddquot += DQUOTINC; 601 602 if (fdq != NULL) { 603 /* 604 * we captured this from the free list 605 * in the first pass through, so go 606 * ahead and use it 607 */ 608 dq = fdq; 609 fdq = NULL; 610 } else if (numdquot < desireddquot) { 611 if (ndq == NULL) { 612 /* 613 * drop the quota list lock since MALLOC may block 614 */ 615 dq_list_unlock(); 616 617 ndq = (struct dquot *)_MALLOC(sizeof *dq, M_DQUOT, M_WAITOK); 618 bzero((char *)ndq, sizeof *dq); 619 620 listlockval = dq_list_lock(); 621 /* 622 * need to look for the entry again in the cache 623 * since we dropped the quota list lock and 624 * someone else may have beaten us to creating it 625 */ 626 goto relookup; 627 } else { 628 /* 629 * we allocated this in the first pass through 630 * and we're still under out target, so go 631 * ahead and use it 632 */ 633 dq = ndq; 634 ndq = NULL; 635 numdquot++; 636 } 637 } else { 638 if (TAILQ_EMPTY(&dqfreelist)) { 639 qf_rele(qfp); 640 dq_list_unlock(); 641 642 if (ndq) { 643 /* 644 * we allocated this in the first pass through 645 * but we're now at the limit of our cache size 646 * so free it 647 */ 648 _FREE(ndq, M_DQUOT); 649 } 650 tablefull("dquot"); 651 *dqp = NODQUOT; 652 return (EUSERS); 653 } 654 dq = TAILQ_FIRST(&dqfreelist); 655 656 dq_lock_internal(dq); 657 658 if (dq_list_lock_changed(listlockval) || dq->dq_cnt || (dq->dq_flags & DQ_MOD)) { 659 /* 660 * we lost the race while we weren't holding 661 * the quota list lock... dq_lock_internal 662 * will drop it to msleep... this dq has been 663 * reclaimed... go find another 664 */ 665 dq_unlock_internal(dq); 666 667 /* 668 * need to look for the entry again in the cache 669 * since we dropped the quota list lock and 670 * someone else may have beaten us to creating it 671 */ 672 goto relookup; 673 } 674 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 675 676 if (dq->dq_qfile != NULL) { 677 LIST_REMOVE(dq, dq_hash); 678 dq->dq_qfile = NULL; 679 dq->dq_id = 0; 680 } 681 dq_unlock_internal(dq); 682 683 /* 684 * because we may have dropped the quota list lock 685 * in the call to dq_lock_internal, we need to 686 * relookup in the hash in case someone else 687 * caused a dq with this identity to be created... 688 * if we don't find it, we'll use this one 689 */ 690 fdq = dq; 691 goto relookup; 692 } 693 /* 694 * we've either freshly allocated a dq 695 * or we've atomically pulled it out of 696 * the hash and freelists... no one else 697 * can have a reference, which means no 698 * one else can be trying to use this dq 699 */ 700 dq_lock_internal(dq); 701 if (dq_list_lock_changed(listlockval)) { 702 dq_unlock_internal(dq); 703 goto relookup; 704 } 705 706 /* 707 * Initialize the contents of the dquot structure. 708 */ 709 dq->dq_cnt = 1; 710 dq->dq_flags = 0; 711 dq->dq_id = id; 712 dq->dq_qfile = qfp; 713 dq->dq_type = type; 714 /* 715 * once we insert it in the hash and 716 * drop the quota_list_lock, it can be 717 * 'found'... however, we're still holding 718 * the dq_lock which will keep us from doing 719 * anything with it until we've finished 720 * initializing it... 721 */ 722 LIST_INSERT_HEAD(dqh, dq, dq_hash); 723 dq_list_unlock(); 724 725 if (ndq) { 726 /* 727 * we allocated this in the first pass through 728 * but we didn't need it, so free it after 729 * we've droped the quota list lock 730 */ 731 _FREE(ndq, M_DQUOT); 732 } 733 734 error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index); 735 736 /* 737 * I/O error in reading quota file, release 738 * quota structure and reflect problem to caller. 739 */ 740 if (error) { 741 dq_list_lock(); 742 743 dq->dq_id = 0; 744 dq->dq_qfile = NULL; 745 LIST_REMOVE(dq, dq_hash); 746 747 dq_unlock_internal(dq); 748 qf_rele(qfp); 749 dq_list_unlock(); 750 751 dqrele(dq); 752 753 *dqp = NODQUOT; 754 return (error); 755 } 756 /* 757 * Check for no limit to enforce. 758 * Initialize time values if necessary. 759 */ 760 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 761 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 762 dq->dq_flags |= DQ_FAKE; 763 if (dq->dq_id != 0) { 764 struct timeval tv; 765 766 microtime(&tv); 767 if (dq->dq_btime == 0) 768 dq->dq_btime = tv.tv_sec + qfp->qf_btime; 769 if (dq->dq_itime == 0) 770 dq->dq_itime = tv.tv_sec + qfp->qf_itime; 771 } 772 dq_list_lock(); 773 dq_unlock_internal(dq); 774 qf_rele(qfp); 775 dq_list_unlock(); 776 777 *dqp = dq; 778 return (0); 779} 780 781/* 782 * Lookup a dqblk structure for the specified identifier and 783 * quota file. If there is no entry for this identifier then 784 * one is inserted. The actual hash table index is returned. 785 */ 786static int 787dqlookup(struct quotafile *qfp, u_int32_t id, struct dqblk *dqb, uint32_t *index) 788{ 789 struct vnode *dqvp; 790 struct vfs_context context; 791 uio_t auio; 792 int i, skip, last; 793 u_int32_t mask; 794 int error = 0; 795 char uio_buf[ UIO_SIZEOF(1) ]; 796 797 798 qf_lock(qfp); 799 800 dqvp = qfp->qf_vp; 801 802 context.vc_thread = current_thread(); 803 context.vc_ucred = qfp->qf_cred; 804 805 mask = qfp->qf_maxentries - 1; 806 i = dqhash1(id, qfp->qf_shift, mask); 807 skip = dqhash2(id, mask); 808 809 for (last = (i + (qfp->qf_maxentries-1) * skip) & mask; 810 i != last; 811 i = (i + skip) & mask) { 812 auio = uio_createwithbuffer(1, dqoffset(i), UIO_SYSSPACE, UIO_READ, 813 &uio_buf[0], sizeof(uio_buf)); 814 uio_addiov(auio, CAST_USER_ADDR_T(dqb), sizeof (struct dqblk)); 815 error = VNOP_READ(dqvp, auio, 0, &context); 816 if (error) { 817 printf("dqlookup: error %d looking up id %u at index %d\n", error, id, i); 818 break; 819 } else if (uio_resid(auio)) { 820 error = EIO; 821 printf("dqlookup: error looking up id %u at index %d\n", id, i); 822 break; 823 } 824 /* 825 * An empty entry means there is no entry 826 * with that id. In this case a new dqb 827 * record will be inserted. 828 */ 829 if (dqb->dqb_id == 0) { 830 bzero(dqb, sizeof(struct dqblk)); 831 dqb->dqb_id = OSSwapHostToBigInt32(id); 832 /* 833 * Write back to reserve entry for this id 834 */ 835 uio_reset(auio, dqoffset(i), UIO_SYSSPACE, UIO_WRITE); 836 uio_addiov(auio, CAST_USER_ADDR_T(dqb), sizeof (struct dqblk)); 837 error = VNOP_WRITE(dqvp, auio, 0, &context); 838 if (uio_resid(auio) && error == 0) 839 error = EIO; 840 if (error == 0) 841 ++qfp->qf_entrycnt; 842 dqb->dqb_id = id; 843 break; 844 } 845 /* An id match means an entry was found. */ 846 if (OSSwapBigToHostInt32(dqb->dqb_id) == id) { 847 dqb->dqb_bhardlimit = OSSwapBigToHostInt64(dqb->dqb_bhardlimit); 848 dqb->dqb_bsoftlimit = OSSwapBigToHostInt64(dqb->dqb_bsoftlimit); 849 dqb->dqb_curbytes = OSSwapBigToHostInt64(dqb->dqb_curbytes); 850 dqb->dqb_ihardlimit = OSSwapBigToHostInt32(dqb->dqb_ihardlimit); 851 dqb->dqb_isoftlimit = OSSwapBigToHostInt32(dqb->dqb_isoftlimit); 852 dqb->dqb_curinodes = OSSwapBigToHostInt32(dqb->dqb_curinodes); 853 dqb->dqb_btime = OSSwapBigToHostInt32(dqb->dqb_btime); 854 dqb->dqb_itime = OSSwapBigToHostInt32(dqb->dqb_itime); 855 dqb->dqb_id = OSSwapBigToHostInt32(dqb->dqb_id); 856 break; 857 } 858 } 859 qf_unlock(qfp); 860 861 *index = i; /* remember index so we don't have to recompute it later */ 862 863 return (error); 864} 865 866 867/* 868 * Release a reference to a dquot. 869 */ 870void 871dqrele(struct dquot *dq) 872{ 873 874 if (dq == NODQUOT) 875 return; 876 dqlock(dq); 877 878 if (dq->dq_cnt > 1) { 879 dq->dq_cnt--; 880 881 dqunlock(dq); 882 return; 883 } 884 if (dq->dq_flags & DQ_MOD) 885 (void) dqsync_locked(dq); 886 dq->dq_cnt--; 887 888 dq_list_lock(); 889 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); 890 dq_unlock_internal(dq); 891 dq_list_unlock(); 892} 893 894/* 895 * Release a reference to a dquot but don't do any I/O. 896 */ 897void 898dqreclaim(struct dquot *dq) 899{ 900 901 if (dq == NODQUOT) 902 return; 903 904 dq_list_lock(); 905 dq_lock_internal(dq); 906 907 if (--dq->dq_cnt > 0) { 908 dq_unlock_internal(dq); 909 dq_list_unlock(); 910 return; 911 } 912 if (dq->dq_flags & DQ_MOD) 913 TAILQ_INSERT_TAIL(&dqdirtylist, dq, dq_freelist); 914 else 915 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); 916 917 dq_unlock_internal(dq); 918 dq_list_unlock(); 919} 920 921/* 922 * Update a quota file's orphaned disk quotas. 923 */ 924void 925dqsync_orphans(struct quotafile *qfp) 926{ 927 struct dquot *dq; 928 929 dq_list_lock(); 930 loop: 931 TAILQ_FOREACH(dq, &dqdirtylist, dq_freelist) { 932 if (dq->dq_qfile != qfp) 933 continue; 934 935 dq_lock_internal(dq); 936 937 if (dq->dq_qfile != qfp) { 938 /* 939 * the identity of this dq changed while 940 * the quota_list_lock was dropped 941 * dq_lock_internal can drop it to msleep 942 */ 943 dq_unlock_internal(dq); 944 goto loop; 945 } 946 if ((dq->dq_flags & DQ_MOD) == 0) { 947 /* 948 * someone cleaned and removed this from 949 * the dq from the dirty list while the 950 * quota_list_lock was dropped 951 */ 952 dq_unlock_internal(dq); 953 goto loop; 954 } 955 if (dq->dq_cnt != 0) 956 panic("dqsync_orphans: dquot in use"); 957 958 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist); 959 960 dq_list_unlock(); 961 /* 962 * we're still holding the dqlock at this point 963 * with the reference count == 0 964 * we shouldn't be able 965 * to pick up another one since we hold dqlock 966 */ 967 (void) dqsync_locked(dq); 968 969 dq_list_lock(); 970 971 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); 972 973 dq_unlock_internal(dq); 974 goto loop; 975 } 976 dq_list_unlock(); 977} 978 979int 980dqsync(struct dquot *dq) 981{ 982 int error = 0; 983 984 if (dq != NODQUOT) { 985 dqlock(dq); 986 987 if ( (dq->dq_flags & DQ_MOD) ) 988 error = dqsync_locked(dq); 989 990 dqunlock(dq); 991 } 992 return (error); 993} 994 995 996/* 997 * Update the disk quota in the quota file. 998 */ 999int 1000dqsync_locked(struct dquot *dq) 1001{ 1002 struct vfs_context context; 1003 struct vnode *dqvp; 1004 struct dqblk dqb, *dqblkp; 1005 uio_t auio; 1006 int error; 1007 char uio_buf[ UIO_SIZEOF(1) ]; 1008 1009 if (dq->dq_id == 0) { 1010 dq->dq_flags &= ~DQ_MOD; 1011 return (0); 1012 } 1013 if (dq->dq_qfile == NULL) 1014 panic("dqsync: NULL dq_qfile"); 1015 if ((dqvp = dq->dq_qfile->qf_vp) == NULLVP) 1016 panic("dqsync: NULL qf_vp"); 1017 1018 auio = uio_createwithbuffer(1, dqoffset(dq->dq_index), UIO_SYSSPACE, 1019 UIO_WRITE, &uio_buf[0], sizeof(uio_buf)); 1020 uio_addiov(auio, CAST_USER_ADDR_T(&dqb), sizeof (struct dqblk)); 1021 1022 context.vc_thread = current_thread(); /* XXX */ 1023 context.vc_ucred = dq->dq_qfile->qf_cred; 1024 1025 dqblkp = &dq->dq_dqb; 1026 dqb.dqb_bhardlimit = OSSwapHostToBigInt64(dqblkp->dqb_bhardlimit); 1027 dqb.dqb_bsoftlimit = OSSwapHostToBigInt64(dqblkp->dqb_bsoftlimit); 1028 dqb.dqb_curbytes = OSSwapHostToBigInt64(dqblkp->dqb_curbytes); 1029 dqb.dqb_ihardlimit = OSSwapHostToBigInt32(dqblkp->dqb_ihardlimit); 1030 dqb.dqb_isoftlimit = OSSwapHostToBigInt32(dqblkp->dqb_isoftlimit); 1031 dqb.dqb_curinodes = OSSwapHostToBigInt32(dqblkp->dqb_curinodes); 1032 dqb.dqb_btime = OSSwapHostToBigInt32(dqblkp->dqb_btime); 1033 dqb.dqb_itime = OSSwapHostToBigInt32(dqblkp->dqb_itime); 1034 dqb.dqb_id = OSSwapHostToBigInt32(dqblkp->dqb_id); 1035 dqb.dqb_spare[0] = 0; 1036 dqb.dqb_spare[1] = 0; 1037 dqb.dqb_spare[2] = 0; 1038 dqb.dqb_spare[3] = 0; 1039 1040 error = VNOP_WRITE(dqvp, auio, 0, &context); 1041 if (uio_resid(auio) && error == 0) 1042 error = EIO; 1043 dq->dq_flags &= ~DQ_MOD; 1044 1045 return (error); 1046} 1047 1048/* 1049 * Flush all entries from the cache for a particular vnode. 1050 */ 1051void 1052dqflush(struct vnode *vp) 1053{ 1054 struct dquot *dq, *nextdq; 1055 struct dqhash *dqh; 1056 1057 if (!dqisinitialized()) 1058 return; 1059 1060 /* 1061 * Move all dquot's that used to refer to this quota 1062 * file off their hash chains (they will eventually 1063 * fall off the head of the free list and be re-used). 1064 */ 1065 dq_list_lock(); 1066 1067 for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) { 1068 for (dq = dqh->lh_first; dq; dq = nextdq) { 1069 nextdq = dq->dq_hash.le_next; 1070 if (dq->dq_qfile->qf_vp != vp) 1071 continue; 1072 if (dq->dq_cnt) 1073 panic("dqflush: stray dquot"); 1074 LIST_REMOVE(dq, dq_hash); 1075 dq->dq_qfile = NULL; 1076 } 1077 } 1078 dq_list_unlock(); 1079} 1080 1081/* 1082 * LP64 support for munging dqblk structure. 1083 * XXX conversion of user_time_t to time_t loses precision; not an issue for 1084 * XXX us now, since we are only ever setting 32 bits worth of time into it. 1085 */ 1086__private_extern__ void 1087munge_dqblk(struct dqblk *dqblkp, struct user_dqblk *user_dqblkp, boolean_t to64) 1088{ 1089 if (to64) { 1090 /* munge kernel (32 bit) dqblk into user (64 bit) dqblk */ 1091 bcopy((caddr_t)dqblkp, (caddr_t)user_dqblkp, offsetof(struct dqblk, dqb_btime)); 1092 user_dqblkp->dqb_id = dqblkp->dqb_id; 1093 user_dqblkp->dqb_itime = dqblkp->dqb_itime; 1094 user_dqblkp->dqb_btime = dqblkp->dqb_btime; 1095 } 1096 else { 1097 1098 /* munge user (64 bit) dqblk into kernel (32 bit) dqblk */ 1099 bcopy((caddr_t)user_dqblkp, (caddr_t)dqblkp, offsetof(struct dqblk, dqb_btime)); 1100 dqblkp->dqb_id = user_dqblkp->dqb_id; 1101 dqblkp->dqb_itime = user_dqblkp->dqb_itime; /* XXX - lose precision */ 1102 dqblkp->dqb_btime = user_dqblkp->dqb_btime; /* XXX - lose precision */ 1103 } 1104} 1105