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*24782Sserge static char sccsid[] = "@(#)quotacheck.c	5.5 (Berkeley) 09/16/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)
138*24782Sserge 			fprintf(stderr, "%s not found in %s\n",
139*24782Sserge 				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;
21012703Smckusick 	FILE *qf;
21112703Smckusick 	u_short uid;
21212703Smckusick 	int cg, i;
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 	}
22712703Smckusick 	qf = fopen(qffile, "r+");
22812703Smckusick 	if (qf == NULL) {
22912703Smckusick 		perror(qffile);
23024661Sserge 		close(fi);
23112703Smckusick 		return (1);
23212660Smckusick 	}
23312703Smckusick 	if (fstat(fileno(qf), &statb) < 0) {
23412703Smckusick 		perror(qffile);
23524661Sserge 		fclose(qf);
23624661Sserge 		close(fi);
23712703Smckusick 		return (1);
23812703Smckusick 	}
23912703Smckusick 	quotadev = statb.st_dev;
24012703Smckusick 	if (stat(fsdev, &statb) < 0) {
24112703Smckusick 		perror(fsdev);
24224661Sserge 		fclose(qf);
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);
24924661Sserge 		fclose(qf);
25024661Sserge 		close(fi);
25112703Smckusick 		return (1);
25212703Smckusick 	}
25324758Sserge 	if (quota(Q_SYNC, 0, quotadev, (caddr_t)0) < 0 &&
25421085Smckusick 	    errno == EINVAL && !warned && vflag) {
25521085Smckusick 		warned++;
25621085Smckusick 		fprintf(stdout,
25721085Smckusick 		    "*** Warning: Quotas are not compiled into this kernel\n");
25821085Smckusick 	}
25912660Smckusick 	sync();
26012660Smckusick 	bread(SBLOCK, (char *)&sblock, SBSIZE);
26112660Smckusick 	ino = 0;
26212660Smckusick 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
26312660Smckusick 		dp = NULL;
26412660Smckusick 		for (i = 0; i < sblock.fs_ipg; i++)
26512660Smckusick 			acct(ginode());
26612660Smckusick 	}
26712703Smckusick 	for (uid = 0; uid <= highuid; uid++) {
26824661Sserge 		fseek(qf, (long)uid * sizeof(struct dqblk), 0);
26912703Smckusick 		i = fread(&dqbuf, sizeof(struct dqblk), 1, qf);
27012703Smckusick 		if (i == 0)
27112802Smckusick 			dqbuf = zerodqbuf;
27218415Smckusick 		fup = lookup(uid);
27324758Sserge 		if (fup == 0)
27424758Sserge 			fup = &zerofileusage;
27512703Smckusick 		if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
27612802Smckusick 		    dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
27712802Smckusick 			fup->fu_usage.du_curinodes = 0;
27812802Smckusick 			fup->fu_usage.du_curblocks = 0;
27912703Smckusick 			continue;
28012802Smckusick 		}
28112703Smckusick 		if (vflag) {
28224758Sserge 			if (pflag)
28324758Sserge 				printf("%s: ", rawdisk);
28412802Smckusick 			if (fup->fu_name[0] != '\0')
28524758Sserge 				printf("%-8s fixed:", fup->fu_name);
28612802Smckusick 			else
28724758Sserge 				printf("#%-7d fixed:", uid);
28824758Sserge 			if (dqbuf.dqb_curinodes != fup->fu_usage.du_curinodes)
28924758Sserge 				fprintf(stdout, "  inodes %d -> %d",
29024758Sserge 					dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
29124758Sserge 			if (dqbuf.dqb_curblocks != fup->fu_usage.du_curblocks)
29224758Sserge 				fprintf(stdout, "  blocks %d -> %d",
29324758Sserge 					dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
29424758Sserge 			fprintf(stdout, "\n");
29512660Smckusick 		}
29612703Smckusick 		dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
29712703Smckusick 		dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
29824661Sserge 		fseek(qf, (long)uid * sizeof(struct dqblk), 0);
29912703Smckusick 		fwrite(&dqbuf, sizeof(struct dqblk), 1, qf);
30012703Smckusick 		quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
30113255Smckusick 		fup->fu_usage.du_curinodes = 0;
30213255Smckusick 		fup->fu_usage.du_curblocks = 0;
30312660Smckusick 	}
30424661Sserge 	fflush(qf);
30518415Smckusick 	ftruncate(fileno(qf), (highuid + 1) * sizeof(struct dqblk));
30624661Sserge 	fclose(qf);
30724661Sserge 	close(fi);
30812703Smckusick 	return (0);
30912660Smckusick }
31012660Smckusick 
31112660Smckusick acct(ip)
31212660Smckusick 	register struct dinode *ip;
31312660Smckusick {
31412703Smckusick 	register struct fileusage *fup;
31512660Smckusick 
31612660Smckusick 	if (ip == NULL)
31712660Smckusick 		return;
31812660Smckusick 	if (ip->di_mode == 0)
31912660Smckusick 		return;
32024758Sserge 	fup = adduid(ip->di_uid);
32112703Smckusick 	fup->fu_usage.du_curinodes++;
32212660Smckusick 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
32312660Smckusick 		return;
32412703Smckusick 	fup->fu_usage.du_curblocks += ip->di_blocks;
32512660Smckusick }
32612660Smckusick 
32712703Smckusick oneof(target, list, n)
32812703Smckusick 	char *target, *list[];
32912703Smckusick 	register int n;
33012660Smckusick {
33112703Smckusick 	register int i;
33212660Smckusick 
33312703Smckusick 	for (i = 0; i < n; i++)
33412703Smckusick 		if (strcmp(target, list[i]) == 0) {
33512703Smckusick 			done |= 1 << i;
33612703Smckusick 			return (1);
33712703Smckusick 		}
33812703Smckusick 	return (0);
33912660Smckusick }
34012660Smckusick 
34112660Smckusick struct dinode *
34212660Smckusick ginode()
34312660Smckusick {
34412660Smckusick 	register unsigned long iblk;
34512660Smckusick 
34612660Smckusick 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
34712660Smckusick 		iblk = itod(&sblock, ino);
34812660Smckusick 		bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
34912660Smckusick 		dp = &itab[ino % INOPB(&sblock)];
35012660Smckusick 	}
35112660Smckusick 	if (ino++ < ROOTINO)
35212660Smckusick 		return(NULL);
35312660Smckusick 	return(dp);
35412660Smckusick }
35512660Smckusick 
35612660Smckusick bread(bno, buf, cnt)
35712660Smckusick 	long unsigned bno;
35812660Smckusick 	char *buf;
35912660Smckusick {
36012660Smckusick 
36112703Smckusick 	lseek(fi, (long)dbtob(bno), 0);
36212660Smckusick 	if (read(fi, buf, cnt) != cnt) {
36324758Sserge 		perror("read");
36412660Smckusick 		exit(1);
36512660Smckusick 	}
36612660Smckusick }
36712660Smckusick 
36812703Smckusick struct fileusage *
36912703Smckusick lookup(uid)
37012703Smckusick 	u_short uid;
37112660Smckusick {
37212703Smckusick 	register struct fileusage *fup;
37312660Smckusick 
37412703Smckusick 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
37512703Smckusick 		if (fup->fu_uid == uid)
37612703Smckusick 			return (fup);
37712703Smckusick 	return ((struct fileusage *)0);
37812660Smckusick }
37912660Smckusick 
38012703Smckusick struct fileusage *
38112703Smckusick adduid(uid)
38212703Smckusick 	u_short uid;
38312660Smckusick {
38412703Smckusick 	struct fileusage *fup, **fhp;
38524661Sserge 	extern char *calloc();
38612660Smckusick 
38712703Smckusick 	fup = lookup(uid);
38812703Smckusick 	if (fup != 0)
38912703Smckusick 		return (fup);
39012703Smckusick 	fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
39112703Smckusick 	if (fup == 0) {
39212703Smckusick 		fprintf(stderr, "out of memory for fileusage structures\n");
39312703Smckusick 		exit(1);
39412703Smckusick 	}
39512703Smckusick 	fhp = &fuhead[uid % FUHASH];
39612703Smckusick 	fup->fu_next = *fhp;
39712703Smckusick 	*fhp = fup;
39812703Smckusick 	fup->fu_uid = uid;
39912703Smckusick 	if (uid > highuid)
40012703Smckusick 		highuid = uid;
40112703Smckusick 	return (fup);
40212660Smckusick }
40312660Smckusick 
40412660Smckusick char *
40512703Smckusick makerawname(name)
40612703Smckusick 	char *name;
40712660Smckusick {
40812703Smckusick 	register char *cp;
40912703Smckusick 	char tmp, ch, *rindex();
41012703Smckusick 	static char rawname[MAXPATHLEN];
41112660Smckusick 
41212703Smckusick 	strcpy(rawname, name);
41324661Sserge 	cp = rindex(rawname, '/');
414*24782Sserge 	if (cp == NULL)
41512703Smckusick 		return (name);
41624661Sserge 	else
41724661Sserge 		cp++;
41812703Smckusick 	for (ch = 'r'; *cp != '\0'; ) {
41912703Smckusick 		tmp = *cp;
42012703Smckusick 		*cp++ = ch;
42112703Smckusick 		ch = tmp;
42212703Smckusick 	}
42312703Smckusick 	*cp++ = ch;
42412703Smckusick 	*cp = '\0';
42512703Smckusick 	return (rawname);
42612660Smckusick }
427