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