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