1/*	$NetBSD: quotaon.c,v 1.31 2022/04/26 15:39:00 hannken Exp $	*/
2
3/*
4 * Copyright (c) 1980, 1990, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#ifndef lint
37__COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
38 The Regents of the University of California.  All rights reserved.");
39#endif /* not lint */
40
41#ifndef lint
42#if 0
43static char sccsid[] = "@(#)quotaon.c	8.1 (Berkeley) 6/6/93";
44#else
45__RCSID("$NetBSD: quotaon.c,v 1.31 2022/04/26 15:39:00 hannken Exp $");
46#endif
47#endif /* not lint */
48
49/*
50 * Turn quota on/off for a filesystem.
51 */
52#include <sys/param.h>
53#include <sys/file.h>
54#include <sys/mount.h>
55
56#include <quota.h>
57#include <ufs/ufs/quota1.h>
58
59#include <err.h>
60#include <fstab.h>
61#include <stdio.h>
62#include <errno.h>
63#include <stdlib.h>
64#include <string.h>
65#include <unistd.h>
66#include <util.h>
67
68
69static int	vflag;		/* verbose */
70
71static void usage(void) __dead;
72static int quotaonoff(struct fstab *, struct quotahandle *, int, int, int,
73    const char *);
74static int readonly(struct fstab *, const char *);
75static int oneof(const char *target, char *list[], int cnt);
76
77int
78main(int argc, char *argv[])
79{
80	struct fstab *fs;
81	struct quotahandle *qh;
82	long argnum, done = 0;
83	int i, offmode = 0, errs = 0;
84	unsigned restrictions;
85	int ch;
86
87	int aflag = 0;		/* all file systems */
88	int gflag = 0;		/* operate on group quotas */
89	int uflag = 0;		/* operate on user quotas */
90	int noguflag = 0;	/* operate on both (by default) */
91
92	if (strcmp(getprogname(), "quotaoff") == 0)
93		offmode++;
94	else if (strcmp(getprogname(), "quotaon") != 0)
95		errx(1, "Name must be quotaon or quotaoff");
96
97	while ((ch = getopt(argc, argv, "avug")) != -1) {
98		switch(ch) {
99		case 'a':
100			aflag++;
101			break;
102		case 'g':
103			gflag++;
104			break;
105		case 'u':
106			uflag++;
107			break;
108		case 'v':
109			vflag++;
110			break;
111		default:
112			usage();
113			break;
114		}
115	}
116	argc -= optind;
117	argv += optind;
118
119	if (argc <= 0 && !aflag)
120		usage();
121
122	if (!gflag && !uflag) {
123		noguflag = 1;
124	}
125
126	/*
127	 * XXX at the moment quota_open also uses getfsent(), but it
128	 * uses it only up front. To avoid conflicting with it, let it
129	 * initialize first.
130	 */
131	qh = quota_open("/");
132	if (qh != NULL) {
133		quota_close(qh);
134	}
135
136	setfsent();
137	while ((fs = getfsent()) != NULL) {
138		char buf[MAXPATHLEN];
139		const char *fsspec;
140		if ((strcmp(fs->fs_vfstype, "ffs") &&
141		     strcmp(fs->fs_vfstype, "lfs")) ||
142		    strcmp(fs->fs_type, FSTAB_RW))
143			continue;
144
145		fsspec = getfsspecname(buf, sizeof(buf), fs->fs_spec);
146		if (fsspec == NULL) {
147			warn("%s", buf);
148			continue;
149		}
150		if (!aflag) {
151			if ((argnum = oneof(fs->fs_file, argv, argc)) < 0 &&
152			    (argnum = oneof(fsspec, argv, argc)) < 0) {
153				continue;
154			}
155			done |= 1U << argnum;
156		}
157
158		qh = quota_open(fs->fs_file);
159		if (qh == NULL) {
160			if (!aflag) {
161				warn("quota_open");
162				errs++;
163			}
164			continue;
165		}
166
167		restrictions = quota_getrestrictions(qh);
168		if ((restrictions & QUOTA_RESTRICT_NEEDSQUOTACHECK) == 0) {
169			/* Not a quota v1 volume, skip it */
170			if (!aflag) {
171				errno = EBUSY;
172				warn("%s", fs->fs_file);
173				errs++;
174			}
175			quota_close(qh);
176			continue;
177		}
178
179		/*
180		 * The idea here is to warn if someone explicitly
181		 * tries to turn on group quotas and there are no
182		 * group quotas, and likewise for user quotas, but not
183		 * to warn if just doing the default thing and one of
184		 * the quota types isn't configured.
185		 */
186
187		if (noguflag) {
188			errs += quotaonoff(fs, qh, offmode, GRPQUOTA, 0, fsspec);
189			errs += quotaonoff(fs, qh, offmode, USRQUOTA, 0, fsspec);
190		}
191		if (gflag) {
192			errs += quotaonoff(fs, qh, offmode, GRPQUOTA, 1, fsspec);
193		}
194		if (uflag) {
195			errs += quotaonoff(fs, qh, offmode, USRQUOTA, 1, fsspec);
196		}
197		quota_close(qh);
198	}
199	endfsent();
200	for (i = 0; i < argc; i++)
201		if ((done & (1U << i)) == 0)
202			warnx("%s not found in fstab", argv[i]);
203	return errs;
204}
205
206static void
207usage(void)
208{
209	const char *p = getprogname();
210	(void) fprintf(stderr, "Usage: %s [-g] [-u] [-v] -a\n"
211	    "\t%s [-g] [-u] [-v] filesys ...\n", p, p);
212	exit(1);
213}
214
215static int
216quotaonoff(struct fstab *fs, struct quotahandle *qh, int offmode, int idtype,
217    int warn_on_enxio, const char *fsspec)
218{
219	const char *mode = (offmode == 1) ? "off" : "on";
220	const char *type;
221
222	if (strcmp(fs->fs_file, "/") && readonly(fs, fsspec)) {
223		return 1;
224	}
225
226	if (offmode) {
227		type = quota_idtype_getname(qh, idtype);
228		if (quota_quotaoff(qh, idtype)) {
229			if (warn_on_enxio || errno != ENXIO) {
230				warn("quota%s for %s", mode, fs->fs_file);
231			}
232			return 1;
233		}
234	} else {
235		if (quota_quotaon(qh, idtype)) {
236			if (warn_on_enxio || errno != ENXIO) {
237				warn("quota%s for %s", mode, fs->fs_file);
238			}
239			return 1;
240		}
241		type = quota_idtype_getname(qh, idtype);
242	}
243
244	if (vflag) {
245		printf("%s: %s quotas turned %s\n",
246		    fs->fs_file, type, mode);
247	}
248	return 0;
249}
250
251/*
252 * Verify file system is mounted and not readonly.
253 */
254static int
255readonly(struct fstab *fs, const char *fsspec)
256{
257	struct statvfs fsbuf;
258
259	if (statvfs(fs->fs_file, &fsbuf) < 0 ||
260	    strcmp(fsbuf.f_mntonname, fs->fs_file) ||
261	    strcmp(fsbuf.f_mntfromname, fsspec)) {
262		printf("%s: not mounted\n", fs->fs_file);
263		return 1;
264	}
265	if (fsbuf.f_flag & MNT_RDONLY) {
266		printf("%s: mounted read-only\n", fs->fs_file);
267		return 1;
268	}
269	return 0;
270}
271
272/*
273 * Check to see if target appears in list of size cnt.
274 */
275static int
276oneof(const char *target, char *list[], int cnt)
277{
278	int i;
279
280	for (i = 0; i < cnt; i++)
281		if (strcmp(target, list[i]) == 0)
282			return i;
283	return -1;
284}
285