]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/edit.c
fiemap: Make max_extents a global var
[thirdparty/xfsprogs-dev.git] / quota / edit.c
1 /*
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
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
7 * published by the Free Software Foundation.
8 *
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.
13 *
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
17 */
18
19 #include <pwd.h>
20 #include <grp.h>
21 #include <ctype.h>
22 #include "input.h"
23 #include "command.h"
24 #include "init.h"
25 #include "quota.h"
26
27 static cmdinfo_t limit_cmd;
28 static cmdinfo_t restore_cmd;
29 static cmdinfo_t timer_cmd;
30 static cmdinfo_t warn_cmd;
31
32 static void
33 limit_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
55 static void
56 timer_help(void)
57 {
58 printf(_(
59 "\n"
60 " modify quota enforcement timeout for the current filesystem\n"
61 "\n"
62 " Example:\n"
63 " 'timer -i 3days'\n"
64 " (soft inode limit timer is changed to 3 days)\n"
65 "\n"
66 " Changes the timeout value associated with the block limits, inode limits\n"
67 " and/or realtime block limits for all users, groups, or projects on the\n"
68 " current filesystem.\n"
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"
84 "\n"));
85 }
86
87 static void
88 warn_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
116 static void
117 set_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
143 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
144 exitcode = 1;
145 fprintf(stderr, _("%s: cannot set limits: %s\n"),
146 progname, strerror(errno));
147 }
148 }
149
150 static void
151 set_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
164 if (uid == -1) {
165 exitcode = 1;
166 fprintf(stderr, _("%s: invalid user name: %s\n"),
167 progname, name);
168 } else
169 set_limits(uid, type, mask, fs_path->fs_name,
170 bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
171 }
172
173 static void
174 set_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
187 if (gid == -1) {
188 exitcode = 1;
189 fprintf(stderr, _("%s: invalid group name: %s\n"),
190 progname, name);
191 } else
192 set_limits(gid, type, mask, fs_path->fs_name,
193 bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
194 }
195
196 static void
197 set_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
210 if (prid == -1) {
211 exitcode = 1;
212 fprintf(stderr, _("%s: invalid project name: %s\n"),
213 progname, name);
214 } else
215 set_limits(prid, type, mask, fs_path->fs_name,
216 bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
217 }
218
219 /* extract number of blocks from an ascii string */
220 static int
221 extractb(
222 char *string,
223 const char *prefix,
224 int length,
225 uint blocksize,
226 uint sectorsize,
227 uint64_t *value)
228 {
229 long long v;
230 char *s = string;
231
232 if (strncmp(string, prefix, length) == 0) {
233 s = string + length + 1;
234 v = cvtnum(blocksize, sectorsize, s);
235 if (v == -1LL) {
236 fprintf(stderr,
237 _("%s: Error: could not parse size %s.\n"),
238 progname, s);
239 return 0;
240 }
241 *value = (uint64_t)v >> 9; /* syscalls use basic blocks */
242 if (v > 0 && *value == 0)
243 fprintf(stderr, _("%s: Warning: `%s' in quota blocks is 0 (unlimited).\n"), progname, s);
244 return 1;
245 }
246 return 0;
247 }
248
249 /* extract number of inodes from an ascii string */
250 static int
251 extracti(
252 char *string,
253 const char *prefix,
254 int length,
255 uint64_t *value)
256 {
257 char *sp, *s = string;
258
259 if (strncmp(string, prefix, length) == 0) {
260 s = string + length + 1;
261 *value = strtoll(s, &sp, 0);
262 return 1;
263 }
264 return 0;
265 }
266
267 static int
268 limit_f(
269 int argc,
270 char **argv)
271 {
272 char *name;
273 uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
274 int c, type = 0, mask = 0, flags = 0;
275 uint bsize, ssize, endoptions;
276
277 init_cvtnum(&bsize, &ssize);
278 bsoft = bhard = isoft = ihard = rtbsoft = rtbhard = 0;
279 while ((c = getopt(argc, argv, "dgpu")) != EOF) {
280 switch (c) {
281 case 'd':
282 flags |= DEFAULTS_FLAG;
283 break;
284 case 'g':
285 type |= XFS_GROUP_QUOTA;
286 break;
287 case 'p':
288 type |= XFS_PROJ_QUOTA;
289 break;
290 case 'u':
291 type |= XFS_USER_QUOTA;
292 break;
293 default:
294 return command_usage(&limit_cmd);
295 }
296 }
297
298 /*
299 * In the usual case, we need at least 2 more arguments -
300 * one (or more) limits and a user name/id.
301 * For setting defaults (-d) we don't want a user name/id.
302 */
303 if (flags & DEFAULTS_FLAG) {
304 if (argc < optind + 1)
305 return command_usage(&limit_cmd);
306 endoptions = 1;
307 } else if (argc < optind + 2) {
308 return command_usage(&limit_cmd);
309 } else {
310 endoptions = 2;
311 }
312
313 /*
314 * Extract limit values from remaining optional arguments.
315 */
316 while (argc > optind + endoptions - 1) {
317 char *s = argv[optind++];
318 if (extractb(s, "bsoft=", 5, bsize, ssize, &bsoft))
319 mask |= FS_DQ_BSOFT;
320 else if (extractb(s, "bhard=", 5, bsize, ssize, &bhard))
321 mask |= FS_DQ_BHARD;
322 else if (extracti(s, "isoft=", 5, &isoft))
323 mask |= FS_DQ_ISOFT;
324 else if (extracti(s, "ihard=", 5, &ihard))
325 mask |= FS_DQ_IHARD;
326 else if (extractb(s, "rtbsoft=", 7, bsize, ssize, &rtbsoft))
327 mask |= FS_DQ_RTBSOFT;
328 else if (extractb(s, "rtbhard=", 7, bsize, ssize, &rtbhard))
329 mask |= FS_DQ_RTBHARD;
330 else {
331 exitcode = 1;
332 fprintf(stderr, _("%s: unrecognised argument %s\n"),
333 progname, s);
334 return 0;
335 }
336 }
337 if (!mask) {
338 exitcode = 1;
339 fprintf(stderr, _("%s: cannot find any valid arguments\n"),
340 progname);
341 return 0;
342 }
343
344 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
345
346 if (!type) {
347 type = XFS_USER_QUOTA;
348 } else if (type != XFS_GROUP_QUOTA &&
349 type != XFS_PROJ_QUOTA &&
350 type != XFS_USER_QUOTA) {
351 return command_usage(&limit_cmd);
352 }
353
354 switch (type) {
355 case XFS_USER_QUOTA:
356 set_user_limits(name, type, mask,
357 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
358 break;
359 case XFS_GROUP_QUOTA:
360 set_group_limits(name, type, mask,
361 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
362 break;
363 case XFS_PROJ_QUOTA:
364 set_project_limits(name, type, mask,
365 &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
366 break;
367 }
368 return 0;
369 }
370
371 /*
372 * Iterate through input file, restoring the limits.
373 * File format is as follows:
374 * fs = <device>
375 * <numeric id> bsoft bhard isoft ihard [rtbsoft rtbhard]
376 */
377 static void
378 restore_file(
379 FILE *fp,
380 uint type)
381 {
382 char buffer[512];
383 char devbuffer[512];
384 char *dev = NULL;
385 uint mask;
386 int cnt;
387 uint32_t id;
388 uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
389
390 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
391 if (strncmp("fs = ", buffer, 5) == 0) {
392 dev = strncpy(devbuffer, buffer+5, sizeof(devbuffer));
393 dev[strlen(dev) - 1] = '\0';
394 continue;
395 }
396 rtbsoft = rtbhard = 0;
397 cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n",
398 &id,
399 (unsigned long long *)&bsoft,
400 (unsigned long long *)&bhard,
401 (unsigned long long *)&isoft,
402 (unsigned long long *)&ihard,
403 (unsigned long long *)&rtbsoft,
404 (unsigned long long *)&rtbhard);
405 if (cnt == 5 || cnt == 7) {
406 mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD;
407 if (cnt == 7)
408 mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD;
409 set_limits(id, type, mask, dev, &bsoft, &bhard,
410 &isoft, &ihard, &rtbsoft, &rtbhard);
411 }
412 }
413 }
414
415 static int
416 restore_f(
417 int argc,
418 char **argv)
419 {
420 FILE *fp = stdin;
421 char *fname = NULL;
422 int c, type = 0;
423
424 while ((c = getopt(argc, argv, "f:gpu")) != EOF) {
425 switch (c) {
426 case 'f':
427 fname = optarg;
428 break;
429 case 'g':
430 type |= XFS_GROUP_QUOTA;
431 break;
432 case 'p':
433 type |= XFS_PROJ_QUOTA;
434 break;
435 case 'u':
436 type |= XFS_USER_QUOTA;
437 break;
438 default:
439 return command_usage(&restore_cmd);
440 }
441 }
442
443 if (argc < optind)
444 return command_usage(&restore_cmd);
445
446 if (!type) {
447 type = XFS_USER_QUOTA;
448 } else if (type != XFS_GROUP_QUOTA &&
449 type != XFS_PROJ_QUOTA &&
450 type != XFS_USER_QUOTA) {
451 return command_usage(&restore_cmd);
452 }
453
454 if (fname) {
455 if ((fp = fopen(fname, "r")) == NULL) {
456 exitcode = 1;
457 fprintf(stderr, _("%s: fopen on %s failed: %s\n"),
458 progname, fname, strerror(errno));
459 return 0;
460 }
461 }
462
463 restore_file(fp, type);
464
465 if (fname)
466 fclose(fp);
467 return 0;
468 }
469
470 static void
471 set_timer(
472 uint type,
473 uint mask,
474 char *dev,
475 uint value)
476 {
477 fs_disk_quota_t d;
478
479 memset(&d, 0, sizeof(d));
480 d.d_version = FS_DQUOT_VERSION;
481 d.d_flags = type;
482 d.d_fieldmask = mask;
483 d.d_itimer = value;
484 d.d_btimer = value;
485 d.d_rtbtimer = value;
486
487 if (xfsquotactl(XFS_SETQLIM, dev, type, 0, (void *)&d) < 0) {
488 exitcode = 1;
489 fprintf(stderr, _("%s: cannot set timer: %s\n"),
490 progname, strerror(errno));
491 }
492 }
493
494 static int
495 timer_f(
496 int argc,
497 char **argv)
498 {
499 uint value;
500 int c, type = 0, mask = 0;
501
502 while ((c = getopt(argc, argv, "bgipru")) != EOF) {
503 switch (c) {
504 case 'b':
505 mask |= FS_DQ_BTIMER;
506 break;
507 case 'i':
508 mask |= FS_DQ_ITIMER;
509 break;
510 case 'r':
511 mask |= FS_DQ_RTBTIMER;
512 break;
513 case 'g':
514 type |= XFS_GROUP_QUOTA;
515 break;
516 case 'p':
517 type |= XFS_PROJ_QUOTA;
518 break;
519 case 'u':
520 type |= XFS_USER_QUOTA;
521 break;
522 default:
523 return command_usage(&timer_cmd);
524 }
525 }
526
527 if (argc != optind + 1)
528 return command_usage(&timer_cmd);
529
530 value = cvttime(argv[optind++]);
531
532 if (!mask)
533 mask = FS_DQ_TIMER_MASK;
534
535 if (!type) {
536 type = XFS_USER_QUOTA;
537 } else if (type != XFS_GROUP_QUOTA &&
538 type != XFS_PROJ_QUOTA &&
539 type != XFS_USER_QUOTA) {
540 return command_usage(&timer_cmd);
541 }
542
543 set_timer(type, mask, fs_path->fs_name, value);
544 return 0;
545 }
546
547 static void
548 set_warnings(
549 uint32_t id,
550 uint type,
551 uint mask,
552 char *dev,
553 uint value)
554 {
555 fs_disk_quota_t d;
556
557 memset(&d, 0, sizeof(d));
558 d.d_version = FS_DQUOT_VERSION;
559 d.d_id = id;
560 d.d_flags = type;
561 d.d_fieldmask = mask;
562 d.d_iwarns = value;
563 d.d_bwarns = value;
564 d.d_rtbwarns = value;
565
566 if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
567 exitcode = 1;
568 fprintf(stderr, _("%s: cannot set warnings: %s\n"),
569 progname, strerror(errno));
570 }
571 }
572
573 static void
574 set_user_warnings(
575 char *name,
576 uint type,
577 uint mask,
578 uint value)
579 {
580 uid_t uid = uid_from_string(name);
581
582 if (uid == -1) {
583 exitcode = 1;
584 fprintf(stderr, _("%s: invalid user name: %s\n"),
585 progname, name);
586 } else
587 set_warnings(uid, type, mask, fs_path->fs_name, value);
588 }
589
590 static void
591 set_group_warnings(
592 char *name,
593 uint type,
594 uint mask,
595 uint value)
596 {
597 gid_t gid = gid_from_string(name);
598
599 if (gid == -1) {
600 exitcode = 1;
601 fprintf(stderr, _("%s: invalid group name: %s\n"),
602 progname, name);
603 } else
604 set_warnings(gid, type, mask, fs_path->fs_name, value);
605 }
606
607 static void
608 set_project_warnings(
609 char *name,
610 uint type,
611 uint mask,
612 uint value)
613 {
614 prid_t prid = prid_from_string(name);
615
616 if (prid == -1) {
617 exitcode = 1;
618 fprintf(stderr, _("%s: invalid project name: %s\n"),
619 progname, name);
620 } else
621 set_warnings(prid, type, mask, fs_path->fs_name, value);
622 }
623
624 static int
625 warn_f(
626 int argc,
627 char **argv)
628 {
629 char *name;
630 uint value;
631 int c, flags = 0, type = 0, mask = 0;
632
633 while ((c = getopt(argc, argv, "bdgipru")) != EOF) {
634 switch (c) {
635 case 'd':
636 flags |= DEFAULTS_FLAG;
637 break;
638 case 'b':
639 mask |= FS_DQ_BWARNS;
640 break;
641 case 'i':
642 mask |= FS_DQ_IWARNS;
643 break;
644 case 'r':
645 mask |= FS_DQ_RTBWARNS;
646 break;
647 case 'g':
648 type |= XFS_GROUP_QUOTA;
649 break;
650 case 'p':
651 type |= XFS_PROJ_QUOTA;
652 break;
653 case 'u':
654 type |= XFS_USER_QUOTA;
655 break;
656 default:
657 return command_usage(&warn_cmd);
658 }
659 }
660
661 /*
662 * In the usual case, we need at least 2 more arguments -
663 * one (or more) value and a user name/id.
664 * For setting defaults (-d) we don't want a user name/id.
665 */
666 if (flags & DEFAULTS_FLAG) {
667 if (argc != optind + 1)
668 return command_usage(&warn_cmd);
669 } else if (argc != optind + 2) {
670 return command_usage(&warn_cmd);
671 }
672
673 value = atoi(argv[optind++]);
674 name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
675
676 if (!mask)
677 mask = FS_DQ_WARNS_MASK;
678
679 if (!type) {
680 type = XFS_USER_QUOTA;
681 } else if (type != XFS_GROUP_QUOTA &&
682 type != XFS_PROJ_QUOTA &&
683 type != XFS_USER_QUOTA) {
684 return command_usage(&warn_cmd);
685 }
686
687 switch (type) {
688 case XFS_USER_QUOTA:
689 set_user_warnings(name, type, mask, value);
690 break;
691 case XFS_GROUP_QUOTA:
692 set_group_warnings(name, type, mask, value);
693 break;
694 case XFS_PROJ_QUOTA:
695 set_project_warnings(name, type, mask, value);
696 break;
697 }
698 return 0;
699 }
700
701 void
702 edit_init(void)
703 {
704 limit_cmd.name = "limit";
705 limit_cmd.cfunc = limit_f;
706 limit_cmd.argmin = 2;
707 limit_cmd.argmax = -1;
708 limit_cmd.args = \
709 _("[-g|-p|-u] bsoft|bhard|isoft|ihard|rtbsoft|rtbhard=N -d|id|name");
710 limit_cmd.oneline = _("modify quota limits");
711 limit_cmd.help = limit_help;
712 limit_cmd.flags = CMD_FLAG_FOREIGN_OK;
713
714 restore_cmd.name = "restore";
715 restore_cmd.cfunc = restore_f;
716 restore_cmd.argmin = 0;
717 restore_cmd.argmax = -1;
718 restore_cmd.args = _("[-g|-p|-u] [-f file]");
719 restore_cmd.oneline = _("restore quota limits from a backup file");
720 restore_cmd.flags = CMD_FLAG_FOREIGN_OK;
721
722 timer_cmd.name = "timer";
723 timer_cmd.cfunc = timer_f;
724 timer_cmd.argmin = 2;
725 timer_cmd.argmax = -1;
726 timer_cmd.args = _("[-bir] [-g|-p|-u] value");
727 timer_cmd.oneline = _("set quota enforcement timeouts");
728 timer_cmd.help = timer_help;
729 timer_cmd.flags = CMD_FLAG_FOREIGN_OK;
730
731 warn_cmd.name = "warn";
732 warn_cmd.cfunc = warn_f;
733 warn_cmd.argmin = 2;
734 warn_cmd.argmax = -1;
735 warn_cmd.args = _("[-bir] [-g|-p|-u] value -d|id|name");
736 warn_cmd.oneline = _("get/set enforcement warning counter");
737 warn_cmd.help = warn_help;
738
739 if (expert) {
740 add_command(&limit_cmd);
741 add_command(&restore_cmd);
742 add_command(&timer_cmd);
743 add_command(&warn_cmd);
744 }
745 }