btrfs-progs: Replace the overkill assert with normal error message.
[platform/upstream/btrfs-progs.git] / cmds-property.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24
25 #include "commands.h"
26 #include "props.h"
27 #include "ctree.h"
28
29 static const char * const property_cmd_group_usage[] = {
30         "btrfs property get/set/list [-t <type>] <object> [<name>] [value]",
31         NULL
32 };
33
34 static const char * const cmd_get_usage[] = {
35         "btrfs property get [-t <type>] <object> [<name>]",
36         "Gets a property from a btrfs object.",
37         "If no name is specified, all properties for the given object are",
38         "printed.",
39         "A filesystem object can be a the filesystem itself, a subvolume,",
40         "an inode or a device. The '-t <type>' option can be used to explicitly",
41         "specify what type of object you meant. This is only needed when a",
42         "property could be set for more then one object type. Possible types",
43         "are s[ubvol], f[ilesystem], i[node] and d[evice].",
44         NULL
45 };
46
47 static const char * const cmd_set_usage[] = {
48         "btrfs property set [-t <type>] <object> <name> <value>",
49         "Sets a property on a btrfs object.",
50         "Please see the help of 'btrfs property get' for a description of",
51         "objects and object types.",
52         NULL
53 };
54
55 static const char * const cmd_list_usage[] = {
56         "btrfs property list [-t <type>] <object>",
57         "Lists available properties with their descriptions for the given object.",
58         "Please see the help of 'btrfs property get' for a description of",
59         "objects and object types.",
60         NULL
61 };
62
63 static int parse_prop(const char *arg, const struct prop_handler *props,
64                       const struct prop_handler **prop_ret)
65 {
66         const struct prop_handler *prop = props;
67
68         for (; prop->name; prop++) {
69                 if (!strcmp(prop->name, arg)) {
70                         *prop_ret = prop;
71                         return 0;
72                 }
73         }
74
75         return -1;
76 }
77
78 static int get_fsid(const char *path, u8 *fsid, int silent)
79 {
80         int ret;
81         int fd;
82         struct btrfs_ioctl_fs_info_args args;
83
84         fd = open(path, O_RDONLY);
85         if (fd < 0) {
86                 ret = -errno;
87                 if (!silent)
88                         fprintf(stderr, "ERROR: open %s failed. %s\n", path,
89                                 strerror(-ret));
90                 goto out;
91         }
92
93         ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
94         if (ret < 0) {
95                 ret = -errno;
96                 goto out;
97         }
98
99         memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
100         ret = 0;
101
102 out:
103         if (fd != -1)
104                 close(fd);
105         return ret;
106 }
107
108 static int check_btrfs_object(const char *object)
109 {
110         int ret;
111         u8 fsid[BTRFS_FSID_SIZE];
112
113         ret = get_fsid(object, fsid, 0);
114         if (ret < 0)
115                 ret = 0;
116         else
117                 ret = 1;
118         return ret;
119 }
120
121 static int check_is_root(const char *object)
122 {
123         int ret;
124         u8 fsid[BTRFS_FSID_SIZE];
125         u8 fsid2[BTRFS_FSID_SIZE];
126         char *tmp;
127
128         tmp = malloc(strlen(object) + 5);
129         if (!tmp) {
130                 ret = -ENOMEM;
131                 goto out;
132         }
133         strcpy(tmp, object);
134         if (tmp[strlen(tmp) - 1] != '/')
135                 strcat(tmp, "/");
136         strcat(tmp, "..");
137
138         ret = get_fsid(object, fsid, 0);
139         if (ret < 0) {
140                 fprintf(stderr, "ERROR: get_fsid for %s failed. %s\n", object,
141                                 strerror(-ret));
142                 goto out;
143         }
144
145         ret = get_fsid(tmp, fsid2, 1);
146         if (ret == -ENOTTY) {
147                 ret = 0;
148                 goto out;
149         } else if (ret == -ENOTDIR) {
150                 ret = 1;
151                 goto out;
152         } else if (ret < 0) {
153                 fprintf(stderr, "ERROR: get_fsid for %s failed. %s\n", tmp,
154                         strerror(-ret));
155                 goto out;
156         }
157
158         if (memcmp(fsid, fsid2, BTRFS_FSID_SIZE)) {
159                 ret = 0;
160                 goto out;
161         }
162
163         ret = 1;
164
165 out:
166         free(tmp);
167         return ret;
168 }
169
170 static int count_bits(int v)
171 {
172         unsigned int tmp = (unsigned int)v;
173         int cnt = 0;
174
175         while (tmp) {
176                 if (tmp & 1)
177                         cnt++;
178                 tmp >>= 1;
179         }
180         return cnt;
181 }
182
183 static int autodetect_object_types(const char *object, int *types_out)
184 {
185         int ret;
186         int is_btrfs_object;
187         int types = 0;
188         struct stat st;
189
190         is_btrfs_object = check_btrfs_object(object);
191
192         ret = lstat(object, &st);
193         if (ret < 0) {
194                 ret = -errno;
195                 goto out;
196         }
197
198         if (is_btrfs_object) {
199                 types |= prop_object_inode;
200                 if (st.st_ino == BTRFS_FIRST_FREE_OBJECTID)
201                         types |= prop_object_subvol;
202
203                 ret = check_is_root(object);
204                 if (ret < 0)
205                         goto out;
206                 if (!ret)
207                         types |= prop_object_root;
208         }
209
210         if (S_ISBLK(st.st_mode))
211                 types |= prop_object_dev;
212
213         ret = 0;
214         *types_out = types;
215
216 out:
217         return ret;
218 }
219
220 static int print_prop_help(const struct prop_handler *prop)
221 {
222         fprintf(stdout, "%-20s%s\n", prop->name, prop->desc);
223         return 0;
224 }
225
226 static int dump_prop(const struct prop_handler *prop,
227                      const char *object,
228                      int types,
229                      int type,
230                      int name_and_help)
231 {
232         int ret = 0;
233
234         if ((types & type) && (prop->types & type)) {
235                 if (!name_and_help)
236                         ret = prop->handler(type, object, prop->name, NULL);
237                 else
238                         ret = print_prop_help(prop);
239         }
240         return ret;
241 }
242
243 static int dump_props(int types, const char *object, int name_and_help)
244 {
245         int ret;
246         int i;
247         int j;
248         const struct prop_handler *prop;
249
250         for (i = 0; prop_handlers[i].name; i++) {
251                 prop = &prop_handlers[i];
252                 for (j = 1; j < __prop_object_max; j <<= 1) {
253                         ret = dump_prop(prop, object, types, j, name_and_help);
254                         if (ret < 0) {
255                                 ret = 50;
256                                 goto out;
257                         }
258                 }
259         }
260
261         ret = 0;
262
263 out:
264         return ret;
265 }
266
267 static int setget_prop(int types, const char *object,
268                        const char *name, const char *value)
269 {
270         int ret;
271         const struct prop_handler *prop = NULL;
272
273         ret = parse_prop(name, prop_handlers, &prop);
274         if (ret == -1) {
275                 fprintf(stderr, "ERROR: property is unknown\n");
276                 ret = 40;
277                 goto out;
278         } else if (ret) {
279                 fprintf(stderr, "ERROR: parse_prop reported unknown error\n");
280                 ret = 42;
281                 goto out;
282         }
283
284         types &= prop->types;
285         if (!types) {
286                 fprintf(stderr,
287                         "ERROR: object is not compatible with property\n");
288                 ret = 47;
289                 goto out;
290         }
291
292         if (count_bits(types) > 1) {
293                 fprintf(stderr,
294                         "ERROR: type of object is ambiguous. Please specify a type by hand.\n");
295                 ret = 48;
296                 goto out;
297         }
298
299         if (value && prop->read_only) {
300                 fprintf(stderr, "ERROR: %s is a read-only property.\n",
301                                 prop->name);
302                 ret = 51;
303                 goto out;
304         }
305
306         ret = prop->handler(types, object, name, value);
307
308         if (ret < 0)
309                 ret = 50;
310         else
311                 ret = 0;
312
313 out:
314         return ret;
315
316 }
317
318 static void parse_args(int argc, char **argv,
319                        const char * const *usage_str,
320                        int *types, char **object,
321                        char **name, char **value)
322 {
323         int ret;
324         char *type_str = NULL;
325
326         optind = 1;
327         while (1) {
328                 int c = getopt(argc, argv, "t:");
329                 if (c < 0)
330                         break;
331
332                 switch (c) {
333                 case 't':
334                         type_str = optarg;
335                         break;
336                 default:
337                         usage(usage_str);
338                 }
339         }
340
341         *types = 0;
342         if (type_str) {
343                 if (!strcmp(type_str, "s") || !strcmp(type_str, "subvol")) {
344                         *types = prop_object_subvol;
345                 } else if (!strcmp(type_str, "f") ||
346                            !strcmp(type_str, "filesystem")) {
347                         *types = prop_object_root;
348                 } else if (!strcmp(type_str, "i") ||
349                            !strcmp(type_str, "inode")) {
350                         *types = prop_object_inode;
351                 } else if (!strcmp(type_str, "d") ||
352                            !strcmp(type_str, "device")) {
353                         *types = prop_object_dev;
354                 } else {
355                         fprintf(stderr, "ERROR: invalid object type.\n");
356                         usage(usage_str);
357                 }
358         }
359
360         if (object && optind < argc)
361                 *object = argv[optind++];
362         if (name && optind < argc)
363                 *name = argv[optind++];
364         if (value && optind < argc)
365                 *value = argv[optind++];
366
367         if (optind != argc) {
368                 fprintf(stderr, "ERROR: invalid arguments.\n");
369                 usage(usage_str);
370         }
371
372         if (!*types && object && *object) {
373                 ret = autodetect_object_types(*object, types);
374                 if (ret < 0) {
375                         fprintf(stderr,
376                                 "ERROR: failed to detect object type. %s\n",
377                                 strerror(-ret));
378                         usage(usage_str);
379                 }
380                 if (!*types) {
381                         fprintf(stderr,
382                                 "ERROR: object is not a btrfs object.\n");
383                         usage(usage_str);
384                 }
385         }
386 }
387
388 static int cmd_get(int argc, char **argv)
389 {
390         int ret;
391         char *object;
392         char *name = NULL;
393         int types = 0;
394
395         if (check_argc_min(argc, 2) || check_argc_max(argc, 5))
396                 usage(cmd_get_usage);
397
398         parse_args(argc, argv, cmd_get_usage, &types, &object, &name, NULL);
399         if (!object) {
400                 fprintf(stderr, "ERROR: invalid arguments.\n");
401                 usage(cmd_set_usage);
402         }
403
404         if (name)
405                 ret = setget_prop(types, object, name, NULL);
406         else
407                 ret = dump_props(types, object, 0);
408
409         return ret;
410 }
411
412 static int cmd_set(int argc, char **argv)
413 {
414         int ret;
415         char *object;
416         char *name;
417         char *value;
418         int types = 0;
419
420         if (check_argc_min(argc, 4) || check_argc_max(argc, 6))
421                 usage(cmd_set_usage);
422
423         parse_args(argc, argv, cmd_set_usage, &types, &object, &name, &value);
424         if (!object || !name || !value) {
425                 fprintf(stderr, "ERROR: invalid arguments.\n");
426                 usage(cmd_set_usage);
427         }
428
429         ret = setget_prop(types, object, name, value);
430
431         return ret;
432 }
433
434 static int cmd_list(int argc, char **argv)
435 {
436         int ret;
437         char *object = NULL;
438         int types = 0;
439
440         if (check_argc_min(argc, 2) || check_argc_max(argc, 4))
441                 usage(cmd_list_usage);
442
443         parse_args(argc, argv, cmd_list_usage, &types, &object, NULL, NULL);
444         if (!object) {
445                 fprintf(stderr, "ERROR: invalid arguments.\n");
446                 usage(cmd_set_usage);
447         }
448
449         ret = dump_props(types, object, 1);
450
451         return ret;
452 }
453
454 const struct cmd_group property_cmd_group = {
455         property_cmd_group_usage, NULL, {
456                 { "get", cmd_get, cmd_get_usage, NULL, 0 },
457                 { "set", cmd_set, cmd_set_usage, NULL, 0 },
458                 { "list", cmd_list, cmd_list_usage, NULL, 0 },
459                 { 0, 0, 0, 0, 0 },
460         }
461 };
462
463 int cmd_property(int argc, char **argv)
464 {
465         return handle_command_group(&property_cmd_group, argc, argv);
466 }