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