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