]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/state.c
xfs: fix maxicount division by zero error
[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;
79ac1ae4 309 if (xfsquotactl(XFS_QUOTAON, dir, type, 0, (void *)&qflags) < 0)
5aead01d
NS
310 perror("XFS_QUOTAON");
311 else if (flags & VERBOSE_FLAG)
312 state_quotafile_mount(stdout, type, mount, flags);
313}
314
315static void
316disable_enforcement(
317 char *dir,
318 uint type,
319 uint qflags,
320 uint flags)
321{
322 fs_path_t *mount;
5aead01d
NS
323
324 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
325 if (!mount) {
e3210fd8 326 exitcode = 1;
5aead01d
NS
327 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
328 return;
329 }
330 dir = mount->fs_name;
79ac1ae4 331 if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0)
5aead01d
NS
332 perror("XFS_QUOTAOFF");
333 else if (flags & VERBOSE_FLAG)
334 state_quotafile_mount(stdout, type, mount, flags);
335}
336
337static void
338quotaoff(
339 char *dir,
340 uint type,
341 uint qflags,
342 uint flags)
343{
344 fs_path_t *mount;
5aead01d
NS
345
346 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
347 if (!mount) {
e3210fd8 348 exitcode = 1;
5aead01d
NS
349 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
350 return;
351 }
352 dir = mount->fs_name;
79ac1ae4 353 if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0)
5aead01d
NS
354 perror("XFS_QUOTAOFF");
355 else if (flags & VERBOSE_FLAG)
356 state_quotafile_mount(stdout, type, mount, flags);
357}
358
79ac1ae4
DD
359static int
360remove_qtype_extents(
361 char *dir,
362 uint type)
363{
364 int error = 0;
365
366 if ((error = xfsquotactl(XFS_QUOTARM, dir, type, 0, (void *)&type)) < 0)
367 perror("XFS_QUOTARM");
368 return error;
369}
370
5aead01d
NS
371static void
372remove_extents(
373 char *dir,
374 uint type,
5aead01d
NS
375 uint flags)
376{
377 fs_path_t *mount;
5aead01d
NS
378
379 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
380 if (!mount) {
e3210fd8 381 exitcode = 1;
5aead01d
NS
382 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
383 return;
384 }
385 dir = mount->fs_name;
79ac1ae4 386 if (type & XFS_USER_QUOTA) {
f8149110 387 if (remove_qtype_extents(dir, XFS_USER_QUOTA) < 0)
79ac1ae4
DD
388 return;
389 }
390 if (type & XFS_GROUP_QUOTA) {
f8149110 391 if (remove_qtype_extents(dir, XFS_GROUP_QUOTA) < 0)
79ac1ae4
DD
392 return;
393 } else if (type & XFS_PROJ_QUOTA) {
f8149110 394 if (remove_qtype_extents(dir, XFS_PROJ_QUOTA) < 0)
79ac1ae4
DD
395 return;
396 }
397 if (flags & VERBOSE_FLAG)
5aead01d
NS
398 state_quotafile_mount(stdout, type, mount, flags);
399}
400
401static int
402enable_f(
403 int argc,
404 char **argv)
405{
406 int c, flags = 0, qflags = 0, type = 0;
407
408 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
409 switch (c) {
410 case 'g':
411 type |= XFS_GROUP_QUOTA;
412 qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
413 break;
414 case 'p':
415 type |= XFS_PROJ_QUOTA;
416 qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
417 break;
418 case 'u':
419 type |= XFS_USER_QUOTA;
420 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
421 break;
422 case 'v':
423 flags |= VERBOSE_FLAG;
424 break;
425 default:
426 return command_usage(&enable_cmd);
427 }
428 }
429
430 if (argc != optind)
431 return command_usage(&enable_cmd);
432
79ac1ae4 433 if (!type) {
5aead01d
NS
434 type |= XFS_USER_QUOTA;
435 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
436 }
437
fa13a00f
NS
438 if (fs_path->fs_flags & FS_MOUNT_POINT)
439 enable_enforcement(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
440 return 0;
441}
442
443static int
444disable_f(
445 int argc,
446 char **argv)
447{
448 int c, flags = 0, qflags = 0, type = 0;
449
450 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
451 switch (c) {
452 case 'g':
453 type |= XFS_GROUP_QUOTA;
79ac1ae4 454 qflags |= XFS_QUOTA_GDQ_ENFD;
5aead01d
NS
455 break;
456 case 'p':
457 type |= XFS_PROJ_QUOTA;
79ac1ae4 458 qflags |= XFS_QUOTA_PDQ_ENFD;
5aead01d
NS
459 break;
460 case 'u':
461 type |= XFS_USER_QUOTA;
79ac1ae4 462 qflags |= XFS_QUOTA_UDQ_ENFD;
5aead01d
NS
463 break;
464 case 'v':
465 flags |= VERBOSE_FLAG;
466 break;
467 default:
468 return command_usage(&disable_cmd);
469 }
470 }
471
472 if (argc != optind)
473 return command_usage(&disable_cmd);
474
79ac1ae4 475 if (!type) {
5aead01d 476 type |= XFS_USER_QUOTA;
79ac1ae4 477 qflags |= XFS_QUOTA_UDQ_ENFD;
5aead01d
NS
478 }
479
fa13a00f
NS
480 if (fs_path->fs_flags & FS_MOUNT_POINT)
481 disable_enforcement(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
482 return 0;
483}
484
485static int
486off_f(
487 int argc,
488 char **argv)
489{
490 int c, flags = 0, qflags = 0, type = 0;
491
492 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
493 switch (c) {
494 case 'g':
495 type |= XFS_GROUP_QUOTA;
496 qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
497 break;
498 case 'p':
499 type |= XFS_PROJ_QUOTA;
500 qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
501 break;
502 case 'u':
503 type |= XFS_USER_QUOTA;
504 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
505 break;
506 case 'v':
507 flags |= VERBOSE_FLAG;
508 break;
509 default:
510 return command_usage(&off_cmd);
511 }
512 }
513
514 if (argc != optind)
515 return command_usage(&off_cmd);
516
79ac1ae4 517 if (!type) {
5aead01d
NS
518 type |= XFS_USER_QUOTA;
519 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
520 }
521
fa13a00f
NS
522 if (fs_path->fs_flags & FS_MOUNT_POINT)
523 quotaoff(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
524 return 0;
525}
526
527static int
528remove_f(
529 int argc,
530 char **argv)
531{
79ac1ae4 532 int c, flags = 0, type = 0;
5aead01d
NS
533
534 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
535 switch (c) {
536 case 'g':
537 type |= XFS_GROUP_QUOTA;
5aead01d
NS
538 break;
539 case 'p':
540 type |= XFS_PROJ_QUOTA;
5aead01d
NS
541 break;
542 case 'u':
543 type |= XFS_USER_QUOTA;
5aead01d
NS
544 break;
545 case 'v':
546 flags |= VERBOSE_FLAG;
547 break;
548 default:
549 return command_usage(&remove_cmd);
550 }
551 }
552
553 if (argc != optind)
554 return command_usage(&remove_cmd);
555
79ac1ae4 556 if (!type) {
5aead01d 557 type |= XFS_USER_QUOTA;
5aead01d
NS
558 }
559
fa13a00f 560 if (fs_path->fs_flags & FS_MOUNT_POINT)
79ac1ae4 561 remove_extents(fs_path->fs_dir, type, flags);
5aead01d
NS
562 return 0;
563}
564
565void
566state_init(void)
567{
ad765595 568 off_cmd.name = "off";
5aead01d
NS
569 off_cmd.cfunc = off_f;
570 off_cmd.argmin = 0;
571 off_cmd.argmax = -1;
572 off_cmd.args = _("[-gpu] [-v]");
573 off_cmd.oneline = _("permanently switch quota off for a path");
574 off_cmd.help = off_help;
575
ad765595 576 state_cmd.name = "state";
5aead01d
NS
577 state_cmd.cfunc = state_f;
578 state_cmd.argmin = 0;
579 state_cmd.argmax = -1;
62790d19 580 state_cmd.args = _("[-gpu] [-a] [-v] [-f file]");
5aead01d
NS
581 state_cmd.oneline = _("get overall quota state information");
582 state_cmd.help = state_help;
29647c8d 583 state_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 584
ad765595 585 enable_cmd.name = "enable";
5aead01d
NS
586 enable_cmd.cfunc = enable_f;
587 enable_cmd.argmin = 0;
588 enable_cmd.argmax = -1;
589 enable_cmd.args = _("[-gpu] [-v]");
590 enable_cmd.oneline = _("enable quota enforcement");
591 enable_cmd.help = enable_help;
592
ad765595 593 disable_cmd.name = "disable";
5aead01d
NS
594 disable_cmd.cfunc = disable_f;
595 disable_cmd.argmin = 0;
596 disable_cmd.argmax = -1;
597 disable_cmd.args = _("[-gpu] [-v]");
598 disable_cmd.oneline = _("disable quota enforcement");
599 disable_cmd.help = disable_help;
600
ad765595 601 remove_cmd.name = "remove";
5aead01d
NS
602 remove_cmd.cfunc = remove_f;
603 remove_cmd.argmin = 0;
604 remove_cmd.argmax = -1;
605 remove_cmd.args = _("[-gpu] [-v]");
606 remove_cmd.oneline = _("remove quota extents from a filesystem");
607 remove_cmd.help = remove_help;
608
609 if (expert) {
610 add_command(&off_cmd);
611 add_command(&state_cmd);
612 add_command(&enable_cmd);
613 add_command(&disable_cmd);
614 add_command(&remove_cmd);
615 }
616}