]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/report.c
xfs_quota: Fix range for -U.
[thirdparty/xfsprogs-dev.git] / quota / report.c
CommitLineData
5aead01d 1/*
da23017d
NS
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
5aead01d 4 *
da23017d
NS
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
5aead01d
NS
7 * published by the Free Software Foundation.
8 *
da23017d
NS
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.
5aead01d 13 *
da23017d
NS
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
5aead01d
NS
17 */
18
19#include <xfs/command.h>
904e8060 20#include <sys/types.h>
5aead01d
NS
21#include <pwd.h>
22#include <grp.h>
ce8d0b3a 23#include <utmp.h>
5aead01d
NS
24#include "init.h"
25#include "quota.h"
26
27static cmdinfo_t dump_cmd;
28static cmdinfo_t report_cmd;
29
30static void
31dump_help(void)
32{
33 dump_cmd.args = _("[-gpu] [-f file]");
34 dump_cmd.oneline = _("dump quota information for backup utilities");
35 printf(_(
36"\n"
37" create a backup file which contains quota limits information\n"
38" -g -- dump out group quota limits\n"
39" -p -- dump out project quota limits\n"
40" -u -- dump out user quota limits (default)\n"
41" -f -- write the dump out to the specified file\n"
42"\n"));
43}
44
45static void
46report_help(void)
47{
bb76b4b3 48 report_cmd.args = _("[-bir] [-gpu] [-ahntLNU] [-f file]");
5aead01d
NS
49 report_cmd.oneline = _("report filesystem quota information");
50 printf(_(
51"\n"
52" report used space and inodes, and quota limits, for a filesystem\n"
53" Example:\n"
54" 'report -igh'\n"
55" (reports inode usage for all groups, in an easy-to-read format)\n"
56" This command is the equivalent of the traditional repquota command, which\n"
57" prints a summary of the disk usage and quotas for the current filesystem,\n"
58" or all filesystems.\n"
59" -a -- report for all mounted filesystems with quota enabled\n"
60" -h -- report in a human-readable format\n"
1774874a
NS
61" -n -- skip identifier-to-name translations, just report IDs\n"
62" -N -- suppress the header from the output\n"
5aead01d 63" -t -- terse output format, hides rows which are all zero\n"
bb76b4b3
AM
64" -L -- lower ID bound to report on\n"
65" -U -- upder ID bound to report on\n"
5aead01d
NS
66" -g -- report group usage and quota information\n"
67" -p -- report project usage and quota information\n"
68" -u -- report user usage and quota information\n"
69" -b -- report blocks-used information only\n"
70" -i -- report inodes-used information only\n"
71" -r -- report realtime-blocks-used information only\n"
72"\n"));
73}
74
75static void
76dump_file(
77 FILE *fp,
78 uint id,
79 uint type,
80 char *dev)
81{
82 fs_disk_quota_t d;
83
84 if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0)
85 return;
1774874a
NS
86 if (!d.d_blk_softlimit && !d.d_blk_hardlimit &&
87 !d.d_ino_softlimit && !d.d_ino_hardlimit &&
88 !d.d_rtb_softlimit && !d.d_rtb_hardlimit)
89 return;
5aead01d 90 fprintf(fp, "fs = %s\n", dev);
1774874a
NS
91 /* this branch is for backward compatibility reasons */
92 if (d.d_rtb_softlimit || d.d_rtb_hardlimit)
93 fprintf(fp, "%-10d %7llu %7llu %7llu %7llu %7llu %7llu\n", id,
94 (unsigned long long)d.d_blk_softlimit,
95 (unsigned long long)d.d_blk_hardlimit,
96 (unsigned long long)d.d_ino_softlimit,
97 (unsigned long long)d.d_ino_hardlimit,
98 (unsigned long long)d.d_rtb_softlimit,
99 (unsigned long long)d.d_rtb_hardlimit);
100 else
101 fprintf(fp, "%-10d %7llu %7llu %7llu %7llu\n", id,
102 (unsigned long long)d.d_blk_softlimit,
103 (unsigned long long)d.d_blk_hardlimit,
104 (unsigned long long)d.d_ino_softlimit,
105 (unsigned long long)d.d_ino_hardlimit);
5aead01d
NS
106}
107
108static void
109dump_limits_any_type(
110 FILE *fp,
111 uint type,
1774874a
NS
112 char *dir,
113 uint lower,
114 uint upper)
5aead01d
NS
115{
116 fs_path_t *mount;
1774874a 117 uint id;
5aead01d
NS
118
119 if ((mount = fs_table_lookup(dir, FS_MOUNT_POINT)) == NULL) {
e3210fd8 120 exitcode = 1;
5aead01d
NS
121 fprintf(stderr, "%s: cannot find mount point %s\n",
122 progname, dir);
123 return;
124 }
125
1774874a 126 if (upper) {
57cc6e78 127 for (id = lower; id <= upper; id++)
1774874a
NS
128 dump_file(fp, id, type, mount->fs_name);
129 return;
130 }
131
5aead01d
NS
132 switch (type) {
133 case XFS_GROUP_QUOTA: {
134 struct group *g;
135 setgrent();
136 while ((g = getgrent()) != NULL)
137 dump_file(fp, g->gr_gid, type, mount->fs_name);
138 endgrent();
139 break;
140 }
141 case XFS_PROJ_QUOTA: {
142 struct fs_project *p;
143 setprent();
144 while ((p = getprent()) != NULL)
145 dump_file(fp, p->pr_prid, type, mount->fs_name);
146 endprent();
147 break;
148 }
149 case XFS_USER_QUOTA: {
150 struct passwd *u;
151 setpwent();
152 while ((u = getpwent()) != NULL)
153 dump_file(fp, u->pw_uid, type, mount->fs_name);
154 endpwent();
155 break;
156 }
157 }
158}
159
160static int
161dump_f(
162 int argc,
163 char **argv)
164{
165 FILE *fp;
166 char *fname = NULL;
1774874a 167 uint lower = 0, upper = 0;
5aead01d
NS
168 int c, type = XFS_USER_QUOTA;
169
1774874a 170 while ((c = getopt(argc, argv, "f:gpuL:U:")) != EOF) {
5aead01d
NS
171 switch(c) {
172 case 'f':
173 fname = optarg;
174 break;
175 case 'g':
176 type = XFS_GROUP_QUOTA;
177 break;
178 case 'p':
179 type = XFS_PROJ_QUOTA;
180 break;
181 case 'u':
182 type = XFS_USER_QUOTA;
183 break;
1774874a
NS
184 case 'L':
185 lower = (uint)atoi(optarg);
186 break;
187 case 'U':
188 upper = (uint)atoi(optarg);
189 break;
5aead01d
NS
190 default:
191 return command_usage(&dump_cmd);
192 }
193 }
194
195 if (argc != optind)
196 return command_usage(&dump_cmd);
197
198 if ((fp = fopen_write_secure(fname)) == NULL)
199 return 0;
200
1774874a 201 dump_limits_any_type(fp, type, fs_path->fs_dir, lower, upper);
5aead01d
NS
202
203 if (fname)
204 fclose(fp);
205
206 return 0;
207}
208
209static void
210report_header(
211 FILE *fp,
212 uint form,
213 uint type,
214 fs_path_t *mount,
215 int flags)
216{
217 char *typename = type_to_string(type);
218 char scratch[64];
219 uint i, count;
220
221 if (flags & NO_HEADER_FLAG)
222 return;
223
224 /* line 1 */
225 fprintf(fp, _("%s quota on %s (%s)\n"),
226 typename, mount->fs_dir, mount->fs_name);
227
228 /* line 2 */
229 for (i = 0; i < 10; i++)
230 fputc(' ', fp);
231 if (form & XFS_BLOCK_QUOTA)
232 fprintf(fp, (flags & HUMAN_FLAG) ?
233 "%13c %s %13c" : "%20c %s %20c",
234 ' ', form_to_string(XFS_BLOCK_QUOTA), ' ');
235 if (form & XFS_INODE_QUOTA)
236 fprintf(fp, (flags & HUMAN_FLAG) ?
237 "%13c %s %13c" : "%20c %s %20c",
238 ' ', form_to_string(XFS_INODE_QUOTA), ' ');
239 if (form & XFS_RTBLOCK_QUOTA)
240 fprintf(fp, (flags & HUMAN_FLAG) ?
241 "%9c %s %9c" : "%15c %s %15c",
242 ' ', form_to_string(XFS_RTBLOCK_QUOTA), ' ');
243 fputc('\n', fp);
244
245 /* line 3 */
246 snprintf(scratch, sizeof(scratch), "%s ID", typename);
247 fprintf(fp, "%-10s ", scratch);
248 if (form & XFS_BLOCK_QUOTA)
249 fprintf(fp, (flags & HUMAN_FLAG) ?
250 _(" Used Soft Hard Warn/Grace ") :
251 _(" Used Soft Hard Warn/Grace "));
252 if (form & XFS_INODE_QUOTA)
253 fprintf(fp, (flags & HUMAN_FLAG) ?
254 _(" Used Soft Hard Warn/Grace ") :
255 _(" Used Soft Hard Warn/ Grace "));
256 if (form & XFS_RTBLOCK_QUOTA)
257 fprintf(fp, (flags & HUMAN_FLAG) ?
258 _(" Used Soft Hard Warn/Grace ") :
259 _(" Used Soft Hard Warn/Grace "));
260 fputc('\n', fp);
261
262 /* line 4 */
263 for (i = 0; i < 10; i++)
264 fputc('-', fp);
265 fputc(' ', fp);
266 count = (flags & HUMAN_FLAG) ? 33 : 50;
267 if (form & XFS_BLOCK_QUOTA) {
268 for (i = 0; i < count; i++)
269 fputc('-', fp);
270 fputc(' ', fp);
271 }
272 if (form & XFS_INODE_QUOTA) {
273 for (i = 0; i < count; i++)
274 fputc('-', fp);
275 fputc(' ', fp);
276 }
277 if (form & XFS_RTBLOCK_QUOTA) {
278 for (i = 0; i < count; i++)
279 fputc('-', fp);
280 fputc(' ', fp);
281 }
282 fputc('\n', fp);
283}
284
285static int
286report_mount(
287 FILE *fp,
288 __uint32_t id,
289 char *name,
290 uint form,
291 uint type,
292 fs_path_t *mount,
293 uint flags)
294{
295 fs_disk_quota_t d;
296 char *dev = mount->fs_name;
297 char c[8], h[8], s[8];
298 uint qflags;
299 int count;
300
301 if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0)
302 return 0;
303
304 if (flags & TERSE_FLAG) {
305 count = 0;
306 if ((form & XFS_BLOCK_QUOTA) && d.d_bcount)
307 count++;
308 if ((form & XFS_INODE_QUOTA) && d.d_icount)
309 count++;
310 if ((form & XFS_RTBLOCK_QUOTA) && d.d_rtbcount)
311 count++;
312 if (!count)
313 return 0;
314 }
315
316 if (!(flags & NO_HEADER_FLAG))
317 report_header(fp, form, type, mount, flags);
318
319 fprintf(fp, "%-10s", name);
320 if (form & XFS_BLOCK_QUOTA) {
321 qflags = (flags & HUMAN_FLAG);
322 if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit)
323 qflags |= LIMIT_FLAG;
324 if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit)
325 qflags |= QUOTA_FLAG;
326 if (flags & HUMAN_FLAG)
327 fprintf(fp, " %6s %6s %6s %02d %8s",
328 bbs_to_string(d.d_bcount, c, sizeof(c)),
329 bbs_to_string(d.d_blk_softlimit, s, sizeof(s)),
330 bbs_to_string(d.d_blk_hardlimit, h, sizeof(h)),
331 d.d_bwarns,
332 time_to_string(d.d_btimer, qflags));
333 else
334 fprintf(fp, " %10llu %10llu %10llu %02d %9s",
335 (unsigned long long)d.d_bcount >> 1,
336 (unsigned long long)d.d_blk_softlimit >> 1,
337 (unsigned long long)d.d_blk_hardlimit >> 1,
338 d.d_bwarns,
339 time_to_string(d.d_btimer, qflags));
340 }
341 if (form & XFS_INODE_QUOTA) {
342 qflags = (flags & HUMAN_FLAG);
343 if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit)
344 qflags |= LIMIT_FLAG;
345 if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit)
346 qflags |= QUOTA_FLAG;
347 if (flags & HUMAN_FLAG)
348 fprintf(fp, " %6s %6s %6s %02d %8s",
349 num_to_string(d.d_icount, c, sizeof(c)),
350 num_to_string(d.d_ino_softlimit, s, sizeof(s)),
351 num_to_string(d.d_ino_hardlimit, h, sizeof(h)),
352 d.d_iwarns,
353 time_to_string(d.d_itimer, qflags));
354 else
355 fprintf(fp, " %10llu %10llu %10llu %02d %9s",
356 (unsigned long long)d.d_icount,
357 (unsigned long long)d.d_ino_softlimit,
358 (unsigned long long)d.d_ino_hardlimit,
359 d.d_iwarns,
360 time_to_string(d.d_itimer, qflags));
361 }
362 if (form & XFS_RTBLOCK_QUOTA) {
363 qflags = (flags & HUMAN_FLAG);
364 if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit)
365 qflags |= LIMIT_FLAG;
366 if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit)
367 qflags |= QUOTA_FLAG;
368 if (flags & HUMAN_FLAG)
369 fprintf(fp, " %6s %6s %6s %02d %8s",
370 bbs_to_string(d.d_rtbcount, c, sizeof(c)),
371 bbs_to_string(d.d_rtb_softlimit, s, sizeof(s)),
372 bbs_to_string(d.d_rtb_hardlimit, h, sizeof(h)),
373 d.d_rtbwarns,
374 time_to_string(d.d_rtbtimer, qflags));
375 else
376 fprintf(fp, " %10llu %10llu %10llu %02d %9s",
377 (unsigned long long)d.d_rtbcount >> 1,
378 (unsigned long long)d.d_rtb_softlimit >> 1,
379 (unsigned long long)d.d_rtb_hardlimit >> 1,
380 d.d_rtbwarns,
381 time_to_string(d.d_rtbtimer, qflags));
382 }
383 fputc('\n', fp);
384 return 1;
385}
386
387static void
388report_user_mount(
389 FILE *fp,
390 uint form,
391 fs_path_t *mount,
1774874a
NS
392 uint lower,
393 uint upper,
5aead01d
NS
394 uint flags)
395{
1774874a 396 struct passwd *u;
ce8d0b3a 397 char n[NMAX];
1774874a
NS
398 uint id;
399
400 if (upper) { /* identifier range specified */
57cc6e78 401 for (id = lower; id <= upper; id++) {
1774874a
NS
402 snprintf(n, sizeof(n)-1, "#%u", id);
403 if (report_mount(fp, id, n,
404 form, XFS_USER_QUOTA, mount, flags))
405 flags |= NO_HEADER_FLAG;
406 }
407 } else {
408 setpwent();
409 while ((u = getpwent()) != NULL) {
410 if (flags & NO_LOOKUP_FLAG)
411 snprintf(n, sizeof(n)-1, "#%u", u->pw_uid);
412 else
413 strncpy(n, u->pw_name, sizeof(n)-1);
414 if (report_mount(fp, u->pw_uid, n,
415 form, XFS_USER_QUOTA, mount, flags))
416 flags |= NO_HEADER_FLAG;
417 }
418 endpwent();
419 }
5aead01d 420
5aead01d
NS
421 if (flags & NO_HEADER_FLAG)
422 fputc('\n', fp);
5aead01d
NS
423}
424
425static void
426report_group_mount(
427 FILE *fp,
428 uint form,
429 fs_path_t *mount,
1774874a
NS
430 uint lower,
431 uint upper,
5aead01d
NS
432 uint flags)
433{
1774874a 434 struct group *g;
ce8d0b3a 435 char n[NMAX];
1774874a
NS
436 uint id;
437
438 if (upper) { /* identifier range specified */
57cc6e78 439 for (id = lower; id <= upper; id++) {
1774874a
NS
440 snprintf(n, sizeof(n)-1, "#%u", id);
441 if (report_mount(fp, id, n,
442 form, XFS_GROUP_QUOTA, mount, flags))
443 flags |= NO_HEADER_FLAG;
444 }
445 } else {
446 setgrent();
447 while ((g = getgrent()) != NULL) {
448 if (flags & NO_LOOKUP_FLAG)
449 snprintf(n, sizeof(n)-1, "#%u", g->gr_gid);
450 else
451 strncpy(n, g->gr_name, sizeof(n)-1);
452 if (report_mount(fp, g->gr_gid, n,
453 form, XFS_GROUP_QUOTA, mount, flags))
454 flags |= NO_HEADER_FLAG;
455 }
456 }
5aead01d
NS
457 if (flags & NO_HEADER_FLAG)
458 fputc('\n', fp);
459 endgrent();
460}
461
462static void
463report_project_mount(
464 FILE *fp,
465 uint form,
466 fs_path_t *mount,
1774874a
NS
467 uint lower,
468 uint upper,
5aead01d
NS
469 uint flags)
470{
471 fs_project_t *p;
ce8d0b3a 472 char n[NMAX];
1774874a
NS
473 uint id;
474
475 if (upper) { /* identifier range specified */
57cc6e78 476 for (id = lower; id <= upper; id++) {
1774874a
NS
477 snprintf(n, sizeof(n)-1, "#%u", id);
478 if (report_mount(fp, id, n,
479 form, XFS_PROJ_QUOTA, mount, flags))
480 flags |= NO_HEADER_FLAG;
481 }
482 } else {
483 setprent();
484 while ((p = getprent()) != NULL) {
485 if (flags & NO_LOOKUP_FLAG)
2a1888c5
NS
486 snprintf(n, sizeof(n)-1, "#%u",
487 (unsigned int)p->pr_prid);
1774874a
NS
488 else
489 strncpy(n, p->pr_name, sizeof(n)-1);
490 if (report_mount(fp, p->pr_prid, n,
491 form, XFS_PROJ_QUOTA, mount, flags))
492 flags |= NO_HEADER_FLAG;
493 }
494 endprent();
495 }
5aead01d 496
5aead01d
NS
497 if (flags & NO_HEADER_FLAG)
498 fputc('\n', fp);
5aead01d
NS
499}
500
501static void
502report_any_type(
503 FILE *fp,
504 uint form,
505 uint type,
506 char *dir,
1774874a
NS
507 uint lower,
508 uint upper,
5aead01d
NS
509 uint flags)
510{
511 fs_cursor_t cursor;
512 fs_path_t *mount;
513
514 if (type & XFS_USER_QUOTA) {
515 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
546bedf4
NS
516 while ((mount = fs_cursor_next_entry(&cursor))) {
517 xfsquotactl(XFS_QSYNC, mount->fs_name,
518 XFS_USER_QUOTA, 0, NULL);
1774874a
NS
519 report_user_mount(fp, form, mount,
520 lower, upper, flags);
546bedf4 521 }
5aead01d
NS
522 }
523 if (type & XFS_GROUP_QUOTA) {
524 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
546bedf4
NS
525 while ((mount = fs_cursor_next_entry(&cursor))) {
526 xfsquotactl(XFS_QSYNC, mount->fs_name,
527 XFS_GROUP_QUOTA, 0, NULL);
1774874a
NS
528 report_group_mount(fp, form, mount,
529 lower, upper, flags);
546bedf4 530 }
5aead01d
NS
531 }
532 if (type & XFS_PROJ_QUOTA) {
533 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
546bedf4
NS
534 while ((mount = fs_cursor_next_entry(&cursor))) {
535 xfsquotactl(XFS_QSYNC, mount->fs_name,
536 XFS_PROJ_QUOTA, 0, NULL);
1774874a
NS
537 report_project_mount(fp, form, mount,
538 lower, upper, flags);
546bedf4 539 }
5aead01d
NS
540 }
541}
542
543static int
544report_f(
545 int argc,
546 char **argv)
547{
548 FILE *fp = NULL;
fa13a00f 549 char *fname = NULL;
1774874a 550 uint lower = 0, upper = 0;
5aead01d
NS
551 int c, flags = 0, type = 0, form = 0;
552
1774874a 553 while ((c = getopt(argc, argv, "abf:ghiL:NnprtuU:")) != EOF) {
5aead01d
NS
554 switch (c) {
555 case 'f':
556 fname = optarg;
557 break;
558 case 'b':
559 form |= XFS_BLOCK_QUOTA;
560 break;
561 case 'i':
562 form |= XFS_INODE_QUOTA;
563 break;
564 case 'r':
565 form |= XFS_RTBLOCK_QUOTA;
566 break;
567 case 'g':
568 type |= XFS_GROUP_QUOTA;
569 break;
570 case 'p':
571 type |= XFS_PROJ_QUOTA;
572 break;
573 case 'u':
574 type |= XFS_USER_QUOTA;
575 break;
576 case 'a':
577 flags |= ALL_MOUNTS_FLAG;
578 break;
579 case 'h':
580 flags |= HUMAN_FLAG;
581 break;
582 case 'n':
1774874a
NS
583 flags |= NO_LOOKUP_FLAG;
584 break;
585 case 'N':
5aead01d
NS
586 flags |= NO_HEADER_FLAG;
587 break;
588 case 't':
589 flags |= TERSE_FLAG;
590 break;
1774874a
NS
591 case 'L':
592 lower = (uint)atoi(optarg);
593 break;
594 case 'U':
595 upper = (uint)atoi(optarg);
596 break;
5aead01d
NS
597 default:
598 return command_usage(&report_cmd);
599 }
600 }
601
602 if (!form)
603 form = XFS_BLOCK_QUOTA;
604
605 if (!type)
606 type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA;
607
608 if ((fp = fopen_write_secure(fname)) == NULL)
609 return 0;
610
fa13a00f
NS
611 if (argc == optind) {
612 if (flags & ALL_MOUNTS_FLAG)
613 report_any_type(fp, form, type, NULL,
614 lower, upper, flags);
615 else if (fs_path->fs_flags & FS_MOUNT_POINT)
616 report_any_type(fp, form, type, fs_path->fs_dir,
617 lower, upper, flags);
618 } else while (argc > optind) {
619 report_any_type(fp, form, type, argv[optind++],
620 lower, upper, flags);
1774874a 621 }
5aead01d
NS
622
623 if (fname)
624 fclose(fp);
625 return 0;
626}
627
628void
629report_init(void)
630{
631 dump_cmd.name = _("dump");
632 dump_cmd.cfunc = dump_f;
633 dump_cmd.argmin = 0;
634 dump_cmd.argmax = -1;
635 dump_cmd.args = _("[-gpu] [-f file]");
636 dump_cmd.oneline = _("dump quota information for backup utilities");
637 dump_cmd.help = dump_help;
638
639 report_cmd.name = _("report");
640 report_cmd.altname = _("repquota");
641 report_cmd.cfunc = report_f;
642 report_cmd.argmin = 0;
643 report_cmd.argmax = -1;
644 report_cmd.args = _("[-bir] [-gpu] [-ahnt] [-f file]");
645 report_cmd.oneline = _("report filesystem quota information");
646 report_cmd.help = report_help;
647
648 if (expert) {
649 add_command(&dump_cmd);
650 add_command(&report_cmd);
651 }
652}