]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/state.c
xfs_quota: command error message improvement
[thirdparty/xfsprogs-dev.git] / quota / state.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
5aead01d 2/*
da23017d
NS
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5aead01d 5 */
29647c8d 6#include <stdbool.h>
6b803e5a 7#include "command.h"
5aead01d
NS
8#include "init.h"
9#include "quota.h"
10
11static cmdinfo_t off_cmd;
12static cmdinfo_t state_cmd;
13static cmdinfo_t enable_cmd;
14static cmdinfo_t disable_cmd;
15static cmdinfo_t remove_cmd;
16
17static void
18off_help(void)
19{
20 printf(_(
21"\n"
22" turn filesystem quota off, both accounting and enforcement\n"
23"\n"
24" Example:\n"
25" 'off -uv' (switch off user quota on the current filesystem)\n"
26" This command is the equivalent of the traditional quotaoff command,\n"
27" which disables quota completely on a mounted filesystem.\n"
28" Note that there is no 'on' command - for XFS filesystems (with the\n"
29" exception of the root filesystem on IRIX) quota can only be enabled\n"
30" at mount time, through the use of one of the quota mount options.\n"
31"\n"
32" The state command is useful for displaying the current state. Using\n"
33" the -v (verbose) option with the 'off' command will display the quota\n"
34" state for the affected filesystem once the operation is complete.\n"
35" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
36" and defaults to user quota (multiple types can be specified).\n"
37"\n"));
38}
39
40static void
41state_help(void)
42{
43 printf(_(
44"\n"
45" query the state of quota on the current filesystem\n"
46"\n"
47" This is a verbose status command, reporting whether or not accounting\n"
48" and/or enforcement are enabled for a filesystem, which inodes are in\n"
49" use as the quota state inodes, and how many extents and blocks are\n"
50" presently being used to hold that information.\n"
51" The quota type is specified via -g (groups), -p (projects) or -u (users)\n"
52" and defaults to user quota (multiple types can be specified).\n"
53"\n"));
54}
55
56static void
57enable_help(void)
58{
59 printf(_(
60"\n"
61" enable quota enforcement on a filesystem\n"
62"\n"
63" If a filesystem is mounted and has quota accounting enabled, but not\n"
64" quota enforcement, enforcement can be enabled with this command.\n"
65" With the -v (verbose) option, the status of the filesystem will be\n"
66" reported after the operation is complete.\n"
67" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
68" and defaults to user quota (multiple types can be specified).\n"
69"\n"));
70}
71
72static void
73disable_help(void)
74{
75 printf(_(
76"\n"
77" disable quota enforcement on a filesystem\n"
78"\n"
79" If a filesystem is mounted and is currently enforcing quota, this\n"
80" provides a mechanism to switch off the enforcement, but continue to\n"
81" perform used space (and used inodes) accounting.\n"
82" The affected quota type is -g (groups), -p (projects) or -u (users).\n"
83"\n"));
84}
85
86static void
87remove_help(void)
88{
89 printf(_(
90"\n"
91" remove any space being used by the quota subsystem\n"
92"\n"
93" Once quota has been switched 'off' on a filesystem, the space that\n"
94" was allocated to holding quota metadata can be freed via this command.\n"
95" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
96" and defaults to user quota (multiple types can be specified).\n"
97"\n"));
98}
99
100static void
101state_qfilestat(
bb92709f
ES
102 FILE *fp,
103 struct fs_path *mount,
104 uint type,
105 struct fs_qfilestatv *qfs,
106 int accounting,
107 int enforcing)
5aead01d
NS
108{
109 fprintf(fp, _("%s quota state on %s (%s)\n"), type_to_string(type),
110 mount->fs_dir, mount->fs_name);
111 fprintf(fp, _(" Accounting: %s\n"), accounting ? _("ON") : _("OFF"));
112 fprintf(fp, _(" Enforcement: %s\n"), enforcing ? _("ON") : _("OFF"));
06065b12
AE
113 if (qfs->qfs_ino != (__u64) -1)
114 fprintf(fp, _(" Inode: #%llu (%llu blocks, %lu extents)\n"),
115 (unsigned long long)qfs->qfs_ino,
116 (unsigned long long)qfs->qfs_nblks,
117 (unsigned long)qfs->qfs_nextents);
118 else
119 fprintf(fp, _(" Inode: N/A\n"));
5aead01d
NS
120}
121
122static void
123state_timelimit(
124 FILE *fp,
125 uint form,
14f8b681 126 uint32_t timelimit)
5aead01d
NS
127{
128 fprintf(fp, _("%s grace time: %s\n"),
129 form_to_string(form),
130 time_to_string(timelimit, VERBOSE_FLAG | ABSOLUTE_FLAG));
131}
132
bb92709f
ES
133/*
134 * fs_quota_stat holds a subset of fs_quota_statv; this copies
135 * the smaller into the larger, leaving any not-present fields
136 * empty. This is so the same reporting function can be used
137 * for both XFS_GETQSTAT and XFS_GETQSTATV results.
138 */
139static void
140state_stat_to_statv(
141 struct fs_quota_stat *s,
142 struct fs_quota_statv *sv)
143{
144 memset(sv, 0, sizeof(struct fs_quota_statv));
145
146 /* shared information */
147 sv->qs_version = s->qs_version;
148 sv->qs_flags = s->qs_flags;
149 sv->qs_incoredqs = s->qs_incoredqs;
150 sv->qs_btimelimit = s->qs_btimelimit;
151 sv->qs_itimelimit = s->qs_itimelimit;
152 sv->qs_rtbtimelimit = s->qs_rtbtimelimit;
153 sv->qs_bwarnlimit = s->qs_bwarnlimit;
154 sv->qs_iwarnlimit = s->qs_iwarnlimit;
155
156 /* Always room for uquota */
157 sv->qs_uquota.qfs_ino = s->qs_uquota.qfs_ino;
158 sv->qs_uquota.qfs_nblks = s->qs_uquota.qfs_nblks;
159 sv->qs_uquota.qfs_nextents = s->qs_uquota.qfs_nextents;
160
161 /*
162 * If we are here, XFS_GETQSTATV failed and XFS_GETQSTAT passed;
163 * that is a very strong hint that we're on a kernel which predates
164 * the on-disk pquota inode; both were added in v3.12. So, we do
165 * some tricksy determination here.
166 * gs_gquota may hold either group quota inode info, or project
167 * quota if that is used instead; which one it actually holds depends
168 * on the quota flags. (If neither is set, neither is used)
169 */
170 if (s->qs_flags & XFS_QUOTA_GDQ_ACCT) {
171 /* gs_gquota holds group quota info */
172 sv->qs_gquota.qfs_ino = s->qs_gquota.qfs_ino;
173 sv->qs_gquota.qfs_nblks = s->qs_gquota.qfs_nblks;
174 sv->qs_gquota.qfs_nextents = s->qs_gquota.qfs_nextents;
175 } else if (s->qs_flags & XFS_QUOTA_PDQ_ACCT) {
176 /* gs_gquota actually holds project quota info */
177 sv->qs_pquota.qfs_ino = s->qs_gquota.qfs_ino;
178 sv->qs_pquota.qfs_nblks = s->qs_gquota.qfs_nblks;
179 sv->qs_pquota.qfs_nextents = s->qs_gquota.qfs_nextents;
180 }
181}
182
5aead01d
NS
183static void
184state_quotafile_mount(
bb92709f
ES
185 FILE *fp,
186 uint type,
187 struct fs_path *mount,
188 uint flags)
5aead01d 189{
bb92709f
ES
190 struct fs_quota_stat s;
191 struct fs_quota_statv sv;
192 char *dev = mount->fs_name;
193
194 sv.qs_version = FS_QSTATV_VERSION1;
195
196 if (xfsquotactl(XFS_GETQSTATV, dev, type, 0, (void *)&sv) < 0) {
197 if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, (void *)&s) < 0) {
198 if (flags & VERBOSE_FLAG)
199 fprintf(fp,
200 _("%s quota are not enabled on %s\n"),
201 type_to_string(type), dev);
202 return;
203 }
204 state_stat_to_statv(&s, &sv);
5aead01d
NS
205 }
206
207 if (type & XFS_USER_QUOTA)
bb92709f
ES
208 state_qfilestat(fp, mount, XFS_USER_QUOTA, &sv.qs_uquota,
209 sv.qs_flags & XFS_QUOTA_UDQ_ACCT,
210 sv.qs_flags & XFS_QUOTA_UDQ_ENFD);
5aead01d 211 if (type & XFS_GROUP_QUOTA)
bb92709f
ES
212 state_qfilestat(fp, mount, XFS_GROUP_QUOTA, &sv.qs_gquota,
213 sv.qs_flags & XFS_QUOTA_GDQ_ACCT,
214 sv.qs_flags & XFS_QUOTA_GDQ_ENFD);
5aead01d 215 if (type & XFS_PROJ_QUOTA)
bb92709f
ES
216 state_qfilestat(fp, mount, XFS_PROJ_QUOTA, &sv.qs_pquota,
217 sv.qs_flags & XFS_QUOTA_PDQ_ACCT,
218 sv.qs_flags & XFS_QUOTA_PDQ_ENFD);
5aead01d 219
bb92709f
ES
220 state_timelimit(fp, XFS_BLOCK_QUOTA, sv.qs_btimelimit);
221 state_timelimit(fp, XFS_INODE_QUOTA, sv.qs_itimelimit);
222 state_timelimit(fp, XFS_RTBLOCK_QUOTA, sv.qs_rtbtimelimit);
5aead01d
NS
223}
224
225static void
226state_quotafile(
227 FILE *fp,
228 uint type,
229 char *dir,
230 uint flags)
231{
232 fs_cursor_t cursor;
233 fs_path_t *mount;
234
235 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
236 while ((mount = fs_cursor_next_entry(&cursor)))
237 state_quotafile_mount(fp, type, mount, flags);
238}
239
240static int
241state_f(
242 int argc,
243 char **argv)
244{
245 FILE *fp = NULL;
246 char *fname = NULL;
247 int c, flags = 0, type = 0;
248
249 while ((c = getopt(argc, argv, "af:gpuv")) != EOF) {
250 switch (c) {
251 case 'a':
252 flags |= ALL_MOUNTS_FLAG;
253 break;
254 case 'f':
255 fname = optarg;
256 break;
257 case 'g':
258 type |= XFS_GROUP_QUOTA;
259 break;
260 case 'p':
261 type |= XFS_PROJ_QUOTA;
262 break;
263 case 'u':
264 type |= XFS_USER_QUOTA;
265 break;
266 case 'v':
267 flags |= VERBOSE_FLAG;
268 break;
269 default:
270 return command_usage(&state_cmd);
271 }
272 }
273
274 if (argc != optind)
275 return command_usage(&state_cmd);
276
277 if ((fp = fopen_write_secure(fname)) == NULL)
278 return 0;
279
280 if (!type)
281 type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA;
282
fa13a00f
NS
283 if (flags & ALL_MOUNTS_FLAG)
284 state_quotafile(fp, type, NULL, flags);
04418c59 285 else if (fs_path && fs_path->fs_flags & FS_MOUNT_POINT)
fa13a00f 286 state_quotafile(fp, type, fs_path->fs_dir, flags);
5aead01d
NS
287
288 if (fname)
289 fclose(fp);
290 return 0;
291}
292
293static void
294enable_enforcement(
295 char *dir,
296 uint type,
297 uint qflags,
298 uint flags)
299{
300 fs_path_t *mount;
5aead01d
NS
301
302 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
303 if (!mount) {
e3210fd8 304 exitcode = 1;
5aead01d
NS
305 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
306 return;
307 }
308 dir = mount->fs_name;
ca42fa70
BD
309 if (xfsquotactl(XFS_QUOTAON, dir, type, 0, (void *)&qflags) < 0) {
310 if (errno == EEXIST)
311 fprintf(stderr,
312 _("Quota enforcement already enabled.\n"));
313 else if (errno == EINVAL || errno == ENOSYS)
314 fprintf(stderr,
315 _("Can't enable enforcement when quota off.\n"));
316 else
317 perror("XFS_QUOTAON");
318 }
5aead01d
NS
319 else if (flags & VERBOSE_FLAG)
320 state_quotafile_mount(stdout, type, mount, flags);
321}
322
323static void
324disable_enforcement(
325 char *dir,
326 uint type,
327 uint qflags,
328 uint flags)
329{
330 fs_path_t *mount;
5aead01d
NS
331
332 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
333 if (!mount) {
e3210fd8 334 exitcode = 1;
5aead01d
NS
335 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
336 return;
337 }
338 dir = mount->fs_name;
ca42fa70
BD
339 if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0) {
340 if (errno == EEXIST)
341 fprintf(stderr,
342 _("Quota enforcement already disabled.\n"));
343 else if (errno == EINVAL || errno == ENOSYS)
344 fprintf(stderr,
345 _("Can't disable enforcement when quota off.\n"));
346 else
347 perror("XFS_QUOTAOFF");
348 }
5aead01d
NS
349 else if (flags & VERBOSE_FLAG)
350 state_quotafile_mount(stdout, type, mount, flags);
351}
352
353static void
354quotaoff(
355 char *dir,
356 uint type,
357 uint qflags,
358 uint flags)
359{
360 fs_path_t *mount;
5aead01d
NS
361
362 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
363 if (!mount) {
e3210fd8 364 exitcode = 1;
5aead01d
NS
365 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
366 return;
367 }
368 dir = mount->fs_name;
ca42fa70
BD
369 if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0) {
370 if (errno == EEXIST || errno == ENOSYS)
371 fprintf(stderr, _("Quota already off.\n"));
372 else
373 perror("XFS_QUOTAOFF");
374 }
5aead01d
NS
375 else if (flags & VERBOSE_FLAG)
376 state_quotafile_mount(stdout, type, mount, flags);
377}
378
79ac1ae4
DD
379static int
380remove_qtype_extents(
381 char *dir,
382 uint type)
383{
384 int error = 0;
385
386 if ((error = xfsquotactl(XFS_QUOTARM, dir, type, 0, (void *)&type)) < 0)
387 perror("XFS_QUOTARM");
388 return error;
389}
390
5aead01d
NS
391static void
392remove_extents(
393 char *dir,
394 uint type,
5aead01d
NS
395 uint flags)
396{
397 fs_path_t *mount;
5aead01d
NS
398
399 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
400 if (!mount) {
e3210fd8 401 exitcode = 1;
5aead01d
NS
402 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
403 return;
404 }
405 dir = mount->fs_name;
79ac1ae4 406 if (type & XFS_USER_QUOTA) {
f8149110 407 if (remove_qtype_extents(dir, XFS_USER_QUOTA) < 0)
79ac1ae4
DD
408 return;
409 }
410 if (type & XFS_GROUP_QUOTA) {
f8149110 411 if (remove_qtype_extents(dir, XFS_GROUP_QUOTA) < 0)
79ac1ae4
DD
412 return;
413 } else if (type & XFS_PROJ_QUOTA) {
f8149110 414 if (remove_qtype_extents(dir, XFS_PROJ_QUOTA) < 0)
79ac1ae4
DD
415 return;
416 }
417 if (flags & VERBOSE_FLAG)
5aead01d
NS
418 state_quotafile_mount(stdout, type, mount, flags);
419}
420
421static int
422enable_f(
423 int argc,
424 char **argv)
425{
426 int c, flags = 0, qflags = 0, type = 0;
427
428 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
429 switch (c) {
430 case 'g':
431 type |= XFS_GROUP_QUOTA;
432 qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
433 break;
434 case 'p':
435 type |= XFS_PROJ_QUOTA;
436 qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
437 break;
438 case 'u':
439 type |= XFS_USER_QUOTA;
440 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
441 break;
442 case 'v':
443 flags |= VERBOSE_FLAG;
444 break;
445 default:
446 return command_usage(&enable_cmd);
447 }
448 }
449
450 if (argc != optind)
451 return command_usage(&enable_cmd);
452
79ac1ae4 453 if (!type) {
5aead01d
NS
454 type |= XFS_USER_QUOTA;
455 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
456 }
457
fa13a00f
NS
458 if (fs_path->fs_flags & FS_MOUNT_POINT)
459 enable_enforcement(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
460 return 0;
461}
462
463static int
464disable_f(
465 int argc,
466 char **argv)
467{
468 int c, flags = 0, qflags = 0, type = 0;
469
470 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
471 switch (c) {
472 case 'g':
473 type |= XFS_GROUP_QUOTA;
79ac1ae4 474 qflags |= XFS_QUOTA_GDQ_ENFD;
5aead01d
NS
475 break;
476 case 'p':
477 type |= XFS_PROJ_QUOTA;
79ac1ae4 478 qflags |= XFS_QUOTA_PDQ_ENFD;
5aead01d
NS
479 break;
480 case 'u':
481 type |= XFS_USER_QUOTA;
79ac1ae4 482 qflags |= XFS_QUOTA_UDQ_ENFD;
5aead01d
NS
483 break;
484 case 'v':
485 flags |= VERBOSE_FLAG;
486 break;
487 default:
488 return command_usage(&disable_cmd);
489 }
490 }
491
492 if (argc != optind)
493 return command_usage(&disable_cmd);
494
79ac1ae4 495 if (!type) {
5aead01d 496 type |= XFS_USER_QUOTA;
79ac1ae4 497 qflags |= XFS_QUOTA_UDQ_ENFD;
5aead01d
NS
498 }
499
fa13a00f
NS
500 if (fs_path->fs_flags & FS_MOUNT_POINT)
501 disable_enforcement(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
502 return 0;
503}
504
505static int
506off_f(
507 int argc,
508 char **argv)
509{
510 int c, flags = 0, qflags = 0, type = 0;
511
512 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
513 switch (c) {
514 case 'g':
515 type |= XFS_GROUP_QUOTA;
516 qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
517 break;
518 case 'p':
519 type |= XFS_PROJ_QUOTA;
520 qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
521 break;
522 case 'u':
523 type |= XFS_USER_QUOTA;
524 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
525 break;
526 case 'v':
527 flags |= VERBOSE_FLAG;
528 break;
529 default:
530 return command_usage(&off_cmd);
531 }
532 }
533
534 if (argc != optind)
535 return command_usage(&off_cmd);
536
79ac1ae4 537 if (!type) {
5aead01d
NS
538 type |= XFS_USER_QUOTA;
539 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
540 }
541
fa13a00f
NS
542 if (fs_path->fs_flags & FS_MOUNT_POINT)
543 quotaoff(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
544 return 0;
545}
546
547static int
548remove_f(
549 int argc,
550 char **argv)
551{
79ac1ae4 552 int c, flags = 0, type = 0;
5aead01d
NS
553
554 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
555 switch (c) {
556 case 'g':
557 type |= XFS_GROUP_QUOTA;
5aead01d
NS
558 break;
559 case 'p':
560 type |= XFS_PROJ_QUOTA;
5aead01d
NS
561 break;
562 case 'u':
563 type |= XFS_USER_QUOTA;
5aead01d
NS
564 break;
565 case 'v':
566 flags |= VERBOSE_FLAG;
567 break;
568 default:
569 return command_usage(&remove_cmd);
570 }
571 }
572
573 if (argc != optind)
574 return command_usage(&remove_cmd);
575
79ac1ae4 576 if (!type) {
5aead01d 577 type |= XFS_USER_QUOTA;
5aead01d
NS
578 }
579
fa13a00f 580 if (fs_path->fs_flags & FS_MOUNT_POINT)
79ac1ae4 581 remove_extents(fs_path->fs_dir, type, flags);
5aead01d
NS
582 return 0;
583}
584
585void
586state_init(void)
587{
ad765595 588 off_cmd.name = "off";
5aead01d
NS
589 off_cmd.cfunc = off_f;
590 off_cmd.argmin = 0;
591 off_cmd.argmax = -1;
592 off_cmd.args = _("[-gpu] [-v]");
593 off_cmd.oneline = _("permanently switch quota off for a path");
594 off_cmd.help = off_help;
595
ad765595 596 state_cmd.name = "state";
5aead01d
NS
597 state_cmd.cfunc = state_f;
598 state_cmd.argmin = 0;
599 state_cmd.argmax = -1;
62790d19 600 state_cmd.args = _("[-gpu] [-a] [-v] [-f file]");
5aead01d
NS
601 state_cmd.oneline = _("get overall quota state information");
602 state_cmd.help = state_help;
29647c8d 603 state_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 604
ad765595 605 enable_cmd.name = "enable";
5aead01d
NS
606 enable_cmd.cfunc = enable_f;
607 enable_cmd.argmin = 0;
608 enable_cmd.argmax = -1;
609 enable_cmd.args = _("[-gpu] [-v]");
610 enable_cmd.oneline = _("enable quota enforcement");
611 enable_cmd.help = enable_help;
612
ad765595 613 disable_cmd.name = "disable";
5aead01d
NS
614 disable_cmd.cfunc = disable_f;
615 disable_cmd.argmin = 0;
616 disable_cmd.argmax = -1;
617 disable_cmd.args = _("[-gpu] [-v]");
618 disable_cmd.oneline = _("disable quota enforcement");
619 disable_cmd.help = disable_help;
620
ad765595 621 remove_cmd.name = "remove";
5aead01d
NS
622 remove_cmd.cfunc = remove_f;
623 remove_cmd.argmin = 0;
624 remove_cmd.argmax = -1;
625 remove_cmd.args = _("[-gpu] [-v]");
626 remove_cmd.oneline = _("remove quota extents from a filesystem");
627 remove_cmd.help = remove_help;
628
629 if (expert) {
630 add_command(&off_cmd);
631 add_command(&state_cmd);
632 add_command(&enable_cmd);
633 add_command(&disable_cmd);
634 add_command(&remove_cmd);
635 }
636}