mirror of
https://github.com/versity/scoutfs.git
synced 2025-12-23 05:25:18 +00:00
Add quota support to utils
Add scoutfs cli commands for managing quotas and add its persistent structures to the print command. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -413,7 +413,7 @@ Initial format version.
|
||||
.TP
|
||||
.B 2
|
||||
Added retention mode by setting the retention attribute. Added the
|
||||
project ID inode attribute.
|
||||
project ID inode attribute. Added quota rules and enforcement.
|
||||
.RE
|
||||
|
||||
.SH CORRUPTION DETECTION
|
||||
|
||||
@@ -80,6 +80,24 @@ static void print_orphan(struct scoutfs_key *key, void *val, int val_len)
|
||||
}
|
||||
|
||||
|
||||
#define SQR_FMT "[%u %llu,%u,%x %llu,%u,%x %llu,%u,%x %u %llu %x]"
|
||||
|
||||
#define SQR_ARGS(r) \
|
||||
(r)->prio, \
|
||||
le64_to_cpu((r)->name_val[0]), (r)->name_source[0], (r)->name_flags[0], \
|
||||
le64_to_cpu((r)->name_val[1]), (r)->name_source[1], (r)->name_flags[1], \
|
||||
le64_to_cpu((r)->name_val[2]), (r)->name_source[2], (r)->name_flags[2], \
|
||||
(r)->op, le64_to_cpu((r)->limit), (r)->rule_flags
|
||||
|
||||
static void print_quota(struct scoutfs_key *key, void *val, int val_len)
|
||||
{
|
||||
struct scoutfs_quota_rule_val *rv = val;
|
||||
|
||||
printf(" quota rule: hash 0x%016llx coll_nr %llu\n"
|
||||
" "SQR_FMT"\n",
|
||||
le64_to_cpu(key->skqr_hash), le64_to_cpu(key->skqr_coll_nr), SQR_ARGS(rv));
|
||||
}
|
||||
|
||||
static void print_xattr_totl(struct scoutfs_key *key, void *val, int val_len)
|
||||
{
|
||||
struct scoutfs_xattr_totl_val *tval = val;
|
||||
@@ -178,6 +196,9 @@ static print_func_t find_printer(u8 zone, u8 type)
|
||||
return print_orphan;
|
||||
}
|
||||
|
||||
if (zone == SCOUTFS_QUOTA_ZONE)
|
||||
return print_quota;
|
||||
|
||||
if (zone == SCOUTFS_XATTR_TOTL_ZONE)
|
||||
return print_xattr_totl;
|
||||
|
||||
|
||||
547
utils/src/quota.c
Normal file
547
utils/src/quota.c
Normal file
@@ -0,0 +1,547 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <argp.h>
|
||||
|
||||
#include "sparse.h"
|
||||
#include "parse.h"
|
||||
#include "util.h"
|
||||
#include "format.h"
|
||||
#include "ioctl.h"
|
||||
#include "cmd.h"
|
||||
#include "util.h"
|
||||
#include "key.h"
|
||||
|
||||
static char opc[] = {
|
||||
[SQ_OP_DATA] = 'D',
|
||||
[SQ_OP_INODE] = 'I',
|
||||
};
|
||||
|
||||
static char nsc[] = {
|
||||
[SQ_NS_LITERAL] = 'L',
|
||||
[SQ_NS_PROJ] = 'P',
|
||||
[SQ_NS_UID] = 'U',
|
||||
[SQ_NS_GID] = 'G',
|
||||
};
|
||||
|
||||
static void printf_rule(struct scoutfs_ioctl_quota_rule *irule)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* priority: [0-9]+ */
|
||||
printf("%3u ", irule->prio);
|
||||
|
||||
/* totl name: ([0-9]+,[LPUG-]+,[S-]+){3} */
|
||||
for (i = 0; i < array_size(irule->name_val); i++) {
|
||||
|
||||
printf("%llu,%c,%c ",
|
||||
irule->name_val[i],
|
||||
nsc[irule->name_source[i]],
|
||||
(irule->name_flags[i] & SQ_NF_SELECT) ? 'S' : '-');
|
||||
}
|
||||
|
||||
/* op: [ID], limit: [0-9]+, flags [C-] */
|
||||
printf("%c %llu %c\n",
|
||||
opc[irule->op], irule->limit, (irule->rule_flags & SQ_RF_TOTL_COUNT) ? 'C' : '-');
|
||||
}
|
||||
|
||||
static int parse_rule(struct scoutfs_ioctl_quota_rule *irule, char *str)
|
||||
{
|
||||
char ns[3];
|
||||
char nf[3];
|
||||
char rf;
|
||||
char op;
|
||||
int ret;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
memset(irule, 0, sizeof(struct scoutfs_ioctl_quota_rule));
|
||||
|
||||
ret = sscanf(str, " %hhu %llu,%c,%c %llu,%c,%c %llu,%c,%c %c %llu %c",
|
||||
&irule->prio, &irule->name_val[0], &ns[0], &nf[0], &irule->name_val[1],
|
||||
&ns[1], &nf[1], &irule->name_val[2], &ns[2], &nf[2], &op, &irule->limit,
|
||||
&rf);
|
||||
if (ret != 13) {
|
||||
printf("invalid rule, missing fields: %s\n", str);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < array_size(irule->name_val); i++) {
|
||||
irule->name_source[i] = SQ_NS__NR;
|
||||
|
||||
for (j = 0; j < array_size(nsc); j++) {
|
||||
if (ns[i] == nsc[j]) {
|
||||
irule->name_source[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irule->name_source[i] == SQ_NS__NR) {
|
||||
printf("invalid name source '%c' in name #%u in rule:\n\t%s\n",
|
||||
ns[i], i + 1, str);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
irule->name_flags[i] = nf[i] == '-' ? 0 :
|
||||
nf[i] == 'S' ? SQ_NF_SELECT :
|
||||
SQ_NF__UNKNOWN;
|
||||
if (irule->name_flags[i] == SQ_NF__UNKNOWN) {
|
||||
printf("invalid name flags '%c' in name #%u in rule:\n\t%s\n",
|
||||
nf[i], i + 1, str);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
irule->op = SQ_NS__NR;
|
||||
for (i = 0; i < array_size(opc); i++) {
|
||||
if (op == opc[i]) {
|
||||
irule->op = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irule->op == SQ_NS__NR) {
|
||||
printf("invalid op '%c' in rule:\n\t%s\n", op, str);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
irule->rule_flags = rf == '-' ? 0 : rf == 'C' ? SQ_RF_TOTL_COUNT : SQ_RF__UNKNOWN;
|
||||
if (irule->rule_flags == SQ_RF__UNKNOWN) {
|
||||
printf("invalid rule flags '%c' in rule:\n\t%s\n", rf, str);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
struct mod_args {
|
||||
char *path;
|
||||
char *rule_str;
|
||||
bool is_add;
|
||||
};
|
||||
|
||||
static int do_mod(struct mod_args *args)
|
||||
{
|
||||
struct scoutfs_ioctl_quota_rule irule;
|
||||
unsigned int cmd;
|
||||
int fd = -1;
|
||||
int ret;
|
||||
|
||||
memset(&irule, 0, sizeof(irule));
|
||||
|
||||
ret = parse_rule(&irule, args->rule_str);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
fd = get_path(args->path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
cmd = args->is_add ? SCOUTFS_IOC_ADD_QUOTA_RULE : SCOUTFS_IOC_DEL_QUOTA_RULE;
|
||||
ret = ioctl(fd, cmd, &irule);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "MOD_QUOTA_RULE ioctl failed: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_mod_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct mod_args *args = state->input;
|
||||
|
||||
switch (key) {
|
||||
case 'p':
|
||||
args->path = strdup_or_error(state, arg);
|
||||
break;
|
||||
case 'r':
|
||||
args->rule_str = strdup_or_error(state, arg);
|
||||
break;
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->path)
|
||||
argp_error(state, "must provide file path");
|
||||
if (!args->rule_str)
|
||||
argp_error(state, "must provide rule string");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct argp_option add_options[] = {
|
||||
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
|
||||
{ "rule", 'r', "RULE_STRING", 0, "Rule string"},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct argp add_argp = {
|
||||
add_options,
|
||||
parse_mod_opt,
|
||||
"",
|
||||
"Add quota rule"
|
||||
};
|
||||
|
||||
static int add_cmd(int argc, char **argv)
|
||||
{
|
||||
struct mod_args args = { .is_add = true, };
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&add_argp, argc, argv, 0, NULL, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_mod(&args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) add_ctor(void)
|
||||
{
|
||||
cmd_register_argp("quota-add", &add_argp, GROUP_CORE, add_cmd);
|
||||
}
|
||||
|
||||
static struct argp_option del_options[] = {
|
||||
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
|
||||
{ "rule", 'r', "RULE_STRING", 0, "Rule string"},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct argp del_argp = {
|
||||
del_options,
|
||||
parse_mod_opt,
|
||||
"",
|
||||
"Delete quota rule"
|
||||
};
|
||||
|
||||
static int del_cmd(int argc, char **argv)
|
||||
{
|
||||
struct mod_args args = { .is_add = false };
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&del_argp, argc, argv, 0, NULL, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_mod(&args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) del_ctor(void)
|
||||
{
|
||||
cmd_register_argp("quota-del", &del_argp, GROUP_CORE, del_cmd);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
struct bulk_args {
|
||||
char *path;
|
||||
bool unsorted;
|
||||
};
|
||||
|
||||
typedef int (*bulk_in_fn)(int fd, struct scoutfs_ioctl_quota_rule *irules, size_t nr,
|
||||
void *in_args);
|
||||
typedef int (*bulk_out_fn)(int fd, struct scoutfs_ioctl_quota_rule *irule, void *out_args);
|
||||
|
||||
static int cmp_irules(const struct scoutfs_ioctl_quota_rule *a,
|
||||
const struct scoutfs_ioctl_quota_rule *b)
|
||||
{
|
||||
return scoutfs_cmp(a->prio, b->prio) ?:
|
||||
scoutfs_cmp(a->name_val[0], b->name_val[0]) ?:
|
||||
scoutfs_cmp(a->name_source[0], b->name_source[0]) ?:
|
||||
scoutfs_cmp(a->name_flags[0], b->name_flags[0]) ?:
|
||||
scoutfs_cmp(a->name_val[1], b->name_val[1]) ?:
|
||||
scoutfs_cmp(a->name_source[1], b->name_source[1]) ?:
|
||||
scoutfs_cmp(a->name_flags[1], b->name_flags[1]) ?:
|
||||
scoutfs_cmp(a->name_val[2], b->name_val[2]) ?:
|
||||
scoutfs_cmp(a->name_source[2], b->name_source[2]) ?:
|
||||
scoutfs_cmp(a->name_flags[2], b->name_flags[2]) ?:
|
||||
scoutfs_cmp(a->op, b->op) ?:
|
||||
scoutfs_cmp(a->limit, b->limit) ?:
|
||||
scoutfs_cmp(a->rule_flags, b->rule_flags);
|
||||
}
|
||||
|
||||
static int compar_irules(const void *a, const void *b)
|
||||
{
|
||||
return -cmp_irules(a, b);
|
||||
}
|
||||
|
||||
static int do_bulk(struct bulk_args *args, bulk_in_fn in_fn, void *in_args,
|
||||
bulk_out_fn out_fn, void *out_args)
|
||||
{
|
||||
struct scoutfs_ioctl_quota_rule *irules = NULL;
|
||||
size_t alloced = 0;
|
||||
size_t nr = 0;
|
||||
size_t batch;
|
||||
size_t i;
|
||||
int fd = -1;
|
||||
int ret;
|
||||
|
||||
fd = get_path(args->path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
for (;;) {
|
||||
if (nr == alloced) {
|
||||
alloced += 1024;
|
||||
irules = realloc(irules, alloced * sizeof(irules[0]));
|
||||
if (!irules) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "memory allocation failed: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = in_fn(fd, &irules[nr], alloced - nr, in_args);
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
batch = ret;
|
||||
|
||||
if (args->unsorted) {
|
||||
for (i = 0; i < batch; i++) {
|
||||
ret = out_fn(fd, &irules[nr + i], out_args);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
nr += batch;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args->unsorted) {
|
||||
qsort(irules, nr, sizeof(irules[0]), compar_irules);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
ret = out_fn(fd, &irules[i], out_args);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (irules)
|
||||
free(irules);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
/* maintain iterator in gqr between calls */
|
||||
static int get_ioctl_in_fn(int fd, struct scoutfs_ioctl_quota_rule *irules, size_t nr,
|
||||
void *in_args)
|
||||
{
|
||||
struct scoutfs_ioctl_get_quota_rules *gqr = in_args;
|
||||
int ret;
|
||||
|
||||
gqr->rules_ptr = (intptr_t)irules;
|
||||
gqr->rules_nr = nr;
|
||||
|
||||
ret = ioctl(fd, SCOUTFS_IOC_GET_QUOTA_RULES, gqr);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "GET_QUOTA_RULES ioctl failed: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_stdin_in_fn(int fd, struct scoutfs_ioctl_quota_rule *irules, size_t nr,
|
||||
void *in_args)
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
ret = getline(&line, &size, stdin);
|
||||
if (ret < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
ret = -errno;
|
||||
fprintf(stderr, "error reading rules: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = parse_rule(&irules[0], line);
|
||||
if (ret == 0)
|
||||
ret = 1;
|
||||
|
||||
free(line);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct mod_ioctl_args {
|
||||
unsigned int cmd;
|
||||
char *which;
|
||||
};
|
||||
|
||||
static int mod_ioctl_out_fn(int fd, struct scoutfs_ioctl_quota_rule *irule, void *out_args)
|
||||
{
|
||||
struct mod_ioctl_args *args = out_args;
|
||||
int ret;
|
||||
|
||||
ret = ioctl(fd, args->cmd, irule);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
printf("Failed to %s following rule:\n ", args->which);
|
||||
printf_rule(irule);
|
||||
fprintf(stderr, "Error: %s (%d)\n", strerror(-ret), -ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int print_out_fn(int fd, struct scoutfs_ioctl_quota_rule *irule, void *out_args)
|
||||
{
|
||||
printf_rule(irule);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
static int parse_bulk_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct bulk_args *args = state->input;
|
||||
|
||||
switch (key) {
|
||||
case 'p':
|
||||
args->path = strdup_or_error(state, arg);
|
||||
break;
|
||||
case 'U':
|
||||
args->unsorted = true;
|
||||
break;
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->path)
|
||||
argp_error(state, "must provide file path");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct argp_option bulk_options[] = {
|
||||
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
|
||||
{ "unsorted", 'U', NULL, 0, "Process rules in unsorted filesystem storage order"},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct argp list_argp = {
|
||||
bulk_options,
|
||||
parse_bulk_opt,
|
||||
"",
|
||||
"List quota rules"
|
||||
};
|
||||
|
||||
static int list_cmd(int argc, char **argv)
|
||||
{
|
||||
struct scoutfs_ioctl_get_quota_rules gqr = {{0,}};
|
||||
struct bulk_args args = {NULL};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&list_argp, argc, argv, 0, NULL, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_bulk(&args, get_ioctl_in_fn, &gqr, print_out_fn, NULL);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) list_ctor(void)
|
||||
{
|
||||
cmd_register_argp("quota-list", &list_argp, GROUP_CORE, list_cmd);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
static struct argp wipe_argp = {
|
||||
bulk_options,
|
||||
parse_bulk_opt,
|
||||
"",
|
||||
"Delete all quota rules"
|
||||
};
|
||||
|
||||
static int wipe_cmd(int argc, char **argv)
|
||||
{
|
||||
struct bulk_args args = {NULL};
|
||||
struct scoutfs_ioctl_get_quota_rules gqr = {{0,}};
|
||||
struct mod_ioctl_args out_args = {
|
||||
.cmd = SCOUTFS_IOC_DEL_QUOTA_RULE,
|
||||
.which = "delete",
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&wipe_argp, argc, argv, 0, NULL, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_bulk(&args, get_ioctl_in_fn, &gqr, mod_ioctl_out_fn, &out_args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) wipe_ctor(void)
|
||||
{
|
||||
cmd_register_argp("quota-wipe", &wipe_argp, GROUP_CORE, wipe_cmd);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
static struct argp restore_argp = {
|
||||
bulk_options,
|
||||
parse_bulk_opt,
|
||||
"",
|
||||
"Restore quota rules from list output on stdin"
|
||||
};
|
||||
|
||||
static int restore_cmd(int argc, char **argv)
|
||||
{
|
||||
struct bulk_args args = {NULL};
|
||||
struct mod_ioctl_args out_args = {
|
||||
.cmd = SCOUTFS_IOC_ADD_QUOTA_RULE,
|
||||
.which = "add",
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&restore_argp, argc, argv, 0, NULL, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_bulk(&args, parse_stdin_in_fn, NULL, mod_ioctl_out_fn, &out_args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) restore_ctor(void)
|
||||
{
|
||||
cmd_register_argp("quota-restore", &restore_argp, GROUP_CORE, restore_cmd);
|
||||
}
|
||||
Reference in New Issue
Block a user