quotacheck.c revision 14280
1218887Sdim/*
2218887Sdim * Copyright (c) 1980, 1990, 1993
3218887Sdim *	The Regents of the University of California.  All rights reserved.
4218887Sdim *
5218887Sdim * This code is derived from software contributed to Berkeley by
6218887Sdim * Robert Elz at The University of Melbourne.
7218887Sdim *
8218887Sdim * Redistribution and use in source and binary forms, with or without
9218887Sdim * modification, are permitted provided that the following conditions
10218887Sdim * are met:
11218887Sdim * 1. Redistributions of source code must retain the above copyright
12218887Sdim *    notice, this list of conditions and the following disclaimer.
13218887Sdim * 2. Redistributions in binary form must reproduce the above copyright
14218887Sdim *    notice, this list of conditions and the following disclaimer in the
15280031Sdim *    documentation and/or other materials provided with the distribution.
16280031Sdim * 3. All advertising materials mentioning features or use of this software
17218887Sdim *    must display the following acknowledgement:
18218887Sdim *	This product includes software developed by the University of
19218887Sdim *	California, Berkeley and its contributors.
20218887Sdim * 4. Neither the name of the University nor the names of its contributors
21226633Sdim *    may be used to endorse or promote products derived from this software
22226633Sdim *    without specific prior written permission.
23249423Sdim *
24249423Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26276479Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27218887Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28218887Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29218887Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30218887Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31218887Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32218887Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33218887Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34218887Sdim * SUCH DAMAGE.
35218887Sdim */
36218887Sdim
37226633Sdim#ifndef lint
38218887Sdimstatic char copyright[] =
39218887Sdim"@(#) Copyright (c) 1980, 1990, 1993\n\
40234353Sdim	The Regents of the University of California.  All rights reserved.\n";
41234353Sdim#endif /* not lint */
42218887Sdim
43234353Sdim#ifndef lint
44218887Sdimstatic char sccsid[] = "@(#)quotacheck.c	8.3 (Berkeley) 1/29/94";
45226633Sdim#endif /* not lint */
46218887Sdim
47226633Sdim/*
48226633Sdim * Fix up / report on disk quotas & usage
49251662Sdim */
50251662Sdim#include <sys/param.h>
51251662Sdim#include <sys/queue.h>
52251662Sdim#include <sys/stat.h>
53218887Sdim
54218887Sdim#include <ufs/ufs/dinode.h>
55218887Sdim#include <ufs/ufs/quota.h>
56218887Sdim#include <ufs/ffs/fs.h>
57218887Sdim
58218887Sdim#include <fcntl.h>
59218887Sdim#include <fstab.h>
60218887Sdim#include <pwd.h>
61218887Sdim#include <grp.h>
62218887Sdim#include <errno.h>
63218887Sdim#include <unistd.h>
64234353Sdim#include <stdio.h>
65218887Sdim#include <stdlib.h>
66234353Sdim#include <string.h>
67218887Sdim
68243830Sdimchar *qfname = QUOTAFILENAME;
69218887Sdimchar *qfextension[] = INITQFNAMES;
70218887Sdimchar *quotagroup = QUOTAGROUP;
71234353Sdim
72234353Sdimunion {
73234353Sdim	struct	fs	sblk;
74234353Sdim	char	dummy[MAXBSIZE];
75234353Sdim} un;
76234353Sdim#define	sblock	un.sblk
77234353Sdimlong dev_bsize = 1;
78234353Sdimlong maxino;
79234353Sdim
80234353Sdimstruct quotaname {
81234353Sdim	long	flags;
82234353Sdim	char	grpqfname[MAXPATHLEN + 1];
83234353Sdim	char	usrqfname[MAXPATHLEN + 1];
84234353Sdim};
85234353Sdim#define	HASUSR	1
86234353Sdim#define	HASGRP	2
87234353Sdim
88234353Sdimstruct fileusage {
89234353Sdim	struct	fileusage *fu_next;
90234353Sdim	u_long	fu_curinodes;
91234353Sdim	u_long	fu_curblocks;
92234353Sdim	u_long	fu_id;
93234353Sdim	char	fu_name[1];
94239462Sdim	/* actually bigger */
95239462Sdim};
96218887Sdim#define FUHASH 1024	/* must be power of two */
97218887Sdimstruct fileusage *fuhead[MAXQUOTAS][FUHASH];
98234353Sdim
99249423Sdimint	aflag;			/* all file systems */
100234353Sdimint	gflag;			/* check group quotas */
101218887Sdimint	uflag;			/* check user quotas */
102234353Sdimint	vflag;			/* verbose */
103234353Sdimint	fi;			/* open disk file descriptor */
104218887Sdimu_long	highid[MAXQUOTAS];	/* highest addid()'ed identifier per type */
105276479Sdim
106218887Sdimstruct fileusage *
107218887Sdim	 addid __P((u_long, int, char *));
108218887Sdimchar	*blockcheck __P((char *));
109218887Sdimvoid	 bread __P((daddr_t, char *, long));
110218887Sdimint	 chkquota __P((char *, char *, struct quotaname *));
111218887Sdimvoid	 err __P((const char *, ...));
112218887Sdimvoid	 freeinodebuf __P((void));
113218887Sdimstruct dinode *
114218887Sdim	 getnextinode __P((ino_t));
115218887Sdimint	 getquotagid __P((void));
116218887Sdimint	 hasquota __P((struct fstab *, int, char **));
117226633Sdimstruct fileusage *
118218887Sdim	 lookup __P((u_long, int));
119226633Sdimvoid	*needchk __P((struct fstab *));
120218887Sdimint	 oneof __P((char *, char*[], int));
121218887Sdimvoid	 resetinodebuf __P((void));
122218887Sdimint	 update __P((char *, char *, int));
123234353Sdimvoid	 usage __P((void));
124218887Sdim
125226633Sdimint
126218887Sdimmain(argc, argv)
127218887Sdim	int argc;
128226633Sdim	char *argv[];
129218887Sdim{
130218887Sdim	register struct fstab *fs;
131226633Sdim	register struct passwd *pw;
132218887Sdim	register struct group *gr;
133226633Sdim	struct quotaname *auxdata;
134218887Sdim	int i, argnum, maxrun, errs;
135218887Sdim	long done = 0;
136218887Sdim	char ch, *name;
137218887Sdim
138276479Sdim	errs = maxrun = 0;
139218887Sdim	while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
140218887Sdim		switch(ch) {
141218887Sdim		case 'a':
142276479Sdim			aflag++;
143218887Sdim			break;
144276479Sdim		case 'g':
145218887Sdim			gflag++;
146218887Sdim			break;
147226633Sdim		case 'u':
148218887Sdim			uflag++;
149218887Sdim			break;
150218887Sdim		case 'v':
151218887Sdim			vflag++;
152234353Sdim			break;
153234353Sdim		case 'l':
154218887Sdim			maxrun = atoi(optarg);
155226633Sdim			break;
156218887Sdim		default:
157218887Sdim			usage();
158234353Sdim		}
159226633Sdim	}
160218887Sdim	argc -= optind;
161218887Sdim	argv += optind;
162234353Sdim	if ((argc == 0 && !aflag) || (argc > 0 && aflag))
163234353Sdim		usage();
164226633Sdim	if (!gflag && !uflag) {
165218887Sdim		gflag++;
166234353Sdim		uflag++;
167218887Sdim	}
168218887Sdim	if (gflag) {
169226633Sdim		setgrent();
170218887Sdim		while ((gr = getgrent()) != NULL)
171226633Sdim			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
172218887Sdim		endgrent();
173276479Sdim	}
174218887Sdim	if (uflag) {
175276479Sdim		setpwent();
176218887Sdim		while ((pw = getpwent()) != NULL)
177226633Sdim			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
178234353Sdim		endpwent();
179234353Sdim	}
180218887Sdim	if (aflag)
181218887Sdim		exit(checkfstab(1, maxrun, needchk, chkquota));
182234353Sdim	if (setfsent() == 0)
183218887Sdim		err("%s: can't open", FSTAB);
184218887Sdim	while ((fs = getfsent()) != NULL) {
185218887Sdim		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
186218887Sdim		    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
187218887Sdim		    (auxdata = needchk(fs)) &&
188276479Sdim		    (name = blockcheck(fs->fs_spec))) {
189234353Sdim			done |= 1 << argnum;
190218887Sdim			errs += chkquota(name, fs->fs_file, auxdata);
191218887Sdim		}
192218887Sdim	}
193226633Sdim	endfsent();
194218887Sdim	for (i = 0; i < argc; i++)
195218887Sdim		if ((done & (1 << i)) == 0)
196218887Sdim			fprintf(stderr, "%s not found in %s\n",
197218887Sdim				argv[i], FSTAB);
198226633Sdim	exit(errs);
199226633Sdim}
200218887Sdim
201218887Sdimvoid
202226633Sdimusage()
203218887Sdim{
204218887Sdim	(void)fprintf(stderr, "usage:\t%s\n\t%s\n",
205226633Sdim		"quotacheck -a [-guv]",
206218887Sdim		"quotacheck [-guv] filesys ...");
207218887Sdim	exit(1);
208218887Sdim}
209226633Sdim
210218887Sdimvoid *
211276479Sdimneedchk(fs)
212218887Sdim	register struct fstab *fs;
213276479Sdim{
214218887Sdim	register struct quotaname *qnp;
215218887Sdim	char *qfnp;
216226633Sdim
217218887Sdim	if (strcmp(fs->fs_vfstype, "ufs") ||
218218887Sdim	    strcmp(fs->fs_type, FSTAB_RW))
219218887Sdim		return (NULL);
220218887Sdim	if ((qnp = malloc(sizeof(*qnp))) == NULL)
221218887Sdim		err("%s", strerror(errno));
222276479Sdim	qnp->flags = 0;
223218887Sdim	if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
224218887Sdim		strcpy(qnp->grpqfname, qfnp);
225218887Sdim		qnp->flags |= HASGRP;
226218887Sdim	}
227226633Sdim	if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
228218887Sdim		strcpy(qnp->usrqfname, qfnp);
229218887Sdim		qnp->flags |= HASUSR;
230218887Sdim	}
231218887Sdim	if (qnp->flags)
232218887Sdim		return (qnp);
233218887Sdim	free(qnp);
234218887Sdim	return (NULL);
235218887Sdim}
236218887Sdim
237218887Sdim/*
238218887Sdim * Scan the specified filesystem to check quota(s) present on it.
239218887Sdim */
240218887Sdimint
241218887Sdimchkquota(fsname, mntpt, qnp)
242218887Sdim	char *fsname, *mntpt;
243218887Sdim	register struct quotaname *qnp;
244276479Sdim{
245218887Sdim	register struct fileusage *fup;
246276479Sdim	register struct dinode *dp;
247218887Sdim	int cg, i, mode, errs = 0;
248218887Sdim	ino_t ino;
249218887Sdim
250218887Sdim	if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
251218887Sdim		perror(fsname);
252218887Sdim		return (1);
253276479Sdim	}
254218887Sdim	if (vflag) {
255218887Sdim		(void)printf("*** Checking ");
256218887Sdim		if (qnp->flags & HASUSR)
257218887Sdim			(void)printf("%s%s", qfextension[USRQUOTA],
258226633Sdim			    (qnp->flags & HASGRP) ? " and " : "");
259218887Sdim		if (qnp->flags & HASGRP)
260218887Sdim			(void)printf("%s", qfextension[GRPQUOTA]);
261218887Sdim		(void)printf(" quotas for %s (%s)\n", fsname, mntpt);
262218887Sdim	}
263218887Sdim	sync();
264218887Sdim	dev_bsize = 1;
265218887Sdim	bread(SBOFF, (char *)&sblock, (long)SBSIZE);
266218887Sdim	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
267218887Sdim	maxino = sblock.fs_ncg * sblock.fs_ipg;
268218887Sdim	resetinodebuf();
269226633Sdim	for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) {
270218887Sdim		for (i = 0; i < sblock.fs_ipg; i++, ino++) {
271218887Sdim			if (ino < ROOTINO)
272226633Sdim				continue;
273218887Sdim			if ((dp = getnextinode(ino)) == NULL)
274226633Sdim				continue;
275226633Sdim			if ((mode = dp->di_mode & IFMT) == 0)
276218887Sdim				continue;
277218887Sdim			if (qnp->flags & HASGRP) {
278218887Sdim				fup = addid((u_long)dp->di_gid, GRPQUOTA,
279226633Sdim				    (char *)0);
280218887Sdim				fup->fu_curinodes++;
281226633Sdim				if (mode == IFREG || mode == IFDIR ||
282218887Sdim				    mode == IFLNK)
283276479Sdim					fup->fu_curblocks += dp->di_blocks;
284218887Sdim			}
285276479Sdim			if (qnp->flags & HASUSR) {
286218887Sdim				fup = addid((u_long)dp->di_uid, USRQUOTA,
287218887Sdim				    (char *)0);
288218887Sdim				fup->fu_curinodes++;
289218887Sdim				if (mode == IFREG || mode == IFDIR ||
290218887Sdim				    mode == IFLNK)
291218887Sdim					fup->fu_curblocks += dp->di_blocks;
292218887Sdim			}
293218887Sdim		}
294218887Sdim	}
295218887Sdim	freeinodebuf();
296218887Sdim	if (qnp->flags & HASUSR)
297218887Sdim		errs += update(mntpt, qnp->usrqfname, USRQUOTA);
298276479Sdim	if (qnp->flags & HASGRP)
299218887Sdim		errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
300218887Sdim	close(fi);
301218887Sdim	return (errs);
302218887Sdim}
303226633Sdim
304218887Sdim/*
305218887Sdim * Update a specified quota file.
306218887Sdim */
307218887Sdimint
308234353Sdimupdate(fsname, quotafile, type)
309234353Sdim	char *fsname, *quotafile;
310234353Sdim	register int type;
311234353Sdim{
312234353Sdim	register struct fileusage *fup;
313234353Sdim	register FILE *qfi, *qfo;
314234353Sdim	register u_long id, lastid;
315234353Sdim	register off_t offset;
316234353Sdim	struct dqblk dqbuf;
317234353Sdim	static int warned = 0;
318234353Sdim	static struct dqblk zerodqbuf;
319234353Sdim	static struct fileusage zerofileusage;
320276479Sdim
321234353Sdim	if ((qfo = fopen(quotafile, "r+")) == NULL) {
322234353Sdim		if (errno == ENOENT)
323234353Sdim			qfo = fopen(quotafile, "w+");
324276479Sdim		if (qfo) {
325234353Sdim			(void) fprintf(stderr,
326234353Sdim			    "quotacheck: creating quota file %s\n", quotafile);
327234353Sdim#define	MODE	(S_IRUSR|S_IWUSR|S_IRGRP)
328234353Sdim			(void) fchown(fileno(qfo), getuid(), getquotagid());
329234353Sdim			(void) fchmod(fileno(qfo), MODE);
330234353Sdim		} else {
331234353Sdim			(void) fprintf(stderr,
332234353Sdim			    "quotacheck: %s: %s\n", quotafile, strerror(errno));
333234353Sdim			return (1);
334276479Sdim		}
335234353Sdim	}
336234353Sdim	if ((qfi = fopen(quotafile, "r")) == NULL) {
337234353Sdim		(void) fprintf(stderr,
338234353Sdim		    "quotacheck: %s: %s\n", quotafile, strerror(errno));
339234353Sdim		(void) fclose(qfo);
340234353Sdim		return (1);
341234353Sdim	}
342234353Sdim	if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
343234353Sdim	    errno == EOPNOTSUPP && !warned && vflag) {
344251662Sdim		warned++;
345251662Sdim		(void)printf("*** Warning: %s\n",
346218887Sdim		    "Quotas are not compiled into this kernel");
347218887Sdim	}
348218887Sdim	for (lastid = highid[type], id = 0, offset = 0; id <= lastid;
349251662Sdim	    id++, offset += sizeof(struct dqblk)) {
350251662Sdim		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
351251662Sdim			dqbuf = zerodqbuf;
352251662Sdim		if ((fup = lookup(id, type)) == 0)
353218887Sdim			fup = &zerofileusage;
354218887Sdim		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
355218887Sdim		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
356276479Sdim			fup->fu_curinodes = 0;
357218887Sdim			fup->fu_curblocks = 0;
358218887Sdim			continue;
359218887Sdim		}
360251662Sdim		if (vflag) {
361251662Sdim			if (aflag)
362251662Sdim				printf("%s: ", fsname);
363251662Sdim			printf("%-8s fixed:", fup->fu_name);
364251662Sdim			if (dqbuf.dqb_curinodes != fup->fu_curinodes)
365251662Sdim				(void)printf("\tinodes %ld -> %ld",
366251662Sdim					dqbuf.dqb_curinodes, fup->fu_curinodes);
367251662Sdim			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
368251662Sdim				(void)printf("\tblocks %ld -> %ld",
369251662Sdim					dqbuf.dqb_curblocks, fup->fu_curblocks);
370251662Sdim			(void)printf("\n");
371251662Sdim		}
372251662Sdim		/*
373251662Sdim		 * Reset time limit if have a soft limit and were
374251662Sdim		 * previously under it, but are now over it.
375251662Sdim		 */
376251662Sdim		if (dqbuf.dqb_bsoftlimit &&
377276479Sdim		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
378218887Sdim		    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
379218887Sdim			dqbuf.dqb_btime = 0;
380218887Sdim		if (dqbuf.dqb_isoftlimit &&
381218887Sdim		    dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
382218887Sdim		    fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
383218887Sdim			dqbuf.dqb_itime = 0;
384218887Sdim		dqbuf.dqb_curinodes = fup->fu_curinodes;
385218887Sdim		dqbuf.dqb_curblocks = fup->fu_curblocks;
386218887Sdim		if (fseek(qfo, (long)offset, SEEK_SET) < 0) {
387218887Sdim			(void) fprintf(stderr,
388218887Sdim		    	    "quotacheck: %s: seek failed: %s\n",
389218887Sdim			    quotafile, strerror(errno));
390218887Sdim			    return(1);
391218887Sdim		}
392276479Sdim		fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
393251662Sdim		(void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
394218887Sdim		    (caddr_t)&dqbuf);
395218887Sdim		fup->fu_curinodes = 0;
396218887Sdim		fup->fu_curblocks = 0;
397226633Sdim	}
398218887Sdim	fclose(qfi);
399218887Sdim	fflush(qfo);
400218887Sdim	ftruncate(fileno(qfo),
401218887Sdim	    (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
402251662Sdim	fclose(qfo);
403251662Sdim	return (0);
404234353Sdim}
405234353Sdim
406234353Sdim/*
407234353Sdim * Check to see if target appears in list of size cnt.
408234353Sdim */
409234353Sdimint
410251662Sdimoneof(target, list, cnt)
411234353Sdim	register char *target, *list[];
412276479Sdim	int cnt;
413234353Sdim{
414234353Sdim	register int i;
415234353Sdim
416234353Sdim	for (i = 0; i < cnt; i++)
417234353Sdim		if (strcmp(target, list[i]) == 0)
418234353Sdim			return (i);
419234353Sdim	return (-1);
420234353Sdim}
421234353Sdim
422234353Sdim/*
423234353Sdim * Determine the group identifier for quota files.
424234353Sdim */
425234353Sdimint
426234353Sdimgetquotagid()
427276479Sdim{
428251662Sdim	struct group *gr;
429234353Sdim
430234353Sdim	if ((gr = getgrnam(quotagroup)) != NULL)
431234353Sdim		return (gr->gr_gid);
432234353Sdim	return (-1);
433234353Sdim}
434234353Sdim
435234353Sdim/*
436234353Sdim * Check to see if a particular quota is to be enabled.
437251662Sdim */
438251662Sdimint
439218887Sdimhasquota(fs, type, qfnamep)
440218887Sdim	register struct fstab *fs;
441218887Sdim	int type;
442218887Sdim	char **qfnamep;
443218887Sdim{
444218887Sdim	register char *opt;
445251662Sdim	char *cp;
446218887Sdim	static char initname, usrname[100], grpname[100];
447218887Sdim	static char buf[BUFSIZ];
448218887Sdim
449218887Sdim	if (!initname) {
450276479Sdim		(void)snprintf(usrname, sizeof(usrname),
451218887Sdim		    "%s%s", qfextension[USRQUOTA], qfname);
452218887Sdim		(void)snprintf(grpname, sizeof(grpname),
453218887Sdim		    "%s%s", qfextension[GRPQUOTA], qfname);
454218887Sdim		initname = 1;
455218887Sdim	}
456218887Sdim	strcpy(buf, fs->fs_mntops);
457218887Sdim	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
458218887Sdim		if ((cp = index(opt, '=')) != NULL)
459218887Sdim			*cp++ = '\0';
460218887Sdim		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
461276479Sdim			break;
462251662Sdim		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
463218887Sdim			break;
464218887Sdim	}
465218887Sdim	if (!opt)
466226633Sdim		return (0);
467218887Sdim	if (cp)
468218887Sdim		*qfnamep = cp;
469218887Sdim	else {
470218887Sdim		(void)snprintf(buf, sizeof(buf),
471218887Sdim		    "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
472218887Sdim		*qfnamep = buf;
473226633Sdim	}
474226633Sdim	return (1);
475218887Sdim}
476226633Sdim
477226633Sdim/*
478226633Sdim * Routines to manage the file usage table.
479218887Sdim *
480218887Sdim * Lookup an id of a specific type.
481218887Sdim */
482226633Sdimstruct fileusage *
483218887Sdimlookup(id, type)
484218887Sdim	u_long id;
485226633Sdim	int type;
486218887Sdim{
487226633Sdim	register struct fileusage *fup;
488226633Sdim
489218887Sdim	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
490218887Sdim		if (fup->fu_id == id)
491218887Sdim			return (fup);
492218887Sdim	return (NULL);
493218887Sdim}
494226633Sdim
495226633Sdim/*
496218887Sdim * Add a new file usage id if it does not already exist.
497243830Sdim */
498243830Sdimstruct fileusage *
499243830Sdimaddid(id, type, name)
500243830Sdim	u_long id;
501276479Sdim	int type;
502218887Sdim	char *name;
503243830Sdim{
504243830Sdim	struct fileusage *fup, **fhp;
505243830Sdim	int len;
506276479Sdim
507243830Sdim	if ((fup = lookup(id, type)) != NULL)
508218887Sdim		return (fup);
509218887Sdim	if (name)
510218887Sdim		len = strlen(name);
511226633Sdim	else
512218887Sdim		len = 10;
513218887Sdim	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
514218887Sdim		err("%s", strerror(errno));
515226633Sdim	fhp = &fuhead[type][id & (FUHASH - 1)];
516226633Sdim	fup->fu_next = *fhp;
517226633Sdim	*fhp = fup;
518226633Sdim	fup->fu_id = id;
519276479Sdim	if (id > highid[type])
520218887Sdim		highid[type] = id;
521276479Sdim	if (name)
522218887Sdim		bcopy(name, fup->fu_name, len + 1);
523234353Sdim	else {
524234353Sdim		(void)sprintf(fup->fu_name, "%lu", id);
525234353Sdim		if (vflag)
526218887Sdim			printf("unknown %cid: %lu\n",
527218887Sdim			    type == USRQUOTA ? 'u' : 'g', id);
528218887Sdim	}
529218887Sdim	return (fup);
530218887Sdim}
531218887Sdim
532218887Sdim/*
533218887Sdim * Special purpose version of ginode used to optimize pass
534234353Sdim * over all the inodes in numerical order.
535234353Sdim */
536234353Sdimino_t nextino, lastinum;
537234353Sdimlong readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
538218887Sdimstruct dinode *inodebuf;
539218887Sdim#define	INOBUFSIZE	56*1024	/* size of buffer to read inodes */
540218887Sdim
541218887Sdimstruct dinode *
542243830Sdimgetnextinode(inumber)
543218887Sdim	ino_t inumber;
544218887Sdim{
545226633Sdim	long size;
546226633Sdim	daddr_t dblk;
547226633Sdim	static struct dinode *dp;
548226633Sdim
549226633Sdim	if (inumber != nextino++ || inumber > maxino)
550226633Sdim		err("bad inode number %d to nextinode", inumber);
551226633Sdim	if (inumber >= lastinum) {
552218887Sdim		readcnt++;
553218887Sdim		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
554218887Sdim		if (readcnt % readpercg == 0) {
555218887Sdim			size = partialsize;
556239462Sdim			lastinum += partialcnt;
557218887Sdim		} else {
558226633Sdim			size = inobufsize;
559226633Sdim			lastinum += fullcnt;
560226633Sdim		}
561226633Sdim		bread(dblk, (char *)inodebuf, size);
562218887Sdim		dp = inodebuf;
563226633Sdim	}
564226633Sdim	return (dp++);
565226633Sdim}
566226633Sdim
567226633Sdim/*
568226633Sdim * Prepare to scan a set of inodes.
569226633Sdim */
570226633Sdimvoid
571226633Sdimresetinodebuf()
572226633Sdim{
573239462Sdim
574218887Sdim	nextino = 0;
575218887Sdim	lastinum = 0;
576226633Sdim	readcnt = 0;
577226633Sdim	inobufsize = blkroundup(&sblock, INOBUFSIZE);
578218887Sdim	fullcnt = inobufsize / sizeof(struct dinode);
579218887Sdim	readpercg = sblock.fs_ipg / fullcnt;
580239462Sdim	partialcnt = sblock.fs_ipg % fullcnt;
581239462Sdim	partialsize = partialcnt * sizeof(struct dinode);
582239462Sdim	if (partialcnt != 0) {
583239462Sdim		readpercg++;
584239462Sdim	} else {
585243830Sdim		partialcnt = fullcnt;
586243830Sdim		partialsize = inobufsize;
587243830Sdim	}
588226633Sdim	if (inodebuf == NULL &&
589243830Sdim	   (inodebuf = malloc((u_int)inobufsize)) == NULL)
590276479Sdim		err("%s", strerror(errno));
591218887Sdim	while (nextino < ROOTINO)
592218887Sdim		getnextinode(nextino);
593218887Sdim}
594218887Sdim
595218887Sdim/*
596218887Sdim * Free up data structures used to scan inodes.
597226633Sdim */
598234353Sdimvoid
599226633Sdimfreeinodebuf()
600218887Sdim{
601226633Sdim
602226633Sdim	if (inodebuf != NULL)
603226633Sdim		free(inodebuf);
604226633Sdim	inodebuf = NULL;
605226633Sdim}
606218887Sdim
607218887Sdim/*
608226633Sdim * Read specified disk blocks.
609226633Sdim */
610226633Sdimvoid
611226633Sdimbread(bno, buf, cnt)
612226633Sdim	daddr_t bno;
613226633Sdim	char *buf;
614226633Sdim	long cnt;
615218887Sdim{
616218887Sdim
617226633Sdim	if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
618226633Sdim	    read(fi, buf, cnt) != cnt)
619226633Sdim		err("block %ld", bno);
620226633Sdim}
621218887Sdim
622218887Sdim#if __STDC__
623226633Sdim#include <stdarg.h>
624218887Sdim#else
625218887Sdim#include <varargs.h>
626218887Sdim#endif
627218887Sdim
628218887Sdimvoid
629218887Sdim#if __STDC__
630226633Sdimerr(const char *fmt, ...)
631226633Sdim#else
632226633Sdimerr(fmt, va_alist)
633226633Sdim	char *fmt;
634218887Sdim        va_dcl
635226633Sdim#endif
636226633Sdim{
637226633Sdim	va_list ap;
638226633Sdim#if __STDC__
639218887Sdim	va_start(ap, fmt);
640218887Sdim#else
641218887Sdim	va_start(ap);
642226633Sdim#endif
643226633Sdim	(void)fprintf(stderr, "quotacheck: ");
644226633Sdim	(void)vfprintf(stderr, fmt, ap);
645226633Sdim	va_end(ap);
646226633Sdim	(void)fprintf(stderr, "\n");
647226633Sdim	exit(1);
648226633Sdim	/* NOTREACHED */
649226633Sdim}
650226633Sdim
651226633Sdim