xref: /csrg-svn/usr.sbin/edquota/edquota.c (revision 38516)
121516Smckusick /*
221516Smckusick  * Copyright (c) 1980 Regents of the University of California.
334362Sbostic  * All rights reserved.
434362Sbostic  *
534362Sbostic  * Redistribution and use in source and binary forms are permitted
634777Sbostic  * provided that the above copyright notice and this paragraph are
734777Sbostic  * duplicated in all such forms and that any documentation,
834777Sbostic  * advertising materials, and other materials related to such
934777Sbostic  * distribution and use acknowledge that the software was developed
1034777Sbostic  * by the University of California, Berkeley.  The name of the
1134777Sbostic  * University may not be used to endorse or promote products derived
1234777Sbostic  * from this software without specific prior written permission.
1334777Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434777Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534777Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621516Smckusick  */
1721516Smckusick 
1812707Smckusick #ifndef lint
1921516Smckusick char copyright[] =
2021516Smckusick "@(#) Copyright (c) 1980 Regents of the University of California.\n\
2121516Smckusick  All rights reserved.\n";
2234362Sbostic #endif /* not lint */
2312707Smckusick 
2421516Smckusick #ifndef lint
25*38516Sbostic static char sccsid[] = "@(#)edquota.c	5.10 (Berkeley) 07/30/89";
2634362Sbostic #endif /* not lint */
2721516Smckusick 
2812707Smckusick /*
2912707Smckusick  * Disk quota editor.
3012707Smckusick  */
3136964Sbostic #include <sys/param.h>
3236964Sbostic #include <sys/stat.h>
3336964Sbostic #include <sys/file.h>
34*38516Sbostic #include <sys/signal.h>
35*38516Sbostic #include <ufs/quota.h>
3612707Smckusick #include <errno.h>
3736964Sbostic #include <fstab.h>
3812707Smckusick #include <pwd.h>
3912707Smckusick #include <ctype.h>
4036964Sbostic #include <stdio.h>
4137267Sbostic #include "pathnames.h"
4212707Smckusick 
4312707Smckusick struct	dquot dq[NMOUNT];
4412707Smckusick struct	dquot odq[NMOUNT];
4512707Smckusick char	dqf[NMOUNT][MAXPATHLEN + 1];
4612707Smckusick char	odqf[NMOUNT][MAXPATHLEN + 1];
4712707Smckusick 
4837976Sbostic char	tmpfil[] = _PATH_TMP;
4912716Smckusick char	*qfname = "quotas";
5012707Smckusick char	*getenv();
5112707Smckusick 
5212707Smckusick main(argc, argv)
5312707Smckusick 	char **argv;
5412707Smckusick {
5512803Smckusick 	int uid;
5612803Smckusick 	char *arg0;
5712707Smckusick 
5812707Smckusick 	mktemp(tmpfil);
5912707Smckusick 	close(creat(tmpfil, 0600));
6012707Smckusick 	chown(tmpfil, getuid(), getgid());
6112707Smckusick 	arg0 = *argv++;
6212707Smckusick 	if (argc < 2) {
6312803Smckusick 		fprintf(stderr, "Usage: %s [-p username] username ...\n", arg0);
6412707Smckusick 		unlink(tmpfil);
6512707Smckusick 		exit(1);
6612707Smckusick 	}
6712707Smckusick 	--argc;
6812707Smckusick 	if (getuid()) {
6912707Smckusick 		fprintf(stderr, "%s: permission denied\n", arg0);
7012707Smckusick 		unlink(tmpfil);
7112707Smckusick 		exit(1);
7212707Smckusick 	}
7312803Smckusick 	if (argc > 2 && strcmp(*argv, "-p") == 0) {
7412803Smckusick 		argc--, argv++;
7512803Smckusick 		uid = getentry(*argv++);
7612803Smckusick 		if (uid < 0) {
7712803Smckusick 			unlink(tmpfil);
7812803Smckusick 			exit(1);
7912803Smckusick 		}
8012803Smckusick 		getprivs(uid);
8112803Smckusick 		argc--;
8212803Smckusick 		while (argc-- > 0) {
8312803Smckusick 			uid = getentry(*argv++);
8412803Smckusick 			if (uid < 0)
8512803Smckusick 				continue;
8612803Smckusick 			getdiscq(uid, odq, odqf);
8712803Smckusick 			putprivs(uid);
8812803Smckusick 		}
8912803Smckusick 		unlink(tmpfil);
9012803Smckusick 		exit(0);
9112803Smckusick 	}
9212803Smckusick 	while (--argc >= 0) {
9312803Smckusick 		uid = getentry(*argv++);
9412803Smckusick 		if (uid < 0)
9512803Smckusick 			continue;
9612803Smckusick 		getprivs(uid);
9712803Smckusick 		if (editit())
9812803Smckusick 			putprivs(uid);
9912803Smckusick 	}
10012707Smckusick 	unlink(tmpfil);
10112707Smckusick 	exit(0);
10212707Smckusick }
10312707Smckusick 
10412803Smckusick getentry(name)
10512803Smckusick 	char *name;
10612707Smckusick {
10712803Smckusick 	struct passwd *pw;
10812803Smckusick 	int uid;
10912707Smckusick 
11012707Smckusick 	if (alldigits(name))
11112707Smckusick 		uid = atoi(name);
11212707Smckusick 	else if (pw = getpwnam(name))
11312707Smckusick 		uid = pw->pw_uid;
11412707Smckusick 	else {
11512803Smckusick 		fprintf(stderr, "%s: no such user\n", name);
11612707Smckusick 		sleep(1);
11712803Smckusick 		return (-1);
11812707Smckusick 	}
11912803Smckusick 	return (uid);
12012707Smckusick }
12112707Smckusick 
12212707Smckusick editit()
12312707Smckusick {
12433609Sbostic 	register int pid, xpid;
12533609Sbostic 	long omask;
12633609Sbostic 	int stat;
12712707Smckusick 
12833609Sbostic 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
12912707Smckusick  top:
13012707Smckusick 	if ((pid = fork()) < 0) {
13112707Smckusick 		extern errno;
13212707Smckusick 
13312707Smckusick 		if (errno == EPROCLIM) {
13412707Smckusick 			fprintf(stderr, "You have too many processes\n");
13512707Smckusick 			return(0);
13612707Smckusick 		}
13712707Smckusick 		if (errno == EAGAIN) {
13812707Smckusick 			sleep(1);
13912707Smckusick 			goto top;
14012707Smckusick 		}
14112707Smckusick 		perror("fork");
14212707Smckusick 		return (0);
14312707Smckusick 	}
14412707Smckusick 	if (pid == 0) {
14512707Smckusick 		register char *ed;
14612707Smckusick 
14713025Ssam 		sigsetmask(omask);
14812707Smckusick 		setgid(getgid());
14912707Smckusick 		setuid(getuid());
15012707Smckusick 		if ((ed = getenv("EDITOR")) == (char *)0)
15137976Sbostic 			ed = _PATH_VI;
15212707Smckusick 		execlp(ed, ed, tmpfil, 0);
15312707Smckusick 		perror(ed);
15412707Smckusick 		exit(1);
15512707Smckusick 	}
15612707Smckusick 	while ((xpid = wait(&stat)) >= 0)
15712707Smckusick 		if (xpid == pid)
15812707Smckusick 			break;
15913025Ssam 	sigsetmask(omask);
16012707Smckusick 	return (!stat);
16112707Smckusick }
16212707Smckusick 
16312707Smckusick getprivs(uid)
16412707Smckusick 	register uid;
16512707Smckusick {
16612707Smckusick 	register i;
16712707Smckusick 	FILE *fd;
16812707Smckusick 
16912707Smckusick 	getdiscq(uid, dq, dqf);
17012707Smckusick 	for (i = 0; i < NMOUNT; i++) {
17112707Smckusick 		odq[i] = dq[i];
17212707Smckusick 		strcpy(odqf[i], dqf[i]);
17312707Smckusick 	}
17412707Smckusick 	if ((fd = fopen(tmpfil, "w")) == NULL) {
17512707Smckusick 		fprintf(stderr, "edquota: ");
17612707Smckusick 		perror(tmpfil);
17712707Smckusick 		exit(1);
17812707Smckusick 	}
17912707Smckusick 	for (i = 0; i < NMOUNT; i++) {
18012707Smckusick 		if (*dqf[i] == '\0')
18112707Smckusick 			continue;
18212707Smckusick 		fprintf(fd,
18312707Smckusick "fs %s blocks (soft = %d, hard = %d) inodes (soft = %d, hard = %d)\n"
18412707Smckusick 			, dqf[i]
18525401Sbloom 			, dbtob(dq[i].dq_bsoftlimit) / 1024
18625401Sbloom 			, dbtob(dq[i].dq_bhardlimit) / 1024
18712707Smckusick 			, dq[i].dq_isoftlimit
18812707Smckusick 			, dq[i].dq_ihardlimit
18912707Smckusick 		);
19012707Smckusick 	}
19112707Smckusick 	fclose(fd);
19212707Smckusick }
19312707Smckusick 
19412707Smckusick putprivs(uid)
19512707Smckusick 	register uid;
19612707Smckusick {
19712707Smckusick 	register i, j;
19812707Smckusick 	int n;
19912707Smckusick 	FILE *fd;
20012707Smckusick 	char line[BUFSIZ];
20112707Smckusick 
20212707Smckusick 	fd = fopen(tmpfil, "r");
20312707Smckusick 	if (fd == NULL) {
20412707Smckusick 		fprintf(stderr, "Can't re-read temp file!!\n");
20512707Smckusick 		return;
20612707Smckusick 	}
20712707Smckusick 	for (i = 0; i < NMOUNT; i++) {
20812707Smckusick 		char *cp, *dp, *next();
20912707Smckusick 
21012707Smckusick 		if (fgets(line, sizeof (line), fd) == NULL)
21112707Smckusick 			break;
21212707Smckusick 		cp = next(line, " \t");
21312707Smckusick 		if (cp == NULL)
21412707Smckusick 			break;
21512707Smckusick 		*cp++ = '\0';
21612707Smckusick 		while (*cp && *cp == '\t' && *cp == ' ')
21712707Smckusick 			cp++;
21812707Smckusick 		dp = cp, cp = next(cp, " \t");
21912707Smckusick 		if (cp == NULL)
22012707Smckusick 			break;
22112707Smckusick 		*cp++ = '\0';
22212707Smckusick 		while (*cp && *cp == '\t' && *cp == ' ')
22312707Smckusick 			cp++;
22412707Smckusick 		strcpy(dqf[i], dp);
22512707Smckusick 		n = sscanf(cp,
22612707Smckusick "blocks (soft = %d, hard = %d) inodes (soft = %hd, hard = %hd)\n"
22712707Smckusick 			, &dq[i].dq_bsoftlimit
22812707Smckusick 			, &dq[i].dq_bhardlimit
22912707Smckusick 			, &dq[i].dq_isoftlimit
23012707Smckusick 			, &dq[i].dq_ihardlimit
23112707Smckusick 		);
23212716Smckusick 		if (n != 4) {
23312716Smckusick 			fprintf(stderr, "%s: bad format\n", cp);
23412716Smckusick 			continue;
23512716Smckusick 		}
23625401Sbloom 		dq[i].dq_bsoftlimit = btodb(dq[i].dq_bsoftlimit * 1024);
23725401Sbloom 		dq[i].dq_bhardlimit = btodb(dq[i].dq_bhardlimit * 1024);
23812707Smckusick 	}
23912707Smckusick 	fclose(fd);
24012707Smckusick 	n = i;
24112707Smckusick 	for (i = 0; i < n; i++) {
24212707Smckusick 		if (*dqf[i] == '\0')
24312707Smckusick 			break;
24412707Smckusick 		for (j = 0; j < NMOUNT; j++) {
24512707Smckusick 			if (strcmp(dqf[i], odqf[j]) == 0)
24612707Smckusick 				break;
24712707Smckusick 		}
24812707Smckusick 		if (j >= NMOUNT)
24912707Smckusick 			continue;
25012707Smckusick 		*odqf[j] = '\0';
25112707Smckusick 		/*
25212707Smckusick 		 * This isn't really good enough, it is quite likely
25312707Smckusick 		 * to have changed while we have been away editing,
25412707Smckusick 		 * but it's not important enough to worry about at
25512707Smckusick 		 * the minute.
25612707Smckusick 		 */
25712707Smckusick 		dq[i].dq_curblocks = odq[j].dq_curblocks;
25812707Smckusick 		dq[i].dq_curinodes = odq[j].dq_curinodes;
25912707Smckusick 		/*
26012707Smckusick 		 * If we've upped the inode or disk block limits
26112707Smckusick 		 * and the guy is out of warnings, reinitialize.
26212707Smckusick 		 */
26312707Smckusick 		if (dq[i].dq_bsoftlimit > odq[j].dq_bsoftlimit &&
26412707Smckusick 		    dq[i].dq_bwarn == 0)
26512707Smckusick 			dq[i].dq_bwarn = MAX_DQ_WARN;
26612707Smckusick 		if (dq[i].dq_isoftlimit > odq[j].dq_isoftlimit &&
26712707Smckusick 		    dq[i].dq_iwarn == 0)
26812707Smckusick 			dq[i].dq_iwarn = MAX_IQ_WARN;
26912707Smckusick 	}
27012707Smckusick 	if (i < NMOUNT) {
27112707Smckusick 		for (j = 0; j < NMOUNT; j++) {
27212707Smckusick 			if (*odqf[j] == '\0')
27312707Smckusick 				continue;
27412707Smckusick 			strcpy(dqf[i], odqf[j]);
27512707Smckusick 			dq[i].dq_isoftlimit = 0;
27612707Smckusick 			dq[i].dq_ihardlimit = 0;
27712707Smckusick 			dq[i].dq_bsoftlimit = 0;
27812707Smckusick 			dq[i].dq_bhardlimit = 0;
27912707Smckusick 			/*
28012707Smckusick 			 * Same applies as just above
28112707Smckusick 			 * but matters not at all, as we are just
28212707Smckusick 			 * turning quota'ing off for this filesys.
28312707Smckusick 			 */
28412707Smckusick 			dq[i].dq_curblocks = odq[j].dq_curblocks;
28512707Smckusick 			dq[i].dq_curinodes = odq[j].dq_curinodes;
28612707Smckusick 			if (++i >= NMOUNT)
28712707Smckusick 				break;
28812707Smckusick 		}
28912707Smckusick 	}
29012707Smckusick 	if (*dqf[0])
29112707Smckusick 		putdiscq(uid, dq, dqf);
29212707Smckusick }
29312707Smckusick 
29412707Smckusick char *
29512707Smckusick next(cp, match)
29612707Smckusick 	register char *cp;
29712707Smckusick 	char *match;
29812707Smckusick {
29912707Smckusick 	register char *dp;
30012707Smckusick 
30112707Smckusick 	while (cp && *cp) {
30212707Smckusick 		for (dp = match; dp && *dp; dp++)
30312707Smckusick 			if (*dp == *cp)
30412707Smckusick 				return (cp);
30512707Smckusick 		cp++;
30612707Smckusick 	}
30712707Smckusick 	return ((char *)0);
30812707Smckusick }
30912707Smckusick 
31012707Smckusick alldigits(s)
31112707Smckusick 	register char *s;
31212707Smckusick {
31312707Smckusick 	register c;
31412707Smckusick 
31512707Smckusick 	c = *s++;
31612707Smckusick 	do {
31712707Smckusick 		if (!isdigit(c))
31812707Smckusick 			return (0);
31912707Smckusick 	} while (c = *s++);
32012707Smckusick 	return (1);
32112707Smckusick }
32212707Smckusick 
32312707Smckusick getdiscq(uid, dq, dqf)
32412707Smckusick 	register uid;
32512707Smckusick 	register struct dquot *dq;
32612707Smckusick 	register char (*dqf)[MAXPATHLEN + 1];
32712707Smckusick {
32812707Smckusick 	register struct fstab *fs;
32912716Smckusick 	char qfilename[MAXPATHLEN + 1];
33021087Smckusick 	struct stat statb;
33121087Smckusick 	struct dqblk dqblk;
33221087Smckusick 	dev_t fsdev;
33321087Smckusick 	int fd;
33421087Smckusick 	static int warned = 0;
33521087Smckusick 	extern int errno;
33612707Smckusick 
33712707Smckusick 	setfsent();
33812707Smckusick 	while (fs = getfsent()) {
33912707Smckusick 		if (stat(fs->fs_spec, &statb) < 0)
34012707Smckusick 			continue;
34112707Smckusick 		fsdev = statb.st_rdev;
34212716Smckusick 		sprintf(qfilename, "%s/%s", fs->fs_file, qfname);
34312716Smckusick 		if (stat(qfilename, &statb) < 0 || statb.st_dev != fsdev)
34412707Smckusick 			continue;
34512707Smckusick 		if (quota(Q_GETDLIM, uid, fsdev, &dqblk) != 0) {
34621087Smckusick 	    		if (errno == EINVAL && !warned) {
34721087Smckusick 				warned++;
34821087Smckusick 				fprintf(stderr, "Warning: %s\n",
34921087Smckusick 				    "Quotas are not compiled into this kernel");
35021087Smckusick 				sleep(3);
35121087Smckusick 			}
35221087Smckusick 			fd = open(qfilename, O_RDONLY);
35312707Smckusick 			if (fd < 0)
35412707Smckusick 				continue;
35512722Smckusick 			lseek(fd, (long)(uid * sizeof dqblk), L_SET);
35624659Sserge 			switch (read(fd, &dqblk, sizeof dqblk)) {
35724659Sserge 			case 0:			/* EOF */
35824659Sserge 				/*
35924659Sserge 				 * Convert implicit 0 quota (EOF)
36024659Sserge 				 * into an explicit one (zero'ed dqblk)
36124659Sserge 				 */
36224659Sserge 				bzero((caddr_t)&dqblk, sizeof dqblk);
36324659Sserge 				break;
36424659Sserge 
36524659Sserge 			case sizeof dqblk:	/* OK */
36624659Sserge 				break;
36724659Sserge 
36824659Sserge 			default:		/* ERROR */
36924659Sserge 				fprintf(stderr, "edquota: read error in ");
37024659Sserge 				perror(qfilename);
37112707Smckusick 				close(fd);
37212707Smckusick 				continue;
37312707Smckusick 			}
37412707Smckusick 			close(fd);
37512707Smckusick 		}
37612707Smckusick 		dq->dq_dqb = dqblk;
37712707Smckusick 		dq->dq_dev = fsdev;
37812707Smckusick 		strcpy(*dqf, fs->fs_file);
37912707Smckusick 		dq++, dqf++;
38012707Smckusick 	}
38112707Smckusick 	endfsent();
38212707Smckusick 	**dqf = '\0';
38312707Smckusick }
38412707Smckusick 
38512707Smckusick putdiscq(uid, dq, dqf)
38612707Smckusick 	register uid;
38712707Smckusick 	register struct dquot *dq;
38812707Smckusick 	register char (*dqf)[MAXPATHLEN + 1];
38912707Smckusick {
39012707Smckusick 	register fd, cnt;
39112707Smckusick 	struct stat sb;
39212707Smckusick 	struct fstab *fs;
39312707Smckusick 
39412707Smckusick 	cnt = 0;
39512707Smckusick 	for (cnt = 0; ++cnt <= NMOUNT && **dqf; dq++, dqf++) {
39612707Smckusick 		fs = getfsfile(*dqf);
39712716Smckusick 		if (fs == NULL) {
39837976Sbostic 			fprintf(stderr, "%s: not in fstab\n", *dqf);
39912716Smckusick 			continue;
40012716Smckusick 		}
40112716Smckusick 		strcat(*dqf, "/");
40212716Smckusick 		strcat(*dqf, qfname);
40312716Smckusick 		if (stat(*dqf, &sb) >= 0)
40412716Smckusick 			quota(Q_SETDLIM, uid, sb.st_dev, &dq->dq_dqb);
40512716Smckusick 		if ((fd = open(*dqf, 1)) < 0) {
40612716Smckusick 			perror(*dqf);
40712716Smckusick 		} else {
40812707Smckusick 			lseek(fd, (long)uid * (long)sizeof (struct dqblk), 0);
40912707Smckusick 			if (write(fd, &dq->dq_dqb, sizeof (struct dqblk)) !=
41012707Smckusick 			    sizeof (struct dqblk)) {
41112707Smckusick 				fprintf(stderr, "edquota: ");
41212707Smckusick 				perror(*dqf);
41312707Smckusick 			}
41412707Smckusick 			close(fd);
41512707Smckusick 		}
41612707Smckusick 	}
41712707Smckusick }
418