1/* $NetBSD: ufs_quota1.c,v 1.17 2012/02/01 05:43:54 dholland Exp $ */ 2 3/* 4 * Copyright (c) 1982, 1986, 1990, 1993, 1995 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Robert Elz at The University of Melbourne. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: ufs_quota1.c,v 1.17 2012/02/01 05:43:54 dholland Exp $"); 39 40#include <sys/param.h> 41#include <sys/kernel.h> 42#include <sys/systm.h> 43#include <sys/namei.h> 44#include <sys/file.h> 45#include <sys/proc.h> 46#include <sys/vnode.h> 47#include <sys/mount.h> 48#include <sys/kauth.h> 49 50#include <ufs/ufs/quota1.h> 51#include <ufs/ufs/inode.h> 52#include <ufs/ufs/ufsmount.h> 53#include <ufs/ufs/ufs_extern.h> 54#include <ufs/ufs/ufs_quota.h> 55 56static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int); 57static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int); 58 59/* 60 * Update disk usage, and take corrective action. 61 */ 62int 63chkdq1(struct inode *ip, int64_t change, kauth_cred_t cred, int flags) 64{ 65 struct dquot *dq; 66 int i; 67 int ncurblocks, error; 68 69 if ((error = getinoquota(ip)) != 0) 70 return error; 71 if (change == 0) 72 return (0); 73 if (change < 0) { 74 for (i = 0; i < MAXQUOTAS; i++) { 75 if ((dq = ip->i_dquot[i]) == NODQUOT) 76 continue; 77 mutex_enter(&dq->dq_interlock); 78 ncurblocks = dq->dq_curblocks + change; 79 if (ncurblocks >= 0) 80 dq->dq_curblocks = ncurblocks; 81 else 82 dq->dq_curblocks = 0; 83 dq->dq_flags &= ~DQ_WARN(QL_BLOCK); 84 dq->dq_flags |= DQ_MOD; 85 mutex_exit(&dq->dq_interlock); 86 } 87 return (0); 88 } 89 for (i = 0; i < MAXQUOTAS; i++) { 90 if ((dq = ip->i_dquot[i]) == NODQUOT) 91 continue; 92 if ((flags & FORCE) == 0 && 93 kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA, 94 KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, KAUTH_ARG(i), 95 KAUTH_ARG(QL_BLOCK), NULL) != 0) { 96 mutex_enter(&dq->dq_interlock); 97 error = chkdqchg(ip, change, cred, i); 98 mutex_exit(&dq->dq_interlock); 99 if (error != 0) 100 return (error); 101 } 102 } 103 for (i = 0; i < MAXQUOTAS; i++) { 104 if ((dq = ip->i_dquot[i]) == NODQUOT) 105 continue; 106 mutex_enter(&dq->dq_interlock); 107 dq->dq_curblocks += change; 108 dq->dq_flags |= DQ_MOD; 109 mutex_exit(&dq->dq_interlock); 110 } 111 return (0); 112} 113 114/* 115 * Check for a valid change to a users allocation. 116 * Issue an error message if appropriate. 117 */ 118static int 119chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type) 120{ 121 struct dquot *dq = ip->i_dquot[type]; 122 long ncurblocks = dq->dq_curblocks + change; 123 124 KASSERT(mutex_owned(&dq->dq_interlock)); 125 /* 126 * If user would exceed their hard limit, disallow space allocation. 127 */ 128 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { 129 if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 && 130 ip->i_uid == kauth_cred_geteuid(cred)) { 131 uprintf("\n%s: write failed, %s disk limit reached\n", 132 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 133 quotatypes[type]); 134 dq->dq_flags |= DQ_WARN(QL_BLOCK); 135 } 136 return (EDQUOT); 137 } 138 /* 139 * If user is over their soft limit for too long, disallow space 140 * allocation. Reset time limit as they cross their soft limit. 141 */ 142 if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { 143 if (dq->dq_curblocks < dq->dq_bsoftlimit) { 144 dq->dq_btime = 145 time_second + ip->i_ump->umq1_btime[type]; 146 if (ip->i_uid == kauth_cred_geteuid(cred)) 147 uprintf("\n%s: warning, %s %s\n", 148 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 149 quotatypes[type], "disk quota exceeded"); 150 return (0); 151 } 152 if (time_second > dq->dq_btime) { 153 if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 && 154 ip->i_uid == kauth_cred_geteuid(cred)) { 155 uprintf("\n%s: write failed, %s %s\n", 156 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 157 quotatypes[type], 158 "disk quota exceeded for too long"); 159 dq->dq_flags |= DQ_WARN(QL_BLOCK); 160 } 161 return (EDQUOT); 162 } 163 } 164 return (0); 165} 166 167/* 168 * Check the inode limit, applying corrective action. 169 */ 170int 171chkiq1(struct inode *ip, int32_t change, kauth_cred_t cred, int flags) 172{ 173 struct dquot *dq; 174 int i; 175 int ncurinodes, error; 176 177 if ((error = getinoquota(ip)) != 0) 178 return error; 179 if (change == 0) 180 return (0); 181 if (change < 0) { 182 for (i = 0; i < MAXQUOTAS; i++) { 183 if ((dq = ip->i_dquot[i]) == NODQUOT) 184 continue; 185 mutex_enter(&dq->dq_interlock); 186 ncurinodes = dq->dq_curinodes + change; 187 if (ncurinodes >= 0) 188 dq->dq_curinodes = ncurinodes; 189 else 190 dq->dq_curinodes = 0; 191 dq->dq_flags &= ~DQ_WARN(QL_FILE); 192 dq->dq_flags |= DQ_MOD; 193 mutex_exit(&dq->dq_interlock); 194 } 195 return (0); 196 } 197 for (i = 0; i < MAXQUOTAS; i++) { 198 if ((dq = ip->i_dquot[i]) == NODQUOT) 199 continue; 200 if ((flags & FORCE) == 0 && kauth_authorize_system(cred, 201 KAUTH_SYSTEM_FS_QUOTA, KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, 202 KAUTH_ARG(i), KAUTH_ARG(QL_FILE), NULL) != 0) { 203 mutex_enter(&dq->dq_interlock); 204 error = chkiqchg(ip, change, cred, i); 205 mutex_exit(&dq->dq_interlock); 206 if (error != 0) 207 return (error); 208 } 209 } 210 for (i = 0; i < MAXQUOTAS; i++) { 211 if ((dq = ip->i_dquot[i]) == NODQUOT) 212 continue; 213 mutex_enter(&dq->dq_interlock); 214 dq->dq_curinodes += change; 215 dq->dq_flags |= DQ_MOD; 216 mutex_exit(&dq->dq_interlock); 217 } 218 return (0); 219} 220 221/* 222 * Check for a valid change to a users allocation. 223 * Issue an error message if appropriate. 224 */ 225static int 226chkiqchg(struct inode *ip, int32_t change, kauth_cred_t cred, int type) 227{ 228 struct dquot *dq = ip->i_dquot[type]; 229 long ncurinodes = dq->dq_curinodes + change; 230 231 KASSERT(mutex_owned(&dq->dq_interlock)); 232 /* 233 * If user would exceed their hard limit, disallow inode allocation. 234 */ 235 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { 236 if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 && 237 ip->i_uid == kauth_cred_geteuid(cred)) { 238 uprintf("\n%s: write failed, %s inode limit reached\n", 239 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 240 quotatypes[type]); 241 dq->dq_flags |= DQ_WARN(QL_FILE); 242 } 243 return (EDQUOT); 244 } 245 /* 246 * If user is over their soft limit for too long, disallow inode 247 * allocation. Reset time limit as they cross their soft limit. 248 */ 249 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { 250 if (dq->dq_curinodes < dq->dq_isoftlimit) { 251 dq->dq_itime = 252 time_second + ip->i_ump->umq1_itime[type]; 253 if (ip->i_uid == kauth_cred_geteuid(cred)) 254 uprintf("\n%s: warning, %s %s\n", 255 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 256 quotatypes[type], "inode quota exceeded"); 257 return (0); 258 } 259 if (time_second > dq->dq_itime) { 260 if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 && 261 ip->i_uid == kauth_cred_geteuid(cred)) { 262 uprintf("\n%s: write failed, %s %s\n", 263 ITOV(ip)->v_mount->mnt_stat.f_mntonname, 264 quotatypes[type], 265 "inode quota exceeded for too long"); 266 dq->dq_flags |= DQ_WARN(QL_FILE); 267 } 268 return (EDQUOT); 269 } 270 } 271 return (0); 272} 273 274int 275quota1_umount(struct mount *mp, int flags) 276{ 277 int i, error; 278 struct ufsmount *ump = VFSTOUFS(mp); 279 struct lwp *l = curlwp; 280 281 if ((ump->um_flags & UFS_QUOTA) == 0) 282 return 0; 283 284 if ((error = vflush(mp, NULLVP, SKIPSYSTEM | flags)) != 0) 285 return (error); 286 287 for (i = 0; i < MAXQUOTAS; i++) { 288 if (ump->um_quotas[i] != NULLVP) { 289 quota1_handle_cmd_quotaoff(l, ump, i); 290 } 291 } 292 return 0; 293} 294 295/* 296 * Code to process quotactl commands. 297 */ 298 299/* 300 * set up a quota file for a particular file system. 301 */ 302int 303quota1_handle_cmd_quotaon(struct lwp *l, struct ufsmount *ump, int type, 304 const char *fname) 305{ 306 struct mount *mp = ump->um_mountp; 307 struct vnode *vp, **vpp, *mvp; 308 struct dquot *dq; 309 int error; 310 struct pathbuf *pb; 311 struct nameidata nd; 312 313 if (ump->um_flags & UFS_QUOTA2) { 314 uprintf("%s: quotas v2 already enabled\n", 315 mp->mnt_stat.f_mntonname); 316 return (EBUSY); 317 } 318 319 if (mp->mnt_wapbl != NULL) { 320 printf("%s: quota v1 cannot be used with -o log\n", 321 mp->mnt_stat.f_mntonname); 322 return (EOPNOTSUPP); 323 } 324 325 vpp = &ump->um_quotas[type]; 326 327 pb = pathbuf_create(fname); 328 if (pb == NULL) { 329 return ENOMEM; 330 } 331 NDINIT(&nd, LOOKUP, FOLLOW, pb); 332 if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) { 333 pathbuf_destroy(pb); 334 return error; 335 } 336 vp = nd.ni_vp; 337 pathbuf_destroy(pb); 338 339 VOP_UNLOCK(vp); 340 if (vp->v_type != VREG) { 341 (void) vn_close(vp, FREAD|FWRITE, l->l_cred); 342 return (EACCES); 343 } 344 if (*vpp != vp) 345 quota1_handle_cmd_quotaoff(l, ump, type); 346 mutex_enter(&dqlock); 347 while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0) 348 cv_wait(&dqcv, &dqlock); 349 ump->umq1_qflags[type] |= QTF_OPENING; 350 mutex_exit(&dqlock); 351 mp->mnt_flag |= MNT_QUOTA; 352 vp->v_vflag |= VV_SYSTEM; /* XXXSMP */ 353 *vpp = vp; 354 /* 355 * Save the credential of the process that turned on quotas. 356 * Set up the time limits for this quota. 357 */ 358 kauth_cred_hold(l->l_cred); 359 ump->um_cred[type] = l->l_cred; 360 ump->umq1_btime[type] = MAX_DQ_TIME; 361 ump->umq1_itime[type] = MAX_IQ_TIME; 362 if (dqget(NULLVP, 0, ump, type, &dq) == 0) { 363 if (dq->dq_btime > 0) 364 ump->umq1_btime[type] = dq->dq_btime; 365 if (dq->dq_itime > 0) 366 ump->umq1_itime[type] = dq->dq_itime; 367 dqrele(NULLVP, dq); 368 } 369 /* Allocate a marker vnode. */ 370 mvp = vnalloc(mp); 371 /* 372 * Search vnodes associated with this mount point, 373 * adding references to quota file being opened. 374 * NB: only need to add dquot's for inodes being modified. 375 */ 376 mutex_enter(&mntvnode_lock); 377again: 378 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) { 379 vmark(mvp, vp); 380 mutex_enter(vp->v_interlock); 381 if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) || 382 vp->v_type == VNON || vp->v_writecount == 0 || 383 (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) { 384 mutex_exit(vp->v_interlock); 385 continue; 386 } 387 mutex_exit(&mntvnode_lock); 388 if (vget(vp, LK_EXCLUSIVE)) { 389 mutex_enter(&mntvnode_lock); 390 (void)vunmark(mvp); 391 goto again; 392 } 393 if ((error = getinoquota(VTOI(vp))) != 0) { 394 vput(vp); 395 mutex_enter(&mntvnode_lock); 396 (void)vunmark(mvp); 397 break; 398 } 399 vput(vp); 400 mutex_enter(&mntvnode_lock); 401 } 402 mutex_exit(&mntvnode_lock); 403 vnfree(mvp); 404 405 mutex_enter(&dqlock); 406 ump->umq1_qflags[type] &= ~QTF_OPENING; 407 cv_broadcast(&dqcv); 408 if (error == 0) 409 ump->um_flags |= UFS_QUOTA; 410 mutex_exit(&dqlock); 411 if (error) 412 quota1_handle_cmd_quotaoff(l, ump, type); 413 return (error); 414} 415 416/* 417 * turn off disk quotas for a filesystem. 418 */ 419int 420quota1_handle_cmd_quotaoff(struct lwp *l, struct ufsmount *ump, int type) 421{ 422 struct mount *mp = ump->um_mountp; 423 struct vnode *vp; 424 struct vnode *qvp, *mvp; 425 struct dquot *dq; 426 struct inode *ip; 427 kauth_cred_t cred; 428 int i, error; 429 430 /* Allocate a marker vnode. */ 431 mvp = vnalloc(mp); 432 433 mutex_enter(&dqlock); 434 while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0) 435 cv_wait(&dqcv, &dqlock); 436 if ((qvp = ump->um_quotas[type]) == NULLVP) { 437 mutex_exit(&dqlock); 438 vnfree(mvp); 439 return (0); 440 } 441 ump->umq1_qflags[type] |= QTF_CLOSING; 442 ump->um_flags &= ~UFS_QUOTA; 443 mutex_exit(&dqlock); 444 /* 445 * Search vnodes associated with this mount point, 446 * deleting any references to quota file being closed. 447 */ 448 mutex_enter(&mntvnode_lock); 449again: 450 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) { 451 vmark(mvp, vp); 452 mutex_enter(vp->v_interlock); 453 if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) || 454 vp->v_type == VNON || 455 (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) { 456 mutex_exit(vp->v_interlock); 457 continue; 458 } 459 mutex_exit(&mntvnode_lock); 460 if (vget(vp, LK_EXCLUSIVE)) { 461 mutex_enter(&mntvnode_lock); 462 (void)vunmark(mvp); 463 goto again; 464 } 465 ip = VTOI(vp); 466 dq = ip->i_dquot[type]; 467 ip->i_dquot[type] = NODQUOT; 468 dqrele(vp, dq); 469 vput(vp); 470 mutex_enter(&mntvnode_lock); 471 } 472 mutex_exit(&mntvnode_lock); 473#ifdef DIAGNOSTIC 474 dqflush(qvp); 475#endif 476 qvp->v_vflag &= ~VV_SYSTEM; 477 error = vn_close(qvp, FREAD|FWRITE, l->l_cred); 478 mutex_enter(&dqlock); 479 ump->um_quotas[type] = NULLVP; 480 cred = ump->um_cred[type]; 481 ump->um_cred[type] = NOCRED; 482 for (i = 0; i < MAXQUOTAS; i++) 483 if (ump->um_quotas[i] != NULLVP) 484 break; 485 ump->umq1_qflags[type] &= ~QTF_CLOSING; 486 cv_broadcast(&dqcv); 487 mutex_exit(&dqlock); 488 kauth_cred_free(cred); 489 if (i == MAXQUOTAS) 490 mp->mnt_flag &= ~MNT_QUOTA; 491 return (error); 492} 493 494int 495quota1_handle_cmd_get(struct ufsmount *ump, const struct quotakey *qk, 496 struct quotaval *qv) 497{ 498 struct dquot *dq; 499 int error; 500 struct quotaval blocks, files; 501 int idtype; 502 id_t id; 503 504 idtype = qk->qk_idtype; 505 id = qk->qk_id; 506 507 if (ump->um_quotas[idtype] == NULLVP) 508 return ENODEV; 509 510 if (id == QUOTA_DEFAULTID) { /* we want the grace period of id 0 */ 511 if ((error = dqget(NULLVP, 0, ump, idtype, &dq)) != 0) 512 return error; 513 514 } else { 515 if ((error = dqget(NULLVP, id, ump, idtype, &dq)) != 0) 516 return error; 517 } 518 dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files); 519 dqrele(NULLVP, dq); 520 if (id == QUOTA_DEFAULTID) { 521 if (blocks.qv_expiretime > 0) 522 blocks.qv_grace = blocks.qv_expiretime; 523 else 524 blocks.qv_grace = MAX_DQ_TIME; 525 if (files.qv_expiretime > 0) 526 files.qv_grace = files.qv_expiretime; 527 else 528 files.qv_grace = MAX_DQ_TIME; 529 } 530 531 switch (qk->qk_objtype) { 532 case QUOTA_OBJTYPE_BLOCKS: 533 *qv = blocks; 534 break; 535 case QUOTA_OBJTYPE_FILES: 536 *qv = files; 537 break; 538 default: 539 return EINVAL; 540 } 541 542 return 0; 543} 544 545static uint32_t 546quota1_encode_limit(uint64_t lim) 547{ 548 if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) { 549 return 0; 550 } 551 return lim; 552} 553 554int 555quota1_handle_cmd_put(struct ufsmount *ump, const struct quotakey *key, 556 const struct quotaval *val) 557{ 558 struct dquot *dq; 559 struct dqblk dqb; 560 int error; 561 562 switch (key->qk_idtype) { 563 case QUOTA_IDTYPE_USER: 564 case QUOTA_IDTYPE_GROUP: 565 break; 566 default: 567 return EINVAL; 568 } 569 570 switch (key->qk_objtype) { 571 case QUOTA_OBJTYPE_BLOCKS: 572 case QUOTA_OBJTYPE_FILES: 573 break; 574 default: 575 return EINVAL; 576 } 577 578 if (ump->um_quotas[key->qk_idtype] == NULLVP) 579 return ENODEV; 580 581 if (key->qk_id == QUOTA_DEFAULTID) { 582 /* just update grace times */ 583 id_t id = 0; 584 585 if ((error = dqget(NULLVP, id, ump, key->qk_idtype, &dq)) != 0) 586 return error; 587 mutex_enter(&dq->dq_interlock); 588 if (val->qv_grace != QUOTA_NOTIME) { 589 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) 590 ump->umq1_btime[key->qk_idtype] = dq->dq_btime = 591 val->qv_grace; 592 if (key->qk_objtype == QUOTA_OBJTYPE_FILES) 593 ump->umq1_itime[key->qk_idtype] = dq->dq_itime = 594 val->qv_grace; 595 } 596 dq->dq_flags |= DQ_MOD; 597 mutex_exit(&dq->dq_interlock); 598 dqrele(NULLVP, dq); 599 return 0; 600 } 601 602 if ((error = dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0) 603 return (error); 604 mutex_enter(&dq->dq_interlock); 605 /* 606 * Copy all but the current values. 607 * Reset time limit if previously had no soft limit or were 608 * under it, but now have a soft limit and are over it. 609 */ 610 dqb.dqb_curblocks = dq->dq_curblocks; 611 dqb.dqb_curinodes = dq->dq_curinodes; 612 dqb.dqb_btime = dq->dq_btime; 613 dqb.dqb_itime = dq->dq_itime; 614 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) { 615 dqb.dqb_bsoftlimit = quota1_encode_limit(val->qv_softlimit); 616 dqb.dqb_bhardlimit = quota1_encode_limit(val->qv_hardlimit); 617 dqb.dqb_isoftlimit = dq->dq_isoftlimit; 618 dqb.dqb_ihardlimit = dq->dq_ihardlimit; 619 } else { 620 KASSERT(key->qk_objtype == QUOTA_OBJTYPE_FILES); 621 dqb.dqb_bsoftlimit = dq->dq_bsoftlimit; 622 dqb.dqb_bhardlimit = dq->dq_bhardlimit; 623 dqb.dqb_isoftlimit = quota1_encode_limit(val->qv_softlimit); 624 dqb.dqb_ihardlimit = quota1_encode_limit(val->qv_hardlimit); 625 } 626 if (dq->dq_id == 0 && val->qv_grace != QUOTA_NOTIME) { 627 /* also update grace time if available */ 628 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) { 629 ump->umq1_btime[key->qk_idtype] = dqb.dqb_btime = 630 val->qv_grace; 631 } 632 if (key->qk_objtype == QUOTA_OBJTYPE_FILES) { 633 ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime = 634 val->qv_grace; 635 } 636 } 637 if (dqb.dqb_bsoftlimit && 638 dq->dq_curblocks >= dqb.dqb_bsoftlimit && 639 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) 640 dqb.dqb_btime = time_second + ump->umq1_btime[key->qk_idtype]; 641 if (dqb.dqb_isoftlimit && 642 dq->dq_curinodes >= dqb.dqb_isoftlimit && 643 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) 644 dqb.dqb_itime = time_second + ump->umq1_itime[key->qk_idtype]; 645 dq->dq_un.dq1_dqb = dqb; 646 if (dq->dq_curblocks < dq->dq_bsoftlimit) 647 dq->dq_flags &= ~DQ_WARN(QL_BLOCK); 648 if (dq->dq_curinodes < dq->dq_isoftlimit) 649 dq->dq_flags &= ~DQ_WARN(QL_FILE); 650 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 651 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 652 dq->dq_flags |= DQ_FAKE; 653 else 654 dq->dq_flags &= ~DQ_FAKE; 655 dq->dq_flags |= DQ_MOD; 656 mutex_exit(&dq->dq_interlock); 657 dqrele(NULLVP, dq); 658 return (0); 659} 660 661 662#if 0 663/* 664 * Q_SETQUOTA - assign an entire dqblk structure. 665 */ 666int 667setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb) 668{ 669 struct dquot *dq; 670 struct dquot *ndq; 671 struct ufsmount *ump = VFSTOUFS(mp); 672 673 674 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0) 675 return (error); 676 dq = ndq; 677 mutex_enter(&dq->dq_interlock); 678 /* 679 * Copy all but the current values. 680 * Reset time limit if previously had no soft limit or were 681 * under it, but now have a soft limit and are over it. 682 */ 683 dqb->dqb_curblocks = dq->dq_curblocks; 684 dqb->dqb_curinodes = dq->dq_curinodes; 685 if (dq->dq_id != 0) { 686 dqb->dqb_btime = dq->dq_btime; 687 dqb->dqb_itime = dq->dq_itime; 688 } 689 if (dqb->dqb_bsoftlimit && 690 dq->dq_curblocks >= dqb->dqb_bsoftlimit && 691 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) 692 dqb->dqb_btime = time_second + ump->umq1_btime[type]; 693 if (dqb->dqb_isoftlimit && 694 dq->dq_curinodes >= dqb->dqb_isoftlimit && 695 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) 696 dqb->dqb_itime = time_second + ump->umq1_itime[type]; 697 dq->dq_un.dq1_dqb = *dqb; 698 if (dq->dq_curblocks < dq->dq_bsoftlimit) 699 dq->dq_flags &= ~DQ_WARN(QL_BLOCK); 700 if (dq->dq_curinodes < dq->dq_isoftlimit) 701 dq->dq_flags &= ~DQ_WARN(QL_FILE); 702 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 703 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 704 dq->dq_flags |= DQ_FAKE; 705 else 706 dq->dq_flags &= ~DQ_FAKE; 707 dq->dq_flags |= DQ_MOD; 708 mutex_exit(&dq->dq_interlock); 709 dqrele(NULLVP, dq); 710 return (0); 711} 712 713/* 714 * Q_SETUSE - set current inode and block usage. 715 */ 716int 717setuse(struct mount *mp, u_long id, int type, void *addr) 718{ 719 struct dquot *dq; 720 struct ufsmount *ump = VFSTOUFS(mp); 721 struct dquot *ndq; 722 struct dqblk usage; 723 int error; 724 725 error = copyin(addr, (void *)&usage, sizeof (struct dqblk)); 726 if (error) 727 return (error); 728 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0) 729 return (error); 730 dq = ndq; 731 mutex_enter(&dq->dq_interlock); 732 /* 733 * Reset time limit if have a soft limit and were 734 * previously under it, but are now over it. 735 */ 736 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && 737 usage.dqb_curblocks >= dq->dq_bsoftlimit) 738 dq->dq_btime = time_second + ump->umq1_btime[type]; 739 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && 740 usage.dqb_curinodes >= dq->dq_isoftlimit) 741 dq->dq_itime = time_second + ump->umq1_itime[type]; 742 dq->dq_curblocks = usage.dqb_curblocks; 743 dq->dq_curinodes = usage.dqb_curinodes; 744 if (dq->dq_curblocks < dq->dq_bsoftlimit) 745 dq->dq_flags &= ~DQ_WARN(QL_BLOCK); 746 if (dq->dq_curinodes < dq->dq_isoftlimit) 747 dq->dq_flags &= ~DQ_WARN(QL_FILE); 748 dq->dq_flags |= DQ_MOD; 749 mutex_exit(&dq->dq_interlock); 750 dqrele(NULLVP, dq); 751 return (0); 752} 753#endif 754 755/* 756 * Q_SYNC - sync quota files to disk. 757 */ 758int 759q1sync(struct mount *mp) 760{ 761 struct ufsmount *ump = VFSTOUFS(mp); 762 struct vnode *vp, *mvp; 763 struct dquot *dq; 764 int i, error; 765 766 /* 767 * Check if the mount point has any quotas. 768 * If not, simply return. 769 */ 770 for (i = 0; i < MAXQUOTAS; i++) 771 if (ump->um_quotas[i] != NULLVP) 772 break; 773 if (i == MAXQUOTAS) 774 return (0); 775 776 /* Allocate a marker vnode. */ 777 mvp = vnalloc(mp); 778 779 /* 780 * Search vnodes associated with this mount point, 781 * synchronizing any modified dquot structures. 782 */ 783 mutex_enter(&mntvnode_lock); 784 again: 785 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) { 786 vmark(mvp, vp); 787 mutex_enter(vp->v_interlock); 788 if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) || 789 vp->v_type == VNON || 790 (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) { 791 mutex_exit(vp->v_interlock); 792 continue; 793 } 794 mutex_exit(&mntvnode_lock); 795 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT); 796 if (error) { 797 mutex_enter(&mntvnode_lock); 798 if (error == ENOENT) { 799 (void)vunmark(mvp); 800 goto again; 801 } 802 continue; 803 } 804 for (i = 0; i < MAXQUOTAS; i++) { 805 dq = VTOI(vp)->i_dquot[i]; 806 if (dq == NODQUOT) 807 continue; 808 mutex_enter(&dq->dq_interlock); 809 if (dq->dq_flags & DQ_MOD) 810 dq1sync(vp, dq); 811 mutex_exit(&dq->dq_interlock); 812 } 813 vput(vp); 814 mutex_enter(&mntvnode_lock); 815 } 816 mutex_exit(&mntvnode_lock); 817 vnfree(mvp); 818 return (0); 819} 820 821/* 822 * Obtain a dquot structure for the specified identifier and quota file 823 * reading the information from the file if necessary. 824 */ 825int 826dq1get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type, 827 struct dquot *dq) 828{ 829 struct iovec aiov; 830 struct uio auio; 831 int error; 832 833 KASSERT(mutex_owned(&dq->dq_interlock)); 834 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY); 835 auio.uio_iov = &aiov; 836 auio.uio_iovcnt = 1; 837 aiov.iov_base = (void *)&dq->dq_un.dq1_dqb; 838 aiov.iov_len = sizeof (struct dqblk); 839 auio.uio_resid = sizeof (struct dqblk); 840 auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); 841 auio.uio_rw = UIO_READ; 842 UIO_SETUP_SYSSPACE(&auio); 843 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); 844 if (auio.uio_resid == sizeof(struct dqblk) && error == 0) 845 memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk)); 846 VOP_UNLOCK(dqvp); 847 /* 848 * I/O error in reading quota file, release 849 * quota structure and reflect problem to caller. 850 */ 851 if (error) 852 return (error); 853 /* 854 * Check for no limit to enforce. 855 * Initialize time values if necessary. 856 */ 857 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 858 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 859 dq->dq_flags |= DQ_FAKE; 860 if (dq->dq_id != 0) { 861 if (dq->dq_btime == 0) 862 dq->dq_btime = time_second + ump->umq1_btime[type]; 863 if (dq->dq_itime == 0) 864 dq->dq_itime = time_second + ump->umq1_itime[type]; 865 } 866 return (0); 867} 868 869/* 870 * Update the disk quota in the quota file. 871 */ 872int 873dq1sync(struct vnode *vp, struct dquot *dq) 874{ 875 struct vnode *dqvp; 876 struct iovec aiov; 877 struct uio auio; 878 int error; 879 880 if (dq == NODQUOT) 881 panic("dq1sync: dquot"); 882 KASSERT(mutex_owned(&dq->dq_interlock)); 883 if ((dq->dq_flags & DQ_MOD) == 0) 884 return (0); 885 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) 886 panic("dq1sync: file"); 887 KASSERT(dqvp != vp); 888 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY); 889 auio.uio_iov = &aiov; 890 auio.uio_iovcnt = 1; 891 aiov.iov_base = (void *)&dq->dq_un.dq1_dqb; 892 aiov.iov_len = sizeof (struct dqblk); 893 auio.uio_resid = sizeof (struct dqblk); 894 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); 895 auio.uio_rw = UIO_WRITE; 896 UIO_SETUP_SYSSPACE(&auio); 897 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); 898 if (auio.uio_resid && error == 0) 899 error = EIO; 900 dq->dq_flags &= ~DQ_MOD; 901 VOP_UNLOCK(dqvp); 902 return (error); 903} 904