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