121517Smckusick /*
2*61532Sbostic  * Copyright (c) 1980, 1990, 1993
3*61532Sbostic  *	The Regents of the University of California.  All rights reserved.
434364Sbostic  *
541411Smckusick  * This code is derived from software contributed to Berkeley by
641411Smckusick  * Robert Elz at The University of Melbourne.
741411Smckusick  *
842707Sbostic  * %sccs.include.redist.c%
921517Smckusick  */
1021517Smckusick 
1112703Smckusick #ifndef lint
12*61532Sbostic static char copyright[] =
13*61532Sbostic "@(#) Copyright (c) 1980, 1990, 1993\n\
14*61532Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1534364Sbostic #endif /* not lint */
1612660Smckusick 
1721517Smckusick #ifndef lint
18*61532Sbostic static char sccsid[] = "@(#)quotacheck.c	8.1 (Berkeley) 06/05/93";
1934364Sbostic #endif /* not lint */
2021517Smckusick 
2112660Smckusick /*
2241411Smckusick  * Fix up / report on disk quotas & usage
2312660Smckusick  */
2412660Smckusick #include <sys/param.h>
2546783Sbostic #include <sys/stat.h>
2654158Sbostic 
2751623Sbostic #include <ufs/ufs/dinode.h>
2851623Sbostic #include <ufs/ufs/quota.h>
2951623Sbostic #include <ufs/ffs/fs.h>
3054158Sbostic 
3146783Sbostic #include <fcntl.h>
3212703Smckusick #include <fstab.h>
3312802Smckusick #include <pwd.h>
3441411Smckusick #include <grp.h>
3546783Sbostic #include <errno.h>
3646783Sbostic #include <unistd.h>
3738511Sbostic #include <stdio.h>
3846783Sbostic #include <stdlib.h>
3946783Sbostic #include <string.h>
4012660Smckusick 
4145251Smckusick char *qfname = QUOTAFILENAME;
4245251Smckusick char *qfextension[] = INITQFNAMES;
4345251Smckusick char *quotagroup = QUOTAGROUP;
4445251Smckusick 
4512660Smckusick union {
4612660Smckusick 	struct	fs	sblk;
4712703Smckusick 	char	dummy[MAXBSIZE];
4812660Smckusick } un;
4912660Smckusick #define	sblock	un.sblk
5041411Smckusick long dev_bsize = 1;
5141411Smckusick long maxino;
5212703Smckusick 
5341443Smckusick struct quotaname {
5441443Smckusick 	long	flags;
5541443Smckusick 	char	grpqfname[MAXPATHLEN + 1];
5641443Smckusick 	char	usrqfname[MAXPATHLEN + 1];
5741443Smckusick };
5841443Smckusick #define	HASUSR	1
5941443Smckusick #define	HASGRP	2
6041443Smckusick 
6112703Smckusick struct fileusage {
6241411Smckusick 	struct	fileusage *fu_next;
6341411Smckusick 	u_long	fu_curinodes;
6441411Smckusick 	u_long	fu_curblocks;
6541411Smckusick 	u_long	fu_id;
6641411Smckusick 	char	fu_name[1];
6741411Smckusick 	/* actually bigger */
6812703Smckusick };
6941411Smckusick #define FUHASH 1024	/* must be power of two */
7041411Smckusick struct fileusage *fuhead[MAXQUOTAS][FUHASH];
7112660Smckusick 
7241411Smckusick int	aflag;			/* all file systems */
7341411Smckusick int	gflag;			/* check group quotas */
7441411Smckusick int	uflag;			/* check user quotas */
7541411Smckusick int	vflag;			/* verbose */
7641411Smckusick int	fi;			/* open disk file descriptor */
7741411Smckusick u_long	highid[MAXQUOTAS];	/* highest addid()'ed identifier per type */
7812703Smckusick 
7954158Sbostic struct fileusage *
8054158Sbostic 	 addid __P((u_long, int, char *));
8154158Sbostic char	*blockcheck __P((char *));
8254158Sbostic void	 bread __P((daddr_t, char *, long));
8354158Sbostic int	 chkquota __P((char *, char *, struct quotaname *));
8454158Sbostic void	 err __P((const char *, ...));
8554158Sbostic void	 freeinodebuf __P((void));
8654158Sbostic struct dinode *
8754158Sbostic 	 getnextinode __P((ino_t));
8854158Sbostic int	 getquotagid __P((void));
8954158Sbostic int	 hasquota __P((struct fstab *, int, char **));
9054158Sbostic struct fileusage *
9154158Sbostic 	 lookup __P((u_long, int));
9254158Sbostic void	*needchk __P((struct fstab *));
9354158Sbostic int	 oneof __P((char *, char*[], int));
9454158Sbostic void	 resetinodebuf __P((void));
9554158Sbostic int	 update __P((char *, char *, int));
9654158Sbostic void	 usage __P((void));
9754158Sbostic 
9854158Sbostic int
9912660Smckusick main(argc, argv)
10012703Smckusick 	int argc;
10154158Sbostic 	char *argv[];
10212660Smckusick {
10312703Smckusick 	register struct fstab *fs;
10412802Smckusick 	register struct passwd *pw;
10541411Smckusick 	register struct group *gr;
10654158Sbostic 	struct quotaname *auxdata;
10741411Smckusick 	int i, argnum, maxrun, errs = 0;
10854158Sbostic 	long done = 0;
10954158Sbostic 	char ch, *name;
11012660Smckusick 
11141411Smckusick 	while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
11241411Smckusick 		switch(ch) {
11341411Smckusick 		case 'a':
11441411Smckusick 			aflag++;
11541411Smckusick 			break;
11641411Smckusick 		case 'g':
11741411Smckusick 			gflag++;
11841411Smckusick 			break;
11941411Smckusick 		case 'u':
12041411Smckusick 			uflag++;
12141411Smckusick 			break;
12241411Smckusick 		case 'v':
12341411Smckusick 			vflag++;
12441411Smckusick 			break;
12541411Smckusick 		case 'l':
12641411Smckusick 			maxrun = atoi(optarg);
12741411Smckusick 			break;
12841411Smckusick 		default:
12941411Smckusick 			usage();
13041411Smckusick 		}
13112660Smckusick 	}
13241411Smckusick 	argc -= optind;
13341411Smckusick 	argv += optind;
13441411Smckusick 	if ((argc == 0 && !aflag) || (argc > 0 && aflag))
13541411Smckusick 		usage();
13641411Smckusick 	if (!gflag && !uflag) {
13741411Smckusick 		gflag++;
13841411Smckusick 		uflag++;
13912703Smckusick 	}
14041411Smckusick 	if (gflag) {
14141411Smckusick 		setgrent();
14241411Smckusick 		while ((gr = getgrent()) != 0)
14341411Smckusick 			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
14441411Smckusick 		endgrent();
14524758Sserge 	}
14641411Smckusick 	if (uflag) {
14741411Smckusick 		setpwent();
14841411Smckusick 		while ((pw = getpwent()) != 0)
14941411Smckusick 			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
15041411Smckusick 		endpwent();
15112660Smckusick 	}
15241411Smckusick 	if (aflag)
15341411Smckusick 		exit(checkfstab(1, maxrun, needchk, chkquota));
15454158Sbostic 	if (setfsent() == 0)
15554158Sbostic 		err("%s: can't open", FSTAB);
15641411Smckusick 	while ((fs = getfsent()) != NULL) {
15741411Smckusick 		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
15841411Smckusick 		    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
15941411Smckusick 		    (auxdata = needchk(fs)) &&
16041411Smckusick 		    (name = blockcheck(fs->fs_spec))) {
16141411Smckusick 			done |= 1 << argnum;
16241411Smckusick 			errs += chkquota(name, fs->fs_file, auxdata);
16324758Sserge 		}
16412660Smckusick 	}
16541411Smckusick 	endfsent();
16612703Smckusick 	for (i = 0; i < argc; i++)
16712703Smckusick 		if ((done & (1 << i)) == 0)
16824782Sserge 			fprintf(stderr, "%s not found in %s\n",
16924782Sserge 				argv[i], FSTAB);
17012703Smckusick 	exit(errs);
17112703Smckusick }
17212660Smckusick 
17354158Sbostic void
17441411Smckusick usage()
17524758Sserge {
17654158Sbostic 	(void)fprintf(stderr, "usage:\t%s\n\t%s\n",
17746783Sbostic 		"quotacheck -a [-guv]",
17846783Sbostic 		"quotacheck [-guv] filesys ...");
17941411Smckusick 	exit(1);
18041411Smckusick }
18124758Sserge 
18254158Sbostic void *
18341411Smckusick needchk(fs)
18441411Smckusick 	register struct fstab *fs;
18541411Smckusick {
18641443Smckusick 	register struct quotaname *qnp;
18741443Smckusick 	char *qfnp;
18824758Sserge 
18941443Smckusick 	if (strcmp(fs->fs_vfstype, "ufs") ||
19041443Smckusick 	    strcmp(fs->fs_type, FSTAB_RW))
19154158Sbostic 		return (NULL);
19254158Sbostic 	if ((qnp = malloc(sizeof(*qnp))) == NULL)
19354158Sbostic 		err("%s", strerror(errno));
19441443Smckusick 	qnp->flags = 0;
19541443Smckusick 	if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
19641443Smckusick 		strcpy(qnp->grpqfname, qfnp);
19741443Smckusick 		qnp->flags |= HASGRP;
19841443Smckusick 	}
19941443Smckusick 	if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
20041443Smckusick 		strcpy(qnp->usrqfname, qfnp);
20141443Smckusick 		qnp->flags |= HASUSR;
20241443Smckusick 	}
20341443Smckusick 	if (qnp->flags)
20454158Sbostic 		return (qnp);
20554158Sbostic 	free(qnp);
20654158Sbostic 	return (NULL);
20741411Smckusick }
20824758Sserge 
20941411Smckusick /*
21041411Smckusick  * Scan the specified filesystem to check quota(s) present on it.
21141411Smckusick  */
21254158Sbostic int
21341443Smckusick chkquota(fsname, mntpt, qnp)
21441411Smckusick 	char *fsname, *mntpt;
21541443Smckusick 	register struct quotaname *qnp;
21641411Smckusick {
21741411Smckusick 	register struct fileusage *fup;
21841411Smckusick 	register struct dinode *dp;
21941411Smckusick 	int cg, i, mode, errs = 0;
22041411Smckusick 	ino_t ino;
22124758Sserge 
22254158Sbostic 	if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
22341411Smckusick 		perror(fsname);
22441411Smckusick 		return (1);
22541411Smckusick 	}
22641411Smckusick 	if (vflag) {
22754158Sbostic 		(void)printf("*** Checking ");
22841443Smckusick 		if (qnp->flags & HASUSR)
22954158Sbostic 			(void)printf("%s%s", qfextension[USRQUOTA],
23041443Smckusick 			    (qnp->flags & HASGRP) ? " and " : "");
23141443Smckusick 		if (qnp->flags & HASGRP)
23254158Sbostic 			(void)printf("%s", qfextension[GRPQUOTA]);
23354158Sbostic 		(void)printf(" quotas for %s (%s)\n", fsname, mntpt);
23441411Smckusick 	}
23541411Smckusick 	sync();
23641411Smckusick 	bread(SBOFF, (char *)&sblock, (long)SBSIZE);
23741411Smckusick 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
23841411Smckusick 	maxino = sblock.fs_ncg * sblock.fs_ipg;
23941411Smckusick 	resetinodebuf();
24041411Smckusick 	for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) {
24141411Smckusick 		for (i = 0; i < sblock.fs_ipg; i++, ino++) {
24241411Smckusick 			if (ino < ROOTINO)
24324758Sserge 				continue;
24441411Smckusick 			if ((dp = getnextinode(ino)) == NULL)
24524758Sserge 				continue;
24641411Smckusick 			if ((mode = dp->di_mode & IFMT) == 0)
24741411Smckusick 				continue;
24841443Smckusick 			if (qnp->flags & HASGRP) {
24941411Smckusick 				fup = addid((u_long)dp->di_gid, GRPQUOTA,
25041411Smckusick 				    (char *)0);
25141411Smckusick 				fup->fu_curinodes++;
25241411Smckusick 				if (mode == IFREG || mode == IFDIR ||
25341411Smckusick 				    mode == IFLNK)
25441411Smckusick 					fup->fu_curblocks += dp->di_blocks;
25524758Sserge 			}
25641443Smckusick 			if (qnp->flags & HASUSR) {
25741411Smckusick 				fup = addid((u_long)dp->di_uid, USRQUOTA,
25841411Smckusick 				    (char *)0);
25941411Smckusick 				fup->fu_curinodes++;
26041411Smckusick 				if (mode == IFREG || mode == IFDIR ||
26141411Smckusick 				    mode == IFLNK)
26241411Smckusick 					fup->fu_curblocks += dp->di_blocks;
26341411Smckusick 			}
26424758Sserge 		}
26541411Smckusick 	}
26641411Smckusick 	freeinodebuf();
26741443Smckusick 	if (qnp->flags & HASUSR)
26841443Smckusick 		errs += update(mntpt, qnp->usrqfname, USRQUOTA);
26941443Smckusick 	if (qnp->flags & HASGRP)
27041443Smckusick 		errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
27141411Smckusick 	close(fi);
27224758Sserge 	return (errs);
27324758Sserge }
27424758Sserge 
27541411Smckusick /*
27641411Smckusick  * Update a specified quota file.
27741411Smckusick  */
27854158Sbostic int
27941443Smckusick update(fsname, quotafile, type)
28041443Smckusick 	char *fsname, *quotafile;
28141411Smckusick 	register int type;
28212703Smckusick {
28312703Smckusick 	register struct fileusage *fup;
28425377Sserge 	register FILE *qfi, *qfo;
28541411Smckusick 	register u_long id, lastid;
28612703Smckusick 	struct dqblk dqbuf;
28721085Smckusick 	static int warned = 0;
28841411Smckusick 	static struct dqblk zerodqbuf;
28941411Smckusick 	static struct fileusage zerofileusage;
29012660Smckusick 
29141411Smckusick 	if ((qfo = fopen(quotafile, "r+")) == NULL) {
29246783Sbostic 		if (errno == ENOENT)
29346783Sbostic 			qfo = fopen(quotafile, "w+");
29446783Sbostic 		if (qfo) {
29546783Sbostic 			(void) fprintf(stderr,
29646783Sbostic 			    "quotacheck: creating quota file %s\n", quotafile);
29746783Sbostic #define	MODE	(S_IRUSR|S_IWUSR|S_IRGRP)
29846783Sbostic 			(void) fchown(fileno(qfo), getuid(), getquotagid());
29946783Sbostic 			(void) fchmod(fileno(qfo), MODE);
30046783Sbostic 		} else {
30146783Sbostic 			(void) fprintf(stderr,
30246783Sbostic 			    "quotacheck: %s: %s\n", quotafile, strerror(errno));
30341411Smckusick 			return (1);
30441411Smckusick 		}
30512660Smckusick 	}
30641411Smckusick 	if ((qfi = fopen(quotafile, "r")) == NULL) {
30746783Sbostic 		(void) fprintf(stderr,
30846783Sbostic 		    "quotacheck: %s: %s\n", quotafile, strerror(errno));
30946783Sbostic 		(void) fclose(qfo);
31012703Smckusick 		return (1);
31112660Smckusick 	}
31241411Smckusick 	if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
31341411Smckusick 	    errno == EOPNOTSUPP && !warned && vflag) {
31421085Smckusick 		warned++;
31554158Sbostic 		(void)printf("*** Warning: %s\n",
31641411Smckusick 		    "Quotas are not compiled into this kernel");
31721085Smckusick 	}
31841411Smckusick 	for (lastid = highid[type], id = 0; id <= lastid; id++) {
31941411Smckusick 		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
32012802Smckusick 			dqbuf = zerodqbuf;
32141411Smckusick 		if ((fup = lookup(id, type)) == 0)
32224758Sserge 			fup = &zerofileusage;
32341411Smckusick 		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
32441411Smckusick 		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
32541411Smckusick 			fup->fu_curinodes = 0;
32641411Smckusick 			fup->fu_curblocks = 0;
32725377Sserge 			fseek(qfo, (long)sizeof(struct dqblk), 1);
32812703Smckusick 			continue;
32912802Smckusick 		}
33012703Smckusick 		if (vflag) {
33141411Smckusick 			if (aflag)
33241411Smckusick 				printf("%s: ", fsname);
33341411Smckusick 			printf("%-8s fixed:", fup->fu_name);
33441411Smckusick 			if (dqbuf.dqb_curinodes != fup->fu_curinodes)
33554158Sbostic 				(void)printf("\tinodes %d -> %d",
33641411Smckusick 					dqbuf.dqb_curinodes, fup->fu_curinodes);
33741411Smckusick 			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
33854158Sbostic 				(void)printf("\tblocks %d -> %d",
33941411Smckusick 					dqbuf.dqb_curblocks, fup->fu_curblocks);
34054158Sbostic 			(void)printf("\n");
34112660Smckusick 		}
34241411Smckusick 		/*
34341411Smckusick 		 * Reset time limit if have a soft limit and were
34441411Smckusick 		 * previously under it, but are now over it.
34541411Smckusick 		 */
34641411Smckusick 		if (dqbuf.dqb_bsoftlimit &&
34741411Smckusick 		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
34841411Smckusick 		    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
34941411Smckusick 			dqbuf.dqb_btime = 0;
35041411Smckusick 		if (dqbuf.dqb_isoftlimit &&
35141411Smckusick 		    dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
35241411Smckusick 		    fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
35341411Smckusick 			dqbuf.dqb_itime = 0;
35441411Smckusick 		dqbuf.dqb_curinodes = fup->fu_curinodes;
35541411Smckusick 		dqbuf.dqb_curblocks = fup->fu_curblocks;
35641411Smckusick 		fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
35741411Smckusick 		(void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
35841411Smckusick 		    (caddr_t)&dqbuf);
35941411Smckusick 		fup->fu_curinodes = 0;
36041411Smckusick 		fup->fu_curblocks = 0;
36112660Smckusick 	}
36241411Smckusick 	fclose(qfi);
36325377Sserge 	fflush(qfo);
36441411Smckusick 	ftruncate(fileno(qfo),
36541411Smckusick 	    (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
36625377Sserge 	fclose(qfo);
36712703Smckusick 	return (0);
36812660Smckusick }
36912660Smckusick 
37041411Smckusick /*
37141411Smckusick  * Check to see if target appears in list of size cnt.
37241411Smckusick  */
37354158Sbostic int
37441411Smckusick oneof(target, list, cnt)
37541411Smckusick 	register char *target, *list[];
37641411Smckusick 	int cnt;
37712660Smckusick {
37812703Smckusick 	register int i;
37912660Smckusick 
38041411Smckusick 	for (i = 0; i < cnt; i++)
38141411Smckusick 		if (strcmp(target, list[i]) == 0)
38241411Smckusick 			return (i);
38341411Smckusick 	return (-1);
38412660Smckusick }
38512660Smckusick 
38641411Smckusick /*
38741443Smckusick  * Determine the group identifier for quota files.
38841443Smckusick  */
38954158Sbostic int
39041443Smckusick getquotagid()
39141443Smckusick {
39241443Smckusick 	struct group *gr;
39341443Smckusick 
39441443Smckusick 	if (gr = getgrnam(quotagroup))
39541443Smckusick 		return (gr->gr_gid);
39641443Smckusick 	return (-1);
39741443Smckusick }
39841443Smckusick 
39941443Smckusick /*
40041411Smckusick  * Check to see if a particular quota is to be enabled.
40141411Smckusick  */
40254158Sbostic int
40341443Smckusick hasquota(fs, type, qfnamep)
40441443Smckusick 	register struct fstab *fs;
40541411Smckusick 	int type;
40641443Smckusick 	char **qfnamep;
40712660Smckusick {
40841411Smckusick 	register char *opt;
40954158Sbostic 	char *cp;
41041411Smckusick 	static char initname, usrname[100], grpname[100];
41141443Smckusick 	static char buf[BUFSIZ];
41212660Smckusick 
41341411Smckusick 	if (!initname) {
41454158Sbostic 		(void)snprintf(usrname, sizeof(usrname),
41554158Sbostic 		    "%s%s", qfextension[USRQUOTA], qfname);
41654158Sbostic 		(void)snprintf(grpname, sizeof(grpname),
41754158Sbostic 		    "%s%s", qfextension[GRPQUOTA], qfname);
41841411Smckusick 		initname = 1;
41912660Smckusick 	}
42041443Smckusick 	strcpy(buf, fs->fs_mntops);
42141411Smckusick 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
42241443Smckusick 		if (cp = index(opt, '='))
42341443Smckusick 			*cp++ = '\0';
42441411Smckusick 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
42541443Smckusick 			break;
42641411Smckusick 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
42741443Smckusick 			break;
42825377Sserge 	}
42941443Smckusick 	if (!opt)
43041443Smckusick 		return (0);
43154158Sbostic 	if (cp)
43241443Smckusick 		*qfnamep = cp;
43354158Sbostic 	else {
43454158Sbostic 		(void)snprintf(buf, sizeof(buf),
43554158Sbostic 		    "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
43654158Sbostic 		*qfnamep = buf;
43741443Smckusick 	}
43841443Smckusick 	return (1);
43912660Smckusick }
44012660Smckusick 
44141411Smckusick /*
44241411Smckusick  * Routines to manage the file usage table.
44341411Smckusick  *
44441411Smckusick  * Lookup an id of a specific type.
44541411Smckusick  */
44612703Smckusick struct fileusage *
44741411Smckusick lookup(id, type)
44841411Smckusick 	u_long id;
44941411Smckusick 	int type;
45012660Smckusick {
45112703Smckusick 	register struct fileusage *fup;
45212660Smckusick 
45341411Smckusick 	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
45441411Smckusick 		if (fup->fu_id == id)
45512703Smckusick 			return (fup);
45654158Sbostic 	return (NULL);
45712660Smckusick }
45812660Smckusick 
45941411Smckusick /*
46041411Smckusick  * Add a new file usage id if it does not already exist.
46141411Smckusick  */
46212703Smckusick struct fileusage *
46341411Smckusick addid(id, type, name)
46441411Smckusick 	u_long id;
46541411Smckusick 	int type;
46641411Smckusick 	char *name;
46712660Smckusick {
46812703Smckusick 	struct fileusage *fup, **fhp;
46941411Smckusick 	int len;
47012660Smckusick 
47141411Smckusick 	if (fup = lookup(id, type))
47212703Smckusick 		return (fup);
47341411Smckusick 	if (name)
47441411Smckusick 		len = strlen(name);
47541411Smckusick 	else
47641411Smckusick 		len = 10;
47754158Sbostic 	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
47854158Sbostic 		err("%s", strerror(errno));
47941411Smckusick 	fhp = &fuhead[type][id & (FUHASH - 1)];
48012703Smckusick 	fup->fu_next = *fhp;
48112703Smckusick 	*fhp = fup;
48241411Smckusick 	fup->fu_id = id;
48341411Smckusick 	if (id > highid[type])
48441411Smckusick 		highid[type] = id;
48554158Sbostic 	if (name)
48641411Smckusick 		bcopy(name, fup->fu_name, len + 1);
48754158Sbostic 	else
48854158Sbostic 		(void)sprintf(fup->fu_name, "%u", id);
48912703Smckusick 	return (fup);
49012660Smckusick }
49112660Smckusick 
49241411Smckusick /*
49341411Smckusick  * Special purpose version of ginode used to optimize pass
49441411Smckusick  * over all the inodes in numerical order.
49541411Smckusick  */
49641411Smckusick ino_t nextino, lastinum;
49741411Smckusick long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
49841411Smckusick struct dinode *inodebuf;
49941411Smckusick #define	INOBUFSIZE	56*1024	/* size of buffer to read inodes */
50041411Smckusick 
50141411Smckusick struct dinode *
50241411Smckusick getnextinode(inumber)
50341411Smckusick 	ino_t inumber;
50412660Smckusick {
50541411Smckusick 	long size;
50641411Smckusick 	daddr_t dblk;
50741411Smckusick 	static struct dinode *dp;
50812660Smckusick 
50954158Sbostic 	if (inumber != nextino++ || inumber > maxino)
51054158Sbostic 		err("bad inode number %d to nextinode", inumber);
51141411Smckusick 	if (inumber >= lastinum) {
51241411Smckusick 		readcnt++;
51341411Smckusick 		dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
51441411Smckusick 		if (readcnt % readpercg == 0) {
51541411Smckusick 			size = partialsize;
51641411Smckusick 			lastinum += partialcnt;
51741411Smckusick 		} else {
51841411Smckusick 			size = inobufsize;
51941411Smckusick 			lastinum += fullcnt;
52041411Smckusick 		}
52141411Smckusick 		bread(dblk, (char *)inodebuf, size);
52241411Smckusick 		dp = inodebuf;
52341411Smckusick 	}
52441411Smckusick 	return (dp++);
52512660Smckusick }
52641411Smckusick 
52741411Smckusick /*
52841411Smckusick  * Prepare to scan a set of inodes.
52941411Smckusick  */
53054158Sbostic void
53141411Smckusick resetinodebuf()
53241411Smckusick {
53341411Smckusick 
53441411Smckusick 	nextino = 0;
53541411Smckusick 	lastinum = 0;
53641411Smckusick 	readcnt = 0;
53741411Smckusick 	inobufsize = blkroundup(&sblock, INOBUFSIZE);
53841411Smckusick 	fullcnt = inobufsize / sizeof(struct dinode);
53941411Smckusick 	readpercg = sblock.fs_ipg / fullcnt;
54041411Smckusick 	partialcnt = sblock.fs_ipg % fullcnt;
54141411Smckusick 	partialsize = partialcnt * sizeof(struct dinode);
54241411Smckusick 	if (partialcnt != 0) {
54341411Smckusick 		readpercg++;
54441411Smckusick 	} else {
54541411Smckusick 		partialcnt = fullcnt;
54641411Smckusick 		partialsize = inobufsize;
54741411Smckusick 	}
54841411Smckusick 	if (inodebuf == NULL &&
54954158Sbostic 	   (inodebuf = malloc((u_int)inobufsize)) == NULL)
55054158Sbostic 		err("%s", strerror(errno));
55141411Smckusick 	while (nextino < ROOTINO)
55241411Smckusick 		getnextinode(nextino);
55341411Smckusick }
55441411Smckusick 
55541411Smckusick /*
55641411Smckusick  * Free up data structures used to scan inodes.
55741411Smckusick  */
55854158Sbostic void
55941411Smckusick freeinodebuf()
56041411Smckusick {
56141411Smckusick 
56241411Smckusick 	if (inodebuf != NULL)
56354158Sbostic 		free(inodebuf);
56441411Smckusick 	inodebuf = NULL;
56541411Smckusick }
56641411Smckusick 
56741411Smckusick /*
56841411Smckusick  * Read specified disk blocks.
56941411Smckusick  */
57054158Sbostic void
57141411Smckusick bread(bno, buf, cnt)
57241411Smckusick 	daddr_t bno;
57341411Smckusick 	char *buf;
57441411Smckusick 	long cnt;
57541411Smckusick {
57641411Smckusick 
57758002Sralph 	if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
57854158Sbostic 	    read(fi, buf, cnt) != cnt)
57954158Sbostic 		err("block %ld", bno);
58054158Sbostic }
58141411Smckusick 
58254158Sbostic #if __STDC__
58354158Sbostic #include <stdarg.h>
58454158Sbostic #else
58554158Sbostic #include <varargs.h>
58654158Sbostic #endif
58754158Sbostic 
58854158Sbostic void
58954158Sbostic #if __STDC__
59054158Sbostic err(const char *fmt, ...)
59154158Sbostic #else
59254158Sbostic err(fmt, va_alist)
59354158Sbostic 	char *fmt;
59454158Sbostic         va_dcl
59554158Sbostic #endif
59654158Sbostic {
59754158Sbostic 	va_list ap;
59854158Sbostic #if __STDC__
59954158Sbostic 	va_start(ap, fmt);
60054158Sbostic #else
60154158Sbostic 	va_start(ap);
60254158Sbostic #endif
60354158Sbostic 	(void)fprintf(stderr, "quotacheck: ");
60454158Sbostic 	(void)vfprintf(stderr, fmt, ap);
60554158Sbostic 	va_end(ap);
60654158Sbostic 	(void)fprintf(stderr, "\n");
60754158Sbostic 	exit(1);
60854158Sbostic 	/* NOTREACHED */
60941411Smckusick }
610