11558Srgrimes/*
21558Srgrimes * Copyright (c) 1980, 1990, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Robert Elz at The University of Melbourne.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 4. Neither the name of the University nor the names of its contributors
171558Srgrimes *    may be used to endorse or promote products derived from this software
181558Srgrimes *    without specific prior written permission.
191558Srgrimes *
201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301558Srgrimes * SUCH DAMAGE.
311558Srgrimes */
321558Srgrimes
33114589Sobrien#if 0
341558Srgrimes#ifndef lint
3537672Scharnierstatic const char copyright[] =
361558Srgrimes"@(#) Copyright (c) 1980, 1990, 1993\n\
371558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381558Srgrimes#endif /* not lint */
391558Srgrimes
401558Srgrimes#ifndef lint
411558Srgrimesstatic char sccsid[] = "@(#)quotacheck.c	8.3 (Berkeley) 1/29/94";
42114589Sobrien#endif /* not lint */
4337672Scharnier#endif
44114589Sobrien#include <sys/cdefs.h>
45114589Sobrien__FBSDID("$FreeBSD: releng/10.3/sbin/quotacheck/quotacheck.c 241013 2012-09-27 23:31:06Z mdf $");
461558Srgrimes
471558Srgrimes/*
481558Srgrimes * Fix up / report on disk quotas & usage
491558Srgrimes */
501558Srgrimes#include <sys/param.h>
5196478Sphk#include <sys/disklabel.h>
52166485Smpp#include <sys/mount.h>
531558Srgrimes#include <sys/stat.h>
541558Srgrimes
551558Srgrimes#include <ufs/ufs/dinode.h>
561558Srgrimes#include <ufs/ufs/quota.h>
571558Srgrimes#include <ufs/ffs/fs.h>
581558Srgrimes
5937672Scharnier#include <err.h>
6037672Scharnier#include <errno.h>
611558Srgrimes#include <fcntl.h>
621558Srgrimes#include <fstab.h>
6337672Scharnier#include <grp.h>
64207736Smckusick#include <libutil.h>
651558Srgrimes#include <pwd.h>
66241013Smdf#include <stdint.h>
671558Srgrimes#include <stdio.h>
681558Srgrimes#include <stdlib.h>
691558Srgrimes#include <string.h>
7037672Scharnier#include <unistd.h>
711558Srgrimes
72175678Smpp#include "quotacheck.h"
73175678Smpp
741558Srgrimeschar *qfname = QUOTAFILENAME;
751558Srgrimeschar *qfextension[] = INITQFNAMES;
761558Srgrimeschar *quotagroup = QUOTAGROUP;
771558Srgrimes
781558Srgrimesunion {
791558Srgrimes	struct	fs	sblk;
801558Srgrimes	char	dummy[MAXBSIZE];
81107094Smckusick} sb_un;
82107094Smckusick#define	sblock	sb_un.sblk
83107094Smckusickunion {
84107094Smckusick	struct	cg	cgblk;
85107094Smckusick	char	dummy[MAXBSIZE];
86107094Smckusick} cg_un;
87107094Smckusick#define	cgblk	cg_un.cgblk
881558Srgrimeslong dev_bsize = 1;
8989797Sphkino_t maxino;
901558Srgrimes
9198542Smckusickunion dinode {
9298542Smckusick	struct ufs1_dinode dp1;
9398542Smckusick	struct ufs2_dinode dp2;
9498542Smckusick};
9598542Smckusick#define	DIP(dp, field) \
9698542Smckusick	((sblock.fs_magic == FS_UFS1_MAGIC) ? \
9798542Smckusick	(dp)->dp1.field : (dp)->dp2.field)
9898542Smckusick
991558Srgrimes#define	HASUSR	1
1001558Srgrimes#define	HASGRP	2
1011558Srgrimes
1021558Srgrimesstruct fileusage {
1031558Srgrimes	struct	fileusage *fu_next;
1041558Srgrimes	u_long	fu_curinodes;
1051558Srgrimes	u_long	fu_curblocks;
1061558Srgrimes	u_long	fu_id;
1071558Srgrimes	char	fu_name[1];
1081558Srgrimes	/* actually bigger */
1091558Srgrimes};
1101558Srgrimes#define FUHASH 1024	/* must be power of two */
1111558Srgrimesstruct fileusage *fuhead[MAXQUOTAS][FUHASH];
1121558Srgrimes
113102231Strhodesint	aflag;			/* all file systems */
114207736Smckusickint	cflag;			/* convert format to 32 or 64 bit size */
1151558Srgrimesint	gflag;			/* check group quotas */
1161558Srgrimesint	uflag;			/* check user quotas */
1171558Srgrimesint	vflag;			/* verbose */
1181558Srgrimesint	fi;			/* open disk file descriptor */
1191558Srgrimes
1201558Srgrimesstruct fileusage *
121207736Smckusick	 addid(u_long, int, char *, const char *);
122107094Smckusickvoid	 bread(ufs2_daddr_t, char *, long);
12392883Simpvoid	 freeinodebuf(void);
12498542Smckusickunion dinode *
12592883Simp	 getnextinode(ino_t);
12692883Simpint	 getquotagid(void);
1271558Srgrimesstruct fileusage *
12892883Simp	 lookup(u_long, int);
12992883Simpint	 oneof(char *, char*[], int);
130207736Smckusickvoid	 printchanges(const char *, int, struct dqblk *, struct fileusage *,
131207736Smckusick	    u_long);
132107094Smckusickvoid	 setinodebuf(ino_t);
133207736Smckusickint	 update(const char *, struct quotafile *, int);
13492883Simpvoid	 usage(void);
1351558Srgrimes
1361558Srgrimesint
137180187Sdesmain(int argc, char *argv[])
1381558Srgrimes{
13989797Sphk	struct fstab *fs;
14089797Sphk	struct passwd *pw;
14189797Sphk	struct group *gr;
142207736Smckusick	struct quotafile *qfu, *qfg;
143124830Sgrehan	int i, argnum, maxrun, errs, ch;
1441558Srgrimes	long done = 0;
145124830Sgrehan	char *name;
1461558Srgrimes
1471558Srgrimes	errs = maxrun = 0;
148207736Smckusick	while ((ch = getopt(argc, argv, "ac:guvl:")) != -1) {
1491558Srgrimes		switch(ch) {
1501558Srgrimes		case 'a':
1511558Srgrimes			aflag++;
1521558Srgrimes			break;
153207736Smckusick		case 'c':
154207736Smckusick			if (cflag)
155207736Smckusick				usage();
156207736Smckusick			cflag = atoi(optarg);
157207736Smckusick			break;
1581558Srgrimes		case 'g':
1591558Srgrimes			gflag++;
1601558Srgrimes			break;
1611558Srgrimes		case 'u':
1621558Srgrimes			uflag++;
1631558Srgrimes			break;
1641558Srgrimes		case 'v':
1651558Srgrimes			vflag++;
1661558Srgrimes			break;
1671558Srgrimes		case 'l':
1681558Srgrimes			maxrun = atoi(optarg);
1691558Srgrimes			break;
1701558Srgrimes		default:
1711558Srgrimes			usage();
1721558Srgrimes		}
1731558Srgrimes	}
1741558Srgrimes	argc -= optind;
1751558Srgrimes	argv += optind;
1761558Srgrimes	if ((argc == 0 && !aflag) || (argc > 0 && aflag))
1771558Srgrimes		usage();
178207736Smckusick	if (cflag && cflag != 32 && cflag != 64)
179207736Smckusick		usage();
1801558Srgrimes	if (!gflag && !uflag) {
1811558Srgrimes		gflag++;
1821558Srgrimes		uflag++;
1831558Srgrimes	}
1841558Srgrimes	if (gflag) {
1851558Srgrimes		setgrent();
18614280Smpp		while ((gr = getgrent()) != NULL)
187166143Smpp			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name,
188166143Smpp			    NULL);
1891558Srgrimes		endgrent();
1901558Srgrimes	}
1911558Srgrimes	if (uflag) {
1921558Srgrimes		setpwent();
19314280Smpp		while ((pw = getpwent()) != NULL)
194166143Smpp			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name,
195166143Smpp			    NULL);
1961558Srgrimes		endpwent();
1971558Srgrimes	}
198164071Sceri	/*
199175678Smpp	 * The maxrun (-l) option is now deprecated.
200164071Sceri	 */
201175678Smpp	if (maxrun > 0)
202175678Smpp		warnx("the -l option is now deprecated");
2031558Srgrimes	if (aflag)
204207736Smckusick		exit(checkfstab(uflag, gflag));
2051558Srgrimes	if (setfsent() == 0)
20626675Scharnier		errx(1, "%s: can't open", FSTAB);
2071558Srgrimes	while ((fs = getfsent()) != NULL) {
2081558Srgrimes		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
209207736Smckusick		     (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
2101558Srgrimes		    (name = blockcheck(fs->fs_spec))) {
2111558Srgrimes			done |= 1 << argnum;
212207736Smckusick			qfu = NULL;
213207736Smckusick			if (uflag)
214207736Smckusick				qfu = quota_open(fs, USRQUOTA, O_CREAT|O_RDWR);
215207736Smckusick			qfg = NULL;
216207736Smckusick			if (gflag)
217207736Smckusick				qfg = quota_open(fs, GRPQUOTA, O_CREAT|O_RDWR);
218207736Smckusick			if (qfu == NULL && qfg == NULL)
219207736Smckusick				continue;
220207736Smckusick			errs += chkquota(name, qfu, qfg);
221207736Smckusick			if (qfu)
222207736Smckusick				quota_close(qfu);
223207736Smckusick			if (qfg)
224207736Smckusick				quota_close(qfg);
2251558Srgrimes		}
2261558Srgrimes	}
2271558Srgrimes	endfsent();
2281558Srgrimes	for (i = 0; i < argc; i++)
2291558Srgrimes		if ((done & (1 << i)) == 0)
2301558Srgrimes			fprintf(stderr, "%s not found in %s\n",
2311558Srgrimes				argv[i], FSTAB);
2321558Srgrimes	exit(errs);
2331558Srgrimes}
2341558Srgrimes
2351558Srgrimesvoid
236180187Sdesusage(void)
2371558Srgrimes{
238180187Sdes	(void)fprintf(stderr, "%s\n%s\n",
239207736Smckusick		"usage: quotacheck [-guv] [-c 32 | 64] [-l maxrun] -a",
240207736Smckusick		"       quotacheck [-guv] [-c 32 | 64] filesystem ...");
2411558Srgrimes	exit(1);
2421558Srgrimes}
2431558Srgrimes
2441558Srgrimes/*
24598542Smckusick * Possible superblock locations ordered from most to least likely.
24698542Smckusick */
24798542Smckusickstatic int sblock_try[] = SBLOCKSEARCH;
24898542Smckusick
24998542Smckusick/*
250102231Strhodes * Scan the specified file system to check quota(s) present on it.
2511558Srgrimes */
2521558Srgrimesint
253207736Smckusickchkquota(char *specname, struct quotafile *qfu, struct quotafile *qfg)
2541558Srgrimes{
25589797Sphk	struct fileusage *fup;
25698542Smckusick	union dinode *dp;
2571558Srgrimes	int cg, i, mode, errs = 0;
258166143Smpp	ino_t ino, inosused, userino = 0, groupino = 0;
259175344Smpp	dev_t dev, userdev = 0, groupdev = 0;
260207736Smckusick	struct stat sb;
261207736Smckusick	const char *mntpt;
262107094Smckusick	char *cp;
2631558Srgrimes
264207736Smckusick	if (qfu != NULL)
265207736Smckusick		mntpt = quota_fsname(qfu);
266207736Smckusick	else if (qfg != NULL)
267207736Smckusick		mntpt = quota_fsname(qfg);
268207736Smckusick	else
269207736Smckusick		errx(1, "null quotafile information passed to chkquota()\n");
270207736Smckusick	if (cflag) {
271207736Smckusick		if (vflag && qfu != NULL)
272207736Smckusick			printf("%s: convert user quota to %d bits\n",
273207736Smckusick			    mntpt, cflag);
274207736Smckusick		if (qfu != NULL && quota_convert(qfu, cflag) < 0) {
275207736Smckusick			if (errno == EBADF)
276207736Smckusick				errx(1,
277207736Smckusick				    "%s: cannot convert an active quota file",
278207736Smckusick				    mntpt);
279207736Smckusick			err(1, "user quota conversion to size %d failed",
280207736Smckusick			    cflag);
281207736Smckusick		}
282207736Smckusick		if (vflag && qfg != NULL)
283207736Smckusick			printf("%s: convert group quota to %d bits\n",
284207736Smckusick			    mntpt, cflag);
285207736Smckusick		if (qfg != NULL && quota_convert(qfg, cflag) < 0) {
286207736Smckusick			if (errno == EBADF)
287207736Smckusick				errx(1,
288207736Smckusick				    "%s: cannot convert an active quota file",
289207736Smckusick				    mntpt);
290207736Smckusick			err(1, "group quota conversion to size %d failed",
291207736Smckusick			    cflag);
292207736Smckusick		}
293207736Smckusick	}
294207736Smckusick	if ((fi = open(specname, O_RDONLY, 0)) < 0) {
295207736Smckusick		warn("%s", specname);
2961558Srgrimes		return (1);
2971558Srgrimes	}
298175344Smpp	if ((stat(mntpt, &sb)) < 0) {
299175344Smpp		warn("%s", mntpt);
300175344Smpp		return (1);
301175344Smpp	}
302175344Smpp	dev = sb.st_dev;
3031558Srgrimes	if (vflag) {
3041558Srgrimes		(void)printf("*** Checking ");
305207736Smckusick		if (qfu)
306207736Smckusick			(void)printf("user%s", qfg ? " and " : "");
307207736Smckusick		if (qfg)
308207736Smckusick			(void)printf("group");
309207736Smckusick		(void)printf(" quotas for %s (%s)\n", specname, mntpt);
3101558Srgrimes	}
311207736Smckusick	if (qfu) {
312207736Smckusick		if (stat(quota_qfname(qfu), &sb) == 0) {
313166143Smpp			userino = sb.st_ino;
314175344Smpp			userdev = sb.st_dev;
315175344Smpp		}
316166143Smpp	}
317207736Smckusick	if (qfg) {
318207736Smckusick		if (stat(quota_qfname(qfg), &sb) == 0) {
319166143Smpp			groupino = sb.st_ino;
320175344Smpp			groupdev = sb.st_dev;
321175344Smpp		}
322166143Smpp	}
3231558Srgrimes	sync();
3249273Sdima	dev_bsize = 1;
32598542Smckusick	for (i = 0; sblock_try[i] != -1; i++) {
32698542Smckusick		bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE);
32798542Smckusick		if ((sblock.fs_magic == FS_UFS1_MAGIC ||
32898542Smckusick		     (sblock.fs_magic == FS_UFS2_MAGIC &&
329107294Smckusick		      sblock.fs_sblockloc == sblock_try[i])) &&
33098542Smckusick		    sblock.fs_bsize <= MAXBSIZE &&
33198542Smckusick		    sblock.fs_bsize >= sizeof(struct fs))
33298542Smckusick			break;
33398542Smckusick	}
33498542Smckusick	if (sblock_try[i] == -1) {
335102231Strhodes		warn("Cannot find file system superblock");
33698542Smckusick		return (1);
33798542Smckusick	}
3381558Srgrimes	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
3391558Srgrimes	maxino = sblock.fs_ncg * sblock.fs_ipg;
340107094Smckusick	for (cg = 0; cg < sblock.fs_ncg; cg++) {
341107094Smckusick		ino = cg * sblock.fs_ipg;
342107094Smckusick		setinodebuf(ino);
343107094Smckusick		bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk),
344107094Smckusick		    sblock.fs_cgsize);
345107094Smckusick		if (sblock.fs_magic == FS_UFS2_MAGIC)
346107094Smckusick			inosused = cgblk.cg_initediblk;
347107094Smckusick		else
348107094Smckusick			inosused = sblock.fs_ipg;
349107094Smckusick		/*
350107094Smckusick		 * If we are using soft updates, then we can trust the
351107094Smckusick		 * cylinder group inode allocation maps to tell us which
352107094Smckusick		 * inodes are allocated. We will scan the used inode map
353107094Smckusick		 * to find the inodes that are really in use, and then
354107094Smckusick		 * read only those inodes in from disk.
355107094Smckusick		 */
356107094Smckusick		if (sblock.fs_flags & FS_DOSOFTDEP) {
357107094Smckusick			if (!cg_chkmagic(&cgblk))
358107094Smckusick				errx(1, "CG %d: BAD MAGIC NUMBER\n", cg);
359107094Smckusick			cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT];
360107094Smckusick			for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
361107094Smckusick				if (*cp == 0)
362107094Smckusick					continue;
363107094Smckusick				for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
364107094Smckusick					if (*cp & i)
365107094Smckusick						break;
366107094Smckusick					inosused--;
367107094Smckusick				}
368107094Smckusick				break;
369107094Smckusick			}
370107094Smckusick			if (inosused <= 0)
3711558Srgrimes				continue;
372107094Smckusick		}
373107094Smckusick		for (i = 0; i < inosused; i++, ino++) {
374107094Smckusick			if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
375107094Smckusick			    (mode = DIP(dp, di_mode) & IFMT) == 0)
3761558Srgrimes				continue;
377166143Smpp			/*
378166143Smpp			 * XXX: Do not account for UIDs or GIDs that appear
379166143Smpp			 * to be negative to prevent generating 100GB+
380166143Smpp			 * quota files.
381166143Smpp			 */
382180187Sdes			if ((int)DIP(dp, di_uid) < 0 ||
383166143Smpp			    (int)DIP(dp, di_gid) < 0) {
384166143Smpp				if (vflag) {
385166143Smpp					if (aflag)
386166143Smpp						(void)printf("%s: ", mntpt);
387241013Smdf			(void)printf("out of range UID/GID (%u/%u) ino=%ju\n",
388166143Smpp					    DIP(dp, di_uid), DIP(dp,di_gid),
389241013Smdf					    (uintmax_t)ino);
390166143Smpp				}
391166143Smpp				continue;
392166143Smpp			}
393166143Smpp
394180187Sdes			/*
395166143Smpp			 * Do not account for file system snapshot files
396166143Smpp			 * or the actual quota data files to be consistent
397166143Smpp			 * with how they are handled inside the kernel.
398166143Smpp			 */
399166143Smpp#ifdef	SF_SNAPSHOT
400166143Smpp			if (DIP(dp, di_flags) & SF_SNAPSHOT)
401166143Smpp				continue;
402166143Smpp#endif
403175344Smpp			if ((ino == userino && dev == userdev) ||
404175344Smpp			    (ino == groupino && dev == groupdev))
405166143Smpp				continue;
406207736Smckusick			if (qfg) {
40798542Smckusick				fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
408166143Smpp				    (char *)0, mntpt);
4091558Srgrimes				fup->fu_curinodes++;
4101558Srgrimes				if (mode == IFREG || mode == IFDIR ||
4111558Srgrimes				    mode == IFLNK)
41298542Smckusick					fup->fu_curblocks += DIP(dp, di_blocks);
4131558Srgrimes			}
414207736Smckusick			if (qfu) {
41598542Smckusick				fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
416166143Smpp				    (char *)0, mntpt);
4171558Srgrimes				fup->fu_curinodes++;
4181558Srgrimes				if (mode == IFREG || mode == IFDIR ||
4191558Srgrimes				    mode == IFLNK)
42098542Smckusick					fup->fu_curblocks += DIP(dp, di_blocks);
4211558Srgrimes			}
4221558Srgrimes		}
4231558Srgrimes	}
4241558Srgrimes	freeinodebuf();
425207736Smckusick	if (qfu)
426207736Smckusick		errs += update(mntpt, qfu, USRQUOTA);
427207736Smckusick	if (qfg)
428207736Smckusick		errs += update(mntpt, qfg, GRPQUOTA);
4291558Srgrimes	close(fi);
430172168Smpp	(void)fflush(stdout);
4311558Srgrimes	return (errs);
4321558Srgrimes}
4331558Srgrimes
4341558Srgrimes/*
4351558Srgrimes * Update a specified quota file.
4361558Srgrimes */
4371558Srgrimesint
438207736Smckusickupdate(const char *fsname, struct quotafile *qf, int type)
4391558Srgrimes{
44089797Sphk	struct fileusage *fup;
441166143Smpp	u_long id, lastid, highid = 0;
4421558Srgrimes	struct dqblk dqbuf;
443166143Smpp	struct stat sb;
4441558Srgrimes	static struct dqblk zerodqbuf;
4451558Srgrimes	static struct fileusage zerofileusage;
4461558Srgrimes
447166143Smpp	/*
448166143Smpp	 * Scan the on-disk quota file and record any usage changes.
449166143Smpp	 */
450207736Smckusick	lastid = quota_maxid(qf);
451207736Smckusick	for (id = 0; id <= lastid; id++) {
452207736Smckusick		if (quota_read(qf, &dqbuf, id) < 0)
4531558Srgrimes			dqbuf = zerodqbuf;
454166143Smpp		if ((fup = lookup(id, type)) == NULL)
4551558Srgrimes			fup = &zerofileusage;
456166143Smpp		if (fup->fu_curinodes || fup->fu_curblocks ||
457166143Smpp		    dqbuf.dqb_bsoftlimit || dqbuf.dqb_bhardlimit ||
458166143Smpp		    dqbuf.dqb_isoftlimit || dqbuf.dqb_ihardlimit)
459166143Smpp			highid = id;
4601558Srgrimes		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
4611558Srgrimes		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
4621558Srgrimes			fup->fu_curinodes = 0;
4631558Srgrimes			fup->fu_curblocks = 0;
4641558Srgrimes			continue;
4651558Srgrimes		}
466166179Smpp		printchanges(fsname, type, &dqbuf, fup, id);
4671558Srgrimes		dqbuf.dqb_curinodes = fup->fu_curinodes;
4681558Srgrimes		dqbuf.dqb_curblocks = fup->fu_curblocks;
469207736Smckusick		(void) quota_write_usage(qf, &dqbuf, id);
4701558Srgrimes		fup->fu_curinodes = 0;
4711558Srgrimes		fup->fu_curblocks = 0;
4721558Srgrimes	}
473166143Smpp
474166143Smpp	/*
475166143Smpp	 * Walk the hash table looking for ids with non-zero usage
476166143Smpp	 * that are not currently recorded in the quota file. E.g.
477166143Smpp	 * ids that are past the end of the current file.
478166143Smpp	 */
479207736Smckusick	for (id = 0; id < FUHASH; id++) {
480207736Smckusick		for (fup = fuhead[type][id]; fup != NULL; fup = fup->fu_next) {
481166143Smpp			if (fup->fu_id <= lastid)
482166143Smpp				continue;
483166143Smpp			if (fup->fu_curinodes == 0 && fup->fu_curblocks == 0)
484166143Smpp				continue;
485166143Smpp			bzero(&dqbuf, sizeof(struct dqblk));
486166143Smpp			if (fup->fu_id > highid)
487166143Smpp				highid = fup->fu_id;
488207736Smckusick			printchanges(fsname, type, &dqbuf, fup, fup->fu_id);
489166143Smpp			dqbuf.dqb_curinodes = fup->fu_curinodes;
490166143Smpp			dqbuf.dqb_curblocks = fup->fu_curblocks;
491207736Smckusick			(void) quota_write_usage(qf, &dqbuf, fup->fu_id);
492166143Smpp			fup->fu_curinodes = 0;
493166143Smpp			fup->fu_curblocks = 0;
494166143Smpp		}
495166143Smpp	}
496207736Smckusick	/*
497207736Smckusick	 * If this is old format file, then size may be smaller,
498207736Smckusick	 * so ensure that we only truncate when it will make things
499207736Smckusick	 * smaller, and not if it will grow an old format file.
500207736Smckusick	 */
501207736Smckusick	if (highid < lastid &&
502207736Smckusick	    stat(quota_qfname(qf), &sb) == 0 &&
503207736Smckusick	    sb.st_size > (((off_t)highid + 2) * sizeof(struct dqblk)))
504207736Smckusick		truncate(quota_qfname(qf),
505207736Smckusick		    (((off_t)highid + 2) * sizeof(struct dqblk)));
5061558Srgrimes	return (0);
5071558Srgrimes}
5081558Srgrimes
5091558Srgrimes/*
5101558Srgrimes * Check to see if target appears in list of size cnt.
5111558Srgrimes */
5121558Srgrimesint
513180187Sdesoneof(char *target, char *list[], int cnt)
5141558Srgrimes{
51589797Sphk	int i;
5161558Srgrimes
5171558Srgrimes	for (i = 0; i < cnt; i++)
5181558Srgrimes		if (strcmp(target, list[i]) == 0)
5191558Srgrimes			return (i);
5201558Srgrimes	return (-1);
5211558Srgrimes}
5221558Srgrimes
5231558Srgrimes/*
5241558Srgrimes * Determine the group identifier for quota files.
5251558Srgrimes */
5261558Srgrimesint
527180187Sdesgetquotagid(void)
5281558Srgrimes{
5291558Srgrimes	struct group *gr;
5301558Srgrimes
53114280Smpp	if ((gr = getgrnam(quotagroup)) != NULL)
5321558Srgrimes		return (gr->gr_gid);
5331558Srgrimes	return (-1);
5341558Srgrimes}
5351558Srgrimes
5361558Srgrimes/*
5371558Srgrimes * Routines to manage the file usage table.
5381558Srgrimes *
5391558Srgrimes * Lookup an id of a specific type.
5401558Srgrimes */
5411558Srgrimesstruct fileusage *
542180187Sdeslookup(u_long id, int type)
5431558Srgrimes{
54489797Sphk	struct fileusage *fup;
5451558Srgrimes
5461558Srgrimes	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
5471558Srgrimes		if (fup->fu_id == id)
5481558Srgrimes			return (fup);
5491558Srgrimes	return (NULL);
5501558Srgrimes}
5511558Srgrimes
5521558Srgrimes/*
5531558Srgrimes * Add a new file usage id if it does not already exist.
5541558Srgrimes */
5551558Srgrimesstruct fileusage *
556207736Smckusickaddid(u_long id, int type, char *name, const char *fsname)
5571558Srgrimes{
5581558Srgrimes	struct fileusage *fup, **fhp;
5591558Srgrimes	int len;
5601558Srgrimes
56114280Smpp	if ((fup = lookup(id, type)) != NULL)
5621558Srgrimes		return (fup);
5631558Srgrimes	if (name)
5641558Srgrimes		len = strlen(name);
5651558Srgrimes	else
566166179Smpp		len = 0;
5671558Srgrimes	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
56837672Scharnier		errx(1, "calloc failed");
5691558Srgrimes	fhp = &fuhead[type][id & (FUHASH - 1)];
5701558Srgrimes	fup->fu_next = *fhp;
5711558Srgrimes	*fhp = fup;
5721558Srgrimes	fup->fu_id = id;
5731558Srgrimes	if (name)
5741558Srgrimes		bcopy(name, fup->fu_name, len + 1);
57514169Smpp	else {
57614169Smpp		(void)sprintf(fup->fu_name, "%lu", id);
577166143Smpp		if (vflag) {
578166143Smpp			if (aflag && fsname != NULL)
579166143Smpp				(void)printf("%s: ", fsname);
580180187Sdes			printf("unknown %cid: %lu\n",
58114169Smpp			    type == USRQUOTA ? 'u' : 'g', id);
582166143Smpp		}
58314169Smpp	}
5841558Srgrimes	return (fup);
5851558Srgrimes}
5861558Srgrimes
5871558Srgrimes/*
5881558Srgrimes * Special purpose version of ginode used to optimize pass
5891558Srgrimes * over all the inodes in numerical order.
5901558Srgrimes */
59198542Smckusickstatic ino_t nextino, lastinum, lastvalidinum;
59298542Smckusickstatic long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
59398542Smckusickstatic caddr_t inodebuf;
59498542Smckusick#define INOBUFSIZE	56*1024		/* size of buffer to read inodes */
5951558Srgrimes
59698542Smckusickunion dinode *
597107094Smckusickgetnextinode(ino_t inumber)
5981558Srgrimes{
5991558Srgrimes	long size;
60098542Smckusick	ufs2_daddr_t dblk;
60198542Smckusick	union dinode *dp;
60298542Smckusick	static caddr_t nextinop;
6031558Srgrimes
60498542Smckusick	if (inumber != nextino++ || inumber > lastvalidinum)
605241013Smdf		errx(1, "bad inode number %ju to nextinode",
606241013Smdf		    (uintmax_t)inumber);
6071558Srgrimes	if (inumber >= lastinum) {
6081558Srgrimes		readcnt++;
6091558Srgrimes		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
6101558Srgrimes		if (readcnt % readpercg == 0) {
6111558Srgrimes			size = partialsize;
6121558Srgrimes			lastinum += partialcnt;
6131558Srgrimes		} else {
6141558Srgrimes			size = inobufsize;
6151558Srgrimes			lastinum += fullcnt;
6161558Srgrimes		}
61798542Smckusick		/*
61898542Smckusick		 * If bread returns an error, it will already have zeroed
61998542Smckusick		 * out the buffer, so we do not need to do so here.
62098542Smckusick		 */
62198542Smckusick		bread(dblk, inodebuf, size);
62298542Smckusick		nextinop = inodebuf;
6231558Srgrimes	}
62498542Smckusick	dp = (union dinode *)nextinop;
62598542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC)
62698542Smckusick		nextinop += sizeof(struct ufs1_dinode);
62798542Smckusick	else
62898542Smckusick		nextinop += sizeof(struct ufs2_dinode);
62998542Smckusick	return (dp);
6301558Srgrimes}
6311558Srgrimes
6321558Srgrimes/*
6331558Srgrimes * Prepare to scan a set of inodes.
6341558Srgrimes */
6351558Srgrimesvoid
636107094Smckusicksetinodebuf(ino_t inum)
6371558Srgrimes{
6381558Srgrimes
639107094Smckusick	if (inum % sblock.fs_ipg != 0)
640241013Smdf		errx(1, "bad inode number %ju to setinodebuf", (uintmax_t)inum);
641107094Smckusick	lastvalidinum = inum + sblock.fs_ipg - 1;
642107094Smckusick	nextino = inum;
643107094Smckusick	lastinum = inum;
6441558Srgrimes	readcnt = 0;
645107094Smckusick	if (inodebuf != NULL)
646107094Smckusick		return;
6471558Srgrimes	inobufsize = blkroundup(&sblock, INOBUFSIZE);
64898542Smckusick	fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
64998542Smckusick	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
6501558Srgrimes	readpercg = sblock.fs_ipg / fullcnt;
6511558Srgrimes	partialcnt = sblock.fs_ipg % fullcnt;
65298542Smckusick	partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
65398542Smckusick	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
6541558Srgrimes	if (partialcnt != 0) {
6551558Srgrimes		readpercg++;
6561558Srgrimes	} else {
6571558Srgrimes		partialcnt = fullcnt;
6581558Srgrimes		partialsize = inobufsize;
6591558Srgrimes	}
660107094Smckusick	if ((inodebuf = malloc((unsigned)inobufsize)) == NULL)
661107094Smckusick		errx(1, "cannot allocate space for inode buffer");
6621558Srgrimes}
6631558Srgrimes
6641558Srgrimes/*
6651558Srgrimes * Free up data structures used to scan inodes.
6661558Srgrimes */
6671558Srgrimesvoid
668180187Sdesfreeinodebuf(void)
6691558Srgrimes{
6701558Srgrimes
6711558Srgrimes	if (inodebuf != NULL)
6721558Srgrimes		free(inodebuf);
6731558Srgrimes	inodebuf = NULL;
6741558Srgrimes}
6751558Srgrimes
6761558Srgrimes/*
6771558Srgrimes * Read specified disk blocks.
6781558Srgrimes */
6791558Srgrimesvoid
680180187Sdesbread(ufs2_daddr_t bno, char *buf, long cnt)
6811558Srgrimes{
6821558Srgrimes
6831558Srgrimes	if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
6841558Srgrimes	    read(fi, buf, cnt) != cnt)
685107094Smckusick		errx(1, "bread failed on block %ld", (long)bno);
6861558Srgrimes}
687166143Smpp
688166143Smpp/*
689166143Smpp * Display updated block and i-node counts.
690166143Smpp */
691166143Smppvoid
692207736Smckusickprintchanges(const char *fsname, int type, struct dqblk *dp,
693180187Sdes    struct fileusage *fup, u_long id)
694166143Smpp{
695166143Smpp	if (!vflag)
696166143Smpp		return;
697166143Smpp	if (aflag)
698166143Smpp		(void)printf("%s: ", fsname);
699166179Smpp	if (fup->fu_name[0] == '\0')
700166179Smpp		(void)printf("%-8lu fixed ", id);
701166179Smpp	else
702166179Smpp		(void)printf("%-8s fixed ", fup->fu_name);
703166179Smpp	switch (type) {
704166179Smpp
705166179Smpp	case GRPQUOTA:
706166179Smpp		(void)printf("(group):");
707166179Smpp		break;
708166179Smpp
709166179Smpp	case USRQUOTA:
710166179Smpp		(void)printf("(user): ");
711166179Smpp		break;
712166179Smpp
713166179Smpp	default:
714166179Smpp		(void)printf("(unknown quota type %d)", type);
715166179Smpp		break;
716166179Smpp	}
717166143Smpp	if (dp->dqb_curinodes != fup->fu_curinodes)
718166143Smpp		(void)printf("\tinodes %lu -> %lu", (u_long)dp->dqb_curinodes,
719166143Smpp		    (u_long)fup->fu_curinodes);
720166143Smpp	if (dp->dqb_curblocks != fup->fu_curblocks)
721180187Sdes		(void)printf("\tblocks %lu -> %lu",
722166143Smpp		    (u_long)dp->dqb_curblocks,
723166143Smpp		    (u_long)fup->fu_curblocks);
724166143Smpp	(void)printf("\n");
725166143Smpp}
726