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