btrfs-progs: qgroup assign: can't handle options
[platform/upstream/btrfs-progs.git] / cmds-qgroup.c
1 /*
2  * Copyright (C) 2012 STRATO.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18
19 #include <sys/ioctl.h>
20 #include <unistd.h>
21 #include <getopt.h>
22
23 #include "ctree.h"
24 #include "ioctl.h"
25
26 #include "commands.h"
27 #include "qgroup.h"
28 #include "utils.h"
29
30 static const char * const qgroup_cmd_group_usage[] = {
31         "btrfs qgroup <command> [options] <path>",
32         NULL
33 };
34
35 static int _cmd_qgroup_assign(int assign, int argc, char **argv,
36                 const char * const *usage_str)
37 {
38         int ret = 0;
39         int fd;
40         int rescan = 0;
41         char *path;
42         struct btrfs_ioctl_qgroup_assign_args args;
43         DIR *dirstream = NULL;
44
45         if (assign) {
46                 while (1) {
47                         enum { GETOPT_VAL_RESCAN = 256 };
48                         static const struct option long_options[] = {
49                                 { "rescan", no_argument, NULL,
50                                         GETOPT_VAL_RESCAN },
51                                 { NULL, 0, NULL, 0 }
52                         };
53                         int c = getopt_long(argc, argv, "", long_options, NULL);
54
55                         if (c < 0)
56                                 break;
57                         switch (c) {
58                         case GETOPT_VAL_RESCAN:
59                                 rescan = 1;
60                                 break;
61                         default:
62                                 /* Usage printed by the caller */
63                                 return -1;
64                         }
65                 }
66         } else {
67                 clean_args_no_options(argc, argv, usage_str);
68         }
69
70         if (check_argc_exact(argc - optind, 3))
71                 usage(usage_str);
72
73         memset(&args, 0, sizeof(args));
74         args.assign = assign;
75         args.src = parse_qgroupid(argv[optind]);
76         args.dst = parse_qgroupid(argv[optind + 1]);
77
78         path = argv[optind + 2];
79
80         /*
81          * FIXME src should accept subvol path
82          */
83         if (btrfs_qgroup_level(args.src) >= btrfs_qgroup_level(args.dst)) {
84                 error("bad relation requested: %s", path);
85                 return 1;
86         }
87         fd = btrfs_open_dir(path, &dirstream, 1);
88         if (fd < 0)
89                 return 1;
90
91         ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
92         if (ret < 0) {
93                 error("unable to assign quota group: %s", strerror(errno));
94                 close_file_or_dir(fd, dirstream);
95                 return 1;
96         }
97
98         /*
99          * If ret > 0, it means assign caused qgroup data inconsistent state.
100          * Schedule a quota rescan if requested.
101          *
102          * The return value change only happens in newer kernel. But will not
103          * cause problem since old kernel has a bug that will never clear
104          * INCONSISTENT bit.
105          */
106         if (ret > 0) {
107                 if (rescan) {
108                         struct btrfs_ioctl_quota_rescan_args qargs;
109
110                         printf("Quota data changed, rescan scheduled\n");
111                         memset(&qargs, 0, sizeof(qargs));
112                         ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &qargs);
113                         if (ret < 0)
114                                 error("quota rescan failed: %s",
115                                         strerror(errno));
116                 } else {
117                         warning("quotas may be inconsistent, rescan needed");
118                 }
119         }
120         close_file_or_dir(fd, dirstream);
121         return ret;
122 }
123
124 static int _cmd_qgroup_create(int create, int argc, char **argv)
125 {
126         int ret = 0;
127         int fd;
128         int e;
129         char *path;
130         struct btrfs_ioctl_qgroup_create_args args;
131         DIR *dirstream = NULL;
132
133         if (check_argc_exact(argc - optind, 2))
134                 return -1;
135
136         memset(&args, 0, sizeof(args));
137         args.create = create;
138         args.qgroupid = parse_qgroupid(argv[optind]);
139         path = argv[optind + 1];
140
141         fd = btrfs_open_dir(path, &dirstream, 1);
142         if (fd < 0)
143                 return 1;
144
145         ret = ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args);
146         e = errno;
147         close_file_or_dir(fd, dirstream);
148         if (ret < 0) {
149                 error("unable to %s quota group: %s",
150                         create ? "create":"destroy", strerror(e));
151                 return 1;
152         }
153         return 0;
154 }
155
156 static int parse_limit(const char *p, unsigned long long *s)
157 {
158         char *endptr;
159         unsigned long long size;
160         unsigned long long CLEAR_VALUE = -1;
161
162         if (strcasecmp(p, "none") == 0) {
163                 *s = CLEAR_VALUE;
164                 return 1;
165         }
166
167         if (p[0] == '-')
168                 return 0;
169
170         size = strtoull(p, &endptr, 10);
171         if (p == endptr)
172                 return 0;
173
174         switch (*endptr) {
175         case 'T':
176         case 't':
177                 size *= 1024;
178                 /* fallthrough */
179         case 'G':
180         case 'g':
181                 size *= 1024;
182                 /* fallthrough */
183         case 'M':
184         case 'm':
185                 size *= 1024;
186                 /* fallthrough */
187         case 'K':
188         case 'k':
189                 size *= 1024;
190                 ++endptr;
191                 break;
192         case 0:
193                 break;
194         default:
195                 return 0;
196         }
197
198         if (*endptr)
199                 return 0;
200
201         *s = size;
202
203         return 1;
204 }
205
206 static const char * const cmd_qgroup_assign_usage[] = {
207         "btrfs qgroup assign [options] <src> <dst> <path>",
208         "Assign SRC as the child qgroup of DST",
209         "",
210         "--rescan       schedule qutoa rescan if needed",
211         "--no-rescan    ",
212         NULL
213 };
214
215 static int cmd_qgroup_assign(int argc, char **argv)
216 {
217         return _cmd_qgroup_assign(1, argc, argv, cmd_qgroup_assign_usage);
218 }
219
220 static const char * const cmd_qgroup_remove_usage[] = {
221         "btrfs qgroup remove <src> <dst> <path>",
222         "Remove a child qgroup SRC from DST.",
223         NULL
224 };
225
226 static int cmd_qgroup_remove(int argc, char **argv)
227 {
228         return _cmd_qgroup_assign(0, argc, argv, cmd_qgroup_remove_usage);
229 }
230
231 static const char * const cmd_qgroup_create_usage[] = {
232         "btrfs qgroup create <qgroupid> <path>",
233         "Create a subvolume quota group.",
234         NULL
235 };
236
237 static int cmd_qgroup_create(int argc, char **argv)
238 {
239         int ret;
240
241         clean_args_no_options(argc, argv, cmd_qgroup_create_usage);
242
243         ret = _cmd_qgroup_create(1, argc, argv);
244
245         if (ret < 0)
246                 usage(cmd_qgroup_create_usage);
247         return ret;
248 }
249
250 static const char * const cmd_qgroup_destroy_usage[] = {
251         "btrfs qgroup destroy <qgroupid> <path>",
252         "Destroy a quota group.",
253         NULL
254 };
255
256 static int cmd_qgroup_destroy(int argc, char **argv)
257 {
258         int ret;
259
260         clean_args_no_options(argc, argv, cmd_qgroup_destroy_usage);
261
262         ret = _cmd_qgroup_create(0, argc, argv);
263
264         if (ret < 0)
265                 usage(cmd_qgroup_destroy_usage);
266         return ret;
267 }
268
269 static const char * const cmd_qgroup_show_usage[] = {
270         "btrfs qgroup show -pcreFf "
271         "[--sort=qgroupid,rfer,excl,max_rfer,max_excl] <path>",
272         "Show subvolume quota groups.",
273         "-p             print parent qgroup id",
274         "-c             print child qgroup id",
275         "-r             print limit of referenced size of qgroup",
276         "-e             print limit of exclusive size of qgroup",
277         "-F             list all qgroups which impact the given path",
278         "               (including ancestral qgroups)",
279         "-f             list all qgroups which impact the given path",
280         "               (excluding ancestral qgroups)",
281         HELPINFO_UNITS_LONG,
282         "--sort=qgroupid,rfer,excl,max_rfer,max_excl",
283         "               list qgroups sorted by specified items",
284         "               you can use '+' or '-' in front of each item.",
285         "               (+:ascending, -:descending, ascending default)",
286         NULL
287 };
288
289 static int cmd_qgroup_show(int argc, char **argv)
290 {
291         char *path;
292         int ret = 0;
293         int fd;
294         int e;
295         DIR *dirstream = NULL;
296         u64 qgroupid;
297         int filter_flag = 0;
298         unsigned unit_mode;
299
300         struct btrfs_qgroup_comparer_set *comparer_set;
301         struct btrfs_qgroup_filter_set *filter_set;
302         filter_set = btrfs_qgroup_alloc_filter_set();
303         comparer_set = btrfs_qgroup_alloc_comparer_set();
304
305         unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
306
307         optind = 1;
308         while (1) {
309                 int c;
310                 static const struct option long_options[] = {
311                         {"sort", required_argument, NULL, 'S'},
312                         { NULL, 0, NULL, 0 }
313                 };
314
315                 c = getopt_long(argc, argv, "pcreFf", long_options, NULL);
316                 if (c < 0)
317                         break;
318                 switch (c) {
319                 case 'p':
320                         btrfs_qgroup_setup_print_column(
321                                 BTRFS_QGROUP_PARENT);
322                         break;
323                 case 'c':
324                         btrfs_qgroup_setup_print_column(
325                                 BTRFS_QGROUP_CHILD);
326                         break;
327                 case 'r':
328                         btrfs_qgroup_setup_print_column(
329                                 BTRFS_QGROUP_MAX_RFER);
330                         break;
331                 case 'e':
332                         btrfs_qgroup_setup_print_column(
333                                 BTRFS_QGROUP_MAX_EXCL);
334                         break;
335                 case 'F':
336                         filter_flag |= 0x1;
337                         break;
338                 case 'f':
339                         filter_flag |= 0x2;
340                         break;
341                 case 'S':
342                         ret = btrfs_qgroup_parse_sort_string(optarg,
343                                                              &comparer_set);
344                         if (ret)
345                                 usage(cmd_qgroup_show_usage);
346                         break;
347                 default:
348                         usage(cmd_qgroup_show_usage);
349                 }
350         }
351         btrfs_qgroup_setup_units(unit_mode);
352
353         if (check_argc_exact(argc - optind, 1))
354                 usage(cmd_qgroup_show_usage);
355
356         path = argv[optind];
357         fd = btrfs_open_dir(path, &dirstream, 1);
358         if (fd < 0) {
359                 btrfs_qgroup_free_filter_set(filter_set);
360                 btrfs_qgroup_free_comparer_set(comparer_set);
361                 return 1;
362         }
363
364         if (filter_flag) {
365                 qgroupid = btrfs_get_path_rootid(fd);
366                 if (filter_flag & 0x1)
367                         btrfs_qgroup_setup_filter(&filter_set,
368                                         BTRFS_QGROUP_FILTER_ALL_PARENT,
369                                         qgroupid);
370                 if (filter_flag & 0x2)
371                         btrfs_qgroup_setup_filter(&filter_set,
372                                         BTRFS_QGROUP_FILTER_PARENT,
373                                         qgroupid);
374         }
375         ret = btrfs_show_qgroups(fd, filter_set, comparer_set);
376         e = errno;
377         close_file_or_dir(fd, dirstream);
378         if (ret < 0)
379                 error("can't list qgroups: %s", strerror(e));
380
381         return !!ret;
382 }
383
384 static const char * const cmd_qgroup_limit_usage[] = {
385         "btrfs qgroup limit [options] <size>|none [<qgroupid>] <path>",
386         "Set the limits a subvolume quota group.",
387         "",
388         "-c   limit amount of data after compression. This is the default,",
389         "     it is currently not possible to turn off this option.",
390         "-e   limit space exclusively assigned to this qgroup",
391         NULL
392 };
393
394 static int cmd_qgroup_limit(int argc, char **argv)
395 {
396         int ret = 0;
397         int fd;
398         int e;
399         char *path = NULL;
400         struct btrfs_ioctl_qgroup_limit_args args;
401         unsigned long long size;
402         int compressed = 0;
403         int exclusive = 0;
404         DIR *dirstream = NULL;
405
406         optind = 1;
407         while (1) {
408                 int c = getopt(argc, argv, "ce");
409                 if (c < 0)
410                         break;
411                 switch (c) {
412                 case 'c':
413                         compressed = 1;
414                         break;
415                 case 'e':
416                         exclusive = 1;
417                         break;
418                 default:
419                         usage(cmd_qgroup_limit_usage);
420                 }
421         }
422
423         if (check_argc_min(argc - optind, 2))
424                 usage(cmd_qgroup_limit_usage);
425
426         if (!parse_limit(argv[optind], &size)) {
427                 error("invalid size argument: %s", argv[optind]);
428                 return 1;
429         }
430
431         memset(&args, 0, sizeof(args));
432         if (compressed)
433                 args.lim.flags |= BTRFS_QGROUP_LIMIT_RFER_CMPR |
434                                   BTRFS_QGROUP_LIMIT_EXCL_CMPR;
435         if (exclusive) {
436                 args.lim.flags |= BTRFS_QGROUP_LIMIT_MAX_EXCL;
437                 args.lim.max_exclusive = size;
438         } else {
439                 args.lim.flags |= BTRFS_QGROUP_LIMIT_MAX_RFER;
440                 args.lim.max_referenced = size;
441         }
442
443         if (argc - optind == 2) {
444                 args.qgroupid = 0;
445                 path = argv[optind + 1];
446                 ret = test_issubvolume(path);
447                 if (ret < 0) {
448                         error("cannot access '%s': %s", path, strerror(-ret));
449                         return 1;
450                 }
451                 if (!ret) {
452                         error("'%s' is not a subvolume", path);
453                         return 1;
454                 }
455                 /*
456                  * keep qgroupid at 0, this indicates that the subvolume the
457                  * fd refers to is to be limited
458                  */
459         } else if (argc - optind == 3) {
460                 args.qgroupid = parse_qgroupid(argv[optind + 1]);
461                 path = argv[optind + 2];
462         } else
463                 usage(cmd_qgroup_limit_usage);
464
465         fd = btrfs_open_dir(path, &dirstream, 1);
466         if (fd < 0)
467                 return 1;
468
469         ret = ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args);
470         e = errno;
471         close_file_or_dir(fd, dirstream);
472         if (ret < 0) {
473                 error("unable to limit requested quota group: %s", strerror(e));
474                 return 1;
475         }
476         return 0;
477 }
478
479 static const char qgroup_cmd_group_info[] =
480 "manage quota groups";
481
482 const struct cmd_group qgroup_cmd_group = {
483         qgroup_cmd_group_usage, qgroup_cmd_group_info, {
484                 { "assign", cmd_qgroup_assign, cmd_qgroup_assign_usage,
485                    NULL, 0 },
486                 { "remove", cmd_qgroup_remove, cmd_qgroup_remove_usage,
487                    NULL, 0 },
488                 { "create", cmd_qgroup_create, cmd_qgroup_create_usage,
489                    NULL, 0 },
490                 { "destroy", cmd_qgroup_destroy, cmd_qgroup_destroy_usage,
491                    NULL, 0 },
492                 { "show", cmd_qgroup_show, cmd_qgroup_show_usage,
493                    NULL, 0 },
494                 { "limit", cmd_qgroup_limit, cmd_qgroup_limit_usage,
495                    NULL, 0 },
496                 NULL_CMD_STRUCT
497         }
498 };
499
500 int cmd_qgroup(int argc, char **argv)
501 {
502         return handle_command_group(&qgroup_cmd_group, argc, argv);
503 }