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