Home | History | Annotate | Line # | Download | only in quotacheck
quotacheck.c revision 1.41
      1 /*	$NetBSD: quotacheck.c,v 1.41 2011/03/06 17:08:43 bouyer 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/cdefs.h>
     36 #ifndef lint
     37 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
     38  The Regents of the University of California.  All rights reserved.");
     39 #endif /* not lint */
     40 
     41 #ifndef lint
     42 #if 0
     43 static char sccsid[] = "@(#)quotacheck.c	8.6 (Berkeley) 4/28/95";
     44 #else
     45 __RCSID("$NetBSD: quotacheck.c,v 1.41 2011/03/06 17:08:43 bouyer Exp $");
     46 #endif
     47 #endif /* not lint */
     48 
     49 /*
     50  * Fix up / report on disk quotas & usage
     51  */
     52 #include <sys/param.h>
     53 #include <sys/stat.h>
     54 #include <sys/queue.h>
     55 #include <sys/statvfs.h>
     56 
     57 #include <ufs/ufs/dinode.h>
     58 #include <ufs/ufs/quota1.h>
     59 #include <ufs/ufs/ufs_bswap.h>
     60 #include <ufs/ffs/fs.h>
     61 #include <ufs/ffs/ffs_extern.h>
     62 
     63 #include <err.h>
     64 #include <fcntl.h>
     65 #include <fstab.h>
     66 #include <pwd.h>
     67 #include <grp.h>
     68 #include <errno.h>
     69 #include <unistd.h>
     70 #include <stdio.h>
     71 #include <stdlib.h>
     72 #include <string.h>
     73 
     74 #include "fsutil.h"
     75 
     76 #ifndef FS_UFS1_MAGIC
     77 # define FS_UFS1_MAGIC		FS_MAGIC /* 0x011954 */
     78 # define FS_UFS1_MAGIC_SWAPPED	0x54190100 /* bswap32(0x011954) */
     79 # define DINODE1_SIZE		sizeof(struct dinode)
     80 # define DINODE2_SIZE		0
     81 #else
     82 # define HAVE_UFSv2	1
     83 #endif
     84 
     85 #ifndef SBLOCKSIZE
     86 # define SBLOCKSIZE	SBSIZE
     87 #endif
     88 #ifndef SBLOCKSEARCH
     89 # define SBLOCKSEARCH	{ SBSIZE, -1 }
     90 #endif
     91 
     92 static const char *qfname = QUOTAFILENAME;
     93 static const char *qfextension[] = INITQFNAMES;
     94 static const char *quotagroup = QUOTAGROUP;
     95 
     96 static union {
     97 	struct	fs	sblk;
     98 	char	dummy[MAXBSIZE];
     99 } un;
    100 #define	sblock	un.sblk
    101 static long dev_bsize;
    102 static long maxino;
    103 
    104 struct quotaname {
    105 	long	flags;
    106 	char	grpqfname[MAXPATHLEN + 1];
    107 	char	usrqfname[MAXPATHLEN + 1];
    108 };
    109 #define	HASUSR	1
    110 #define	HASGRP	2
    111 
    112 struct fileusage {
    113 	struct	fileusage *fu_next;
    114 	u_long	fu_curinodes;
    115 	u_long	fu_curblocks;
    116 	u_int32_t fu_id;		/* uid_t, gid_t */
    117 	char	fu_name[1];
    118 	/* actually bigger */
    119 };
    120 #define FUHASH 1024	/* must be power of two */
    121 static struct fileusage *fuhead[MAXQUOTAS][FUHASH];
    122 
    123 
    124 union comb_dinode {
    125 #ifdef HAVE_UFSv2
    126 	struct ufs1_dinode dp1;
    127 	struct ufs2_dinode dp2;
    128 #else
    129 	struct dinode dp1;
    130 #endif
    131 };
    132 #ifdef HAVE_UFSv2
    133 #define DIP(dp, field) \
    134 	(is_ufs2 ? (dp)->dp2.di_##field : (dp)->dp1.di_##field)
    135 #else
    136 #define DIP(dp, field) (dp)->dp1.di_##field
    137 #endif
    138 
    139 
    140 static int	aflag;		/* all file systems */
    141 static int	gflag;		/* check group quotas */
    142 static int	uflag;		/* check user quotas */
    143 static int	vflag;		/* verbose */
    144 static int	qflag;		/* quick but untidy mode */
    145 static int	fi;		/* open disk file descriptor */
    146 static u_int32_t highid[MAXQUOTAS];/* highest addid()'ed identifier per type */
    147 static int needswap;	/* FS is in swapped order */
    148 static int got_siginfo = 0; /* got a siginfo signal */
    149 static int is_ufs2;
    150 
    151 
    152 int main __P((int, char *[]));
    153 static void usage __P((void));
    154 static void *needchk __P((struct fstab *));
    155 static int chkquota __P((const char *, const char *, const char *, void *,
    156     pid_t *));
    157 static int update __P((const char *, const char *, int));
    158 static u_int32_t skipforward __P((u_int32_t, u_int32_t, FILE *));
    159 static int oneof __P((const char *, char *[], int));
    160 static int getquotagid __P((void));
    161 static int hasquota __P((struct fstab *, int, char **));
    162 static struct fileusage *lookup __P((u_int32_t, int));
    163 static struct fileusage *addid __P((u_int32_t, int, const char *));
    164 static u_int32_t subsequent __P((u_int32_t, int));
    165 static union comb_dinode *getnextinode __P((ino_t));
    166 static void setinodebuf __P((ino_t));
    167 static void freeinodebuf __P((void));
    168 static void bread __P((daddr_t, char *, long));
    169 static void infohandler __P((int sig));
    170 static void swap_dinode1(union comb_dinode *, int);
    171 #ifdef HAVE_UFSv2
    172 static void swap_dinode2(union comb_dinode *, int);
    173 #endif
    174 
    175 int
    176 main(argc, argv)
    177 	int argc;
    178 	char *argv[];
    179 {
    180 	struct fstab *fs;
    181 	struct passwd *pw;
    182 	struct group *gr;
    183 	struct quotaname *auxdata;
    184 	int i, argnum, maxrun, errs;
    185 	long done = 0;
    186 	int flags = CHECK_PREEN;
    187 	const char *name;
    188 	int ch;
    189 
    190 	errs = maxrun = 0;
    191 	while ((ch = getopt(argc, argv, "aguvqdl:")) != -1) {
    192 		switch(ch) {
    193 		case 'a':
    194 			aflag++;
    195 			break;
    196 		case 'd':
    197 			flags |= CHECK_DEBUG;
    198 			break;
    199 		case 'g':
    200 			gflag++;
    201 			break;
    202 		case 'u':
    203 			uflag++;
    204 			break;
    205 		case 'q':
    206 			qflag++;
    207 			break;
    208 		case 'v':
    209 			vflag++;
    210 			break;
    211 		case 'l':
    212 			maxrun = atoi(optarg);
    213 			break;
    214 		default:
    215 			usage();
    216 		}
    217 	}
    218 	argc -= optind;
    219 	argv += optind;
    220 	if ((argc == 0 && !aflag) || (argc > 0 && aflag) || (!aflag && maxrun))
    221 		usage();
    222 	if (!gflag && !uflag) {
    223 		gflag++;
    224 		uflag++;
    225 	}
    226 
    227 	/* If -a, we do not want to pay the cost of processing every
    228 	 * group and password entry if there are no filesystems with quotas
    229 	 */
    230 	if (aflag) {
    231 		i = 0;
    232 		while ((fs = getfsent()) != NULL) {
    233 			if (needchk(fs))
    234 				i=1;
    235 		}
    236 		endfsent();
    237 		if (!i)	/* No filesystems with quotas */
    238 			exit(0);
    239 	}
    240 
    241 	if (gflag) {
    242 		setgrent();
    243 		while ((gr = getgrent()) != 0)
    244 			(void) addid((u_int32_t)gr->gr_gid, GRPQUOTA, gr->gr_name);
    245 		endgrent();
    246 	}
    247 	if (uflag) {
    248 		setpwent();
    249 		while ((pw = getpwent()) != 0)
    250 			(void) addid((u_int32_t)pw->pw_uid, USRQUOTA, pw->pw_name);
    251 		endpwent();
    252 	}
    253 	if (aflag)
    254 		exit(checkfstab(flags, maxrun, needchk, chkquota));
    255 	if (setfsent() == 0)
    256 		err(1, "%s: can't open", FSTAB);
    257 	while ((fs = getfsent()) != NULL) {
    258 		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
    259 		    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
    260 		    (auxdata = needchk(fs)) &&
    261 		    (name = blockcheck(fs->fs_spec))) {
    262 			done |= 1 << argnum;
    263 			errs += chkquota(fs->fs_type, name, fs->fs_file,
    264 			    auxdata, NULL);
    265 		}
    266 	}
    267 	endfsent();
    268 	for (i = 0; i < argc; i++)
    269 		if ((done & (1 << i)) == 0)
    270 			fprintf(stderr, "%s not found in %s\n",
    271 				argv[i], FSTAB);
    272 	exit(errs);
    273 }
    274 
    275 static void
    276 usage()
    277 {
    278 
    279 	(void)fprintf(stderr,
    280 	    "usage:\t%s -a [-gquv] [-l maxparallel]\n\t%s [-gquv] filesys ...\n", getprogname(),
    281 	    getprogname());
    282 	exit(1);
    283 }
    284 
    285 static void *
    286 needchk(fs)
    287 	struct fstab *fs;
    288 {
    289 	struct quotaname *qnp;
    290 	char *qfnp;
    291 
    292 	if (strcmp(fs->fs_vfstype, "ffs") ||
    293 	    strcmp(fs->fs_type, FSTAB_RW))
    294 		return (NULL);
    295 	if ((qnp = malloc(sizeof(*qnp))) == NULL)
    296 		err(1, "%s", strerror(errno));
    297 	qnp->flags = 0;
    298 	if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
    299 		strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname));
    300 		qnp->flags |= HASGRP;
    301 	}
    302 	if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
    303 		strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname));
    304 		qnp->flags |= HASUSR;
    305 	}
    306 	if (qnp->flags)
    307 		return (qnp);
    308 	free(qnp);
    309 	return (NULL);
    310 }
    311 
    312 off_t sblock_try[] = SBLOCKSEARCH;
    313 
    314 /*
    315  * Scan the specified filesystem to check quota(s) present on it.
    316  */
    317 static int
    318 chkquota(type, fsname, mntpt, v, pid)
    319 	const char *type, *fsname, *mntpt;
    320 	void *v;
    321 	pid_t *pid;
    322 {
    323 	struct quotaname *qnp = v;
    324 	struct fileusage *fup;
    325 	union comb_dinode *dp;
    326 	int cg, i, mode, errs = 0, inosused;
    327 	ino_t ino;
    328 	struct cg *cgp;
    329 	char msgbuf[4096];
    330 
    331 	if (pid != NULL) {
    332 		fflush(stdout);
    333 		switch ((*pid = fork())) {
    334 		default:
    335 			return 0;
    336 		case 0:
    337 			break;
    338 		case -1:
    339 			err(1, "Cannot fork");
    340 		}
    341 		setvbuf(stdout, msgbuf, _IOFBF, sizeof msgbuf);
    342 	}
    343 
    344 	if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
    345 		warn("Cannot open %s", fsname);
    346 		if (pid != NULL)
    347 			exit(1);
    348 		return 1;
    349 	}
    350 	if (vflag) {
    351 		(void)printf("*** Checking ");
    352 		if (qnp->flags & HASUSR)
    353 			(void)printf("%s%s", qfextension[USRQUOTA],
    354 			    (qnp->flags & HASGRP) ? " and " : "");
    355 		if (qnp->flags & HASGRP)
    356 			(void)printf("%s", qfextension[GRPQUOTA]);
    357 		(void)printf(" quotas for %s (%s)\n", fsname, mntpt);
    358 		fflush(stdout);
    359 	}
    360 	signal(SIGINFO, infohandler);
    361 	sync();
    362 	dev_bsize = 1;
    363 
    364 	for (i = 0; ; i++) {
    365 		if (sblock_try[i] == -1) {
    366 			warnx("%s: superblock not found", fsname);
    367 			if (pid != NULL)
    368 				exit(1);
    369 			return 1;
    370 		}
    371 		bread(sblock_try[i], (char *)&sblock, SBLOCKSIZE);
    372 		switch (sblock.fs_magic) {
    373 #ifdef HAVE_UFSv2
    374 		case FS_UFS2_MAGIC:
    375 			is_ufs2 = 1;
    376 			/*FALLTHROUGH*/
    377 #endif
    378 		case FS_UFS1_MAGIC:
    379 			break;
    380 #ifdef HAVE_UFSv2
    381 		case FS_UFS2_MAGIC_SWAPPED:
    382 			is_ufs2 = 1;
    383 			/*FALLTHROUGH*/
    384 #endif
    385 		case FS_UFS1_MAGIC_SWAPPED:
    386 			needswap = 1;
    387 			ffs_sb_swap(&sblock, &sblock);
    388 			break;
    389 		default:
    390 			continue;
    391 		}
    392 
    393 #ifdef HAVE_UFSv2
    394 		if (is_ufs2 || sblock.fs_old_flags & FS_FLAGS_UPDATED) {
    395 			if (sblock.fs_sblockloc != sblock_try[i])
    396 				continue;
    397 		} else {
    398 			if (sblock_try[i] == SBLOCK_UFS2)
    399 				continue;
    400 		}
    401 #endif
    402 		break;
    403 	}
    404 
    405 	cgp = malloc(sblock.fs_cgsize);
    406 	if (cgp == NULL) {
    407 		warn("%s: can't allocate %d bytes of cg space", fsname,
    408 		    sblock.fs_cgsize);
    409 		if (pid != NULL)
    410 			exit(1);
    411 		return 1;
    412 	}
    413 
    414 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
    415 	maxino = sblock.fs_ncg * sblock.fs_ipg;
    416 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
    417 		ino = cg * sblock.fs_ipg;
    418 		setinodebuf(ino);
    419 #ifdef HAVE_UFSv2
    420 		if (sblock.fs_magic == FS_UFS2_MAGIC) {
    421 			bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)cgp,
    422 			    sblock.fs_cgsize);
    423 			if (needswap)
    424 				ffs_cg_swap(cgp, cgp, &sblock);
    425 			inosused = cgp->cg_initediblk;
    426 		} else
    427 #endif
    428 			inosused = sblock.fs_ipg;
    429 		for (i = 0; i < inosused; i++, ino++) {
    430 			if (got_siginfo) {
    431 				fprintf(stderr,
    432 				    "%s: cyl group %d of %d (%d%%)\n",
    433 				    fsname, cg, sblock.fs_ncg,
    434 				    cg * 100 / sblock.fs_ncg);
    435 				got_siginfo = 0;
    436 			}
    437 			if (ino < ROOTINO)
    438 				continue;
    439 			if ((dp = getnextinode(ino)) == NULL)
    440 				continue;
    441 			if ((mode = DIP(dp, mode) & IFMT) == 0)
    442 				continue;
    443 			if (qnp->flags & HASGRP) {
    444 				fup = addid(DIP(dp, gid), GRPQUOTA,
    445 				    (char *)0);
    446 				fup->fu_curinodes++;
    447 				if (mode == IFREG || mode == IFDIR ||
    448 				    mode == IFLNK)
    449 					fup->fu_curblocks += DIP(dp, blocks);
    450 			}
    451 			if (qnp->flags & HASUSR) {
    452 				fup = addid(DIP(dp, uid), USRQUOTA,
    453 				    (char *)0);
    454 				fup->fu_curinodes++;
    455 				if (mode == IFREG || mode == IFDIR ||
    456 				    mode == IFLNK)
    457 					fup->fu_curblocks += DIP(dp, blocks);
    458 			}
    459 		}
    460 	}
    461 	freeinodebuf();
    462 	free(cgp);
    463 	if (qnp->flags & HASUSR)
    464 		errs += update(mntpt, qnp->usrqfname, USRQUOTA);
    465 	if (qnp->flags & HASGRP)
    466 		errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
    467 	close(fi);
    468 	if (pid != NULL)
    469 		exit(errs);
    470 	return errs;
    471 }
    472 
    473 /*
    474  * Update a specified quota file.
    475  */
    476 static int
    477 update(fsname, quotafile, type)
    478 	const char *fsname, *quotafile;
    479 	int type;
    480 {
    481 	struct fileusage *fup;
    482 	FILE *qfi, *qfo;
    483 	u_int32_t id, lastid, nextid;
    484 	int need_seek;
    485 	struct dqblk dqbuf;
    486 	static struct dqblk zerodqbuf;
    487 	static struct fileusage zerofileusage;
    488 	struct statvfs *fst;
    489 	int nfst, i;
    490 
    491 	nfst = getmntinfo(&fst, MNT_WAIT);
    492 	if (nfst == 0)
    493 		errx(1, "no filesystems mounted!");
    494 
    495 	for (i = 0; i < nfst; i++) {
    496 		if (strncmp(fst[i].f_fstypename, "ffs",
    497 		    sizeof(fst[i].f_fstypename)) == 0 &&
    498 		    strncmp(fst[i].f_mntonname, fsname,
    499 		    sizeof(fst[i].f_mntonname)) == 0 &&
    500 		    (fst[i].f_flag & ST_QUOTA) != 0) {
    501 			warnx("filesystem %s has quotas already turned on",
    502 			    fsname);
    503 		}
    504 	}
    505 
    506 	if ((qfo = fopen(quotafile, "r+")) == NULL) {
    507 		if (errno == ENOENT)
    508 			qfo = fopen(quotafile, "w+");
    509 		if (qfo) {
    510 			(void) fprintf(stderr,
    511 			    "quotacheck: creating quota file %s\n", quotafile);
    512 #define	MODE	(S_IRUSR|S_IWUSR|S_IRGRP)
    513 			(void) fchown(fileno(qfo), getuid(), getquotagid());
    514 			(void) fchmod(fileno(qfo), MODE);
    515 		} else {
    516 			(void) fprintf(stderr,
    517 			    "quotacheck: %s: %s\n", quotafile, strerror(errno));
    518 			return (1);
    519 		}
    520 	}
    521 	if ((qfi = fopen(quotafile, "r")) == NULL) {
    522 		(void) fprintf(stderr,
    523 		    "quotacheck: %s: %s\n", quotafile, strerror(errno));
    524 		(void) fclose(qfo);
    525 		return (1);
    526 	}
    527 	need_seek = 1;
    528 	for (lastid = highid[type], id = 0; id <= lastid; id = nextid) {
    529 		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
    530 			dqbuf = zerodqbuf;
    531 		if ((fup = lookup(id, type)) == 0)
    532 			fup = &zerofileusage;
    533 
    534 		nextid = subsequent(id, type);
    535 		if (nextid > 0 && nextid != id + 1) /* watch out for id == UINT32_MAX */
    536 			nextid = skipforward(id, nextid, qfi);
    537 
    538 		if (got_siginfo) {
    539 			/* XXX this could try to show percentage through the ID list */
    540 			fprintf(stderr,
    541 			    "%s: updating %s quotas for id=%" PRIu32 " (%s)\n", fsname,
    542 			    qfextension[type < MAXQUOTAS ? type : MAXQUOTAS],
    543 			    id, fup->fu_name);
    544 			got_siginfo = 0;
    545 		}
    546 		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
    547 		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
    548 			fup->fu_curinodes = 0;	/* reset usage  */
    549 			fup->fu_curblocks = 0;	/* for next filesystem */
    550 
    551 			need_seek = 1;
    552 			if (id == UINT32_MAX || nextid == 0) {	/* infinite loop avoidance (OR do as "nextid < id"?) */
    553 				break;
    554 			}
    555 			continue;
    556 		}
    557 		if (vflag) {
    558 			if (aflag)
    559 				printf("%s: ", fsname);
    560 			printf("%-8s fixed:", fup->fu_name);
    561 			if (dqbuf.dqb_curinodes != fup->fu_curinodes)
    562 				(void)printf("\tinodes %d -> %ld",
    563 					dqbuf.dqb_curinodes, fup->fu_curinodes);
    564 			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
    565 				(void)printf("\tblocks %d -> %ld",
    566 					dqbuf.dqb_curblocks, fup->fu_curblocks);
    567 			(void)printf("\n");
    568 		}
    569 		/*
    570 		 * Reset time limit if have a soft limit and were
    571 		 * previously under it, but are now over it.
    572 		 */
    573 		if (dqbuf.dqb_bsoftlimit &&
    574 		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
    575 		    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
    576 			dqbuf.dqb_btime = 0;
    577 		if (dqbuf.dqb_isoftlimit &&
    578 		    dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
    579 		    fup->fu_curinodes >= dqbuf.dqb_isoftlimit)
    580 			dqbuf.dqb_itime = 0;
    581 		dqbuf.dqb_curinodes = fup->fu_curinodes;
    582 		dqbuf.dqb_curblocks = fup->fu_curblocks;
    583 
    584 		if (need_seek) {
    585 			(void) fseeko(qfo, (off_t)id * sizeof(struct dqblk),
    586 			    SEEK_SET);
    587 			need_seek = nextid != id + 1;
    588 		}
    589 		(void) fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
    590 
    591 		fup->fu_curinodes = 0;
    592 		fup->fu_curblocks = 0;
    593 		if (id == UINT32_MAX || nextid == 0) {	/* infinite loop avoidance (OR do as "nextid < id"?) */
    594 			break;
    595 		}
    596 	}
    597 	(void) fclose(qfi);
    598 	(void) fflush(qfo);
    599 	if (highid[type] != UINT32_MAX)
    600 		(void) ftruncate(fileno(qfo),
    601 		    (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
    602 	(void) fclose(qfo);
    603 	return (0);
    604 }
    605 
    606 u_int32_t
    607 skipforward(cur, to, qfi)
    608 	u_int32_t cur, to;
    609 	FILE *qfi;
    610 {
    611 	struct dqblk dqbuf;
    612 
    613 	if (qflag) {
    614 		(void) fseeko(qfi, (off_t)to * sizeof(struct dqblk), SEEK_SET);
    615 		return (to);
    616 	}
    617 
    618 	while (++cur < to) {
    619 		/*
    620 		 * if EOF occurs, nothing left to read, we're done
    621 		 */
    622 		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
    623 			return (to);
    624 
    625 		/*
    626 		 * If we find an entry that shows usage, before the next
    627 		 * id that has actual usage, we have to stop here, so the
    628 		 * incorrect entry can be corrected in the file
    629 		 */
    630 		if (dqbuf.dqb_curinodes != 0 || dqbuf.dqb_curblocks != 0) {
    631 			(void)fseek(qfi, -(long)sizeof(struct dqblk), SEEK_CUR);
    632 			return (cur);
    633 		}
    634 	}
    635 	return (to);
    636 }
    637 
    638 /*
    639  * Check to see if target appears in list of size cnt.
    640  */
    641 static int
    642 oneof(target, list, cnt)
    643 	const char *target;
    644 	char *list[];
    645 	int cnt;
    646 {
    647 	int i;
    648 
    649 	for (i = 0; i < cnt; i++)
    650 		if (strcmp(target, list[i]) == 0)
    651 			return (i);
    652 	return (-1);
    653 }
    654 
    655 /*
    656  * Determine the group identifier for quota files.
    657  */
    658 static int
    659 getquotagid()
    660 {
    661 	struct group *gr;
    662 
    663 	if ((gr = getgrnam(quotagroup)) != NULL)
    664 		return (gr->gr_gid);
    665 	return (-1);
    666 }
    667 
    668 /*
    669  * Check to see if a particular quota is to be enabled.
    670  */
    671 static int
    672 hasquota(fs, type, qfnamep)
    673 	struct fstab *fs;
    674 	int type;
    675 	char **qfnamep;
    676 {
    677 	char *opt;
    678 	char *cp = NULL;
    679 	static char initname, usrname[100], grpname[100];
    680 	static char buf[BUFSIZ];
    681 
    682 	if (!initname) {
    683 		(void)snprintf(usrname, sizeof(usrname),
    684 		    "%s%s", qfextension[USRQUOTA], qfname);
    685 		(void)snprintf(grpname, sizeof(grpname),
    686 		    "%s%s", qfextension[GRPQUOTA], qfname);
    687 		initname = 1;
    688 	}
    689 	(void) strlcpy(buf, fs->fs_mntops, sizeof(buf));
    690 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
    691 		if ((cp = strchr(opt, '=')) != NULL)
    692 			*cp++ = '\0';
    693 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
    694 			break;
    695 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
    696 			break;
    697 	}
    698 	if (!opt)
    699 		return (0);
    700 	if (cp)
    701 		*qfnamep = cp;
    702 	else {
    703 		(void)snprintf(buf, sizeof(buf),
    704 		    "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
    705 		*qfnamep = buf;
    706 	}
    707 	return (1);
    708 }
    709 
    710 /*
    711  * Routines to manage the file usage table.
    712  *
    713  * Lookup an id of a specific type.
    714  */
    715 static struct fileusage *
    716 lookup(id, type)
    717 	u_int32_t id;
    718 	int type;
    719 {
    720 	struct fileusage *fup;
    721 
    722 	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
    723 		if (fup->fu_id == id)
    724 			return (fup);
    725 	return (NULL);
    726 }
    727 
    728 /*
    729  * Add a new file usage id if it does not already exist.
    730  */
    731 static struct fileusage *
    732 addid(id, type, name)
    733 	u_int32_t id;
    734 	int type;
    735 	const char *name;
    736 {
    737 	struct fileusage *fup, **fhp;
    738 	int len;
    739 
    740 	if ((fup = lookup(id, type)) != NULL)
    741 		return (fup);
    742 	if (name)
    743 		len = strlen(name);
    744 	else
    745 		len = 10;
    746 	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
    747 		err(1, "%s", strerror(errno));
    748 	fhp = &fuhead[type][id & (FUHASH - 1)];
    749 	fup->fu_next = *fhp;
    750 	*fhp = fup;
    751 	fup->fu_id = id;
    752 	if (id > highid[type])
    753 		highid[type] = id;
    754 	if (name)
    755 		memmove(fup->fu_name, name, len + 1);
    756 	else
    757 		(void) sprintf(fup->fu_name, "%" PRIu32, id);
    758 	return (fup);
    759 }
    760 
    761 static u_int32_t
    762 subsequent(id, type)
    763 	u_int32_t id;
    764 	int type;
    765 {
    766 	struct fileusage *fup, **iup, **cup;
    767 	u_int32_t next, offset;
    768 
    769 	next = highid[type] + 1;
    770 	offset = 0;
    771 	cup = iup = &fuhead[type][id & (FUHASH-1)];
    772 	do {
    773 		++offset;
    774 		if (++cup >= &fuhead[type][FUHASH])
    775 			cup = &fuhead[type][0];
    776 		for (fup = *cup; fup != 0; fup = fup->fu_next) {
    777 			if (fup->fu_id > id && fup->fu_id <= id + offset)
    778 				return (fup->fu_id);
    779 			if (fup->fu_id > id && fup->fu_id < next)
    780 				next = fup->fu_id;
    781 		}
    782 	} while (cup != iup);
    783 
    784 	return next;
    785 }
    786 
    787 /*
    788  * Special purpose version of ginode used to optimize first pass
    789  * over all the inodes in numerical order.
    790  */
    791 static ino_t nextino, lastinum, lastvalidinum;
    792 static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
    793 static union comb_dinode *inodebuf;
    794 #define INOBUFSIZE	56*1024	/* size of buffer to read inodes */
    795 
    796 union comb_dinode *
    797 getnextinode(inumber)
    798 	ino_t inumber;
    799 {
    800 	long size;
    801 	daddr_t dblk;
    802 	static union comb_dinode *dp;
    803 	union comb_dinode *ret;
    804 
    805 	if (inumber != nextino++ || inumber > lastvalidinum) {
    806 		errx(1, "bad inode number %llu to nextinode",
    807 		    (unsigned long long)inumber);
    808 	}
    809 
    810 	if (inumber >= lastinum) {
    811 		readcnt++;
    812 		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
    813 		if (readcnt % readpercg == 0) {
    814 			size = partialsize;
    815 			lastinum += partialcnt;
    816 		} else {
    817 			size = inobufsize;
    818 			lastinum += fullcnt;
    819 		}
    820 		(void)bread(dblk, (caddr_t)inodebuf, size);
    821 		if (needswap) {
    822 #ifdef HAVE_UFSv2
    823 			if (is_ufs2)
    824 				swap_dinode2(inodebuf, lastinum - inumber);
    825 			else
    826 #endif
    827 				swap_dinode1(inodebuf, lastinum - inumber);
    828 		}
    829 		dp = (union comb_dinode *)inodebuf;
    830 	}
    831 	ret = dp;
    832 	dp = (union comb_dinode *)
    833 	    ((char *)dp + (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE));
    834 	return ret;
    835 }
    836 
    837 void
    838 setinodebuf(inum)
    839 	ino_t inum;
    840 {
    841 
    842 	if (inum % sblock.fs_ipg != 0)
    843 		errx(1, "bad inode number %llu to setinodebuf",
    844 		    (unsigned long long)inum);
    845 
    846 	lastvalidinum = inum + sblock.fs_ipg - 1;
    847 	nextino = inum;
    848 	lastinum = inum;
    849 	readcnt = 0;
    850 	if (inodebuf != NULL)
    851 		return;
    852 	inobufsize = blkroundup(&sblock, INOBUFSIZE);
    853 	fullcnt = inobufsize / (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE);
    854 	readpercg = sblock.fs_ipg / fullcnt;
    855 	partialcnt = sblock.fs_ipg % fullcnt;
    856 	partialsize = partialcnt * (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE);
    857 	if (partialcnt != 0) {
    858 		readpercg++;
    859 	} else {
    860 		partialcnt = fullcnt;
    861 		partialsize = inobufsize;
    862 	}
    863 	if (inodebuf == NULL &&
    864 	    (inodebuf = malloc((unsigned)inobufsize)) == NULL)
    865 		errx(1, "Cannot allocate space for inode buffer");
    866 	while (nextino < ROOTINO)
    867 		getnextinode(nextino);
    868 }
    869 
    870 void
    871 freeinodebuf()
    872 {
    873 
    874 	if (inodebuf != NULL)
    875 		free((char *)inodebuf);
    876 	inodebuf = NULL;
    877 }
    878 
    879 
    880 #ifdef HAVE_UFSv2
    881 static void
    882 swap_dinode1(union comb_dinode *dp, int n)
    883 {
    884 	int i;
    885 	struct ufs1_dinode *dp1;
    886 
    887 	dp1 = (struct ufs1_dinode *)&dp->dp1;
    888 	for (i = 0; i < n; i++, dp1++)
    889 		ffs_dinode1_swap(dp1, dp1);
    890 }
    891 
    892 static void
    893 swap_dinode2(union comb_dinode *dp, int n)
    894 {
    895 	int i;
    896 	struct ufs2_dinode *dp2;
    897 
    898 	dp2 = (struct ufs2_dinode *)&dp->dp2;
    899 	for (i = 0; i < n; i++, dp2++)
    900 		ffs_dinode2_swap(dp2, dp2);
    901 }
    902 
    903 #else
    904 
    905 static void
    906 swap_dinode1(union comb_dinode *dp, int n)
    907 {
    908 	int i;
    909 	struct dinode *dp1;
    910 
    911 	dp1 = (struct dinode *) &dp->dp1;
    912 	for (i = 0; i < n; i++, dp1++)
    913 		ffs_dinode_swap(dp1, dp1);
    914 }
    915 
    916 #endif
    917 
    918 /*
    919  * Read specified disk blocks.
    920  */
    921 static void
    922 bread(bno, buf, cnt)
    923 	daddr_t bno;
    924 	char *buf;
    925 	long cnt;
    926 {
    927 
    928 	if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
    929 	    read(fi, buf, cnt) != cnt)
    930 		err(1, "block %lld", (long long)bno);
    931 }
    932 
    933 void
    934 infohandler(int sig)
    935 {
    936 	got_siginfo = 1;
    937 }
    938