Home | History | Annotate | Line # | Download | only in libquota
quota_oldfiles.c revision 1.2
      1 /*	$NetBSD: quota_oldfiles.c,v 1.2 2012/01/09 15:45:19 dholland Exp $	*/
      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/types.h>
     36 #include <sys/stat.h>
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <unistd.h>
     41 #include <fcntl.h>
     42 #include <limits.h>
     43 #include <fstab.h>
     44 #include <errno.h>
     45 #include <err.h>
     46 
     47 #include <ufs/ufs/quota1.h>
     48 
     49 #include <quota.h>
     50 #include "quotapvt.h"
     51 
     52 struct oldfiles_quotacursor {
     53 	unsigned oqc_doingusers;
     54 	unsigned oqc_doinggroups;
     55 
     56 	unsigned oqc_numusers;
     57 	unsigned oqc_numgroups;
     58 
     59 	unsigned oqc_didusers;
     60 	unsigned oqc_didgroups;
     61 	unsigned oqc_diddefault;
     62 	unsigned oqc_pos;
     63 	unsigned oqc_didblocks;
     64 };
     65 
     66 static uint64_t
     67 dqblk_getlimit(uint32_t val)
     68 {
     69 	if (val == 0) {
     70 		return QUOTA_NOLIMIT;
     71 	} else {
     72 		return val - 1;
     73 	}
     74 }
     75 
     76 static uint32_t
     77 dqblk_setlimit(uint64_t val)
     78 {
     79 	if (val == QUOTA_NOLIMIT && val >= 0xffffffffUL) {
     80 		return 0;
     81 	} else {
     82 		return (uint32_t)val + 1;
     83 	}
     84 }
     85 
     86 static void
     87 dqblk_getblocks(const struct dqblk *dq, struct quotaval *qv)
     88 {
     89 	qv->qv_hardlimit = dqblk_getlimit(dq->dqb_bhardlimit);
     90 	qv->qv_softlimit = dqblk_getlimit(dq->dqb_bsoftlimit);
     91 	qv->qv_usage = dq->dqb_curblocks;
     92 	qv->qv_expiretime = dq->dqb_btime;
     93 	qv->qv_grace = QUOTA_NOTIME;
     94 }
     95 
     96 static void
     97 dqblk_getfiles(const struct dqblk *dq, struct quotaval *qv)
     98 {
     99 	qv->qv_hardlimit = dqblk_getlimit(dq->dqb_ihardlimit);
    100 	qv->qv_softlimit = dqblk_getlimit(dq->dqb_isoftlimit);
    101 	qv->qv_usage = dq->dqb_curinodes;
    102 	qv->qv_expiretime = dq->dqb_itime;
    103 	qv->qv_grace = QUOTA_NOTIME;
    104 }
    105 
    106 static void
    107 dqblk_putblocks(const struct quotaval *qv, struct dqblk *dq)
    108 {
    109 	dq->dqb_bhardlimit = dqblk_setlimit(qv->qv_hardlimit);
    110 	dq->dqb_bsoftlimit = dqblk_setlimit(qv->qv_softlimit);
    111 	dq->dqb_curblocks = qv->qv_usage;
    112 	dq->dqb_btime = qv->qv_expiretime;
    113 	/* ignore qv->qv_grace */
    114 }
    115 
    116 static void
    117 dqblk_putfiles(const struct quotaval *qv, struct dqblk *dq)
    118 {
    119 	dq->dqb_ihardlimit = dqblk_setlimit(qv->qv_hardlimit);
    120 	dq->dqb_isoftlimit = dqblk_setlimit(qv->qv_softlimit);
    121 	dq->dqb_curinodes = qv->qv_usage;
    122 	dq->dqb_itime = qv->qv_expiretime;
    123 	/* ignore qv->qv_grace */
    124 }
    125 
    126 static int
    127 __quota_oldfiles_open(struct quotahandle *qh, const char *path, int *fd_ret)
    128 {
    129 	int fd;
    130 
    131 	fd = open(path, O_RDWR);
    132 	if (fd < 0 && (errno == EACCES || errno == EROFS)) {
    133 		fd = open(path, O_RDONLY);
    134 		if (fd < 0) {
    135 			return -1;
    136 		}
    137 	}
    138 	*fd_ret = fd;
    139 	return 0;
    140 }
    141 
    142 int
    143 __quota_oldfiles_initialize(struct quotahandle *qh)
    144 {
    145 	static const char *const names[] = INITQFNAMES;
    146 
    147 	struct fstab *fs;
    148 	char buf[sizeof(fs->fs_mntops)];
    149 	char *opt, *state, *s;
    150 	char path[PATH_MAX];
    151 	const char *userquotafile, *groupquotafile;
    152 	int hasuserquota, hasgroupquota;
    153 
    154 	if (qh->qh_hasoldfiles) {
    155 		/* already initialized */
    156 		return 0;
    157 	}
    158 
    159 	/*
    160 	 * Find the fstab entry.
    161 	 *
    162 	 * XXX: should be able to handle not just ffs quota1 files but
    163 	 * also lfs and even ext2fs.
    164 	 */
    165 	setfsent();
    166 	while ((fs = getfsent()) != NULL) {
    167 		if (!strcmp(fs->fs_vfstype, "ffs") &&
    168 		    !strcmp(fs->fs_file, qh->qh_mountpoint)) {
    169 			break;
    170 		}
    171 	}
    172 	endfsent();
    173 
    174 	if (fs == NULL) {
    175 		warnx("%s not found in fstab", qh->qh_mountpoint);
    176 		errno = ENXIO;
    177 		return -1;
    178 	}
    179 
    180 	/*
    181 	 * Inspect the mount options to find the quota files.
    182 	 * XXX this info should be gotten from the kernel.
    183 	 *
    184 	 * The options are:
    185 	 *    userquota[=path]          enable user quotas
    186 	 *    groupquota[=path]         enable group quotas
    187 	 */
    188 	hasuserquota = hasgroupquota = 0;
    189 	userquotafile = groupquotafile = NULL;
    190 	strlcpy(buf, fs->fs_mntops, sizeof(buf));
    191 	for (opt = strtok_r(buf, ",", &state);
    192 	     opt != NULL;
    193 	     opt = strtok_r(NULL, ",", &state)) {
    194 		s = strchr(opt, '=');
    195 		if (s != NULL) {
    196 			*(s++) = '\0';
    197 		}
    198 		if (!strcmp(opt, "userquota")) {
    199 			hasuserquota = 1;
    200 			if (s != NULL) {
    201 				userquotafile = s;
    202 			}
    203 		} else if (!strcmp(opt, "groupquota")) {
    204 			hasgroupquota = 1;
    205 			if (s != NULL) {
    206 				groupquotafile = s;
    207 			}
    208 		}
    209 	}
    210 
    211 	if (!hasuserquota && !hasgroupquota) {
    212 		errno = ENXIO;
    213 		return -1;
    214 	}
    215 
    216 	if (hasuserquota) {
    217 		if (userquotafile == NULL) {
    218 			(void)snprintf(path, sizeof(path), "%s/%s.%s",
    219 				       fs->fs_file,
    220 				       QUOTAFILENAME, names[USRQUOTA]);
    221 			userquotafile = path;
    222 		}
    223 		if (__quota_oldfiles_open(qh, userquotafile,
    224 					  &qh->qh_userfile)) {
    225 			return -1;
    226 		}
    227 	}
    228 	if (hasgroupquota) {
    229 		if (groupquotafile == NULL) {
    230 			(void)snprintf(path, sizeof(path), "%s/%s.%s",
    231 				       fs->fs_file,
    232 				       QUOTAFILENAME, names[GRPQUOTA]);
    233 			groupquotafile = path;
    234 		}
    235 		if (__quota_oldfiles_open(qh, groupquotafile,
    236 					  &qh->qh_groupfile)) {
    237 			return -1;
    238 		}
    239 	}
    240 
    241 	qh->qh_hasoldfiles = 1;
    242 
    243 	return 0;
    244 }
    245 
    246 const char *
    247 __quota_oldfiles_getimplname(struct quotahandle *qh)
    248 {
    249 	return "ffs quota1 direct file access";
    250 }
    251 
    252 static int
    253 __quota_oldfiles_doget(struct quotahandle *qh, const struct quotakey *qk,
    254 		       struct quotaval *qv, int *isallzero)
    255 {
    256 	int file;
    257 	off_t pos;
    258 	struct dqblk dq;
    259 	ssize_t result;
    260 
    261 	switch (qk->qk_idtype) {
    262 	    case QUOTA_IDTYPE_USER:
    263 		file = qh->qh_userfile;
    264 		break;
    265 	    case QUOTA_IDTYPE_GROUP:
    266 		file = qh->qh_groupfile;
    267 		break;
    268 	    default:
    269 		errno = EINVAL;
    270 		return -1;
    271 	}
    272 
    273 	if (qk->qk_id == QUOTA_DEFAULTID) {
    274 		pos = 0;
    275 	} else {
    276 		pos = qk->qk_id * sizeof(struct dqblk);
    277 	}
    278 
    279 	result = pread(file, &dq, sizeof(dq), pos);
    280 	if (result < 0) {
    281 		return -1;
    282 	} else if (result == 0) {
    283 		/* Past EOF; no quota info on file for this ID */
    284 		errno = ENOENT;
    285 		return -1;
    286 	} else if ((size_t)result != sizeof(dq)) {
    287 		errno = EFTYPE;
    288 		return -1;
    289 	}
    290 
    291 	switch (qk->qk_objtype) {
    292 	    case QUOTA_OBJTYPE_BLOCKS:
    293 		dqblk_getblocks(&dq, qv);
    294 		break;
    295 	    case QUOTA_OBJTYPE_FILES:
    296 		dqblk_getfiles(&dq, qv);
    297 		break;
    298 	    default:
    299 		errno = EINVAL;
    300 		return -1;
    301 	}
    302 
    303 	if (qk->qk_id == QUOTA_DEFAULTID) {
    304 		qv->qv_usage = 0;
    305 		qv->qv_grace = qv->qv_expiretime;
    306 		qv->qv_expiretime = QUOTA_NOTIME;
    307 	} else if (qk->qk_id == 0) {
    308 		qv->qv_hardlimit = 0;
    309 		qv->qv_softlimit = 0;
    310 		qv->qv_expiretime = QUOTA_NOTIME;
    311 		qv->qv_grace = QUOTA_NOTIME;
    312 	}
    313 
    314 	if (isallzero != NULL) {
    315 		if (dq.dqb_bhardlimit == 0 &&
    316 		    dq.dqb_bsoftlimit == 0 &&
    317 		    dq.dqb_curblocks == 0 &&
    318 		    dq.dqb_ihardlimit == 0 &&
    319 		    dq.dqb_isoftlimit == 0 &&
    320 		    dq.dqb_curinodes == 0 &&
    321 		    dq.dqb_btime == 0 &&
    322 		    dq.dqb_itime == 0) {
    323 			*isallzero = 1;
    324 		} else {
    325 			*isallzero = 0;
    326 		}
    327 	}
    328 
    329 	return 0;
    330 }
    331 
    332 static int
    333 __quota_oldfiles_doput(struct quotahandle *qh, const struct quotakey *qk,
    334 		       const struct quotaval *qv)
    335 {
    336 	int file;
    337 	off_t pos;
    338 	struct quotaval qv2;
    339 	struct dqblk dq;
    340 	ssize_t result;
    341 
    342 	switch (qk->qk_idtype) {
    343 	    case QUOTA_IDTYPE_USER:
    344 		file = qh->qh_userfile;
    345 		break;
    346 	    case QUOTA_IDTYPE_GROUP:
    347 		file = qh->qh_groupfile;
    348 		break;
    349 	    default:
    350 		errno = EINVAL;
    351 		return -1;
    352 	}
    353 
    354 	if (qk->qk_id == QUOTA_DEFAULTID) {
    355 		pos = 0;
    356 	} else {
    357 		pos = qk->qk_id * sizeof(struct dqblk);
    358 	}
    359 
    360 	result = pread(file, &dq, sizeof(dq), pos);
    361 	if (result < 0) {
    362 		return -1;
    363 	} else if (result == 0) {
    364 		/* Past EOF; fill in a blank dq to start from */
    365 		dq.dqb_bhardlimit = 0;
    366 		dq.dqb_bsoftlimit = 0;
    367 		dq.dqb_curblocks = 0;
    368 		dq.dqb_ihardlimit = 0;
    369 		dq.dqb_isoftlimit = 0;
    370 		dq.dqb_curinodes = 0;
    371 		dq.dqb_btime = 0;
    372 		dq.dqb_itime = 0;
    373 	} else if ((size_t)result != sizeof(dq)) {
    374 		errno = EFTYPE;
    375 		return -1;
    376 	}
    377 
    378 	switch (qk->qk_objtype) {
    379 	    case QUOTA_OBJTYPE_BLOCKS:
    380 		dqblk_getblocks(&dq, &qv2);
    381 		break;
    382 	    case QUOTA_OBJTYPE_FILES:
    383 		dqblk_getfiles(&dq, &qv2);
    384 		break;
    385 	    default:
    386 		errno = EINVAL;
    387 		return -1;
    388 	}
    389 
    390 	if (qk->qk_id == QUOTA_DEFAULTID) {
    391 		qv2.qv_hardlimit = qv->qv_hardlimit;
    392 		qv2.qv_softlimit = qv->qv_softlimit;
    393 		/* leave qv2.qv_usage unchanged */
    394 		qv2.qv_expiretime = qv->qv_grace;
    395 		/* skip qv2.qv_grace */
    396 
    397 		/* ignore qv->qv_usage */
    398 		/* ignore qv->qv_expiretime */
    399 	} else if (qk->qk_id == 0) {
    400 		/* leave qv2.qv_hardlimit unchanged */
    401 		/* leave qv2.qv_softlimit unchanged */
    402 		qv2.qv_usage = qv->qv_usage;
    403 		/* leave qv2.qv_expiretime unchanged */
    404 		/* skip qv2.qv_grace */
    405 
    406 		/* ignore qv->qv_hardlimit */
    407 		/* ignore qv->qv_softlimit */
    408 		/* ignore qv->qv_expiretime */
    409 		/* ignore qv->qv_grace */
    410 	} else {
    411 		qv2 = *qv;
    412 	}
    413 
    414 	switch (qk->qk_objtype) {
    415 	    case QUOTA_OBJTYPE_BLOCKS:
    416 		dqblk_putblocks(&qv2, &dq);
    417 		break;
    418 	    case QUOTA_OBJTYPE_FILES:
    419 		dqblk_putfiles(&qv2, &dq);
    420 		break;
    421 	    default:
    422 		errno = EINVAL;
    423 		return -1;
    424 	}
    425 
    426 	result = pwrite(file, &dq, sizeof(dq), pos);
    427 	if (result < 0) {
    428 		return -1;
    429 	} else if ((size_t)result != sizeof(dq)) {
    430 		/* ? */
    431 		errno = EFTYPE;
    432 		return -1;
    433 	}
    434 
    435 	return 0;
    436 }
    437 
    438 int
    439 __quota_oldfiles_get(struct quotahandle *qh, const struct quotakey *qk,
    440 		     struct quotaval *qv)
    441 {
    442 	return __quota_oldfiles_doget(qh, qk, qv, NULL);
    443 }
    444 
    445 int
    446 __quota_oldfiles_put(struct quotahandle *qh, const struct quotakey *qk,
    447 		     const struct quotaval *qv)
    448 {
    449 	return __quota_oldfiles_doput(qh, qk, qv);
    450 }
    451 
    452 int
    453 __quota_oldfiles_delete(struct quotahandle *qh, const struct quotakey *qk)
    454 {
    455 	struct quotaval qv;
    456 
    457 	quotaval_clear(&qv);
    458 	return __quota_oldfiles_doput(qh, qk, &qv);
    459 }
    460 
    461 struct oldfiles_quotacursor *
    462 __quota_oldfiles_cursor_create(struct quotahandle *qh)
    463 {
    464 	struct oldfiles_quotacursor *oqc;
    465 	struct stat st;
    466 	int serrno;
    467 
    468 	oqc = malloc(sizeof(*oqc));
    469 	if (oqc == NULL) {
    470 		return NULL;
    471 	}
    472 
    473 	oqc->oqc_didusers = 0;
    474 	oqc->oqc_didgroups = 0;
    475 	oqc->oqc_diddefault = 0;
    476 	oqc->oqc_pos = 0;
    477 	oqc->oqc_didblocks = 0;
    478 
    479 	if (qh->qh_userfile >= 0) {
    480 		oqc->oqc_doingusers = 1;
    481 	} else {
    482 		oqc->oqc_doingusers = 0;
    483 		oqc->oqc_didusers = 1;
    484 	}
    485 
    486 	if (qh->qh_groupfile >= 0) {
    487 		oqc->oqc_doinggroups = 1;
    488 	} else {
    489 		oqc->oqc_doinggroups = 0;
    490 		oqc->oqc_didgroups = 1;
    491 	}
    492 
    493 	if (fstat(qh->qh_userfile, &st) < 0) {
    494 		serrno = errno;
    495 		free(oqc);
    496 		errno = serrno;
    497 		return NULL;
    498 	}
    499 	oqc->oqc_numusers = st.st_size / sizeof(struct dqblk);
    500 
    501 	if (fstat(qh->qh_groupfile, &st) < 0) {
    502 		serrno = errno;
    503 		free(oqc);
    504 		errno = serrno;
    505 		return NULL;
    506 	}
    507 	oqc->oqc_numgroups = st.st_size / sizeof(struct dqblk);
    508 
    509 	return oqc;
    510 }
    511 
    512 void
    513 __quota_oldfiles_cursor_destroy(struct oldfiles_quotacursor *oqc)
    514 {
    515 	free(oqc);
    516 }
    517 
    518 int
    519 __quota_oldfiles_cursor_skipidtype(struct oldfiles_quotacursor *oqc,
    520 				   unsigned idtype)
    521 {
    522 	switch (idtype) {
    523 	    case QUOTA_IDTYPE_USER:
    524 		oqc->oqc_doingusers = 0;
    525 		oqc->oqc_didusers = 1;
    526 		break;
    527 	    case QUOTA_IDTYPE_GROUP:
    528 		oqc->oqc_doinggroups = 0;
    529 		oqc->oqc_didgroups = 1;
    530 		break;
    531 	    default:
    532 		errno = EINVAL;
    533 		return -1;
    534 	}
    535 	return 0;
    536 }
    537 
    538 int
    539 __quota_oldfiles_cursor_get(struct quotahandle *qh,
    540 			    struct oldfiles_quotacursor *oqc,
    541 			    struct quotakey *key, struct quotaval *val)
    542 {
    543 	unsigned maxpos;
    544 	int isallzero;
    545 
    546 	/* in case one of the sizes is zero */
    547 	if (!oqc->oqc_didusers && oqc->oqc_pos >= oqc->oqc_numusers) {
    548 		oqc->oqc_didusers = 1;
    549 	}
    550 	if (!oqc->oqc_didgroups && oqc->oqc_pos >= oqc->oqc_numgroups) {
    551 		oqc->oqc_didgroups = 1;
    552 	}
    553 
    554  again:
    555 	/*
    556 	 * Figure out what to get
    557 	 */
    558 
    559 	if (!oqc->oqc_didusers) {
    560 		key->qk_idtype = QUOTA_IDTYPE_USER;
    561 		maxpos = oqc->oqc_numusers;
    562 	} else if (!oqc->oqc_didgroups) {
    563 		key->qk_idtype = QUOTA_IDTYPE_GROUP;
    564 		maxpos = oqc->oqc_numgroups;
    565 	} else {
    566 		errno = ENOENT;
    567 		return -1;
    568 	}
    569 
    570 	if (!oqc->oqc_diddefault) {
    571 		key->qk_id = QUOTA_DEFAULTID;
    572 	} else {
    573 		key->qk_id = oqc->oqc_pos;
    574 	}
    575 
    576 	if (!oqc->oqc_didblocks) {
    577 		key->qk_objtype = QUOTA_OBJTYPE_BLOCKS;
    578 	} else {
    579 		key->qk_objtype = QUOTA_OBJTYPE_FILES;
    580 	}
    581 
    582 	/*
    583 	 * Get it
    584 	 */
    585 
    586 	if (__quota_oldfiles_doget(qh, key, val, &isallzero)) {
    587 		return -1;
    588 	}
    589 
    590 	/*
    591 	 * Advance the cursor
    592 	 */
    593 	if (!oqc->oqc_didblocks) {
    594 		oqc->oqc_didblocks = 1;
    595 	} else {
    596 		oqc->oqc_didblocks = 0;
    597 		if (!oqc->oqc_diddefault) {
    598 			oqc->oqc_diddefault = 1;
    599 		} else {
    600 			oqc->oqc_pos++;
    601 			if (oqc->oqc_pos >= maxpos) {
    602 				oqc->oqc_pos = 0;
    603 				oqc->oqc_diddefault = 0;
    604 				if (!oqc->oqc_didusers) {
    605 					oqc->oqc_didusers = 1;
    606 				} else {
    607 					oqc->oqc_didgroups = 1;
    608 				}
    609 			}
    610 		}
    611 	}
    612 
    613 	/*
    614 	 * If we got an all-zero dqblk (e.g. from the middle of a hole
    615 	 * in the quota file) don't bother returning it to the caller.
    616 	 *
    617 	 * ...unless we're at the end of the data, to avoid going past
    618 	 * the end and generating a spurious failure. There's no
    619 	 * reasonable way to make _atend detect empty entries at the
    620 	 * end of the quota files.
    621 	 */
    622 	if (isallzero && (!oqc->oqc_didusers || !oqc->oqc_didgroups)) {
    623 		goto again;
    624 	}
    625 	return 0;
    626 }
    627 
    628 int
    629 __quota_oldfiles_cursor_getn(struct quotahandle *qh,
    630 			     struct oldfiles_quotacursor *oqc,
    631 			     struct quotakey *keys, struct quotaval *vals,
    632 			     unsigned maxnum)
    633 {
    634 	unsigned i;
    635 
    636 	if (maxnum > INT_MAX) {
    637 		/* joker, eh? */
    638 		errno = EINVAL;
    639 		return -1;
    640 	}
    641 
    642 	for (i=0; i<maxnum; i++) {
    643 		if (__quota_oldfiles_cursor_atend(oqc)) {
    644 			break;
    645 		}
    646 		if (__quota_oldfiles_cursor_get(qh, oqc, &keys[i], &vals[i])) {
    647 			if (i > 0) {
    648 				/*
    649 				 * Succeed witih what we have so far;
    650 				 * the next attempt will hit the same
    651 				 * error again.
    652 				 */
    653 				break;
    654 			}
    655 			return -1;
    656 		}
    657 	}
    658 	return i;
    659 
    660 }
    661 
    662 int
    663 __quota_oldfiles_cursor_atend(struct oldfiles_quotacursor *oqc)
    664 {
    665 	/* in case one of the sizes is zero */
    666 	if (!oqc->oqc_didusers && oqc->oqc_pos >= oqc->oqc_numusers) {
    667 		oqc->oqc_didusers = 1;
    668 	}
    669 	if (!oqc->oqc_didgroups && oqc->oqc_pos >= oqc->oqc_numgroups) {
    670 		oqc->oqc_didgroups = 1;
    671 	}
    672 
    673 	return oqc->oqc_didusers && oqc->oqc_didgroups;
    674 }
    675 
    676 int
    677 __quota_oldfiles_cursor_rewind(struct oldfiles_quotacursor *oqc)
    678 {
    679 	oqc->oqc_didusers = 0;
    680 	oqc->oqc_didgroups = 0;
    681 	oqc->oqc_diddefault = 0;
    682 	oqc->oqc_pos = 0;
    683 	oqc->oqc_didblocks = 0;
    684 	return 0;
    685 }
    686