Btrfs-progs: fix arguments check of qgroup limit
[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
28 static const char * const qgroup_cmd_group_usage[] = {
29         "btrfs qgroup <command> [options] <path>",
30         NULL
31 };
32
33 static u64 parse_qgroupid(char *p)
34 {
35         char *s = strchr(p, '/');
36         u64 level;
37         u64 id;
38
39         if (!s)
40                 return atoll(p);
41         level = atoll(p);
42         id = atoll(s + 1);
43
44         return (level << 48) | id;
45 }
46
47 static int qgroup_assign(int assign, int argc, char **argv)
48 {
49         int ret = 0;
50         int fd;
51         int e;
52         char *path = argv[3];
53         struct btrfs_ioctl_qgroup_assign_args args;
54
55         if (check_argc_exact(argc, 4))
56                 return -1;
57
58         memset(&args, 0, sizeof(args));
59         args.assign = assign;
60         args.src = parse_qgroupid(argv[1]);
61         args.dst = parse_qgroupid(argv[2]);
62
63         /*
64          * FIXME src should accept subvol path
65          */
66         if (args.src >= args.dst) {
67                 fprintf(stderr, "ERROR: bad relation requested '%s'\n", path);
68                 return 12;
69         }
70         fd = open_file_or_dir(path);
71         if (fd < 0) {
72                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
73                 return 12;
74         }
75
76         ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
77         e = errno;
78         close(fd);
79         if (ret < 0) {
80                 fprintf(stderr, "ERROR: unable to assign quota group: %s\n",
81                         strerror(e));
82                 return 30;
83         }
84         return 0;
85 }
86
87 static int qgroup_create(int create, int argc, char **argv)
88 {
89         int ret = 0;
90         int fd;
91         int e;
92         char *path = argv[2];
93         struct btrfs_ioctl_qgroup_create_args args;
94
95         if (check_argc_exact(argc, 3))
96                 return -1;
97
98         memset(&args, 0, sizeof(args));
99         args.create = create;
100         args.qgroupid = parse_qgroupid(argv[1]);
101
102         fd = open_file_or_dir(path);
103         if (fd < 0) {
104                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
105                 return 12;
106         }
107
108         ret = ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args);
109         e = errno;
110         close(fd);
111         if (ret < 0) {
112                 fprintf(stderr, "ERROR: unable to create quota group: %s\n",
113                         strerror(e));
114                 return 30;
115         }
116         return 0;
117 }
118
119 void print_qgroup_info(u64 objectid, struct btrfs_qgroup_info_item *info)
120 {
121         printf("%llu/%llu %lld %lld\n", objectid >> 48,
122                 objectid & ((1ll << 48) - 1),
123                 btrfs_stack_qgroup_info_referenced(info),
124                 btrfs_stack_qgroup_info_exclusive(info));
125 }
126
127 int list_qgroups(int fd)
128 {
129         int ret;
130         struct btrfs_ioctl_search_args args;
131         struct btrfs_ioctl_search_key *sk = &args.key;
132         struct btrfs_ioctl_search_header *sh;
133         unsigned long off = 0;
134         unsigned int i;
135         int e;
136         struct btrfs_qgroup_info_item *info;
137
138         memset(&args, 0, sizeof(args));
139
140         /* search in the quota tree */
141         sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
142
143         /*
144          * set the min and max to backref keys.  The search will
145          * only send back this type of key now.
146          */
147         sk->max_type = BTRFS_QGROUP_INFO_KEY;
148         sk->min_type = BTRFS_QGROUP_INFO_KEY;
149         sk->max_objectid = 0;
150         sk->max_offset = (u64)-1;
151         sk->max_transid = (u64)-1;
152
153         /* just a big number, doesn't matter much */
154         sk->nr_items = 4096;
155
156         while (1) {
157                 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
158                 e = errno;
159                 if (ret < 0) {
160                         fprintf(stderr,
161                                 "ERROR: can't perform the search - %s\n",
162                                 strerror(e));
163                         return ret;
164                 }
165                 /* the ioctl returns the number of item it found in nr_items */
166                 if (sk->nr_items == 0)
167                         break;
168
169                 off = 0;
170
171                 /*
172                  * for each item, pull the key out of the header and then
173                  * read the root_ref item it contains
174                  */
175                 for (i = 0; i < sk->nr_items; i++) {
176                         sh = (struct btrfs_ioctl_search_header *)(args.buf +
177                                                                   off);
178                         off += sizeof(*sh);
179
180                         if (sh->objectid != 0)
181                                 goto done;
182
183                         if (sh->type != BTRFS_QGROUP_INFO_KEY)
184                                 goto done;
185
186                         info = (struct btrfs_qgroup_info_item *)
187                                         (args.buf + off);
188                         print_qgroup_info(sh->offset, info);
189
190                         off += sh->len;
191
192                         /*
193                          * record the mins in sk so we can make sure the
194                          * next search doesn't repeat this root
195                          */
196                         sk->min_offset = sh->offset;
197                 }
198                 sk->nr_items = 4096;
199                 /*
200                  * this iteration is done, step forward one qgroup for the next
201                  * ioctl
202                  */
203                 if (sk->min_offset < (u64)-1)
204                         sk->min_offset++;
205                 else
206                         break;
207         }
208
209 done:
210         return ret;
211 }
212
213 static int parse_limit(const char *p, unsigned long long *s)
214 {
215         char *endptr;
216         unsigned long long size;
217
218         if (strcasecmp(p, "none") == 0) {
219                 *s = 0;
220                 return 1;
221         }
222         size = strtoull(p, &endptr, 10);
223         switch (*endptr) {
224         case 'T':
225         case 't':
226                 size *= 1024;
227         case 'G':
228         case 'g':
229                 size *= 1024;
230         case 'M':
231         case 'm':
232                 size *= 1024;
233         case 'K':
234         case 'k':
235                 size *= 1024;
236                 ++endptr;
237                 break;
238         case 0:
239                 break;
240         default:
241                 return 0;
242         }
243
244         if (*endptr)
245                 return 0;
246
247         *s = size;
248
249         return 1;
250 }
251
252 static const char * const cmd_qgroup_assign_usage[] = {
253         "btrfs qgroup assign <src> <dst> <path>",
254         "Enable subvolume qgroup support for a filesystem.",
255         NULL
256 };
257
258 static int cmd_qgroup_assign(int argc, char **argv)
259 {
260         int ret = qgroup_assign(1, argc, argv);
261         if (ret < 0)
262                 usage(cmd_qgroup_assign_usage);
263         return ret;
264 }
265
266 static const char * const cmd_qgroup_remove_usage[] = {
267         "btrfs qgroup remove <src> <dst> <path>",
268         "Remove a subvol from a quota group.",
269         NULL
270 };
271
272 static int cmd_qgroup_remove(int argc, char **argv)
273 {
274         int ret = qgroup_assign(0, argc, argv);
275         if (ret < 0)
276                 usage(cmd_qgroup_remove_usage);
277         return ret;
278 }
279
280 static const char * const cmd_qgroup_create_usage[] = {
281         "btrfs qgroup create <qgroupid> <path>",
282         "Create a subvolume quota group.",
283         NULL
284 };
285
286 static int cmd_qgroup_create(int argc, char **argv)
287 {
288         int ret = qgroup_create(1, argc, argv);
289         if (ret < 0)
290                 usage(cmd_qgroup_create_usage);
291         return ret;
292 }
293
294 static const char * const cmd_qgroup_destroy_usage[] = {
295         "btrfs qgroup destroy <qgroupid> <path>",
296         "Destroy a subvolume quota group.",
297         NULL
298 };
299
300 static int cmd_qgroup_destroy(int argc, char **argv)
301 {
302         int ret = qgroup_create(0, argc, argv);
303         if (ret < 0)
304                 usage(cmd_qgroup_destroy_usage);
305         return ret;
306 }
307
308 static const char * const cmd_qgroup_show_usage[] = {
309         "btrfs qgroup show <path>",
310         "Show all subvolume quota groups.",
311         NULL
312 };
313
314 static int cmd_qgroup_show(int argc, char **argv)
315 {
316         int ret = 0;
317         int fd;
318         char *path = argv[1];
319
320         if (check_argc_exact(argc, 2))
321                 usage(cmd_qgroup_show_usage);
322
323         fd = open_file_or_dir(path);
324         if (fd < 0) {
325                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
326                 return 12;
327         }
328
329         ret = list_qgroups(fd);
330         if (ret < 0) {
331                 fprintf(stderr, "ERROR: can't list qgroups\n");
332                 return 30;
333         }
334
335         close(fd);
336
337         return ret;
338 }
339
340 static const char * const cmd_qgroup_limit_usage[] = {
341         "btrfs qgroup limit [options] <size>|none [<qgroupid>] <path>",
342         "Limit the size of a subvolume quota group.",
343         "",
344         "-c   limit amount of data after compression",
345         "-e   limit space exclusively assigned to this qgroup",
346         NULL
347 };
348
349 static int cmd_qgroup_limit(int argc, char **argv)
350 {
351         int ret = 0;
352         int fd;
353         int e;
354         char *path;
355         struct btrfs_ioctl_qgroup_limit_args args;
356         unsigned long long size;
357         int compressed = 0;
358         int exclusive = 0;
359
360         optind = 1;
361         while (1) {
362                 int c = getopt(argc, argv, "ce");
363                 if (c < 0)
364                         break;
365                 switch (c) {
366                 case 'c':
367                         compressed = 1;
368                         break;
369                 case 'e':
370                         exclusive = 1;
371                         break;
372                 default:
373                         usage(cmd_qgroup_limit_usage);
374                 }
375         }
376
377         if (check_argc_min(argc - optind, 2))
378                 usage(cmd_qgroup_limit_usage);
379
380         if (!parse_limit(argv[optind], &size)) {
381                 fprintf(stderr, "Invalid size argument given\n");
382                 return 1;
383         }
384
385         memset(&args, 0, sizeof(args));
386         args.qgroupid = parse_qgroupid(argv[optind + 1]);
387         if (size) {
388                 if (compressed)
389                         args.lim.flags |= BTRFS_QGROUP_LIMIT_RFER_CMPR |
390                                           BTRFS_QGROUP_LIMIT_EXCL_CMPR;
391                 if (exclusive) {
392                         args.lim.flags |= BTRFS_QGROUP_LIMIT_MAX_EXCL;
393                         args.lim.max_exclusive = size;
394                 } else {
395                         args.lim.flags |= BTRFS_QGROUP_LIMIT_MAX_RFER;
396                         args.lim.max_referenced = size;
397                 }
398         }
399
400         if (args.qgroupid == 0) {
401                 if (check_argc_exact(argc - optind, 2))
402                         usage(cmd_qgroup_limit_usage);
403                 path = argv[optind + 1];
404                 ret = test_issubvolume(path);
405                 if (ret < 0) {
406                         fprintf(stderr, "ERROR: error accessing '%s'\n", path);
407                         return 12;
408                 }
409                 if (!ret) {
410                         fprintf(stderr, "ERROR: '%s' is not a subvolume\n",
411                                 path);
412                         return 13;
413                 }
414                 /*
415                  * keep qgroupid at 0, this indicates that the subvolume the
416                  * fd refers to is to be limited
417                  */
418         } else {
419                 if (check_argc_exact(argc - optind, 3))
420                         usage(cmd_qgroup_limit_usage);
421                 path = argv[optind + 2];
422         }
423
424         fd = open_file_or_dir(path);
425         if (fd < 0) {
426                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
427                 return 12;
428         }
429
430         ret = ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args);
431         e = errno;
432         close(fd);
433         if (ret < 0) {
434                 fprintf(stderr, "ERROR: unable to limit requested quota group: "
435                         "%s\n", strerror(e));
436                 return 30;
437         }
438         return 0;
439 }
440
441 const struct cmd_group qgroup_cmd_group = {
442         qgroup_cmd_group_usage, NULL, {
443                 { "assign", cmd_qgroup_assign, cmd_qgroup_assign_usage, 0, 0 },
444                 { "remove", cmd_qgroup_remove, cmd_qgroup_remove_usage, 0, 0 },
445                 { "create", cmd_qgroup_create, cmd_qgroup_create_usage, 0, 0 },
446                 { "destroy", cmd_qgroup_destroy,
447                   cmd_qgroup_destroy_usage, 0, 0 },
448                 { "show", cmd_qgroup_show, cmd_qgroup_show_usage, 0, 0 },
449                 { "limit", cmd_qgroup_limit, cmd_qgroup_limit_usage, 0, 0 },
450                 { 0, 0, 0, 0, 0 }
451         }
452 };
453
454 int cmd_qgroup(int argc, char **argv)
455 {
456         return handle_command_group(&qgroup_cmd_group, argc, argv);
457 }