1 /* 2 * Copyright (c) 1982, 1986, 1990, 1993 3 * The Regents of the University of California. 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 8.3 (Berkeley) 08/19/94 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 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p); 347 if (error = vn_open(&nd, FREAD|FWRITE, 0)) 348 return (error); 349 vp = nd.ni_vp; 350 VOP_UNLOCK(vp); 351 if (vp->v_type != VREG) { 352 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); 353 return (EACCES); 354 } 355 if (vfs_busy(mp)) { 356 (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); 357 return (EBUSY); 358 } 359 if (*vpp != vp) 360 quotaoff(p, mp, type); 361 ump->um_qflags[type] |= QTF_OPENING; 362 mp->mnt_flag |= MNT_QUOTA; 363 vp->v_flag |= VSYSTEM; 364 *vpp = vp; 365 /* 366 * Save the credential of the process that turned on quotas. 367 * Set up the time limits for this quota. 368 */ 369 crhold(p->p_ucred); 370 ump->um_cred[type] = p->p_ucred; 371 ump->um_btime[type] = MAX_DQ_TIME; 372 ump->um_itime[type] = MAX_IQ_TIME; 373 if (dqget(NULLVP, 0, ump, type, &dq) == 0) { 374 if (dq->dq_btime > 0) 375 ump->um_btime[type] = dq->dq_btime; 376 if (dq->dq_itime > 0) 377 ump->um_itime[type] = dq->dq_itime; 378 dqrele(NULLVP, dq); 379 } 380 /* 381 * Search vnodes associated with this mount point, 382 * adding references to quota file being opened. 383 * NB: only need to add dquot's for inodes being modified. 384 */ 385 again: 386 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { 387 nextvp = vp->v_mntvnodes.le_next; 388 if (vp->v_writecount == 0) 389 continue; 390 if (vget(vp, 1)) 391 goto again; 392 if (error = getinoquota(VTOI(vp))) { 393 vput(vp); 394 break; 395 } 396 vput(vp); 397 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp) 398 goto again; 399 } 400 ump->um_qflags[type] &= ~QTF_OPENING; 401 if (error) 402 quotaoff(p, mp, type); 403 vfs_unbusy(mp); 404 return (error); 405 } 406 407 /* 408 * Q_QUOTAOFF - turn off disk quotas for a filesystem. 409 */ 410 int 411 quotaoff(p, mp, type) 412 struct proc *p; 413 struct mount *mp; 414 register int type; 415 { 416 register struct vnode *vp; 417 struct vnode *qvp, *nextvp; 418 struct ufsmount *ump = VFSTOUFS(mp); 419 register struct dquot *dq; 420 register struct inode *ip; 421 int error; 422 423 if ((mp->mnt_flag & MNT_MPBUSY) == 0) 424 panic("quotaoff: not busy"); 425 if ((qvp = ump->um_quotas[type]) == NULLVP) 426 return (0); 427 ump->um_qflags[type] |= QTF_CLOSING; 428 /* 429 * Search vnodes associated with this mount point, 430 * deleting any references to quota file being closed. 431 */ 432 again: 433 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { 434 nextvp = vp->v_mntvnodes.le_next; 435 if (vget(vp, 1)) 436 goto again; 437 ip = VTOI(vp); 438 dq = ip->i_dquot[type]; 439 ip->i_dquot[type] = NODQUOT; 440 dqrele(vp, dq); 441 vput(vp); 442 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp) 443 goto again; 444 } 445 dqflush(qvp); 446 qvp->v_flag &= ~VSYSTEM; 447 error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p); 448 ump->um_quotas[type] = NULLVP; 449 crfree(ump->um_cred[type]); 450 ump->um_cred[type] = NOCRED; 451 ump->um_qflags[type] &= ~QTF_CLOSING; 452 for (type = 0; type < MAXQUOTAS; type++) 453 if (ump->um_quotas[type] != NULLVP) 454 break; 455 if (type == MAXQUOTAS) 456 mp->mnt_flag &= ~MNT_QUOTA; 457 return (error); 458 } 459 460 /* 461 * Q_GETQUOTA - return current values in a dqblk structure. 462 */ 463 int 464 getquota(mp, id, type, addr) 465 struct mount *mp; 466 u_long id; 467 int type; 468 caddr_t addr; 469 { 470 struct dquot *dq; 471 int error; 472 473 if (error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) 474 return (error); 475 error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk)); 476 dqrele(NULLVP, dq); 477 return (error); 478 } 479 480 /* 481 * Q_SETQUOTA - assign an entire dqblk structure. 482 */ 483 int 484 setquota(mp, id, type, addr) 485 struct mount *mp; 486 u_long id; 487 int type; 488 caddr_t addr; 489 { 490 register struct dquot *dq; 491 struct dquot *ndq; 492 struct ufsmount *ump = VFSTOUFS(mp); 493 struct dqblk newlim; 494 int error; 495 496 if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk))) 497 return (error); 498 if (error = dqget(NULLVP, id, ump, type, &ndq)) 499 return (error); 500 dq = ndq; 501 while (dq->dq_flags & DQ_LOCK) { 502 dq->dq_flags |= DQ_WANT; 503 sleep((caddr_t)dq, PINOD+1); 504 } 505 /* 506 * Copy all but the current values. 507 * Reset time limit if previously had no soft limit or were 508 * under it, but now have a soft limit and are over it. 509 */ 510 newlim.dqb_curblocks = dq->dq_curblocks; 511 newlim.dqb_curinodes = dq->dq_curinodes; 512 if (dq->dq_id != 0) { 513 newlim.dqb_btime = dq->dq_btime; 514 newlim.dqb_itime = dq->dq_itime; 515 } 516 if (newlim.dqb_bsoftlimit && 517 dq->dq_curblocks >= newlim.dqb_bsoftlimit && 518 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) 519 newlim.dqb_btime = time.tv_sec + ump->um_btime[type]; 520 if (newlim.dqb_isoftlimit && 521 dq->dq_curinodes >= newlim.dqb_isoftlimit && 522 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) 523 newlim.dqb_itime = time.tv_sec + ump->um_itime[type]; 524 dq->dq_dqb = newlim; 525 if (dq->dq_curblocks < dq->dq_bsoftlimit) 526 dq->dq_flags &= ~DQ_BLKS; 527 if (dq->dq_curinodes < dq->dq_isoftlimit) 528 dq->dq_flags &= ~DQ_INODS; 529 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 530 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 531 dq->dq_flags |= DQ_FAKE; 532 else 533 dq->dq_flags &= ~DQ_FAKE; 534 dq->dq_flags |= DQ_MOD; 535 dqrele(NULLVP, dq); 536 return (0); 537 } 538 539 /* 540 * Q_SETUSE - set current inode and block usage. 541 */ 542 int 543 setuse(mp, id, type, addr) 544 struct mount *mp; 545 u_long id; 546 int type; 547 caddr_t addr; 548 { 549 register struct dquot *dq; 550 struct ufsmount *ump = VFSTOUFS(mp); 551 struct dquot *ndq; 552 struct dqblk usage; 553 int error; 554 555 if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk))) 556 return (error); 557 if (error = dqget(NULLVP, id, ump, type, &ndq)) 558 return (error); 559 dq = ndq; 560 while (dq->dq_flags & DQ_LOCK) { 561 dq->dq_flags |= DQ_WANT; 562 sleep((caddr_t)dq, PINOD+1); 563 } 564 /* 565 * Reset time limit if have a soft limit and were 566 * previously under it, but are now over it. 567 */ 568 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && 569 usage.dqb_curblocks >= dq->dq_bsoftlimit) 570 dq->dq_btime = time.tv_sec + ump->um_btime[type]; 571 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && 572 usage.dqb_curinodes >= dq->dq_isoftlimit) 573 dq->dq_itime = time.tv_sec + ump->um_itime[type]; 574 dq->dq_curblocks = usage.dqb_curblocks; 575 dq->dq_curinodes = usage.dqb_curinodes; 576 if (dq->dq_curblocks < dq->dq_bsoftlimit) 577 dq->dq_flags &= ~DQ_BLKS; 578 if (dq->dq_curinodes < dq->dq_isoftlimit) 579 dq->dq_flags &= ~DQ_INODS; 580 dq->dq_flags |= DQ_MOD; 581 dqrele(NULLVP, dq); 582 return (0); 583 } 584 585 /* 586 * Q_SYNC - sync quota files to disk. 587 */ 588 int 589 qsync(mp) 590 struct mount *mp; 591 { 592 struct ufsmount *ump = VFSTOUFS(mp); 593 register struct vnode *vp, *nextvp; 594 register struct dquot *dq; 595 register int i; 596 597 /* 598 * Check if the mount point has any quotas. 599 * If not, simply return. 600 */ 601 if ((mp->mnt_flag & MNT_MPBUSY) == 0) 602 panic("qsync: not busy"); 603 for (i = 0; i < MAXQUOTAS; i++) 604 if (ump->um_quotas[i] != NULLVP) 605 break; 606 if (i == MAXQUOTAS) 607 return (0); 608 /* 609 * Search vnodes associated with this mount point, 610 * synchronizing any modified dquot structures. 611 */ 612 again: 613 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { 614 nextvp = vp->v_mntvnodes.le_next; 615 if (VOP_ISLOCKED(vp)) 616 continue; 617 if (vget(vp, 1)) 618 goto again; 619 for (i = 0; i < MAXQUOTAS; i++) { 620 dq = VTOI(vp)->i_dquot[i]; 621 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) 622 dqsync(vp, dq); 623 } 624 vput(vp); 625 if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp) 626 goto again; 627 } 628 return (0); 629 } 630 631 /* 632 * Code pertaining to management of the in-core dquot data structures. 633 */ 634 #define DQHASH(dqvp, id) \ 635 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash]) 636 LIST_HEAD(dqhash, dquot) *dqhashtbl; 637 u_long dqhash; 638 639 /* 640 * Dquot free list. 641 */ 642 #define DQUOTINC 5 /* minimum free dquots desired */ 643 TAILQ_HEAD(dqfreelist, dquot) dqfreelist; 644 long numdquot, desireddquot = DQUOTINC; 645 646 /* 647 * Initialize the quota system. 648 */ 649 void 650 dqinit() 651 { 652 653 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); 654 TAILQ_INIT(&dqfreelist); 655 } 656 657 /* 658 * Obtain a dquot structure for the specified identifier and quota file 659 * reading the information from the file if necessary. 660 */ 661 int 662 dqget(vp, id, ump, type, dqp) 663 struct vnode *vp; 664 u_long id; 665 register struct ufsmount *ump; 666 register int type; 667 struct dquot **dqp; 668 { 669 register struct dquot *dq; 670 struct dqhash *dqh; 671 register struct vnode *dqvp; 672 struct iovec aiov; 673 struct uio auio; 674 int error; 675 676 dqvp = ump->um_quotas[type]; 677 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { 678 *dqp = NODQUOT; 679 return (EINVAL); 680 } 681 /* 682 * Check the cache first. 683 */ 684 dqh = DQHASH(dqvp, id); 685 for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) { 686 if (dq->dq_id != id || 687 dq->dq_ump->um_quotas[dq->dq_type] != dqvp) 688 continue; 689 /* 690 * Cache hit with no references. Take 691 * the structure off the free list. 692 */ 693 if (dq->dq_cnt == 0) 694 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 695 DQREF(dq); 696 *dqp = dq; 697 return (0); 698 } 699 /* 700 * Not in cache, allocate a new one. 701 */ 702 if (dqfreelist.tqh_first == NODQUOT && 703 numdquot < MAXQUOTAS * desiredvnodes) 704 desireddquot += DQUOTINC; 705 if (numdquot < desireddquot) { 706 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK); 707 bzero((char *)dq, sizeof *dq); 708 numdquot++; 709 } else { 710 if ((dq = dqfreelist.tqh_first) == NULL) { 711 tablefull("dquot"); 712 *dqp = NODQUOT; 713 return (EUSERS); 714 } 715 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) 716 panic("free dquot isn't"); 717 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 718 LIST_REMOVE(dq, dq_hash); 719 } 720 /* 721 * Initialize the contents of the dquot structure. 722 */ 723 if (vp != dqvp) 724 VOP_LOCK(dqvp); 725 LIST_INSERT_HEAD(dqh, dq, dq_hash); 726 DQREF(dq); 727 dq->dq_flags = DQ_LOCK; 728 dq->dq_id = id; 729 dq->dq_ump = ump; 730 dq->dq_type = type; 731 auio.uio_iov = &aiov; 732 auio.uio_iovcnt = 1; 733 aiov.iov_base = (caddr_t)&dq->dq_dqb; 734 aiov.iov_len = sizeof (struct dqblk); 735 auio.uio_resid = sizeof (struct dqblk); 736 auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); 737 auio.uio_segflg = UIO_SYSSPACE; 738 auio.uio_rw = UIO_READ; 739 auio.uio_procp = (struct proc *)0; 740 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); 741 if (auio.uio_resid == sizeof(struct dqblk) && error == 0) 742 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk)); 743 if (vp != dqvp) 744 VOP_UNLOCK(dqvp); 745 if (dq->dq_flags & DQ_WANT) 746 wakeup((caddr_t)dq); 747 dq->dq_flags = 0; 748 /* 749 * I/O error in reading quota file, release 750 * quota structure and reflect problem to caller. 751 */ 752 if (error) { 753 LIST_REMOVE(dq, dq_hash); 754 dqrele(vp, dq); 755 *dqp = NODQUOT; 756 return (error); 757 } 758 /* 759 * Check for no limit to enforce. 760 * Initialize time values if necessary. 761 */ 762 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 763 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 764 dq->dq_flags |= DQ_FAKE; 765 if (dq->dq_id != 0) { 766 if (dq->dq_btime == 0) 767 dq->dq_btime = time.tv_sec + ump->um_btime[type]; 768 if (dq->dq_itime == 0) 769 dq->dq_itime = time.tv_sec + ump->um_itime[type]; 770 } 771 *dqp = dq; 772 return (0); 773 } 774 775 /* 776 * Obtain a reference to a dquot. 777 */ 778 void 779 dqref(dq) 780 struct dquot *dq; 781 { 782 783 dq->dq_cnt++; 784 } 785 786 /* 787 * Release a reference to a dquot. 788 */ 789 void 790 dqrele(vp, dq) 791 struct vnode *vp; 792 register struct dquot *dq; 793 { 794 795 if (dq == NODQUOT) 796 return; 797 if (dq->dq_cnt > 1) { 798 dq->dq_cnt--; 799 return; 800 } 801 if (dq->dq_flags & DQ_MOD) 802 (void) dqsync(vp, dq); 803 if (--dq->dq_cnt > 0) 804 return; 805 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); 806 } 807 808 /* 809 * Update the disk quota in the quota file. 810 */ 811 int 812 dqsync(vp, dq) 813 struct vnode *vp; 814 register struct dquot *dq; 815 { 816 struct vnode *dqvp; 817 struct iovec aiov; 818 struct uio auio; 819 int error; 820 821 if (dq == NODQUOT) 822 panic("dqsync: dquot"); 823 if ((dq->dq_flags & DQ_MOD) == 0) 824 return (0); 825 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) 826 panic("dqsync: file"); 827 if (vp != dqvp) 828 VOP_LOCK(dqvp); 829 while (dq->dq_flags & DQ_LOCK) { 830 dq->dq_flags |= DQ_WANT; 831 sleep((caddr_t)dq, PINOD+2); 832 if ((dq->dq_flags & DQ_MOD) == 0) { 833 if (vp != dqvp) 834 VOP_UNLOCK(dqvp); 835 return (0); 836 } 837 } 838 dq->dq_flags |= DQ_LOCK; 839 auio.uio_iov = &aiov; 840 auio.uio_iovcnt = 1; 841 aiov.iov_base = (caddr_t)&dq->dq_dqb; 842 aiov.iov_len = sizeof (struct dqblk); 843 auio.uio_resid = sizeof (struct dqblk); 844 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); 845 auio.uio_segflg = UIO_SYSSPACE; 846 auio.uio_rw = UIO_WRITE; 847 auio.uio_procp = (struct proc *)0; 848 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); 849 if (auio.uio_resid && error == 0) 850 error = EIO; 851 if (dq->dq_flags & DQ_WANT) 852 wakeup((caddr_t)dq); 853 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); 854 if (vp != dqvp) 855 VOP_UNLOCK(dqvp); 856 return (error); 857 } 858 859 /* 860 * Flush all entries from the cache for a particular vnode. 861 */ 862 void 863 dqflush(vp) 864 register struct vnode *vp; 865 { 866 register struct dquot *dq, *nextdq; 867 struct dqhash *dqh; 868 869 /* 870 * Move all dquot's that used to refer to this quota 871 * file off their hash chains (they will eventually 872 * fall off the head of the free list and be re-used). 873 */ 874 for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) { 875 for (dq = dqh->lh_first; dq; dq = nextdq) { 876 nextdq = dq->dq_hash.le_next; 877 if (dq->dq_ump->um_quotas[dq->dq_type] != vp) 878 continue; 879 if (dq->dq_cnt) 880 panic("dqflush: stray dquot"); 881 LIST_REMOVE(dq, dq_hash); 882 dq->dq_ump = (struct ufsmount *)0; 883 } 884 } 885 } 886