blob: 8f9718f1984cf4dce3535cbcaa1b62a6e6235287 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include <stdbool.h>
#include "command.h"
#include "init.h"
#include "quota.h"
static cmdinfo_t off_cmd;
static cmdinfo_t state_cmd;
static cmdinfo_t enable_cmd;
static cmdinfo_t disable_cmd;
static cmdinfo_t remove_cmd;
static void
off_help(void)
{
printf(_(
"\n"
" turn filesystem quota off, both accounting and enforcement\n"
"\n"
" Example:\n"
" 'off -uv' (switch off user quota on the current filesystem)\n"
" This command is the equivalent of the traditional quotaoff command,\n"
" which disables quota completely on a mounted filesystem.\n"
" Note that there is no 'on' command - for XFS filesystems (with the\n"
" exception of the root filesystem on IRIX) quota can only be enabled\n"
" at mount time, through the use of one of the quota mount options.\n"
"\n"
" The state command is useful for displaying the current state. Using\n"
" the -v (verbose) option with the 'off' command will display the quota\n"
" state for the affected filesystem once the operation is complete.\n"
" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
" and defaults to user quota (multiple types can be specified).\n"
"\n"));
}
static void
state_help(void)
{
printf(_(
"\n"
" query the state of quota on the current filesystem\n"
"\n"
" This is a verbose status command, reporting whether or not accounting\n"
" and/or enforcement are enabled for a filesystem, which inodes are in\n"
" use as the quota state inodes, and how many extents and blocks are\n"
" presently being used to hold that information.\n"
" The quota type is specified via -g (groups), -p (projects) or -u (users)\n"
" and defaults to user quota (multiple types can be specified).\n"
"\n"));
}
static void
enable_help(void)
{
printf(_(
"\n"
" enable quota enforcement on a filesystem\n"
"\n"
" If a filesystem is mounted and has quota accounting enabled, but not\n"
" quota enforcement, enforcement can be enabled with this command.\n"
" With the -v (verbose) option, the status of the filesystem will be\n"
" reported after the operation is complete.\n"
" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
" and defaults to user quota (multiple types can be specified).\n"
"\n"));
}
static void
disable_help(void)
{
printf(_(
"\n"
" disable quota enforcement on a filesystem\n"
"\n"
" If a filesystem is mounted and is currently enforcing quota, this\n"
" provides a mechanism to switch off the enforcement, but continue to\n"
" perform used space (and used inodes) accounting.\n"
" The affected quota type is -g (groups), -p (projects) or -u (users).\n"
"\n"));
}
static void
remove_help(void)
{
printf(_(
"\n"
" remove any space being used by the quota subsystem\n"
"\n"
" Once quota has been switched 'off' on a filesystem, the space that\n"
" was allocated to holding quota metadata can be freed via this command.\n"
" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
" and defaults to user quota (multiple types can be specified).\n"
"\n"));
}
static void
state_qfilestat(
FILE *fp,
struct fs_path *mount,
uint type,
struct fs_qfilestatv *qfs,
int accounting,
int enforcing)
{
fprintf(fp, _("%s quota state on %s (%s)\n"), type_to_string(type),
mount->fs_dir, mount->fs_name);
fprintf(fp, _(" Accounting: %s\n"), accounting ? _("ON") : _("OFF"));
fprintf(fp, _(" Enforcement: %s\n"), enforcing ? _("ON") : _("OFF"));
if (qfs->qfs_ino != (__u64) -1)
fprintf(fp, _(" Inode: #%llu (%llu blocks, %lu extents)\n"),
(unsigned long long)qfs->qfs_ino,
(unsigned long long)qfs->qfs_nblks,
(unsigned long)qfs->qfs_nextents);
else
fprintf(fp, _(" Inode: N/A\n"));
}
static void
state_timelimit(
FILE *fp,
uint form,
uint32_t timelimit)
{
fprintf(fp, _("%s grace time: %s\n"),
form_to_string(form),
time_to_string(timelimit, VERBOSE_FLAG | ABSOLUTE_FLAG));
}
/*
* fs_quota_stat holds a subset of fs_quota_statv; this copies
* the smaller into the larger, leaving any not-present fields
* empty. This is so the same reporting function can be used
* for both XFS_GETQSTAT and XFS_GETQSTATV results.
*/
static void
state_stat_to_statv(
struct fs_quota_stat *s,
struct fs_quota_statv *sv)
{
memset(sv, 0, sizeof(struct fs_quota_statv));
/* shared information */
sv->qs_version = s->qs_version;
sv->qs_flags = s->qs_flags;
sv->qs_incoredqs = s->qs_incoredqs;
sv->qs_btimelimit = s->qs_btimelimit;
sv->qs_itimelimit = s->qs_itimelimit;
sv->qs_rtbtimelimit = s->qs_rtbtimelimit;
sv->qs_bwarnlimit = s->qs_bwarnlimit;
sv->qs_iwarnlimit = s->qs_iwarnlimit;
/* Always room for uquota */
sv->qs_uquota.qfs_ino = s->qs_uquota.qfs_ino;
sv->qs_uquota.qfs_nblks = s->qs_uquota.qfs_nblks;
sv->qs_uquota.qfs_nextents = s->qs_uquota.qfs_nextents;
/*
* If we are here, XFS_GETQSTATV failed and XFS_GETQSTAT passed;
* that is a very strong hint that we're on a kernel which predates
* the on-disk pquota inode; both were added in v3.12. So, we do
* some tricksy determination here.
* gs_gquota may hold either group quota inode info, or project
* quota if that is used instead; which one it actually holds depends
* on the quota flags. (If neither is set, neither is used)
*/
if (s->qs_flags & XFS_QUOTA_GDQ_ACCT) {
/* gs_gquota holds group quota info */
sv->qs_gquota.qfs_ino = s->qs_gquota.qfs_ino;
sv->qs_gquota.qfs_nblks = s->qs_gquota.qfs_nblks;
sv->qs_gquota.qfs_nextents = s->qs_gquota.qfs_nextents;
} else if (s->qs_flags & XFS_QUOTA_PDQ_ACCT) {
/* gs_gquota actually holds project quota info */
sv->qs_pquota.qfs_ino = s->qs_gquota.qfs_ino;
sv->qs_pquota.qfs_nblks = s->qs_gquota.qfs_nblks;
sv->qs_pquota.qfs_nextents = s->qs_gquota.qfs_nextents;
}
}
static void
state_quotafile_mount(
FILE *fp,
uint type,
struct fs_path *mount,
uint flags)
{
struct fs_quota_stat s;
struct fs_quota_statv sv;
char *dev = mount->fs_name;
sv.qs_version = FS_QSTATV_VERSION1;
if (xfsquotactl(XFS_GETQSTATV, dev, type, 0, (void *)&sv) < 0) {
if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, (void *)&s) < 0) {
if (flags & VERBOSE_FLAG)
fprintf(fp,
_("%s quota are not enabled on %s\n"),
type_to_string(type), dev);
return;
}
state_stat_to_statv(&s, &sv);
}
if (type & XFS_USER_QUOTA)
state_qfilestat(fp, mount, XFS_USER_QUOTA, &sv.qs_uquota,
sv.qs_flags & XFS_QUOTA_UDQ_ACCT,
sv.qs_flags & XFS_QUOTA_UDQ_ENFD);
if (type & XFS_GROUP_QUOTA)
state_qfilestat(fp, mount, XFS_GROUP_QUOTA, &sv.qs_gquota,
sv.qs_flags & XFS_QUOTA_GDQ_ACCT,
sv.qs_flags & XFS_QUOTA_GDQ_ENFD);
if (type & XFS_PROJ_QUOTA)
state_qfilestat(fp, mount, XFS_PROJ_QUOTA, &sv.qs_pquota,
sv.qs_flags & XFS_QUOTA_PDQ_ACCT,
sv.qs_flags & XFS_QUOTA_PDQ_ENFD);
state_timelimit(fp, XFS_BLOCK_QUOTA, sv.qs_btimelimit);
state_timelimit(fp, XFS_INODE_QUOTA, sv.qs_itimelimit);
state_timelimit(fp, XFS_RTBLOCK_QUOTA, sv.qs_rtbtimelimit);
}
static void
state_quotafile(
FILE *fp,
uint type,
char *dir,
uint flags)
{
fs_cursor_t cursor;
fs_path_t *mount;
fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
while ((mount = fs_cursor_next_entry(&cursor)))
state_quotafile_mount(fp, type, mount, flags);
}
static int
state_f(
int argc,
char **argv)
{
FILE *fp = NULL;
char *fname = NULL;
int c, flags = 0, type = 0;
while ((c = getopt(argc, argv, "af:gpuv")) != EOF) {
switch (c) {
case 'a':
flags |= ALL_MOUNTS_FLAG;
break;
case 'f':
fname = optarg;
break;
case 'g':
type |= XFS_GROUP_QUOTA;
break;
case 'p':
type |= XFS_PROJ_QUOTA;
break;
case 'u':
type |= XFS_USER_QUOTA;
break;
case 'v':
flags |= VERBOSE_FLAG;
break;
default:
return command_usage(&state_cmd);
}
}
if (argc != optind)
return command_usage(&state_cmd);
if ((fp = fopen_write_secure(fname)) == NULL)
return 0;
if (!type)
type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA;
if (flags & ALL_MOUNTS_FLAG)
state_quotafile(fp, type, NULL, flags);
else if (fs_path && fs_path->fs_flags & FS_MOUNT_POINT)
state_quotafile(fp, type, fs_path->fs_dir, flags);
if (fname)
fclose(fp);
return 0;
}
static void
enable_enforcement(
char *dir,
uint type,
uint qflags,
uint flags)
{
fs_path_t *mount;
mount = fs_table_lookup(dir, FS_MOUNT_POINT);
if (!mount) {
exitcode = 1;
fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
return;
}
dir = mount->fs_name;
if (xfsquotactl(XFS_QUOTAON, dir, type, 0, (void *)&qflags) < 0)
perror("XFS_QUOTAON");
else if (flags & VERBOSE_FLAG)
state_quotafile_mount(stdout, type, mount, flags);
}
static void
disable_enforcement(
char *dir,
uint type,
uint qflags,
uint flags)
{
fs_path_t *mount;
mount = fs_table_lookup(dir, FS_MOUNT_POINT);
if (!mount) {
exitcode = 1;
fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
return;
}
dir = mount->fs_name;
if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0)
perror("XFS_QUOTAOFF");
else if (flags & VERBOSE_FLAG)
state_quotafile_mount(stdout, type, mount, flags);
}
static void
quotaoff(
char *dir,
uint type,
uint qflags,
uint flags)
{
fs_path_t *mount;
mount = fs_table_lookup(dir, FS_MOUNT_POINT);
if (!mount) {
exitcode = 1;
fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
return;
}
dir = mount->fs_name;
if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0)
perror("XFS_QUOTAOFF");
else if (flags & VERBOSE_FLAG)
state_quotafile_mount(stdout, type, mount, flags);
}
static int
remove_qtype_extents(
char *dir,
uint type)
{
int error = 0;
if ((error = xfsquotactl(XFS_QUOTARM, dir, type, 0, (void *)&type)) < 0)
perror("XFS_QUOTARM");
return error;
}
static void
remove_extents(
char *dir,
uint type,
uint flags)
{
fs_path_t *mount;
mount = fs_table_lookup(dir, FS_MOUNT_POINT);
if (!mount) {
exitcode = 1;
fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
return;
}
dir = mount->fs_name;
if (type & XFS_USER_QUOTA) {
if (remove_qtype_extents(dir, XFS_USER_QUOTA) < 0)
return;
}
if (type & XFS_GROUP_QUOTA) {
if (remove_qtype_extents(dir, XFS_GROUP_QUOTA) < 0)
return;
} else if (type & XFS_PROJ_QUOTA) {
if (remove_qtype_extents(dir, XFS_PROJ_QUOTA) < 0)
return;
}
if (flags & VERBOSE_FLAG)
state_quotafile_mount(stdout, type, mount, flags);
}
static int
enable_f(
int argc,
char **argv)
{
int c, flags = 0, qflags = 0, type = 0;
while ((c = getopt(argc, argv, "gpuv")) != EOF) {
switch (c) {
case 'g':
type |= XFS_GROUP_QUOTA;
qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
break;
case 'p':
type |= XFS_PROJ_QUOTA;
qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
break;
case 'u':
type |= XFS_USER_QUOTA;
qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
break;
case 'v':
flags |= VERBOSE_FLAG;
break;
default:
return command_usage(&enable_cmd);
}
}
if (argc != optind)
return command_usage(&enable_cmd);
if (!type) {
type |= XFS_USER_QUOTA;
qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
}
if (fs_path->fs_flags & FS_MOUNT_POINT)
enable_enforcement(fs_path->fs_dir, type, qflags, flags);
return 0;
}
static int
disable_f(
int argc,
char **argv)
{
int c, flags = 0, qflags = 0, type = 0;
while ((c = getopt(argc, argv, "gpuv")) != EOF) {
switch (c) {
case 'g':
type |= XFS_GROUP_QUOTA;
qflags |= XFS_QUOTA_GDQ_ENFD;
break;
case 'p':
type |= XFS_PROJ_QUOTA;
qflags |= XFS_QUOTA_PDQ_ENFD;
break;
case 'u':
type |= XFS_USER_QUOTA;
qflags |= XFS_QUOTA_UDQ_ENFD;
break;
case 'v':
flags |= VERBOSE_FLAG;
break;
default:
return command_usage(&disable_cmd);
}
}
if (argc != optind)
return command_usage(&disable_cmd);
if (!type) {
type |= XFS_USER_QUOTA;
qflags |= XFS_QUOTA_UDQ_ENFD;
}
if (fs_path->fs_flags & FS_MOUNT_POINT)
disable_enforcement(fs_path->fs_dir, type, qflags, flags);
return 0;
}
static int
off_f(
int argc,
char **argv)
{
int c, flags = 0, qflags = 0, type = 0;
while ((c = getopt(argc, argv, "gpuv")) != EOF) {
switch (c) {
case 'g':
type |= XFS_GROUP_QUOTA;
qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
break;
case 'p':
type |= XFS_PROJ_QUOTA;
qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
break;
case 'u':
type |= XFS_USER_QUOTA;
qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
break;
case 'v':
flags |= VERBOSE_FLAG;
break;
default:
return command_usage(&off_cmd);
}
}
if (argc != optind)
return command_usage(&off_cmd);
if (!type) {
type |= XFS_USER_QUOTA;
qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
}
if (fs_path->fs_flags & FS_MOUNT_POINT)
quotaoff(fs_path->fs_dir, type, qflags, flags);
return 0;
}
static int
remove_f(
int argc,
char **argv)
{
int c, flags = 0, type = 0;
while ((c = getopt(argc, argv, "gpuv")) != EOF) {
switch (c) {
case 'g':
type |= XFS_GROUP_QUOTA;
break;
case 'p':
type |= XFS_PROJ_QUOTA;
break;
case 'u':
type |= XFS_USER_QUOTA;
break;
case 'v':
flags |= VERBOSE_FLAG;
break;
default:
return command_usage(&remove_cmd);
}
}
if (argc != optind)
return command_usage(&remove_cmd);
if (!type) {
type |= XFS_USER_QUOTA;
}
if (fs_path->fs_flags & FS_MOUNT_POINT)
remove_extents(fs_path->fs_dir, type, flags);
return 0;
}
void
state_init(void)
{
off_cmd.name = "off";
off_cmd.cfunc = off_f;
off_cmd.argmin = 0;
off_cmd.argmax = -1;
off_cmd.args = _("[-gpu] [-v]");
off_cmd.oneline = _("permanently switch quota off for a path");
off_cmd.help = off_help;
state_cmd.name = "state";
state_cmd.cfunc = state_f;
state_cmd.argmin = 0;
state_cmd.argmax = -1;
state_cmd.args = _("[-gpu] [-a] [-v] [-f file]");
state_cmd.oneline = _("get overall quota state information");
state_cmd.help = state_help;
state_cmd.flags = CMD_FLAG_FOREIGN_OK;
enable_cmd.name = "enable";
enable_cmd.cfunc = enable_f;
enable_cmd.argmin = 0;
enable_cmd.argmax = -1;
enable_cmd.args = _("[-gpu] [-v]");
enable_cmd.oneline = _("enable quota enforcement");
enable_cmd.help = enable_help;
disable_cmd.name = "disable";
disable_cmd.cfunc = disable_f;
disable_cmd.argmin = 0;
disable_cmd.argmax = -1;
disable_cmd.args = _("[-gpu] [-v]");
disable_cmd.oneline = _("disable quota enforcement");
disable_cmd.help = disable_help;
remove_cmd.name = "remove";
remove_cmd.cfunc = remove_f;
remove_cmd.argmin = 0;
remove_cmd.argmax = -1;
remove_cmd.args = _("[-gpu] [-v]");
remove_cmd.oneline = _("remove quota extents from a filesystem");
remove_cmd.help = remove_help;
if (expert) {
add_command(&off_cmd);
add_command(&state_cmd);
add_command(&enable_cmd);
add_command(&disable_cmd);
add_command(&remove_cmd);
}
}