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
43 static 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 
69 static int          vflag;              /* verbose */
70 
71 static void usage(void) __dead;
72 static int quotaonoff(struct fstab *, struct quotahandle *, int, int, int,
73     const char *);
74 static int readonly(struct fstab *, const char *);
75 static int oneof(const char *target, char *list[], int cnt);
76 
77 int
main(int argc,char * argv[])78 main(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 
206 static void
usage(void)207 usage(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 
215 static int
quotaonoff(struct fstab * fs,struct quotahandle * qh,int offmode,int idtype,int warn_on_enxio,const char * fsspec)216 quotaonoff(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  */
254 static int
readonly(struct fstab * fs,const char * fsspec)255 readonly(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  */
275 static int
oneof(const char * target,char * list[],int cnt)276 oneof(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