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