121517Smckusick /*
221517Smckusick  * Copyright (c) 1980 Regents of the University of California.
321517Smckusick  * All rights reserved.  The Berkeley software License Agreement
421517Smckusick  * specifies the terms and conditions for redistribution.
521517Smckusick  */
621517Smckusick 
712703Smckusick #ifndef lint
821517Smckusick char copyright[] =
921517Smckusick "@(#) Copyright (c) 1980 Regents of the University of California.\n\
1021517Smckusick  All rights reserved.\n";
1121517Smckusick #endif not lint
1212660Smckusick 
1321517Smckusick #ifndef lint
14*25377Sserge static char sccsid[] = "@(#)quotacheck.c	5.6 (Berkeley) 11/03/85";
1521517Smckusick #endif not lint
1621517Smckusick 
1712660Smckusick /*
1812660Smckusick  * Fix up / report on disc quotas & usage
1912660Smckusick  */
2012660Smckusick #include <stdio.h>
2112660Smckusick #include <ctype.h>
2212660Smckusick #include <signal.h>
2321085Smckusick #include <errno.h>
2412660Smckusick #include <sys/param.h>
2512660Smckusick #include <sys/inode.h>
2612660Smckusick #include <sys/fs.h>
2712660Smckusick #include <sys/quota.h>
2812660Smckusick #include <sys/stat.h>
2924758Sserge #include <sys/wait.h>
3012703Smckusick #include <fstab.h>
3112802Smckusick #include <pwd.h>
3212660Smckusick 
3312660Smckusick union {
3412660Smckusick 	struct	fs	sblk;
3512703Smckusick 	char	dummy[MAXBSIZE];
3612660Smckusick } un;
3712660Smckusick #define	sblock	un.sblk
3812703Smckusick 
3912703Smckusick #define	ITABSZ	256
4012660Smckusick struct	dinode	itab[ITABSZ];
4112660Smckusick struct	dinode	*dp;
4212660Smckusick 
4312802Smckusick #define LOGINNAMESIZE 8
4412703Smckusick struct fileusage {
4512802Smckusick 	struct fileusage *fu_next;
4612703Smckusick 	struct dqusage fu_usage;
4712703Smckusick 	u_short	fu_uid;
4812802Smckusick 	char fu_name[LOGINNAMESIZE + 1];
4912703Smckusick };
5012703Smckusick #define FUHASH 997
5112703Smckusick struct fileusage *fuhead[FUHASH];
5212703Smckusick struct fileusage *lookup();
5312703Smckusick struct fileusage *adduid();
5412703Smckusick int highuid;
5512660Smckusick 
5612703Smckusick int fi;
5712703Smckusick ino_t ino;
5812703Smckusick long done;
5912660Smckusick struct	passwd	*getpwent();
6012660Smckusick struct	dinode	*ginode();
6112703Smckusick char *malloc(), *makerawname();
6212660Smckusick 
6312703Smckusick int	vflag;		/* verbose */
6412703Smckusick int	aflag;		/* all file systems */
6524758Sserge int	pflag;		/* fsck like parallel check */
6612703Smckusick 
6712703Smckusick char *qfname = "quotas";
6812703Smckusick char quotafile[MAXPATHLEN + 1];
6912802Smckusick struct dqblk zerodqbuf;
7024758Sserge struct fileusage zerofileusage;
7112703Smckusick 
7212660Smckusick main(argc, argv)
7312703Smckusick 	int argc;
7412660Smckusick 	char **argv;
7512660Smckusick {
7612703Smckusick 	register struct fstab *fs;
7712802Smckusick 	register struct fileusage *fup;
7812802Smckusick 	register struct passwd *pw;
7912703Smckusick 	int i, errs = 0;
8012660Smckusick 
8112703Smckusick again:
8212703Smckusick 	argc--, argv++;
8312703Smckusick 	if (argc > 0 && strcmp(*argv, "-v") == 0) {
8412703Smckusick 		vflag++;
8512703Smckusick 		goto again;
8612660Smckusick 	}
8712703Smckusick 	if (argc > 0 && strcmp(*argv, "-a") == 0) {
8812703Smckusick 		aflag++;
8912703Smckusick 		goto again;
9012703Smckusick 	}
9124758Sserge 	if (argc > 0 && strcmp(*argv, "-p") == 0) {
9224758Sserge 		pflag++;
9324758Sserge 		goto again;
9424758Sserge 	}
9512703Smckusick 	if (argc <= 0 && !aflag) {
9612703Smckusick 		fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
9724758Sserge 			"quotacheck [-v] [-p] -a",
9824758Sserge 			"quotacheck [-v] [-p] filesys ...");
9912660Smckusick 		exit(1);
10012660Smckusick 	}
10124661Sserge 
10224661Sserge 	setpwent();
10324661Sserge 	while ((pw = getpwent()) != 0) {
10424661Sserge 		fup = lookup(pw->pw_uid);
10524758Sserge 		if (fup == 0) {
10624661Sserge 			fup = adduid(pw->pw_uid);
10724758Sserge 			strncpy(fup->fu_name, pw->pw_name,
10824758Sserge 				sizeof(fup->fu_name));
10924758Sserge 		}
11012802Smckusick 	}
11124661Sserge 	endpwent();
11224661Sserge 
11324758Sserge 	if (pflag)
11424758Sserge 		errs = preen(argc, argv);
11524758Sserge 	else {
11624758Sserge 		if (setfsent() == 0) {
11724758Sserge 			fprintf(stderr, "Can't open ");
11824758Sserge 			perror(FSTAB);
11924758Sserge 			exit(8);
12024758Sserge 		}
12124758Sserge 		while ((fs = getfsent()) != NULL) {
12224758Sserge 			if (aflag &&
12324758Sserge 			    (fs->fs_type == 0 ||
12424758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
12524758Sserge 				continue;
12624758Sserge 			if (!aflag &&
12724758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
12824758Sserge 			      oneof(fs->fs_spec, argv, argc)))
12924758Sserge 				continue;
13024758Sserge 			(void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname);
13124758Sserge 			errs += chkquota(fs->fs_spec, fs->fs_file, quotafile);
13224758Sserge 		}
13324758Sserge 		endfsent();
13412660Smckusick 	}
13524758Sserge 
13612703Smckusick 	for (i = 0; i < argc; i++)
13712703Smckusick 		if ((done & (1 << i)) == 0)
13824782Sserge 			fprintf(stderr, "%s not found in %s\n",
13924782Sserge 				argv[i], FSTAB);
14012703Smckusick 	exit(errs);
14112703Smckusick }
14212660Smckusick 
14324758Sserge preen(argc, argv)
14424758Sserge 	int argc;
14524758Sserge 	char **argv;
14624758Sserge {
14724758Sserge 	register struct fstab *fs;
14824758Sserge 	register int passno, anygtr;
14924758Sserge 	register int errs;
15024758Sserge 	union wait status;
15124758Sserge 
15224758Sserge 	passno = 1;
15324758Sserge 	errs = 0;
15424758Sserge 	do {
15524758Sserge 		anygtr = 0;
15624758Sserge 
15724758Sserge 		if (setfsent() == 0) {
15824758Sserge 			fprintf(stderr, "Can't open ");
15924758Sserge 			perror(FSTAB);
16024758Sserge 			exit(8);
16124758Sserge 		}
16224758Sserge 
16324758Sserge 		while ((fs = getfsent()) != NULL) {
16424758Sserge 			if (fs->fs_passno > passno)
16524758Sserge 				anygtr = 1;
16624758Sserge 
16724758Sserge 			if (aflag &&
16824758Sserge 			    (fs->fs_type == 0 ||
16924758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
17024758Sserge 				continue;
17124758Sserge 
17224758Sserge 			if (!aflag &&
17324758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
17424758Sserge 			      oneof(fs->fs_spec, argv, argc)))
17524758Sserge 				continue;
17624758Sserge 
17724758Sserge 			if (fs->fs_passno != passno)
17824758Sserge 				continue;
17924758Sserge 
18024758Sserge 			switch (fork()) {
18124758Sserge 			case -1:
18224758Sserge 				perror("fork");
18324758Sserge 				exit(8);
18424758Sserge 				break;
18524758Sserge 
18624758Sserge 			case 0:
18724758Sserge 				sprintf(quotafile, "%s/%s",
18824758Sserge 					fs->fs_file, qfname);
18924758Sserge 				exit(chkquota(fs->fs_spec,
19024758Sserge 					fs->fs_file, quotafile));
19124758Sserge 			}
19224758Sserge 		}
19324758Sserge 
19424758Sserge 		while (wait(&status) != -1)
19524758Sserge 			errs += status.w_retcode;
19624758Sserge 
19724758Sserge 		passno++;
19824758Sserge 	} while (anygtr);
19924758Sserge 
20024758Sserge 	return (errs);
20124758Sserge }
20224758Sserge 
20324661Sserge chkquota(fsdev, fsfile, qffile)
20412703Smckusick 	char *fsdev;
20524661Sserge 	char *fsfile;
20612703Smckusick 	char *qffile;
20712703Smckusick {
20812703Smckusick 	register struct fileusage *fup;
20912703Smckusick 	dev_t quotadev;
210*25377Sserge 	register FILE *qfi, *qfo;
21112703Smckusick 	u_short uid;
212*25377Sserge 	int cg, i, fdo;
21312703Smckusick 	char *rawdisk;
21412703Smckusick 	struct stat statb;
21512703Smckusick 	struct dqblk dqbuf;
21621085Smckusick 	static int warned = 0;
21721085Smckusick 	extern int errno;
21812660Smckusick 
21912703Smckusick 	rawdisk = makerawname(fsdev);
22012703Smckusick 	if (vflag)
22124758Sserge 		fprintf(stdout, "*** Checking quotas for %s (%s)\n", rawdisk, fsfile);
22212703Smckusick 	fi = open(rawdisk, 0);
22312703Smckusick 	if (fi < 0) {
22412703Smckusick 		perror(rawdisk);
22512703Smckusick 		return (1);
22612660Smckusick 	}
227*25377Sserge 	qfi = fopen(qffile, "r");
228*25377Sserge 	if (qfi == NULL) {
22912703Smckusick 		perror(qffile);
23024661Sserge 		close(fi);
23112703Smckusick 		return (1);
23212660Smckusick 	}
233*25377Sserge 	if (fstat(fileno(qfi), &statb) < 0) {
23412703Smckusick 		perror(qffile);
235*25377Sserge 		fclose(qfi);
23624661Sserge 		close(fi);
23712703Smckusick 		return (1);
23812703Smckusick 	}
23912703Smckusick 	quotadev = statb.st_dev;
24012703Smckusick 	if (stat(fsdev, &statb) < 0) {
24112703Smckusick 		perror(fsdev);
242*25377Sserge 		fclose(qfi);
24324661Sserge 		close(fi);
24412703Smckusick 		return (1);
24512703Smckusick 	}
24612703Smckusick 	if (quotadev != statb.st_rdev) {
24712703Smckusick 		fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
24812703Smckusick 			qffile, quotadev, fsdev, statb.st_rdev);
249*25377Sserge 		fclose(qfi);
25024661Sserge 		close(fi);
25112703Smckusick 		return (1);
25212703Smckusick 	}
253*25377Sserge 	/*
254*25377Sserge 	 * Must do fdopen(open(qffile, 1), "w") instead of fopen(qffile, "w")
255*25377Sserge 	 * because fopen(qffile, "w") would truncate the quota file.
256*25377Sserge 	 */
257*25377Sserge 	fdo = open(qffile, 1);
258*25377Sserge 	if (fdo < 0 || (qfo = fdopen(fdo, "w")) == NULL) {
259*25377Sserge 		perror(qffile);
260*25377Sserge 		if (fdo >= 0)
261*25377Sserge 			close(fdo);
262*25377Sserge 		fclose(qfi);
263*25377Sserge 		close(fi);
264*25377Sserge 		return (1);
265*25377Sserge 	}
26624758Sserge 	if (quota(Q_SYNC, 0, quotadev, (caddr_t)0) < 0 &&
26721085Smckusick 	    errno == EINVAL && !warned && vflag) {
26821085Smckusick 		warned++;
26921085Smckusick 		fprintf(stdout,
27021085Smckusick 		    "*** Warning: Quotas are not compiled into this kernel\n");
27121085Smckusick 	}
27212660Smckusick 	sync();
27312660Smckusick 	bread(SBLOCK, (char *)&sblock, SBSIZE);
27412660Smckusick 	ino = 0;
27512660Smckusick 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
27612660Smckusick 		dp = NULL;
27712660Smckusick 		for (i = 0; i < sblock.fs_ipg; i++)
27812660Smckusick 			acct(ginode());
27912660Smckusick 	}
28012703Smckusick 	for (uid = 0; uid <= highuid; uid++) {
281*25377Sserge 		i = fread(&dqbuf, sizeof(struct dqblk), 1, qfi);
28212703Smckusick 		if (i == 0)
28312802Smckusick 			dqbuf = zerodqbuf;
28418415Smckusick 		fup = lookup(uid);
28524758Sserge 		if (fup == 0)
28624758Sserge 			fup = &zerofileusage;
28712703Smckusick 		if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
28812802Smckusick 		    dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
28912802Smckusick 			fup->fu_usage.du_curinodes = 0;
29012802Smckusick 			fup->fu_usage.du_curblocks = 0;
291*25377Sserge 			fseek(qfo, (long)sizeof(struct dqblk), 1);
29212703Smckusick 			continue;
29312802Smckusick 		}
29412703Smckusick 		if (vflag) {
29524758Sserge 			if (pflag)
29624758Sserge 				printf("%s: ", rawdisk);
29712802Smckusick 			if (fup->fu_name[0] != '\0')
29824758Sserge 				printf("%-8s fixed:", fup->fu_name);
29912802Smckusick 			else
30024758Sserge 				printf("#%-7d fixed:", uid);
30124758Sserge 			if (dqbuf.dqb_curinodes != fup->fu_usage.du_curinodes)
302*25377Sserge 				fprintf(stdout, "\tinodes %d -> %d",
30324758Sserge 					dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
30424758Sserge 			if (dqbuf.dqb_curblocks != fup->fu_usage.du_curblocks)
305*25377Sserge 				fprintf(stdout, "\tblocks %d -> %d",
30624758Sserge 					dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
30724758Sserge 			fprintf(stdout, "\n");
30812660Smckusick 		}
30912703Smckusick 		dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
31012703Smckusick 		dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
311*25377Sserge 		fwrite(&dqbuf, sizeof(struct dqblk), 1, qfo);
31212703Smckusick 		quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
31313255Smckusick 		fup->fu_usage.du_curinodes = 0;
31413255Smckusick 		fup->fu_usage.du_curblocks = 0;
31512660Smckusick 	}
316*25377Sserge 	fflush(qfo);
317*25377Sserge 	ftruncate(fileno(qfo), (off_t)((highuid + 1) * sizeof(struct dqblk)));
318*25377Sserge 	fclose(qfi);
319*25377Sserge 	fclose(qfo);
32024661Sserge 	close(fi);
32112703Smckusick 	return (0);
32212660Smckusick }
32312660Smckusick 
32412660Smckusick acct(ip)
32512660Smckusick 	register struct dinode *ip;
32612660Smckusick {
32712703Smckusick 	register struct fileusage *fup;
32812660Smckusick 
32912660Smckusick 	if (ip == NULL)
33012660Smckusick 		return;
33112660Smckusick 	if (ip->di_mode == 0)
33212660Smckusick 		return;
33324758Sserge 	fup = adduid(ip->di_uid);
33412703Smckusick 	fup->fu_usage.du_curinodes++;
33512660Smckusick 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
33612660Smckusick 		return;
33712703Smckusick 	fup->fu_usage.du_curblocks += ip->di_blocks;
33812660Smckusick }
33912660Smckusick 
34012703Smckusick oneof(target, list, n)
34112703Smckusick 	char *target, *list[];
34212703Smckusick 	register int n;
34312660Smckusick {
34412703Smckusick 	register int i;
34512660Smckusick 
34612703Smckusick 	for (i = 0; i < n; i++)
34712703Smckusick 		if (strcmp(target, list[i]) == 0) {
34812703Smckusick 			done |= 1 << i;
34912703Smckusick 			return (1);
35012703Smckusick 		}
35112703Smckusick 	return (0);
35212660Smckusick }
35312660Smckusick 
35412660Smckusick struct dinode *
35512660Smckusick ginode()
35612660Smckusick {
35712660Smckusick 	register unsigned long iblk;
35812660Smckusick 
35912660Smckusick 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
36012660Smckusick 		iblk = itod(&sblock, ino);
36112660Smckusick 		bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
36212660Smckusick 		dp = &itab[ino % INOPB(&sblock)];
36312660Smckusick 	}
36412660Smckusick 	if (ino++ < ROOTINO)
36512660Smckusick 		return(NULL);
36612660Smckusick 	return(dp);
36712660Smckusick }
36812660Smckusick 
36912660Smckusick bread(bno, buf, cnt)
37012660Smckusick 	long unsigned bno;
37112660Smckusick 	char *buf;
37212660Smckusick {
373*25377Sserge 	extern	off_t lseek();
374*25377Sserge 	register off_t pos;
37512660Smckusick 
376*25377Sserge 	pos = (off_t)dbtob(bno);
377*25377Sserge 	if (lseek(fi, pos, 0) != pos) {
378*25377Sserge 		perror("lseek");
379*25377Sserge 		exit(1);
380*25377Sserge 	}
381*25377Sserge 
38212660Smckusick 	if (read(fi, buf, cnt) != cnt) {
38324758Sserge 		perror("read");
38412660Smckusick 		exit(1);
38512660Smckusick 	}
38612660Smckusick }
38712660Smckusick 
38812703Smckusick struct fileusage *
38912703Smckusick lookup(uid)
39012703Smckusick 	u_short uid;
39112660Smckusick {
39212703Smckusick 	register struct fileusage *fup;
39312660Smckusick 
39412703Smckusick 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
39512703Smckusick 		if (fup->fu_uid == uid)
39612703Smckusick 			return (fup);
39712703Smckusick 	return ((struct fileusage *)0);
39812660Smckusick }
39912660Smckusick 
40012703Smckusick struct fileusage *
40112703Smckusick adduid(uid)
40212703Smckusick 	u_short uid;
40312660Smckusick {
40412703Smckusick 	struct fileusage *fup, **fhp;
40524661Sserge 	extern char *calloc();
40612660Smckusick 
40712703Smckusick 	fup = lookup(uid);
40812703Smckusick 	if (fup != 0)
40912703Smckusick 		return (fup);
41012703Smckusick 	fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
41112703Smckusick 	if (fup == 0) {
41212703Smckusick 		fprintf(stderr, "out of memory for fileusage structures\n");
41312703Smckusick 		exit(1);
41412703Smckusick 	}
41512703Smckusick 	fhp = &fuhead[uid % FUHASH];
41612703Smckusick 	fup->fu_next = *fhp;
41712703Smckusick 	*fhp = fup;
41812703Smckusick 	fup->fu_uid = uid;
41912703Smckusick 	if (uid > highuid)
42012703Smckusick 		highuid = uid;
42112703Smckusick 	return (fup);
42212660Smckusick }
42312660Smckusick 
42412660Smckusick char *
42512703Smckusick makerawname(name)
42612703Smckusick 	char *name;
42712660Smckusick {
42812703Smckusick 	register char *cp;
42912703Smckusick 	char tmp, ch, *rindex();
43012703Smckusick 	static char rawname[MAXPATHLEN];
43112660Smckusick 
43212703Smckusick 	strcpy(rawname, name);
43324661Sserge 	cp = rindex(rawname, '/');
43424782Sserge 	if (cp == NULL)
43512703Smckusick 		return (name);
43624661Sserge 	else
43724661Sserge 		cp++;
43812703Smckusick 	for (ch = 'r'; *cp != '\0'; ) {
43912703Smckusick 		tmp = *cp;
44012703Smckusick 		*cp++ = ch;
44112703Smckusick 		ch = tmp;
44212703Smckusick 	}
44312703Smckusick 	*cp++ = ch;
44412703Smckusick 	*cp = '\0';
44512703Smckusick 	return (rawname);
44612660Smckusick }
447