1 /* $NetBSD: ufs_quota1.c,v 1.22 2016/06/20 00:52:04 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.22 2016/06/20 00:52:04 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 56 static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int); 57 static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int); 58 59 /* 60 * Update disk usage, and take corrective action. 61 */ 62 int 63 chkdq1(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 */ 118 static int 119 chkdqchg(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 */ 170 int 171 chkiq1(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 */ 225 static int 226 chkiqchg(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 274 int 275 quota1_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 */ 302 int 303 quota1_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; 308 struct vnode_iterator *marker; 309 struct dquot *dq; 310 int error; 311 struct pathbuf *pb; 312 struct nameidata nd; 313 314 if (ump->um_flags & UFS_QUOTA2) { 315 uprintf("%s: quotas v2 already enabled\n", 316 mp->mnt_stat.f_mntonname); 317 return (EBUSY); 318 } 319 320 if (mp->mnt_wapbl != NULL) { 321 printf("%s: quota v1 cannot be used with -o log\n", 322 mp->mnt_stat.f_mntonname); 323 return (EOPNOTSUPP); 324 } 325 326 vpp = &ump->um_quotas[type]; 327 328 pb = pathbuf_create(fname); 329 if (pb == NULL) { 330 return ENOMEM; 331 } 332 NDINIT(&nd, LOOKUP, FOLLOW, pb); 333 if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) { 334 pathbuf_destroy(pb); 335 return error; 336 } 337 vp = nd.ni_vp; 338 pathbuf_destroy(pb); 339 340 VOP_UNLOCK(vp); 341 if (vp->v_type != VREG) { 342 (void) vn_close(vp, FREAD|FWRITE, l->l_cred); 343 return (EACCES); 344 } 345 if (*vpp != vp) 346 quota1_handle_cmd_quotaoff(l, ump, type); 347 mutex_enter(&dqlock); 348 while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0) 349 cv_wait(&dqcv, &dqlock); 350 ump->umq1_qflags[type] |= QTF_OPENING; 351 mutex_exit(&dqlock); 352 mp->mnt_flag |= MNT_QUOTA; 353 vp->v_vflag |= VV_SYSTEM; /* XXXSMP */ 354 *vpp = vp; 355 /* 356 * Save the credential of the process that turned on quotas. 357 * Set up the time limits for this quota. 358 */ 359 kauth_cred_hold(l->l_cred); 360 ump->um_cred[type] = l->l_cred; 361 ump->umq1_btime[type] = MAX_DQ_TIME; 362 ump->umq1_itime[type] = MAX_IQ_TIME; 363 if (dqget(NULLVP, 0, ump, type, &dq) == 0) { 364 if (dq->dq_btime > 0) 365 ump->umq1_btime[type] = dq->dq_btime; 366 if (dq->dq_itime > 0) 367 ump->umq1_itime[type] = dq->dq_itime; 368 dqrele(NULLVP, dq); 369 } 370 /* 371 * Search vnodes associated with this mount point, 372 * adding references to quota file being opened. 373 * NB: only need to add dquot's for inodes being modified. 374 */ 375 vfs_vnode_iterator_init(mp, &marker); 376 while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) { 377 error = vn_lock(vp, LK_EXCLUSIVE); 378 if (error) { 379 vrele(vp); 380 continue; 381 } 382 mutex_enter(vp->v_interlock); 383 if (VTOI(vp) == NULL || vp->v_type == VNON || 384 vp->v_writecount == 0) { 385 mutex_exit(vp->v_interlock); 386 vput(vp); 387 continue; 388 } 389 mutex_exit(vp->v_interlock); 390 if ((error = getinoquota(VTOI(vp))) != 0) { 391 vput(vp); 392 break; 393 } 394 vput(vp); 395 } 396 vfs_vnode_iterator_destroy(marker); 397 398 mutex_enter(&dqlock); 399 ump->umq1_qflags[type] &= ~QTF_OPENING; 400 cv_broadcast(&dqcv); 401 if (error == 0) 402 ump->um_flags |= UFS_QUOTA; 403 mutex_exit(&dqlock); 404 if (error) 405 quota1_handle_cmd_quotaoff(l, ump, type); 406 return (error); 407 } 408 409 /* 410 * turn off disk quotas for a filesystem. 411 */ 412 int 413 quota1_handle_cmd_quotaoff(struct lwp *l, struct ufsmount *ump, int type) 414 { 415 struct mount *mp = ump->um_mountp; 416 struct vnode *vp; 417 struct vnode *qvp; 418 struct vnode_iterator *marker; 419 struct dquot *dq; 420 struct inode *ip; 421 kauth_cred_t cred; 422 int i, error; 423 424 mutex_enter(&dqlock); 425 while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0) 426 cv_wait(&dqcv, &dqlock); 427 if ((qvp = ump->um_quotas[type]) == NULLVP) { 428 mutex_exit(&dqlock); 429 return (0); 430 } 431 ump->umq1_qflags[type] |= QTF_CLOSING; 432 ump->um_flags &= ~UFS_QUOTA; 433 mutex_exit(&dqlock); 434 /* 435 * Search vnodes associated with this mount point, 436 * deleting any references to quota file being closed. 437 */ 438 vfs_vnode_iterator_init(mp, &marker); 439 while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) { 440 error = vn_lock(vp, LK_EXCLUSIVE); 441 if (error) { 442 vrele(vp); 443 continue; 444 } 445 ip = VTOI(vp); 446 if (ip == NULL || vp->v_type == VNON) { 447 vput(vp); 448 continue; 449 } 450 dq = ip->i_dquot[type]; 451 ip->i_dquot[type] = NODQUOT; 452 dqrele(vp, dq); 453 vput(vp); 454 } 455 vfs_vnode_iterator_destroy(marker); 456 #ifdef DIAGNOSTIC 457 dqflush(qvp); 458 #endif 459 qvp->v_vflag &= ~VV_SYSTEM; 460 error = vn_close(qvp, FREAD|FWRITE, l->l_cred); 461 mutex_enter(&dqlock); 462 ump->um_quotas[type] = NULLVP; 463 cred = ump->um_cred[type]; 464 ump->um_cred[type] = NOCRED; 465 for (i = 0; i < MAXQUOTAS; i++) 466 if (ump->um_quotas[i] != NULLVP) 467 break; 468 ump->umq1_qflags[type] &= ~QTF_CLOSING; 469 cv_broadcast(&dqcv); 470 mutex_exit(&dqlock); 471 kauth_cred_free(cred); 472 if (i == MAXQUOTAS) 473 mp->mnt_flag &= ~MNT_QUOTA; 474 return (error); 475 } 476 477 int 478 quota1_handle_cmd_get(struct ufsmount *ump, const struct quotakey *qk, 479 struct quotaval *qv) 480 { 481 struct dquot *dq; 482 int error; 483 struct quotaval blocks, files; 484 int idtype; 485 id_t id; 486 487 idtype = qk->qk_idtype; 488 id = qk->qk_id; 489 490 if (ump->um_quotas[idtype] == NULLVP) 491 return ENODEV; 492 493 if (id == QUOTA_DEFAULTID) { /* we want the grace period of id 0 */ 494 if ((error = dqget(NULLVP, 0, ump, idtype, &dq)) != 0) 495 return error; 496 497 } else { 498 if ((error = dqget(NULLVP, id, ump, idtype, &dq)) != 0) 499 return error; 500 } 501 dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files); 502 dqrele(NULLVP, dq); 503 if (id == QUOTA_DEFAULTID) { 504 if (blocks.qv_expiretime > 0) 505 blocks.qv_grace = blocks.qv_expiretime; 506 else 507 blocks.qv_grace = MAX_DQ_TIME; 508 if (files.qv_expiretime > 0) 509 files.qv_grace = files.qv_expiretime; 510 else 511 files.qv_grace = MAX_DQ_TIME; 512 } 513 514 switch (qk->qk_objtype) { 515 case QUOTA_OBJTYPE_BLOCKS: 516 *qv = blocks; 517 break; 518 case QUOTA_OBJTYPE_FILES: 519 *qv = files; 520 break; 521 default: 522 return EINVAL; 523 } 524 525 return 0; 526 } 527 528 static uint32_t 529 quota1_encode_limit(uint64_t lim) 530 { 531 if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) { 532 return 0; 533 } 534 return lim; 535 } 536 537 int 538 quota1_handle_cmd_put(struct ufsmount *ump, const struct quotakey *key, 539 const struct quotaval *val) 540 { 541 struct dquot *dq; 542 struct dqblk dqb; 543 int error; 544 545 switch (key->qk_idtype) { 546 case QUOTA_IDTYPE_USER: 547 case QUOTA_IDTYPE_GROUP: 548 break; 549 default: 550 return EINVAL; 551 } 552 553 switch (key->qk_objtype) { 554 case QUOTA_OBJTYPE_BLOCKS: 555 case QUOTA_OBJTYPE_FILES: 556 break; 557 default: 558 return EINVAL; 559 } 560 561 if (ump->um_quotas[key->qk_idtype] == NULLVP) 562 return ENODEV; 563 564 if (key->qk_id == QUOTA_DEFAULTID) { 565 /* just update grace times */ 566 id_t id = 0; 567 568 if ((error = dqget(NULLVP, id, ump, key->qk_idtype, &dq)) != 0) 569 return error; 570 mutex_enter(&dq->dq_interlock); 571 if (val->qv_grace != QUOTA_NOTIME) { 572 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) 573 ump->umq1_btime[key->qk_idtype] = dq->dq_btime = 574 val->qv_grace; 575 if (key->qk_objtype == QUOTA_OBJTYPE_FILES) 576 ump->umq1_itime[key->qk_idtype] = dq->dq_itime = 577 val->qv_grace; 578 } 579 dq->dq_flags |= DQ_MOD; 580 mutex_exit(&dq->dq_interlock); 581 dqrele(NULLVP, dq); 582 return 0; 583 } 584 585 if ((error = dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0) 586 return (error); 587 mutex_enter(&dq->dq_interlock); 588 /* 589 * Copy all but the current values. 590 * Reset time limit if previously had no soft limit or were 591 * under it, but now have a soft limit and are over it. 592 */ 593 dqb.dqb_curblocks = dq->dq_curblocks; 594 dqb.dqb_curinodes = dq->dq_curinodes; 595 dqb.dqb_btime = dq->dq_btime; 596 dqb.dqb_itime = dq->dq_itime; 597 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) { 598 dqb.dqb_bsoftlimit = quota1_encode_limit(val->qv_softlimit); 599 dqb.dqb_bhardlimit = quota1_encode_limit(val->qv_hardlimit); 600 dqb.dqb_isoftlimit = dq->dq_isoftlimit; 601 dqb.dqb_ihardlimit = dq->dq_ihardlimit; 602 } else { 603 KASSERT(key->qk_objtype == QUOTA_OBJTYPE_FILES); 604 dqb.dqb_bsoftlimit = dq->dq_bsoftlimit; 605 dqb.dqb_bhardlimit = dq->dq_bhardlimit; 606 dqb.dqb_isoftlimit = quota1_encode_limit(val->qv_softlimit); 607 dqb.dqb_ihardlimit = quota1_encode_limit(val->qv_hardlimit); 608 } 609 if (dq->dq_id == 0 && val->qv_grace != QUOTA_NOTIME) { 610 /* also update grace time if available */ 611 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) { 612 ump->umq1_btime[key->qk_idtype] = dqb.dqb_btime = 613 val->qv_grace; 614 } 615 if (key->qk_objtype == QUOTA_OBJTYPE_FILES) { 616 ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime = 617 val->qv_grace; 618 } 619 } 620 if (dqb.dqb_bsoftlimit && 621 dq->dq_curblocks >= dqb.dqb_bsoftlimit && 622 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) 623 dqb.dqb_btime = time_second + ump->umq1_btime[key->qk_idtype]; 624 if (dqb.dqb_isoftlimit && 625 dq->dq_curinodes >= dqb.dqb_isoftlimit && 626 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) 627 dqb.dqb_itime = time_second + ump->umq1_itime[key->qk_idtype]; 628 dq->dq_un.dq1_dqb = dqb; 629 if (dq->dq_curblocks < dq->dq_bsoftlimit) 630 dq->dq_flags &= ~DQ_WARN(QL_BLOCK); 631 if (dq->dq_curinodes < dq->dq_isoftlimit) 632 dq->dq_flags &= ~DQ_WARN(QL_FILE); 633 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 634 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 635 dq->dq_flags |= DQ_FAKE; 636 else 637 dq->dq_flags &= ~DQ_FAKE; 638 dq->dq_flags |= DQ_MOD; 639 mutex_exit(&dq->dq_interlock); 640 dqrele(NULLVP, dq); 641 return (0); 642 } 643 644 645 #if 0 646 /* 647 * Q_SETQUOTA - assign an entire dqblk structure. 648 */ 649 int 650 setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb) 651 { 652 struct dquot *dq; 653 struct dquot *ndq; 654 struct ufsmount *ump = VFSTOUFS(mp); 655 656 657 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0) 658 return (error); 659 dq = ndq; 660 mutex_enter(&dq->dq_interlock); 661 /* 662 * Copy all but the current values. 663 * Reset time limit if previously had no soft limit or were 664 * under it, but now have a soft limit and are over it. 665 */ 666 dqb->dqb_curblocks = dq->dq_curblocks; 667 dqb->dqb_curinodes = dq->dq_curinodes; 668 if (dq->dq_id != 0) { 669 dqb->dqb_btime = dq->dq_btime; 670 dqb->dqb_itime = dq->dq_itime; 671 } 672 if (dqb->dqb_bsoftlimit && 673 dq->dq_curblocks >= dqb->dqb_bsoftlimit && 674 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) 675 dqb->dqb_btime = time_second + ump->umq1_btime[type]; 676 if (dqb->dqb_isoftlimit && 677 dq->dq_curinodes >= dqb->dqb_isoftlimit && 678 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) 679 dqb->dqb_itime = time_second + ump->umq1_itime[type]; 680 dq->dq_un.dq1_dqb = *dqb; 681 if (dq->dq_curblocks < dq->dq_bsoftlimit) 682 dq->dq_flags &= ~DQ_WARN(QL_BLOCK); 683 if (dq->dq_curinodes < dq->dq_isoftlimit) 684 dq->dq_flags &= ~DQ_WARN(QL_FILE); 685 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 686 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 687 dq->dq_flags |= DQ_FAKE; 688 else 689 dq->dq_flags &= ~DQ_FAKE; 690 dq->dq_flags |= DQ_MOD; 691 mutex_exit(&dq->dq_interlock); 692 dqrele(NULLVP, dq); 693 return (0); 694 } 695 696 /* 697 * Q_SETUSE - set current inode and block usage. 698 */ 699 int 700 setuse(struct mount *mp, u_long id, int type, void *addr) 701 { 702 struct dquot *dq; 703 struct ufsmount *ump = VFSTOUFS(mp); 704 struct dquot *ndq; 705 struct dqblk usage; 706 int error; 707 708 error = copyin(addr, (void *)&usage, sizeof (struct dqblk)); 709 if (error) 710 return (error); 711 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0) 712 return (error); 713 dq = ndq; 714 mutex_enter(&dq->dq_interlock); 715 /* 716 * Reset time limit if have a soft limit and were 717 * previously under it, but are now over it. 718 */ 719 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && 720 usage.dqb_curblocks >= dq->dq_bsoftlimit) 721 dq->dq_btime = time_second + ump->umq1_btime[type]; 722 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && 723 usage.dqb_curinodes >= dq->dq_isoftlimit) 724 dq->dq_itime = time_second + ump->umq1_itime[type]; 725 dq->dq_curblocks = usage.dqb_curblocks; 726 dq->dq_curinodes = usage.dqb_curinodes; 727 if (dq->dq_curblocks < dq->dq_bsoftlimit) 728 dq->dq_flags &= ~DQ_WARN(QL_BLOCK); 729 if (dq->dq_curinodes < dq->dq_isoftlimit) 730 dq->dq_flags &= ~DQ_WARN(QL_FILE); 731 dq->dq_flags |= DQ_MOD; 732 mutex_exit(&dq->dq_interlock); 733 dqrele(NULLVP, dq); 734 return (0); 735 } 736 #endif 737 738 /* 739 * Q_SYNC - sync quota files to disk. 740 */ 741 int 742 q1sync(struct mount *mp) 743 { 744 struct ufsmount *ump = VFSTOUFS(mp); 745 struct vnode *vp; 746 struct vnode_iterator *marker; 747 struct dquot *dq; 748 int i, error; 749 750 /* 751 * Check if the mount point has any quotas. 752 * If not, simply return. 753 */ 754 for (i = 0; i < MAXQUOTAS; i++) 755 if (ump->um_quotas[i] != NULLVP) 756 break; 757 if (i == MAXQUOTAS) 758 return (0); 759 760 /* 761 * Search vnodes associated with this mount point, 762 * synchronizing any modified dquot structures. 763 */ 764 vfs_vnode_iterator_init(mp, &marker); 765 while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) { 766 error = vn_lock(vp, LK_EXCLUSIVE); 767 if (error) { 768 vrele(vp); 769 continue; 770 } 771 if (VTOI(vp) == NULL || vp->v_type == VNON) { 772 vput(vp); 773 continue; 774 } 775 for (i = 0; i < MAXQUOTAS; i++) { 776 dq = VTOI(vp)->i_dquot[i]; 777 if (dq == NODQUOT) 778 continue; 779 mutex_enter(&dq->dq_interlock); 780 if (dq->dq_flags & DQ_MOD) 781 dq1sync(vp, dq); 782 mutex_exit(&dq->dq_interlock); 783 } 784 vput(vp); 785 } 786 vfs_vnode_iterator_destroy(marker); 787 return (0); 788 } 789 790 /* 791 * Obtain a dquot structure for the specified identifier and quota file 792 * reading the information from the file if necessary. 793 */ 794 int 795 dq1get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type, 796 struct dquot *dq) 797 { 798 struct iovec aiov; 799 struct uio auio; 800 int error; 801 802 KASSERT(mutex_owned(&dq->dq_interlock)); 803 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY); 804 auio.uio_iov = &aiov; 805 auio.uio_iovcnt = 1; 806 aiov.iov_base = (void *)&dq->dq_un.dq1_dqb; 807 aiov.iov_len = sizeof (struct dqblk); 808 auio.uio_resid = sizeof (struct dqblk); 809 auio.uio_offset = (off_t)id * sizeof (struct dqblk); 810 auio.uio_rw = UIO_READ; 811 UIO_SETUP_SYSSPACE(&auio); 812 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); 813 if (auio.uio_resid == sizeof(struct dqblk) && error == 0) 814 memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk)); 815 VOP_UNLOCK(dqvp); 816 /* 817 * I/O error in reading quota file, release 818 * quota structure and reflect problem to caller. 819 */ 820 if (error) 821 return (error); 822 /* 823 * Check for no limit to enforce. 824 * Initialize time values if necessary. 825 */ 826 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && 827 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) 828 dq->dq_flags |= DQ_FAKE; 829 if (dq->dq_id != 0) { 830 if (dq->dq_btime == 0) 831 dq->dq_btime = time_second + ump->umq1_btime[type]; 832 if (dq->dq_itime == 0) 833 dq->dq_itime = time_second + ump->umq1_itime[type]; 834 } 835 return (0); 836 } 837 838 /* 839 * Update the disk quota in the quota file. 840 */ 841 int 842 dq1sync(struct vnode *vp, struct dquot *dq) 843 { 844 struct vnode *dqvp; 845 struct iovec aiov; 846 struct uio auio; 847 int error; 848 849 if (dq == NODQUOT) 850 panic("dq1sync: dquot"); 851 KASSERT(mutex_owned(&dq->dq_interlock)); 852 if ((dq->dq_flags & DQ_MOD) == 0) 853 return (0); 854 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) 855 panic("dq1sync: file"); 856 KASSERT(dqvp != vp); 857 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY); 858 auio.uio_iov = &aiov; 859 auio.uio_iovcnt = 1; 860 aiov.iov_base = (void *)&dq->dq_un.dq1_dqb; 861 aiov.iov_len = sizeof (struct dqblk); 862 auio.uio_resid = sizeof (struct dqblk); 863 auio.uio_offset = (off_t)dq->dq_id * sizeof (struct dqblk); 864 auio.uio_rw = UIO_WRITE; 865 UIO_SETUP_SYSSPACE(&auio); 866 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); 867 if (auio.uio_resid && error == 0) 868 error = EIO; 869 dq->dq_flags &= ~DQ_MOD; 870 VOP_UNLOCK(dqvp); 871 return (error); 872 } 873