121517Smckusick /*
221517Smckusick  * Copyright (c) 1980 Regents of the University of California.
334364Sbostic  * All rights reserved.
434364Sbostic  *
534364Sbostic  * Redistribution and use in source and binary forms are permitted
6*34778Sbostic  * provided that the above copyright notice and this paragraph are
7*34778Sbostic  * duplicated in all such forms and that any documentation,
8*34778Sbostic  * advertising materials, and other materials related to such
9*34778Sbostic  * distribution and use acknowledge that the software was developed
10*34778Sbostic  * by the University of California, Berkeley.  The name of the
11*34778Sbostic  * University may not be used to endorse or promote products derived
12*34778Sbostic  * from this software without specific prior written permission.
13*34778Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34778Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34778Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621517Smckusick  */
1721517Smckusick 
1812703Smckusick #ifndef lint
1921517Smckusick char copyright[] =
2021517Smckusick "@(#) Copyright (c) 1980 Regents of the University of California.\n\
2121517Smckusick  All rights reserved.\n";
2234364Sbostic #endif /* not lint */
2312660Smckusick 
2421517Smckusick #ifndef lint
25*34778Sbostic static char sccsid[] = "@(#)quotacheck.c	5.10 (Berkeley) 06/18/88";
2634364Sbostic #endif /* not lint */
2721517Smckusick 
2812660Smckusick /*
2912660Smckusick  * Fix up / report on disc quotas & usage
3012660Smckusick  */
3112660Smckusick #include <stdio.h>
3212660Smckusick #include <ctype.h>
3312660Smckusick #include <signal.h>
3421085Smckusick #include <errno.h>
3512660Smckusick #include <sys/param.h>
3612660Smckusick #include <sys/inode.h>
3712660Smckusick #include <sys/fs.h>
3812660Smckusick #include <sys/quota.h>
3912660Smckusick #include <sys/stat.h>
4024758Sserge #include <sys/wait.h>
4112703Smckusick #include <fstab.h>
4212802Smckusick #include <pwd.h>
4312660Smckusick 
4412660Smckusick union {
4512660Smckusick 	struct	fs	sblk;
4612703Smckusick 	char	dummy[MAXBSIZE];
4712660Smckusick } un;
4812660Smckusick #define	sblock	un.sblk
4912703Smckusick 
5012703Smckusick #define	ITABSZ	256
5112660Smckusick struct	dinode	itab[ITABSZ];
5212660Smckusick struct	dinode	*dp;
5312660Smckusick 
5412802Smckusick #define LOGINNAMESIZE 8
5512703Smckusick struct fileusage {
5612802Smckusick 	struct fileusage *fu_next;
5712703Smckusick 	struct dqusage fu_usage;
5812703Smckusick 	u_short	fu_uid;
5912802Smckusick 	char fu_name[LOGINNAMESIZE + 1];
6012703Smckusick };
6112703Smckusick #define FUHASH 997
6212703Smckusick struct fileusage *fuhead[FUHASH];
6312703Smckusick struct fileusage *lookup();
6412703Smckusick struct fileusage *adduid();
6512703Smckusick int highuid;
6612660Smckusick 
6712703Smckusick int fi;
6812703Smckusick ino_t ino;
6912703Smckusick long done;
7012660Smckusick struct	passwd	*getpwent();
7112660Smckusick struct	dinode	*ginode();
7212703Smckusick char *malloc(), *makerawname();
7312660Smckusick 
7412703Smckusick int	vflag;		/* verbose */
7512703Smckusick int	aflag;		/* all file systems */
7624758Sserge int	pflag;		/* fsck like parallel check */
7712703Smckusick 
7812703Smckusick char *qfname = "quotas";
7912703Smckusick char quotafile[MAXPATHLEN + 1];
8012802Smckusick struct dqblk zerodqbuf;
8124758Sserge struct fileusage zerofileusage;
8230558Smckusick long dev_bsize = 1;
8312703Smckusick 
8412660Smckusick main(argc, argv)
8512703Smckusick 	int argc;
8612660Smckusick 	char **argv;
8712660Smckusick {
8812703Smckusick 	register struct fstab *fs;
8912802Smckusick 	register struct fileusage *fup;
9012802Smckusick 	register struct passwd *pw;
9112703Smckusick 	int i, errs = 0;
9212660Smckusick 
9312703Smckusick again:
9412703Smckusick 	argc--, argv++;
9512703Smckusick 	if (argc > 0 && strcmp(*argv, "-v") == 0) {
9612703Smckusick 		vflag++;
9712703Smckusick 		goto again;
9812660Smckusick 	}
9912703Smckusick 	if (argc > 0 && strcmp(*argv, "-a") == 0) {
10012703Smckusick 		aflag++;
10112703Smckusick 		goto again;
10212703Smckusick 	}
10324758Sserge 	if (argc > 0 && strcmp(*argv, "-p") == 0) {
10424758Sserge 		pflag++;
10524758Sserge 		goto again;
10624758Sserge 	}
10712703Smckusick 	if (argc <= 0 && !aflag) {
10812703Smckusick 		fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
10924758Sserge 			"quotacheck [-v] [-p] -a",
11024758Sserge 			"quotacheck [-v] [-p] filesys ...");
11112660Smckusick 		exit(1);
11212660Smckusick 	}
11324661Sserge 
11424661Sserge 	setpwent();
11524661Sserge 	while ((pw = getpwent()) != 0) {
11624661Sserge 		fup = lookup(pw->pw_uid);
11724758Sserge 		if (fup == 0) {
11824661Sserge 			fup = adduid(pw->pw_uid);
11924758Sserge 			strncpy(fup->fu_name, pw->pw_name,
12024758Sserge 				sizeof(fup->fu_name));
12124758Sserge 		}
12212802Smckusick 	}
12324661Sserge 	endpwent();
12424661Sserge 
12524758Sserge 	if (pflag)
12624758Sserge 		errs = preen(argc, argv);
12724758Sserge 	else {
12824758Sserge 		if (setfsent() == 0) {
12924758Sserge 			fprintf(stderr, "Can't open ");
13024758Sserge 			perror(FSTAB);
13124758Sserge 			exit(8);
13224758Sserge 		}
13324758Sserge 		while ((fs = getfsent()) != NULL) {
13424758Sserge 			if (aflag &&
13524758Sserge 			    (fs->fs_type == 0 ||
13624758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
13724758Sserge 				continue;
13824758Sserge 			if (!aflag &&
13924758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
14024758Sserge 			      oneof(fs->fs_spec, argv, argc)))
14124758Sserge 				continue;
14224758Sserge 			(void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname);
14324758Sserge 			errs += chkquota(fs->fs_spec, fs->fs_file, quotafile);
14424758Sserge 		}
14524758Sserge 		endfsent();
14612660Smckusick 	}
14724758Sserge 
14812703Smckusick 	for (i = 0; i < argc; i++)
14912703Smckusick 		if ((done & (1 << i)) == 0)
15024782Sserge 			fprintf(stderr, "%s not found in %s\n",
15124782Sserge 				argv[i], FSTAB);
15212703Smckusick 	exit(errs);
15312703Smckusick }
15412660Smckusick 
15524758Sserge preen(argc, argv)
15624758Sserge 	int argc;
15724758Sserge 	char **argv;
15824758Sserge {
15924758Sserge 	register struct fstab *fs;
16024758Sserge 	register int passno, anygtr;
16124758Sserge 	register int errs;
16224758Sserge 	union wait status;
16324758Sserge 
16424758Sserge 	passno = 1;
16524758Sserge 	errs = 0;
16624758Sserge 	do {
16724758Sserge 		anygtr = 0;
16824758Sserge 
16924758Sserge 		if (setfsent() == 0) {
17024758Sserge 			fprintf(stderr, "Can't open ");
17124758Sserge 			perror(FSTAB);
17224758Sserge 			exit(8);
17324758Sserge 		}
17424758Sserge 
17524758Sserge 		while ((fs = getfsent()) != NULL) {
17624758Sserge 			if (fs->fs_passno > passno)
17724758Sserge 				anygtr = 1;
17824758Sserge 
17924758Sserge 			if (aflag &&
18024758Sserge 			    (fs->fs_type == 0 ||
18124758Sserge 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
18224758Sserge 				continue;
18324758Sserge 
18424758Sserge 			if (!aflag &&
18524758Sserge 			    !(oneof(fs->fs_file, argv, argc) ||
18624758Sserge 			      oneof(fs->fs_spec, argv, argc)))
18724758Sserge 				continue;
18824758Sserge 
18924758Sserge 			if (fs->fs_passno != passno)
19024758Sserge 				continue;
19124758Sserge 
19224758Sserge 			switch (fork()) {
19324758Sserge 			case -1:
19424758Sserge 				perror("fork");
19524758Sserge 				exit(8);
19624758Sserge 				break;
19724758Sserge 
19824758Sserge 			case 0:
19932445Sbostic 				(void) sprintf(quotafile, "%s/%s",
20024758Sserge 					fs->fs_file, qfname);
20124758Sserge 				exit(chkquota(fs->fs_spec,
20224758Sserge 					fs->fs_file, quotafile));
20324758Sserge 			}
20424758Sserge 		}
20524758Sserge 
20624758Sserge 		while (wait(&status) != -1)
20724758Sserge 			errs += status.w_retcode;
20824758Sserge 
20924758Sserge 		passno++;
21024758Sserge 	} while (anygtr);
21124758Sserge 
21224758Sserge 	return (errs);
21324758Sserge }
21424758Sserge 
21524661Sserge chkquota(fsdev, fsfile, qffile)
21612703Smckusick 	char *fsdev;
21724661Sserge 	char *fsfile;
21812703Smckusick 	char *qffile;
21912703Smckusick {
22012703Smckusick 	register struct fileusage *fup;
22112703Smckusick 	dev_t quotadev;
22225377Sserge 	register FILE *qfi, *qfo;
22312703Smckusick 	u_short uid;
22425377Sserge 	int cg, i, fdo;
22512703Smckusick 	char *rawdisk;
22612703Smckusick 	struct stat statb;
22712703Smckusick 	struct dqblk dqbuf;
22821085Smckusick 	static int warned = 0;
22921085Smckusick 	extern int errno;
23012660Smckusick 
23112703Smckusick 	rawdisk = makerawname(fsdev);
23212703Smckusick 	if (vflag)
23324758Sserge 		fprintf(stdout, "*** Checking quotas for %s (%s)\n", rawdisk, fsfile);
23412703Smckusick 	fi = open(rawdisk, 0);
23512703Smckusick 	if (fi < 0) {
23612703Smckusick 		perror(rawdisk);
23712703Smckusick 		return (1);
23812660Smckusick 	}
23925377Sserge 	qfi = fopen(qffile, "r");
24025377Sserge 	if (qfi == NULL) {
24112703Smckusick 		perror(qffile);
24224661Sserge 		close(fi);
24312703Smckusick 		return (1);
24412660Smckusick 	}
24525377Sserge 	if (fstat(fileno(qfi), &statb) < 0) {
24612703Smckusick 		perror(qffile);
24725377Sserge 		fclose(qfi);
24824661Sserge 		close(fi);
24912703Smckusick 		return (1);
25012703Smckusick 	}
25112703Smckusick 	quotadev = statb.st_dev;
25212703Smckusick 	if (stat(fsdev, &statb) < 0) {
25312703Smckusick 		perror(fsdev);
25425377Sserge 		fclose(qfi);
25524661Sserge 		close(fi);
25612703Smckusick 		return (1);
25712703Smckusick 	}
25812703Smckusick 	if (quotadev != statb.st_rdev) {
25912703Smckusick 		fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
26012703Smckusick 			qffile, quotadev, fsdev, statb.st_rdev);
26125377Sserge 		fclose(qfi);
26224661Sserge 		close(fi);
26312703Smckusick 		return (1);
26412703Smckusick 	}
26525377Sserge 	/*
26625377Sserge 	 * Must do fdopen(open(qffile, 1), "w") instead of fopen(qffile, "w")
26725377Sserge 	 * because fopen(qffile, "w") would truncate the quota file.
26825377Sserge 	 */
26925377Sserge 	fdo = open(qffile, 1);
27025377Sserge 	if (fdo < 0 || (qfo = fdopen(fdo, "w")) == NULL) {
27125377Sserge 		perror(qffile);
27225377Sserge 		if (fdo >= 0)
27325377Sserge 			close(fdo);
27425377Sserge 		fclose(qfi);
27525377Sserge 		close(fi);
27625377Sserge 		return (1);
27725377Sserge 	}
27824758Sserge 	if (quota(Q_SYNC, 0, quotadev, (caddr_t)0) < 0 &&
27921085Smckusick 	    errno == EINVAL && !warned && vflag) {
28021085Smckusick 		warned++;
28121085Smckusick 		fprintf(stdout,
28221085Smckusick 		    "*** Warning: Quotas are not compiled into this kernel\n");
28321085Smckusick 	}
28412660Smckusick 	sync();
28530558Smckusick 	bread(SBOFF, (char *)&sblock, SBSIZE);
28630558Smckusick 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
28712660Smckusick 	ino = 0;
28812660Smckusick 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
28912660Smckusick 		dp = NULL;
29012660Smckusick 		for (i = 0; i < sblock.fs_ipg; i++)
29112660Smckusick 			acct(ginode());
29212660Smckusick 	}
29312703Smckusick 	for (uid = 0; uid <= highuid; uid++) {
29425377Sserge 		i = fread(&dqbuf, sizeof(struct dqblk), 1, qfi);
29512703Smckusick 		if (i == 0)
29612802Smckusick 			dqbuf = zerodqbuf;
29718415Smckusick 		fup = lookup(uid);
29824758Sserge 		if (fup == 0)
29924758Sserge 			fup = &zerofileusage;
30012703Smckusick 		if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
30112802Smckusick 		    dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
30212802Smckusick 			fup->fu_usage.du_curinodes = 0;
30312802Smckusick 			fup->fu_usage.du_curblocks = 0;
30425377Sserge 			fseek(qfo, (long)sizeof(struct dqblk), 1);
30512703Smckusick 			continue;
30612802Smckusick 		}
30712703Smckusick 		if (vflag) {
30824758Sserge 			if (pflag)
30924758Sserge 				printf("%s: ", rawdisk);
31012802Smckusick 			if (fup->fu_name[0] != '\0')
31124758Sserge 				printf("%-8s fixed:", fup->fu_name);
31212802Smckusick 			else
31324758Sserge 				printf("#%-7d fixed:", uid);
31424758Sserge 			if (dqbuf.dqb_curinodes != fup->fu_usage.du_curinodes)
31525377Sserge 				fprintf(stdout, "\tinodes %d -> %d",
31624758Sserge 					dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
31724758Sserge 			if (dqbuf.dqb_curblocks != fup->fu_usage.du_curblocks)
31825377Sserge 				fprintf(stdout, "\tblocks %d -> %d",
31924758Sserge 					dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
32024758Sserge 			fprintf(stdout, "\n");
32112660Smckusick 		}
32212703Smckusick 		dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
32312703Smckusick 		dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
32425377Sserge 		fwrite(&dqbuf, sizeof(struct dqblk), 1, qfo);
32512703Smckusick 		quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
32613255Smckusick 		fup->fu_usage.du_curinodes = 0;
32713255Smckusick 		fup->fu_usage.du_curblocks = 0;
32812660Smckusick 	}
32925377Sserge 	fflush(qfo);
33025377Sserge 	ftruncate(fileno(qfo), (off_t)((highuid + 1) * sizeof(struct dqblk)));
33125377Sserge 	fclose(qfi);
33225377Sserge 	fclose(qfo);
33324661Sserge 	close(fi);
33412703Smckusick 	return (0);
33512660Smckusick }
33612660Smckusick 
33712660Smckusick acct(ip)
33812660Smckusick 	register struct dinode *ip;
33912660Smckusick {
34012703Smckusick 	register struct fileusage *fup;
34112660Smckusick 
34212660Smckusick 	if (ip == NULL)
34312660Smckusick 		return;
34412660Smckusick 	if (ip->di_mode == 0)
34512660Smckusick 		return;
34624758Sserge 	fup = adduid(ip->di_uid);
34712703Smckusick 	fup->fu_usage.du_curinodes++;
34812660Smckusick 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
34912660Smckusick 		return;
35012703Smckusick 	fup->fu_usage.du_curblocks += ip->di_blocks;
35112660Smckusick }
35212660Smckusick 
35312703Smckusick oneof(target, list, n)
35412703Smckusick 	char *target, *list[];
35512703Smckusick 	register int n;
35612660Smckusick {
35712703Smckusick 	register int i;
35812660Smckusick 
35912703Smckusick 	for (i = 0; i < n; i++)
36012703Smckusick 		if (strcmp(target, list[i]) == 0) {
36112703Smckusick 			done |= 1 << i;
36212703Smckusick 			return (1);
36312703Smckusick 		}
36412703Smckusick 	return (0);
36512660Smckusick }
36612660Smckusick 
36712660Smckusick struct dinode *
36812660Smckusick ginode()
36912660Smckusick {
37012660Smckusick 	register unsigned long iblk;
37112660Smckusick 
37212660Smckusick 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
37312660Smckusick 		iblk = itod(&sblock, ino);
37412660Smckusick 		bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
37512660Smckusick 		dp = &itab[ino % INOPB(&sblock)];
37612660Smckusick 	}
37712660Smckusick 	if (ino++ < ROOTINO)
37812660Smckusick 		return(NULL);
37912660Smckusick 	return(dp);
38012660Smckusick }
38112660Smckusick 
38212660Smckusick bread(bno, buf, cnt)
38312660Smckusick 	long unsigned bno;
38412660Smckusick 	char *buf;
38512660Smckusick {
38612660Smckusick 
38730558Smckusick 	if (lseek(fi, bno * dev_bsize, 0) < 0) {
38825377Sserge 		perror("lseek");
38925377Sserge 		exit(1);
39025377Sserge 	}
39125377Sserge 
39212660Smckusick 	if (read(fi, buf, cnt) != cnt) {
39324758Sserge 		perror("read");
39412660Smckusick 		exit(1);
39512660Smckusick 	}
39612660Smckusick }
39712660Smckusick 
39812703Smckusick struct fileusage *
39912703Smckusick lookup(uid)
40012703Smckusick 	u_short uid;
40112660Smckusick {
40212703Smckusick 	register struct fileusage *fup;
40312660Smckusick 
40412703Smckusick 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
40512703Smckusick 		if (fup->fu_uid == uid)
40612703Smckusick 			return (fup);
40712703Smckusick 	return ((struct fileusage *)0);
40812660Smckusick }
40912660Smckusick 
41012703Smckusick struct fileusage *
41112703Smckusick adduid(uid)
41212703Smckusick 	u_short uid;
41312660Smckusick {
41412703Smckusick 	struct fileusage *fup, **fhp;
41524661Sserge 	extern char *calloc();
41612660Smckusick 
41712703Smckusick 	fup = lookup(uid);
41812703Smckusick 	if (fup != 0)
41912703Smckusick 		return (fup);
42012703Smckusick 	fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
42112703Smckusick 	if (fup == 0) {
42212703Smckusick 		fprintf(stderr, "out of memory for fileusage structures\n");
42312703Smckusick 		exit(1);
42412703Smckusick 	}
42512703Smckusick 	fhp = &fuhead[uid % FUHASH];
42612703Smckusick 	fup->fu_next = *fhp;
42712703Smckusick 	*fhp = fup;
42812703Smckusick 	fup->fu_uid = uid;
42912703Smckusick 	if (uid > highuid)
43012703Smckusick 		highuid = uid;
43112703Smckusick 	return (fup);
43212660Smckusick }
43312660Smckusick 
43412660Smckusick char *
43512703Smckusick makerawname(name)
43612703Smckusick 	char *name;
43712660Smckusick {
43812703Smckusick 	register char *cp;
43912703Smckusick 	char tmp, ch, *rindex();
44012703Smckusick 	static char rawname[MAXPATHLEN];
44112660Smckusick 
44212703Smckusick 	strcpy(rawname, name);
44324661Sserge 	cp = rindex(rawname, '/');
44424782Sserge 	if (cp == NULL)
44512703Smckusick 		return (name);
44624661Sserge 	else
44724661Sserge 		cp++;
44812703Smckusick 	for (ch = 'r'; *cp != '\0'; ) {
44912703Smckusick 		tmp = *cp;
45012703Smckusick 		*cp++ = ch;
45112703Smckusick 		ch = tmp;
45212703Smckusick 	}
45312703Smckusick 	*cp++ = ch;
45412703Smckusick 	*cp = '\0';
45512703Smckusick 	return (rawname);
45612660Smckusick }
457