Initial commit to Gerrit
[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
46 #include "quotaon.h"
47 #include "quota.h"
48 #include "quotasys.h"
49
50 #define FL_USER 1
51 #define FL_GROUP 2
52 #define FL_VERBOSE 4
53 #define FL_ALL 8
54 #define FL_STAT 16
55 #define FL_OFF 32
56
57 int flags, fmt = -1;
58 char *progname;
59 char **mntpoints;
60 int mntcnt;
61 char *xarg = NULL;
62
63 static void usage(void)
64 {
65         errstr(_("Usage:\n\t%s [-guvp] [-F quotaformat] [-x state] -a\n\
66 \t%s [-guvp] [-F quotaformat] [-x state] filesys ...\n\n\
67 -a, --all                turn quotas on for all filesystems\n\
68 -f, --off                turn quotas off\n\
69 -u, --user               operate on user quotas\n\
70 -g, --group              operate on group quotas\n\
71 -p, --print-state        print whether quotas are on or off\n\
72 -x, --xfs-command=cmd    perform XFS quota command\n\
73 -F, --format=formatname  operate on specific quota format\n\
74 -v, --verbose            print more messages\n\
75 -h, --help               display this help text and exit\n\
76 -V, --version            display version information and exit\n"), progname, progname);
77         exit(1);
78 }
79
80 static void parse_options(int argcnt, char **argstr)
81 {
82         int c;
83         struct option long_opts[] = {
84                 { "all", 0, NULL, 'a' },
85                 { "off", 0, NULL, 'f' },
86                 { "verbose", 0, NULL, 'v' },
87                 { "user", 0, NULL, 'u' },
88                 { "group", 0, NULL, 'g' },
89                 { "print-state", 0, NULL, 'p' },
90                 { "xfs-command", 1, NULL, 'x' },
91                 { "format", 1, NULL, 'F' },
92                 { "version", 0, NULL, 'V' },
93                 { "help", 0, NULL, 'h' },
94                 { NULL, 0, NULL, 0 }
95         };
96
97         while ((c = getopt_long(argcnt, argstr, "afvugpx:VF:h", long_opts, NULL)) != -1) {
98                 switch (c) {
99                   case 'a':
100                           flags |= FL_ALL;
101                           break;
102                   case 'f':
103                           flags |= FL_OFF;
104                           break;
105                   case 'g':
106                           flags |= FL_GROUP;
107                           break;
108                   case 'u':
109                           flags |= FL_USER;
110                           break;
111                   case 'v':
112                           flags |= FL_VERBOSE;
113                           break;
114                   case 'x':
115                           xarg = optarg;
116                           break;
117                   case 'p':
118                           flags |= FL_STAT;
119                           break;
120                   case 'F':
121                           if ((fmt = name2fmt(optarg)) == QF_ERROR)
122                                 exit(1);
123                           break;
124                   case 'V':
125                           version();
126                           exit(0);
127                   case 'h':
128                   default:
129                           usage();
130                 }
131         }
132         if ((flags & FL_ALL && optind != argcnt) || (!(flags & FL_ALL) && optind == argcnt)) {
133                 fputs(_("Bad number of arguments.\n"), stderr);
134                 usage();
135         }
136         if (fmt == QF_RPC) {
137                 fputs(_("Cannot turn on/off quotas via RPC.\n"), stderr);
138                 exit(1);
139         }
140         if (!(flags & (FL_USER | FL_GROUP)))
141                 flags |= FL_USER | FL_GROUP;
142         if (!(flags & FL_ALL)) {
143                 mntpoints = argstr + optind;
144                 mntcnt = argcnt - optind;
145         }
146 }
147
148 /*
149  *      Enable/disable rsquash on given filesystem
150  */
151 static int quotarsquashonoff(const char *quotadev, int type, int flags)
152 {
153 #if defined(MNTOPT_RSQUASH)
154         int ret;
155
156         if (kernel_iface == IFACE_GENERIC) {
157                 int qcmd = QCMD(Q_SETINFO, type);
158                 struct if_dqinfo info;
159
160                 info.dqi_flags = V1_DQF_RSQUASH;
161                 info.dqi_valid = IIF_FLAGS;
162                 ret = quotactl(qcmd, quotadev, 0, (void *)&info);
163         }
164         else {
165                 int mode = (flags & STATEFLAG_OFF) ? 0 : 1;
166                 int qcmd = QCMD(Q_V1_RSQUASH, type);
167
168                 ret = quotactl(qcmd, quotadev, 0, (void *)&mode);
169         }
170         if (ret < 0) {
171                 errstr(_("set root_squash on %s: %s\n"), quotadev, strerror(errno));
172                 return 1;
173         }
174         if ((flags & STATEFLAG_VERBOSE) && (flags & STATEFLAG_OFF))
175                 printf(_("%s: %s root_squash turned off\n"), quotadev, type2name(type));
176         else if ((flags & STATEFLAG_VERBOSE) && (flags & STATEFLAG_ON))
177                 printf(_("%s: %s root_squash turned on\n"), quotadev, type2name(type));
178 #endif
179         return 0;
180 }
181
182 /*
183  *      Enable/disable VFS quota on given filesystem
184  */
185 static int quotaonoff(char *quotadev, char *quotadir, char *quotafile, int type, int fmt, int flags)
186 {
187         int qcmd, kqf;
188
189         if (flags & STATEFLAG_OFF) {
190                 if (kernel_iface == IFACE_GENERIC)
191                         qcmd = QCMD(Q_QUOTAOFF, type);
192                 else
193                         qcmd = QCMD(Q_6_5_QUOTAOFF, type);
194                 if (quotactl(qcmd, quotadev, 0, NULL) < 0) {
195                         errstr(_("quotactl on %s [%s]: %s\n"), quotadev, quotadir, strerror(errno));
196                         return 1;
197                 }
198                 if (flags & STATEFLAG_VERBOSE)
199                         printf(_("%s [%s]: %s quotas turned off\n"), quotadev, quotadir, type2name(type));
200                 return 0;
201         }
202         if (kernel_iface == IFACE_GENERIC) {
203                 qcmd = QCMD(Q_QUOTAON, type);
204                 kqf = util2kernfmt(fmt);
205         }
206         else {
207                 qcmd = QCMD(Q_6_5_QUOTAON, type);
208                 kqf = 0;
209         }
210         if (quotactl(qcmd, quotadev, kqf, (void *)quotafile) < 0) {
211                 if (errno == ENOENT)
212                         errstr(_("cannot find %s on %s [%s]\n"), quotafile, quotadev, quotadir);
213                 else
214                         errstr(_("using %s on %s [%s]: %s\n"), quotafile, quotadev, quotadir, strerror(errno));
215                 if (errno == EINVAL)
216                         errstr(_("Maybe create new quota files with quotacheck(8)?\n"));
217                 else if (errno == ESRCH)
218                         errstr(_("Quota format not supported in kernel.\n"));
219                 return 1;
220         }
221         if (flags & STATEFLAG_VERBOSE)
222                 printf(_("%s [%s]: %s quotas turned on\n"), quotadev, quotadir, type2name(type));
223         return 0;
224 }
225
226 /*
227  *      Enable/disable quota/rootsquash on given filesystem (version 1)
228  */
229 static int v1_newstate(struct mntent *mnt, int type, char *file, int flags, int fmt)
230 {
231         int errs = 0;
232         const char *dev = get_device_name(mnt->mnt_fsname);
233
234         if (!dev)
235                 return 1;
236         if ((flags & STATEFLAG_OFF) && hasmntopt(mnt, MNTOPT_RSQUASH))
237                 errs += quotarsquashonoff(dev, type, flags);
238         if (hasquota(mnt, type, 0))
239                 errs += quotaonoff((char *)dev, mnt->mnt_dir, file, type, QF_VFSOLD, flags);
240         if ((flags & STATEFLAG_ON) && hasmntopt(mnt, MNTOPT_RSQUASH))
241                 errs += quotarsquashonoff(dev, type, flags);
242         free((char *)dev);
243         return errs;
244 }
245
246 /*
247  *      Enable/disable quota on given filesystem (generic VFS quota)
248  */
249 static int v2_newstate(struct mntent *mnt, int type, char *file, int flags, int fmt)
250 {
251         const char *dev = get_device_name(mnt->mnt_fsname);
252         int errs = 0;
253
254         if (!dev)
255                 return 1;
256         if (hasquota(mnt, type, 0))
257                 errs = quotaonoff((char *)dev, mnt->mnt_dir, file, type, fmt, flags);
258         free((char *)dev);
259         return errs;
260 }
261
262 /*
263  *      For both VFS quota formats, need to pass in the quota file;
264  *      for XFS quota manager, pass on the -x command line option.
265  */
266 static int newstate(struct mntent *mnt, int type, char *extra)
267 {
268         int sflags, ret = 0;
269
270         sflags = flags & FL_OFF ? STATEFLAG_OFF : STATEFLAG_ON;
271         if (flags & FL_VERBOSE)
272                 sflags |= STATEFLAG_VERBOSE;
273         if (flags & FL_ALL)
274                 sflags |= STATEFLAG_ALL;
275
276         if (!strcmp(mnt->mnt_type, MNTTYPE_XFS)) {      /* XFS filesystem has special handling... */
277                 if (!kern_qfmt_supp(QF_XFS)) {
278                         errstr(_("Cannot change state of XFS quota. It's not compiled in kernel.\n"));
279                         return 1;
280                 }
281                 if ((flags & FL_OFF && (kern_quota_on(mnt->mnt_fsname, USRQUOTA, QF_XFS) != -1
282                     || kern_quota_on(mnt->mnt_fsname, GRPQUOTA, QF_XFS) != -1))
283                     || (!(flags & FL_OFF) && kern_quota_on(mnt->mnt_fsname, type, QF_XFS) == -1))
284                         ret = xfs_newstate(mnt, type, extra, sflags);
285         }
286         else if (meta_qf_fstype(mnt->mnt_type)) {
287                 if (!hasquota(mnt, type, 0))
288                         return 0;
289                 /* Must be non-empty because empty path is always invalid. */
290                 ret = v2_newstate(mnt, type, ".", sflags, QF_VFSV0);
291         }
292         else {
293                 int usefmt;
294
295                 if (!hasquota(mnt, type, 0))
296                         return 0;
297                 if (fmt == -1) {
298                         if (get_qf_name(mnt, type, QF_VFSV0,
299                                         NF_FORMAT, &extra) >= 0)
300                                 usefmt = QF_VFSV0;
301                         else if (get_qf_name(mnt, type, QF_VFSV1,
302                                         NF_FORMAT, &extra) >= 0)
303                                 usefmt = QF_VFSV1;
304                         else if (get_qf_name(mnt, type, QF_VFSOLD,
305                                         NF_FORMAT, &extra) >= 0)
306                                 usefmt = QF_VFSOLD;
307                         else {
308                                 errstr(_("Cannot find quota file on %s [%s] to turn quotas on/off.\n"), mnt->mnt_dir, mnt->mnt_fsname);
309                                 return 1;
310                         }
311                 } else {
312                         if (get_qf_name(mnt, type, fmt, NF_FORMAT, &extra) < 0) {
313                                 errstr(_("Quota file on %s [%s] does not exist or has wrong format.\n"), mnt->mnt_dir, mnt->mnt_fsname);
314                                 return 1;
315                         }
316                         usefmt = fmt;
317                 }
318                 if (is_tree_qfmt(usefmt))
319                         ret = v2_newstate(mnt, type, extra, sflags, usefmt);
320                 else
321                         ret = v1_newstate(mnt, type, extra, sflags, QF_VFSOLD);
322                 free(extra);
323         }
324         return ret;
325 }
326
327 /* Print state of quota (on/off) */
328 static int print_state(struct mntent *mnt, int type)
329 {
330         int on = 0;
331
332         if (!strcmp(mnt->mnt_type, MNTTYPE_XFS)) {
333                 if (kern_qfmt_supp(QF_XFS))
334                         on = kern_quota_on(mnt->mnt_fsname, type, QF_XFS) != -1;
335         }
336         else if (kernel_iface == IFACE_GENERIC)
337                 on = kern_quota_on(mnt->mnt_fsname, type, -1) != -1;
338         else if (kern_qfmt_supp(QF_VFSV0))
339                 on = kern_quota_on(mnt->mnt_fsname, type, QF_VFSV0) != -1;
340         else if (kern_qfmt_supp(QF_VFSOLD))
341                 on = kern_quota_on(mnt->mnt_fsname, type, QF_VFSOLD) != -1;
342
343         printf(_("%s quota on %s (%s) is %s\n"), type2name(type), mnt->mnt_dir, mnt->mnt_fsname,
344           on ? _("on") : _("off"));
345         
346         return on;
347 }
348
349 int main(int argc, char **argv)
350 {
351         struct mntent *mnt;
352         int errs = 0;
353
354         gettexton();
355
356         progname = basename(argv[0]);
357         if (strcmp(progname, "quotaoff") == 0)
358                 flags |= FL_OFF;
359         else if (strcmp(progname, "quotaon") != 0)
360                 die(1, _("Name must be quotaon or quotaoff not %s\n"), progname);
361
362         parse_options(argc, argv);
363
364         init_kernel_interface();
365         if (fmt != -1 && !kern_qfmt_supp(fmt))
366                 die(1, _("Required format %s not supported by kernel.\n"), fmt2name(fmt));
367         else if (!kern_qfmt_supp(-1))
368                 errstr(_("Warning: No quota format detected in the kernel.\n"));
369
370         if (init_mounts_scan(mntcnt, mntpoints, MS_XFS_DISABLED | MS_LOCALONLY) < 0)
371                 return 1;
372         while ((mnt = get_next_mount())) {
373                 if (nfs_fstype(mnt->mnt_type)) {
374                         if (!(flags & FL_ALL))
375                                 fprintf(stderr, "%s: Quota cannot be turned on on NFS filesystem\n", mnt->mnt_fsname);
376                         continue;
377                 }
378
379                 if (!(flags & FL_STAT)) {
380                         if (flags & FL_GROUP)
381                                 errs += newstate(mnt, GRPQUOTA, xarg);
382                         if (flags & FL_USER)
383                                 errs += newstate(mnt, USRQUOTA, xarg);
384                 }
385                 else {
386                         if (flags & FL_GROUP)
387                                 errs += print_state(mnt, GRPQUOTA);
388                         if (flags & FL_USER)
389                                 errs += print_state(mnt, USRQUOTA);
390                 }
391         }
392         end_mounts_scan();
393
394         return errs;
395 }
396