blob: fe1922412672615d3bb4dc78ab458eeab1c4c72e [file] [log] [blame]
/*
* Copyright (c) 1980, 1990 Regents of the University of California. All
* rights reserved.
*
* This code is derived from software contributed to Berkeley by Robert Elz at
* The University of Melbourne.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. 3. All advertising
* materials mentioning features or use of this software must display the
* following acknowledgement: This product includes software developed by the
* University of California, Berkeley and its contributors. 4. Neither the
* name of the University nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
/*
* Turn quota on/off for a filesystem.
*/
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "quotaon.h"
#include "quota.h"
#include "quotasys.h"
#define FL_USER 1
#define FL_GROUP 2
#define FL_VERBOSE 4
#define FL_ALL 8
#define FL_STAT 16
#define FL_OFF 32
#define FL_PROJECT 64
static int flags, fmt = -1;
char *progname;
static char **mntpoints;
static int mntcnt;
static char *xarg = NULL;
static void usage(void)
{
errstr(_("Usage:\n\t%s [-guPvp] [-F quotaformat] [-x state] -a\n\
\t%s [-guPvp] [-F quotaformat] [-x state] filesys ...\n\n\
-a, --all %s\n\
-f, --off turn quotas off\n\
-u, --user operate on user quotas\n\
-g, --group operate on group quotas\n\
-P, --project operate on project quotas\n\
-p, --print-state print whether quotas are on or off\n\
-x, --xfs-command=cmd perform XFS quota command\n\
-F, --format=formatname operate on specific quota format\n\
-v, --verbose print more messages\n\
-h, --help display this help text and exit\n\
-V, --version display version information and exit\n"),
progname, progname,
strcmp(progname, "quotaon") ? _("turn quotas off for all filesystems") :
_("turn quotas on for all filesystems"));
exit(1);
}
static void parse_options(int argcnt, char **argstr)
{
int c;
struct option long_opts[] = {
{ "all", 0, NULL, 'a' },
{ "off", 0, NULL, 'f' },
{ "verbose", 0, NULL, 'v' },
{ "user", 0, NULL, 'u' },
{ "group", 0, NULL, 'g' },
{ "project", 0, NULL, 'P' },
{ "print-state", 0, NULL, 'p' },
{ "xfs-command", 1, NULL, 'x' },
{ "format", 1, NULL, 'F' },
{ "version", 0, NULL, 'V' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argcnt, argstr, "afvugpPx:VF:h", long_opts, NULL)) != -1) {
switch (c) {
case 'a':
flags |= FL_ALL;
break;
case 'f':
flags |= FL_OFF;
break;
case 'g':
flags |= FL_GROUP;
break;
case 'u':
flags |= FL_USER;
break;
case 'P':
flags |= FL_PROJECT;
break;
case 'v':
flags |= FL_VERBOSE;
break;
case 'x':
xarg = optarg;
break;
case 'p':
flags |= FL_STAT;
break;
case 'F':
if ((fmt = name2fmt(optarg)) == QF_ERROR)
exit(1);
break;
case 'V':
version();
exit(0);
case 'h':
default:
usage();
}
}
if ((flags & FL_ALL && optind != argcnt) || (!(flags & FL_ALL) && optind == argcnt)) {
fputs(_("Bad number of arguments.\n"), stderr);
usage();
}
if (fmt == QF_RPC) {
fputs(_("Cannot turn on/off quotas via RPC.\n"), stderr);
exit(1);
}
if (!(flags & (FL_USER | FL_GROUP | FL_PROJECT)))
flags |= FL_USER | FL_GROUP | FL_PROJECT;
if (!(flags & FL_ALL)) {
mntpoints = argstr + optind;
mntcnt = argcnt - optind;
}
}
int pinfo(char *fmt, ...)
{
va_list arg;
int ret;
if (!(flags & FL_VERBOSE))
return 0;
va_start(arg, fmt);
ret = vprintf(fmt, arg);
va_end(arg);
return ret;
}
/*
* Enable/disable rsquash on given filesystem
*/
static int quotarsquashonoff(const char *quotadev, int type, int flags)
{
#if defined(MNTOPT_RSQUASH)
int ret;
if (kernel_iface == IFACE_GENERIC) {
int qcmd = QCMD(Q_SETINFO, type);
struct if_dqinfo info;
info.dqi_flags = V1_DQF_RSQUASH;
info.dqi_valid = IIF_FLAGS;
ret = quotactl(qcmd, quotadev, 0, (void *)&info);
}
else {
int mode = (flags & STATEFLAG_OFF) ? 0 : 1;
int qcmd = QCMD(Q_V1_RSQUASH, type);
ret = quotactl(qcmd, quotadev, 0, (void *)&mode);
}
if (ret < 0) {
errstr(_("set root_squash on %s: %s\n"), quotadev, strerror(errno));
return 1;
}
if (flags & STATEFLAG_OFF)
pinfo(_("%s: %s root_squash turned off\n"), quotadev, type2name(type));
else if (flags & STATEFLAG_ON)
pinfo(_("%s: %s root_squash turned on\n"), quotadev, type2name(type));
#endif
return 0;
}
/*
* Enable/disable VFS quota on given filesystem
*/
static int quotaonoff(const char *quotadev, const char *quotadir, char *quotafile, int type, int fmt, int flags)
{
int qcmd, kqf;
if (flags & STATEFLAG_OFF) {
if (kernel_iface == IFACE_GENERIC)
qcmd = QCMD(Q_QUOTAOFF, type);
else
qcmd = QCMD(Q_6_5_QUOTAOFF, type);
if (quotactl(qcmd, quotadev, 0, NULL) < 0) {
errstr(_("quotactl on %s [%s]: %s\n"), quotadev, quotadir, strerror(errno));
return 1;
}
pinfo(_("%s [%s]: %s quotas turned off\n"), quotadev, quotadir, _(type2name(type)));
return 0;
}
if (kernel_iface == IFACE_GENERIC) {
qcmd = QCMD(Q_QUOTAON, type);
kqf = util2kernfmt(fmt);
}
else {
qcmd = QCMD(Q_6_5_QUOTAON, type);
kqf = 0;
}
if (quotactl(qcmd, quotadev, kqf, (void *)quotafile) < 0) {
if (errno == ENOENT)
errstr(_("cannot find %s on %s [%s]\n"), quotafile, quotadev, quotadir);
else
errstr(_("using %s on %s [%s]: %s\n"), quotafile, quotadev, quotadir, strerror(errno));
if (errno == EINVAL)
errstr(_("Maybe create new quota files with quotacheck(8)?\n"));
else if (errno == ESRCH)
errstr(_("Quota format not supported in kernel.\n"));
return 1;
}
pinfo(_("%s [%s]: %s quotas turned on\n"), quotadev, quotadir, _(type2name(type)));
return 0;
}
/*
* Enable/disable quota/rootsquash on given filesystem (version 1)
*/
static int v1_newstate(struct mount_entry *mnt, int type, char *file, int flags, int fmt)
{
int errs = 0;
if ((flags & STATEFLAG_OFF) && str_hasmntopt(mnt->me_opts, MNTOPT_RSQUASH))
errs += quotarsquashonoff(mnt->me_devname, type, flags);
errs += quotaonoff(mnt->me_devname, mnt->me_dir, file, type, QF_VFSOLD, flags);
if ((flags & STATEFLAG_ON) && str_hasmntopt(mnt->me_opts, MNTOPT_RSQUASH))
errs += quotarsquashonoff(mnt->me_devname, type, flags);
return errs;
}
/*
* Enable/disable quota on given filesystem (generic VFS quota)
*/
static int v2_newstate(struct mount_entry *mnt, int type, char *file, int flags, int fmt)
{
return quotaonoff(mnt->me_devname, mnt->me_dir, file, type, fmt, flags);
}
/*
* For both VFS quota formats, need to pass in the quota file;
* for XFS quota manager, pass on the -x command line option.
*/
static int newstate(struct mount_entry *mnt, int type, char *extra)
{
int sflags, ret = 0;
sflags = flags & FL_OFF ? STATEFLAG_OFF : STATEFLAG_ON;
if (flags & FL_ALL)
sflags |= STATEFLAG_ALL;
if (!strcmp(mnt->me_type, MNTTYPE_GFS2)) {
errstr(_("Cannot change state of GFS2 quota.\n"));
return 1;
} else if (!strcmp(mnt->me_type, MNTTYPE_XFS)) { /* XFS filesystem has special handling... */
if (!kern_qfmt_supp(QF_XFS)) {
errstr(_("Cannot change state of XFS quota. It's not compiled in kernel.\n"));
return 1;
}
ret = xfs_newstate(mnt, type, extra, sflags);
}
else if (mnt->me_qfmt[type] == QF_META) {
/* Must be non-empty because empty path is always invalid. */
ret = v2_newstate(mnt, type, ".", sflags, QF_VFSV0);
}
else {
int usefmt;
if (!me_hasquota(mnt, type))
return 0;
if (fmt == -1) {
if (get_qf_name(mnt, type, QF_VFSV0,
NF_FORMAT, &extra) >= 0)
usefmt = QF_VFSV0;
else if (get_qf_name(mnt, type, QF_VFSV1,
NF_FORMAT, &extra) >= 0)
usefmt = QF_VFSV1;
else if (get_qf_name(mnt, type, QF_VFSOLD,
NF_FORMAT, &extra) >= 0)
usefmt = QF_VFSOLD;
else {
errstr(_("Cannot find quota file on %s [%s] to turn quotas on/off.\n"), mnt->me_dir, mnt->me_devname);
return 1;
}
} else {
if (get_qf_name(mnt, type, fmt, NF_FORMAT, &extra) < 0) {
errstr(_("Quota file on %s [%s] does not exist or has wrong format.\n"), mnt->me_dir, mnt->me_devname);
return 1;
}
usefmt = fmt;
}
if (is_tree_qfmt(usefmt))
ret = v2_newstate(mnt, type, extra, sflags, usefmt);
else
ret = v1_newstate(mnt, type, extra, sflags, QF_VFSOLD);
free(extra);
}
return ret;
}
/* Print state of quota (on/off) */
static int print_state(struct mount_entry *mnt, int type)
{
int on = 0;
char *state;
if (kern_qfmt_supp(QF_XFS)) {
on = kern_quota_state_xfs(mnt->me_devname, type);
if (!strcmp(mnt->me_type, MNTTYPE_XFS) ||
!strcmp(mnt->me_type, MNTTYPE_GFS2) || on >= 0) {
if (on < 0)
on = 0;
if (!(flags & FL_VERBOSE))
goto print_state;
if (on == 0)
state = _("off");
else if (on == 1)
state = _("on (accounting)");
else
state = _("on (enforced)");
goto print;
}
}
if (kernel_iface == IFACE_GENERIC)
on = kern_quota_on(mnt, type, -1) != -1;
else if (kern_qfmt_supp(QF_VFSV0))
on = kern_quota_on(mnt, type, QF_VFSV0) != -1;
else if (kern_qfmt_supp(QF_VFSOLD))
on = kern_quota_on(mnt, type, QF_VFSOLD) != -1;
print_state:
state = on ? _("on") : _("off");
print:
printf(_("%s quota on %s (%s) is %s\n"), _(type2name(type)),
mnt->me_dir, mnt->me_devname, state);
return on > 0;
}
int main(int argc, char **argv)
{
struct mount_entry *mnt;
int errs = 0;
gettexton();
progname = basename(argv[0]);
if (strcmp(progname, "quotaoff") == 0)
flags |= FL_OFF;
else if (strcmp(progname, "quotaon") != 0)
die(1, _("Name must be quotaon or quotaoff not %s\n"), progname);
parse_options(argc, argv);
init_kernel_interface();
if (fmt != -1 && !kern_qfmt_supp(fmt))
die(1, _("Required format %s not supported by kernel.\n"), fmt2name(fmt));
else if (!kern_qfmt_supp(-1))
errstr(_("Warning: No quota format detected in the kernel.\n"));
if (init_mounts_scan(mntcnt, mntpoints, MS_XFS_DISABLED | MS_LOCALONLY) < 0)
return 1;
while ((mnt = get_next_mount())) {
if (nfs_fstype(mnt->me_type)) {
if (!(flags & FL_ALL))
errstr(_("%s: Quota cannot be turned on on NFS filesystem\n"), mnt->me_devname);
continue;
}
if (!(flags & FL_STAT)) {
if (flags & FL_GROUP)
errs += newstate(mnt, GRPQUOTA, xarg);
if (flags & FL_USER)
errs += newstate(mnt, USRQUOTA, xarg);
if (flags & FL_PROJECT)
errs += newstate(mnt, PRJQUOTA, xarg);
}
else {
if (flags & FL_GROUP)
errs += print_state(mnt, GRPQUOTA);
if (flags & FL_USER)
errs += print_state(mnt, USRQUOTA);
if (flags & FL_PROJECT)
errs += print_state(mnt, PRJQUOTA);
}
}
end_mounts_scan();
return errs;
}