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