1 /* 2 * Copyright (c) 1982, 1986, 1990 Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Robert Elz at The University of Melbourne. 7 * 8 * %sccs.include.redist.c% 9 * 10 * @(#)ufs_quota.c 7.12 (Berkeley) 11/01/91 11 */ 12 #include <sys/param.h> 13 #include <sys/kernel.h> 14 #include <sys/systm.h> 15 #include <sys/namei.h> 16 #include <sys/malloc.h> 17 #include <sys/file.h> 18 #include <sys/proc.h> 19 #include <sys/vnode.h> 20 #include <sys/mount.h> 21 22 #include <ufs/ufs/quota.h> 23 #include <ufs/ufs/inode.h> 24 #include <ufs/ufs/ufsmount.h> 25 #include <ufs/ufs/ufs_extern.h> 26 27 /* 28 * Quota name to error message mapping. 29 */ 30 static char *quotatypes[] = INITQFNAMES; 31 32 /* 33 * Set up the quotas for an inode. 34 * 35 * This routine completely defines the semantics of quotas. 36 * If other criterion want to be used to establish quotas, the 37 * MAXQUOTAS value in quotas.h should be increased, and the 38 * additional dquots set up here. 39 */ 40 int 41 getinoquota(ip) 42 register struct inode *ip; 43 { 44 struct ufsmount *ump; 45 struct vnode *vp = ITOV(ip); 46 int error; 47 48 ump = VFSTOUFS(vp->v_mount); 49 /* 50 * Set up the user quota based on file uid. 51 * EINVAL means that quotas are not enabled. 52 */ 53 if (ip->i_dquot[USRQUOTA] == NODQUOT && 54 (error = 55 dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) && 56 error != EINVAL) 57 return (error); 58 /* 59 * Set up the group quota based on file gid. 60 * EINVAL means that quotas are not enabled. 61 */ 62 if (ip->i_dquot[GRPQUOTA] == NODQUOT && 63 (error = 64 dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) && 65 error != EINVAL) 66 return (error); 67 return (0); 68 } 69 70 /* 71 * Update disk usage, and take corrective action. 72 */ 73 int 74 chkdq(ip, change, cred, flags) 75 register struct inode *ip; 76 long change; 77 struct ucred *cred; 78 int flags; 79 { 80 register struct dquot *dq; 81 register int i; 82 int ncurblocks, error; 83 84 #ifdef DIAGNOSTIC 85 if ((flags & CHOWN) == 0) 86 chkdquot(ip); 87 #endif 88 if (change == 0) 89 return (0); 90 if (change < 0) { 91 for (i = 0; i < MAXQUOTAS; i++) { 92 if ((dq = ip->i_dquot[i]) == NODQUOT) 93 continue; 94 while (dq->dq_flags & DQ_LOCK) { 95 dq->dq_flags |= DQ_WANT; 96 sleep((caddr_t)dq, PINOD+1); 97 } 98 ncurblocks = dq->dq_curblocks + change; 99 if (ncurblocks >= 0) 100 dq->dq_curblocks = ncurblocks; 101 else 102 dq->dq_curblocks = 0; 103 dq->dq_flags &= ~DQ_BLKS; 104 dq->dq_flags |= DQ_MOD; 105 } 106 return (0); 107 } 108 if ((flags & FORCE) == 0 && cred->cr_uid != 0) { 109 for (i = 0; i < MAXQUOTAS; i++) { 110 if ((dq = ip->i_dquot[i]) == NODQUOT) 111 continue; 112 if (error = chkdqchg(ip, change, cred, i)) 113 return (error); 114 } 115 } 116 for (i = 0; i < MAXQUOTAS; i++) { 117 if ((dq = ip->i_dquot[i]) == NODQUOT) 118 continue; 119 while (dq->dq_flags & DQ_LOCK) { 120 dq->dq_flags |= DQ_WANT; 121 sleep((caddr_t)dq, PINOD+1); 122 } 123 dq->dq_curblocks += change; 124 dq->dq_flags |= DQ_MOD; 125 } 126 return (0); 127 } 128 129 /* 130 * Check for a valid change to a users allocation. 131 * Issue an error message if appropriate. 132 */ 133 int 134 chkdqchg(ip, change, cred, type) 135 struct inode *ip; 136 long change; 137 struct ucred *cred; 138 int type; 139 { 140 register struct dquot *dq = ip->i_dquot[type]; 141 long ncurblocks = dq->dq_curblocks + change; 142 143 /* 144 * If user would exceed their hard limit, disallow space allocation. 145 */ 146 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { 147 if ((dq->dq_flags & DQ_BLKS) == 0 && 148 ip->i_uid == cred->cr_uid) { 149 uprintf("\n%s: write failed, %s disk limit reached\n", 150 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 151 quotatypes[type]); 152 dq->dq_flags |= DQ_BLKS; 153 } 154 return (EDQUOT); 155 } 156 /* 157 * If user is over their soft limit for too long, disallow space 158 * allocation. Reset time limit as they cross their soft limit. 159 */ 160 if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { 161 if (dq->dq_curblocks < dq->dq_bsoftlimit) { 162 dq->dq_btime = time.tv_sec + 163 VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type]; 164 if (ip->i_uid == cred->cr_uid) 165 uprintf("\n%s: warning, %s %s\n", 166 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 167 quotatypes[type], "disk quota exceeded"); 168 return (0); 169 } 170 if (time.tv_sec > dq->dq_btime) { 171 if ((dq->dq_flags & DQ_BLKS) == 0 && 172 ip->i_uid == cred->cr_uid) { 173 uprintf("\n%s: write failed, %s %s\n", 174 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 175 quotatypes[type], 176 "disk quota exceeded for too long"); 177 dq->dq_flags |= DQ_BLKS; 178 } 179 return (EDQUOT); 180 } 181 } 182 return (0); 183 } 184 185 /* 186 * Check the inode limit, applying corrective action. 187 */ 188 int 189 chkiq(ip, change, cred, flags) 190 register struct inode *ip; 191 long change; 192 struct ucred *cred; 193 int flags; 194 { 195 register struct dquot *dq; 196 register int i; 197 int ncurinodes, error; 198 199 #ifdef DIAGNOSTIC 200 if ((flags & CHOWN) == 0) 201 chkdquot(ip); 202 #endif 203 if (change == 0) 204 return (0); 205 if (change < 0) { 206 for (i = 0; i < MAXQUOTAS; i++) { 207 if ((dq = ip->i_dquot[i]) == NODQUOT) 208 continue; 209 while (dq->dq_flags & DQ_LOCK) { 210 dq->dq_flags |= DQ_WANT; 211 sleep((caddr_t)dq, PINOD+1); 212 } 213 ncurinodes = dq->dq_curinodes + change; 214 if (ncurinodes >= 0) 215 dq->dq_curinodes = ncurinodes; 216 else 217 dq->dq_curinodes = 0; 218 dq->dq_flags &= ~DQ_INODS; 219 dq->dq_flags |= DQ_MOD; 220 } 221 return (0); 222 } 223 if ((flags & FORCE) == 0 && cred->cr_uid != 0) { 224 for (i = 0; i < MAXQUOTAS; i++) { 225 if ((dq = ip->i_dquot[i]) == NODQUOT) 226 continue; 227 if (error = chkiqchg(ip, change, cred, i)) 228 return (error); 229 } 230 } 231 for (i = 0; i < MAXQUOTAS; i++) { 232 if ((dq = ip->i_dquot[i]) == NODQUOT) 233 continue; 234 while (dq->dq_flags & DQ_LOCK) { 235 dq->dq_flags |= DQ_WANT; 236 sleep((caddr_t)dq, PINOD+1); 237 } 238 dq->dq_curinodes += change; 239 dq->dq_flags |= DQ_MOD; 240 } 241 return (0); 242 } 243 244 /* 245 * Check for a valid change to a users allocation. 246 * Issue an error message if appropriate. 247 */ 248 int 249 chkiqchg(ip, change, cred, type) 250 struct inode *ip; 251 long change; 252 struct ucred *cred; 253 int type; 254 { 255 register struct dquot *dq = ip->i_dquot[type]; 256 long ncurinodes = dq->dq_curinodes + change; 257 258 /* 259 * If user would exceed their hard limit, disallow inode allocation. 260 */ 261 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { 262 if ((dq->dq_flags & DQ_INODS) == 0 && 263 ip->i_uid == cred->cr_uid) { 264 uprintf("\n%s: write failed, %s inode limit reached\n", 265 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 266 quotatypes[type]); 267 dq->dq_flags |= DQ_INODS; 268 } 269 return (EDQUOT); 270 } 271 /* 272 * If user is over their soft limit for too long, disallow inode 273 * allocation. Reset time limit as they cross their soft limit. 274 */ 275 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { 276 if (dq->dq_curinodes < dq->dq_isoftlimit) { 277 dq->dq_itime = time.tv_sec + 278 VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type]; 279 if (ip->i_uid == cred->cr_uid) 280 uprintf("\n%s: warning, %s %s\n", 281 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 282 quotatypes[type], "inode quota exceeded"); 283 return (0); 284 } 285 if (time.tv_sec > dq->dq_itime) { 286 if ((dq->dq_flags & DQ_INODS) == 0 && 287 ip->i_uid == cred->cr_uid) { 288 uprintf("\n%s: write failed, %s %s\n", 289 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 290 quotatypes[type], 291 "inode quota exceeded for too long"); 292 dq->dq_flags |= DQ_INODS; 293 } 294 return (EDQUOT); 295 } 296 } 297 return (0); 298 } 299 300 #ifdef DIAGNOSTIC 301 /* 302 * On filesystems with quotas enabled, it is an error for a file to change 303 * size and not to have a dquot structure associated with it. 304 */ 305 void 306 chkdquot(ip) 307 register struct inode *ip; 308 { 309 struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount); 310 register int i; 311 312 for (i = 0; i < MAXQUOTAS; i++) { 313 if (ump->um_quotas[i] == NULLVP || 314 (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING))) 315 continue; 316 if (ip->i_dquot[i] == NODQUOT) { 317 vprint("chkdquot: missing dquot", ITOV(ip)); 318 panic("missing dquot"); 319 } 320 } 321 } 322 #endif 323 324 /* 325 * Code to process quotactl commands. 326 */ 327 328 /* 329 * Q_QUOTAON - set up a quota file for a particular file system. 330 */ 331 int 332 quotaon(p, mp, type, fname) 333 struct proc *p; 334 struct mount *mp; 335 register int type; 336 caddr_t fname; 337 { 338 register struct ufsmount *ump = VFSTOUFS(mp); 339 register struct vnode *vp, **vpp; 340 struct vnode *nextvp; 341 struct dquot *dq; 342 int error; 343 struct nameidata nd; 344 345 vpp = &ump->um_quotas[type]; 346 nd.ni_segflg = UIO_USERSPACE; 347 nd.ni_dirp = fname; 348 if (error = vn_open(&nd, p, FREAD|FWRITE, 0)) 349 return (error); 350 vp = nd.ni_vp; 351 VOP_UNLOCK(vp); 352 if (vp->v_type != VREG) { 353 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); 354 return (EACCES); 355 } 356 if (vfs_busy(mp)) { 357 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); 358 return (EBUSY); 359 } 360 if (*vpp != vp) 361 quotaoff(p, mp, type); 362 ump->um_qflags[type] |= QTF_OPENING; 363 mp->mnt_flag |= MNT_QUOTA; 364 vp->v_flag |= VSYSTEM; 365 *vpp = vp; 366 /* 367 * Save the credential of the process that turned on quotas. 368 * Set up the time limits for this quota. 369 */ 370 crhold(p->p_ucred); 371 ump->um_cred[type] = p->p_ucred; 372 ump->um_btime[type] = MAX_DQ_TIME; 373 ump->um_itime[type] = MAX_IQ_TIME; 374 if (dqget(NULLVP, 0, ump, type, &dq) == 0) { 375 if (dq->dq_btime > 0) 376 ump->um_btime[type] = dq->dq_btime; 377 if (dq->dq_itime > 0) 378 ump->um_itime[type] = dq->dq_itime; 379 dqrele(NULLVP, dq); 380 } 381 /* 382 * Search vnodes associated with this mount point, 383 * adding references to quota file being opened. 384 * NB: only need to add dquot's for inodes being modified. 385 */ 386 again: 387 for (vp = mp->mnt_mounth; vp; vp = nextvp) { 388 nextvp = vp->v_mountf; 389 if (vp->v_writecount == 0) 390 continue; 391 if (vget(vp)) 392 goto again; 393 if (error = getinoquota(VTOI(vp))) { 394 vput(vp); 395 break; 396 } 397 vput(vp); 398 if (vp->v_mountf != nextvp || vp->v_mount != mp) 399 goto again; 400 } 401 ump->um_qflags[type] &= ~QTF_OPENING; 402 if (error) 403 quotaoff(p, mp, type); 404 vfs_unbusy(mp); 405 return (error); 406 } 407 408 /* 409 * Q_QUOTAOFF - turn off disk quotas for a filesystem. 410 */ 411 int 412 quotaoff(p, mp, type) 413 struct proc *p; 414 struct mount *mp; 415 register int type; 416 { 417 register struct vnode *vp; 418 struct vnode *qvp, *nextvp; 419 struct ufsmount *ump = VFSTOUFS(mp); 420 register struct dquot *dq; 421 register struct inode *ip; 422 int error; 423 424 if ((mp->mnt_flag & MNT_MPBUSY) == 0) 425 panic("quotaoff: not busy"); 426 if ((qvp = ump->um_quotas[type]) == NULLVP) 427 return (0); 428 ump->um_qflags[type] |= QTF_CLOSING; 429 /* 430 * Search vnodes associated with this mount point, 431 * deleting any references to quota file being closed. 432 */ 433 again: 434 for (vp = mp->mnt_mounth; vp; vp = nextvp) { 435 nextvp = vp->v_mountf; 436 if (vget(vp)) 437 goto again; 438 ip = VTOI(vp); 439 dq = ip->i_dquot[type]; 440 ip->i_dquot[type] = NODQUOT; 441 dqrele(vp, dq); 442 vput(vp); 443 if (vp->v_mountf != nextvp || vp->v_mount != mp) 444 goto again; 445 } 446 dqflush(qvp); 447 qvp->v_flag &= ~VSYSTEM; 448 error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p); 449 ump->um_quotas[type] = NULLVP; 450 crfree(ump->um_cred[type]); 451 ump->um_cred[type] = NOCRED; 452 ump->um_qflags[type] &= ~QTF_CLOSING; 453 for (type = 0; type < MAXQUOTAS; type++) 454 if (ump->um_quotas[type] != NULLVP) 455 break; 456 if (type == MAXQUOTAS) 457 mp->mnt_flag &= ~MNT_QUOTA; 458 return (error); 459 } 460 461 /* 462 * Q_GETQUOTA - return current values in a dqblk structure. 463 */ 464 int 465 getquota(mp, id, type, addr) 466 struct mount *mp; 467 u_long id; 468 int type; 469 caddr_t addr; 470 { 471 struct dquot *dq; 472 int error; 473 474 if (error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) 475 return (error); 476 error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk)); 477 dqrele(NULLVP, dq); 478 return (error); 479 } 480 481 /* 482 * Q_SETQUOTA - assign an entire dqblk structure. 483 */ 484 int 485 setquota(mp, id, type, addr) 486 struct mount *mp; 487 u_long id; 488 int type; 489 caddr_t addr; 490 { 491 register struct dquot *dq; 492 struct dquot *ndq; 493 struct ufsmount *ump = VFSTOUFS(mp); 494 struct dqblk newlim; 495 int error; 496 497 if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk))) 498 return (error); 499 if (error = dqget(NULLVP, id, ump, type, &ndq)) 500 return (error); 501 dq = ndq; 502 while (dq->dq_flags & DQ_LOCK) { 503 dq->dq_flags |= DQ_WANT; 504 sleep((caddr_t)dq, PINOD+1); 505 } 506 /* 507 * Copy all but the current values. 508 * Reset time limit if previously had no soft limit or were 509 * under it, but now have a soft limit and are over it. 510 */ 511 newlim.dqb_curblocks = dq->dq_curblocks; 512 newlim.dqb_curinodes = dq->dq_curinodes; 513 if (dq->dq_id != 0) { 514 newlim.dqb_btime = dq->dq_btime; 515 newlim.dqb_itime = dq->dq_itime; 516 } 517 if (newlim.dqb_bsoftlimit && 518 dq->dq_curblocks >= newlim.dqb_bsoftlimit && 519 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) 520 newlim.dqb_btime = time.tv_sec + ump->um_btime[type]; 521 if (newlim.dqb_isoftlimit && 522 dq->dq_curinodes >= newlim.dqb_isoftlimit && 523 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) 524 newlim.dqb_itime = time.tv_sec + ump->um_itime[type]; 525 dq->dq_dqb = newlim; 526 if (dq->dq_curblocks < dq->dq_bsoftlimit) 527 dq->dq_flags &= ~DQ_BLKS; 528 if (dq->dq_curinodes < dq->dq_isoftlimit) 529 dq->dq_flags &= ~DQ_INODS; 530 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 531 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 532 dq->dq_flags |= DQ_FAKE; 533 else 534 dq->dq_flags &= ~DQ_FAKE; 535 dq->dq_flags |= DQ_MOD; 536 dqrele(NULLVP, dq); 537 return (0); 538 } 539 540 /* 541 * Q_SETUSE - set current inode and block usage. 542 */ 543 int 544 setuse(mp, id, type, addr) 545 struct mount *mp; 546 u_long id; 547 int type; 548 caddr_t addr; 549 { 550 register struct dquot *dq; 551 struct ufsmount *ump = VFSTOUFS(mp); 552 struct dquot *ndq; 553 struct dqblk usage; 554 int error; 555 556 if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk))) 557 return (error); 558 if (error = dqget(NULLVP, id, ump, type, &ndq)) 559 return (error); 560 dq = ndq; 561 while (dq->dq_flags & DQ_LOCK) { 562 dq->dq_flags |= DQ_WANT; 563 sleep((caddr_t)dq, PINOD+1); 564 } 565 /* 566 * Reset time limit if have a soft limit and were 567 * previously under it, but are now over it. 568 */ 569 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && 570 usage.dqb_curblocks >= dq->dq_bsoftlimit) 571 dq->dq_btime = time.tv_sec + ump->um_btime[type]; 572 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && 573 usage.dqb_curinodes >= dq->dq_isoftlimit) 574 dq->dq_itime = time.tv_sec + ump->um_itime[type]; 575 dq->dq_curblocks = usage.dqb_curblocks; 576 dq->dq_curinodes = usage.dqb_curinodes; 577 if (dq->dq_curblocks < dq->dq_bsoftlimit) 578 dq->dq_flags &= ~DQ_BLKS; 579 if (dq->dq_curinodes < dq->dq_isoftlimit) 580 dq->dq_flags &= ~DQ_INODS; 581 dq->dq_flags |= DQ_MOD; 582 dqrele(NULLVP, dq); 583 return (0); 584 } 585 586 /* 587 * Q_SYNC - sync quota files to disk. 588 */ 589 int 590 qsync(mp) 591 struct mount *mp; 592 { 593 struct ufsmount *ump = VFSTOUFS(mp); 594 register struct vnode *vp, *nextvp; 595 register struct dquot *dq; 596 register int i; 597 598 /* 599 * Check if the mount point has any quotas. 600 * If not, simply return. 601 */ 602 if ((mp->mnt_flag & MNT_MPBUSY) == 0) 603 panic("qsync: not busy"); 604 for (i = 0; i < MAXQUOTAS; i++) 605 if (ump->um_quotas[i] != NULLVP) 606 break; 607 if (i == MAXQUOTAS) 608 return (0); 609 /* 610 * Search vnodes associated with this mount point, 611 * synchronizing any modified dquot structures. 612 */ 613 again: 614 for (vp = mp->mnt_mounth; vp; vp = nextvp) { 615 nextvp = vp->v_mountf; 616 if (VOP_ISLOCKED(vp)) 617 continue; 618 if (vget(vp)) 619 goto again; 620 for (i = 0; i < MAXQUOTAS; i++) { 621 dq = VTOI(vp)->i_dquot[i]; 622 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) 623 dqsync(vp, dq); 624 } 625 vput(vp); 626 if (vp->v_mountf != nextvp || vp->v_mount != mp) 627 goto again; 628 } 629 return (0); 630 } 631 632 /* 633 * Code pertaining to management of the in-core dquot data structures. 634 */ 635 636 /* 637 * Dquot cache - hash chain headers. 638 */ 639 union dqhead { 640 union dqhead *dqh_head[2]; 641 struct dquot *dqh_chain[2]; 642 }; 643 #define dqh_forw dqh_chain[0] 644 #define dqh_back dqh_chain[1] 645 646 union dqhead *dqhashtbl; 647 long dqhash; 648 649 /* 650 * Dquot free list. 651 */ 652 #define DQUOTINC 5 /* minimum free dquots desired */ 653 struct dquot *dqfreel, **dqback = &dqfreel; 654 long numdquot, desireddquot = DQUOTINC; 655 656 /* 657 * Initialize the quota system. 658 */ 659 void 660 dqinit() 661 { 662 register union dqhead *dhp; 663 register long dqhashsize; 664 665 dqhashsize = roundup((desiredvnodes + 1) * sizeof *dhp / 2, 666 NBPG * CLSIZE); 667 dqhashtbl = (union dqhead *)malloc(dqhashsize, M_DQUOT, M_WAITOK); 668 for (dqhash = 1; dqhash <= dqhashsize / sizeof *dhp; dqhash <<= 1) 669 /* void */; 670 dqhash = (dqhash >> 1) - 1; 671 for (dhp = &dqhashtbl[dqhash]; dhp >= dqhashtbl; dhp--) { 672 dhp->dqh_head[0] = dhp; 673 dhp->dqh_head[1] = dhp; 674 } 675 } 676 677 /* 678 * Obtain a dquot structure for the specified identifier and quota file 679 * reading the information from the file if necessary. 680 */ 681 int 682 dqget(vp, id, ump, type, dqp) 683 struct vnode *vp; 684 u_long id; 685 register struct ufsmount *ump; 686 register int type; 687 struct dquot **dqp; 688 { 689 register struct dquot *dq; 690 register union dqhead *dh; 691 register struct dquot *dp; 692 register struct vnode *dqvp; 693 struct iovec aiov; 694 struct uio auio; 695 int error; 696 697 dqvp = ump->um_quotas[type]; 698 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { 699 *dqp = NODQUOT; 700 return (EINVAL); 701 } 702 /* 703 * Check the cache first. 704 */ 705 dh = &dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash]; 706 for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = dq->dq_forw) { 707 if (dq->dq_id != id || 708 dq->dq_ump->um_quotas[dq->dq_type] != dqvp) 709 continue; 710 /* 711 * Cache hit with no references. Take 712 * the structure off the free list. 713 */ 714 if (dq->dq_cnt == 0) { 715 dp = dq->dq_freef; 716 if (dp != NODQUOT) 717 dp->dq_freeb = dq->dq_freeb; 718 else 719 dqback = dq->dq_freeb; 720 *dq->dq_freeb = dp; 721 } 722 DQREF(dq); 723 *dqp = dq; 724 return (0); 725 } 726 /* 727 * Not in cache, allocate a new one. 728 */ 729 if (dqfreel == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes) 730 desireddquot += DQUOTINC; 731 if (numdquot < desireddquot) { 732 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK); 733 bzero((char *)dq, sizeof *dq); 734 numdquot++; 735 } else { 736 if ((dq = dqfreel) == NULL) { 737 tablefull("dquot"); 738 *dqp = NODQUOT; 739 return (EUSERS); 740 } 741 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) 742 panic("free dquot isn't"); 743 if ((dp = dq->dq_freef) != NODQUOT) 744 dp->dq_freeb = &dqfreel; 745 else 746 dqback = &dqfreel; 747 dqfreel = dp; 748 dq->dq_freef = NULL; 749 dq->dq_freeb = NULL; 750 remque(dq); 751 } 752 /* 753 * Initialize the contents of the dquot structure. 754 */ 755 if (vp != dqvp) 756 VOP_LOCK(dqvp); 757 insque(dq, dh); 758 DQREF(dq); 759 dq->dq_flags = DQ_LOCK; 760 dq->dq_id = id; 761 dq->dq_ump = ump; 762 dq->dq_type = type; 763 auio.uio_iov = &aiov; 764 auio.uio_iovcnt = 1; 765 aiov.iov_base = (caddr_t)&dq->dq_dqb; 766 aiov.iov_len = sizeof (struct dqblk); 767 auio.uio_resid = sizeof (struct dqblk); 768 auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); 769 auio.uio_segflg = UIO_SYSSPACE; 770 auio.uio_rw = UIO_READ; 771 auio.uio_procp = (struct proc *)0; 772 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); 773 if (auio.uio_resid == sizeof(struct dqblk) && error == 0) 774 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk)); 775 if (vp != dqvp) 776 VOP_UNLOCK(dqvp); 777 if (dq->dq_flags & DQ_WANT) 778 wakeup((caddr_t)dq); 779 dq->dq_flags = 0; 780 /* 781 * I/O error in reading quota file, release 782 * quota structure and reflect problem to caller. 783 */ 784 if (error) { 785 remque(dq); 786 dq->dq_forw = dq; /* on a private, unfindable hash list */ 787 dq->dq_back = dq; 788 dqrele(vp, dq); 789 *dqp = NODQUOT; 790 return (error); 791 } 792 /* 793 * Check for no limit to enforce. 794 * Initialize time values if necessary. 795 */ 796 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 797 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 798 dq->dq_flags |= DQ_FAKE; 799 if (dq->dq_id != 0) { 800 if (dq->dq_btime == 0) 801 dq->dq_btime = time.tv_sec + ump->um_btime[type]; 802 if (dq->dq_itime == 0) 803 dq->dq_itime = time.tv_sec + ump->um_itime[type]; 804 } 805 *dqp = dq; 806 return (0); 807 } 808 809 /* 810 * Obtain a reference to a dquot. 811 */ 812 void 813 dqref(dq) 814 struct dquot *dq; 815 { 816 817 dq->dq_cnt++; 818 } 819 820 /* 821 * Release a reference to a dquot. 822 */ 823 void 824 dqrele(vp, dq) 825 struct vnode *vp; 826 register struct dquot *dq; 827 { 828 829 if (dq == NODQUOT) 830 return; 831 if (dq->dq_cnt > 1) { 832 dq->dq_cnt--; 833 return; 834 } 835 if (dq->dq_flags & DQ_MOD) 836 (void) dqsync(vp, dq); 837 if (--dq->dq_cnt > 0) 838 return; 839 if (dqfreel != NODQUOT) { 840 *dqback = dq; 841 dq->dq_freeb = dqback; 842 } else { 843 dqfreel = dq; 844 dq->dq_freeb = &dqfreel; 845 } 846 dq->dq_freef = NODQUOT; 847 dqback = &dq->dq_freef; 848 } 849 850 /* 851 * Update the disk quota in the quota file. 852 */ 853 int 854 dqsync(vp, dq) 855 struct vnode *vp; 856 register struct dquot *dq; 857 { 858 struct vnode *dqvp; 859 struct iovec aiov; 860 struct uio auio; 861 int error; 862 863 if (dq == NODQUOT) 864 panic("dqsync: dquot"); 865 if ((dq->dq_flags & DQ_MOD) == 0) 866 return (0); 867 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) 868 panic("dqsync: file"); 869 if (vp != dqvp) 870 VOP_LOCK(dqvp); 871 while (dq->dq_flags & DQ_LOCK) { 872 dq->dq_flags |= DQ_WANT; 873 sleep((caddr_t)dq, PINOD+2); 874 if ((dq->dq_flags & DQ_MOD) == 0) { 875 if (vp != dqvp) 876 VOP_UNLOCK(dqvp); 877 return (0); 878 } 879 } 880 dq->dq_flags |= DQ_LOCK; 881 auio.uio_iov = &aiov; 882 auio.uio_iovcnt = 1; 883 aiov.iov_base = (caddr_t)&dq->dq_dqb; 884 aiov.iov_len = sizeof (struct dqblk); 885 auio.uio_resid = sizeof (struct dqblk); 886 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); 887 auio.uio_segflg = UIO_SYSSPACE; 888 auio.uio_rw = UIO_WRITE; 889 auio.uio_procp = (struct proc *)0; 890 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); 891 if (auio.uio_resid && error == 0) 892 error = EIO; 893 if (dq->dq_flags & DQ_WANT) 894 wakeup((caddr_t)dq); 895 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); 896 if (vp != dqvp) 897 VOP_UNLOCK(dqvp); 898 return (error); 899 } 900 901 /* 902 * Flush all entries from the cache for a particular vnode. 903 */ 904 void 905 dqflush(vp) 906 register struct vnode *vp; 907 { 908 register union dqhead *dh; 909 register struct dquot *dq, *nextdq; 910 911 /* 912 * Move all dquot's that used to refer to this quota 913 * file off their hash chains (they will eventually 914 * fall off the head of the free list and be re-used). 915 */ 916 for (dh = &dqhashtbl[dqhash]; dh >= dqhashtbl; dh--) { 917 for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = nextdq) { 918 nextdq = dq->dq_forw; 919 if (dq->dq_ump->um_quotas[dq->dq_type] != vp) 920 continue; 921 if (dq->dq_cnt) 922 panic("dqflush: stray dquot"); 923 remque(dq); 924 dq->dq_forw = dq; 925 dq->dq_back = dq; 926 dq->dq_ump = (struct ufsmount *)0; 927 } 928 } 929 } 930