quotacheck.c revision 164071
1298192Sdelphij/*
2298192Sdelphij * Copyright (c) 1980, 1990, 1993
3298192Sdelphij *	The Regents of the University of California.  All rights reserved.
4298192Sdelphij *
5298192Sdelphij * This code is derived from software contributed to Berkeley by
6298192Sdelphij * Robert Elz at The University of Melbourne.
7298192Sdelphij *
8298192Sdelphij * Redistribution and use in source and binary forms, with or without
9298192Sdelphij * modification, are permitted provided that the following conditions
10298192Sdelphij * are met:
11298192Sdelphij * 1. Redistributions of source code must retain the above copyright
12298192Sdelphij *    notice, this list of conditions and the following disclaimer.
13298192Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
14298192Sdelphij *    notice, this list of conditions and the following disclaimer in the
15298192Sdelphij *    documentation and/or other materials provided with the distribution.
16298192Sdelphij * 4. Neither the name of the University nor the names of its contributors
17298192Sdelphij *    may be used to endorse or promote products derived from this software
18298192Sdelphij *    without specific prior written permission.
19298192Sdelphij *
20298192Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21298192Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22298192Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23298192Sdelphij * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24298192Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25298192Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26298192Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27298192Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28298192Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29298192Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30298192Sdelphij * SUCH DAMAGE.
31298192Sdelphij */
32298192Sdelphij
33298192Sdelphij#if 0
34298192Sdelphij#ifndef lint
35298192Sdelphijstatic const char copyright[] =
36298192Sdelphij"@(#) Copyright (c) 1980, 1990, 1993\n\
37298192Sdelphij	The Regents of the University of California.  All rights reserved.\n";
38298192Sdelphij#endif /* not lint */
39298192Sdelphij
40298192Sdelphij#ifndef lint
41298192Sdelphijstatic char sccsid[] = "@(#)quotacheck.c	8.3 (Berkeley) 1/29/94";
42298192Sdelphij#endif /* not lint */
43298192Sdelphij#endif
44298192Sdelphij#include <sys/cdefs.h>
45298192Sdelphij__FBSDID("$FreeBSD: head/sbin/quotacheck/quotacheck.c 164071 2006-11-07 19:07:52Z ceri $");
46298192Sdelphij
47288143Sdelphij/*
48288143Sdelphij * Fix up / report on disk quotas & usage
49288143Sdelphij */
50288143Sdelphij#include <sys/param.h>
51288143Sdelphij#include <sys/disklabel.h>
52288143Sdelphij#include <sys/stat.h>
53288143Sdelphij
54288143Sdelphij#include <ufs/ufs/dinode.h>
55288143Sdelphij#include <ufs/ufs/quota.h>
56288143Sdelphij#include <ufs/ffs/fs.h>
57288143Sdelphij
58288143Sdelphij#include <err.h>
59288143Sdelphij#include <errno.h>
60288143Sdelphij#include <fcntl.h>
61288143Sdelphij#include <fstab.h>
62288143Sdelphij#include <grp.h>
63287453Sdelphij#include <pwd.h>
64287453Sdelphij#include <stdio.h>
65287453Sdelphij#include <stdlib.h>
66287453Sdelphij#include <string.h>
67284277Sdelphij#include <unistd.h>
68284237Sdelphij
69284277Sdelphijchar *qfname = QUOTAFILENAME;
70284277Sdelphijchar *qfextension[] = INITQFNAMES;
71284237Sdelphijchar *quotagroup = QUOTAGROUP;
72284237Sdelphij
73284237Sdelphijunion {
74284237Sdelphij	struct	fs	sblk;
75284237Sdelphij	char	dummy[MAXBSIZE];
76284237Sdelphij} sb_un;
77284237Sdelphij#define	sblock	sb_un.sblk
78284237Sdelphijunion {
79284237Sdelphij	struct	cg	cgblk;
80284237Sdelphij	char	dummy[MAXBSIZE];
81284237Sdelphij} cg_un;
82284237Sdelphij#define	cgblk	cg_un.cgblk
83284237Sdelphijlong dev_bsize = 1;
84284237Sdelphijino_t maxino;
85284237Sdelphij
86284237Sdelphijunion dinode {
87284237Sdelphij	struct ufs1_dinode dp1;
88284237Sdelphij	struct ufs2_dinode dp2;
89284237Sdelphij};
90284237Sdelphij#define	DIP(dp, field) \
91284237Sdelphij	((sblock.fs_magic == FS_UFS1_MAGIC) ? \
92284237Sdelphij	(dp)->dp1.field : (dp)->dp2.field)
93284237Sdelphij
94284237Sdelphijstruct quotaname {
95284237Sdelphij	long	flags;
96284237Sdelphij	char	grpqfname[PATH_MAX];
97284237Sdelphij	char	usrqfname[PATH_MAX];
98284237Sdelphij};
99284237Sdelphij#define	HASUSR	1
100284237Sdelphij#define	HASGRP	2
101284237Sdelphij
102284237Sdelphijstruct fileusage {
103284237Sdelphij	struct	fileusage *fu_next;
104284237Sdelphij	u_long	fu_curinodes;
105284237Sdelphij	u_long	fu_curblocks;
106284237Sdelphij	u_long	fu_id;
107284237Sdelphij	char	fu_name[1];
108284237Sdelphij	/* actually bigger */
109284237Sdelphij};
110284237Sdelphij#define FUHASH 1024	/* must be power of two */
111284237Sdelphijstruct fileusage *fuhead[MAXQUOTAS][FUHASH];
112284237Sdelphij
113284237Sdelphijint	aflag;			/* all file systems */
114284237Sdelphijint	gflag;			/* check group quotas */
115284237Sdelphijint	uflag;			/* check user quotas */
116284237Sdelphijint	vflag;			/* verbose */
117284237Sdelphijint	fi;			/* open disk file descriptor */
118284237Sdelphiju_long	highid[MAXQUOTAS];	/* highest addid()'ed identifier per type */
119284237Sdelphij
120276577Sdelphijstruct fileusage *
121276577Sdelphij	 addid(u_long, int, char *);
122276577Sdelphijchar	*blockcheck(char *);
123276577Sdelphijvoid	 bread(ufs2_daddr_t, char *, long);
124276577Sdelphijextern int checkfstab(int, int, void * (*)(struct fstab *),
125276577Sdelphij				int (*)(char *, char *, struct quotaname *));
126276577Sdelphijint	 chkquota(char *, char *, struct quotaname *);
127276577Sdelphijvoid	 freeinodebuf(void);
128276577Sdelphijunion dinode *
129276577Sdelphij	 getnextinode(ino_t);
130276577Sdelphijint	 getquotagid(void);
131276577Sdelphijint	 hasquota(struct fstab *, int, char **);
132276577Sdelphijstruct fileusage *
133276577Sdelphij	 lookup(u_long, int);
134276577Sdelphijvoid	*needchk(struct fstab *);
135276577Sdelphijint	 oneof(char *, char*[], int);
136276577Sdelphijvoid	 setinodebuf(ino_t);
137276577Sdelphijint	 update(char *, char *, int);
138276577Sdelphijvoid	 usage(void);
139276577Sdelphij
140276577Sdelphijint
141276577Sdelphijmain(argc, argv)
142276577Sdelphij	int argc;
143276577Sdelphij	char *argv[];
144276577Sdelphij{
145276577Sdelphij	struct fstab *fs;
146275698Sdelphij	struct passwd *pw;
147275698Sdelphij	struct group *gr;
148275698Sdelphij	struct quotaname *auxdata;
149275698Sdelphij	int i, argnum, maxrun, errs, ch;
150275698Sdelphij	long done = 0;
151275698Sdelphij	char *name;
152275698Sdelphij
153275698Sdelphij	errs = maxrun = 0;
154275698Sdelphij	while ((ch = getopt(argc, argv, "aguvl:")) != -1) {
155275698Sdelphij		switch(ch) {
156275698Sdelphij		case 'a':
157275698Sdelphij			aflag++;
158275698Sdelphij			break;
159275698Sdelphij		case 'g':
160275698Sdelphij			gflag++;
161275698Sdelphij			break;
162275698Sdelphij		case 'u':
163275698Sdelphij			uflag++;
164275698Sdelphij			break;
165275698Sdelphij		case 'v':
166275698Sdelphij			vflag++;
167275698Sdelphij			break;
168275698Sdelphij		case 'l':
169275698Sdelphij			maxrun = atoi(optarg);
170275698Sdelphij			break;
171276577Sdelphij		default:
172276577Sdelphij			usage();
173275698Sdelphij		}
174275698Sdelphij	}
175275698Sdelphij	argc -= optind;
176275698Sdelphij	argv += optind;
177275698Sdelphij	if ((argc == 0 && !aflag) || (argc > 0 && aflag))
178275698Sdelphij		usage();
179275698Sdelphij	if (!gflag && !uflag) {
180275698Sdelphij		gflag++;
181275698Sdelphij		uflag++;
182275698Sdelphij	}
183275698Sdelphij	if (gflag) {
184275698Sdelphij		setgrent();
185275698Sdelphij		while ((gr = getgrent()) != NULL)
186275698Sdelphij			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
187275698Sdelphij		endgrent();
188275698Sdelphij	}
189275698Sdelphij	if (uflag) {
190275698Sdelphij		setpwent();
191275698Sdelphij		while ((pw = getpwent()) != NULL)
192275698Sdelphij			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
193275698Sdelphij		endpwent();
194275698Sdelphij	}
195275698Sdelphij	/*
196275698Sdelphij	 * Setting maxrun (-l) makes no sense without the -a flag.
197275698Sdelphij	 * Historically this was never an error, so we just warn.
198275698Sdelphij	 */
199275698Sdelphij	if (maxrun > 0 && !aflag)
200275698Sdelphij		warnx("ignoring -l without -a");
201275698Sdelphij	if (aflag)
202275698Sdelphij		exit(checkfstab(1, maxrun, needchk, chkquota));
203275698Sdelphij	if (setfsent() == 0)
204275698Sdelphij		errx(1, "%s: can't open", FSTAB);
205275698Sdelphij	while ((fs = getfsent()) != NULL) {
206275698Sdelphij		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
207275698Sdelphij		    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
208275698Sdelphij		    (auxdata = needchk(fs)) &&
209275698Sdelphij		    (name = blockcheck(fs->fs_spec))) {
210275698Sdelphij			done |= 1 << argnum;
211275698Sdelphij			errs += chkquota(name, fs->fs_file, auxdata);
212275698Sdelphij		}
213275698Sdelphij	}
214267897Sdelphij	endfsent();
215267897Sdelphij	for (i = 0; i < argc; i++)
216267897Sdelphij		if ((done & (1 << i)) == 0)
217267897Sdelphij			fprintf(stderr, "%s not found in %s\n",
218267897Sdelphij				argv[i], FSTAB);
219267897Sdelphij	exit(errs);
220267897Sdelphij}
221267897Sdelphij
222267897Sdelphijvoid
223267897Sdelphijusage()
224267897Sdelphij{
225267897Sdelphij	(void)fprintf(stderr, "%s\n%s\n",
226267897Sdelphij		"usage: quotacheck [-guv] [-l maxrun] -a",
227267897Sdelphij		"       quotacheck [-guv] filesystem ...");
228267897Sdelphij	exit(1);
229267897Sdelphij}
230267897Sdelphij
231267897Sdelphijvoid *
232267897Sdelphijneedchk(fs)
233267897Sdelphij	struct fstab *fs;
234267897Sdelphij{
235267897Sdelphij	struct quotaname *qnp;
236267897Sdelphij	char *qfnp;
237267897Sdelphij
238267897Sdelphij	if (strcmp(fs->fs_vfstype, "ufs") ||
239267897Sdelphij	    strcmp(fs->fs_type, FSTAB_RW))
240267897Sdelphij		return (NULL);
241267897Sdelphij	if ((qnp = malloc(sizeof(*qnp))) == NULL)
242267897Sdelphij		errx(1, "malloc failed");
243267897Sdelphij	qnp->flags = 0;
244267897Sdelphij	if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
245267897Sdelphij		strcpy(qnp->grpqfname, qfnp);
246267897Sdelphij		qnp->flags |= HASGRP;
247267897Sdelphij	}
248267897Sdelphij	if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
249267897Sdelphij		strcpy(qnp->usrqfname, qfnp);
250267897Sdelphij		qnp->flags |= HASUSR;
251267897Sdelphij	}
252267897Sdelphij	if (qnp->flags)
253267897Sdelphij		return (qnp);
254267897Sdelphij	free(qnp);
255267897Sdelphij	return (NULL);
256267897Sdelphij}
257267897Sdelphij
258267897Sdelphij/*
259267897Sdelphij * Possible superblock locations ordered from most to least likely.
260267897Sdelphij */
261267897Sdelphijstatic int sblock_try[] = SBLOCKSEARCH;
262267897Sdelphij
263267897Sdelphij/*
264267897Sdelphij * Scan the specified file system to check quota(s) present on it.
265267897Sdelphij */
266267897Sdelphijint
267267897Sdelphijchkquota(fsname, mntpt, qnp)
268267897Sdelphij	char *fsname, *mntpt;
269267897Sdelphij	struct quotaname *qnp;
270267897Sdelphij{
271267897Sdelphij	struct fileusage *fup;
272267897Sdelphij	union dinode *dp;
273267897Sdelphij	int cg, i, mode, errs = 0;
274267897Sdelphij	ino_t ino, inosused;
275267897Sdelphij	char *cp;
276267897Sdelphij
277267897Sdelphij	if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
278267897Sdelphij		warn("%s", fsname);
279267897Sdelphij		return (1);
280267897Sdelphij	}
281267897Sdelphij	if (vflag) {
282267897Sdelphij		(void)printf("*** Checking ");
283267897Sdelphij		if (qnp->flags & HASUSR)
284267897Sdelphij			(void)printf("%s%s", qfextension[USRQUOTA],
285267897Sdelphij			    (qnp->flags & HASGRP) ? " and " : "");
286267897Sdelphij		if (qnp->flags & HASGRP)
287267897Sdelphij			(void)printf("%s", qfextension[GRPQUOTA]);
288267897Sdelphij		(void)printf(" quotas for %s (%s)\n", fsname, mntpt);
289267897Sdelphij	}
290267897Sdelphij	sync();
291267897Sdelphij	dev_bsize = 1;
292267897Sdelphij	for (i = 0; sblock_try[i] != -1; i++) {
293267897Sdelphij		bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE);
294267897Sdelphij		if ((sblock.fs_magic == FS_UFS1_MAGIC ||
295267897Sdelphij		     (sblock.fs_magic == FS_UFS2_MAGIC &&
296267897Sdelphij		      sblock.fs_sblockloc == sblock_try[i])) &&
297267897Sdelphij		    sblock.fs_bsize <= MAXBSIZE &&
298267897Sdelphij		    sblock.fs_bsize >= sizeof(struct fs))
299267897Sdelphij			break;
300267897Sdelphij	}
301267897Sdelphij	if (sblock_try[i] == -1) {
302267897Sdelphij		warn("Cannot find file system superblock");
303267897Sdelphij		return (1);
304267897Sdelphij	}
305267897Sdelphij	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
306267897Sdelphij	maxino = sblock.fs_ncg * sblock.fs_ipg;
307267897Sdelphij	for (cg = 0; cg < sblock.fs_ncg; cg++) {
308267897Sdelphij		ino = cg * sblock.fs_ipg;
309267897Sdelphij		setinodebuf(ino);
310267897Sdelphij		bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk),
311267897Sdelphij		    sblock.fs_cgsize);
312267897Sdelphij		if (sblock.fs_magic == FS_UFS2_MAGIC)
313267897Sdelphij			inosused = cgblk.cg_initediblk;
314267897Sdelphij		else
315267897Sdelphij			inosused = sblock.fs_ipg;
316267897Sdelphij		/*
317267897Sdelphij		 * If we are using soft updates, then we can trust the
318267897Sdelphij		 * cylinder group inode allocation maps to tell us which
319267897Sdelphij		 * inodes are allocated. We will scan the used inode map
320267897Sdelphij		 * to find the inodes that are really in use, and then
321267897Sdelphij		 * read only those inodes in from disk.
322267897Sdelphij		 */
323267897Sdelphij		if (sblock.fs_flags & FS_DOSOFTDEP) {
324267897Sdelphij			if (!cg_chkmagic(&cgblk))
325267897Sdelphij				errx(1, "CG %d: BAD MAGIC NUMBER\n", cg);
326267897Sdelphij			cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT];
327267897Sdelphij			for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
328267897Sdelphij				if (*cp == 0)
329267897Sdelphij					continue;
330267897Sdelphij				for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
331267897Sdelphij					if (*cp & i)
332267897Sdelphij						break;
333267897Sdelphij					inosused--;
334267897Sdelphij				}
335267897Sdelphij				break;
336267897Sdelphij			}
337267897Sdelphij			if (inosused <= 0)
338267897Sdelphij				continue;
339267897Sdelphij		}
340267897Sdelphij		for (i = 0; i < inosused; i++, ino++) {
341267897Sdelphij			if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
342267897Sdelphij			    (mode = DIP(dp, di_mode) & IFMT) == 0)
343267897Sdelphij				continue;
344267897Sdelphij			if (qnp->flags & HASGRP) {
345267897Sdelphij				fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
346267897Sdelphij				    (char *)0);
347267897Sdelphij				fup->fu_curinodes++;
348267897Sdelphij				if (mode == IFREG || mode == IFDIR ||
349267897Sdelphij				    mode == IFLNK)
350267897Sdelphij					fup->fu_curblocks += DIP(dp, di_blocks);
351267897Sdelphij			}
352267897Sdelphij			if (qnp->flags & HASUSR) {
353267897Sdelphij				fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
354267897Sdelphij				    (char *)0);
355267897Sdelphij				fup->fu_curinodes++;
356267897Sdelphij				if (mode == IFREG || mode == IFDIR ||
357267897Sdelphij				    mode == IFLNK)
358267897Sdelphij					fup->fu_curblocks += DIP(dp, di_blocks);
359267897Sdelphij			}
360267897Sdelphij		}
361267897Sdelphij	}
362267897Sdelphij	freeinodebuf();
363267897Sdelphij	if (qnp->flags & HASUSR)
364267897Sdelphij		errs += update(mntpt, qnp->usrqfname, USRQUOTA);
365267897Sdelphij	if (qnp->flags & HASGRP)
366267897Sdelphij		errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
367267897Sdelphij	close(fi);
368267897Sdelphij	return (errs);
369267897Sdelphij}
370267897Sdelphij
371267897Sdelphij/*
372267897Sdelphij * Update a specified quota file.
373267897Sdelphij */
374267897Sdelphijint
375267897Sdelphijupdate(fsname, quotafile, type)
376267897Sdelphij	char *fsname, *quotafile;
377267897Sdelphij	int type;
378267897Sdelphij{
379267897Sdelphij	struct fileusage *fup;
380267897Sdelphij	FILE *qfi, *qfo;
381267897Sdelphij	u_long id, lastid;
382267897Sdelphij	off_t offset;
383267897Sdelphij	struct dqblk dqbuf;
384267897Sdelphij	static int warned = 0;
385267897Sdelphij	static struct dqblk zerodqbuf;
386267897Sdelphij	static struct fileusage zerofileusage;
387267897Sdelphij
388267897Sdelphij	if ((qfo = fopen(quotafile, "r+")) == NULL) {
389267897Sdelphij		if (errno == ENOENT)
390267897Sdelphij			qfo = fopen(quotafile, "w+");
391267897Sdelphij		if (qfo) {
392267897Sdelphij			warnx("creating quota file %s", quotafile);
393267897Sdelphij#define	MODE	(S_IRUSR|S_IWUSR|S_IRGRP)
394267897Sdelphij			(void) fchown(fileno(qfo), getuid(), getquotagid());
395267897Sdelphij			(void) fchmod(fileno(qfo), MODE);
396267897Sdelphij		} else {
397267897Sdelphij			warn("%s", quotafile);
398267897Sdelphij			return (1);
399267897Sdelphij		}
400267897Sdelphij	}
401267897Sdelphij	if ((qfi = fopen(quotafile, "r")) == NULL) {
402267897Sdelphij		warn("%s", quotafile);
403267897Sdelphij		(void) fclose(qfo);
404267897Sdelphij		return (1);
405267897Sdelphij	}
406267897Sdelphij	if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
407267897Sdelphij	    errno == EOPNOTSUPP && !warned && vflag) {
408267897Sdelphij		warned++;
409267897Sdelphij		(void)printf("*** Warning: %s\n",
410267897Sdelphij		    "Quotas are not compiled into this kernel");
411267897Sdelphij	}
412267897Sdelphij	for (lastid = highid[type], id = 0, offset = 0; id <= lastid;
413267897Sdelphij	    id++, offset += sizeof(struct dqblk)) {
414267897Sdelphij		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
415267897Sdelphij			dqbuf = zerodqbuf;
416267897Sdelphij		if ((fup = lookup(id, type)) == 0)
417267897Sdelphij			fup = &zerofileusage;
418267897Sdelphij		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
419267897Sdelphij		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
420267897Sdelphij			fup->fu_curinodes = 0;
421267897Sdelphij			fup->fu_curblocks = 0;
422267897Sdelphij			continue;
423267897Sdelphij		}
424267897Sdelphij		if (vflag) {
425267897Sdelphij			if (aflag)
426267897Sdelphij				printf("%s: ", fsname);
427267897Sdelphij			printf("%-8s fixed:", fup->fu_name);
428267897Sdelphij			if (dqbuf.dqb_curinodes != fup->fu_curinodes)
429267897Sdelphij				(void)printf("\tinodes %lu -> %lu",
430267897Sdelphij				    (u_long)dqbuf.dqb_curinodes,
431267897Sdelphij				    (u_long)fup->fu_curinodes);
432267897Sdelphij			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
433267897Sdelphij				(void)printf("\tblocks %lu -> %lu",
434267897Sdelphij				    (u_long)dqbuf.dqb_curblocks,
435267897Sdelphij				    (u_long)fup->fu_curblocks);
436267897Sdelphij			(void)printf("\n");
437267897Sdelphij		}
438267897Sdelphij		/*
439267897Sdelphij		 * Reset time limit if have a soft limit and were
440267897Sdelphij		 * previously under it, but are now over it.
441267897Sdelphij		 */
442267897Sdelphij		if (dqbuf.dqb_bsoftlimit &&
443267897Sdelphij		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
444267897Sdelphij		    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
445267897Sdelphij			dqbuf.dqb_btime = 0;
446267897Sdelphij		if (dqbuf.dqb_isoftlimit &&
447267897Sdelphij		    dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
448267897Sdelphij		    fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
449267897Sdelphij			dqbuf.dqb_itime = 0;
450267897Sdelphij		dqbuf.dqb_curinodes = fup->fu_curinodes;
451267897Sdelphij		dqbuf.dqb_curblocks = fup->fu_curblocks;
452267897Sdelphij		if (fseek(qfo, offset, SEEK_SET) < 0) {
453267897Sdelphij			warn("%s: seek failed", quotafile);
454267897Sdelphij			return(1);
455267897Sdelphij		}
456234449Sobrien		fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
457234449Sobrien		(void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
458234449Sobrien		    (caddr_t)&dqbuf);
459234449Sobrien		fup->fu_curinodes = 0;
460234449Sobrien		fup->fu_curblocks = 0;
461234449Sobrien	}
462234449Sobrien	fclose(qfi);
463234449Sobrien	fflush(qfo);
464234449Sobrien	ftruncate(fileno(qfo),
465234449Sobrien	    (((off_t)highid[type] + 1) * sizeof(struct dqblk)));
466234449Sobrien	fclose(qfo);
467234449Sobrien	return (0);
468234449Sobrien}
469234449Sobrien
470234449Sobrien/*
471234449Sobrien * Check to see if target appears in list of size cnt.
472234449Sobrien */
473234449Sobrienint
474234449Sobrienoneof(target, list, cnt)
475234449Sobrien	char *target, *list[];
476234449Sobrien	int cnt;
477234449Sobrien{
478234449Sobrien	int i;
479234449Sobrien
480234449Sobrien	for (i = 0; i < cnt; i++)
481234449Sobrien		if (strcmp(target, list[i]) == 0)
482234449Sobrien			return (i);
483234449Sobrien	return (-1);
484234449Sobrien}
485234449Sobrien
486234449Sobrien/*
487234449Sobrien * Determine the group identifier for quota files.
488234449Sobrien */
489234449Sobrienint
490234449Sobriengetquotagid()
491234449Sobrien{
492234449Sobrien	struct group *gr;
493234449Sobrien
494234449Sobrien	if ((gr = getgrnam(quotagroup)) != NULL)
495234449Sobrien		return (gr->gr_gid);
496234449Sobrien	return (-1);
497234449Sobrien}
498234449Sobrien
499234449Sobrien/*
500234449Sobrien * Check to see if a particular quota is to be enabled.
501234449Sobrien */
502234449Sobrienint
503234449Sobrienhasquota(fs, type, qfnamep)
504234449Sobrien	struct fstab *fs;
505234449Sobrien	int type;
506234449Sobrien	char **qfnamep;
507234449Sobrien{
508234449Sobrien	char *opt;
509234449Sobrien	char *cp;
510234449Sobrien	static char initname, usrname[100], grpname[100];
511234449Sobrien	static char buf[BUFSIZ];
512234449Sobrien
513234449Sobrien	if (!initname) {
514234449Sobrien		(void)snprintf(usrname, sizeof(usrname),
515234449Sobrien		    "%s%s", qfextension[USRQUOTA], qfname);
516234449Sobrien		(void)snprintf(grpname, sizeof(grpname),
517234449Sobrien		    "%s%s", qfextension[GRPQUOTA], qfname);
518234449Sobrien		initname = 1;
519234449Sobrien	}
520234449Sobrien	strcpy(buf, fs->fs_mntops);
521234449Sobrien	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
522234449Sobrien		if ((cp = index(opt, '=')) != NULL)
523234449Sobrien			*cp++ = '\0';
524234449Sobrien		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
525234449Sobrien			break;
526234449Sobrien		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
527234449Sobrien			break;
528234449Sobrien	}
529234449Sobrien	if (!opt)
530234449Sobrien		return (0);
531234449Sobrien	if (cp)
532234449Sobrien		*qfnamep = cp;
533234449Sobrien	else {
534234449Sobrien		(void)snprintf(buf, sizeof(buf),
535234449Sobrien		    "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
536234449Sobrien		*qfnamep = buf;
537234449Sobrien	}
538234449Sobrien	return (1);
539234449Sobrien}
540234449Sobrien
541234449Sobrien/*
542234449Sobrien * Routines to manage the file usage table.
543234449Sobrien *
544234449Sobrien * Lookup an id of a specific type.
545234449Sobrien */
546234449Sobrienstruct fileusage *
547234449Sobrienlookup(id, type)
548234449Sobrien	u_long id;
549234449Sobrien	int type;
550234449Sobrien{
551234449Sobrien	struct fileusage *fup;
552234449Sobrien
553234449Sobrien	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
554234449Sobrien		if (fup->fu_id == id)
555234449Sobrien			return (fup);
556234449Sobrien	return (NULL);
557234449Sobrien}
558234449Sobrien
559234449Sobrien/*
560234449Sobrien * Add a new file usage id if it does not already exist.
561234449Sobrien */
562234449Sobrienstruct fileusage *
563234449Sobrienaddid(id, type, name)
564234449Sobrien	u_long id;
565234449Sobrien	int type;
566234449Sobrien	char *name;
567234449Sobrien{
568234449Sobrien	struct fileusage *fup, **fhp;
569234449Sobrien	int len;
570234449Sobrien
571234449Sobrien	if ((fup = lookup(id, type)) != NULL)
572234449Sobrien		return (fup);
573234449Sobrien	if (name)
574234449Sobrien		len = strlen(name);
575234449Sobrien	else
576234449Sobrien		len = 10;
577234449Sobrien	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
578234449Sobrien		errx(1, "calloc failed");
579234449Sobrien	fhp = &fuhead[type][id & (FUHASH - 1)];
580234449Sobrien	fup->fu_next = *fhp;
581234449Sobrien	*fhp = fup;
582234449Sobrien	fup->fu_id = id;
583234449Sobrien	if (id > highid[type])
584234449Sobrien		highid[type] = id;
585234449Sobrien	if (name)
586234449Sobrien		bcopy(name, fup->fu_name, len + 1);
587234449Sobrien	else {
588234449Sobrien		(void)sprintf(fup->fu_name, "%lu", id);
589234449Sobrien		if (vflag)
590234449Sobrien			printf("unknown %cid: %lu\n",
591234449Sobrien			    type == USRQUOTA ? 'u' : 'g', id);
592234449Sobrien	}
593234449Sobrien	return (fup);
594234449Sobrien}
595234449Sobrien
596234449Sobrien/*
597234449Sobrien * Special purpose version of ginode used to optimize pass
598234449Sobrien * over all the inodes in numerical order.
599234449Sobrien */
600234449Sobrienstatic ino_t nextino, lastinum, lastvalidinum;
601234449Sobrienstatic long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
602234449Sobrienstatic caddr_t inodebuf;
603234449Sobrien#define INOBUFSIZE	56*1024		/* size of buffer to read inodes */
604234449Sobrien
605234449Sobrienunion dinode *
606234449Sobriengetnextinode(ino_t inumber)
607234449Sobrien{
608234449Sobrien	long size;
609234449Sobrien	ufs2_daddr_t dblk;
610234449Sobrien	union dinode *dp;
611234449Sobrien	static caddr_t nextinop;
612234449Sobrien
613234449Sobrien	if (inumber != nextino++ || inumber > lastvalidinum)
614234449Sobrien		errx(1, "bad inode number %d to nextinode", inumber);
615234449Sobrien	if (inumber >= lastinum) {
616234449Sobrien		readcnt++;
617234449Sobrien		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
618234449Sobrien		if (readcnt % readpercg == 0) {
619234449Sobrien			size = partialsize;
620234449Sobrien			lastinum += partialcnt;
621234449Sobrien		} else {
622234449Sobrien			size = inobufsize;
623234449Sobrien			lastinum += fullcnt;
624234449Sobrien		}
625234449Sobrien		/*
626234449Sobrien		 * If bread returns an error, it will already have zeroed
627234449Sobrien		 * out the buffer, so we do not need to do so here.
628234449Sobrien		 */
629234449Sobrien		bread(dblk, inodebuf, size);
630234449Sobrien		nextinop = inodebuf;
631234449Sobrien	}
632234449Sobrien	dp = (union dinode *)nextinop;
633234449Sobrien	if (sblock.fs_magic == FS_UFS1_MAGIC)
634234449Sobrien		nextinop += sizeof(struct ufs1_dinode);
635234449Sobrien	else
636234449Sobrien		nextinop += sizeof(struct ufs2_dinode);
637234449Sobrien	return (dp);
638234449Sobrien}
639234449Sobrien
640234449Sobrien/*
641234449Sobrien * Prepare to scan a set of inodes.
642234449Sobrien */
643234449Sobrienvoid
644234449Sobriensetinodebuf(ino_t inum)
645234449Sobrien{
646234449Sobrien
647234449Sobrien	if (inum % sblock.fs_ipg != 0)
648234449Sobrien		errx(1, "bad inode number %d to setinodebuf", inum);
649234449Sobrien	lastvalidinum = inum + sblock.fs_ipg - 1;
650234449Sobrien	nextino = inum;
651234449Sobrien	lastinum = inum;
652234449Sobrien	readcnt = 0;
653234449Sobrien	if (inodebuf != NULL)
654234449Sobrien		return;
655234449Sobrien	inobufsize = blkroundup(&sblock, INOBUFSIZE);
656234449Sobrien	fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
657234449Sobrien	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
658234449Sobrien	readpercg = sblock.fs_ipg / fullcnt;
659234449Sobrien	partialcnt = sblock.fs_ipg % fullcnt;
660234449Sobrien	partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
661234449Sobrien	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
662234449Sobrien	if (partialcnt != 0) {
663234449Sobrien		readpercg++;
664234449Sobrien	} else {
665234449Sobrien		partialcnt = fullcnt;
666234449Sobrien		partialsize = inobufsize;
667234449Sobrien	}
668234449Sobrien	if ((inodebuf = malloc((unsigned)inobufsize)) == NULL)
669234449Sobrien		errx(1, "cannot allocate space for inode buffer");
670234449Sobrien}
671234449Sobrien
672234449Sobrien/*
673234449Sobrien * Free up data structures used to scan inodes.
674234449Sobrien */
675234449Sobrienvoid
676234449Sobrienfreeinodebuf()
677234449Sobrien{
678234449Sobrien
679234449Sobrien	if (inodebuf != NULL)
680234449Sobrien		free(inodebuf);
681234449Sobrien	inodebuf = NULL;
682234449Sobrien}
683234449Sobrien
684234449Sobrien/*
685234449Sobrien * Read specified disk blocks.
686234449Sobrien */
687234449Sobrienvoid
688234449Sobrienbread(bno, buf, cnt)
689234449Sobrien	ufs2_daddr_t bno;
690234449Sobrien	char *buf;
691234449Sobrien	long cnt;
692234449Sobrien{
693234449Sobrien
694234449Sobrien	if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
695234449Sobrien	    read(fi, buf, cnt) != cnt)
696234449Sobrien		errx(1, "bread failed on block %ld", (long)bno);
697234449Sobrien}
698234449Sobrien