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