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