1/*
2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright (c) 1980, 1990, 1993
25 *	The Regents of the University of California.  All rights reserved.
26 *
27 * This code is derived from software contributed to Berkeley by
28 * Robert Elz at The University of Melbourne.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 *    notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 *    notice, this list of conditions and the following disclaimer in the
37 *    documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 *    must display the following acknowledgement:
40 *	This product includes software developed by the University of
41 *	California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 *    may be used to endorse or promote products derived from this software
44 *    without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 */
58
59
60/*
61 * Fix up / report on disk quotas & usage
62 */
63#include <sys/param.h>
64#include <sys/stat.h>
65#include <sys/queue.h>
66#ifdef __APPLE__
67#include <sys/mount.h>
68#endif /* __APPLE__ */
69
70#include <sys/quota.h>
71
72#include <fcntl.h>
73#include <fstab.h>
74#include <pwd.h>
75#include <grp.h>
76#include <errno.h>
77#include <unistd.h>
78#include <stdio.h>
79#include <stdlib.h>
80#include <string.h>
81#include <err.h>
82
83#ifdef __APPLE__
84#include <libkern/OSByteOrder.h>
85#endif /* __APPLE__ */
86
87#include "quotacheck.h"
88
89char *qfname = QUOTAFILENAME;
90char *qfextension[] = INITQFNAMES;
91char *quotagroup = QUOTAGROUP;
92
93#ifdef __APPLE__
94u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
95#endif /* __APPLE__ */
96
97
98#define FUHASH 1024	/* must be power of two */
99struct fileusage *fuhead[MAXQUOTAS][FUHASH];
100
101int	aflag;			/* all file systems */
102int	gflag;			/* check group quotas */
103int	uflag;			/* check user quotas */
104int	vflag;			/* verbose */
105
106#ifdef __APPLE__
107int	maxentries;		/* maximum entries in disk quota file */
108#else
109u_long	highid[MAXQUOTAS];	/* highest addid()'ed identifier per type */
110#endif /* __APPLE__ */
111
112
113#ifdef __APPLE__
114char *	blockcheck(char *);
115int	chkquota(char *, char *, char *, struct quotaname *);
116int	getquotagid(void);
117int	hasquota(struct statfs *, int, char **);
118struct fileusage *
119	lookup(u_long, int);
120void *	needchk(struct statfs *);
121int	oneof(char *, char*[], int);
122int	qfinsert(FILE *, struct dqblk *, int, int);
123int	qfmaxentries(char *, int);
124void	usage(void);
125void	dqbuftohost(struct dqblk *dqbp);
126
127#else
128struct fileusage *
129	 addid __P((uid_t, int, char *));
130char	*blockcheck __P((char *));
131int	 chkquota __P((char *, char *, struct quotaname *));
132int	 getquotagid __P((void));
133int	 hasquota __P((struct fstab *, int, char **));
134struct fileusage *
135	 lookup __P((u_long, int));
136void	*needchk __P((struct fstab *));
137int	 oneof __P((char *, char*[], int));
138int	 update __P((char *, char *, int));
139void	 usage __P((void));
140#endif /* __APPLE__ */
141
142
143int
144main(argc, argv)
145	int argc;
146	char *argv[];
147{
148#ifndef __APPLE__
149	register struct fstab *fs;
150	register struct passwd *pw;
151	register struct group *gr;
152#endif /* !__APPLE__ */
153	struct quotaname *auxdata;
154	int i, argnum, maxrun, errs;
155	long done = 0;
156	char ch, *name;
157
158#ifdef __APPLE__
159        int nfst;
160        struct statfs *fst;
161#endif /* __APPLE__ */
162
163	errs = maxrun = 0;
164	while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
165		switch(ch) {
166		case 'a':
167			aflag++;
168			break;
169		case 'g':
170			gflag++;
171			break;
172		case 'u':
173			uflag++;
174			break;
175		case 'v':
176			vflag++;
177			break;
178		case 'l':
179			maxrun = atoi(optarg);
180			break;
181		default:
182			usage();
183		}
184	}
185	argc -= optind;
186	argv += optind;
187	if ((argc == 0 && !aflag) || (argc > 0 && aflag))
188		usage();
189	if (!gflag && !uflag) {
190		gflag++;
191		uflag++;
192	}
193
194#ifdef __APPLE__
195	nfst = getmntinfo(&fst, MNT_WAIT);
196	if (nfst==0) {
197		fprintf(stderr, "quotacheck: no mounted filesystems\n");
198		exit(1);
199	}
200
201	for (i=0; i<nfst; i++) {
202		if(strcmp(fst[i].f_fstypename, "hfs")) {
203			continue;
204		}
205
206		if (aflag) {
207			if ((auxdata = needchk(&fst[i])) &&
208			    (name = blockcheck(fst[i].f_mntfromname))) {
209				errs += chkquota(fst[i].f_fstypename, name, fst[i].f_mntonname, auxdata);
210			}
211
212			if (i+1 == nfst)
213				exit (errs);
214			else
215				continue;
216	  	}
217
218		if (((argnum = oneof(fst[i].f_mntonname, argv, argc)) >= 0 ||
219		    (argnum = oneof(fst[i].f_mntfromname, argv, argc)) >= 0) &&
220		    (auxdata = needchk(&fst[i])) &&
221		    (name = blockcheck(fst[i].f_mntfromname))) {
222			done |= 1 << argnum;
223			errs += chkquota(fst[i].f_fstypename, name, fst[i].f_mntonname, auxdata);
224		}
225	} /* end for loop */
226
227	for (i = 0; i < argc; i++)
228		if ((done & (1 << i)) == 0)
229			fprintf(stderr, "%s not identified as a quota filesystem\n",
230				argv[i]);
231
232	exit(errs);
233#else
234	if (gflag) {
235		setgrent();
236		while ((gr = getgrent()) != 0)
237			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
238		endgrent();
239	}
240	if (uflag) {
241		setpwent();
242		while ((pw = getpwent()) != 0)
243			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
244		endpwent();
245	}
246	if (aflag)
247		exit(checkfstab(1, maxrun, needchk, chkquota));
248	if (setfsent() == 0)
249		err(1, "%s: can't open", FSTAB);
250	while ((fs = getfsent()) != NULL) {
251		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
252		    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
253		    (auxdata = needchk(fs)) &&
254		    (name = blockcheck(fs->fs_spec))) {
255			done |= 1 << argnum;
256			errs += chkquota(name, fs->fs_file, auxdata);
257		}
258	}
259	endfsent();
260	for (i = 0; i < argc; i++)
261		if ((done & (1 << i)) == 0)
262			fprintf(stderr, "%s not found in %s\n",
263				argv[i], FSTAB);
264	exit(errs);
265#endif /* __APPLE__ */
266}
267
268void
269usage()
270{
271	(void)fprintf(stderr, "usage:\t%s\n\t%s\n",
272		"quotacheck -a [-guv]",
273		"quotacheck [-guv] filesys ...");
274	exit(1);
275}
276
277#ifdef __APPLE__
278void *
279needchk(fst)
280	struct statfs *fst;
281{
282	register struct quotaname *qnp;
283	char *qfnp;
284
285	if(strcmp(fst->f_fstypename, "hfs")) {
286		return(NULL);
287	}
288	if(fst->f_flags & MNT_RDONLY)
289		return (NULL);
290	if ((qnp = malloc(sizeof(*qnp))) == NULL)
291		err(1, "%s", strerror(errno));
292	qnp->flags = 0;
293	if (gflag && hasquota(fst, GRPQUOTA, &qfnp)) {
294		strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname));
295		qnp->flags |= HASGRP;
296	}
297	if (uflag && hasquota(fst, USRQUOTA, &qfnp)) {
298		strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname));
299		qnp->flags |= HASUSR;
300	}
301	if (qnp->flags)
302		return (qnp);
303	free(qnp);
304	return (NULL);
305}
306#else
307void *
308needchk(fs)
309	register struct fstab *fs;
310{
311	register struct quotaname *qnp;
312	char *qfnp;
313
314	if (strcmp(fs->fs_type, FSTAB_RW))
315		return (NULL);
316
317	if ((qnp = malloc(sizeof(*qnp))) == NULL)
318		err(1, "%s", strerror(errno));
319	qnp->flags = 0;
320	if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
321		strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname));
322		qnp->flags |= HASGRP;
323	}
324	if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
325		strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname));
326		qnp->flags |= HASUSR;
327	}
328	if (qnp->flags)
329		return (qnp);
330	free(qnp);
331	return (NULL);
332}
333#endif /* __APPLE__ */
334
335/*
336 * Scan the specified filesystem to check quota(s) present on it.
337 */
338int
339chkquota(fstype, fsname, mntpt, qnp)
340	char *fstype, *fsname, *mntpt;
341	register struct quotaname *qnp;
342{
343	int errs = 1;
344
345	if (vflag) {
346		fprintf(stdout, "*** Checking ");
347		if (qnp->flags & HASUSR)
348			fprintf(stdout, "%s%s", qfextension[USRQUOTA],
349			    (qnp->flags & HASGRP) ? " and " : "");
350		if (qnp->flags & HASGRP)
351			fprintf(stdout, "%s", qfextension[GRPQUOTA]);
352		fprintf(stdout, " quotas for %s (%s)\n", fsname, mntpt);
353	}
354
355	if(strcmp(fstype, "hfs") == 0)
356		errs = chkquota_hfs(fsname, mntpt, qnp);
357
358	return (errs);
359}
360
361/*
362 * Update a specified quota file.
363 */
364#ifdef __APPLE__
365int
366update(fsname, quotafile, type)
367	char *fsname, *quotafile;
368	register int type;
369{
370	register struct fileusage *fup;
371	register FILE *qfi, *qfo;
372	register u_long i;
373	struct dqblk dqbuf;
374	struct dqfilehdr dqhdr = {0};
375	int m, shift;
376	int idcnt = 0;
377	static int warned = 0;
378	static struct dqblk zerodqbuf;
379	static struct fileusage zerofileusage;
380
381	if ((qfo = fopen(quotafile, "r+")) == NULL) {
382		if (errno == ENOENT)
383			qfo = fopen(quotafile, "w+");
384		if (qfo) {
385			fprintf(stderr,
386			    "quotacheck: creating quota file %s\n", quotafile);
387#define	MODE	(S_IRUSR|S_IWUSR|S_IRGRP)
388			(void) fchown(fileno(qfo), getuid(), getquotagid());
389			(void) fchmod(fileno(qfo), MODE);
390		} else {
391			fprintf(stderr,
392			    "quotacheck: %s: %s\n", quotafile, strerror(errno));
393			return (1);
394		}
395	}
396	if ((qfi = fopen(quotafile, "r")) == NULL) {
397		fprintf(stderr,
398		    "quotacheck: %s: %s\n", quotafile, strerror(errno));
399		(void) fclose(qfo);
400		return (1);
401	}
402	if (quotactl(fsname, QCMD(Q_SYNC, type), 0, 0) < 0 &&
403		errno == ENOTSUP && !warned && vflag) {
404		warned++;
405		fprintf(stdout, "*** Warning: %s\n",
406		    "Quotas are not compiled into this kernel");
407	}
408
409	/* Read in the quota file header. */
410	if (fread((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qfi) > 0) {
411		/* Check for reverse endian file. */
412		if (OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type] &&
413		    OSSwapInt32(dqhdr.dqh_magic) == quotamagic[type]) {
414			fprintf(stderr,
415			    "quotacheck: %s: not in big endian byte order\n",
416			    quotafile);
417			(void) fclose(qfo);
418			(void) fclose(qfi);
419			return (1);
420		}
421		/* Sanity check the quota file header. */
422		if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) ||
423		    (OSSwapBigToHostInt32(dqhdr.dqh_version) > QF_VERSION) ||
424		    (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) {
425			fprintf(stderr,
426			    "quotacheck: %s: not a valid quota file\n",
427			    quotafile);
428			(void) fclose(qfo);
429			(void) fclose(qfi);
430			return (1);
431		}
432		m = OSSwapBigToHostInt32(dqhdr.dqh_maxentries);
433	} else /* empty file */ {
434		if (maxentries)
435			m = maxentries;
436		else
437			m = qfmaxentries(fsname, type);
438
439		ftruncate(fileno(qfo), (off_t)((m + 1) * sizeof(struct dqblk)));
440
441		/* Initialize file header in big endian. */
442		dqhdr.dqh_magic = OSSwapHostToBigInt32(quotamagic[type]);
443		dqhdr.dqh_version = OSSwapHostToBigConstInt32(QF_VERSION);
444		dqhdr.dqh_maxentries = OSSwapHostToBigInt32(m);
445		dqhdr.dqh_btime = OSSwapHostToBigConstInt32(MAX_DQ_TIME);
446		dqhdr.dqh_itime = OSSwapHostToBigConstInt32(MAX_IQ_TIME);
447		memmove(dqhdr.dqh_string, QF_STRING_TAG, strlen(QF_STRING_TAG));
448		goto orphans;  /* just insert all new records */
449	}
450
451	/* Examine all the entries in the quota file. */
452	for (i = 0; i < m; i++) {
453		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) {
454			fprintf(stderr,
455			    "quotacheck: problem reading at index %ld\n", i);
456			continue;
457		}
458		if (dqbuf.dqb_id == 0)
459			continue;
460
461		++idcnt;
462		if ((fup = lookup(OSSwapBigToHostInt32(dqbuf.dqb_id), type)) == 0)
463			fup = &zerofileusage;
464		else
465			fup->fu_checked = 1;
466
467		if (OSSwapBigToHostInt32(dqbuf.dqb_curinodes) == fup->fu_curinodes &&
468		    OSSwapBigToHostInt64(dqbuf.dqb_curbytes) == fup->fu_curbytes) {
469			fup->fu_curinodes = 0;
470			fup->fu_curbytes = 0;
471			continue;
472		}
473		if (vflag) {
474			if (aflag)
475				fprintf(stdout, "%s: ", fsname);
476			fprintf(stdout, "%-12s fixed:", fup->fu_name);
477			if (OSSwapBigToHostInt32(dqbuf.dqb_curinodes) != fup->fu_curinodes)
478				fprintf(stdout, "\tinodes %u -> %u",
479				    OSSwapBigToHostInt32(dqbuf.dqb_curinodes), fup->fu_curinodes);
480			if (OSSwapBigToHostInt64(dqbuf.dqb_curbytes) != fup->fu_curbytes)
481				fprintf(stdout, "\t1K blocks %qd -> %qd",
482				    (OSSwapBigToHostInt64(dqbuf.dqb_curbytes)/1024), (fup->fu_curbytes/1024));
483			fprintf(stdout, "\n");
484		}
485		/*
486		 * Reset time limit if have a soft limit and were
487		 * previously under it, but are now over it.
488		 */
489		if (dqbuf.dqb_bsoftlimit != 0 &&
490		    OSSwapBigToHostInt64(dqbuf.dqb_curbytes) < OSSwapBigToHostInt64(dqbuf.dqb_bsoftlimit) &&
491		    fup->fu_curbytes >= OSSwapBigToHostInt64(dqbuf.dqb_bsoftlimit))
492			dqbuf.dqb_btime = 0;
493		if (dqbuf.dqb_isoftlimit != 0 &&
494		    OSSwapBigToHostInt32(dqbuf.dqb_curinodes) < OSSwapBigToHostInt32(dqbuf.dqb_isoftlimit) &&
495		    fup->fu_curinodes >= OSSwapBigToHostInt32(dqbuf.dqb_isoftlimit))
496			dqbuf.dqb_itime = 0;
497		dqbuf.dqb_curinodes = OSSwapHostToBigInt32(fup->fu_curinodes);
498		dqbuf.dqb_curbytes = OSSwapHostToBigInt64(fup->fu_curbytes);
499
500		/* Write dqblk in big endian. */
501		fseek(qfo, dqoffset(i), SEEK_SET);
502		fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
503
504		dqbuftohost(&dqbuf);
505		(void) quotactl(fsname, QCMD(Q_SETUSE, type), dqbuf.dqb_id,
506		    (caddr_t)&dqbuf);
507		fup->fu_curinodes = 0;
508		fup->fu_curbytes = 0;
509	}
510orphans:
511	/* Look for any fileusage orphans */
512
513	shift = dqhashshift(m);
514	for (i = 0; i < FUHASH; ++i) {
515		for (fup = fuhead[type][i]; fup != 0; fup = fup->fu_next) {
516			if (fup->fu_checked || fup->fu_id == 0)
517				continue;
518			if (vflag) {
519				if (aflag)
520					fprintf(stdout, "%s: ", fsname);
521				fprintf(stdout,
522				    "%-12s added:\tinodes %u\t1K blocks %qd\n",
523				    fup->fu_name, fup->fu_curinodes,
524				    (fup->fu_curbytes/1024));
525			}
526			/* Initialize dqbuf in big endian. */
527			dqbuf = zerodqbuf;
528			dqbuf.dqb_id = OSSwapHostToBigInt32(fup->fu_id);
529			dqbuf.dqb_curinodes = OSSwapHostToBigInt32(fup->fu_curinodes);
530			dqbuf.dqb_curbytes = OSSwapHostToBigInt64(fup->fu_curbytes);
531			/* insert this dqb */
532			if (qfinsert(qfo, &dqbuf, m, shift)) {
533				i = FUHASH;
534				break;
535			}
536
537			dqbuftohost(&dqbuf);
538			(void) quotactl(fsname, QCMD(Q_SETUSE, type),
539				dqbuf.dqb_id, (caddr_t)&dqbuf);
540			++idcnt;
541		}
542	}
543
544	/* Write the quota file header */
545	dqhdr.dqh_entrycnt = OSSwapHostToBigInt32(idcnt);
546	fseek(qfo, (long)0, SEEK_SET);
547	fwrite((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qfo);
548
549	fclose(qfi);
550	fflush(qfo);
551	fclose(qfo);
552	return (0);
553}
554
555/* Convert a dqblk to host native byte order. */
556void
557dqbuftohost(struct dqblk *dqbp)
558{
559	dqbp->dqb_bhardlimit = OSSwapBigToHostInt64(dqbp->dqb_bhardlimit);
560	dqbp->dqb_bsoftlimit = OSSwapBigToHostInt64(dqbp->dqb_bsoftlimit);
561	dqbp->dqb_curbytes   = OSSwapBigToHostInt64(dqbp->dqb_curbytes);
562	dqbp->dqb_ihardlimit = OSSwapBigToHostInt32(dqbp->dqb_ihardlimit);
563	dqbp->dqb_isoftlimit = OSSwapBigToHostInt32(dqbp->dqb_isoftlimit);
564	dqbp->dqb_curinodes  = OSSwapBigToHostInt32(dqbp->dqb_curinodes);
565	dqbp->dqb_btime      = OSSwapBigToHostInt32(dqbp->dqb_btime);
566	dqbp->dqb_itime      = OSSwapBigToHostInt32(dqbp->dqb_itime);
567	dqbp->dqb_id         = OSSwapBigToHostInt32(dqbp->dqb_id);
568}
569
570#else
571int
572update(fsname, quotafile, type)
573	char *fsname, *quotafile;
574	register int type;
575{
576	register struct fileusage *fup;
577	register FILE *qfi, *qfo;
578	register u_long id, lastid;
579	struct dqblk dqbuf;
580	static int warned = 0;
581	static struct dqblk zerodqbuf;
582	static struct fileusage zerofileusage;
583
584	if ((qfo = fopen(quotafile, "r+")) == NULL) {
585		if (errno == ENOENT)
586			qfo = fopen(quotafile, "w+");
587		if (qfo) {
588			(void) fprintf(stderr,
589			    "quotacheck: creating quota file %s\n", quotafile);
590#define	MODE	(S_IRUSR|S_IWUSR|S_IRGRP)
591			(void) fchown(fileno(qfo), getuid(), getquotagid());
592			(void) fchmod(fileno(qfo), MODE);
593		} else {
594			(void) fprintf(stderr,
595			    "quotacheck: %s: %s\n", quotafile, strerror(errno));
596			return (1);
597		}
598	}
599	if ((qfi = fopen(quotafile, "r")) == NULL) {
600		(void) fprintf(stderr,
601		    "quotacheck: %s: %s\n", quotafile, strerror(errno));
602		(void) fclose(qfo);
603		return (1);
604	}
605	if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
606	    errno == ENOTSUP && !warned && vflag) {
607		warned++;
608		(void)printf("*** Warning: %s\n",
609		    "Quotas are not compiled into this kernel");
610	}
611	for (lastid = highid[type], id = 0; id <= lastid; id++) {
612		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
613			dqbuf = zerodqbuf;
614		if ((fup = lookup(id, type)) == 0)
615			fup = &zerofileusage;
616		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
617		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
618			fup->fu_curinodes = 0;
619			fup->fu_curblocks = 0;
620			fseek(qfo, (long)sizeof(struct dqblk), 1);
621			continue;
622		}
623		if (vflag) {
624			if (aflag)
625				printf("%s: ", fsname);
626			printf("%-8s fixed:", fup->fu_name);
627			if (dqbuf.dqb_curinodes != fup->fu_curinodes)
628				(void)printf("\tinodes %d -> %d",
629					dqbuf.dqb_curinodes, fup->fu_curinodes);
630			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
631				(void)printf("\tblocks %d -> %d",
632					dqbuf.dqb_curblocks, fup->fu_curblocks);
633			(void)printf("\n");
634		}
635		/*
636		 * Reset time limit if have a soft limit and were
637		 * previously under it, but are now over it.
638		 */
639		if (dqbuf.dqb_bsoftlimit &&
640		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
641		    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
642			dqbuf.dqb_btime = 0;
643		if (dqbuf.dqb_isoftlimit &&
644		    dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
645		    fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
646			dqbuf.dqb_itime = 0;
647		dqbuf.dqb_curinodes = fup->fu_curinodes;
648		dqbuf.dqb_curblocks = fup->fu_curblocks;
649		fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
650		(void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
651		    (caddr_t)&dqbuf);
652		fup->fu_curinodes = 0;
653		fup->fu_curblocks = 0;
654	}
655	fclose(qfi);
656	fflush(qfo);
657	ftruncate(fileno(qfo),
658	    (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
659	fclose(qfo);
660	return (0);
661}
662#endif /* __APPLE__ */
663
664#ifdef __APPLE__
665/*
666 * Insert an entry into a quota file.
667 *
668 * The dqblk pointed to by dqbp is in big endian.
669 */
670int
671qfinsert(file, dqbp, maxentries, shift)
672	FILE *file;
673	struct dqblk *dqbp;
674	int maxentries, shift;
675{
676	struct dqblk dqbuf;
677	int i, skip, last;
678	u_int32_t mask;
679	u_int32_t id;
680	off_t offset;
681
682	id = OSSwapBigToHostInt32(dqbp->dqb_id);
683	if (id == 0)
684		return (0);
685	mask = maxentries - 1;
686	i = dqhash1(id, dqhashshift(maxentries), mask);
687	skip = dqhash2(id, mask);
688
689	for (last = (i + (maxentries-1) * skip) & mask;
690	     i != last;
691	     i = (i + skip) & mask) {
692		offset = dqoffset(i);
693		fseek(file, (long)offset, SEEK_SET);
694		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, file) == 0) {
695			fprintf(stderr, "quotacheck: read error at index %d\n", i);
696			return (EIO);
697		}
698		/*
699		 * Stop when an empty entry is found
700		 * or we encounter a matching id.
701		 */
702		if (dqbuf.dqb_id == 0 || dqbuf.dqb_id == dqbp->dqb_id) {
703			dqbuf = *dqbp;
704			fseek(file, (long)offset, SEEK_SET);
705			fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, file);
706			return (0);
707		}
708	}
709	fprintf(stderr, "quotacheck: exceeded maximum entries (%d)\n", maxentries);
710	return (ENOSPC);
711}
712
713#define ONEGIGABYTE        (1024*1024*1024)
714
715/*
716 * Calculate the size of the hash table from the size of
717 * the file system.  The open addressing hashing used on
718 * the quota file assumes that this table will never be
719 * more than 90% full.
720 */
721int
722qfmaxentries(mntpt, type)
723	char *mntpt;
724	int type;
725{
726	struct statfs fs_stat;
727	u_int64_t fs_size;
728	int max = 0;
729
730	if (statfs(mntpt, &fs_stat)) {
731		fprintf(stderr, "quotacheck: %s: %s\n",
732		    mntpt, strerror(errno));
733		return (0);
734	}
735	fs_size = (u_int64_t)fs_stat.f_blocks * (u_int64_t)fs_stat.f_bsize;
736
737	if (type == USRQUOTA) {
738		max = QF_USERS_PER_GB * (fs_size / ONEGIGABYTE);
739
740		if (max < QF_MIN_USERS)
741			max = QF_MIN_USERS;
742		else if (max > QF_MAX_USERS)
743			max = QF_MAX_USERS;
744	} else if (type == GRPQUOTA) {
745		max = QF_GROUPS_PER_GB * (fs_size / ONEGIGABYTE);
746
747		if (max < QF_MIN_GROUPS)
748			max = QF_MIN_GROUPS;
749		else if (max > QF_MAX_GROUPS)
750			max = QF_MAX_GROUPS;
751	}
752	/* Round up to a power of 2 */
753	if (max && !powerof2(max)) {
754		int x = max;
755		max = 4;
756		while (x>>1 != 1) {
757			x = x >> 1;
758			max = max << 1;
759		}
760	}
761	return (max);
762}
763#endif /* __APPLE__ */
764
765/*
766 * Check to see if target appears in list of size cnt.
767 */
768int
769oneof(target, list, cnt)
770	register char *target, *list[];
771	int cnt;
772{
773	register int i;
774
775	for (i = 0; i < cnt; i++)
776		if (strcmp(target, list[i]) == 0)
777			return (i);
778	return (-1);
779}
780
781/*
782 * Determine the group identifier for quota files.
783 */
784int
785getquotagid()
786{
787	struct group *gr;
788
789	if ((gr = getgrnam(quotagroup)))
790		return (gr->gr_gid);
791	return (-1);
792}
793
794/*
795 * Check to see if a particular quota is to be enabled.
796 */
797#ifdef __APPLE__
798int
799hasquota(fst, type, qfnamep)
800	struct statfs *fst;
801	int type;
802	char **qfnamep;
803{
804	struct stat sb;
805	static char initname, usrname[100], grpname[100];
806	static char buf[BUFSIZ];
807
808	if (!initname) {
809		(void)snprintf(usrname, sizeof(usrname),
810		    "%s%s", qfextension[USRQUOTA], qfname);
811		(void)snprintf(grpname, sizeof(grpname),
812		    "%s%s", qfextension[GRPQUOTA], qfname);
813		initname = 1;
814	}
815
816        /*
817	  We only support the default path to the
818	  on disk quota files.
819	*/
820
821        (void)snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname,
822		       QUOTAOPSNAME, qfextension[type] );
823        if (stat(buf, &sb) != 0) {
824          /* There appears to be no mount option file */
825          return(0);
826        }
827
828	(void)snprintf(buf, sizeof(buf),
829		       "%s/%s.%s", fst->f_mntonname, qfname, qfextension[type]);
830	*qfnamep = buf;
831
832	return (1);
833}
834#else
835int
836hasquota(fs, type, qfnamep)
837	register struct fstab *fs;
838	int type;
839	char **qfnamep;
840{
841	register char *opt;
842	char *cp;
843	static char initname, usrname[100], grpname[100];
844	static char buf[BUFSIZ];
845
846	if (!initname) {
847		(void)snprintf(usrname, sizeof(usrname),
848		    "%s%s", qfextension[USRQUOTA], qfname);
849		(void)snprintf(grpname, sizeof(grpname),
850		    "%s%s", qfextension[GRPQUOTA], qfname);
851		initname = 1;
852	}
853	strlcpy(buf, fs->fs_mntops, sizeof(buf));
854	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
855		if (cp = strchr(opt, '='))
856			*cp++ = '\0';
857		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
858			break;
859		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
860			break;
861	}
862	if (!opt)
863		return (0);
864	if (cp)
865		*qfnamep = cp;
866	else {
867		(void)snprintf(buf, sizeof(buf),
868		    "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
869		*qfnamep = buf;
870	}
871	return (1);
872}
873#endif /* __APPLE__ */
874
875/*
876 * Routines to manage the file usage table.
877 *
878 * Lookup an id of a specific type.
879 */
880struct fileusage *
881lookup(id, type)
882	u_long id;
883	int type;
884{
885	register struct fileusage *fup;
886
887	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
888		if (fup->fu_id == id)
889			return (fup);
890	return (NULL);
891}
892
893/*
894 * Add a new file usage id if it does not already exist.
895 */
896#ifdef __APPLE__
897struct fileusage *
898addid(id, type)
899	uid_t id;
900	int type;
901{
902	struct fileusage *fup, **fhp;
903	struct passwd *pw;
904	struct group *gr;
905	char *name;
906	size_t len;
907
908	if ((fup = lookup(id, type)))
909		return (fup);
910
911	name = NULL;
912	len = 10;
913	switch (type) {
914	case USRQUOTA:
915		if ((pw = getpwuid(id)) != 0) {
916			name = pw->pw_name;
917			len = strlen(name);
918		}
919		break;
920	case GRPQUOTA:
921		if ((gr = getgrgid(id)) != 0) {
922			name = gr->gr_name;
923			len = strlen(name);
924		}
925		break;
926	}
927
928	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
929		err(1, "%s", strerror(errno));
930	fhp = &fuhead[type][id & (FUHASH - 1)];
931	fup->fu_next = *fhp;
932	*fhp = fup;
933	fup->fu_id = id;
934	if (name)
935		memmove(fup->fu_name, name, len + 1);
936	else
937		(void)sprintf(fup->fu_name, "%u", (unsigned int)id);
938
939	return (fup);
940}
941#else
942struct fileusage *
943addid(id, type, name)
944	u_long id;
945	int type;
946	char *name;
947{
948	struct fileusage *fup, **fhp;
949	int len;
950
951	if (fup = lookup(id, type))
952		return (fup);
953	if (name)
954		len = strlen(name);
955	else
956		len = 10;
957	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
958		err(1, "%s", strerror(errno));
959	fhp = &fuhead[type][id & (FUHASH - 1)];
960	fup->fu_next = *fhp;
961	*fhp = fup;
962	fup->fu_id = id;
963	if (id > highid[type])
964		highid[type] = id;
965	if (name)
966		memmove(fup->fu_name, name, len + 1);
967	else
968		(void)sprintf(fup->fu_name, "%u", id);
969	return (fup);
970}
971#endif /* __APPLE__ */
972
973