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