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