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