]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/edit.c
xfs_quota: support editing and reporting quotas with bigtime
[thirdparty/xfsprogs-dev.git] / quota / edit.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
NS
5 */
6
7#include <pwd.h>
8#include <grp.h>
9#include <ctype.h>
6b803e5a
CH
10#include "input.h"
11#include "command.h"
5aead01d
NS
12#include "init.h"
13#include "quota.h"
14
15static cmdinfo_t limit_cmd;
16static cmdinfo_t restore_cmd;
17static cmdinfo_t timer_cmd;
18static cmdinfo_t warn_cmd;
19
20static void
21limit_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
43static void
44timer_help(void)
45{
46 printf(_(
47"\n"
1774874a 48" modify quota enforcement timeout for the current filesystem\n"
5aead01d
NS
49"\n"
50" Example:\n"
1774874a
NS
51" 'timer -i 3days'\n"
52" (soft inode limit timer is changed to 3 days)\n"
5aead01d
NS
53"\n"
54" Changes the timeout value associated with the block limits, inode limits\n"
1774874a
NS
55" and/or realtime block limits for all users, groups, or projects on the\n"
56" current filesystem.\n"
5aead01d
NS
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"
5aead01d
NS
72"\n"));
73}
74
75static void
76warn_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
67a73d61
ES
104static uint32_t
105id_from_string(
106 char *name,
107 int type)
108{
109 uint32_t id = -1;
110 const char *type_name = "unknown type";
111
112 switch (type) {
113 case XFS_USER_QUOTA:
114 type_name = "user";
115 id = uid_from_string(name);
116 break;
117 case XFS_GROUP_QUOTA:
118 type_name = "group";
119 id = gid_from_string(name);
120 break;
121 case XFS_PROJ_QUOTA:
122 type_name = "project";
123 id = prid_from_string(name);
124 break;
125 default:
126 ASSERT(0);
127 break;
128 }
129
130 if (id == -1) {
131 fprintf(stderr, _("%s: invalid %s name: %s\n"),
132 type_name, progname, name);
133 exitcode = 1;
134 }
135 return id;
136}
137
5aead01d
NS
138static void
139set_limits(
14f8b681 140 uint32_t id,
5aead01d
NS
141 uint type,
142 uint mask,
143 char *dev,
14f8b681
DW
144 uint64_t *bsoft,
145 uint64_t *bhard,
146 uint64_t *isoft,
147 uint64_t *ihard,
148 uint64_t *rtbsoft,
149 uint64_t *rtbhard)
5aead01d
NS
150{
151 fs_disk_quota_t d;
152
153 memset(&d, 0, sizeof(d));
154 d.d_version = FS_DQUOT_VERSION;
155 d.d_id = id;
156 d.d_flags = type;
157 d.d_fieldmask = mask;
158 d.d_blk_hardlimit = *bhard;
159 d.d_blk_softlimit = *bsoft;
160 d.d_ino_hardlimit = *ihard;
161 d.d_ino_softlimit = *isoft;
162 d.d_rtb_hardlimit = *rtbhard;
163 d.d_rtb_softlimit = *rtbsoft;
164
e3210fd8
AM
165 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
166 exitcode = 1;
5aead01d
NS
167 fprintf(stderr, _("%s: cannot set limits: %s\n"),
168 progname, strerror(errno));
e3210fd8 169 }
5aead01d
NS
170}
171
1774874a 172/* extract number of blocks from an ascii string */
5aead01d 173static int
1774874a 174extractb(
5aead01d
NS
175 char *string,
176 const char *prefix,
177 int length,
178 uint blocksize,
179 uint sectorsize,
14f8b681 180 uint64_t *value)
5aead01d 181{
7fd39587 182 long long v;
5aead01d
NS
183 char *s = string;
184
185 if (strncmp(string, prefix, length) == 0) {
186 s = string + length + 1;
7fd39587
ES
187 v = cvtnum(blocksize, sectorsize, s);
188 if (v == -1LL) {
189 fprintf(stderr,
190 _("%s: Error: could not parse size %s.\n"),
191 progname, s);
192 return 0;
193 }
14f8b681 194 *value = (uint64_t)v >> 9; /* syscalls use basic blocks */
39d09609
AM
195 if (v > 0 && *value == 0)
196 fprintf(stderr, _("%s: Warning: `%s' in quota blocks is 0 (unlimited).\n"), progname, s);
5aead01d
NS
197 return 1;
198 }
199 return 0;
200}
201
1774874a
NS
202/* extract number of inodes from an ascii string */
203static int
204extracti(
205 char *string,
206 const char *prefix,
207 int length,
14f8b681 208 uint64_t *value)
1774874a
NS
209{
210 char *sp, *s = string;
211
212 if (strncmp(string, prefix, length) == 0) {
213 s = string + length + 1;
214 *value = strtoll(s, &sp, 0);
215 return 1;
216 }
217 return 0;
218}
219
5aead01d
NS
220static int
221limit_f(
222 int argc,
223 char **argv)
224{
225 char *name;
67a73d61 226 uint32_t id;
14f8b681 227 uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
5aead01d
NS
228 int c, type = 0, mask = 0, flags = 0;
229 uint bsize, ssize, endoptions;
230
231 init_cvtnum(&bsize, &ssize);
232 bsoft = bhard = isoft = ihard = rtbsoft = rtbhard = 0;
233 while ((c = getopt(argc, argv, "dgpu")) != EOF) {
234 switch (c) {
235 case 'd':
236 flags |= DEFAULTS_FLAG;
237 break;
238 case 'g':
c614d3bf 239 type |= XFS_GROUP_QUOTA;
5aead01d
NS
240 break;
241 case 'p':
c614d3bf 242 type |= XFS_PROJ_QUOTA;
5aead01d
NS
243 break;
244 case 'u':
c614d3bf 245 type |= XFS_USER_QUOTA;
5aead01d
NS
246 break;
247 default:
248 return command_usage(&limit_cmd);
249 }
250 }
251
252 /*
253 * In the usual case, we need at least 2 more arguments -
254 * one (or more) limits and a user name/id.
255 * For setting defaults (-d) we don't want a user name/id.
256 */
257 if (flags & DEFAULTS_FLAG) {
258 if (argc < optind + 1)
259 return command_usage(&limit_cmd);
260 endoptions = 1;
261 } else if (argc < optind + 2) {
262 return command_usage(&limit_cmd);
263 } else {
264 endoptions = 2;
265 }
266
267 /*
268 * Extract limit values from remaining optional arguments.
269 */
270 while (argc > optind + endoptions - 1) {
271 char *s = argv[optind++];
1774874a 272 if (extractb(s, "bsoft=", 5, bsize, ssize, &bsoft))
5aead01d 273 mask |= FS_DQ_BSOFT;
1774874a 274 else if (extractb(s, "bhard=", 5, bsize, ssize, &bhard))
5aead01d 275 mask |= FS_DQ_BHARD;
1774874a 276 else if (extracti(s, "isoft=", 5, &isoft))
5aead01d 277 mask |= FS_DQ_ISOFT;
1774874a 278 else if (extracti(s, "ihard=", 5, &ihard))
5aead01d 279 mask |= FS_DQ_IHARD;
1774874a 280 else if (extractb(s, "rtbsoft=", 7, bsize, ssize, &rtbsoft))
5aead01d 281 mask |= FS_DQ_RTBSOFT;
1774874a 282 else if (extractb(s, "rtbhard=", 7, bsize, ssize, &rtbhard))
5aead01d
NS
283 mask |= FS_DQ_RTBHARD;
284 else {
e3210fd8 285 exitcode = 1;
5aead01d
NS
286 fprintf(stderr, _("%s: unrecognised argument %s\n"),
287 progname, s);
288 return 0;
289 }
290 }
291 if (!mask) {
e3210fd8 292 exitcode = 1;
5aead01d
NS
293 fprintf(stderr, _("%s: cannot find any valid arguments\n"),
294 progname);
295 return 0;
296 }
297
298 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
299
c614d3bf 300 if (!type) {
5aead01d 301 type = XFS_USER_QUOTA;
c614d3bf
ZL
302 } else if (type != XFS_GROUP_QUOTA &&
303 type != XFS_PROJ_QUOTA &&
304 type != XFS_USER_QUOTA) {
305 return command_usage(&limit_cmd);
306 }
5aead01d 307
67a73d61
ES
308
309 id = id_from_string(name, type);
eaa5b0b7
DW
310 if (id == -1)
311 return 0;
312
313 set_limits(id, type, mask, fs_path->fs_name,
314 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
5aead01d
NS
315 return 0;
316}
317
318/*
319 * Iterate through input file, restoring the limits.
320 * File format is as follows:
321 * fs = <device>
322 * <numeric id> bsoft bhard isoft ihard [rtbsoft rtbhard]
323 */
324static void
325restore_file(
326 FILE *fp,
327 uint type)
328{
329 char buffer[512];
7f0d0b55 330 char dev[512];
5aead01d
NS
331 uint mask;
332 int cnt;
14f8b681
DW
333 uint32_t id;
334 uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
5aead01d
NS
335
336 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
337 if (strncmp("fs = ", buffer, 5) == 0) {
7f0d0b55
DW
338 /*
339 * Copy the device name to dev, strip off the trailing
340 * newline, and move on to the next line.
341 */
342 strncpy(dev, buffer + 5, sizeof(dev) - 1);
5aead01d
NS
343 dev[strlen(dev) - 1] = '\0';
344 continue;
345 }
346 rtbsoft = rtbhard = 0;
347 cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n",
348 &id,
7a1525fd
NS
349 (unsigned long long *)&bsoft,
350 (unsigned long long *)&bhard,
351 (unsigned long long *)&isoft,
352 (unsigned long long *)&ihard,
353 (unsigned long long *)&rtbsoft,
354 (unsigned long long *)&rtbhard);
5aead01d
NS
355 if (cnt == 5 || cnt == 7) {
356 mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD;
357 if (cnt == 7)
358 mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD;
359 set_limits(id, type, mask, dev, &bsoft, &bhard,
360 &isoft, &ihard, &rtbsoft, &rtbhard);
361 }
362 }
363}
364
365static int
366restore_f(
367 int argc,
368 char **argv)
369{
370 FILE *fp = stdin;
371 char *fname = NULL;
372 int c, type = 0;
373
374 while ((c = getopt(argc, argv, "f:gpu")) != EOF) {
375 switch (c) {
376 case 'f':
377 fname = optarg;
378 break;
379 case 'g':
c614d3bf 380 type |= XFS_GROUP_QUOTA;
5aead01d
NS
381 break;
382 case 'p':
c614d3bf 383 type |= XFS_PROJ_QUOTA;
5aead01d
NS
384 break;
385 case 'u':
c614d3bf 386 type |= XFS_USER_QUOTA;
5aead01d
NS
387 break;
388 default:
389 return command_usage(&restore_cmd);
390 }
391 }
392
393 if (argc < optind)
394 return command_usage(&restore_cmd);
395
c614d3bf 396 if (!type) {
5aead01d 397 type = XFS_USER_QUOTA;
c614d3bf
ZL
398 } else if (type != XFS_GROUP_QUOTA &&
399 type != XFS_PROJ_QUOTA &&
400 type != XFS_USER_QUOTA) {
401 return command_usage(&restore_cmd);
402 }
5aead01d
NS
403
404 if (fname) {
405 if ((fp = fopen(fname, "r")) == NULL) {
e3210fd8 406 exitcode = 1;
5aead01d
NS
407 fprintf(stderr, _("%s: fopen on %s failed: %s\n"),
408 progname, fname, strerror(errno));
409 return 0;
410 }
411 }
412
413 restore_file(fp, type);
414
415 if (fname)
416 fclose(fp);
417 return 0;
418}
419
f3eb31d9
DW
420time64_t
421decode_timer(
422 const struct fs_disk_quota *d,
423 __s32 timer_lo,
424 __s8 timer_hi)
425{
426 if (d->d_fieldmask & FS_DQ_BIGTIME)
427 return (uint32_t)timer_lo | (int64_t)timer_hi << 32;
428 return timer_lo;
429}
430
431static inline void
432encode_timer(
433 const struct fs_disk_quota *d,
434 __s32 *timer_lo,
435 __s8 *timer_hi,
436 time64_t timer)
437{
438 *timer_lo = timer;
439 if (d->d_fieldmask & FS_DQ_BIGTIME)
440 *timer_hi = timer >> 32;
441 else
442 *timer_hi = 0;
443}
444
445static inline bool want_bigtime(time64_t timer)
446{
447 return timer > INT32_MAX || timer < INT32_MIN;
448}
449
450static void
451encode_timers(
452 struct fs_disk_quota *d,
453 time64_t btimer,
454 time64_t itimer,
455 time64_t rtbtimer)
456{
457 d->d_fieldmask &= ~FS_DQ_BIGTIME;
458 if (want_bigtime(btimer) || want_bigtime(itimer) ||
459 want_bigtime(rtbtimer))
460 d->d_fieldmask |= FS_DQ_BIGTIME;
461
462 encode_timer(d, &d->d_btimer, &d->d_btimer_hi, btimer);
463 encode_timer(d, &d->d_itimer, &d->d_itimer_hi, itimer);
464 encode_timer(d, &d->d_rtbtimer, &d->d_rtbtimer_hi, rtbtimer);
465}
466
5aead01d
NS
467static void
468set_timer(
bf6d4cf9
DW
469 uint32_t id,
470 uint type,
471 uint mask,
472 char *dev,
473 time64_t value)
5aead01d 474{
bf6d4cf9 475 struct fs_disk_quota d;
f3eb31d9 476 time64_t btimer, itimer, rtbtimer;
5aead01d
NS
477
478 memset(&d, 0, sizeof(d));
36dc471c
ES
479
480 /*
481 * If id is specified we are extending grace time by value
482 * Otherwise we are setting the default grace time
483 */
484 if (id) {
485 time_t now;
486
487 /* Get quota to find out whether user is past soft limits */
488 if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) {
489 exitcode = 1;
490 fprintf(stderr, _("%s: cannot get quota: %s\n"),
491 progname, strerror(errno));
492 return;
493 }
494
495 time(&now);
496
f3eb31d9
DW
497 btimer = decode_timer(&d, d.d_btimer, d.d_btimer_hi);
498 itimer = decode_timer(&d, d.d_itimer, d.d_itimer_hi);
499 rtbtimer = decode_timer(&d, d.d_rtbtimer, d.d_rtbtimer_hi);
500
36dc471c
ES
501 /* Only set grace time if user is already past soft limit */
502 if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit)
f3eb31d9 503 btimer = now + value;
36dc471c 504 if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit)
f3eb31d9 505 itimer = now + value;
36dc471c 506 if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit)
f3eb31d9 507 rtbtimer = now + value;
36dc471c 508 } else {
f3eb31d9
DW
509 btimer = value;
510 itimer = value;
511 rtbtimer = value;
36dc471c
ES
512 }
513
5aead01d 514 d.d_version = FS_DQUOT_VERSION;
5aead01d
NS
515 d.d_flags = type;
516 d.d_fieldmask = mask;
36dc471c 517 d.d_id = id;
f3eb31d9 518 encode_timers(&d, btimer, itimer, rtbtimer);
5aead01d 519
36dc471c 520 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
e3210fd8 521 exitcode = 1;
5aead01d
NS
522 fprintf(stderr, _("%s: cannot set timer: %s\n"),
523 progname, strerror(errno));
e3210fd8 524 }
5aead01d
NS
525}
526
5aead01d
NS
527static int
528timer_f(
529 int argc,
530 char **argv)
531{
bf6d4cf9 532 time64_t value;
36dc471c
ES
533 char *name = NULL;
534 uint32_t id = 0;
535 int c, flags = 0, type = 0, mask = 0;
5aead01d 536
36dc471c 537 while ((c = getopt(argc, argv, "bdgipru")) != EOF) {
5aead01d 538 switch (c) {
36dc471c
ES
539 case 'd':
540 flags |= DEFAULTS_FLAG;
541 break;
5aead01d
NS
542 case 'b':
543 mask |= FS_DQ_BTIMER;
544 break;
545 case 'i':
546 mask |= FS_DQ_ITIMER;
547 break;
548 case 'r':
549 mask |= FS_DQ_RTBTIMER;
550 break;
551 case 'g':
c614d3bf 552 type |= XFS_GROUP_QUOTA;
5aead01d
NS
553 break;
554 case 'p':
c614d3bf 555 type |= XFS_PROJ_QUOTA;
5aead01d
NS
556 break;
557 case 'u':
c614d3bf 558 type |= XFS_USER_QUOTA;
5aead01d
NS
559 break;
560 default:
561 return command_usage(&timer_cmd);
562 }
563 }
564
36dc471c
ES
565 /*
566 * Older versions of the command did not accept -d|id|name,
567 * so in that case we assume we're setting default timer,
568 * and the last arg is the timer value.
569 *
570 * Otherwise, if the defaults flag is set, we expect 1 more arg for
571 * timer value ; if not, 2 more args: 1 for value, one for id/name.
572 */
573 if (!(flags & DEFAULTS_FLAG) && (argc == optind + 1)) {
574 value = cvttime(argv[optind++]);
575 } else if (flags & DEFAULTS_FLAG) {
576 if (argc != optind + 1)
577 return command_usage(&timer_cmd);
578 value = cvttime(argv[optind++]);
579 } else if (argc == optind + 2) {
580 value = cvttime(argv[optind++]);
581 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
582 } else
5aead01d 583 return command_usage(&timer_cmd);
5aead01d 584
5aead01d 585
36dc471c 586 /* if none of -bir specified, set them all */
5aead01d
NS
587 if (!mask)
588 mask = FS_DQ_TIMER_MASK;
589
c614d3bf 590 if (!type) {
5aead01d 591 type = XFS_USER_QUOTA;
c614d3bf 592 } else if (type != XFS_GROUP_QUOTA &&
36dc471c
ES
593 type != XFS_PROJ_QUOTA &&
594 type != XFS_USER_QUOTA) {
c614d3bf
ZL
595 return command_usage(&timer_cmd);
596 }
5aead01d 597
36dc471c
ES
598 if (name)
599 id = id_from_string(name, type);
600
eaa5b0b7
DW
601 if (id == -1)
602 return 0;
36dc471c 603
eaa5b0b7 604 set_timer(id, type, mask, fs_path->fs_name, value);
5aead01d
NS
605 return 0;
606}
607
608static void
609set_warnings(
14f8b681 610 uint32_t id,
5aead01d
NS
611 uint type,
612 uint mask,
613 char *dev,
614 uint value)
615{
616 fs_disk_quota_t d;
617
618 memset(&d, 0, sizeof(d));
619 d.d_version = FS_DQUOT_VERSION;
620 d.d_id = id;
621 d.d_flags = type;
622 d.d_fieldmask = mask;
623 d.d_iwarns = value;
624 d.d_bwarns = value;
625 d.d_rtbwarns = value;
626
e3210fd8
AM
627 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
628 exitcode = 1;
5aead01d
NS
629 fprintf(stderr, _("%s: cannot set warnings: %s\n"),
630 progname, strerror(errno));
e3210fd8 631 }
5aead01d
NS
632}
633
5aead01d
NS
634static int
635warn_f(
636 int argc,
637 char **argv)
638{
639 char *name;
67a73d61 640 uint32_t id;
5aead01d
NS
641 uint value;
642 int c, flags = 0, type = 0, mask = 0;
643
644 while ((c = getopt(argc, argv, "bdgipru")) != EOF) {
645 switch (c) {
646 case 'd':
647 flags |= DEFAULTS_FLAG;
648 break;
649 case 'b':
650 mask |= FS_DQ_BWARNS;
651 break;
652 case 'i':
653 mask |= FS_DQ_IWARNS;
654 break;
655 case 'r':
656 mask |= FS_DQ_RTBWARNS;
657 break;
658 case 'g':
c614d3bf 659 type |= XFS_GROUP_QUOTA;
5aead01d
NS
660 break;
661 case 'p':
c614d3bf 662 type |= XFS_PROJ_QUOTA;
5aead01d
NS
663 break;
664 case 'u':
c614d3bf 665 type |= XFS_USER_QUOTA;
5aead01d
NS
666 break;
667 default:
668 return command_usage(&warn_cmd);
669 }
670 }
671
672 /*
673 * In the usual case, we need at least 2 more arguments -
674 * one (or more) value and a user name/id.
675 * For setting defaults (-d) we don't want a user name/id.
676 */
677 if (flags & DEFAULTS_FLAG) {
678 if (argc != optind + 1)
679 return command_usage(&warn_cmd);
680 } else if (argc != optind + 2) {
681 return command_usage(&warn_cmd);
682 }
683
684 value = atoi(argv[optind++]);
685 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
686
687 if (!mask)
688 mask = FS_DQ_WARNS_MASK;
689
c614d3bf 690 if (!type) {
5aead01d 691 type = XFS_USER_QUOTA;
c614d3bf
ZL
692 } else if (type != XFS_GROUP_QUOTA &&
693 type != XFS_PROJ_QUOTA &&
694 type != XFS_USER_QUOTA) {
695 return command_usage(&warn_cmd);
696 }
5aead01d 697
67a73d61 698 id = id_from_string(name, type);
eaa5b0b7
DW
699 if (id == -1)
700 return 0;
67a73d61 701
eaa5b0b7 702 set_warnings(id, type, mask, fs_path->fs_name, value);
5aead01d
NS
703 return 0;
704}
705
706void
707edit_init(void)
708{
ad765595 709 limit_cmd.name = "limit";
5aead01d
NS
710 limit_cmd.cfunc = limit_f;
711 limit_cmd.argmin = 2;
712 limit_cmd.argmax = -1;
713 limit_cmd.args = \
c614d3bf 714 _("[-g|-p|-u] bsoft|bhard|isoft|ihard|rtbsoft|rtbhard=N -d|id|name");
5aead01d
NS
715 limit_cmd.oneline = _("modify quota limits");
716 limit_cmd.help = limit_help;
7e59be67 717 limit_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 718
ad765595 719 restore_cmd.name = "restore";
5aead01d
NS
720 restore_cmd.cfunc = restore_f;
721 restore_cmd.argmin = 0;
722 restore_cmd.argmax = -1;
c614d3bf 723 restore_cmd.args = _("[-g|-p|-u] [-f file]");
5aead01d 724 restore_cmd.oneline = _("restore quota limits from a backup file");
7e59be67 725 restore_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 726
ad765595 727 timer_cmd.name = "timer";
5aead01d 728 timer_cmd.cfunc = timer_f;
36dc471c 729 timer_cmd.argmin = 1;
5aead01d 730 timer_cmd.argmax = -1;
36dc471c 731 timer_cmd.args = _("[-bir] [-g|-p|-u] value [-d|id|name]");
75f70580 732 timer_cmd.oneline = _("set quota enforcement timeouts");
5aead01d 733 timer_cmd.help = timer_help;
7e59be67 734 timer_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 735
ad765595 736 warn_cmd.name = "warn";
5aead01d
NS
737 warn_cmd.cfunc = warn_f;
738 warn_cmd.argmin = 2;
739 warn_cmd.argmax = -1;
c614d3bf 740 warn_cmd.args = _("[-bir] [-g|-p|-u] value -d|id|name");
5aead01d
NS
741 warn_cmd.oneline = _("get/set enforcement warning counter");
742 warn_cmd.help = warn_help;
743
744 if (expert) {
745 add_command(&limit_cmd);
746 add_command(&restore_cmd);
747 add_command(&timer_cmd);
748 add_command(&warn_cmd);
749 }
750}