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 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include "opt_ffs.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/fcntl.h> 43 #include <sys/kernel.h> 44 #include <sys/lock.h> 45 #include <sys/malloc.h> 46 #include <sys/mount.h> 47 #include <sys/mutex.h> 48 #include <sys/namei.h> 49 #include <sys/priv.h> 50 #include <sys/proc.h> 51 #include <sys/socket.h> 52 #include <sys/stat.h> 53 #include <sys/sysctl.h> 54 #include <sys/vnode.h> 55 56 #include <ufs/ufs/extattr.h> 57 #include <ufs/ufs/quota.h> 58 #include <ufs/ufs/inode.h> 59 #include <ufs/ufs/ufsmount.h> 60 #include <ufs/ufs/ufs_extern.h> 61 62 static int unprivileged_get_quota = 0; 63 SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_get_quota, CTLFLAG_RW, 64 &unprivileged_get_quota, 0, 65 "Unprivileged processes may retrieve quotas for other uids and gids"); 66 67 static MALLOC_DEFINE(M_DQUOT, "ufs_quota", "UFS quota entries"); 68 69 /* 70 * Quota name to error message mapping. 71 */ 72 static char *quotatypes[] = INITQFNAMES; 73 74 static int chkdqchg(struct inode *, ufs2_daddr_t, struct ucred *, int); 75 static int chkiqchg(struct inode *, int, struct ucred *, int); 76 static int dqget(struct vnode *, 77 u_long, struct ufsmount *, int, struct dquot **); 78 static int dqsync(struct vnode *, struct dquot *); 79 static void dqflush(struct vnode *); 80 81 #ifdef DIAGNOSTIC 82 static void dqref(struct dquot *); 83 static void chkdquot(struct inode *); 84 #endif 85 86 /* 87 * Set up the quotas for an inode. 88 * 89 * This routine completely defines the semantics of quotas. 90 * If other criterion want to be used to establish quotas, the 91 * MAXQUOTAS value in quotas.h should be increased, and the 92 * additional dquots set up here. 93 */ 94 int 95 getinoquota(ip) 96 struct inode *ip; 97 { 98 struct ufsmount *ump; 99 struct vnode *vp; 100 int error; 101 102 vp = ITOV(ip); 103 104 /* 105 * Disk quotas must be turned off for system files. Currently 106 * snapshot and quota files. 107 */ 108 if ((vp->v_vflag & VV_SYSTEM) != 0) 109 return (0); 110 /* 111 * XXX: Turn off quotas for files with a negative UID or GID. 112 * This prevents the creation of 100GB+ quota files. 113 */ 114 if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0) 115 return (0); 116 ump = VFSTOUFS(vp->v_mount); 117 /* 118 * Set up the user quota based on file uid. 119 * EINVAL means that quotas are not enabled. 120 */ 121 if (ip->i_dquot[USRQUOTA] == NODQUOT && 122 (error = 123 dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) && 124 error != EINVAL) 125 return (error); 126 /* 127 * Set up the group quota based on file gid. 128 * EINVAL means that quotas are not enabled. 129 */ 130 if (ip->i_dquot[GRPQUOTA] == NODQUOT && 131 (error = 132 dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) && 133 error != EINVAL) 134 return (error); 135 return (0); 136 } 137 138 /* 139 * Update disk usage, and take corrective action. 140 */ 141 int 142 chkdq(ip, change, cred, flags) 143 struct inode *ip; 144 ufs2_daddr_t change; 145 struct ucred *cred; 146 int flags; 147 { 148 struct dquot *dq; 149 ufs2_daddr_t ncurblocks; 150 struct vnode *vp = ITOV(ip); 151 int i, error; 152 153 /* 154 * Disk quotas must be turned off for system files. Currently 155 * snapshot and quota files. 156 */ 157 if ((vp->v_vflag & VV_SYSTEM) != 0) 158 return (0); 159 /* 160 * XXX: Turn off quotas for files with a negative UID or GID. 161 * This prevents the creation of 100GB+ quota files. 162 */ 163 if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0) 164 return (0); 165 #ifdef DIAGNOSTIC 166 if ((flags & CHOWN) == 0) 167 chkdquot(ip); 168 #endif 169 if (change == 0) 170 return (0); 171 if (change < 0) { 172 for (i = 0; i < MAXQUOTAS; i++) { 173 if ((dq = ip->i_dquot[i]) == NODQUOT) 174 continue; 175 while (dq->dq_flags & DQ_LOCK) { 176 dq->dq_flags |= DQ_WANT; 177 (void) tsleep(dq, PINOD+1, "chkdq1", 0); 178 } 179 ncurblocks = dq->dq_curblocks + change; 180 if (ncurblocks >= 0) 181 dq->dq_curblocks = ncurblocks; 182 else 183 dq->dq_curblocks = 0; 184 dq->dq_flags &= ~DQ_BLKS; 185 dq->dq_flags |= DQ_MOD; 186 } 187 return (0); 188 } 189 if ((flags & FORCE) == 0 && priv_check_cred(cred, 190 PRIV_VFS_EXCEEDQUOTA, 0)) { 191 for (i = 0; i < MAXQUOTAS; i++) { 192 if ((dq = ip->i_dquot[i]) == NODQUOT) 193 continue; 194 error = chkdqchg(ip, change, cred, i); 195 if (error) 196 return (error); 197 } 198 } 199 for (i = 0; i < MAXQUOTAS; i++) { 200 if ((dq = ip->i_dquot[i]) == NODQUOT) 201 continue; 202 while (dq->dq_flags & DQ_LOCK) { 203 dq->dq_flags |= DQ_WANT; 204 (void) tsleep(dq, PINOD+1, "chkdq2", 0); 205 } 206 /* Reset timer when crossing soft limit */ 207 if (dq->dq_curblocks + change >= dq->dq_bsoftlimit && 208 dq->dq_curblocks < dq->dq_bsoftlimit) 209 dq->dq_btime = time_second + 210 VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i]; 211 dq->dq_curblocks += change; 212 dq->dq_flags |= DQ_MOD; 213 } 214 return (0); 215 } 216 217 /* 218 * Check for a valid change to a users allocation. 219 * Issue an error message if appropriate. 220 */ 221 static int 222 chkdqchg(ip, change, cred, type) 223 struct inode *ip; 224 ufs2_daddr_t change; 225 struct ucred *cred; 226 int type; 227 { 228 struct dquot *dq = ip->i_dquot[type]; 229 ufs2_daddr_t ncurblocks = dq->dq_curblocks + change; 230 231 /* 232 * If user would exceed their hard limit, disallow space allocation. 233 */ 234 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { 235 if ((dq->dq_flags & DQ_BLKS) == 0 && 236 ip->i_uid == cred->cr_uid) { 237 uprintf("\n%s: write failed, %s disk limit reached\n", 238 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 239 quotatypes[type]); 240 dq->dq_flags |= DQ_BLKS; 241 } 242 return (EDQUOT); 243 } 244 /* 245 * If user is over their soft limit for too long, disallow space 246 * allocation. Reset time limit as they cross their soft limit. 247 */ 248 if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { 249 if (dq->dq_curblocks < dq->dq_bsoftlimit) { 250 dq->dq_btime = time_second + 251 VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type]; 252 if (ip->i_uid == cred->cr_uid) 253 uprintf("\n%s: warning, %s %s\n", 254 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 255 quotatypes[type], "disk quota exceeded"); 256 return (0); 257 } 258 if (time_second > dq->dq_btime) { 259 if ((dq->dq_flags & DQ_BLKS) == 0 && 260 ip->i_uid == cred->cr_uid) { 261 uprintf("\n%s: write failed, %s %s\n", 262 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 263 quotatypes[type], 264 "disk quota exceeded for too long"); 265 dq->dq_flags |= DQ_BLKS; 266 } 267 return (EDQUOT); 268 } 269 } 270 return (0); 271 } 272 273 /* 274 * Check the inode limit, applying corrective action. 275 */ 276 int 277 chkiq(ip, change, cred, flags) 278 struct inode *ip; 279 int change; 280 struct ucred *cred; 281 int flags; 282 { 283 struct dquot *dq; 284 ino_t ncurinodes; 285 int i, error; 286 287 #ifdef DIAGNOSTIC 288 if ((flags & CHOWN) == 0) 289 chkdquot(ip); 290 #endif 291 if (change == 0) 292 return (0); 293 if (change < 0) { 294 for (i = 0; i < MAXQUOTAS; i++) { 295 if ((dq = ip->i_dquot[i]) == NODQUOT) 296 continue; 297 while (dq->dq_flags & DQ_LOCK) { 298 dq->dq_flags |= DQ_WANT; 299 (void) tsleep(dq, PINOD+1, "chkiq1", 0); 300 } 301 ncurinodes = dq->dq_curinodes + change; 302 /* XXX: ncurinodes is unsigned */ 303 if (dq->dq_curinodes != 0 && ncurinodes >= 0) 304 dq->dq_curinodes = ncurinodes; 305 else 306 dq->dq_curinodes = 0; 307 dq->dq_flags &= ~DQ_INODS; 308 dq->dq_flags |= DQ_MOD; 309 } 310 return (0); 311 } 312 if ((flags & FORCE) == 0 && priv_check_cred(cred, 313 PRIV_VFS_EXCEEDQUOTA, 0)) { 314 for (i = 0; i < MAXQUOTAS; i++) { 315 if ((dq = ip->i_dquot[i]) == NODQUOT) 316 continue; 317 error = chkiqchg(ip, change, cred, i); 318 if (error) 319 return (error); 320 } 321 } 322 for (i = 0; i < MAXQUOTAS; i++) { 323 if ((dq = ip->i_dquot[i]) == NODQUOT) 324 continue; 325 while (dq->dq_flags & DQ_LOCK) { 326 dq->dq_flags |= DQ_WANT; 327 (void) tsleep(dq, PINOD+1, "chkiq2", 0); 328 } 329 /* Reset timer when crossing soft limit */ 330 if (dq->dq_curinodes + change >= dq->dq_isoftlimit && 331 dq->dq_curinodes < dq->dq_isoftlimit) 332 dq->dq_itime = time_second + 333 VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i]; 334 dq->dq_curinodes += change; 335 dq->dq_flags |= DQ_MOD; 336 } 337 return (0); 338 } 339 340 /* 341 * Check for a valid change to a users allocation. 342 * Issue an error message if appropriate. 343 */ 344 static int 345 chkiqchg(ip, change, cred, type) 346 struct inode *ip; 347 int change; 348 struct ucred *cred; 349 int type; 350 { 351 struct dquot *dq = ip->i_dquot[type]; 352 ino_t ncurinodes = dq->dq_curinodes + change; 353 354 /* 355 * If user would exceed their hard limit, disallow inode allocation. 356 */ 357 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { 358 if ((dq->dq_flags & DQ_INODS) == 0 && 359 ip->i_uid == cred->cr_uid) { 360 uprintf("\n%s: write failed, %s inode limit reached\n", 361 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 362 quotatypes[type]); 363 dq->dq_flags |= DQ_INODS; 364 } 365 return (EDQUOT); 366 } 367 /* 368 * If user is over their soft limit for too long, disallow inode 369 * allocation. Reset time limit as they cross their soft limit. 370 */ 371 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { 372 if (dq->dq_curinodes < dq->dq_isoftlimit) { 373 dq->dq_itime = time_second + 374 VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type]; 375 if (ip->i_uid == cred->cr_uid) 376 uprintf("\n%s: warning, %s %s\n", 377 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 378 quotatypes[type], "inode quota exceeded"); 379 return (0); 380 } 381 if (time_second > dq->dq_itime) { 382 if ((dq->dq_flags & DQ_INODS) == 0 && 383 ip->i_uid == cred->cr_uid) { 384 uprintf("\n%s: write failed, %s %s\n", 385 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 386 quotatypes[type], 387 "inode quota exceeded for too long"); 388 dq->dq_flags |= DQ_INODS; 389 } 390 return (EDQUOT); 391 } 392 } 393 return (0); 394 } 395 396 #ifdef DIAGNOSTIC 397 /* 398 * On filesystems with quotas enabled, it is an error for a file to change 399 * size and not to have a dquot structure associated with it. 400 */ 401 static void 402 chkdquot(ip) 403 struct inode *ip; 404 { 405 struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount); 406 struct vnode *vp = ITOV(ip); 407 int i; 408 409 /* 410 * Disk quotas must be turned off for system files. Currently 411 * these are snapshots and quota files. 412 */ 413 if ((vp->v_vflag & VV_SYSTEM) != 0) 414 return; 415 /* 416 * XXX: Turn off quotas for files with a negative UID or GID. 417 * This prevents the creation of 100GB+ quota files. 418 */ 419 if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0) 420 return; 421 for (i = 0; i < MAXQUOTAS; i++) { 422 if (ump->um_quotas[i] == NULLVP || 423 (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING))) 424 continue; 425 if (ip->i_dquot[i] == NODQUOT) { 426 vprint("chkdquot: missing dquot", ITOV(ip)); 427 panic("chkdquot: missing dquot"); 428 } 429 } 430 } 431 #endif 432 433 /* 434 * Code to process quotactl commands. 435 */ 436 437 /* 438 * Q_QUOTAON - set up a quota file for a particular filesystem. 439 */ 440 int 441 quotaon(td, mp, type, fname) 442 struct thread *td; 443 struct mount *mp; 444 int type; 445 void *fname; 446 { 447 struct ufsmount *ump; 448 struct vnode *vp, **vpp; 449 struct vnode *mvp; 450 struct dquot *dq; 451 int error, flags; 452 struct nameidata nd; 453 454 error = priv_check_cred(td->td_ucred, PRIV_UFS_QUOTAON, 0); 455 if (error) 456 return (error); 457 458 ump = VFSTOUFS(mp); 459 vpp = &ump->um_quotas[type]; 460 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td); 461 flags = FREAD | FWRITE; 462 error = vn_open(&nd, &flags, 0, -1); 463 if (error) 464 return (error); 465 NDFREE(&nd, NDF_ONLY_PNBUF); 466 vp = nd.ni_vp; 467 VOP_UNLOCK(vp, 0, td); 468 if (vp->v_type != VREG) { 469 (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 470 return (EACCES); 471 } 472 if (*vpp != vp) 473 quotaoff(td, mp, type); 474 ump->um_qflags[type] |= QTF_OPENING; 475 MNT_ILOCK(mp); 476 mp->mnt_flag |= MNT_QUOTA; 477 MNT_IUNLOCK(mp); 478 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 479 vp->v_vflag |= VV_SYSTEM; 480 VOP_UNLOCK(vp, 0, td); 481 *vpp = vp; 482 /* 483 * Save the credential of the process that turned on quotas. 484 * Set up the time limits for this quota. 485 */ 486 ump->um_cred[type] = crhold(td->td_ucred); 487 ump->um_btime[type] = MAX_DQ_TIME; 488 ump->um_itime[type] = MAX_IQ_TIME; 489 if (dqget(NULLVP, 0, ump, type, &dq) == 0) { 490 if (dq->dq_btime > 0) 491 ump->um_btime[type] = dq->dq_btime; 492 if (dq->dq_itime > 0) 493 ump->um_itime[type] = dq->dq_itime; 494 dqrele(NULLVP, dq); 495 } 496 /* 497 * Search vnodes associated with this mount point, 498 * adding references to quota file being opened. 499 * NB: only need to add dquot's for inodes being modified. 500 */ 501 MNT_ILOCK(mp); 502 again: 503 MNT_VNODE_FOREACH(vp, mp, mvp) { 504 VI_LOCK(vp); 505 MNT_IUNLOCK(mp); 506 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { 507 MNT_ILOCK(mp); 508 MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp); 509 goto again; 510 } 511 if (vp->v_type == VNON || vp->v_writecount == 0) { 512 VOP_UNLOCK(vp, 0, td); 513 vrele(vp); 514 MNT_ILOCK(mp); 515 continue; 516 } 517 error = getinoquota(VTOI(vp)); 518 VOP_UNLOCK(vp, 0, td); 519 vrele(vp); 520 MNT_ILOCK(mp); 521 if (error) { 522 MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp); 523 break; 524 } 525 } 526 MNT_IUNLOCK(mp); 527 ump->um_qflags[type] &= ~QTF_OPENING; 528 if (error) 529 quotaoff(td, mp, type); 530 return (error); 531 } 532 533 /* 534 * Q_QUOTAOFF - turn off disk quotas for a filesystem. 535 */ 536 int 537 quotaoff(td, mp, type) 538 struct thread *td; 539 struct mount *mp; 540 int type; 541 { 542 struct vnode *vp; 543 struct vnode *qvp, *mvp; 544 struct ufsmount *ump; 545 struct dquot *dq; 546 struct inode *ip; 547 int error; 548 549 error = priv_check_cred(td->td_ucred, PRIV_UFS_QUOTAOFF, 0); 550 if (error) 551 return (error); 552 553 ump = VFSTOUFS(mp); 554 if ((qvp = ump->um_quotas[type]) == NULLVP) 555 return (0); 556 ump->um_qflags[type] |= QTF_CLOSING; 557 /* 558 * Search vnodes associated with this mount point, 559 * deleting any references to quota file being closed. 560 */ 561 MNT_ILOCK(mp); 562 again: 563 MNT_VNODE_FOREACH(vp, mp, mvp) { 564 VI_LOCK(vp); 565 MNT_IUNLOCK(mp); 566 if (vp->v_type == VNON) { 567 VI_UNLOCK(vp); 568 MNT_ILOCK(mp); 569 continue; 570 } 571 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { 572 MNT_ILOCK(mp); 573 MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp); 574 goto again; 575 } 576 ip = VTOI(vp); 577 dq = ip->i_dquot[type]; 578 ip->i_dquot[type] = NODQUOT; 579 dqrele(vp, dq); 580 VOP_UNLOCK(vp, 0, td); 581 vrele(vp); 582 MNT_ILOCK(mp); 583 } 584 MNT_IUNLOCK(mp); 585 dqflush(qvp); 586 vn_lock(qvp, LK_EXCLUSIVE | LK_RETRY, td); 587 qvp->v_vflag &= ~VV_SYSTEM; 588 VOP_UNLOCK(qvp, 0, td); 589 error = vn_close(qvp, FREAD|FWRITE, td->td_ucred, td); 590 ump->um_quotas[type] = NULLVP; 591 crfree(ump->um_cred[type]); 592 ump->um_cred[type] = NOCRED; 593 ump->um_qflags[type] &= ~QTF_CLOSING; 594 for (type = 0; type < MAXQUOTAS; type++) 595 if (ump->um_quotas[type] != NULLVP) 596 break; 597 if (type == MAXQUOTAS) { 598 MNT_ILOCK(mp); 599 mp->mnt_flag &= ~MNT_QUOTA; 600 MNT_IUNLOCK(mp); 601 } 602 return (error); 603 } 604 605 /* 606 * Q_GETQUOTA - return current values in a dqblk structure. 607 */ 608 int 609 getquota(td, mp, id, type, addr) 610 struct thread *td; 611 struct mount *mp; 612 u_long id; 613 int type; 614 void *addr; 615 { 616 struct dquot *dq; 617 int error; 618 619 switch (type) { 620 case USRQUOTA: 621 if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) { 622 error = priv_check_cred(td->td_ucred, 623 PRIV_VFS_GETQUOTA, SUSER_ALLOWJAIL); 624 if (error) 625 return (error); 626 } 627 break; 628 629 case GRPQUOTA: 630 if (!groupmember(id, td->td_ucred) && 631 !unprivileged_get_quota) { 632 error = priv_check_cred(td->td_ucred, 633 PRIV_VFS_GETQUOTA, SUSER_ALLOWJAIL); 634 if (error) 635 return (error); 636 } 637 break; 638 639 default: 640 return (EINVAL); 641 } 642 643 error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq); 644 if (error) 645 return (error); 646 error = copyout(&dq->dq_dqb, addr, sizeof (struct dqblk)); 647 dqrele(NULLVP, dq); 648 return (error); 649 } 650 651 /* 652 * Q_SETQUOTA - assign an entire dqblk structure. 653 */ 654 int 655 setquota(td, mp, id, type, addr) 656 struct thread *td; 657 struct mount *mp; 658 u_long id; 659 int type; 660 void *addr; 661 { 662 struct dquot *dq; 663 struct dquot *ndq; 664 struct ufsmount *ump; 665 struct dqblk newlim; 666 int error; 667 668 error = priv_check_cred(td->td_ucred, PRIV_VFS_SETQUOTA, 669 SUSER_ALLOWJAIL); 670 if (error) 671 return (error); 672 673 ump = VFSTOUFS(mp); 674 error = copyin(addr, &newlim, sizeof (struct dqblk)); 675 if (error) 676 return (error); 677 error = dqget(NULLVP, id, ump, type, &ndq); 678 if (error) 679 return (error); 680 dq = ndq; 681 while (dq->dq_flags & DQ_LOCK) { 682 dq->dq_flags |= DQ_WANT; 683 (void) tsleep(dq, PINOD+1, "setqta", 0); 684 } 685 /* 686 * Copy all but the current values. 687 * Reset time limit if previously had no soft limit or were 688 * under it, but now have a soft limit and are over it. 689 */ 690 newlim.dqb_curblocks = dq->dq_curblocks; 691 newlim.dqb_curinodes = dq->dq_curinodes; 692 if (dq->dq_id != 0) { 693 newlim.dqb_btime = dq->dq_btime; 694 newlim.dqb_itime = dq->dq_itime; 695 } 696 if (newlim.dqb_bsoftlimit && 697 dq->dq_curblocks >= newlim.dqb_bsoftlimit && 698 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) 699 newlim.dqb_btime = time_second + ump->um_btime[type]; 700 if (newlim.dqb_isoftlimit && 701 dq->dq_curinodes >= newlim.dqb_isoftlimit && 702 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) 703 newlim.dqb_itime = time_second + ump->um_itime[type]; 704 dq->dq_dqb = newlim; 705 if (dq->dq_curblocks < dq->dq_bsoftlimit) 706 dq->dq_flags &= ~DQ_BLKS; 707 if (dq->dq_curinodes < dq->dq_isoftlimit) 708 dq->dq_flags &= ~DQ_INODS; 709 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 710 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 711 dq->dq_flags |= DQ_FAKE; 712 else 713 dq->dq_flags &= ~DQ_FAKE; 714 dq->dq_flags |= DQ_MOD; 715 dqrele(NULLVP, dq); 716 return (0); 717 } 718 719 /* 720 * Q_SETUSE - set current inode and block usage. 721 */ 722 int 723 setuse(td, mp, id, type, addr) 724 struct thread *td; 725 struct mount *mp; 726 u_long id; 727 int type; 728 void *addr; 729 { 730 struct dquot *dq; 731 struct ufsmount *ump; 732 struct dquot *ndq; 733 struct dqblk usage; 734 int error; 735 736 error = priv_check_cred(td->td_ucred, PRIV_UFS_SETUSE, 0); 737 if (error) 738 return (error); 739 740 ump = VFSTOUFS(mp); 741 error = copyin(addr, &usage, sizeof (struct dqblk)); 742 if (error) 743 return (error); 744 error = dqget(NULLVP, id, ump, type, &ndq); 745 if (error) 746 return (error); 747 dq = ndq; 748 while (dq->dq_flags & DQ_LOCK) { 749 dq->dq_flags |= DQ_WANT; 750 (void) tsleep(dq, PINOD+1, "setuse", 0); 751 } 752 /* 753 * Reset time limit if have a soft limit and were 754 * previously under it, but are now over it. 755 */ 756 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && 757 usage.dqb_curblocks >= dq->dq_bsoftlimit) 758 dq->dq_btime = time_second + ump->um_btime[type]; 759 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && 760 usage.dqb_curinodes >= dq->dq_isoftlimit) 761 dq->dq_itime = time_second + ump->um_itime[type]; 762 dq->dq_curblocks = usage.dqb_curblocks; 763 dq->dq_curinodes = usage.dqb_curinodes; 764 if (dq->dq_curblocks < dq->dq_bsoftlimit) 765 dq->dq_flags &= ~DQ_BLKS; 766 if (dq->dq_curinodes < dq->dq_isoftlimit) 767 dq->dq_flags &= ~DQ_INODS; 768 dq->dq_flags |= DQ_MOD; 769 dqrele(NULLVP, dq); 770 return (0); 771 } 772 773 /* 774 * Q_SYNC - sync quota files to disk. 775 */ 776 int 777 qsync(mp) 778 struct mount *mp; 779 { 780 struct ufsmount *ump = VFSTOUFS(mp); 781 struct thread *td = curthread; /* XXX */ 782 struct vnode *vp, *mvp; 783 struct dquot *dq; 784 int i, error; 785 786 /* 787 * Check if the mount point has any quotas. 788 * If not, simply return. 789 */ 790 for (i = 0; i < MAXQUOTAS; i++) 791 if (ump->um_quotas[i] != NULLVP) 792 break; 793 if (i == MAXQUOTAS) 794 return (0); 795 /* 796 * Search vnodes associated with this mount point, 797 * synchronizing any modified dquot structures. 798 */ 799 MNT_ILOCK(mp); 800 again: 801 MNT_VNODE_FOREACH(vp, mp, mvp) { 802 VI_LOCK(vp); 803 MNT_IUNLOCK(mp); 804 if (vp->v_type == VNON) { 805 VI_UNLOCK(vp); 806 MNT_ILOCK(mp); 807 continue; 808 } 809 error = vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td); 810 if (error) { 811 MNT_ILOCK(mp); 812 if (error == ENOENT) { 813 MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp); 814 goto again; 815 } 816 continue; 817 } 818 for (i = 0; i < MAXQUOTAS; i++) { 819 dq = VTOI(vp)->i_dquot[i]; 820 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) 821 dqsync(vp, dq); 822 } 823 vput(vp); 824 MNT_ILOCK(mp); 825 } 826 MNT_IUNLOCK(mp); 827 return (0); 828 } 829 830 /* 831 * Code pertaining to management of the in-core dquot data structures. 832 */ 833 #define DQHASH(dqvp, id) \ 834 (&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash]) 835 static LIST_HEAD(dqhash, dquot) *dqhashtbl; 836 static u_long dqhash; 837 838 /* 839 * Dquot free list. 840 */ 841 #define DQUOTINC 5 /* minimum free dquots desired */ 842 static TAILQ_HEAD(dqfreelist, dquot) dqfreelist; 843 static long numdquot, desireddquot = DQUOTINC; 844 845 /* 846 * Initialize the quota system. 847 */ 848 void 849 dqinit() 850 { 851 852 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); 853 TAILQ_INIT(&dqfreelist); 854 } 855 856 /* 857 * Shut down the quota system. 858 */ 859 void 860 dquninit() 861 { 862 struct dquot *dq; 863 864 hashdestroy(dqhashtbl, M_DQUOT, dqhash); 865 while ((dq = TAILQ_FIRST(&dqfreelist)) != NULL) { 866 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 867 free(dq, M_DQUOT); 868 } 869 } 870 871 /* 872 * Obtain a dquot structure for the specified identifier and quota file 873 * reading the information from the file if necessary. 874 */ 875 static int 876 dqget(vp, id, ump, type, dqp) 877 struct vnode *vp; 878 u_long id; 879 struct ufsmount *ump; 880 int type; 881 struct dquot **dqp; 882 { 883 struct thread *td = curthread; /* XXX */ 884 struct dquot *dq; 885 struct dqhash *dqh; 886 struct vnode *dqvp; 887 struct iovec aiov; 888 struct uio auio; 889 int error; 890 891 /* XXX: Disallow negative id values to prevent the 892 * creation of 100GB+ quota data files. 893 */ 894 if ((int)id < 0) 895 return (EINVAL); 896 dqvp = ump->um_quotas[type]; 897 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { 898 *dqp = NODQUOT; 899 return (EINVAL); 900 } 901 /* 902 * Check the cache first. 903 */ 904 dqh = DQHASH(dqvp, id); 905 LIST_FOREACH(dq, dqh, dq_hash) { 906 if (dq->dq_id != id || 907 dq->dq_ump->um_quotas[dq->dq_type] != dqvp) 908 continue; 909 /* 910 * Cache hit with no references. Take 911 * the structure off the free list. 912 */ 913 if (dq->dq_cnt == 0) 914 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 915 DQREF(dq); 916 *dqp = dq; 917 return (0); 918 } 919 /* 920 * Not in cache, allocate a new one. 921 */ 922 if (TAILQ_FIRST(&dqfreelist) == NODQUOT && 923 numdquot < MAXQUOTAS * desiredvnodes) 924 desireddquot += DQUOTINC; 925 if (numdquot < desireddquot) { 926 dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, 927 M_WAITOK | M_ZERO); 928 numdquot++; 929 } else { 930 if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) { 931 tablefull("dquot"); 932 *dqp = NODQUOT; 933 return (EUSERS); 934 } 935 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) 936 panic("dqget: free dquot isn't"); 937 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); 938 if (dq->dq_ump != NULL) 939 LIST_REMOVE(dq, dq_hash); 940 } 941 /* 942 * Initialize the contents of the dquot structure. 943 */ 944 if (vp != dqvp) 945 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); 946 LIST_INSERT_HEAD(dqh, dq, dq_hash); 947 DQREF(dq); 948 dq->dq_flags = DQ_LOCK; 949 dq->dq_id = id; 950 dq->dq_ump = ump; 951 dq->dq_type = type; 952 auio.uio_iov = &aiov; 953 auio.uio_iovcnt = 1; 954 aiov.iov_base = &dq->dq_dqb; 955 aiov.iov_len = sizeof (struct dqblk); 956 auio.uio_resid = sizeof (struct dqblk); 957 auio.uio_offset = (off_t)id * sizeof (struct dqblk); 958 auio.uio_segflg = UIO_SYSSPACE; 959 auio.uio_rw = UIO_READ; 960 auio.uio_td = (struct thread *)0; 961 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); 962 if (auio.uio_resid == sizeof(struct dqblk) && error == 0) 963 bzero(&dq->dq_dqb, sizeof(struct dqblk)); 964 if (vp != dqvp) 965 VOP_UNLOCK(dqvp, 0, td); 966 if (dq->dq_flags & DQ_WANT) 967 wakeup(dq); 968 dq->dq_flags = 0; 969 /* 970 * I/O error in reading quota file, release 971 * quota structure and reflect problem to caller. 972 */ 973 if (error) { 974 LIST_REMOVE(dq, dq_hash); 975 dqrele(vp, dq); 976 *dqp = NODQUOT; 977 return (error); 978 } 979 /* 980 * Check for no limit to enforce. 981 * Initialize time values if necessary. 982 */ 983 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 984 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 985 dq->dq_flags |= DQ_FAKE; 986 if (dq->dq_id != 0) { 987 if (dq->dq_btime == 0) { 988 dq->dq_btime = time_second + ump->um_btime[type]; 989 if (dq->dq_bsoftlimit && 990 dq->dq_curblocks >= dq->dq_bsoftlimit) 991 dq->dq_flags |= DQ_MOD; 992 } 993 if (dq->dq_itime == 0) { 994 dq->dq_itime = time_second + ump->um_itime[type]; 995 if (dq->dq_isoftlimit && 996 dq->dq_curinodes >= dq->dq_isoftlimit) 997 dq->dq_flags |= DQ_MOD; 998 } 999 } 1000 *dqp = dq; 1001 return (0); 1002 } 1003 1004 #ifdef DIAGNOSTIC 1005 /* 1006 * Obtain a reference to a dquot. 1007 */ 1008 static void 1009 dqref(dq) 1010 struct dquot *dq; 1011 { 1012 1013 dq->dq_cnt++; 1014 } 1015 #endif 1016 1017 /* 1018 * Release a reference to a dquot. 1019 */ 1020 void 1021 dqrele(vp, dq) 1022 struct vnode *vp; 1023 struct dquot *dq; 1024 { 1025 1026 if (dq == NODQUOT) 1027 return; 1028 if (dq->dq_cnt > 1) { 1029 dq->dq_cnt--; 1030 return; 1031 } 1032 if (dq->dq_flags & DQ_MOD) 1033 (void) dqsync(vp, dq); 1034 if (--dq->dq_cnt > 0) 1035 return; 1036 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); 1037 } 1038 1039 /* 1040 * Update the disk quota in the quota file. 1041 */ 1042 static int 1043 dqsync(vp, dq) 1044 struct vnode *vp; 1045 struct dquot *dq; 1046 { 1047 struct thread *td = curthread; /* XXX */ 1048 struct vnode *dqvp; 1049 struct iovec aiov; 1050 struct uio auio; 1051 int error; 1052 struct mount *mp; 1053 1054 mp = NULL; 1055 if (dq == NODQUOT) 1056 panic("dqsync: dquot"); 1057 if ((dq->dq_flags & DQ_MOD) == 0) 1058 return (0); 1059 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) 1060 panic("dqsync: file"); 1061 (void) vn_start_secondary_write(dqvp, &mp, V_WAIT); 1062 if (vp != dqvp) 1063 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); 1064 while (dq->dq_flags & DQ_LOCK) { 1065 dq->dq_flags |= DQ_WANT; 1066 (void) tsleep(dq, PINOD+2, "dqsync", 0); 1067 if ((dq->dq_flags & DQ_MOD) == 0) { 1068 if (vp != dqvp) 1069 VOP_UNLOCK(dqvp, 0, td); 1070 vn_finished_secondary_write(mp); 1071 return (0); 1072 } 1073 } 1074 dq->dq_flags |= DQ_LOCK; 1075 auio.uio_iov = &aiov; 1076 auio.uio_iovcnt = 1; 1077 aiov.iov_base = &dq->dq_dqb; 1078 aiov.iov_len = sizeof (struct dqblk); 1079 auio.uio_resid = sizeof (struct dqblk); 1080 auio.uio_offset = (off_t)dq->dq_id * sizeof (struct dqblk); 1081 auio.uio_segflg = UIO_SYSSPACE; 1082 auio.uio_rw = UIO_WRITE; 1083 auio.uio_td = (struct thread *)0; 1084 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); 1085 if (auio.uio_resid && error == 0) 1086 error = EIO; 1087 if (dq->dq_flags & DQ_WANT) 1088 wakeup(dq); 1089 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); 1090 if (vp != dqvp) 1091 VOP_UNLOCK(dqvp, 0, td); 1092 vn_finished_secondary_write(mp); 1093 return (error); 1094 } 1095 1096 /* 1097 * Flush all entries from the cache for a particular vnode. 1098 */ 1099 static void 1100 dqflush(vp) 1101 struct vnode *vp; 1102 { 1103 struct dquot *dq, *nextdq; 1104 struct dqhash *dqh; 1105 1106 /* 1107 * Move all dquot's that used to refer to this quota 1108 * file off their hash chains (they will eventually 1109 * fall off the head of the free list and be re-used). 1110 */ 1111 for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) { 1112 for (dq = LIST_FIRST(dqh); dq; dq = nextdq) { 1113 nextdq = LIST_NEXT(dq, dq_hash); 1114 if (dq->dq_ump->um_quotas[dq->dq_type] != vp) 1115 continue; 1116 if (dq->dq_cnt) 1117 panic("dqflush: stray dquot"); 1118 LIST_REMOVE(dq, dq_hash); 1119 dq->dq_ump = (struct ufsmount *)0; 1120 } 1121 } 1122 } 1123