]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/edit.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / quota / edit.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include <pwd.h>
8 #include <grp.h>
9 #include <ctype.h>
10 #include "input.h"
11 #include "command.h"
12 #include "init.h"
13 #include "quota.h"
14
15 static cmdinfo_t limit_cmd;
16 static cmdinfo_t restore_cmd;
17 static cmdinfo_t timer_cmd;
18 static cmdinfo_t warn_cmd;
19
20 static void
21 limit_help(void)
22 {
23 printf(_(
24 "\n"
25 " modify quota limits for the specified user\n"
26 "\n"
27 " Example:\n"
28 " 'limit bsoft=100m bhard=110m tanya\n"
29 "\n"
30 " Changes the soft and/or hard block limits, inode limits and/or realtime\n"
31 " block limits that are currently being used for the specified user, group,\n"
32 " or project. The filesystem identified by the current path is modified.\n"
33 " -d -- set the default values, used the first time a file is created\n"
34 " -g -- modify group quota limits\n"
35 " -p -- modify project quota limits\n"
36 " -u -- modify user quota limits\n"
37 " The block limit values can be specified with a units suffix - accepted\n"
38 " units are: k (kilobytes), m (megabytes), g (gigabytes), and t (terabytes).\n"
39 " The user/group/project can be specified either by name or by number.\n"
40 "\n"));
41 }
42
43 static void
44 timer_help(void)
45 {
46 printf(_(
47 "\n"
48 " modify quota enforcement timeout for the current filesystem\n"
49 "\n"
50 " Example:\n"
51 " 'timer -i 3days'\n"
52 " (soft inode limit timer is changed to 3 days)\n"
53 "\n"
54 " Changes the timeout value associated with the block limits, inode limits\n"
55 " and/or realtime block limits for all users, groups, or projects on the\n"
56 " current filesystem.\n"
57 " As soon as a user consumes the amount of space or number of inodes set as\n"
58 " the soft limit, a timer is started. If the timer expires and the user is\n"
59 " still over the soft limit, the soft limit is enforced as the hard limit.\n"
60 " The default timeout is 7 days.\n"
61 " -d -- set the default values, used the first time a file is created\n"
62 " -g -- modify group quota timer\n"
63 " -p -- modify project quota timer\n"
64 " -u -- modify user quota timer\n"
65 " -b -- modify the blocks-used timer\n"
66 " -i -- modify the inodes-used timer\n"
67 " -r -- modify the blocks-used timer for the (optional) realtime subvolume\n"
68 " The timeout value is specified as a number of seconds, by default.\n"
69 " However, a suffix may be used to alternatively specify minutes (m),\n"
70 " hours (h), days (d), or weeks (w) - either the full word or the first\n"
71 " letter of the word can be used.\n"
72 "\n"));
73 }
74
75 static void
76 warn_help(void)
77 {
78 printf(_(
79 "\n"
80 " modify the number of quota warnings sent to the specified user\n"
81 "\n"
82 " Example:\n"
83 " 'warn 2 jimmy'\n"
84 " (tell the quota system that two warnings have been sent to user jimmy)\n"
85 "\n"
86 " Changes the warning count associated with the block limits, inode limits\n"
87 " and/or realtime block limits for the specified user, group, or project.\n"
88 " When a user has been warned the maximum number of times allowed, the soft\n"
89 " limit is enforced as the hard limit. It is intended as an alternative to\n"
90 " the timeout system, where the system administrator updates a count of the\n"
91 " number of warnings issued to people, and they are penalised if the warnings\n"
92 " are ignored.\n"
93 " -d -- set maximum warning count, which triggers soft limit enforcement\n"
94 " -g -- set group quota warning count\n"
95 " -p -- set project quota warning count\n"
96 " -u -- set user quota warning count\n"
97 " -b -- set the blocks-used warning count\n"
98 " -i -- set the inodes-used warning count\n"
99 " -r -- set the blocks-used warn count for the (optional) realtime subvolume\n"
100 " The user/group/project can be specified either by name or by number.\n"
101 "\n"));
102 }
103
104 static void
105 set_limits(
106 uint32_t id,
107 uint type,
108 uint mask,
109 char *dev,
110 uint64_t *bsoft,
111 uint64_t *bhard,
112 uint64_t *isoft,
113 uint64_t *ihard,
114 uint64_t *rtbsoft,
115 uint64_t *rtbhard)
116 {
117 fs_disk_quota_t d;
118
119 memset(&d, 0, sizeof(d));
120 d.d_version = FS_DQUOT_VERSION;
121 d.d_id = id;
122 d.d_flags = type;
123 d.d_fieldmask = mask;
124 d.d_blk_hardlimit = *bhard;
125 d.d_blk_softlimit = *bsoft;
126 d.d_ino_hardlimit = *ihard;
127 d.d_ino_softlimit = *isoft;
128 d.d_rtb_hardlimit = *rtbhard;
129 d.d_rtb_softlimit = *rtbsoft;
130
131 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
132 exitcode = 1;
133 fprintf(stderr, _("%s: cannot set limits: %s\n"),
134 progname, strerror(errno));
135 }
136 }
137
138 static void
139 set_user_limits(
140 char *name,
141 uint type,
142 uint mask,
143 uint64_t *bsoft,
144 uint64_t *bhard,
145 uint64_t *isoft,
146 uint64_t *ihard,
147 uint64_t *rtbsoft,
148 uint64_t *rtbhard)
149 {
150 uid_t uid = uid_from_string(name);
151
152 if (uid == -1) {
153 exitcode = 1;
154 fprintf(stderr, _("%s: invalid user name: %s\n"),
155 progname, name);
156 } else
157 set_limits(uid, type, mask, fs_path->fs_name,
158 bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
159 }
160
161 static void
162 set_group_limits(
163 char *name,
164 uint type,
165 uint mask,
166 uint64_t *bsoft,
167 uint64_t *bhard,
168 uint64_t *isoft,
169 uint64_t *ihard,
170 uint64_t *rtbsoft,
171 uint64_t *rtbhard)
172 {
173 gid_t gid = gid_from_string(name);
174
175 if (gid == -1) {
176 exitcode = 1;
177 fprintf(stderr, _("%s: invalid group name: %s\n"),
178 progname, name);
179 } else
180 set_limits(gid, type, mask, fs_path->fs_name,
181 bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
182 }
183
184 static void
185 set_project_limits(
186 char *name,
187 uint type,
188 uint mask,
189 uint64_t *bsoft,
190 uint64_t *bhard,
191 uint64_t *isoft,
192 uint64_t *ihard,
193 uint64_t *rtbsoft,
194 uint64_t *rtbhard)
195 {
196 prid_t prid = prid_from_string(name);
197
198 if (prid == -1) {
199 exitcode = 1;
200 fprintf(stderr, _("%s: invalid project name: %s\n"),
201 progname, name);
202 } else
203 set_limits(prid, type, mask, fs_path->fs_name,
204 bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
205 }
206
207 /* extract number of blocks from an ascii string */
208 static int
209 extractb(
210 char *string,
211 const char *prefix,
212 int length,
213 uint blocksize,
214 uint sectorsize,
215 uint64_t *value)
216 {
217 long long v;
218 char *s = string;
219
220 if (strncmp(string, prefix, length) == 0) {
221 s = string + length + 1;
222 v = cvtnum(blocksize, sectorsize, s);
223 if (v == -1LL) {
224 fprintf(stderr,
225 _("%s: Error: could not parse size %s.\n"),
226 progname, s);
227 return 0;
228 }
229 *value = (uint64_t)v >> 9; /* syscalls use basic blocks */
230 if (v > 0 && *value == 0)
231 fprintf(stderr, _("%s: Warning: `%s' in quota blocks is 0 (unlimited).\n"), progname, s);
232 return 1;
233 }
234 return 0;
235 }
236
237 /* extract number of inodes from an ascii string */
238 static int
239 extracti(
240 char *string,
241 const char *prefix,
242 int length,
243 uint64_t *value)
244 {
245 char *sp, *s = string;
246
247 if (strncmp(string, prefix, length) == 0) {
248 s = string + length + 1;
249 *value = strtoll(s, &sp, 0);
250 return 1;
251 }
252 return 0;
253 }
254
255 static int
256 limit_f(
257 int argc,
258 char **argv)
259 {
260 char *name;
261 uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
262 int c, type = 0, mask = 0, flags = 0;
263 uint bsize, ssize, endoptions;
264
265 init_cvtnum(&bsize, &ssize);
266 bsoft = bhard = isoft = ihard = rtbsoft = rtbhard = 0;
267 while ((c = getopt(argc, argv, "dgpu")) != EOF) {
268 switch (c) {
269 case 'd':
270 flags |= DEFAULTS_FLAG;
271 break;
272 case 'g':
273 type |= XFS_GROUP_QUOTA;
274 break;
275 case 'p':
276 type |= XFS_PROJ_QUOTA;
277 break;
278 case 'u':
279 type |= XFS_USER_QUOTA;
280 break;
281 default:
282 return command_usage(&limit_cmd);
283 }
284 }
285
286 /*
287 * In the usual case, we need at least 2 more arguments -
288 * one (or more) limits and a user name/id.
289 * For setting defaults (-d) we don't want a user name/id.
290 */
291 if (flags & DEFAULTS_FLAG) {
292 if (argc < optind + 1)
293 return command_usage(&limit_cmd);
294 endoptions = 1;
295 } else if (argc < optind + 2) {
296 return command_usage(&limit_cmd);
297 } else {
298 endoptions = 2;
299 }
300
301 /*
302 * Extract limit values from remaining optional arguments.
303 */
304 while (argc > optind + endoptions - 1) {
305 char *s = argv[optind++];
306 if (extractb(s, "bsoft=", 5, bsize, ssize, &bsoft))
307 mask |= FS_DQ_BSOFT;
308 else if (extractb(s, "bhard=", 5, bsize, ssize, &bhard))
309 mask |= FS_DQ_BHARD;
310 else if (extracti(s, "isoft=", 5, &isoft))
311 mask |= FS_DQ_ISOFT;
312 else if (extracti(s, "ihard=", 5, &ihard))
313 mask |= FS_DQ_IHARD;
314 else if (extractb(s, "rtbsoft=", 7, bsize, ssize, &rtbsoft))
315 mask |= FS_DQ_RTBSOFT;
316 else if (extractb(s, "rtbhard=", 7, bsize, ssize, &rtbhard))
317 mask |= FS_DQ_RTBHARD;
318 else {
319 exitcode = 1;
320 fprintf(stderr, _("%s: unrecognised argument %s\n"),
321 progname, s);
322 return 0;
323 }
324 }
325 if (!mask) {
326 exitcode = 1;
327 fprintf(stderr, _("%s: cannot find any valid arguments\n"),
328 progname);
329 return 0;
330 }
331
332 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
333
334 if (!type) {
335 type = XFS_USER_QUOTA;
336 } else if (type != XFS_GROUP_QUOTA &&
337 type != XFS_PROJ_QUOTA &&
338 type != XFS_USER_QUOTA) {
339 return command_usage(&limit_cmd);
340 }
341
342 switch (type) {
343 case XFS_USER_QUOTA:
344 set_user_limits(name, type, mask,
345 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
346 break;
347 case XFS_GROUP_QUOTA:
348 set_group_limits(name, type, mask,
349 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
350 break;
351 case XFS_PROJ_QUOTA:
352 set_project_limits(name, type, mask,
353 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
354 break;
355 }
356 return 0;
357 }
358
359 /*
360 * Iterate through input file, restoring the limits.
361 * File format is as follows:
362 * fs = <device>
363 * <numeric id> bsoft bhard isoft ihard [rtbsoft rtbhard]
364 */
365 static void
366 restore_file(
367 FILE *fp,
368 uint type)
369 {
370 char buffer[512];
371 char dev[512];
372 uint mask;
373 int cnt;
374 uint32_t id;
375 uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
376
377 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
378 if (strncmp("fs = ", buffer, 5) == 0) {
379 /*
380 * Copy the device name to dev, strip off the trailing
381 * newline, and move on to the next line.
382 */
383 strncpy(dev, buffer + 5, sizeof(dev) - 1);
384 dev[strlen(dev) - 1] = '\0';
385 continue;
386 }
387 rtbsoft = rtbhard = 0;
388 cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n",
389 &id,
390 (unsigned long long *)&bsoft,
391 (unsigned long long *)&bhard,
392 (unsigned long long *)&isoft,
393 (unsigned long long *)&ihard,
394 (unsigned long long *)&rtbsoft,
395 (unsigned long long *)&rtbhard);
396 if (cnt == 5 || cnt == 7) {
397 mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD;
398 if (cnt == 7)
399 mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD;
400 set_limits(id, type, mask, dev, &bsoft, &bhard,
401 &isoft, &ihard, &rtbsoft, &rtbhard);
402 }
403 }
404 }
405
406 static int
407 restore_f(
408 int argc,
409 char **argv)
410 {
411 FILE *fp = stdin;
412 char *fname = NULL;
413 int c, type = 0;
414
415 while ((c = getopt(argc, argv, "f:gpu")) != EOF) {
416 switch (c) {
417 case 'f':
418 fname = optarg;
419 break;
420 case 'g':
421 type |= XFS_GROUP_QUOTA;
422 break;
423 case 'p':
424 type |= XFS_PROJ_QUOTA;
425 break;
426 case 'u':
427 type |= XFS_USER_QUOTA;
428 break;
429 default:
430 return command_usage(&restore_cmd);
431 }
432 }
433
434 if (argc < optind)
435 return command_usage(&restore_cmd);
436
437 if (!type) {
438 type = XFS_USER_QUOTA;
439 } else if (type != XFS_GROUP_QUOTA &&
440 type != XFS_PROJ_QUOTA &&
441 type != XFS_USER_QUOTA) {
442 return command_usage(&restore_cmd);
443 }
444
445 if (fname) {
446 if ((fp = fopen(fname, "r")) == NULL) {
447 exitcode = 1;
448 fprintf(stderr, _("%s: fopen on %s failed: %s\n"),
449 progname, fname, strerror(errno));
450 return 0;
451 }
452 }
453
454 restore_file(fp, type);
455
456 if (fname)
457 fclose(fp);
458 return 0;
459 }
460
461 static void
462 set_timer(
463 uint type,
464 uint mask,
465 char *dev,
466 uint value)
467 {
468 fs_disk_quota_t d;
469
470 memset(&d, 0, sizeof(d));
471 d.d_version = FS_DQUOT_VERSION;
472 d.d_flags = type;
473 d.d_fieldmask = mask;
474 d.d_itimer = value;
475 d.d_btimer = value;
476 d.d_rtbtimer = value;
477
478 if (xfsquotactl(XFS_SETQLIM, dev, type, 0, (void *)&d) < 0) {
479 exitcode = 1;
480 fprintf(stderr, _("%s: cannot set timer: %s\n"),
481 progname, strerror(errno));
482 }
483 }
484
485 static int
486 timer_f(
487 int argc,
488 char **argv)
489 {
490 uint value;
491 int c, type = 0, mask = 0;
492
493 while ((c = getopt(argc, argv, "bgipru")) != EOF) {
494 switch (c) {
495 case 'b':
496 mask |= FS_DQ_BTIMER;
497 break;
498 case 'i':
499 mask |= FS_DQ_ITIMER;
500 break;
501 case 'r':
502 mask |= FS_DQ_RTBTIMER;
503 break;
504 case 'g':
505 type |= XFS_GROUP_QUOTA;
506 break;
507 case 'p':
508 type |= XFS_PROJ_QUOTA;
509 break;
510 case 'u':
511 type |= XFS_USER_QUOTA;
512 break;
513 default:
514 return command_usage(&timer_cmd);
515 }
516 }
517
518 if (argc != optind + 1)
519 return command_usage(&timer_cmd);
520
521 value = cvttime(argv[optind++]);
522
523 if (!mask)
524 mask = FS_DQ_TIMER_MASK;
525
526 if (!type) {
527 type = XFS_USER_QUOTA;
528 } else if (type != XFS_GROUP_QUOTA &&
529 type != XFS_PROJ_QUOTA &&
530 type != XFS_USER_QUOTA) {
531 return command_usage(&timer_cmd);
532 }
533
534 set_timer(type, mask, fs_path->fs_name, value);
535 return 0;
536 }
537
538 static void
539 set_warnings(
540 uint32_t id,
541 uint type,
542 uint mask,
543 char *dev,
544 uint value)
545 {
546 fs_disk_quota_t d;
547
548 memset(&d, 0, sizeof(d));
549 d.d_version = FS_DQUOT_VERSION;
550 d.d_id = id;
551 d.d_flags = type;
552 d.d_fieldmask = mask;
553 d.d_iwarns = value;
554 d.d_bwarns = value;
555 d.d_rtbwarns = value;
556
557 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
558 exitcode = 1;
559 fprintf(stderr, _("%s: cannot set warnings: %s\n"),
560 progname, strerror(errno));
561 }
562 }
563
564 static void
565 set_user_warnings(
566 char *name,
567 uint type,
568 uint mask,
569 uint value)
570 {
571 uid_t uid = uid_from_string(name);
572
573 if (uid == -1) {
574 exitcode = 1;
575 fprintf(stderr, _("%s: invalid user name: %s\n"),
576 progname, name);
577 } else
578 set_warnings(uid, type, mask, fs_path->fs_name, value);
579 }
580
581 static void
582 set_group_warnings(
583 char *name,
584 uint type,
585 uint mask,
586 uint value)
587 {
588 gid_t gid = gid_from_string(name);
589
590 if (gid == -1) {
591 exitcode = 1;
592 fprintf(stderr, _("%s: invalid group name: %s\n"),
593 progname, name);
594 } else
595 set_warnings(gid, type, mask, fs_path->fs_name, value);
596 }
597
598 static void
599 set_project_warnings(
600 char *name,
601 uint type,
602 uint mask,
603 uint value)
604 {
605 prid_t prid = prid_from_string(name);
606
607 if (prid == -1) {
608 exitcode = 1;
609 fprintf(stderr, _("%s: invalid project name: %s\n"),
610 progname, name);
611 } else
612 set_warnings(prid, type, mask, fs_path->fs_name, value);
613 }
614
615 static int
616 warn_f(
617 int argc,
618 char **argv)
619 {
620 char *name;
621 uint value;
622 int c, flags = 0, type = 0, mask = 0;
623
624 while ((c = getopt(argc, argv, "bdgipru")) != EOF) {
625 switch (c) {
626 case 'd':
627 flags |= DEFAULTS_FLAG;
628 break;
629 case 'b':
630 mask |= FS_DQ_BWARNS;
631 break;
632 case 'i':
633 mask |= FS_DQ_IWARNS;
634 break;
635 case 'r':
636 mask |= FS_DQ_RTBWARNS;
637 break;
638 case 'g':
639 type |= XFS_GROUP_QUOTA;
640 break;
641 case 'p':
642 type |= XFS_PROJ_QUOTA;
643 break;
644 case 'u':
645 type |= XFS_USER_QUOTA;
646 break;
647 default:
648 return command_usage(&warn_cmd);
649 }
650 }
651
652 /*
653 * In the usual case, we need at least 2 more arguments -
654 * one (or more) value and a user name/id.
655 * For setting defaults (-d) we don't want a user name/id.
656 */
657 if (flags & DEFAULTS_FLAG) {
658 if (argc != optind + 1)
659 return command_usage(&warn_cmd);
660 } else if (argc != optind + 2) {
661 return command_usage(&warn_cmd);
662 }
663
664 value = atoi(argv[optind++]);
665 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
666
667 if (!mask)
668 mask = FS_DQ_WARNS_MASK;
669
670 if (!type) {
671 type = XFS_USER_QUOTA;
672 } else if (type != XFS_GROUP_QUOTA &&
673 type != XFS_PROJ_QUOTA &&
674 type != XFS_USER_QUOTA) {
675 return command_usage(&warn_cmd);
676 }
677
678 switch (type) {
679 case XFS_USER_QUOTA:
680 set_user_warnings(name, type, mask, value);
681 break;
682 case XFS_GROUP_QUOTA:
683 set_group_warnings(name, type, mask, value);
684 break;
685 case XFS_PROJ_QUOTA:
686 set_project_warnings(name, type, mask, value);
687 break;
688 }
689 return 0;
690 }
691
692 void
693 edit_init(void)
694 {
695 limit_cmd.name = "limit";
696 limit_cmd.cfunc = limit_f;
697 limit_cmd.argmin = 2;
698 limit_cmd.argmax = -1;
699 limit_cmd.args = \
700 _("[-g|-p|-u] bsoft|bhard|isoft|ihard|rtbsoft|rtbhard=N -d|id|name");
701 limit_cmd.oneline = _("modify quota limits");
702 limit_cmd.help = limit_help;
703 limit_cmd.flags = CMD_FLAG_FOREIGN_OK;
704
705 restore_cmd.name = "restore";
706 restore_cmd.cfunc = restore_f;
707 restore_cmd.argmin = 0;
708 restore_cmd.argmax = -1;
709 restore_cmd.args = _("[-g|-p|-u] [-f file]");
710 restore_cmd.oneline = _("restore quota limits from a backup file");
711 restore_cmd.flags = CMD_FLAG_FOREIGN_OK;
712
713 timer_cmd.name = "timer";
714 timer_cmd.cfunc = timer_f;
715 timer_cmd.argmin = 2;
716 timer_cmd.argmax = -1;
717 timer_cmd.args = _("[-bir] [-g|-p|-u] value");
718 timer_cmd.oneline = _("set quota enforcement timeouts");
719 timer_cmd.help = timer_help;
720 timer_cmd.flags = CMD_FLAG_FOREIGN_OK;
721
722 warn_cmd.name = "warn";
723 warn_cmd.cfunc = warn_f;
724 warn_cmd.argmin = 2;
725 warn_cmd.argmax = -1;
726 warn_cmd.args = _("[-bir] [-g|-p|-u] value -d|id|name");
727 warn_cmd.oneline = _("get/set enforcement warning counter");
728 warn_cmd.help = warn_help;
729
730 if (expert) {
731 add_command(&limit_cmd);
732 add_command(&restore_cmd);
733 add_command(&timer_cmd);
734 add_command(&warn_cmd);
735 }
736 }