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.4 (Berkeley) 05/14/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 (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, LK_EXCLUSIVE, p)) 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 struct vnode *vp; 417 struct vnode *qvp, *nextvp; 418 struct ufsmount *ump = VFSTOUFS(mp); 419 struct dquot *dq; 420 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, LK_EXCLUSIVE, p)) 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 struct proc *p = curproc; /* XXX */ 594 struct vnode *vp, *nextvp; 595 struct dquot *dq; 596 int i, error; 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 simple_lock(&mntvnode_slock); 614 again: 615 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { 616 if (vp->v_mount != mp) 617 goto again; 618 nextvp = vp->v_mntvnodes.le_next; 619 simple_lock(&vp->v_interlock); 620 simple_unlock(&mntvnode_slock); 621 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); 622 if (error) { 623 simple_lock(&mntvnode_slock); 624 if (error == ENOENT) 625 goto again; 626 continue; 627 } 628 for (i = 0; i < MAXQUOTAS; i++) { 629 dq = VTOI(vp)->i_dquot[i]; 630 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) 631 dqsync(vp, dq); 632 } 633 vput(vp); 634 simple_lock(&mntvnode_slock); 635 if (vp->v_mntvnodes.le_next != nextvp) 636 goto again; 637 } 638 simple_unlock(&mntvnode_slock); 639 return (0); 640 } 641 642 /* 643 * Code pertaining to management of the in-core dquot data structures. 644 */ 645 #define DQHASH(dqvp, id) \ 646 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash]) 647 LIST_HEAD(dqhash, dquot) *dqhashtbl; 648 u_long dqhash; 649 650 /* 651 * Dquot free list. 652 */ 653 #define DQUOTINC 5 /* minimum free dquots desired */ 654 TAILQ_HEAD(dqfreelist, dquot) dqfreelist; 655 long numdquot, desireddquot = DQUOTINC; 656 657 /* 658 * Initialize the quota system. 659 */ 660 void 661 dqinit() 662 { 663 664 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); 665 TAILQ_INIT(&dqfreelist); 666 } 667 668 /* 669 * Obtain a dquot structure for the specified identifier and quota file 670 * reading the information from the file if necessary. 671 */ 672 int 673 dqget(vp, id, ump, type, dqp) 674 struct vnode *vp; 675 u_long id; 676 register struct ufsmount *ump; 677 register int type; 678 struct dquot **dqp; 679 { 680 struct proc *p = curproc; /* XXX */ 681 struct dquot *dq; 682 struct dqhash *dqh; 683 struct vnode *dqvp; 684 struct iovec aiov; 685 struct uio auio; 686 int error; 687 688 dqvp = ump->um_quotas[type]; 689 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { 690 *dqp = NODQUOT; 691 return (EINVAL); 692 } 693 /* 694 * Check the cache first. 695 */ 696 dqh = DQHASH(dqvp, id); 697 for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) { 698 if (dq->dq_id != id || 699 dq->dq_ump->um_quotas[dq->dq_type] != dqvp) 700 continue; 701 /* 702 * Cache hit with no references. Take 703 * the structure off the free list. 704 */ 705 if (dq->dq_cnt == 0) 706 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 707 DQREF(dq); 708 *dqp = dq; 709 return (0); 710 } 711 /* 712 * Not in cache, allocate a new one. 713 */ 714 if (dqfreelist.tqh_first == NODQUOT && 715 numdquot < MAXQUOTAS * desiredvnodes) 716 desireddquot += DQUOTINC; 717 if (numdquot < desireddquot) { 718 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK); 719 bzero((char *)dq, sizeof *dq); 720 numdquot++; 721 } else { 722 if ((dq = dqfreelist.tqh_first) == NULL) { 723 tablefull("dquot"); 724 *dqp = NODQUOT; 725 return (EUSERS); 726 } 727 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) 728 panic("free dquot isn't"); 729 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 730 LIST_REMOVE(dq, dq_hash); 731 } 732 /* 733 * Initialize the contents of the dquot structure. 734 */ 735 if (vp != dqvp) 736 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); 737 LIST_INSERT_HEAD(dqh, dq, dq_hash); 738 DQREF(dq); 739 dq->dq_flags = DQ_LOCK; 740 dq->dq_id = id; 741 dq->dq_ump = ump; 742 dq->dq_type = type; 743 auio.uio_iov = &aiov; 744 auio.uio_iovcnt = 1; 745 aiov.iov_base = (caddr_t)&dq->dq_dqb; 746 aiov.iov_len = sizeof (struct dqblk); 747 auio.uio_resid = sizeof (struct dqblk); 748 auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); 749 auio.uio_segflg = UIO_SYSSPACE; 750 auio.uio_rw = UIO_READ; 751 auio.uio_procp = (struct proc *)0; 752 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); 753 if (auio.uio_resid == sizeof(struct dqblk) && error == 0) 754 bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk)); 755 if (vp != dqvp) 756 VOP_UNLOCK(dqvp, 0, p); 757 if (dq->dq_flags & DQ_WANT) 758 wakeup((caddr_t)dq); 759 dq->dq_flags = 0; 760 /* 761 * I/O error in reading quota file, release 762 * quota structure and reflect problem to caller. 763 */ 764 if (error) { 765 LIST_REMOVE(dq, dq_hash); 766 dqrele(vp, dq); 767 *dqp = NODQUOT; 768 return (error); 769 } 770 /* 771 * Check for no limit to enforce. 772 * Initialize time values if necessary. 773 */ 774 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 775 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 776 dq->dq_flags |= DQ_FAKE; 777 if (dq->dq_id != 0) { 778 if (dq->dq_btime == 0) 779 dq->dq_btime = time.tv_sec + ump->um_btime[type]; 780 if (dq->dq_itime == 0) 781 dq->dq_itime = time.tv_sec + ump->um_itime[type]; 782 } 783 *dqp = dq; 784 return (0); 785 } 786 787 /* 788 * Obtain a reference to a dquot. 789 */ 790 void 791 dqref(dq) 792 struct dquot *dq; 793 { 794 795 dq->dq_cnt++; 796 } 797 798 /* 799 * Release a reference to a dquot. 800 */ 801 void 802 dqrele(vp, dq) 803 struct vnode *vp; 804 register struct dquot *dq; 805 { 806 807 if (dq == NODQUOT) 808 return; 809 if (dq->dq_cnt > 1) { 810 dq->dq_cnt--; 811 return; 812 } 813 if (dq->dq_flags & DQ_MOD) 814 (void) dqsync(vp, dq); 815 if (--dq->dq_cnt > 0) 816 return; 817 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); 818 } 819 820 /* 821 * Update the disk quota in the quota file. 822 */ 823 int 824 dqsync(vp, dq) 825 struct vnode *vp; 826 struct dquot *dq; 827 { 828 struct proc *p = curproc; /* XXX */ 829 struct vnode *dqvp; 830 struct iovec aiov; 831 struct uio auio; 832 int error; 833 834 if (dq == NODQUOT) 835 panic("dqsync: dquot"); 836 if ((dq->dq_flags & DQ_MOD) == 0) 837 return (0); 838 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) 839 panic("dqsync: file"); 840 if (vp != dqvp) 841 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); 842 while (dq->dq_flags & DQ_LOCK) { 843 dq->dq_flags |= DQ_WANT; 844 sleep((caddr_t)dq, PINOD+2); 845 if ((dq->dq_flags & DQ_MOD) == 0) { 846 if (vp != dqvp) 847 VOP_UNLOCK(dqvp, 0, p); 848 return (0); 849 } 850 } 851 dq->dq_flags |= DQ_LOCK; 852 auio.uio_iov = &aiov; 853 auio.uio_iovcnt = 1; 854 aiov.iov_base = (caddr_t)&dq->dq_dqb; 855 aiov.iov_len = sizeof (struct dqblk); 856 auio.uio_resid = sizeof (struct dqblk); 857 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); 858 auio.uio_segflg = UIO_SYSSPACE; 859 auio.uio_rw = UIO_WRITE; 860 auio.uio_procp = (struct proc *)0; 861 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); 862 if (auio.uio_resid && error == 0) 863 error = EIO; 864 if (dq->dq_flags & DQ_WANT) 865 wakeup((caddr_t)dq); 866 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); 867 if (vp != dqvp) 868 VOP_UNLOCK(dqvp, 0, p); 869 return (error); 870 } 871 872 /* 873 * Flush all entries from the cache for a particular vnode. 874 */ 875 void 876 dqflush(vp) 877 register struct vnode *vp; 878 { 879 register struct dquot *dq, *nextdq; 880 struct dqhash *dqh; 881 882 /* 883 * Move all dquot's that used to refer to this quota 884 * file off their hash chains (they will eventually 885 * fall off the head of the free list and be re-used). 886 */ 887 for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) { 888 for (dq = dqh->lh_first; dq; dq = nextdq) { 889 nextdq = dq->dq_hash.le_next; 890 if (dq->dq_ump->um_quotas[dq->dq_type] != vp) 891 continue; 892 if (dq->dq_cnt) 893 panic("dqflush: stray dquot"); 894 LIST_REMOVE(dq, dq_hash); 895 dq->dq_ump = (struct ufsmount *)0; 896 } 897 } 898 } 899