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*30558Smckusick static char sccsid[] = "@(#)quotacheck.c	5.7 (Berkeley) 02/23/87";
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;
71*30558Smckusick long dev_bsize = 1;
7212703Smckusick 
7312660Smckusick main(argc, argv)
7412703Smckusick 	int argc;
7512660Smckusick 	char **argv;
7612660Smckusick {
7712703Smckusick 	register struct fstab *fs;
7812802Smckusick 	register struct fileusage *fup;
7912802Smckusick 	register struct passwd *pw;
8012703Smckusick 	int i, errs = 0;
8112660Smckusick 
8212703Smckusick again:
8312703Smckusick 	argc--, argv++;
8412703Smckusick 	if (argc > 0 && strcmp(*argv, "-v") == 0) {
8512703Smckusick 		vflag++;
8612703Smckusick 		goto again;
8712660Smckusick 	}
8812703Smckusick 	if (argc > 0 && strcmp(*argv, "-a") == 0) {
8912703Smckusick 		aflag++;
9012703Smckusick 		goto again;
9112703Smckusick 	}
9224758Sserge 	if (argc > 0 && strcmp(*argv, "-p") == 0) {
9324758Sserge 		pflag++;
9424758Sserge 		goto again;
9524758Sserge 	}
9612703Smckusick 	if (argc <= 0 && !aflag) {
9712703Smckusick 		fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
9824758Sserge 			"quotacheck [-v] [-p] -a",
9924758Sserge 			"quotacheck [-v] [-p] filesys ...");
10012660Smckusick 		exit(1);
10112660Smckusick 	}
10224661Sserge 
10324661Sserge 	setpwent();
10424661Sserge 	while ((pw = getpwent()) != 0) {
10524661Sserge 		fup = lookup(pw->pw_uid);
10624758Sserge 		if (fup == 0) {
10724661Sserge 			fup = adduid(pw->pw_uid);
10824758Sserge 			strncpy(fup->fu_name, pw->pw_name,
10924758Sserge 				sizeof(fup->fu_name));
11024758Sserge 		}
11112802Smckusick 	}
11224661Sserge 	endpwent();
11324661Sserge 
11424758Sserge 	if (pflag)
11524758Sserge 		errs = preen(argc, argv);
11624758Sserge 	else {
11724758Sserge 		if (setfsent() == 0) {
11824758Sserge 			fprintf(stderr, "Can't open ");
11924758Sserge 			perror(FSTAB);
12024758Sserge 			exit(8);
12124758Sserge 		}
12224758Sserge 		while ((fs = getfsent()) != NULL) {
12324758Sserge 			if (aflag &&
12424758Sserge 			    (fs->fs_type == 0 ||
12524758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
12624758Sserge 				continue;
12724758Sserge 			if (!aflag &&
12824758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
12924758Sserge 			      oneof(fs->fs_spec, argv, argc)))
13024758Sserge 				continue;
13124758Sserge 			(void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname);
13224758Sserge 			errs += chkquota(fs->fs_spec, fs->fs_file, quotafile);
13324758Sserge 		}
13424758Sserge 		endfsent();
13512660Smckusick 	}
13624758Sserge 
13712703Smckusick 	for (i = 0; i < argc; i++)
13812703Smckusick 		if ((done & (1 << i)) == 0)
13924782Sserge 			fprintf(stderr, "%s not found in %s\n",
14024782Sserge 				argv[i], FSTAB);
14112703Smckusick 	exit(errs);
14212703Smckusick }
14312660Smckusick 
14424758Sserge preen(argc, argv)
14524758Sserge 	int argc;
14624758Sserge 	char **argv;
14724758Sserge {
14824758Sserge 	register struct fstab *fs;
14924758Sserge 	register int passno, anygtr;
15024758Sserge 	register int errs;
15124758Sserge 	union wait status;
15224758Sserge 
15324758Sserge 	passno = 1;
15424758Sserge 	errs = 0;
15524758Sserge 	do {
15624758Sserge 		anygtr = 0;
15724758Sserge 
15824758Sserge 		if (setfsent() == 0) {
15924758Sserge 			fprintf(stderr, "Can't open ");
16024758Sserge 			perror(FSTAB);
16124758Sserge 			exit(8);
16224758Sserge 		}
16324758Sserge 
16424758Sserge 		while ((fs = getfsent()) != NULL) {
16524758Sserge 			if (fs->fs_passno > passno)
16624758Sserge 				anygtr = 1;
16724758Sserge 
16824758Sserge 			if (aflag &&
16924758Sserge 			    (fs->fs_type == 0 ||
17024758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
17124758Sserge 				continue;
17224758Sserge 
17324758Sserge 			if (!aflag &&
17424758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
17524758Sserge 			      oneof(fs->fs_spec, argv, argc)))
17624758Sserge 				continue;
17724758Sserge 
17824758Sserge 			if (fs->fs_passno != passno)
17924758Sserge 				continue;
18024758Sserge 
18124758Sserge 			switch (fork()) {
18224758Sserge 			case -1:
18324758Sserge 				perror("fork");
18424758Sserge 				exit(8);
18524758Sserge 				break;
18624758Sserge 
18724758Sserge 			case 0:
18824758Sserge 				sprintf(quotafile, "%s/%s",
18924758Sserge 					fs->fs_file, qfname);
19024758Sserge 				exit(chkquota(fs->fs_spec,
19124758Sserge 					fs->fs_file, quotafile));
19224758Sserge 			}
19324758Sserge 		}
19424758Sserge 
19524758Sserge 		while (wait(&status) != -1)
19624758Sserge 			errs += status.w_retcode;
19724758Sserge 
19824758Sserge 		passno++;
19924758Sserge 	} while (anygtr);
20024758Sserge 
20124758Sserge 	return (errs);
20224758Sserge }
20324758Sserge 
20424661Sserge chkquota(fsdev, fsfile, qffile)
20512703Smckusick 	char *fsdev;
20624661Sserge 	char *fsfile;
20712703Smckusick 	char *qffile;
20812703Smckusick {
20912703Smckusick 	register struct fileusage *fup;
21012703Smckusick 	dev_t quotadev;
21125377Sserge 	register FILE *qfi, *qfo;
21212703Smckusick 	u_short uid;
21325377Sserge 	int cg, i, fdo;
21412703Smckusick 	char *rawdisk;
21512703Smckusick 	struct stat statb;
21612703Smckusick 	struct dqblk dqbuf;
21721085Smckusick 	static int warned = 0;
21821085Smckusick 	extern int errno;
21912660Smckusick 
22012703Smckusick 	rawdisk = makerawname(fsdev);
22112703Smckusick 	if (vflag)
22224758Sserge 		fprintf(stdout, "*** Checking quotas for %s (%s)\n", rawdisk, fsfile);
22312703Smckusick 	fi = open(rawdisk, 0);
22412703Smckusick 	if (fi < 0) {
22512703Smckusick 		perror(rawdisk);
22612703Smckusick 		return (1);
22712660Smckusick 	}
22825377Sserge 	qfi = fopen(qffile, "r");
22925377Sserge 	if (qfi == NULL) {
23012703Smckusick 		perror(qffile);
23124661Sserge 		close(fi);
23212703Smckusick 		return (1);
23312660Smckusick 	}
23425377Sserge 	if (fstat(fileno(qfi), &statb) < 0) {
23512703Smckusick 		perror(qffile);
23625377Sserge 		fclose(qfi);
23724661Sserge 		close(fi);
23812703Smckusick 		return (1);
23912703Smckusick 	}
24012703Smckusick 	quotadev = statb.st_dev;
24112703Smckusick 	if (stat(fsdev, &statb) < 0) {
24212703Smckusick 		perror(fsdev);
24325377Sserge 		fclose(qfi);
24424661Sserge 		close(fi);
24512703Smckusick 		return (1);
24612703Smckusick 	}
24712703Smckusick 	if (quotadev != statb.st_rdev) {
24812703Smckusick 		fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
24912703Smckusick 			qffile, quotadev, fsdev, statb.st_rdev);
25025377Sserge 		fclose(qfi);
25124661Sserge 		close(fi);
25212703Smckusick 		return (1);
25312703Smckusick 	}
25425377Sserge 	/*
25525377Sserge 	 * Must do fdopen(open(qffile, 1), "w") instead of fopen(qffile, "w")
25625377Sserge 	 * because fopen(qffile, "w") would truncate the quota file.
25725377Sserge 	 */
25825377Sserge 	fdo = open(qffile, 1);
25925377Sserge 	if (fdo < 0 || (qfo = fdopen(fdo, "w")) == NULL) {
26025377Sserge 		perror(qffile);
26125377Sserge 		if (fdo >= 0)
26225377Sserge 			close(fdo);
26325377Sserge 		fclose(qfi);
26425377Sserge 		close(fi);
26525377Sserge 		return (1);
26625377Sserge 	}
26724758Sserge 	if (quota(Q_SYNC, 0, quotadev, (caddr_t)0) < 0 &&
26821085Smckusick 	    errno == EINVAL && !warned && vflag) {
26921085Smckusick 		warned++;
27021085Smckusick 		fprintf(stdout,
27121085Smckusick 		    "*** Warning: Quotas are not compiled into this kernel\n");
27221085Smckusick 	}
27312660Smckusick 	sync();
274*30558Smckusick 	bread(SBOFF, (char *)&sblock, SBSIZE);
275*30558Smckusick 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
27612660Smckusick 	ino = 0;
27712660Smckusick 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
27812660Smckusick 		dp = NULL;
27912660Smckusick 		for (i = 0; i < sblock.fs_ipg; i++)
28012660Smckusick 			acct(ginode());
28112660Smckusick 	}
28212703Smckusick 	for (uid = 0; uid <= highuid; uid++) {
28325377Sserge 		i = fread(&dqbuf, sizeof(struct dqblk), 1, qfi);
28412703Smckusick 		if (i == 0)
28512802Smckusick 			dqbuf = zerodqbuf;
28618415Smckusick 		fup = lookup(uid);
28724758Sserge 		if (fup == 0)
28824758Sserge 			fup = &zerofileusage;
28912703Smckusick 		if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
29012802Smckusick 		    dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
29112802Smckusick 			fup->fu_usage.du_curinodes = 0;
29212802Smckusick 			fup->fu_usage.du_curblocks = 0;
29325377Sserge 			fseek(qfo, (long)sizeof(struct dqblk), 1);
29412703Smckusick 			continue;
29512802Smckusick 		}
29612703Smckusick 		if (vflag) {
29724758Sserge 			if (pflag)
29824758Sserge 				printf("%s: ", rawdisk);
29912802Smckusick 			if (fup->fu_name[0] != '\0')
30024758Sserge 				printf("%-8s fixed:", fup->fu_name);
30112802Smckusick 			else
30224758Sserge 				printf("#%-7d fixed:", uid);
30324758Sserge 			if (dqbuf.dqb_curinodes != fup->fu_usage.du_curinodes)
30425377Sserge 				fprintf(stdout, "\tinodes %d -> %d",
30524758Sserge 					dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
30624758Sserge 			if (dqbuf.dqb_curblocks != fup->fu_usage.du_curblocks)
30725377Sserge 				fprintf(stdout, "\tblocks %d -> %d",
30824758Sserge 					dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
30924758Sserge 			fprintf(stdout, "\n");
31012660Smckusick 		}
31112703Smckusick 		dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
31212703Smckusick 		dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
31325377Sserge 		fwrite(&dqbuf, sizeof(struct dqblk), 1, qfo);
31412703Smckusick 		quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
31513255Smckusick 		fup->fu_usage.du_curinodes = 0;
31613255Smckusick 		fup->fu_usage.du_curblocks = 0;
31712660Smckusick 	}
31825377Sserge 	fflush(qfo);
31925377Sserge 	ftruncate(fileno(qfo), (off_t)((highuid + 1) * sizeof(struct dqblk)));
32025377Sserge 	fclose(qfi);
32125377Sserge 	fclose(qfo);
32224661Sserge 	close(fi);
32312703Smckusick 	return (0);
32412660Smckusick }
32512660Smckusick 
32612660Smckusick acct(ip)
32712660Smckusick 	register struct dinode *ip;
32812660Smckusick {
32912703Smckusick 	register struct fileusage *fup;
33012660Smckusick 
33112660Smckusick 	if (ip == NULL)
33212660Smckusick 		return;
33312660Smckusick 	if (ip->di_mode == 0)
33412660Smckusick 		return;
33524758Sserge 	fup = adduid(ip->di_uid);
33612703Smckusick 	fup->fu_usage.du_curinodes++;
33712660Smckusick 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
33812660Smckusick 		return;
33912703Smckusick 	fup->fu_usage.du_curblocks += ip->di_blocks;
34012660Smckusick }
34112660Smckusick 
34212703Smckusick oneof(target, list, n)
34312703Smckusick 	char *target, *list[];
34412703Smckusick 	register int n;
34512660Smckusick {
34612703Smckusick 	register int i;
34712660Smckusick 
34812703Smckusick 	for (i = 0; i < n; i++)
34912703Smckusick 		if (strcmp(target, list[i]) == 0) {
35012703Smckusick 			done |= 1 << i;
35112703Smckusick 			return (1);
35212703Smckusick 		}
35312703Smckusick 	return (0);
35412660Smckusick }
35512660Smckusick 
35612660Smckusick struct dinode *
35712660Smckusick ginode()
35812660Smckusick {
35912660Smckusick 	register unsigned long iblk;
36012660Smckusick 
36112660Smckusick 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
36212660Smckusick 		iblk = itod(&sblock, ino);
36312660Smckusick 		bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
36412660Smckusick 		dp = &itab[ino % INOPB(&sblock)];
36512660Smckusick 	}
36612660Smckusick 	if (ino++ < ROOTINO)
36712660Smckusick 		return(NULL);
36812660Smckusick 	return(dp);
36912660Smckusick }
37012660Smckusick 
37112660Smckusick bread(bno, buf, cnt)
37212660Smckusick 	long unsigned bno;
37312660Smckusick 	char *buf;
37412660Smckusick {
37512660Smckusick 
376*30558Smckusick 	if (lseek(fi, bno * dev_bsize, 0) < 0) {
37725377Sserge 		perror("lseek");
37825377Sserge 		exit(1);
37925377Sserge 	}
38025377Sserge 
38112660Smckusick 	if (read(fi, buf, cnt) != cnt) {
38224758Sserge 		perror("read");
38312660Smckusick 		exit(1);
38412660Smckusick 	}
38512660Smckusick }
38612660Smckusick 
38712703Smckusick struct fileusage *
38812703Smckusick lookup(uid)
38912703Smckusick 	u_short uid;
39012660Smckusick {
39112703Smckusick 	register struct fileusage *fup;
39212660Smckusick 
39312703Smckusick 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
39412703Smckusick 		if (fup->fu_uid == uid)
39512703Smckusick 			return (fup);
39612703Smckusick 	return ((struct fileusage *)0);
39712660Smckusick }
39812660Smckusick 
39912703Smckusick struct fileusage *
40012703Smckusick adduid(uid)
40112703Smckusick 	u_short uid;
40212660Smckusick {
40312703Smckusick 	struct fileusage *fup, **fhp;
40424661Sserge 	extern char *calloc();
40512660Smckusick 
40612703Smckusick 	fup = lookup(uid);
40712703Smckusick 	if (fup != 0)
40812703Smckusick 		return (fup);
40912703Smckusick 	fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
41012703Smckusick 	if (fup == 0) {
41112703Smckusick 		fprintf(stderr, "out of memory for fileusage structures\n");
41212703Smckusick 		exit(1);
41312703Smckusick 	}
41412703Smckusick 	fhp = &fuhead[uid % FUHASH];
41512703Smckusick 	fup->fu_next = *fhp;
41612703Smckusick 	*fhp = fup;
41712703Smckusick 	fup->fu_uid = uid;
41812703Smckusick 	if (uid > highuid)
41912703Smckusick 		highuid = uid;
42012703Smckusick 	return (fup);
42112660Smckusick }
42212660Smckusick 
42312660Smckusick char *
42412703Smckusick makerawname(name)
42512703Smckusick 	char *name;
42612660Smckusick {
42712703Smckusick 	register char *cp;
42812703Smckusick 	char tmp, ch, *rindex();
42912703Smckusick 	static char rawname[MAXPATHLEN];
43012660Smckusick 
43112703Smckusick 	strcpy(rawname, name);
43224661Sserge 	cp = rindex(rawname, '/');
43324782Sserge 	if (cp == NULL)
43412703Smckusick 		return (name);
43524661Sserge 	else
43624661Sserge 		cp++;
43712703Smckusick 	for (ch = 'r'; *cp != '\0'; ) {
43812703Smckusick 		tmp = *cp;
43912703Smckusick 		*cp++ = ch;
44012703Smckusick 		ch = tmp;
44112703Smckusick 	}
44212703Smckusick 	*cp++ = ch;
44312703Smckusick 	*cp = '\0';
44412703Smckusick 	return (rawname);
44512660Smckusick }
446