1/*
2 * Copyright (c) 2002-2007 Apple 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 * Disk quota reporting program.
62 */
63#include <sys/param.h>
64#include <sys/file.h>
65#ifdef __APPLE__
66#include <sys/mount.h>
67#endif /* __APPLE */
68#include <sys/stat.h>
69#include <sys/queue.h>
70
71#include <sys/quota.h>
72#include <libkern/OSByteOrder.h>
73
74#include <ctype.h>
75#include <errno.h>
76#include <fstab.h>
77#include <grp.h>
78#include <pwd.h>
79#include <stdio.h>
80#include <stdlib.h>
81#include <string.h>
82#include <unistd.h>
83
84char *qfname = QUOTAFILENAME;
85char *qfextension[] = INITQFNAMES;
86
87#ifdef __APPLE__
88u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
89#endif /* __APPLE__ */
90
91struct quotause {
92	struct	quotause *next;
93	long	flags;
94	struct	dqblk dqblk;
95	char	fsname[MAXPATHLEN + 1];
96} *getprivs();
97#define	FOUND	0x01
98
99int	qflag;
100int	vflag;
101
102int	alldigits __P((char *));
103int	hasquota __P((struct statfs *, int, char **));
104void	heading __P((int, u_long, char *, char *));
105void	showgid __P((u_long));
106void 	showuid __P((u_long));
107void	showgrpname __P((char *));
108void	showquotas __P((int, u_long, char *));
109void	showusrname __P((char *));
110void	usage __P((void));
111
112#ifdef __APPLE__
113int	qflookup(int, u_long, int, struct dqblk *);
114#endif /* __APPLE__ */
115
116int
117main(argc, argv)
118	char *argv[];
119{
120	int ngroups;
121	gid_t gidset[NGROUPS];
122	int i, gflag = 0, uflag = 0;
123	char ch;
124
125#if 0
126	if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == ENOTSUP) {
127		fprintf(stderr, "There are no quotas on this system\n");
128		exit(0);
129	}
130#endif
131
132	while ((ch = getopt(argc, argv, "ugvq")) != EOF) {
133		switch(ch) {
134		case 'g':
135			gflag++;
136			break;
137		case 'u':
138			uflag++;
139			break;
140		case 'v':
141			vflag++;
142			break;
143		case 'q':
144			qflag++;
145			break;
146		default:
147			usage();
148		}
149	}
150	argc -= optind;
151	argv += optind;
152	if (!uflag && !gflag)
153		uflag++;
154	if (argc == 0) {
155		if (uflag)
156			showuid(getuid());
157		if (gflag) {
158			ngroups = getgroups(NGROUPS, gidset);
159			if (ngroups < 0) {
160				perror("quota: getgroups");
161				exit(1);
162			}
163			for (i = 1; i < ngroups; i++)
164				showgid(gidset[i]);
165		}
166		exit(0);
167	}
168	if (uflag && gflag)
169		usage();
170	if (uflag) {
171		for (; argc > 0; argc--, argv++) {
172			if (alldigits(*argv))
173				showuid(atoi(*argv));
174			else
175				showusrname(*argv);
176		}
177		exit(0);
178	}
179	if (gflag) {
180		for (; argc > 0; argc--, argv++) {
181			if (alldigits(*argv))
182				showgid(atoi(*argv));
183			else
184				showgrpname(*argv);
185		}
186		exit(0);
187	}
188	exit(0);
189}
190
191void
192usage()
193{
194
195	fprintf(stderr, "%s\n%s\n%s\n",
196		"Usage: quota [-guqv]",
197		"\tquota [-qv] -u username ...",
198		"\tquota [-qv] -g groupname ...");
199	exit(1);
200}
201
202/*
203 * Print out quotas for a specified user identifier.
204 */
205void
206showuid(uid)
207	u_long uid;
208{
209	struct passwd *pwd = getpwuid(uid);
210	u_long myuid;
211	char *name;
212
213	if (pwd == NULL)
214		name = "(no account)";
215	else
216		name = pwd->pw_name;
217	myuid = getuid();
218	if (uid != myuid && myuid != 0) {
219		printf("quota: %s (uid %ld): permission denied\n", name, uid);
220		return;
221	}
222	showquotas(USRQUOTA, uid, name);
223}
224
225/*
226 * Print out quotas for a specifed user name.
227 */
228void
229showusrname(name)
230	char *name;
231{
232	struct passwd *pwd = getpwnam(name);
233	u_long myuid;
234
235	if (pwd == NULL) {
236		fprintf(stderr, "quota: %s: unknown user\n", name);
237		return;
238	}
239	myuid = getuid();
240	if (pwd->pw_uid != myuid && myuid != 0) {
241		fprintf(stderr, "quota: %s (uid %d): permission denied\n",
242		    name, pwd->pw_uid);
243		return;
244	}
245	showquotas(USRQUOTA, pwd->pw_uid, name);
246}
247
248/*
249 * Print out quotas for a specified group identifier.
250 */
251void
252showgid(gid)
253	u_long gid;
254{
255	struct group *grp = getgrgid(gid);
256	int ngroups;
257	gid_t gidset[NGROUPS];
258	register int i;
259	char *name;
260
261	if (grp == NULL)
262		name = "(no entry)";
263	else
264		name = grp->gr_name;
265	ngroups = getgroups(NGROUPS, gidset);
266	if (ngroups < 0) {
267		perror("quota: getgroups");
268		return;
269	}
270	for (i = 1; i < ngroups; i++)
271		if (gid == gidset[i])
272			break;
273	if (i >= ngroups && getuid() != 0) {
274		fprintf(stderr, "quota: %s (gid %ld): permission denied\n",
275		    name, gid);
276		return;
277	}
278	showquotas(GRPQUOTA, gid, name);
279}
280
281/*
282 * Print out quotas for a specifed group name.
283 */
284void
285showgrpname(name)
286	char *name;
287{
288	struct group *grp = getgrnam(name);
289	int ngroups;
290	gid_t gidset[NGROUPS];
291	register int i;
292
293	if (grp == NULL) {
294		fprintf(stderr, "quota: %s: unknown group\n", name);
295		return;
296	}
297	ngroups = getgroups(NGROUPS, gidset);
298	if (ngroups < 0) {
299		perror("quota: getgroups");
300		return;
301	}
302	for (i = 1; i < ngroups; i++)
303		if (grp->gr_gid == gidset[i])
304			break;
305	if (i >= ngroups && getuid() != 0) {
306		fprintf(stderr, "quota: %s (gid %d): permission denied\n",
307		    name, grp->gr_gid);
308		return;
309	}
310	showquotas(GRPQUOTA, grp->gr_gid, name);
311}
312
313void
314showquotas(type, id, name)
315	int type;
316	u_long id;
317	char *name;
318{
319	register struct quotause *qup;
320	struct quotause *quplist, *getprivs();
321	char *msgi, *msgb, *timeprt();
322	int lines = 0;
323	static time_t now;
324
325	if (now == 0)
326		time(&now);
327	quplist = getprivs(id, type);
328	for (qup = quplist; qup; qup = qup->next) {
329		if (!vflag &&
330		    qup->dqblk.dqb_isoftlimit == 0 &&
331		    qup->dqblk.dqb_ihardlimit == 0 &&
332		    qup->dqblk.dqb_bsoftlimit == 0 &&
333		    qup->dqblk.dqb_bhardlimit == 0)
334			continue;
335		msgi = (char *)0;
336		if (qup->dqblk.dqb_ihardlimit &&
337		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) {
338			msgi = "File limit reached on";
339		} else if (qup->dqblk.dqb_isoftlimit &&
340		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) {
341			if (qup->dqblk.dqb_itime > now) {
342				msgi = "In file grace period on";
343			} else {
344				msgi = "Over file quota on";
345			}
346		}
347		msgb = (char *)0;
348#ifdef __APPLE__
349		if (qup->dqblk.dqb_bhardlimit &&
350		    qup->dqblk.dqb_curbytes >= qup->dqblk.dqb_bhardlimit) {
351			msgb = "Block limit reached on";
352		} else if (qup->dqblk.dqb_bsoftlimit &&
353		    qup->dqblk.dqb_curbytes >= qup->dqblk.dqb_bsoftlimit) {
354			if (qup->dqblk.dqb_btime > now) {
355				msgb = "In block grace period on";
356			} else {
357				msgb = "Over block quota on";
358			}
359		}
360#else
361		if (qup->dqblk.dqb_bhardlimit &&
362		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) {
363			msgb = "Block limit reached on";
364		} else if (qup->dqblk.dqb_bsoftlimit &&
365		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) {
366			if (qup->dqblk.dqb_btime > now) {
367				msgb = "In block grace period on";
368			} else {
369				msgb = "Over block quota on";
370			}
371		}
372#endif /* __APPLE__ */
373
374		if (qflag) {
375			if ((msgi != (char *)0 || msgb != (char *)0) &&
376			    lines++ == 0)
377				heading(type, id, name, "");
378			if (msgi != (char *)0)
379				printf("\t%s %s\n", msgi, qup->fsname);
380			if (msgb != (char *)0)
381				printf("\t%s %s\n", msgb, qup->fsname);
382			continue;
383		}
384#ifdef __APPLE__
385		if (vflag ||
386		    qup->dqblk.dqb_curbytes ||
387		    qup->dqblk.dqb_curinodes) {
388			if (lines++ == 0)
389				heading(type, id, name, "");
390
391			printf("%15s %12qd%c %12qd %12qd %8s"
392				, qup->fsname
393				, qup->dqblk.dqb_curbytes / 1024
394				, (msgb == (char *)0) ? ' ' : '*'
395				, qup->dqblk.dqb_bsoftlimit / 1024
396				, qup->dqblk.dqb_bhardlimit / 1024
397				, (msgb == (char *)0) ? ""
398				    : timeprt(qup->dqblk.dqb_btime));
399#else
400		if (vflag ||
401		    qup->dqblk.dqb_curblocks ||
402		    qup->dqblk.dqb_curinodes) {
403			if (lines++ == 0)
404				heading(type, id, name, "");
405
406			printf("%15s%8d%c%7d%8d%8s"
407				, qup->fsname
408				, dbtob(qup->dqblk.dqb_curblocks) / 1024
409				, (msgb == (char *)0) ? ' ' : '*'
410				, dbtob(qup->dqblk.dqb_bsoftlimit) / 1024
411				, dbtob(qup->dqblk.dqb_bhardlimit) / 1024
412				, (msgb == (char *)0) ? ""
413				    : timeprt(qup->dqblk.dqb_btime));
414#endif /* __APPLE__ */
415			printf("%8d%c%7d%8d%8s\n"
416				, qup->dqblk.dqb_curinodes
417				, (msgi == (char *)0) ? ' ' : '*'
418				, qup->dqblk.dqb_isoftlimit
419				, qup->dqblk.dqb_ihardlimit
420				, (msgi == (char *)0) ? ""
421				    : timeprt(qup->dqblk.dqb_itime)
422			);
423			continue;
424		}
425	}
426	if (!qflag && lines == 0)
427		heading(type, id, name, "none");
428}
429
430void
431heading(type, id, name, tag)
432	int type;
433	u_long id;
434	char *name, *tag;
435{
436
437	printf("Disk quotas for %s %s (%cid %ld): %s\n", qfextension[type],
438	    name, *qfextension[type], id, tag);
439	if (!qflag && tag[0] == '\0') {
440#ifdef __APPLE__
441		printf("%15s %12s  %12s %12s %8s%8s %7s%8s%8s\n"
442			, "Filesystem"
443			, "1K blocks"
444#else
445		printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
446			, "Filesystem"
447			, "blocks"
448#endif /* __APPLE __*/
449			, "quota"
450			, "limit"
451			, "grace"
452			, "files"
453			, "quota"
454			, "limit"
455			, "grace"
456		);
457	}
458}
459
460/*
461 * Calculate the grace period and return a printable string for it.
462 */
463char *
464timeprt(seconds)
465	time_t seconds;
466{
467	time_t hours, minutes;
468	static char buf[20];
469	static time_t now;
470
471	if (now == 0)
472		time(&now);
473	if (now > seconds)
474		return ("none");
475	seconds -= now;
476	minutes = (seconds + 30) / 60;
477	hours = (minutes + 30) / 60;
478	if (hours >= 36) {
479		snprintf(buf, sizeof(buf), "%ddays", (int)((hours + 12) / 24));
480		return (buf);
481	}
482	if (minutes >= 60) {
483		sprintf(buf, "%2d:%d", (int)(minutes / 60), (int)(minutes % 60));
484		return (buf);
485	}
486	sprintf(buf, "%2d", (int)minutes);
487	return (buf);
488}
489
490typedef struct {
491	int alloced;
492	int nels;
493	char **strings;
494} mount_list_t;
495
496static void *
497init_list(int max)
498{
499	mount_list_t *retval = NULL;
500	retval = malloc(sizeof(*retval));
501	if (retval) {
502		retval->strings = malloc(sizeof(char*) * max);
503		if (retval->strings) {
504			retval->alloced = max;
505		} else {
506			retval->alloced = 0;
507		}
508		retval->nels = 0;
509	}
510	return retval;
511}
512static void
513free_list(void *list)
514{
515	mount_list_t *tmp = list;
516	int i;
517
518	if (tmp == NULL)
519		return;
520	for (i = 0; i < tmp->nels; i++) {
521		free(tmp->strings[i]);
522	}
523	free(tmp);
524	return;
525}
526
527static int
528hasseen(void *tmp, const char *mp)
529{
530	int retval = 0;
531	mount_list_t *list = tmp;
532	int i;
533
534	if (tmp == NULL || mp == NULL)
535		goto done;
536
537	/*
538	 * This could also be a binary search, but then we'd have to sort
539	 * after each addition.  We may want to change the behaviour based
540	 * on the number of elements in the array.
541	 */
542
543	for (i = 0; i < list->nels; i++) {
544		if (strcmp(list->strings[i], mp) == 0) {
545			retval = 1;
546			goto done;
547		}
548	}
549	if (list->nels <= list->alloced) {
550		char **a = realloc(list->strings, (list->alloced + 10) * sizeof(char*));
551		if (a) {
552			list->alloced = list->alloced + 10;
553			list->strings = a;
554		} else {
555			goto done;
556		}
557	}
558	list->strings[list->nels++] = strdup(mp);
559
560done:
561	return retval;
562}
563
564/*
565 * Collect the requested quota information.
566 */
567#ifdef __APPLE__
568struct quotause *
569getprivs(id, quotatype)
570	register long id;
571	int quotatype;
572{
573        struct statfs *fst;
574	register struct quotause *qup, *quptail;
575	struct quotause *quphead;
576	struct dqblk dqb;
577	char *qfpathname;
578	int qcmd, fd;
579	int nfst, i;
580	int error;
581	void *cache;
582
583	quptail = quphead = (struct quotause *)0;
584	qcmd = QCMD(Q_GETQUOTA, quotatype);
585
586        nfst = getmntinfo(&fst, MNT_WAIT);
587        if (nfst==0) {
588          fprintf(stderr, "quota: no mounted filesystems\n");
589          exit(1);
590        }
591
592	cache = init_list(nfst);
593
594        for (i=0; i<nfst; i++) {
595		if (hasseen(cache, fst[i].f_mntonname))
596			continue;
597		error = quotactl(fst[i].f_mntonname, qcmd, id, (char *)&dqb);
598		if (error) {
599			if (strcmp(fst[i].f_fstypename, "hfs"))
600				continue;
601			if (!hasquota(&fst[i], quotatype, &qfpathname))
602				continue;
603		}
604		if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) {
605			fprintf(stderr, "quota: out of memory\n");
606			exit(2);
607		}
608		if (!error) {
609			bcopy(&dqb, &qup->dqblk, sizeof(dqb));
610		} else {
611			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
612				perror(qfpathname);
613				free(qup);
614				continue;
615			}
616			if ((error = qflookup(fd, id, quotatype, &qup->dqblk))) {
617				perror(qfpathname);
618				close(fd);
619				free(qup);
620				continue;
621			}
622			close(fd);
623		}
624		strlcpy(qup->fsname, fst[i].f_mntonname, sizeof(qup->fsname));
625		if (quphead == NULL)
626			quphead = qup;
627		else
628			quptail->next = qup;
629		quptail = qup;
630		qup->next = 0;
631	}
632	free_list(cache);
633
634	return (quphead);
635}
636#else
637struct quotause *
638getprivs(id, quotatype)
639	register long id;
640	int quotatype;
641{
642	register struct fstab *fs;
643	register struct quotause *qup, *quptail;
644	struct quotause *quphead;
645	char *qfpathname;
646	int qcmd, fd;
647
648	setfsent();
649	quphead = (struct quotause *)0;
650	qcmd = QCMD(Q_GETQUOTA, quotatype);
651	while (fs = getfsent()) {
652		if (strcmp(fs->fs_vfstype, "ufs"))
653			continue;
654		if (!hasquota(fs, quotatype, &qfpathname))
655			continue;
656		if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) {
657			fprintf(stderr, "quota: out of memory\n");
658			exit(2);
659		}
660		if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
661			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
662				perror(qfpathname);
663				free(qup);
664				continue;
665			}
666			lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET);
667			switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
668			case 0:			/* EOF */
669				/*
670				 * Convert implicit 0 quota (EOF)
671				 * into an explicit one (zero'ed dqblk)
672				 */
673				bzero((caddr_t)&qup->dqblk,
674				    sizeof(struct dqblk));
675				break;
676
677			case sizeof(struct dqblk):	/* OK */
678				break;
679
680			default:		/* ERROR */
681				fprintf(stderr, "quota: read error");
682				perror(qfpathname);
683				close(fd);
684				free(qup);
685				continue;
686			}
687			close(fd);
688		}
689		strlcpy(qup->fsname, fs->fs_file, sizeof(qup->fsname));
690		if (quphead == NULL)
691			quphead = qup;
692		else
693			quptail->next = qup;
694		quptail = qup;
695		qup->next = 0;
696	}
697	endfsent();
698	return (quphead);
699}
700#endif /* __APPLE__ */
701
702
703#ifdef __APPLE__
704/*
705 * Lookup an entry in the quota file.
706 */
707int
708qflookup(fd, id, type, dqbp)
709	int fd;
710	u_long id;
711	int type;
712	struct dqblk *dqbp;
713{
714	struct dqfilehdr dqhdr;
715	int i, skip, last, m;
716	u_long mask;
717
718	bzero(dqbp, sizeof(struct dqblk));
719
720	if (id == 0)
721		return (0);
722
723	lseek(fd, 0, L_SET);
724	if (read(fd, &dqhdr, sizeof(struct dqfilehdr)) != sizeof(struct dqfilehdr)) {
725		fprintf(stderr, "quota: read error\n");
726		return (errno);
727	}
728
729	/* Sanity check the quota file header. */
730	if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) ||
731	    (OSSwapBigToHostInt32(dqhdr.dqh_version) > QF_VERSION) ||
732	    (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) {
733		fprintf(stderr, "quota: invalid quota file header\n");
734		return (EINVAL);
735	}
736
737	m = OSSwapBigToHostInt32(dqhdr.dqh_maxentries);
738	mask = m - 1;
739	i = dqhash1(id, dqhashshift(m), mask);
740	skip = dqhash2(id, mask);
741
742	for (last = (i + (m-1) * skip) & mask;
743	     i != last;
744	     i = (i + skip) & mask) {
745		lseek(fd, dqoffset(i), L_SET);
746		if (read(fd, dqbp, sizeof(struct dqblk)) < sizeof(struct dqblk)) {
747			fprintf(stderr, "quota: read error at index %d\n", i);
748			return (errno);
749		}
750		/*
751		 * Stop when an empty entry is found
752		 * or we encounter a matching id.
753		 */
754		if (dqbp->dqb_id == 0 || OSSwapBigToHostInt32(dqbp->dqb_id) == id)
755			break;
756	}
757	/* Put data in host native byte order. */
758	dqbp->dqb_bhardlimit = OSSwapBigToHostInt64(dqbp->dqb_bhardlimit);
759	dqbp->dqb_bsoftlimit = OSSwapBigToHostInt64(dqbp->dqb_bsoftlimit);
760	dqbp->dqb_curbytes   = OSSwapBigToHostInt64(dqbp->dqb_curbytes);
761	dqbp->dqb_ihardlimit = OSSwapBigToHostInt32(dqbp->dqb_ihardlimit);
762	dqbp->dqb_isoftlimit = OSSwapBigToHostInt32(dqbp->dqb_isoftlimit);
763	dqbp->dqb_curinodes  = OSSwapBigToHostInt32(dqbp->dqb_curinodes);
764	dqbp->dqb_btime      = OSSwapBigToHostInt32(dqbp->dqb_btime);
765	dqbp->dqb_itime      = OSSwapBigToHostInt32(dqbp->dqb_itime);
766	dqbp->dqb_id         = OSSwapBigToHostInt32(dqbp->dqb_id);
767
768	return (0);
769}
770#endif /* __APPLE__ */
771
772
773/*
774 * Check to see if a particular quota is to be enabled.
775 */
776#ifdef __APPLE__
777int
778hasquota(fst, type, qfnamep)
779	register struct statfs *fst;
780	int type;
781	char **qfnamep;
782{
783        struct stat sb;
784	static char initname, usrname[100], grpname[100];
785	static char buf[BUFSIZ];
786
787	if (!initname) {
788		snprintf(usrname, sizeof(usrname), "%s%s", qfextension[USRQUOTA], qfname);
789		snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname);
790		initname = 1;
791	}
792        /*
793	  We only support the default path to the
794	  on disk quota files.
795	*/
796
797        (void)snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname,
798		      QUOTAOPSNAME, qfextension[type] );
799        if (stat(buf, &sb) != 0) {
800          /* There appears to be no mount option file */
801          return(0);
802        }
803
804	(void) snprintf(buf, sizeof(buf),  "%s/%s.%s", fst->f_mntonname, qfname, qfextension[type]);
805	*qfnamep = buf;
806	return (1);
807}
808#else
809hasquota(fs, type, qfnamep)
810	register struct fstab *fs;
811	int type;
812	char **qfnamep;
813{
814	register char *opt;
815	char *cp, *index(), *strtok();
816	static char initname, usrname[100], grpname[100];
817	static char buf[BUFSIZ];
818
819	if (!initname) {
820		snprintf(usrname, sizeof(usrname), "%s%s", qfextension[USRQUOTA], qfname);
821		snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname);
822		initname = 1;
823	}
824	strlcpy(buf, fs->fs_mntops, sizeof(buf));
825	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
826		if (cp = index(opt, '='))
827			*cp++ = '\0';
828		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
829			break;
830		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
831			break;
832	}
833	if (!opt)
834		return (0);
835	if (cp) {
836		*qfnamep = cp;
837		return (1);
838	}
839	(void) snprintf(buf, sizeof(buf), "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
840	*qfnamep = buf;
841	return (1);
842}
843#endif /* __APPLE__ */
844
845int
846alldigits(s)
847	register char *s;
848{
849	register int c;
850
851	c = *s++;
852	do {
853		if (!isdigit(c))
854			return (0);
855	} while ((c = *s++));
856	return (1);
857}
858