]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/edit.c
xfs_quota: refactor code to generate id from name
[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);
310 if (id >= 0)
311 set_limits(id, type, mask, fs_path->fs_name,
312 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
313 else
314 exitcode = -1;
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
420static void
421set_timer(
5aead01d
NS
422 uint type,
423 uint mask,
424 char *dev,
425 uint value)
426{
427 fs_disk_quota_t d;
428
429 memset(&d, 0, sizeof(d));
430 d.d_version = FS_DQUOT_VERSION;
5aead01d
NS
431 d.d_flags = type;
432 d.d_fieldmask = mask;
433 d.d_itimer = value;
434 d.d_btimer = value;
435 d.d_rtbtimer = value;
436
e3210fd8
AM
437 if (xfsquotactl(XFS_SETQLIM, dev, type, 0, (void *)&d) < 0) {
438 exitcode = 1;
5aead01d
NS
439 fprintf(stderr, _("%s: cannot set timer: %s\n"),
440 progname, strerror(errno));
e3210fd8 441 }
5aead01d
NS
442}
443
5aead01d
NS
444static int
445timer_f(
446 int argc,
447 char **argv)
448{
5aead01d 449 uint value;
1774874a 450 int c, type = 0, mask = 0;
5aead01d 451
1774874a 452 while ((c = getopt(argc, argv, "bgipru")) != EOF) {
5aead01d 453 switch (c) {
5aead01d
NS
454 case 'b':
455 mask |= FS_DQ_BTIMER;
456 break;
457 case 'i':
458 mask |= FS_DQ_ITIMER;
459 break;
460 case 'r':
461 mask |= FS_DQ_RTBTIMER;
462 break;
463 case 'g':
c614d3bf 464 type |= XFS_GROUP_QUOTA;
5aead01d
NS
465 break;
466 case 'p':
c614d3bf 467 type |= XFS_PROJ_QUOTA;
5aead01d
NS
468 break;
469 case 'u':
c614d3bf 470 type |= XFS_USER_QUOTA;
5aead01d
NS
471 break;
472 default:
473 return command_usage(&timer_cmd);
474 }
475 }
476
1774874a 477 if (argc != optind + 1)
5aead01d 478 return command_usage(&timer_cmd);
5aead01d
NS
479
480 value = cvttime(argv[optind++]);
5aead01d
NS
481
482 if (!mask)
483 mask = FS_DQ_TIMER_MASK;
484
c614d3bf 485 if (!type) {
5aead01d 486 type = XFS_USER_QUOTA;
c614d3bf
ZL
487 } else if (type != XFS_GROUP_QUOTA &&
488 type != XFS_PROJ_QUOTA &&
489 type != XFS_USER_QUOTA) {
490 return command_usage(&timer_cmd);
491 }
5aead01d 492
1774874a 493 set_timer(type, mask, fs_path->fs_name, value);
5aead01d
NS
494 return 0;
495}
496
497static void
498set_warnings(
14f8b681 499 uint32_t id,
5aead01d
NS
500 uint type,
501 uint mask,
502 char *dev,
503 uint value)
504{
505 fs_disk_quota_t d;
506
507 memset(&d, 0, sizeof(d));
508 d.d_version = FS_DQUOT_VERSION;
509 d.d_id = id;
510 d.d_flags = type;
511 d.d_fieldmask = mask;
512 d.d_iwarns = value;
513 d.d_bwarns = value;
514 d.d_rtbwarns = value;
515
e3210fd8
AM
516 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
517 exitcode = 1;
5aead01d
NS
518 fprintf(stderr, _("%s: cannot set warnings: %s\n"),
519 progname, strerror(errno));
e3210fd8 520 }
5aead01d
NS
521}
522
5aead01d
NS
523static int
524warn_f(
525 int argc,
526 char **argv)
527{
528 char *name;
67a73d61 529 uint32_t id;
5aead01d
NS
530 uint value;
531 int c, flags = 0, type = 0, mask = 0;
532
533 while ((c = getopt(argc, argv, "bdgipru")) != EOF) {
534 switch (c) {
535 case 'd':
536 flags |= DEFAULTS_FLAG;
537 break;
538 case 'b':
539 mask |= FS_DQ_BWARNS;
540 break;
541 case 'i':
542 mask |= FS_DQ_IWARNS;
543 break;
544 case 'r':
545 mask |= FS_DQ_RTBWARNS;
546 break;
547 case 'g':
c614d3bf 548 type |= XFS_GROUP_QUOTA;
5aead01d
NS
549 break;
550 case 'p':
c614d3bf 551 type |= XFS_PROJ_QUOTA;
5aead01d
NS
552 break;
553 case 'u':
c614d3bf 554 type |= XFS_USER_QUOTA;
5aead01d
NS
555 break;
556 default:
557 return command_usage(&warn_cmd);
558 }
559 }
560
561 /*
562 * In the usual case, we need at least 2 more arguments -
563 * one (or more) value and a user name/id.
564 * For setting defaults (-d) we don't want a user name/id.
565 */
566 if (flags & DEFAULTS_FLAG) {
567 if (argc != optind + 1)
568 return command_usage(&warn_cmd);
569 } else if (argc != optind + 2) {
570 return command_usage(&warn_cmd);
571 }
572
573 value = atoi(argv[optind++]);
574 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
575
576 if (!mask)
577 mask = FS_DQ_WARNS_MASK;
578
c614d3bf 579 if (!type) {
5aead01d 580 type = XFS_USER_QUOTA;
c614d3bf
ZL
581 } else if (type != XFS_GROUP_QUOTA &&
582 type != XFS_PROJ_QUOTA &&
583 type != XFS_USER_QUOTA) {
584 return command_usage(&warn_cmd);
585 }
5aead01d 586
67a73d61
ES
587 id = id_from_string(name, type);
588 if (id >= 0)
589 set_warnings(id, type, mask, fs_path->fs_name, value);
590 else
591 exitcode = -1;
592
5aead01d
NS
593 return 0;
594}
595
596void
597edit_init(void)
598{
ad765595 599 limit_cmd.name = "limit";
5aead01d
NS
600 limit_cmd.cfunc = limit_f;
601 limit_cmd.argmin = 2;
602 limit_cmd.argmax = -1;
603 limit_cmd.args = \
c614d3bf 604 _("[-g|-p|-u] bsoft|bhard|isoft|ihard|rtbsoft|rtbhard=N -d|id|name");
5aead01d
NS
605 limit_cmd.oneline = _("modify quota limits");
606 limit_cmd.help = limit_help;
7e59be67 607 limit_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 608
ad765595 609 restore_cmd.name = "restore";
5aead01d
NS
610 restore_cmd.cfunc = restore_f;
611 restore_cmd.argmin = 0;
612 restore_cmd.argmax = -1;
c614d3bf 613 restore_cmd.args = _("[-g|-p|-u] [-f file]");
5aead01d 614 restore_cmd.oneline = _("restore quota limits from a backup file");
7e59be67 615 restore_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 616
ad765595 617 timer_cmd.name = "timer";
5aead01d
NS
618 timer_cmd.cfunc = timer_f;
619 timer_cmd.argmin = 2;
620 timer_cmd.argmax = -1;
c614d3bf 621 timer_cmd.args = _("[-bir] [-g|-p|-u] value");
75f70580 622 timer_cmd.oneline = _("set quota enforcement timeouts");
5aead01d 623 timer_cmd.help = timer_help;
7e59be67 624 timer_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 625
ad765595 626 warn_cmd.name = "warn";
5aead01d
NS
627 warn_cmd.cfunc = warn_f;
628 warn_cmd.argmin = 2;
629 warn_cmd.argmax = -1;
c614d3bf 630 warn_cmd.args = _("[-bir] [-g|-p|-u] value -d|id|name");
5aead01d
NS
631 warn_cmd.oneline = _("get/set enforcement warning counter");
632 warn_cmd.help = warn_help;
633
634 if (expert) {
635 add_command(&limit_cmd);
636 add_command(&restore_cmd);
637 add_command(&timer_cmd);
638 add_command(&warn_cmd);
639 }
640}