121517Smckusick /*
221517Smckusick  * Copyright (c) 1980 Regents of the University of California.
3*34364Sbostic  * All rights reserved.
4*34364Sbostic  *
5*34364Sbostic  * Redistribution and use in source and binary forms are permitted
6*34364Sbostic  * provided that this notice is preserved and that due credit is given
7*34364Sbostic  * to the University of California at Berkeley. The name of the University
8*34364Sbostic  * may not be used to endorse or promote products derived from this
9*34364Sbostic  * software without specific prior written permission. This software
10*34364Sbostic  * is provided ``as is'' without express or implied warranty.
1121517Smckusick  */
1221517Smckusick 
1312703Smckusick #ifndef lint
1421517Smckusick char copyright[] =
1521517Smckusick "@(#) Copyright (c) 1980 Regents of the University of California.\n\
1621517Smckusick  All rights reserved.\n";
17*34364Sbostic #endif /* not lint */
1812660Smckusick 
1921517Smckusick #ifndef lint
20*34364Sbostic static char sccsid[] = "@(#)quotacheck.c	5.9 (Berkeley) 05/20/88";
21*34364Sbostic #endif /* not lint */
2221517Smckusick 
2312660Smckusick /*
2412660Smckusick  * Fix up / report on disc quotas & usage
2512660Smckusick  */
2612660Smckusick #include <stdio.h>
2712660Smckusick #include <ctype.h>
2812660Smckusick #include <signal.h>
2921085Smckusick #include <errno.h>
3012660Smckusick #include <sys/param.h>
3112660Smckusick #include <sys/inode.h>
3212660Smckusick #include <sys/fs.h>
3312660Smckusick #include <sys/quota.h>
3412660Smckusick #include <sys/stat.h>
3524758Sserge #include <sys/wait.h>
3612703Smckusick #include <fstab.h>
3712802Smckusick #include <pwd.h>
3812660Smckusick 
3912660Smckusick union {
4012660Smckusick 	struct	fs	sblk;
4112703Smckusick 	char	dummy[MAXBSIZE];
4212660Smckusick } un;
4312660Smckusick #define	sblock	un.sblk
4412703Smckusick 
4512703Smckusick #define	ITABSZ	256
4612660Smckusick struct	dinode	itab[ITABSZ];
4712660Smckusick struct	dinode	*dp;
4812660Smckusick 
4912802Smckusick #define LOGINNAMESIZE 8
5012703Smckusick struct fileusage {
5112802Smckusick 	struct fileusage *fu_next;
5212703Smckusick 	struct dqusage fu_usage;
5312703Smckusick 	u_short	fu_uid;
5412802Smckusick 	char fu_name[LOGINNAMESIZE + 1];
5512703Smckusick };
5612703Smckusick #define FUHASH 997
5712703Smckusick struct fileusage *fuhead[FUHASH];
5812703Smckusick struct fileusage *lookup();
5912703Smckusick struct fileusage *adduid();
6012703Smckusick int highuid;
6112660Smckusick 
6212703Smckusick int fi;
6312703Smckusick ino_t ino;
6412703Smckusick long done;
6512660Smckusick struct	passwd	*getpwent();
6612660Smckusick struct	dinode	*ginode();
6712703Smckusick char *malloc(), *makerawname();
6812660Smckusick 
6912703Smckusick int	vflag;		/* verbose */
7012703Smckusick int	aflag;		/* all file systems */
7124758Sserge int	pflag;		/* fsck like parallel check */
7212703Smckusick 
7312703Smckusick char *qfname = "quotas";
7412703Smckusick char quotafile[MAXPATHLEN + 1];
7512802Smckusick struct dqblk zerodqbuf;
7624758Sserge struct fileusage zerofileusage;
7730558Smckusick long dev_bsize = 1;
7812703Smckusick 
7912660Smckusick main(argc, argv)
8012703Smckusick 	int argc;
8112660Smckusick 	char **argv;
8212660Smckusick {
8312703Smckusick 	register struct fstab *fs;
8412802Smckusick 	register struct fileusage *fup;
8512802Smckusick 	register struct passwd *pw;
8612703Smckusick 	int i, errs = 0;
8712660Smckusick 
8812703Smckusick again:
8912703Smckusick 	argc--, argv++;
9012703Smckusick 	if (argc > 0 && strcmp(*argv, "-v") == 0) {
9112703Smckusick 		vflag++;
9212703Smckusick 		goto again;
9312660Smckusick 	}
9412703Smckusick 	if (argc > 0 && strcmp(*argv, "-a") == 0) {
9512703Smckusick 		aflag++;
9612703Smckusick 		goto again;
9712703Smckusick 	}
9824758Sserge 	if (argc > 0 && strcmp(*argv, "-p") == 0) {
9924758Sserge 		pflag++;
10024758Sserge 		goto again;
10124758Sserge 	}
10212703Smckusick 	if (argc <= 0 && !aflag) {
10312703Smckusick 		fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
10424758Sserge 			"quotacheck [-v] [-p] -a",
10524758Sserge 			"quotacheck [-v] [-p] filesys ...");
10612660Smckusick 		exit(1);
10712660Smckusick 	}
10824661Sserge 
10924661Sserge 	setpwent();
11024661Sserge 	while ((pw = getpwent()) != 0) {
11124661Sserge 		fup = lookup(pw->pw_uid);
11224758Sserge 		if (fup == 0) {
11324661Sserge 			fup = adduid(pw->pw_uid);
11424758Sserge 			strncpy(fup->fu_name, pw->pw_name,
11524758Sserge 				sizeof(fup->fu_name));
11624758Sserge 		}
11712802Smckusick 	}
11824661Sserge 	endpwent();
11924661Sserge 
12024758Sserge 	if (pflag)
12124758Sserge 		errs = preen(argc, argv);
12224758Sserge 	else {
12324758Sserge 		if (setfsent() == 0) {
12424758Sserge 			fprintf(stderr, "Can't open ");
12524758Sserge 			perror(FSTAB);
12624758Sserge 			exit(8);
12724758Sserge 		}
12824758Sserge 		while ((fs = getfsent()) != NULL) {
12924758Sserge 			if (aflag &&
13024758Sserge 			    (fs->fs_type == 0 ||
13124758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
13224758Sserge 				continue;
13324758Sserge 			if (!aflag &&
13424758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
13524758Sserge 			      oneof(fs->fs_spec, argv, argc)))
13624758Sserge 				continue;
13724758Sserge 			(void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname);
13824758Sserge 			errs += chkquota(fs->fs_spec, fs->fs_file, quotafile);
13924758Sserge 		}
14024758Sserge 		endfsent();
14112660Smckusick 	}
14224758Sserge 
14312703Smckusick 	for (i = 0; i < argc; i++)
14412703Smckusick 		if ((done & (1 << i)) == 0)
14524782Sserge 			fprintf(stderr, "%s not found in %s\n",
14624782Sserge 				argv[i], FSTAB);
14712703Smckusick 	exit(errs);
14812703Smckusick }
14912660Smckusick 
15024758Sserge preen(argc, argv)
15124758Sserge 	int argc;
15224758Sserge 	char **argv;
15324758Sserge {
15424758Sserge 	register struct fstab *fs;
15524758Sserge 	register int passno, anygtr;
15624758Sserge 	register int errs;
15724758Sserge 	union wait status;
15824758Sserge 
15924758Sserge 	passno = 1;
16024758Sserge 	errs = 0;
16124758Sserge 	do {
16224758Sserge 		anygtr = 0;
16324758Sserge 
16424758Sserge 		if (setfsent() == 0) {
16524758Sserge 			fprintf(stderr, "Can't open ");
16624758Sserge 			perror(FSTAB);
16724758Sserge 			exit(8);
16824758Sserge 		}
16924758Sserge 
17024758Sserge 		while ((fs = getfsent()) != NULL) {
17124758Sserge 			if (fs->fs_passno > passno)
17224758Sserge 				anygtr = 1;
17324758Sserge 
17424758Sserge 			if (aflag &&
17524758Sserge 			    (fs->fs_type == 0 ||
17624758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
17724758Sserge 				continue;
17824758Sserge 
17924758Sserge 			if (!aflag &&
18024758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
18124758Sserge 			      oneof(fs->fs_spec, argv, argc)))
18224758Sserge 				continue;
18324758Sserge 
18424758Sserge 			if (fs->fs_passno != passno)
18524758Sserge 				continue;
18624758Sserge 
18724758Sserge 			switch (fork()) {
18824758Sserge 			case -1:
18924758Sserge 				perror("fork");
19024758Sserge 				exit(8);
19124758Sserge 				break;
19224758Sserge 
19324758Sserge 			case 0:
19432445Sbostic 				(void) sprintf(quotafile, "%s/%s",
19524758Sserge 					fs->fs_file, qfname);
19624758Sserge 				exit(chkquota(fs->fs_spec,
19724758Sserge 					fs->fs_file, quotafile));
19824758Sserge 			}
19924758Sserge 		}
20024758Sserge 
20124758Sserge 		while (wait(&status) != -1)
20224758Sserge 			errs += status.w_retcode;
20324758Sserge 
20424758Sserge 		passno++;
20524758Sserge 	} while (anygtr);
20624758Sserge 
20724758Sserge 	return (errs);
20824758Sserge }
20924758Sserge 
21024661Sserge chkquota(fsdev, fsfile, qffile)
21112703Smckusick 	char *fsdev;
21224661Sserge 	char *fsfile;
21312703Smckusick 	char *qffile;
21412703Smckusick {
21512703Smckusick 	register struct fileusage *fup;
21612703Smckusick 	dev_t quotadev;
21725377Sserge 	register FILE *qfi, *qfo;
21812703Smckusick 	u_short uid;
21925377Sserge 	int cg, i, fdo;
22012703Smckusick 	char *rawdisk;
22112703Smckusick 	struct stat statb;
22212703Smckusick 	struct dqblk dqbuf;
22321085Smckusick 	static int warned = 0;
22421085Smckusick 	extern int errno;
22512660Smckusick 
22612703Smckusick 	rawdisk = makerawname(fsdev);
22712703Smckusick 	if (vflag)
22824758Sserge 		fprintf(stdout, "*** Checking quotas for %s (%s)\n", rawdisk, fsfile);
22912703Smckusick 	fi = open(rawdisk, 0);
23012703Smckusick 	if (fi < 0) {
23112703Smckusick 		perror(rawdisk);
23212703Smckusick 		return (1);
23312660Smckusick 	}
23425377Sserge 	qfi = fopen(qffile, "r");
23525377Sserge 	if (qfi == NULL) {
23612703Smckusick 		perror(qffile);
23724661Sserge 		close(fi);
23812703Smckusick 		return (1);
23912660Smckusick 	}
24025377Sserge 	if (fstat(fileno(qfi), &statb) < 0) {
24112703Smckusick 		perror(qffile);
24225377Sserge 		fclose(qfi);
24324661Sserge 		close(fi);
24412703Smckusick 		return (1);
24512703Smckusick 	}
24612703Smckusick 	quotadev = statb.st_dev;
24712703Smckusick 	if (stat(fsdev, &statb) < 0) {
24812703Smckusick 		perror(fsdev);
24925377Sserge 		fclose(qfi);
25024661Sserge 		close(fi);
25112703Smckusick 		return (1);
25212703Smckusick 	}
25312703Smckusick 	if (quotadev != statb.st_rdev) {
25412703Smckusick 		fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
25512703Smckusick 			qffile, quotadev, fsdev, statb.st_rdev);
25625377Sserge 		fclose(qfi);
25724661Sserge 		close(fi);
25812703Smckusick 		return (1);
25912703Smckusick 	}
26025377Sserge 	/*
26125377Sserge 	 * Must do fdopen(open(qffile, 1), "w") instead of fopen(qffile, "w")
26225377Sserge 	 * because fopen(qffile, "w") would truncate the quota file.
26325377Sserge 	 */
26425377Sserge 	fdo = open(qffile, 1);
26525377Sserge 	if (fdo < 0 || (qfo = fdopen(fdo, "w")) == NULL) {
26625377Sserge 		perror(qffile);
26725377Sserge 		if (fdo >= 0)
26825377Sserge 			close(fdo);
26925377Sserge 		fclose(qfi);
27025377Sserge 		close(fi);
27125377Sserge 		return (1);
27225377Sserge 	}
27324758Sserge 	if (quota(Q_SYNC, 0, quotadev, (caddr_t)0) < 0 &&
27421085Smckusick 	    errno == EINVAL && !warned && vflag) {
27521085Smckusick 		warned++;
27621085Smckusick 		fprintf(stdout,
27721085Smckusick 		    "*** Warning: Quotas are not compiled into this kernel\n");
27821085Smckusick 	}
27912660Smckusick 	sync();
28030558Smckusick 	bread(SBOFF, (char *)&sblock, SBSIZE);
28130558Smckusick 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
28212660Smckusick 	ino = 0;
28312660Smckusick 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
28412660Smckusick 		dp = NULL;
28512660Smckusick 		for (i = 0; i < sblock.fs_ipg; i++)
28612660Smckusick 			acct(ginode());
28712660Smckusick 	}
28812703Smckusick 	for (uid = 0; uid <= highuid; uid++) {
28925377Sserge 		i = fread(&dqbuf, sizeof(struct dqblk), 1, qfi);
29012703Smckusick 		if (i == 0)
29112802Smckusick 			dqbuf = zerodqbuf;
29218415Smckusick 		fup = lookup(uid);
29324758Sserge 		if (fup == 0)
29424758Sserge 			fup = &zerofileusage;
29512703Smckusick 		if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
29612802Smckusick 		    dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
29712802Smckusick 			fup->fu_usage.du_curinodes = 0;
29812802Smckusick 			fup->fu_usage.du_curblocks = 0;
29925377Sserge 			fseek(qfo, (long)sizeof(struct dqblk), 1);
30012703Smckusick 			continue;
30112802Smckusick 		}
30212703Smckusick 		if (vflag) {
30324758Sserge 			if (pflag)
30424758Sserge 				printf("%s: ", rawdisk);
30512802Smckusick 			if (fup->fu_name[0] != '\0')
30624758Sserge 				printf("%-8s fixed:", fup->fu_name);
30712802Smckusick 			else
30824758Sserge 				printf("#%-7d fixed:", uid);
30924758Sserge 			if (dqbuf.dqb_curinodes != fup->fu_usage.du_curinodes)
31025377Sserge 				fprintf(stdout, "\tinodes %d -> %d",
31124758Sserge 					dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
31224758Sserge 			if (dqbuf.dqb_curblocks != fup->fu_usage.du_curblocks)
31325377Sserge 				fprintf(stdout, "\tblocks %d -> %d",
31424758Sserge 					dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
31524758Sserge 			fprintf(stdout, "\n");
31612660Smckusick 		}
31712703Smckusick 		dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
31812703Smckusick 		dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
31925377Sserge 		fwrite(&dqbuf, sizeof(struct dqblk), 1, qfo);
32012703Smckusick 		quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
32113255Smckusick 		fup->fu_usage.du_curinodes = 0;
32213255Smckusick 		fup->fu_usage.du_curblocks = 0;
32312660Smckusick 	}
32425377Sserge 	fflush(qfo);
32525377Sserge 	ftruncate(fileno(qfo), (off_t)((highuid + 1) * sizeof(struct dqblk)));
32625377Sserge 	fclose(qfi);
32725377Sserge 	fclose(qfo);
32824661Sserge 	close(fi);
32912703Smckusick 	return (0);
33012660Smckusick }
33112660Smckusick 
33212660Smckusick acct(ip)
33312660Smckusick 	register struct dinode *ip;
33412660Smckusick {
33512703Smckusick 	register struct fileusage *fup;
33612660Smckusick 
33712660Smckusick 	if (ip == NULL)
33812660Smckusick 		return;
33912660Smckusick 	if (ip->di_mode == 0)
34012660Smckusick 		return;
34124758Sserge 	fup = adduid(ip->di_uid);
34212703Smckusick 	fup->fu_usage.du_curinodes++;
34312660Smckusick 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
34412660Smckusick 		return;
34512703Smckusick 	fup->fu_usage.du_curblocks += ip->di_blocks;
34612660Smckusick }
34712660Smckusick 
34812703Smckusick oneof(target, list, n)
34912703Smckusick 	char *target, *list[];
35012703Smckusick 	register int n;
35112660Smckusick {
35212703Smckusick 	register int i;
35312660Smckusick 
35412703Smckusick 	for (i = 0; i < n; i++)
35512703Smckusick 		if (strcmp(target, list[i]) == 0) {
35612703Smckusick 			done |= 1 << i;
35712703Smckusick 			return (1);
35812703Smckusick 		}
35912703Smckusick 	return (0);
36012660Smckusick }
36112660Smckusick 
36212660Smckusick struct dinode *
36312660Smckusick ginode()
36412660Smckusick {
36512660Smckusick 	register unsigned long iblk;
36612660Smckusick 
36712660Smckusick 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
36812660Smckusick 		iblk = itod(&sblock, ino);
36912660Smckusick 		bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
37012660Smckusick 		dp = &itab[ino % INOPB(&sblock)];
37112660Smckusick 	}
37212660Smckusick 	if (ino++ < ROOTINO)
37312660Smckusick 		return(NULL);
37412660Smckusick 	return(dp);
37512660Smckusick }
37612660Smckusick 
37712660Smckusick bread(bno, buf, cnt)
37812660Smckusick 	long unsigned bno;
37912660Smckusick 	char *buf;
38012660Smckusick {
38112660Smckusick 
38230558Smckusick 	if (lseek(fi, bno * dev_bsize, 0) < 0) {
38325377Sserge 		perror("lseek");
38425377Sserge 		exit(1);
38525377Sserge 	}
38625377Sserge 
38712660Smckusick 	if (read(fi, buf, cnt) != cnt) {
38824758Sserge 		perror("read");
38912660Smckusick 		exit(1);
39012660Smckusick 	}
39112660Smckusick }
39212660Smckusick 
39312703Smckusick struct fileusage *
39412703Smckusick lookup(uid)
39512703Smckusick 	u_short uid;
39612660Smckusick {
39712703Smckusick 	register struct fileusage *fup;
39812660Smckusick 
39912703Smckusick 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
40012703Smckusick 		if (fup->fu_uid == uid)
40112703Smckusick 			return (fup);
40212703Smckusick 	return ((struct fileusage *)0);
40312660Smckusick }
40412660Smckusick 
40512703Smckusick struct fileusage *
40612703Smckusick adduid(uid)
40712703Smckusick 	u_short uid;
40812660Smckusick {
40912703Smckusick 	struct fileusage *fup, **fhp;
41024661Sserge 	extern char *calloc();
41112660Smckusick 
41212703Smckusick 	fup = lookup(uid);
41312703Smckusick 	if (fup != 0)
41412703Smckusick 		return (fup);
41512703Smckusick 	fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
41612703Smckusick 	if (fup == 0) {
41712703Smckusick 		fprintf(stderr, "out of memory for fileusage structures\n");
41812703Smckusick 		exit(1);
41912703Smckusick 	}
42012703Smckusick 	fhp = &fuhead[uid % FUHASH];
42112703Smckusick 	fup->fu_next = *fhp;
42212703Smckusick 	*fhp = fup;
42312703Smckusick 	fup->fu_uid = uid;
42412703Smckusick 	if (uid > highuid)
42512703Smckusick 		highuid = uid;
42612703Smckusick 	return (fup);
42712660Smckusick }
42812660Smckusick 
42912660Smckusick char *
43012703Smckusick makerawname(name)
43112703Smckusick 	char *name;
43212660Smckusick {
43312703Smckusick 	register char *cp;
43412703Smckusick 	char tmp, ch, *rindex();
43512703Smckusick 	static char rawname[MAXPATHLEN];
43612660Smckusick 
43712703Smckusick 	strcpy(rawname, name);
43824661Sserge 	cp = rindex(rawname, '/');
43924782Sserge 	if (cp == NULL)
44012703Smckusick 		return (name);
44124661Sserge 	else
44224661Sserge 		cp++;
44312703Smckusick 	for (ch = 'r'; *cp != '\0'; ) {
44412703Smckusick 		tmp = *cp;
44512703Smckusick 		*cp++ = ch;
44612703Smckusick 		ch = tmp;
44712703Smckusick 	}
44812703Smckusick 	*cp++ = ch;
44912703Smckusick 	*cp = '\0';
45012703Smckusick 	return (rawname);
45112660Smckusick }
452