Update to 4.01.
[profile/ivi/quota.git] / quotaon.c
1 /*
2  * Copyright (c) 1980, 1990 Regents of the University of California. All
3  * rights reserved.
4  * 
5  * This code is derived from software contributed to Berkeley by Robert Elz at
6  * 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 are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution. 3. All advertising
15  * materials mentioning features or use of this software must display the
16  * following acknowledgement: This product includes software developed by the
17  * University of California, Berkeley and its contributors. 4. Neither the
18  * name of the University nor the names of its contributors may be used to
19  * endorse or promote products derived from this software without specific
20  * prior written permission.
21  * 
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
26  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * 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 "config.h"
36
37 /*
38  * Turn quota on/off for a filesystem.
39  */
40 #include <stdio.h>
41 #include <errno.h>
42 #include <getopt.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46
47 #include "quotaon.h"
48 #include "quota.h"
49 #include "quotasys.h"
50
51 #define FL_USER 1
52 #define FL_GROUP 2
53 #define FL_VERBOSE 4
54 #define FL_ALL 8
55 #define FL_STAT 16
56 #define FL_OFF 32
57
58 static int flags, fmt = -1;
59 char *progname;
60 static char **mntpoints;
61 static int mntcnt;
62 static char *xarg = NULL;
63
64 static void usage(void)
65 {
66         errstr(_("Usage:\n\t%s [-guvp] [-F quotaformat] [-x state] -a\n\
67 \t%s [-guvp] [-F quotaformat] [-x state] filesys ...\n\n\
68 -a, --all                %s\n\
69 -f, --off                turn quotas off\n\
70 -u, --user               operate on user quotas\n\
71 -g, --group              operate on group quotas\n\
72 -p, --print-state        print whether quotas are on or off\n\
73 -x, --xfs-command=cmd    perform XFS quota command\n\
74 -F, --format=formatname  operate on specific quota format\n\
75 -v, --verbose            print more messages\n\
76 -h, --help               display this help text and exit\n\
77 -V, --version            display version information and exit\n"),
78  progname, progname,
79  strcmp(progname, "quotaon") ? _("turn quotas off for all filesystems") :
80                                _("turn quotas on for all filesystems"));
81         exit(1);
82 }
83
84 static void parse_options(int argcnt, char **argstr)
85 {
86         int c;
87         struct option long_opts[] = {
88                 { "all", 0, NULL, 'a' },
89                 { "off", 0, NULL, 'f' },
90                 { "verbose", 0, NULL, 'v' },
91                 { "user", 0, NULL, 'u' },
92                 { "group", 0, NULL, 'g' },
93                 { "print-state", 0, NULL, 'p' },
94                 { "xfs-command", 1, NULL, 'x' },
95                 { "format", 1, NULL, 'F' },
96                 { "version", 0, NULL, 'V' },
97                 { "help", 0, NULL, 'h' },
98                 { NULL, 0, NULL, 0 }
99         };
100
101         while ((c = getopt_long(argcnt, argstr, "afvugpx:VF:h", long_opts, NULL)) != -1) {
102                 switch (c) {
103                   case 'a':
104                           flags |= FL_ALL;
105                           break;
106                   case 'f':
107                           flags |= FL_OFF;
108                           break;
109                   case 'g':
110                           flags |= FL_GROUP;
111                           break;
112                   case 'u':
113                           flags |= FL_USER;
114                           break;
115                   case 'v':
116                           flags |= FL_VERBOSE;
117                           break;
118                   case 'x':
119                           xarg = optarg;
120                           break;
121                   case 'p':
122                           flags |= FL_STAT;
123                           break;
124                   case 'F':
125                           if ((fmt = name2fmt(optarg)) == QF_ERROR)
126                                 exit(1);
127                           break;
128                   case 'V':
129                           version();
130                           exit(0);
131                   case 'h':
132                   default:
133                           usage();
134                 }
135         }
136         if ((flags & FL_ALL && optind != argcnt) || (!(flags & FL_ALL) && optind == argcnt)) {
137                 fputs(_("Bad number of arguments.\n"), stderr);
138                 usage();
139         }
140         if (fmt == QF_RPC) {
141                 fputs(_("Cannot turn on/off quotas via RPC.\n"), stderr);
142                 exit(1);
143         }
144         if (!(flags & (FL_USER | FL_GROUP)))
145                 flags |= FL_USER | FL_GROUP;
146         if (!(flags & FL_ALL)) {
147                 mntpoints = argstr + optind;
148                 mntcnt = argcnt - optind;
149         }
150 }
151
152 int pinfo(char *fmt, ...)
153 {
154         va_list arg;
155         int ret;
156
157         if (!(flags & FL_VERBOSE))
158                 return 0;
159         va_start(arg, fmt);
160         ret = vprintf(fmt, arg);
161         va_end(arg);
162         return ret;
163 }
164
165 /*
166  *      Enable/disable rsquash on given filesystem
167  */
168 static int quotarsquashonoff(const char *quotadev, int type, int flags)
169 {
170 #if defined(MNTOPT_RSQUASH)
171         int ret;
172
173         if (kernel_iface == IFACE_GENERIC) {
174                 int qcmd = QCMD(Q_SETINFO, type);
175                 struct if_dqinfo info;
176
177                 info.dqi_flags = V1_DQF_RSQUASH;
178                 info.dqi_valid = IIF_FLAGS;
179                 ret = quotactl(qcmd, quotadev, 0, (void *)&info);
180         }
181         else {
182                 int mode = (flags & STATEFLAG_OFF) ? 0 : 1;
183                 int qcmd = QCMD(Q_V1_RSQUASH, type);
184
185                 ret = quotactl(qcmd, quotadev, 0, (void *)&mode);
186         }
187         if (ret < 0) {
188                 errstr(_("set root_squash on %s: %s\n"), quotadev, strerror(errno));
189                 return 1;
190         }
191         if (flags & STATEFLAG_OFF)
192                 pinfo(_("%s: %s root_squash turned off\n"), quotadev, type2name(type));
193         else if (flags & STATEFLAG_ON)
194                 pinfo(_("%s: %s root_squash turned on\n"), quotadev, type2name(type));
195 #endif
196         return 0;
197 }
198
199 /*
200  *      Enable/disable VFS quota on given filesystem
201  */
202 static int quotaonoff(const char *quotadev, const char *quotadir, char *quotafile, int type, int fmt, int flags)
203 {
204         int qcmd, kqf;
205
206         if (flags & STATEFLAG_OFF) {
207                 if (kernel_iface == IFACE_GENERIC)
208                         qcmd = QCMD(Q_QUOTAOFF, type);
209                 else
210                         qcmd = QCMD(Q_6_5_QUOTAOFF, type);
211                 if (quotactl(qcmd, quotadev, 0, NULL) < 0) {
212                         errstr(_("quotactl on %s [%s]: %s\n"), quotadev, quotadir, strerror(errno));
213                         return 1;
214                 }
215                 pinfo(_("%s [%s]: %s quotas turned off\n"), quotadev, quotadir, _(type2name(type)));
216                 return 0;
217         }
218         if (kernel_iface == IFACE_GENERIC) {
219                 qcmd = QCMD(Q_QUOTAON, type);
220                 kqf = util2kernfmt(fmt);
221         }
222         else {
223                 qcmd = QCMD(Q_6_5_QUOTAON, type);
224                 kqf = 0;
225         }
226         if (quotactl(qcmd, quotadev, kqf, (void *)quotafile) < 0) {
227                 if (errno == ENOENT)
228                         errstr(_("cannot find %s on %s [%s]\n"), quotafile, quotadev, quotadir);
229                 else
230                         errstr(_("using %s on %s [%s]: %s\n"), quotafile, quotadev, quotadir, strerror(errno));
231                 if (errno == EINVAL)
232                         errstr(_("Maybe create new quota files with quotacheck(8)?\n"));
233                 else if (errno == ESRCH)
234                         errstr(_("Quota format not supported in kernel.\n"));
235                 return 1;
236         }
237         pinfo(_("%s [%s]: %s quotas turned on\n"), quotadev, quotadir, _(type2name(type)));
238         return 0;
239 }
240
241 /*
242  *      Enable/disable quota/rootsquash on given filesystem (version 1)
243  */
244 static int v1_newstate(struct mount_entry *mnt, int type, char *file, int flags, int fmt)
245 {
246         int errs = 0;
247
248         if ((flags & STATEFLAG_OFF) && str_hasmntopt(mnt->me_opts, MNTOPT_RSQUASH))
249                 errs += quotarsquashonoff(mnt->me_devname, type, flags);
250         errs += quotaonoff(mnt->me_devname, mnt->me_dir, file, type, QF_VFSOLD, flags);
251         if ((flags & STATEFLAG_ON) && str_hasmntopt(mnt->me_opts, MNTOPT_RSQUASH))
252                 errs += quotarsquashonoff(mnt->me_devname, type, flags);
253         return errs;
254 }
255
256 /*
257  *      Enable/disable quota on given filesystem (generic VFS quota)
258  */
259 static int v2_newstate(struct mount_entry *mnt, int type, char *file, int flags, int fmt)
260 {
261         return quotaonoff(mnt->me_devname, mnt->me_dir, file, type, fmt, flags);
262 }
263
264 /*
265  *      For both VFS quota formats, need to pass in the quota file;
266  *      for XFS quota manager, pass on the -x command line option.
267  */
268 static int newstate(struct mount_entry *mnt, int type, char *extra)
269 {
270         int sflags, ret = 0;
271
272         sflags = flags & FL_OFF ? STATEFLAG_OFF : STATEFLAG_ON;
273         if (flags & FL_ALL)
274                 sflags |= STATEFLAG_ALL;
275
276         if (!strcmp(mnt->me_type, MNTTYPE_GFS2)) {
277                 errstr(_("Cannot change state of GFS2 quota.\n"));
278                 return 1;
279         } else if (!strcmp(mnt->me_type, MNTTYPE_XFS)) {        /* XFS filesystem has special handling... */
280                 if (!kern_qfmt_supp(QF_XFS)) {
281                         errstr(_("Cannot change state of XFS quota. It's not compiled in kernel.\n"));
282                         return 1;
283                 }
284                 ret = xfs_newstate(mnt, type, extra, sflags);
285         }
286         else if (mnt->me_qfmt[type] == QF_META) {
287                 /* Must be non-empty because empty path is always invalid. */
288                 ret = v2_newstate(mnt, type, ".", sflags, QF_VFSV0);
289         }
290         else {
291                 int usefmt;
292
293                 if (!me_hasquota(mnt, type))
294                         return 0;
295                 if (fmt == -1) {
296                         if (get_qf_name(mnt, type, QF_VFSV0,
297                                         NF_FORMAT, &extra) >= 0)
298                                 usefmt = QF_VFSV0;
299                         else if (get_qf_name(mnt, type, QF_VFSV1,
300                                         NF_FORMAT, &extra) >= 0)
301                                 usefmt = QF_VFSV1;
302                         else if (get_qf_name(mnt, type, QF_VFSOLD,
303                                         NF_FORMAT, &extra) >= 0)
304                                 usefmt = QF_VFSOLD;
305                         else {
306                                 errstr(_("Cannot find quota file on %s [%s] to turn quotas on/off.\n"), mnt->me_dir, mnt->me_devname);
307                                 return 1;
308                         }
309                 } else {
310                         if (get_qf_name(mnt, type, fmt, NF_FORMAT, &extra) < 0) {
311                                 errstr(_("Quota file on %s [%s] does not exist or has wrong format.\n"), mnt->me_dir, mnt->me_devname);
312                                 return 1;
313                         }
314                         usefmt = fmt;
315                 }
316                 if (is_tree_qfmt(usefmt))
317                         ret = v2_newstate(mnt, type, extra, sflags, usefmt);
318                 else
319                         ret = v1_newstate(mnt, type, extra, sflags, QF_VFSOLD);
320                 free(extra);
321         }
322         return ret;
323 }
324
325 /* Print state of quota (on/off) */
326 static int print_state(struct mount_entry *mnt, int type)
327 {
328         int on = 0;
329
330         if (!strcmp(mnt->me_type, MNTTYPE_XFS) ||
331             !strcmp(mnt->me_type, MNTTYPE_GFS2)) {
332                 if (kern_qfmt_supp(QF_XFS))
333                         on = kern_quota_on(mnt, type, QF_XFS) != -1;
334         }
335         else if (kernel_iface == IFACE_GENERIC)
336                 on = kern_quota_on(mnt, type, -1) != -1;
337         else if (kern_qfmt_supp(QF_VFSV0))
338                 on = kern_quota_on(mnt, type, QF_VFSV0) != -1;
339         else if (kern_qfmt_supp(QF_VFSOLD))
340                 on = kern_quota_on(mnt, type, QF_VFSOLD) != -1;
341
342         printf(_("%s quota on %s (%s) is %s\n"), _(type2name(type)), mnt->me_dir, mnt->me_devname,
343           on ? _("on") : _("off"));
344         
345         return on;
346 }
347
348 int main(int argc, char **argv)
349 {
350         struct mount_entry *mnt;
351         int errs = 0;
352
353         gettexton();
354
355         progname = basename(argv[0]);
356         if (strcmp(progname, "quotaoff") == 0)
357                 flags |= FL_OFF;
358         else if (strcmp(progname, "quotaon") != 0)
359                 die(1, _("Name must be quotaon or quotaoff not %s\n"), progname);
360
361         parse_options(argc, argv);
362
363         init_kernel_interface();
364         if (fmt != -1 && !kern_qfmt_supp(fmt))
365                 die(1, _("Required format %s not supported by kernel.\n"), fmt2name(fmt));
366         else if (!kern_qfmt_supp(-1))
367                 errstr(_("Warning: No quota format detected in the kernel.\n"));
368
369         if (init_mounts_scan(mntcnt, mntpoints, MS_XFS_DISABLED | MS_LOCALONLY) < 0)
370                 return 1;
371         while ((mnt = get_next_mount())) {
372                 if (nfs_fstype(mnt->me_type)) {
373                         if (!(flags & FL_ALL))
374                                 errstr(_("%s: Quota cannot be turned on on NFS filesystem\n"), mnt->me_devname);
375                         continue;
376                 }
377
378                 if (!(flags & FL_STAT)) {
379                         if (flags & FL_GROUP)
380                                 errs += newstate(mnt, GRPQUOTA, xarg);
381                         if (flags & FL_USER)
382                                 errs += newstate(mnt, USRQUOTA, xarg);
383                 }
384                 else {
385                         if (flags & FL_GROUP)
386                                 errs += print_state(mnt, GRPQUOTA);
387                         if (flags & FL_USER)
388                                 errs += print_state(mnt, USRQUOTA);
389                 }
390         }
391         end_mounts_scan();
392
393         return errs;
394 }
395