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$");
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>
661558Srgrimes#include <stdio.h>
671558Srgrimes#include <stdlib.h>
681558Srgrimes#include <string.h>
6937672Scharnier#include <unistd.h>
701558Srgrimes
71175678Smpp#include "quotacheck.h"
72175678Smpp
731558Srgrimeschar *qfname = QUOTAFILENAME;
741558Srgrimeschar *qfextension[] = INITQFNAMES;
751558Srgrimeschar *quotagroup = QUOTAGROUP;
761558Srgrimes
771558Srgrimesunion {
781558Srgrimes	struct	fs	sblk;
791558Srgrimes	char	dummy[MAXBSIZE];
80107094Smckusick} sb_un;
81107094Smckusick#define	sblock	sb_un.sblk
82107094Smckusickunion {
83107094Smckusick	struct	cg	cgblk;
84107094Smckusick	char	dummy[MAXBSIZE];
85107094Smckusick} cg_un;
86107094Smckusick#define	cgblk	cg_un.cgblk
871558Srgrimeslong dev_bsize = 1;
8889797Sphkino_t maxino;
891558Srgrimes
9098542Smckusickunion dinode {
9198542Smckusick	struct ufs1_dinode dp1;
9298542Smckusick	struct ufs2_dinode dp2;
9398542Smckusick};
9498542Smckusick#define	DIP(dp, field) \
9598542Smckusick	((sblock.fs_magic == FS_UFS1_MAGIC) ? \
9698542Smckusick	(dp)->dp1.field : (dp)->dp2.field)
9798542Smckusick
981558Srgrimes#define	HASUSR	1
991558Srgrimes#define	HASGRP	2
1001558Srgrimes
1011558Srgrimesstruct fileusage {
1021558Srgrimes	struct	fileusage *fu_next;
1031558Srgrimes	u_long	fu_curinodes;
1041558Srgrimes	u_long	fu_curblocks;
1051558Srgrimes	u_long	fu_id;
1061558Srgrimes	char	fu_name[1];
1071558Srgrimes	/* actually bigger */
1081558Srgrimes};
1091558Srgrimes#define FUHASH 1024	/* must be power of two */
1101558Srgrimesstruct fileusage *fuhead[MAXQUOTAS][FUHASH];
1111558Srgrimes
112102231Strhodesint	aflag;			/* all file systems */
113207736Smckusickint	cflag;			/* convert format to 32 or 64 bit size */
1141558Srgrimesint	gflag;			/* check group quotas */
1151558Srgrimesint	uflag;			/* check user quotas */
1161558Srgrimesint	vflag;			/* verbose */
1171558Srgrimesint	fi;			/* open disk file descriptor */
1181558Srgrimes
1191558Srgrimesstruct fileusage *
120207736Smckusick	 addid(u_long, int, char *, const char *);
121107094Smckusickvoid	 bread(ufs2_daddr_t, char *, long);
12292883Simpvoid	 freeinodebuf(void);
12398542Smckusickunion dinode *
12492883Simp	 getnextinode(ino_t);
12592883Simpint	 getquotagid(void);
1261558Srgrimesstruct fileusage *
12792883Simp	 lookup(u_long, int);
12892883Simpint	 oneof(char *, char*[], int);
129207736Smckusickvoid	 printchanges(const char *, int, struct dqblk *, struct fileusage *,
130207736Smckusick	    u_long);
131107094Smckusickvoid	 setinodebuf(ino_t);
132207736Smckusickint	 update(const char *, struct quotafile *, int);
13392883Simpvoid	 usage(void);
1341558Srgrimes
1351558Srgrimesint
136180187Sdesmain(int argc, char *argv[])
1371558Srgrimes{
13889797Sphk	struct fstab *fs;
13989797Sphk	struct passwd *pw;
14089797Sphk	struct group *gr;
141207736Smckusick	struct quotafile *qfu, *qfg;
142124830Sgrehan	int i, argnum, maxrun, errs, ch;
1431558Srgrimes	long done = 0;
144124830Sgrehan	char *name;
1451558Srgrimes
1461558Srgrimes	errs = maxrun = 0;
147207736Smckusick	while ((ch = getopt(argc, argv, "ac:guvl:")) != -1) {
1481558Srgrimes		switch(ch) {
1491558Srgrimes		case 'a':
1501558Srgrimes			aflag++;
1511558Srgrimes			break;
152207736Smckusick		case 'c':
153207736Smckusick			if (cflag)
154207736Smckusick				usage();
155207736Smckusick			cflag = atoi(optarg);
156207736Smckusick			break;
1571558Srgrimes		case 'g':
1581558Srgrimes			gflag++;
1591558Srgrimes			break;
1601558Srgrimes		case 'u':
1611558Srgrimes			uflag++;
1621558Srgrimes			break;
1631558Srgrimes		case 'v':
1641558Srgrimes			vflag++;
1651558Srgrimes			break;
1661558Srgrimes		case 'l':
1671558Srgrimes			maxrun = atoi(optarg);
1681558Srgrimes			break;
1691558Srgrimes		default:
1701558Srgrimes			usage();
1711558Srgrimes		}
1721558Srgrimes	}
1731558Srgrimes	argc -= optind;
1741558Srgrimes	argv += optind;
1751558Srgrimes	if ((argc == 0 && !aflag) || (argc > 0 && aflag))
1761558Srgrimes		usage();
177207736Smckusick	if (cflag && cflag != 32 && cflag != 64)
178207736Smckusick		usage();
1791558Srgrimes	if (!gflag && !uflag) {
1801558Srgrimes		gflag++;
1811558Srgrimes		uflag++;
1821558Srgrimes	}
1831558Srgrimes	if (gflag) {
1841558Srgrimes		setgrent();
18514280Smpp		while ((gr = getgrent()) != NULL)
186166143Smpp			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name,
187166143Smpp			    NULL);
1881558Srgrimes		endgrent();
1891558Srgrimes	}
1901558Srgrimes	if (uflag) {
1911558Srgrimes		setpwent();
19214280Smpp		while ((pw = getpwent()) != NULL)
193166143Smpp			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name,
194166143Smpp			    NULL);
1951558Srgrimes		endpwent();
1961558Srgrimes	}
197164071Sceri	/*
198175678Smpp	 * The maxrun (-l) option is now deprecated.
199164071Sceri	 */
200175678Smpp	if (maxrun > 0)
201175678Smpp		warnx("the -l option is now deprecated");
2021558Srgrimes	if (aflag)
203207736Smckusick		exit(checkfstab(uflag, gflag));
2041558Srgrimes	if (setfsent() == 0)
20526675Scharnier		errx(1, "%s: can't open", FSTAB);
2061558Srgrimes	while ((fs = getfsent()) != NULL) {
2071558Srgrimes		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
208207736Smckusick		     (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
2091558Srgrimes		    (name = blockcheck(fs->fs_spec))) {
2101558Srgrimes			done |= 1 << argnum;
211207736Smckusick			qfu = NULL;
212207736Smckusick			if (uflag)
213207736Smckusick				qfu = quota_open(fs, USRQUOTA, O_CREAT|O_RDWR);
214207736Smckusick			qfg = NULL;
215207736Smckusick			if (gflag)
216207736Smckusick				qfg = quota_open(fs, GRPQUOTA, O_CREAT|O_RDWR);
217207736Smckusick			if (qfu == NULL && qfg == NULL)
218207736Smckusick				continue;
219207736Smckusick			errs += chkquota(name, qfu, qfg);
220207736Smckusick			if (qfu)
221207736Smckusick				quota_close(qfu);
222207736Smckusick			if (qfg)
223207736Smckusick				quota_close(qfg);
2241558Srgrimes		}
2251558Srgrimes	}
2261558Srgrimes	endfsent();
2271558Srgrimes	for (i = 0; i < argc; i++)
2281558Srgrimes		if ((done & (1 << i)) == 0)
2291558Srgrimes			fprintf(stderr, "%s not found in %s\n",
2301558Srgrimes				argv[i], FSTAB);
2311558Srgrimes	exit(errs);
2321558Srgrimes}
2331558Srgrimes
2341558Srgrimesvoid
235180187Sdesusage(void)
2361558Srgrimes{
237180187Sdes	(void)fprintf(stderr, "%s\n%s\n",
238207736Smckusick		"usage: quotacheck [-guv] [-c 32 | 64] [-l maxrun] -a",
239207736Smckusick		"       quotacheck [-guv] [-c 32 | 64] filesystem ...");
2401558Srgrimes	exit(1);
2411558Srgrimes}
2421558Srgrimes
2431558Srgrimes/*
24498542Smckusick * Possible superblock locations ordered from most to least likely.
24598542Smckusick */
24698542Smckusickstatic int sblock_try[] = SBLOCKSEARCH;
24798542Smckusick
24898542Smckusick/*
249102231Strhodes * Scan the specified file system to check quota(s) present on it.
2501558Srgrimes */
2511558Srgrimesint
252207736Smckusickchkquota(char *specname, struct quotafile *qfu, struct quotafile *qfg)
2531558Srgrimes{
25489797Sphk	struct fileusage *fup;
25598542Smckusick	union dinode *dp;
2561558Srgrimes	int cg, i, mode, errs = 0;
257166143Smpp	ino_t ino, inosused, userino = 0, groupino = 0;
258175344Smpp	dev_t dev, userdev = 0, groupdev = 0;
259207736Smckusick	struct stat sb;
260207736Smckusick	const char *mntpt;
261107094Smckusick	char *cp;
2621558Srgrimes
263207736Smckusick	if (qfu != NULL)
264207736Smckusick		mntpt = quota_fsname(qfu);
265207736Smckusick	else if (qfg != NULL)
266207736Smckusick		mntpt = quota_fsname(qfg);
267207736Smckusick	else
268207736Smckusick		errx(1, "null quotafile information passed to chkquota()\n");
269207736Smckusick	if (cflag) {
270207736Smckusick		if (vflag && qfu != NULL)
271207736Smckusick			printf("%s: convert user quota to %d bits\n",
272207736Smckusick			    mntpt, cflag);
273207736Smckusick		if (qfu != NULL && quota_convert(qfu, cflag) < 0) {
274207736Smckusick			if (errno == EBADF)
275207736Smckusick				errx(1,
276207736Smckusick				    "%s: cannot convert an active quota file",
277207736Smckusick				    mntpt);
278207736Smckusick			err(1, "user quota conversion to size %d failed",
279207736Smckusick			    cflag);
280207736Smckusick		}
281207736Smckusick		if (vflag && qfg != NULL)
282207736Smckusick			printf("%s: convert group quota to %d bits\n",
283207736Smckusick			    mntpt, cflag);
284207736Smckusick		if (qfg != NULL && quota_convert(qfg, cflag) < 0) {
285207736Smckusick			if (errno == EBADF)
286207736Smckusick				errx(1,
287207736Smckusick				    "%s: cannot convert an active quota file",
288207736Smckusick				    mntpt);
289207736Smckusick			err(1, "group quota conversion to size %d failed",
290207736Smckusick			    cflag);
291207736Smckusick		}
292207736Smckusick	}
293207736Smckusick	if ((fi = open(specname, O_RDONLY, 0)) < 0) {
294207736Smckusick		warn("%s", specname);
2951558Srgrimes		return (1);
2961558Srgrimes	}
297175344Smpp	if ((stat(mntpt, &sb)) < 0) {
298175344Smpp		warn("%s", mntpt);
299175344Smpp		return (1);
300175344Smpp	}
301175344Smpp	dev = sb.st_dev;
3021558Srgrimes	if (vflag) {
3031558Srgrimes		(void)printf("*** Checking ");
304207736Smckusick		if (qfu)
305207736Smckusick			(void)printf("user%s", qfg ? " and " : "");
306207736Smckusick		if (qfg)
307207736Smckusick			(void)printf("group");
308207736Smckusick		(void)printf(" quotas for %s (%s)\n", specname, mntpt);
3091558Srgrimes	}
310207736Smckusick	if (qfu) {
311207736Smckusick		if (stat(quota_qfname(qfu), &sb) == 0) {
312166143Smpp			userino = sb.st_ino;
313175344Smpp			userdev = sb.st_dev;
314175344Smpp		}
315166143Smpp	}
316207736Smckusick	if (qfg) {
317207736Smckusick		if (stat(quota_qfname(qfg), &sb) == 0) {
318166143Smpp			groupino = sb.st_ino;
319175344Smpp			groupdev = sb.st_dev;
320175344Smpp		}
321166143Smpp	}
3221558Srgrimes	sync();
3239273Sdima	dev_bsize = 1;
32498542Smckusick	for (i = 0; sblock_try[i] != -1; i++) {
32598542Smckusick		bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE);
32698542Smckusick		if ((sblock.fs_magic == FS_UFS1_MAGIC ||
32798542Smckusick		     (sblock.fs_magic == FS_UFS2_MAGIC &&
328107294Smckusick		      sblock.fs_sblockloc == sblock_try[i])) &&
32998542Smckusick		    sblock.fs_bsize <= MAXBSIZE &&
33098542Smckusick		    sblock.fs_bsize >= sizeof(struct fs))
33198542Smckusick			break;
33298542Smckusick	}
33398542Smckusick	if (sblock_try[i] == -1) {
334102231Strhodes		warn("Cannot find file system superblock");
33598542Smckusick		return (1);
33698542Smckusick	}
3371558Srgrimes	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
3381558Srgrimes	maxino = sblock.fs_ncg * sblock.fs_ipg;
339107094Smckusick	for (cg = 0; cg < sblock.fs_ncg; cg++) {
340107094Smckusick		ino = cg * sblock.fs_ipg;
341107094Smckusick		setinodebuf(ino);
342107094Smckusick		bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk),
343107094Smckusick		    sblock.fs_cgsize);
344107094Smckusick		if (sblock.fs_magic == FS_UFS2_MAGIC)
345107094Smckusick			inosused = cgblk.cg_initediblk;
346107094Smckusick		else
347107094Smckusick			inosused = sblock.fs_ipg;
348107094Smckusick		/*
349107094Smckusick		 * If we are using soft updates, then we can trust the
350107094Smckusick		 * cylinder group inode allocation maps to tell us which
351107094Smckusick		 * inodes are allocated. We will scan the used inode map
352107094Smckusick		 * to find the inodes that are really in use, and then
353107094Smckusick		 * read only those inodes in from disk.
354107094Smckusick		 */
355107094Smckusick		if (sblock.fs_flags & FS_DOSOFTDEP) {
356107094Smckusick			if (!cg_chkmagic(&cgblk))
357107094Smckusick				errx(1, "CG %d: BAD MAGIC NUMBER\n", cg);
358107094Smckusick			cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT];
359107094Smckusick			for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
360107094Smckusick				if (*cp == 0)
361107094Smckusick					continue;
362107094Smckusick				for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
363107094Smckusick					if (*cp & i)
364107094Smckusick						break;
365107094Smckusick					inosused--;
366107094Smckusick				}
367107094Smckusick				break;
368107094Smckusick			}
369107094Smckusick			if (inosused <= 0)
3701558Srgrimes				continue;
371107094Smckusick		}
372107094Smckusick		for (i = 0; i < inosused; i++, ino++) {
373107094Smckusick			if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO ||
374107094Smckusick			    (mode = DIP(dp, di_mode) & IFMT) == 0)
3751558Srgrimes				continue;
376166143Smpp			/*
377166143Smpp			 * XXX: Do not account for UIDs or GIDs that appear
378166143Smpp			 * to be negative to prevent generating 100GB+
379166143Smpp			 * quota files.
380166143Smpp			 */
381180187Sdes			if ((int)DIP(dp, di_uid) < 0 ||
382166143Smpp			    (int)DIP(dp, di_gid) < 0) {
383166143Smpp				if (vflag) {
384166143Smpp					if (aflag)
385166143Smpp						(void)printf("%s: ", mntpt);
386166143Smpp			(void)printf("out of range UID/GID (%u/%u) ino=%u\n",
387166143Smpp					    DIP(dp, di_uid), DIP(dp,di_gid),
388166143Smpp					    ino);
389166143Smpp				}
390166143Smpp				continue;
391166143Smpp			}
392166143Smpp
393180187Sdes			/*
394166143Smpp			 * Do not account for file system snapshot files
395166143Smpp			 * or the actual quota data files to be consistent
396166143Smpp			 * with how they are handled inside the kernel.
397166143Smpp			 */
398166143Smpp#ifdef	SF_SNAPSHOT
399166143Smpp			if (DIP(dp, di_flags) & SF_SNAPSHOT)
400166143Smpp				continue;
401166143Smpp#endif
402175344Smpp			if ((ino == userino && dev == userdev) ||
403175344Smpp			    (ino == groupino && dev == groupdev))
404166143Smpp				continue;
405207736Smckusick			if (qfg) {
40698542Smckusick				fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA,
407166143Smpp				    (char *)0, mntpt);
4081558Srgrimes				fup->fu_curinodes++;
4091558Srgrimes				if (mode == IFREG || mode == IFDIR ||
4101558Srgrimes				    mode == IFLNK)
41198542Smckusick					fup->fu_curblocks += DIP(dp, di_blocks);
4121558Srgrimes			}
413207736Smckusick			if (qfu) {
41498542Smckusick				fup = addid((u_long)DIP(dp, di_uid), USRQUOTA,
415166143Smpp				    (char *)0, mntpt);
4161558Srgrimes				fup->fu_curinodes++;
4171558Srgrimes				if (mode == IFREG || mode == IFDIR ||
4181558Srgrimes				    mode == IFLNK)
41998542Smckusick					fup->fu_curblocks += DIP(dp, di_blocks);
4201558Srgrimes			}
4211558Srgrimes		}
4221558Srgrimes	}
4231558Srgrimes	freeinodebuf();
424207736Smckusick	if (qfu)
425207736Smckusick		errs += update(mntpt, qfu, USRQUOTA);
426207736Smckusick	if (qfg)
427207736Smckusick		errs += update(mntpt, qfg, GRPQUOTA);
4281558Srgrimes	close(fi);
429172168Smpp	(void)fflush(stdout);
4301558Srgrimes	return (errs);
4311558Srgrimes}
4321558Srgrimes
4331558Srgrimes/*
4341558Srgrimes * Update a specified quota file.
4351558Srgrimes */
4361558Srgrimesint
437207736Smckusickupdate(const char *fsname, struct quotafile *qf, int type)
4381558Srgrimes{
43989797Sphk	struct fileusage *fup;
440166143Smpp	u_long id, lastid, highid = 0;
4411558Srgrimes	struct dqblk dqbuf;
442166143Smpp	struct stat sb;
4431558Srgrimes	static struct dqblk zerodqbuf;
4441558Srgrimes	static struct fileusage zerofileusage;
4451558Srgrimes
446166143Smpp	/*
447166143Smpp	 * Scan the on-disk quota file and record any usage changes.
448166143Smpp	 */
449207736Smckusick	lastid = quota_maxid(qf);
450207736Smckusick	for (id = 0; id <= lastid; id++) {
451207736Smckusick		if (quota_read(qf, &dqbuf, id) < 0)
4521558Srgrimes			dqbuf = zerodqbuf;
453166143Smpp		if ((fup = lookup(id, type)) == NULL)
4541558Srgrimes			fup = &zerofileusage;
455166143Smpp		if (fup->fu_curinodes || fup->fu_curblocks ||
456166143Smpp		    dqbuf.dqb_bsoftlimit || dqbuf.dqb_bhardlimit ||
457166143Smpp		    dqbuf.dqb_isoftlimit || dqbuf.dqb_ihardlimit)
458166143Smpp			highid = id;
4591558Srgrimes		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
4601558Srgrimes		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
4611558Srgrimes			fup->fu_curinodes = 0;
4621558Srgrimes			fup->fu_curblocks = 0;
4631558Srgrimes			continue;
4641558Srgrimes		}
465166179Smpp		printchanges(fsname, type, &dqbuf, fup, id);
4661558Srgrimes		dqbuf.dqb_curinodes = fup->fu_curinodes;
4671558Srgrimes		dqbuf.dqb_curblocks = fup->fu_curblocks;
468207736Smckusick		(void) quota_write_usage(qf, &dqbuf, id);
4691558Srgrimes		fup->fu_curinodes = 0;
4701558Srgrimes		fup->fu_curblocks = 0;
4711558Srgrimes	}
472166143Smpp
473166143Smpp	/*
474166143Smpp	 * Walk the hash table looking for ids with non-zero usage
475166143Smpp	 * that are not currently recorded in the quota file. E.g.
476166143Smpp	 * ids that are past the end of the current file.
477166143Smpp	 */
478207736Smckusick	for (id = 0; id < FUHASH; id++) {
479207736Smckusick		for (fup = fuhead[type][id]; fup != NULL; fup = fup->fu_next) {
480166143Smpp			if (fup->fu_id <= lastid)
481166143Smpp				continue;
482166143Smpp			if (fup->fu_curinodes == 0 && fup->fu_curblocks == 0)
483166143Smpp				continue;
484166143Smpp			bzero(&dqbuf, sizeof(struct dqblk));
485166143Smpp			if (fup->fu_id > highid)
486166143Smpp				highid = fup->fu_id;
487207736Smckusick			printchanges(fsname, type, &dqbuf, fup, fup->fu_id);
488166143Smpp			dqbuf.dqb_curinodes = fup->fu_curinodes;
489166143Smpp			dqbuf.dqb_curblocks = fup->fu_curblocks;
490207736Smckusick			(void) quota_write_usage(qf, &dqbuf, fup->fu_id);
491166143Smpp			fup->fu_curinodes = 0;
492166143Smpp			fup->fu_curblocks = 0;
493166143Smpp		}
494166143Smpp	}
495207736Smckusick	/*
496207736Smckusick	 * If this is old format file, then size may be smaller,
497207736Smckusick	 * so ensure that we only truncate when it will make things
498207736Smckusick	 * smaller, and not if it will grow an old format file.
499207736Smckusick	 */
500207736Smckusick	if (highid < lastid &&
501207736Smckusick	    stat(quota_qfname(qf), &sb) == 0 &&
502207736Smckusick	    sb.st_size > (((off_t)highid + 2) * sizeof(struct dqblk)))
503207736Smckusick		truncate(quota_qfname(qf),
504207736Smckusick		    (((off_t)highid + 2) * sizeof(struct dqblk)));
5051558Srgrimes	return (0);
5061558Srgrimes}
5071558Srgrimes
5081558Srgrimes/*
5091558Srgrimes * Check to see if target appears in list of size cnt.
5101558Srgrimes */
5111558Srgrimesint
512180187Sdesoneof(char *target, char *list[], int cnt)
5131558Srgrimes{
51489797Sphk	int i;
5151558Srgrimes
5161558Srgrimes	for (i = 0; i < cnt; i++)
5171558Srgrimes		if (strcmp(target, list[i]) == 0)
5181558Srgrimes			return (i);
5191558Srgrimes	return (-1);
5201558Srgrimes}
5211558Srgrimes
5221558Srgrimes/*
5231558Srgrimes * Determine the group identifier for quota files.
5241558Srgrimes */
5251558Srgrimesint
526180187Sdesgetquotagid(void)
5271558Srgrimes{
5281558Srgrimes	struct group *gr;
5291558Srgrimes
53014280Smpp	if ((gr = getgrnam(quotagroup)) != NULL)
5311558Srgrimes		return (gr->gr_gid);
5321558Srgrimes	return (-1);
5331558Srgrimes}
5341558Srgrimes
5351558Srgrimes/*
5361558Srgrimes * Routines to manage the file usage table.
5371558Srgrimes *
5381558Srgrimes * Lookup an id of a specific type.
5391558Srgrimes */
5401558Srgrimesstruct fileusage *
541180187Sdeslookup(u_long id, int type)
5421558Srgrimes{
54389797Sphk	struct fileusage *fup;
5441558Srgrimes
5451558Srgrimes	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
5461558Srgrimes		if (fup->fu_id == id)
5471558Srgrimes			return (fup);
5481558Srgrimes	return (NULL);
5491558Srgrimes}
5501558Srgrimes
5511558Srgrimes/*
5521558Srgrimes * Add a new file usage id if it does not already exist.
5531558Srgrimes */
5541558Srgrimesstruct fileusage *
555207736Smckusickaddid(u_long id, int type, char *name, const char *fsname)
5561558Srgrimes{
5571558Srgrimes	struct fileusage *fup, **fhp;
5581558Srgrimes	int len;
5591558Srgrimes
56014280Smpp	if ((fup = lookup(id, type)) != NULL)
5611558Srgrimes		return (fup);
5621558Srgrimes	if (name)
5631558Srgrimes		len = strlen(name);
5641558Srgrimes	else
565166179Smpp		len = 0;
5661558Srgrimes	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
56737672Scharnier		errx(1, "calloc failed");
5681558Srgrimes	fhp = &fuhead[type][id & (FUHASH - 1)];
5691558Srgrimes	fup->fu_next = *fhp;
5701558Srgrimes	*fhp = fup;
5711558Srgrimes	fup->fu_id = id;
5721558Srgrimes	if (name)
5731558Srgrimes		bcopy(name, fup->fu_name, len + 1);
57414169Smpp	else {
57514169Smpp		(void)sprintf(fup->fu_name, "%lu", id);
576166143Smpp		if (vflag) {
577166143Smpp			if (aflag && fsname != NULL)
578166143Smpp				(void)printf("%s: ", fsname);
579180187Sdes			printf("unknown %cid: %lu\n",
58014169Smpp			    type == USRQUOTA ? 'u' : 'g', id);
581166143Smpp		}
58214169Smpp	}
5831558Srgrimes	return (fup);
5841558Srgrimes}
5851558Srgrimes
5861558Srgrimes/*
5871558Srgrimes * Special purpose version of ginode used to optimize pass
5881558Srgrimes * over all the inodes in numerical order.
5891558Srgrimes */
59098542Smckusickstatic ino_t nextino, lastinum, lastvalidinum;
59198542Smckusickstatic long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
59298542Smckusickstatic caddr_t inodebuf;
59398542Smckusick#define INOBUFSIZE	56*1024		/* size of buffer to read inodes */
5941558Srgrimes
59598542Smckusickunion dinode *
596107094Smckusickgetnextinode(ino_t inumber)
5971558Srgrimes{
5981558Srgrimes	long size;
59998542Smckusick	ufs2_daddr_t dblk;
60098542Smckusick	union dinode *dp;
60198542Smckusick	static caddr_t nextinop;
6021558Srgrimes
60398542Smckusick	if (inumber != nextino++ || inumber > lastvalidinum)
60426675Scharnier		errx(1, "bad inode number %d to nextinode", inumber);
6051558Srgrimes	if (inumber >= lastinum) {
6061558Srgrimes		readcnt++;
6071558Srgrimes		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
6081558Srgrimes		if (readcnt % readpercg == 0) {
6091558Srgrimes			size = partialsize;
6101558Srgrimes			lastinum += partialcnt;
6111558Srgrimes		} else {
6121558Srgrimes			size = inobufsize;
6131558Srgrimes			lastinum += fullcnt;
6141558Srgrimes		}
61598542Smckusick		/*
61698542Smckusick		 * If bread returns an error, it will already have zeroed
61798542Smckusick		 * out the buffer, so we do not need to do so here.
61898542Smckusick		 */
61998542Smckusick		bread(dblk, inodebuf, size);
62098542Smckusick		nextinop = inodebuf;
6211558Srgrimes	}
62298542Smckusick	dp = (union dinode *)nextinop;
62398542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC)
62498542Smckusick		nextinop += sizeof(struct ufs1_dinode);
62598542Smckusick	else
62698542Smckusick		nextinop += sizeof(struct ufs2_dinode);
62798542Smckusick	return (dp);
6281558Srgrimes}
6291558Srgrimes
6301558Srgrimes/*
6311558Srgrimes * Prepare to scan a set of inodes.
6321558Srgrimes */
6331558Srgrimesvoid
634107094Smckusicksetinodebuf(ino_t inum)
6351558Srgrimes{
6361558Srgrimes
637107094Smckusick	if (inum % sblock.fs_ipg != 0)
638107094Smckusick		errx(1, "bad inode number %d to setinodebuf", inum);
639107094Smckusick	lastvalidinum = inum + sblock.fs_ipg - 1;
640107094Smckusick	nextino = inum;
641107094Smckusick	lastinum = inum;
6421558Srgrimes	readcnt = 0;
643107094Smckusick	if (inodebuf != NULL)
644107094Smckusick		return;
6451558Srgrimes	inobufsize = blkroundup(&sblock, INOBUFSIZE);
64698542Smckusick	fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
64798542Smckusick	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
6481558Srgrimes	readpercg = sblock.fs_ipg / fullcnt;
6491558Srgrimes	partialcnt = sblock.fs_ipg % fullcnt;
65098542Smckusick	partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
65198542Smckusick	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
6521558Srgrimes	if (partialcnt != 0) {
6531558Srgrimes		readpercg++;
6541558Srgrimes	} else {
6551558Srgrimes		partialcnt = fullcnt;
6561558Srgrimes		partialsize = inobufsize;
6571558Srgrimes	}
658107094Smckusick	if ((inodebuf = malloc((unsigned)inobufsize)) == NULL)
659107094Smckusick		errx(1, "cannot allocate space for inode buffer");
6601558Srgrimes}
6611558Srgrimes
6621558Srgrimes/*
6631558Srgrimes * Free up data structures used to scan inodes.
6641558Srgrimes */
6651558Srgrimesvoid
666180187Sdesfreeinodebuf(void)
6671558Srgrimes{
6681558Srgrimes
6691558Srgrimes	if (inodebuf != NULL)
6701558Srgrimes		free(inodebuf);
6711558Srgrimes	inodebuf = NULL;
6721558Srgrimes}
6731558Srgrimes
6741558Srgrimes/*
6751558Srgrimes * Read specified disk blocks.
6761558Srgrimes */
6771558Srgrimesvoid
678180187Sdesbread(ufs2_daddr_t bno, char *buf, long cnt)
6791558Srgrimes{
6801558Srgrimes
6811558Srgrimes	if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
6821558Srgrimes	    read(fi, buf, cnt) != cnt)
683107094Smckusick		errx(1, "bread failed on block %ld", (long)bno);
6841558Srgrimes}
685166143Smpp
686166143Smpp/*
687166143Smpp * Display updated block and i-node counts.
688166143Smpp */
689166143Smppvoid
690207736Smckusickprintchanges(const char *fsname, int type, struct dqblk *dp,
691180187Sdes    struct fileusage *fup, u_long id)
692166143Smpp{
693166143Smpp	if (!vflag)
694166143Smpp		return;
695166143Smpp	if (aflag)
696166143Smpp		(void)printf("%s: ", fsname);
697166179Smpp	if (fup->fu_name[0] == '\0')
698166179Smpp		(void)printf("%-8lu fixed ", id);
699166179Smpp	else
700166179Smpp		(void)printf("%-8s fixed ", fup->fu_name);
701166179Smpp	switch (type) {
702166179Smpp
703166179Smpp	case GRPQUOTA:
704166179Smpp		(void)printf("(group):");
705166179Smpp		break;
706166179Smpp
707166179Smpp	case USRQUOTA:
708166179Smpp		(void)printf("(user): ");
709166179Smpp		break;
710166179Smpp
711166179Smpp	default:
712166179Smpp		(void)printf("(unknown quota type %d)", type);
713166179Smpp		break;
714166179Smpp	}
715166143Smpp	if (dp->dqb_curinodes != fup->fu_curinodes)
716166143Smpp		(void)printf("\tinodes %lu -> %lu", (u_long)dp->dqb_curinodes,
717166143Smpp		    (u_long)fup->fu_curinodes);
718166143Smpp	if (dp->dqb_curblocks != fup->fu_curblocks)
719180187Sdes		(void)printf("\tblocks %lu -> %lu",
720166143Smpp		    (u_long)dp->dqb_curblocks,
721166143Smpp		    (u_long)fup->fu_curblocks);
722166143Smpp	(void)printf("\n");
723166143Smpp}
724