1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 1980, 1990, 1993 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 35#include <sys/cdefs.h> 36__RCSID("$NetBSD$"); 37 38#include <sys/types.h> 39#include <sys/stat.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <unistd.h> 44#include <fcntl.h> 45#include <limits.h> 46#include <fstab.h> 47#include <errno.h> 48#include <err.h> 49 50#include <ufs/ufs/quota1.h> 51 52#include <quota.h> 53#include "quotapvt.h" 54 55struct oldfiles_fstabentry { 56 char *ofe_mountpoint; 57 int ofe_hasuserquota; 58 int ofe_hasgroupquota; 59 char *ofe_userquotafile; 60 char *ofe_groupquotafile; 61}; 62 63struct oldfiles_quotacursor { 64 unsigned oqc_doingusers; 65 unsigned oqc_doinggroups; 66 67 unsigned oqc_numusers; 68 unsigned oqc_numgroups; 69 70 unsigned oqc_didusers; 71 unsigned oqc_didgroups; 72 unsigned oqc_diddefault; 73 unsigned oqc_pos; 74 unsigned oqc_didblocks; 75}; 76 77static struct oldfiles_fstabentry *__quota_oldfiles_fstab; 78static unsigned __quota_oldfiles_numfstab; 79static unsigned __quota_oldfiles_maxfstab; 80static int __quota_oldfiles_fstab_loaded; 81 82static const struct oldfiles_fstabentry * 83__quota_oldfiles_find_fstabentry(const char *mountpoint) 84{ 85 unsigned i; 86 87 for (i = 0; i < __quota_oldfiles_numfstab; i++) { 88 if (!strcmp(mountpoint, 89 __quota_oldfiles_fstab[i].ofe_mountpoint)) { 90 return &__quota_oldfiles_fstab[i]; 91 } 92 } 93 return NULL; 94} 95 96static int 97__quota_oldfiles_add_fstabentry(struct oldfiles_fstabentry *ofe) 98{ 99 unsigned newmax; 100 struct oldfiles_fstabentry *newptr; 101 102 if (__quota_oldfiles_numfstab + 1 >= __quota_oldfiles_maxfstab) { 103 if (__quota_oldfiles_maxfstab == 0) { 104 newmax = 4; 105 } else { 106 newmax = __quota_oldfiles_maxfstab * 2; 107 } 108 newptr = realloc(__quota_oldfiles_fstab, 109 newmax * sizeof(__quota_oldfiles_fstab[0])); 110 if (newptr == NULL) { 111 return -1; 112 } 113 __quota_oldfiles_maxfstab = newmax; 114 __quota_oldfiles_fstab = newptr; 115 } 116 117 __quota_oldfiles_fstab[__quota_oldfiles_numfstab++] = *ofe; 118 return 0; 119} 120 121static int 122__quota_oldfiles_fill_fstabentry(const struct fstab *fs, 123 struct oldfiles_fstabentry *ofe) 124{ 125 char buf[256]; 126 char *opt, *state, *s; 127 int serrno; 128 int ret = 0; 129 130 /* 131 * Inspect the mount options to find the quota files. 132 * XXX this info should be gotten from the kernel. 133 * 134 * The options are: 135 * userquota[=path] enable user quotas 136 * groupquota[=path] enable group quotas 137 */ 138 139 ofe->ofe_mountpoint = NULL; 140 ofe->ofe_hasuserquota = ofe->ofe_hasgroupquota = 0; 141 ofe->ofe_userquotafile = ofe->ofe_groupquotafile = NULL; 142 143 strlcpy(buf, fs->fs_mntops, sizeof(buf)); 144 for (opt = strtok_r(buf, ",", &state); 145 opt != NULL; 146 opt = strtok_r(NULL, ",", &state)) { 147 s = strchr(opt, '='); 148 if (s != NULL) { 149 *(s++) = '\0'; 150 } 151 if (!strcmp(opt, "userquota")) { 152 ret = 1; 153 ofe->ofe_hasuserquota = 1; 154 if (s != NULL) { 155 ofe->ofe_userquotafile = strdup(s); 156 if (ofe->ofe_userquotafile == NULL) { 157 goto fail; 158 } 159 } 160 } else if (!strcmp(opt, "groupquota")) { 161 ret = 1; 162 ofe->ofe_hasgroupquota = 1; 163 if (s != NULL) { 164 ofe->ofe_groupquotafile = strdup(s); 165 if (ofe->ofe_groupquotafile == NULL) { 166 goto fail; 167 } 168 } 169 } 170 } 171 172 if (ret == 1) { 173 ofe->ofe_mountpoint = strdup(fs->fs_file); 174 if (ofe->ofe_mountpoint == NULL) { 175 goto fail; 176 } 177 } 178 179 return ret; 180 181fail: 182 serrno = errno; 183 if (ofe->ofe_mountpoint != NULL) { 184 free(ofe->ofe_mountpoint); 185 } 186 if (ofe->ofe_groupquotafile != NULL) { 187 free(ofe->ofe_groupquotafile); 188 } 189 if (ofe->ofe_userquotafile != NULL) { 190 free(ofe->ofe_userquotafile); 191 } 192 errno = serrno; 193 return -1; 194} 195 196void 197__quota_oldfiles_load_fstab(void) 198{ 199 struct oldfiles_fstabentry ofe; 200 struct fstab *fs; 201 int result; 202 203 if (__quota_oldfiles_fstab_loaded) { 204 return; 205 } 206 207 /* 208 * Check if fstab file exists before trying to parse it. 209 * Avoid warnings from {get,set}fsent() if missing. 210 */ 211 if (access(_PATH_FSTAB, F_OK) == -1 && errno == ENOENT) 212 return; 213 214 /* 215 * XXX: should be able to handle ext2fs quota1 files too 216 * 217 * XXX: should use getfsent_r(), but there isn't one. 218 */ 219 setfsent(); 220 while ((fs = getfsent()) != NULL) { 221 if (!strcmp(fs->fs_vfstype, "ffs") || 222 !strcmp(fs->fs_vfstype, "lfs")) { 223 result = __quota_oldfiles_fill_fstabentry(fs, &ofe); 224 if (result == -1) { 225 goto failed; 226 } 227 if (result == 0) { 228 continue; 229 } 230 if (__quota_oldfiles_add_fstabentry(&ofe)) { 231 goto failed; 232 } 233 } 234 } 235 endfsent(); 236 __quota_oldfiles_fstab_loaded = 1; 237 238 return; 239failed: 240 warn("Failed reading fstab"); 241 return; 242} 243 244int 245__quota_oldfiles_infstab(const char *mountpoint) 246{ 247 return __quota_oldfiles_find_fstabentry(mountpoint) != NULL; 248} 249 250static void 251__quota_oldfiles_defquotafile(struct quotahandle *qh, int idtype, 252 char *buf, size_t maxlen) 253{ 254 static const char *const names[] = INITQFNAMES; 255 256 (void)snprintf(buf, maxlen, "%s/%s.%s", 257 qh->qh_mountpoint, 258 QUOTAFILENAME, names[USRQUOTA]); 259} 260 261const char * 262__quota_oldfiles_getquotafile(struct quotahandle *qh, int idtype, 263 char *buf, size_t maxlen) 264{ 265 const struct oldfiles_fstabentry *ofe; 266 const char *file; 267 268 ofe = __quota_oldfiles_find_fstabentry(qh->qh_mountpoint); 269 if (ofe == NULL) { 270 errno = ENXIO; 271 return NULL; 272 } 273 274 switch (idtype) { 275 case USRQUOTA: 276 if (!ofe->ofe_hasuserquota) { 277 errno = ENXIO; 278 return NULL; 279 } 280 file = ofe->ofe_userquotafile; 281 break; 282 case GRPQUOTA: 283 if (!ofe->ofe_hasgroupquota) { 284 errno = ENXIO; 285 return NULL; 286 } 287 file = ofe->ofe_groupquotafile; 288 break; 289 default: 290 errno = EINVAL; 291 return NULL; 292 } 293 294 if (file == NULL) { 295 __quota_oldfiles_defquotafile(qh, idtype, buf, maxlen); 296 file = buf; 297 } 298 return file; 299} 300 301static uint64_t 302dqblk_getlimit(uint32_t val) 303{ 304 if (val == 0) { 305 return QUOTA_NOLIMIT; 306 } else { 307 return val - 1; 308 } 309} 310 311static uint32_t 312dqblk_setlimit(uint64_t val) 313{ 314 if (val == QUOTA_NOLIMIT && val >= 0xffffffffUL) { 315 return 0; 316 } else { 317 return (uint32_t)val + 1; 318 } 319} 320 321static void 322dqblk_getblocks(const struct dqblk *dq, struct quotaval *qv) 323{ 324 qv->qv_hardlimit = dqblk_getlimit(dq->dqb_bhardlimit); 325 qv->qv_softlimit = dqblk_getlimit(dq->dqb_bsoftlimit); 326 qv->qv_usage = dq->dqb_curblocks; 327 qv->qv_expiretime = dq->dqb_btime; 328 qv->qv_grace = QUOTA_NOTIME; 329} 330 331static void 332dqblk_getfiles(const struct dqblk *dq, struct quotaval *qv) 333{ 334 qv->qv_hardlimit = dqblk_getlimit(dq->dqb_ihardlimit); 335 qv->qv_softlimit = dqblk_getlimit(dq->dqb_isoftlimit); 336 qv->qv_usage = dq->dqb_curinodes; 337 qv->qv_expiretime = dq->dqb_itime; 338 qv->qv_grace = QUOTA_NOTIME; 339} 340 341static void 342dqblk_putblocks(const struct quotaval *qv, struct dqblk *dq) 343{ 344 dq->dqb_bhardlimit = dqblk_setlimit(qv->qv_hardlimit); 345 dq->dqb_bsoftlimit = dqblk_setlimit(qv->qv_softlimit); 346 dq->dqb_curblocks = qv->qv_usage; 347 dq->dqb_btime = qv->qv_expiretime; 348 /* ignore qv->qv_grace */ 349} 350 351static void 352dqblk_putfiles(const struct quotaval *qv, struct dqblk *dq) 353{ 354 dq->dqb_ihardlimit = dqblk_setlimit(qv->qv_hardlimit); 355 dq->dqb_isoftlimit = dqblk_setlimit(qv->qv_softlimit); 356 dq->dqb_curinodes = qv->qv_usage; 357 dq->dqb_itime = qv->qv_expiretime; 358 /* ignore qv->qv_grace */ 359} 360 361static int 362__quota_oldfiles_open(struct quotahandle *qh, const char *path, int *fd_ret) 363{ 364 int fd; 365 366 fd = open(path, O_RDWR); 367 if (fd < 0 && (errno == EACCES || errno == EROFS)) { 368 fd = open(path, O_RDONLY); 369 if (fd < 0) { 370 return -1; 371 } 372 } 373 *fd_ret = fd; 374 return 0; 375} 376 377int 378__quota_oldfiles_initialize(struct quotahandle *qh) 379{ 380 const struct oldfiles_fstabentry *ofe; 381 char path[PATH_MAX]; 382 const char *userquotafile, *groupquotafile; 383 384 if (qh->qh_oldfilesopen) { 385 /* already initialized */ 386 return 0; 387 } 388 389 /* 390 * Find the fstab entry. 391 */ 392 ofe = __quota_oldfiles_find_fstabentry(qh->qh_mountpoint); 393 if (ofe == NULL) { 394 warnx("%s not found in fstab", qh->qh_mountpoint); 395 errno = ENXIO; 396 return -1; 397 } 398 399 if (!ofe->ofe_hasuserquota && !ofe->ofe_hasgroupquota) { 400 errno = ENXIO; 401 return -1; 402 } 403 404 if (ofe->ofe_hasuserquota) { 405 userquotafile = ofe->ofe_userquotafile; 406 if (userquotafile == NULL) { 407 __quota_oldfiles_defquotafile(qh, USRQUOTA, 408 path, sizeof(path)); 409 userquotafile = path; 410 } 411 if (__quota_oldfiles_open(qh, userquotafile, 412 &qh->qh_userfile)) { 413 return -1; 414 } 415 } 416 if (ofe->ofe_hasgroupquota) { 417 groupquotafile = ofe->ofe_groupquotafile; 418 if (groupquotafile == NULL) { 419 __quota_oldfiles_defquotafile(qh, GRPQUOTA, 420 path, sizeof(path)); 421 groupquotafile = path; 422 } 423 if (__quota_oldfiles_open(qh, groupquotafile, 424 &qh->qh_groupfile)) { 425 return -1; 426 } 427 } 428 429 qh->qh_oldfilesopen = 1; 430 431 return 0; 432} 433 434const char * 435__quota_oldfiles_getimplname(struct quotahandle *qh) 436{ 437 return "ufs/ffs quota v1 file access"; 438} 439 440int 441__quota_oldfiles_quotaon(struct quotahandle *qh, int idtype) 442{ 443 int result; 444 445 /* 446 * If we have the quota files open, close them. 447 */ 448 449 if (qh->qh_oldfilesopen) { 450 if (qh->qh_userfile >= 0) { 451 close(qh->qh_userfile); 452 qh->qh_userfile = -1; 453 } 454 if (qh->qh_groupfile >= 0) { 455 close(qh->qh_groupfile); 456 qh->qh_groupfile = -1; 457 } 458 qh->qh_oldfilesopen = 0; 459 } 460 461 /* 462 * Go over to the syscall interface. 463 */ 464 465 result = __quota_kernel_quotaon(qh, idtype); 466 if (result < 0) { 467 return -1; 468 } 469 470 /* 471 * We succeeded, so all further access should be via the 472 * kernel. 473 */ 474 475 qh->qh_mode = QUOTA_MODE_KERNEL; 476 return 0; 477} 478 479static int 480__quota_oldfiles_doget(struct quotahandle *qh, const struct quotakey *qk, 481 struct quotaval *qv, int *isallzero) 482{ 483 int file; 484 off_t pos; 485 struct dqblk dq; 486 ssize_t result; 487 488 if (!qh->qh_oldfilesopen) { 489 if (__quota_oldfiles_initialize(qh)) { 490 return -1; 491 } 492 } 493 494 switch (qk->qk_idtype) { 495 case QUOTA_IDTYPE_USER: 496 file = qh->qh_userfile; 497 break; 498 case QUOTA_IDTYPE_GROUP: 499 file = qh->qh_groupfile; 500 break; 501 default: 502 errno = EINVAL; 503 return -1; 504 } 505 506 if (qk->qk_id == QUOTA_DEFAULTID) { 507 pos = 0; 508 } else { 509 pos = qk->qk_id * sizeof(struct dqblk); 510 } 511 512 result = pread(file, &dq, sizeof(dq), pos); 513 if (result < 0) { 514 return -1; 515 } else if (result == 0) { 516 /* Past EOF; no quota info on file for this ID */ 517 errno = ENOENT; 518 return -1; 519 } else if ((size_t)result != sizeof(dq)) { 520 errno = EFTYPE; 521 return -1; 522 } 523 524 switch (qk->qk_objtype) { 525 case QUOTA_OBJTYPE_BLOCKS: 526 dqblk_getblocks(&dq, qv); 527 break; 528 case QUOTA_OBJTYPE_FILES: 529 dqblk_getfiles(&dq, qv); 530 break; 531 default: 532 errno = EINVAL; 533 return -1; 534 } 535 536 if (qk->qk_id == QUOTA_DEFAULTID) { 537 qv->qv_usage = 0; 538 qv->qv_grace = qv->qv_expiretime; 539 qv->qv_expiretime = QUOTA_NOTIME; 540 } else if (qk->qk_id == 0) { 541 qv->qv_hardlimit = 0; 542 qv->qv_softlimit = 0; 543 qv->qv_expiretime = QUOTA_NOTIME; 544 qv->qv_grace = QUOTA_NOTIME; 545 } 546 547 if (isallzero != NULL) { 548 if (dq.dqb_bhardlimit == 0 && 549 dq.dqb_bsoftlimit == 0 && 550 dq.dqb_curblocks == 0 && 551 dq.dqb_ihardlimit == 0 && 552 dq.dqb_isoftlimit == 0 && 553 dq.dqb_curinodes == 0 && 554 dq.dqb_btime == 0 && 555 dq.dqb_itime == 0) { 556 *isallzero = 1; 557 } else { 558 *isallzero = 0; 559 } 560 } 561 562 return 0; 563} 564 565static int 566__quota_oldfiles_doput(struct quotahandle *qh, const struct quotakey *qk, 567 const struct quotaval *qv) 568{ 569 int file; 570 off_t pos; 571 struct quotaval qv2; 572 struct dqblk dq; 573 ssize_t result; 574 575 if (!qh->qh_oldfilesopen) { 576 if (__quota_oldfiles_initialize(qh)) { 577 return -1; 578 } 579 } 580 581 switch (qk->qk_idtype) { 582 case QUOTA_IDTYPE_USER: 583 file = qh->qh_userfile; 584 break; 585 case QUOTA_IDTYPE_GROUP: 586 file = qh->qh_groupfile; 587 break; 588 default: 589 errno = EINVAL; 590 return -1; 591 } 592 593 if (qk->qk_id == QUOTA_DEFAULTID) { 594 pos = 0; 595 } else { 596 pos = qk->qk_id * sizeof(struct dqblk); 597 } 598 599 result = pread(file, &dq, sizeof(dq), pos); 600 if (result < 0) { 601 return -1; 602 } else if (result == 0) { 603 /* Past EOF; fill in a blank dq to start from */ 604 dq.dqb_bhardlimit = 0; 605 dq.dqb_bsoftlimit = 0; 606 dq.dqb_curblocks = 0; 607 dq.dqb_ihardlimit = 0; 608 dq.dqb_isoftlimit = 0; 609 dq.dqb_curinodes = 0; 610 dq.dqb_btime = 0; 611 dq.dqb_itime = 0; 612 } else if ((size_t)result != sizeof(dq)) { 613 errno = EFTYPE; 614 return -1; 615 } 616 617 switch (qk->qk_objtype) { 618 case QUOTA_OBJTYPE_BLOCKS: 619 dqblk_getblocks(&dq, &qv2); 620 break; 621 case QUOTA_OBJTYPE_FILES: 622 dqblk_getfiles(&dq, &qv2); 623 break; 624 default: 625 errno = EINVAL; 626 return -1; 627 } 628 629 if (qk->qk_id == QUOTA_DEFAULTID) { 630 qv2.qv_hardlimit = qv->qv_hardlimit; 631 qv2.qv_softlimit = qv->qv_softlimit; 632 /* leave qv2.qv_usage unchanged */ 633 qv2.qv_expiretime = qv->qv_grace; 634 /* skip qv2.qv_grace */ 635 636 /* ignore qv->qv_usage */ 637 /* ignore qv->qv_expiretime */ 638 } else if (qk->qk_id == 0) { 639 /* leave qv2.qv_hardlimit unchanged */ 640 /* leave qv2.qv_softlimit unchanged */ 641 qv2.qv_usage = qv->qv_usage; 642 /* leave qv2.qv_expiretime unchanged */ 643 /* skip qv2.qv_grace */ 644 645 /* ignore qv->qv_hardlimit */ 646 /* ignore qv->qv_softlimit */ 647 /* ignore qv->qv_expiretime */ 648 /* ignore qv->qv_grace */ 649 } else { 650 qv2 = *qv; 651 } 652 653 switch (qk->qk_objtype) { 654 case QUOTA_OBJTYPE_BLOCKS: 655 dqblk_putblocks(&qv2, &dq); 656 break; 657 case QUOTA_OBJTYPE_FILES: 658 dqblk_putfiles(&qv2, &dq); 659 break; 660 default: 661 errno = EINVAL; 662 return -1; 663 } 664 665 result = pwrite(file, &dq, sizeof(dq), pos); 666 if (result < 0) { 667 return -1; 668 } else if ((size_t)result != sizeof(dq)) { 669 /* ? */ 670 errno = EFTYPE; 671 return -1; 672 } 673 674 return 0; 675} 676 677int 678__quota_oldfiles_get(struct quotahandle *qh, const struct quotakey *qk, 679 struct quotaval *qv) 680{ 681 return __quota_oldfiles_doget(qh, qk, qv, NULL); 682} 683 684int 685__quota_oldfiles_put(struct quotahandle *qh, const struct quotakey *qk, 686 const struct quotaval *qv) 687{ 688 return __quota_oldfiles_doput(qh, qk, qv); 689} 690 691int 692__quota_oldfiles_delete(struct quotahandle *qh, const struct quotakey *qk) 693{ 694 struct quotaval qv; 695 696 quotaval_clear(&qv); 697 return __quota_oldfiles_doput(qh, qk, &qv); 698} 699 700struct oldfiles_quotacursor * 701__quota_oldfiles_cursor_create(struct quotahandle *qh) 702{ 703 struct oldfiles_quotacursor *oqc; 704 struct stat st; 705 int serrno; 706 707 /* quota_opencursor calls initialize for us, no need to do it here */ 708 709 oqc = malloc(sizeof(*oqc)); 710 if (oqc == NULL) { 711 return NULL; 712 } 713 714 oqc->oqc_didusers = 0; 715 oqc->oqc_didgroups = 0; 716 oqc->oqc_diddefault = 0; 717 oqc->oqc_pos = 0; 718 oqc->oqc_didblocks = 0; 719 720 if (qh->qh_userfile >= 0) { 721 oqc->oqc_doingusers = 1; 722 } else { 723 oqc->oqc_doingusers = 0; 724 oqc->oqc_didusers = 1; 725 } 726 727 if (qh->qh_groupfile >= 0) { 728 oqc->oqc_doinggroups = 1; 729 } else { 730 oqc->oqc_doinggroups = 0; 731 oqc->oqc_didgroups = 1; 732 } 733 734 if (fstat(qh->qh_userfile, &st) < 0) { 735 serrno = errno; 736 free(oqc); 737 errno = serrno; 738 return NULL; 739 } 740 oqc->oqc_numusers = st.st_size / sizeof(struct dqblk); 741 742 if (fstat(qh->qh_groupfile, &st) < 0) { 743 serrno = errno; 744 free(oqc); 745 errno = serrno; 746 return NULL; 747 } 748 oqc->oqc_numgroups = st.st_size / sizeof(struct dqblk); 749 750 return oqc; 751} 752 753void 754__quota_oldfiles_cursor_destroy(struct oldfiles_quotacursor *oqc) 755{ 756 free(oqc); 757} 758 759int 760__quota_oldfiles_cursor_skipidtype(struct oldfiles_quotacursor *oqc, 761 int idtype) 762{ 763 switch (idtype) { 764 case QUOTA_IDTYPE_USER: 765 oqc->oqc_doingusers = 0; 766 oqc->oqc_didusers = 1; 767 break; 768 case QUOTA_IDTYPE_GROUP: 769 oqc->oqc_doinggroups = 0; 770 oqc->oqc_didgroups = 1; 771 break; 772 default: 773 errno = EINVAL; 774 return -1; 775 } 776 return 0; 777} 778 779int 780__quota_oldfiles_cursor_get(struct quotahandle *qh, 781 struct oldfiles_quotacursor *oqc, 782 struct quotakey *key, struct quotaval *val) 783{ 784 unsigned maxpos; 785 int isallzero; 786 787 /* in case one of the sizes is zero */ 788 if (!oqc->oqc_didusers && oqc->oqc_pos >= oqc->oqc_numusers) { 789 oqc->oqc_didusers = 1; 790 } 791 if (!oqc->oqc_didgroups && oqc->oqc_pos >= oqc->oqc_numgroups) { 792 oqc->oqc_didgroups = 1; 793 } 794 795 again: 796 /* 797 * Figure out what to get 798 */ 799 800 if (!oqc->oqc_didusers) { 801 key->qk_idtype = QUOTA_IDTYPE_USER; 802 maxpos = oqc->oqc_numusers; 803 } else if (!oqc->oqc_didgroups) { 804 key->qk_idtype = QUOTA_IDTYPE_GROUP; 805 maxpos = oqc->oqc_numgroups; 806 } else { 807 errno = ENOENT; 808 return -1; 809 } 810 811 if (!oqc->oqc_diddefault) { 812 key->qk_id = QUOTA_DEFAULTID; 813 } else { 814 key->qk_id = oqc->oqc_pos; 815 } 816 817 if (!oqc->oqc_didblocks) { 818 key->qk_objtype = QUOTA_OBJTYPE_BLOCKS; 819 } else { 820 key->qk_objtype = QUOTA_OBJTYPE_FILES; 821 } 822 823 /* 824 * Get it 825 */ 826 827 if (__quota_oldfiles_doget(qh, key, val, &isallzero)) { 828 return -1; 829 } 830 831 /* 832 * Advance the cursor 833 */ 834 if (!oqc->oqc_didblocks) { 835 oqc->oqc_didblocks = 1; 836 } else { 837 oqc->oqc_didblocks = 0; 838 if (!oqc->oqc_diddefault) { 839 oqc->oqc_diddefault = 1; 840 } else { 841 oqc->oqc_pos++; 842 if (oqc->oqc_pos >= maxpos) { 843 oqc->oqc_pos = 0; 844 oqc->oqc_diddefault = 0; 845 if (!oqc->oqc_didusers) { 846 oqc->oqc_didusers = 1; 847 } else { 848 oqc->oqc_didgroups = 1; 849 } 850 } 851 } 852 } 853 854 /* 855 * If we got an all-zero dqblk (e.g. from the middle of a hole 856 * in the quota file) don't bother returning it to the caller. 857 * 858 * ...unless we're at the end of the data, to avoid going past 859 * the end and generating a spurious failure. There's no 860 * reasonable way to make _atend detect empty entries at the 861 * end of the quota files. 862 */ 863 if (isallzero && (!oqc->oqc_didusers || !oqc->oqc_didgroups)) { 864 goto again; 865 } 866 return 0; 867} 868 869int 870__quota_oldfiles_cursor_getn(struct quotahandle *qh, 871 struct oldfiles_quotacursor *oqc, 872 struct quotakey *keys, struct quotaval *vals, 873 unsigned maxnum) 874{ 875 unsigned i; 876 877 if (maxnum > INT_MAX) { 878 /* joker, eh? */ 879 errno = EINVAL; 880 return -1; 881 } 882 883 for (i=0; i<maxnum; i++) { 884 if (__quota_oldfiles_cursor_atend(oqc)) { 885 break; 886 } 887 if (__quota_oldfiles_cursor_get(qh, oqc, &keys[i], &vals[i])) { 888 if (i > 0) { 889 /* 890 * Succeed witih what we have so far; 891 * the next attempt will hit the same 892 * error again. 893 */ 894 break; 895 } 896 return -1; 897 } 898 } 899 return i; 900 901} 902 903int 904__quota_oldfiles_cursor_atend(struct oldfiles_quotacursor *oqc) 905{ 906 /* in case one of the sizes is zero */ 907 if (!oqc->oqc_didusers && oqc->oqc_pos >= oqc->oqc_numusers) { 908 oqc->oqc_didusers = 1; 909 } 910 if (!oqc->oqc_didgroups && oqc->oqc_pos >= oqc->oqc_numgroups) { 911 oqc->oqc_didgroups = 1; 912 } 913 914 return oqc->oqc_didusers && oqc->oqc_didgroups; 915} 916 917int 918__quota_oldfiles_cursor_rewind(struct oldfiles_quotacursor *oqc) 919{ 920 oqc->oqc_didusers = 0; 921 oqc->oqc_didgroups = 0; 922 oqc->oqc_diddefault = 0; 923 oqc->oqc_pos = 0; 924 oqc->oqc_didblocks = 0; 925 return 0; 926} 927