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