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*24661Sserge static char sccsid[] = "@(#)quotacheck.c	5.3 (Berkeley) 09/09/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>
2912703Smckusick #include <fstab.h>
3012802Smckusick #include <pwd.h>
3112660Smckusick 
3212660Smckusick union {
3312660Smckusick 	struct	fs	sblk;
3412703Smckusick 	char	dummy[MAXBSIZE];
3512660Smckusick } un;
3612660Smckusick #define	sblock	un.sblk
3712703Smckusick 
3812703Smckusick #define	ITABSZ	256
3912660Smckusick struct	dinode	itab[ITABSZ];
4012660Smckusick struct	dinode	*dp;
4112660Smckusick long	blocks;
4212660Smckusick dev_t	dev;
4312660Smckusick 
4412802Smckusick #define LOGINNAMESIZE 8
4512703Smckusick struct fileusage {
4612802Smckusick 	struct fileusage *fu_next;
4712703Smckusick 	struct dqusage fu_usage;
4812703Smckusick 	u_short	fu_uid;
4912802Smckusick 	char fu_name[LOGINNAMESIZE + 1];
5012703Smckusick };
5112703Smckusick #define FUHASH 997
5212703Smckusick struct fileusage *fuhead[FUHASH];
5312703Smckusick struct fileusage *lookup();
5412703Smckusick struct fileusage *adduid();
5512703Smckusick int highuid;
5612660Smckusick 
5712703Smckusick int fi;
5812703Smckusick ino_t ino;
5912703Smckusick long done;
6012660Smckusick struct	passwd	*getpwent();
6112660Smckusick struct	dinode	*ginode();
6212703Smckusick char *malloc(), *makerawname();
6312660Smckusick 
6412703Smckusick int	vflag;		/* verbose */
6512703Smckusick int	aflag;		/* all file systems */
6612703Smckusick 
6712703Smckusick char *qfname = "quotas";
6812703Smckusick char quotafile[MAXPATHLEN + 1];
6912802Smckusick struct dqblk zerodqbuf;
7012703Smckusick 
7112660Smckusick main(argc, argv)
7212703Smckusick 	int argc;
7312660Smckusick 	char **argv;
7412660Smckusick {
7512703Smckusick 	register struct fstab *fs;
7612802Smckusick 	register struct fileusage *fup;
7712802Smckusick 	register struct passwd *pw;
7812703Smckusick 	int i, errs = 0;
7912660Smckusick 
8012703Smckusick again:
8112703Smckusick 	argc--, argv++;
8212703Smckusick 	if (argc > 0 && strcmp(*argv, "-v") == 0) {
8312703Smckusick 		vflag++;
8412703Smckusick 		goto again;
8512660Smckusick 	}
8612703Smckusick 	if (argc > 0 && strcmp(*argv, "-a") == 0) {
8712703Smckusick 		aflag++;
8812703Smckusick 		goto again;
8912703Smckusick 	}
9012703Smckusick 	if (argc <= 0 && !aflag) {
9112703Smckusick 		fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
9212703Smckusick 			"quotacheck [-v] -a",
9312703Smckusick 			"quotacheck [-v] filesys ...");
9412660Smckusick 		exit(1);
9512660Smckusick 	}
96*24661Sserge 
97*24661Sserge 	setpwent();
98*24661Sserge 	while ((pw = getpwent()) != 0) {
99*24661Sserge 		fup = lookup(pw->pw_uid);
100*24661Sserge 		if (fup == 0)
101*24661Sserge 			fup = adduid(pw->pw_uid);
102*24661Sserge 		strncpy(fup->fu_name, pw->pw_name,
103*24661Sserge 			sizeof(fup->fu_name));
10412802Smckusick 	}
105*24661Sserge 	endpwent();
106*24661Sserge 
10712703Smckusick 	setfsent();
10812703Smckusick 	while ((fs = getfsent()) != NULL) {
10912703Smckusick 		if (aflag &&
11012703Smckusick 		    (fs->fs_type == 0 || strcmp(fs->fs_type, "rq") != 0))
11112703Smckusick 			continue;
11212703Smckusick 		if (!aflag &&
11312703Smckusick 		    !(oneof(fs->fs_file, argv, argc) ||
11412703Smckusick 		      oneof(fs->fs_spec, argv, argc)))
11512703Smckusick 			continue;
11612703Smckusick 		(void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname);
117*24661Sserge 		errs += chkquota(fs->fs_spec, fs->fs_file, quotafile);
11812660Smckusick 	}
11912703Smckusick 	endfsent();
12012703Smckusick 	for (i = 0; i < argc; i++)
12112703Smckusick 		if ((done & (1 << i)) == 0)
12212703Smckusick 			fprintf(stderr, "%s not found in /etc/fstab\n",
12312703Smckusick 				argv[i]);
12412703Smckusick 	exit(errs);
12512703Smckusick }
12612660Smckusick 
127*24661Sserge chkquota(fsdev, fsfile, qffile)
12812703Smckusick 	char *fsdev;
129*24661Sserge 	char *fsfile;
13012703Smckusick 	char *qffile;
13112703Smckusick {
13212703Smckusick 	register struct fileusage *fup;
13312703Smckusick 	dev_t quotadev;
13412703Smckusick 	FILE *qf;
13512703Smckusick 	u_short uid;
13612703Smckusick 	int cg, i;
13712703Smckusick 	char *rawdisk;
13812703Smckusick 	struct stat statb;
13912703Smckusick 	struct dqblk dqbuf;
14021085Smckusick 	static int warned = 0;
14121085Smckusick 	extern int errno;
14212660Smckusick 
14312703Smckusick 	rawdisk = makerawname(fsdev);
14412703Smckusick 	if (vflag)
145*24661Sserge 		fprintf(stdout, "*** Check quotas for %s (%s)\n", rawdisk, fsfile);
14612703Smckusick 	fi = open(rawdisk, 0);
14712703Smckusick 	if (fi < 0) {
14812703Smckusick 		perror(rawdisk);
14912703Smckusick 		return (1);
15012660Smckusick 	}
15112703Smckusick 	qf = fopen(qffile, "r+");
15212703Smckusick 	if (qf == NULL) {
15312703Smckusick 		perror(qffile);
154*24661Sserge 		close(fi);
15512703Smckusick 		return (1);
15612660Smckusick 	}
15712703Smckusick 	if (fstat(fileno(qf), &statb) < 0) {
15812703Smckusick 		perror(qffile);
159*24661Sserge 		fclose(qf);
160*24661Sserge 		close(fi);
16112703Smckusick 		return (1);
16212703Smckusick 	}
16312703Smckusick 	quotadev = statb.st_dev;
16412703Smckusick 	if (stat(fsdev, &statb) < 0) {
16512703Smckusick 		perror(fsdev);
166*24661Sserge 		fclose(qf);
167*24661Sserge 		close(fi);
16812703Smckusick 		return (1);
16912703Smckusick 	}
17012703Smckusick 	if (quotadev != statb.st_rdev) {
17112703Smckusick 		fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
17212703Smckusick 			qffile, quotadev, fsdev, statb.st_rdev);
173*24661Sserge 		fclose(qf);
174*24661Sserge 		close(fi);
17512703Smckusick 		return (1);
17612703Smckusick 	}
17721085Smckusick 	if (quota(Q_SYNC, 0, quotadev, 0) < 0 &&
17821085Smckusick 	    errno == EINVAL && !warned && vflag) {
17921085Smckusick 		warned++;
18021085Smckusick 		fprintf(stdout,
18121085Smckusick 		    "*** Warning: Quotas are not compiled into this kernel\n");
18221085Smckusick 	}
18312660Smckusick 	sync();
18412660Smckusick 	bread(SBLOCK, (char *)&sblock, SBSIZE);
18512660Smckusick 	ino = 0;
18612660Smckusick 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
18712660Smckusick 		dp = NULL;
18812660Smckusick 		for (i = 0; i < sblock.fs_ipg; i++)
18912660Smckusick 			acct(ginode());
19012660Smckusick 	}
19112703Smckusick 	for (uid = 0; uid <= highuid; uid++) {
192*24661Sserge 		fseek(qf, (long)uid * sizeof(struct dqblk), 0);
19312703Smckusick 		i = fread(&dqbuf, sizeof(struct dqblk), 1, qf);
19412703Smckusick 		if (i == 0)
19512802Smckusick 			dqbuf = zerodqbuf;
19618415Smckusick 		fup = lookup(uid);
19718415Smckusick 		if (fup == 0) {
19823519Smckusick 			if ((dqbuf.dqb_curinodes != 0 ||
19923519Smckusick 			    dqbuf.dqb_curblocks != 0) &&
20023519Smckusick 			    !feof(qf)) {
20123519Smckusick 				dqbuf.dqb_curinodes = 0;
20223519Smckusick 				dqbuf.dqb_curblocks = 0;
203*24661Sserge 				fseek(qf, (long)uid * sizeof(struct dqblk), 0);
20423519Smckusick 				fwrite(&dqbuf, sizeof(struct dqblk), 1, qf);
20518415Smckusick 			}
20618415Smckusick 			continue;
20718415Smckusick 		}
20812703Smckusick 		if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
20912802Smckusick 		    dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
21012802Smckusick 			fup->fu_usage.du_curinodes = 0;
21112802Smckusick 			fup->fu_usage.du_curblocks = 0;
21212703Smckusick 			continue;
21312802Smckusick 		}
21412703Smckusick 		if (vflag) {
21512802Smckusick 			if (fup->fu_name[0] != '\0')
21612802Smckusick 				printf("%-10s fixed:", fup->fu_name);
21712802Smckusick 			else
21812802Smckusick 				printf("#%-9d fixed:", uid);
21912703Smckusick 			fprintf(stdout, " inodes (old %d, new %d)",
22012703Smckusick 			    dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
22112703Smckusick 			fprintf(stdout, " blocks (old %d, new %d)\n",
22212703Smckusick 			    dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
22312660Smckusick 		}
22412703Smckusick 		dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
22512703Smckusick 		dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
226*24661Sserge 		fseek(qf, (long)uid * sizeof(struct dqblk), 0);
22712703Smckusick 		fwrite(&dqbuf, sizeof(struct dqblk), 1, qf);
22812703Smckusick 		quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
22913255Smckusick 		fup->fu_usage.du_curinodes = 0;
23013255Smckusick 		fup->fu_usage.du_curblocks = 0;
23112660Smckusick 	}
232*24661Sserge 	fflush(qf);
23318415Smckusick 	ftruncate(fileno(qf), (highuid + 1) * sizeof(struct dqblk));
234*24661Sserge 	fclose(qf);
235*24661Sserge 	close(fi);
23612703Smckusick 	return (0);
23712660Smckusick }
23812660Smckusick 
23912660Smckusick acct(ip)
24012660Smckusick 	register struct dinode *ip;
24112660Smckusick {
24212660Smckusick 	register n;
24312703Smckusick 	register struct fileusage *fup;
24412660Smckusick 
24512660Smckusick 	if (ip == NULL)
24612660Smckusick 		return;
24712660Smckusick 	if (ip->di_mode == 0)
24812660Smckusick 		return;
24912703Smckusick 	fup = lookup(ip->di_uid);
25012703Smckusick 	if (fup == 0)
25112703Smckusick 		fup = adduid(ip->di_uid);
25212703Smckusick 	fup->fu_usage.du_curinodes++;
25312660Smckusick 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
25412660Smckusick 		return;
25512703Smckusick 	fup->fu_usage.du_curblocks += ip->di_blocks;
25612660Smckusick }
25712660Smckusick 
25812703Smckusick oneof(target, list, n)
25912703Smckusick 	char *target, *list[];
26012703Smckusick 	register int n;
26112660Smckusick {
26212703Smckusick 	register int i;
26312660Smckusick 
26412703Smckusick 	for (i = 0; i < n; i++)
26512703Smckusick 		if (strcmp(target, list[i]) == 0) {
26612703Smckusick 			done |= 1 << i;
26712703Smckusick 			return (1);
26812703Smckusick 		}
26912703Smckusick 	return (0);
27012660Smckusick }
27112660Smckusick 
27212660Smckusick struct dinode *
27312660Smckusick ginode()
27412660Smckusick {
27512660Smckusick 	register unsigned long iblk;
27612660Smckusick 
27712660Smckusick 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
27812660Smckusick 		iblk = itod(&sblock, ino);
27912660Smckusick 		bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
28012660Smckusick 		dp = &itab[ino % INOPB(&sblock)];
28112660Smckusick 	}
28212660Smckusick 	if (ino++ < ROOTINO)
28312660Smckusick 		return(NULL);
28412660Smckusick 	return(dp);
28512660Smckusick }
28612660Smckusick 
28712660Smckusick bread(bno, buf, cnt)
28812660Smckusick 	long unsigned bno;
28912660Smckusick 	char *buf;
29012660Smckusick {
29112660Smckusick 
29212703Smckusick 	lseek(fi, (long)dbtob(bno), 0);
29312660Smckusick 	if (read(fi, buf, cnt) != cnt) {
29412660Smckusick 		printf("read error %u\n", bno);
29512660Smckusick 		exit(1);
29612660Smckusick 	}
29712660Smckusick }
29812660Smckusick 
29912703Smckusick struct fileusage *
30012703Smckusick lookup(uid)
30112703Smckusick 	u_short uid;
30212660Smckusick {
30312703Smckusick 	register struct fileusage *fup;
30412660Smckusick 
30512703Smckusick 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
30612703Smckusick 		if (fup->fu_uid == uid)
30712703Smckusick 			return (fup);
30812703Smckusick 	return ((struct fileusage *)0);
30912660Smckusick }
31012660Smckusick 
31112703Smckusick struct fileusage *
31212703Smckusick adduid(uid)
31312703Smckusick 	u_short uid;
31412660Smckusick {
31512703Smckusick 	struct fileusage *fup, **fhp;
316*24661Sserge 	extern char *calloc();
31712660Smckusick 
31812703Smckusick 	fup = lookup(uid);
31912703Smckusick 	if (fup != 0)
32012703Smckusick 		return (fup);
32112703Smckusick 	fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
32212703Smckusick 	if (fup == 0) {
32312703Smckusick 		fprintf(stderr, "out of memory for fileusage structures\n");
32412703Smckusick 		exit(1);
32512703Smckusick 	}
32612703Smckusick 	fhp = &fuhead[uid % FUHASH];
32712703Smckusick 	fup->fu_next = *fhp;
32812703Smckusick 	*fhp = fup;
32912703Smckusick 	fup->fu_uid = uid;
33012703Smckusick 	if (uid > highuid)
33112703Smckusick 		highuid = uid;
33212703Smckusick 	return (fup);
33312660Smckusick }
33412660Smckusick 
33512660Smckusick char *
33612703Smckusick makerawname(name)
33712703Smckusick 	char *name;
33812660Smckusick {
33912703Smckusick 	register char *cp;
34012703Smckusick 	char tmp, ch, *rindex();
34112703Smckusick 	static char rawname[MAXPATHLEN];
34212660Smckusick 
34312703Smckusick 	strcpy(rawname, name);
344*24661Sserge 	cp = rindex(rawname, '/');
345*24661Sserge 	if (cp == NULL || cp[1] == 'r')
34612703Smckusick 		return (name);
347*24661Sserge 	else
348*24661Sserge 		cp++;
34912703Smckusick 	for (ch = 'r'; *cp != '\0'; ) {
35012703Smckusick 		tmp = *cp;
35112703Smckusick 		*cp++ = ch;
35212703Smckusick 		ch = tmp;
35312703Smckusick 	}
35412703Smckusick 	*cp++ = ch;
35512703Smckusick 	*cp = '\0';
35612703Smckusick 	return (rawname);
35712660Smckusick }
358