1/*
2 * Copyright (c) 1980, 1990, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Robert Elz at The University of Melbourne.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*
34 * Turn quota on/off for a filesystem.
35 */
36#include <sys/types.h>
37#include <sys/mount.h>
38#include <ufs/ufs/quota.h>
39
40#include <err.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <fstab.h>
45#include <unistd.h>
46
47char *qfname = QUOTAFILENAME;
48char *qfextension[] = INITQFNAMES;
49
50int	aflag;		/* all file systems */
51int	gflag;		/* operate on group quotas */
52int	uflag;		/* operate on user quotas */
53int	vflag;		/* verbose */
54
55void	usage(char *whoami);
56int	hasquota(struct fstab *fs, int type, char **qfnamep, int force);
57int	quotaonoff(struct fstab *fs, int offmode, int type, char *qfpathname);
58int	oneof(char *target, char *list[], int cnt);
59int	readonly(struct fstab *fs);
60
61
62int
63main(int argc, char *argv[])
64{
65	struct fstab *fs;
66	char *qfnp, *whoami;
67	long argnum, done = 0;
68	int i, offmode = 0, errs = 0;
69	int ch;
70
71	whoami = strrchr(*argv, '/') + 1;
72	if (whoami == (char *)1)
73		whoami = *argv;
74	if (strcmp(whoami, "quotaoff") == 0)
75		offmode = 1;
76	else if (strcmp(whoami, "quotaon") != 0) {
77		fprintf(stderr, "Name must be quotaon or quotaoff not %s\n",
78			whoami);
79		exit(1);
80	}
81	while ((ch = getopt(argc, argv, "avug")) != -1) {
82		switch (ch) {
83		case 'a':
84			aflag = 1;
85			break;
86		case 'g':
87			gflag = 1;
88			break;
89		case 'u':
90			uflag = 1;
91			break;
92		case 'v':
93			vflag = 1;
94			break;
95		default:
96			usage(whoami);
97		}
98	}
99	argc -= optind;
100	argv += optind;
101	if (argc <= 0 && !aflag)
102		usage(whoami);
103	if (!gflag && !uflag) {
104		gflag = 1;
105		uflag = 1;
106	}
107	setfsent();
108	while ((fs = getfsent()) != NULL) {
109		if (strcmp(fs->fs_type, FSTAB_RW))
110			continue;
111		if (strcmp(fs->fs_vfstype, "ffs") &&
112		    strcmp(fs->fs_vfstype, "ufs") &&
113		    strcmp(fs->fs_vfstype, "mfs"))
114			continue;
115		if (aflag) {
116			if (gflag && hasquota(fs, GRPQUOTA, &qfnp, 0))
117				errs += quotaonoff(fs, offmode, GRPQUOTA, qfnp);
118			if (uflag && hasquota(fs, USRQUOTA, &qfnp, 0))
119				errs += quotaonoff(fs, offmode, USRQUOTA, qfnp);
120			continue;
121		}
122		if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
123		    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) {
124			done |= 1 << argnum;
125			if (gflag) {
126				hasquota(fs, GRPQUOTA, &qfnp, 1);
127				errs += quotaonoff(fs, offmode, GRPQUOTA, qfnp);
128			}
129			if (uflag) {
130				hasquota(fs, USRQUOTA, &qfnp, 1);
131				errs += quotaonoff(fs, offmode, USRQUOTA, qfnp);
132			}
133		}
134	}
135	endfsent();
136	for (i = 0; i < argc; i++)
137		if ((done & (1 << i)) == 0)
138			fprintf(stderr, "%s not found in fstab\n",
139				argv[i]);
140	exit(errs);
141}
142
143void
144usage(char *whoami)
145{
146
147	fprintf(stderr, "usage: %s [-aguv] filesystem ...\n", whoami);
148	exit(1);
149}
150
151int
152quotaonoff(struct fstab *fs, int offmode, int type, char *qfpathname)
153{
154	if (strcmp(fs->fs_file, "/") && readonly(fs))
155		return (1);
156	if (offmode) {
157		if (quotactl(fs->fs_file, QCMD(Q_QUOTAOFF, type), 0, 0) < 0) {
158			fprintf(stderr, "quotaoff: ");
159			perror(fs->fs_file);
160			return (1);
161		}
162		if (vflag)
163			printf("%s: %s quotas turned off\n", fs->fs_file,
164			    qfextension[type]);
165		return (0);
166	}
167	if (quotactl(fs->fs_file, QCMD(Q_QUOTAON, type), 0, qfpathname) < 0) {
168		warn("%s: %s quotas using %s", fs->fs_file,
169		    qfextension[type], qfpathname);
170		return (1);
171	}
172	if (vflag)
173		printf("%s: %s quotas turned on\n", fs->fs_file,
174		    qfextension[type]);
175	return (0);
176}
177
178/*
179 * Check to see if target appears in list of size cnt.
180 */
181int
182oneof(char *target, char *list[], int cnt)
183{
184	int i;
185
186	for (i = 0; i < cnt; i++)
187		if (strcmp(target, list[i]) == 0)
188			return (i);
189	return (-1);
190}
191
192/*
193 * Check to see if a particular quota is to be enabled.
194 */
195int
196hasquota(struct fstab *fs, int type, char **qfnamep, int force)
197{
198	char *opt;
199	char *cp;
200	static char initname, usrname[100], grpname[100];
201	static char buf[BUFSIZ];
202
203	if (!initname) {
204		snprintf(usrname, sizeof usrname, "%s%s",
205		    qfextension[USRQUOTA], qfname);
206		snprintf(grpname, sizeof grpname, "%s%s",
207		    qfextension[GRPQUOTA], qfname);
208		initname = 1;
209	}
210	strlcpy(buf, fs->fs_mntops, sizeof buf);
211	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
212		if ((cp = strchr(opt, '=')) != NULL)
213			*cp++ = '\0';
214		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
215			break;
216		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
217			break;
218	}
219	if (!force && !opt)
220		return (0);
221	if (cp) {
222		*qfnamep = cp;
223		return (1);
224	}
225	(void) snprintf(buf, sizeof buf, "%s/%s.%s", fs->fs_file,
226	    qfname, qfextension[type]);
227	*qfnamep = buf;
228	return (1);
229}
230
231/*
232 * Verify file system is mounted and not readonly.
233 * MFS is special -- it puts "mfs:" in the kernel's mount table
234 */
235int
236readonly(struct fstab *fs)
237{
238	struct statfs fsbuf;
239
240	if (statfs(fs->fs_file, &fsbuf) < 0 ||
241	    strcmp(fsbuf.f_mntonname, fs->fs_file) ||
242	    strcmp(fsbuf.f_mntfromname, fs->fs_spec)) {
243		if (strcmp(fs->fs_file, "mfs") ||
244		    memcmp(fsbuf.f_mntfromname, "mfs:", sizeof("mfs:")-1))
245			;
246		else {
247			printf("%s: not mounted\n", fs->fs_file);
248			return (1);
249		}
250	}
251	if (fsbuf.f_flags & MNT_RDONLY) {
252		printf("%s: mounted read-only\n", fs->fs_file);
253		return (1);
254	}
255	return (0);
256}
257