Btrfs-progs: fix detection of root objects in cmds-property.c
[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                         break;
339                 }
340         }
341
342         *types = 0;
343         if (type_str) {
344                 if (!strcmp(type_str, "s") || !strcmp(type_str, "subvol")) {
345                         *types = prop_object_subvol;
346                 } else if (!strcmp(type_str, "f") ||
347                            !strcmp(type_str, "filesystem")) {
348                         *types = prop_object_root;
349                 } else if (!strcmp(type_str, "i") ||
350                            !strcmp(type_str, "inode")) {
351                         *types = prop_object_inode;
352                 } else if (!strcmp(type_str, "d") ||
353                            !strcmp(type_str, "device")) {
354                         *types = prop_object_dev;
355                 } else {
356                         fprintf(stderr, "ERROR: invalid object type.\n");
357                         usage(usage_str);
358                 }
359         }
360
361         if (object && optind < argc)
362                 *object = argv[optind++];
363         if (name && optind < argc)
364                 *name = argv[optind++];
365         if (value && optind < argc)
366                 *value = argv[optind++];
367
368         if (optind != argc) {
369                 fprintf(stderr, "ERROR: invalid arguments.\n");
370                 usage(usage_str);
371         }
372
373         if (!*types && object && *object) {
374                 ret = autodetect_object_types(*object, types);
375                 if (ret < 0) {
376                         fprintf(stderr,
377                                 "ERROR: failed to detect object type. %s\n",
378                                 strerror(-ret));
379                         usage(usage_str);
380                 }
381                 if (!*types) {
382                         fprintf(stderr,
383                                 "ERROR: object is not a btrfs object.\n");
384                         usage(usage_str);
385                 }
386         }
387 }
388
389 static int cmd_get(int argc, char **argv)
390 {
391         int ret;
392         char *object;
393         char *name = NULL;
394         int types = 0;
395
396         if (check_argc_min(argc, 2) || check_argc_max(argc, 4))
397                 usage(cmd_get_usage);
398
399         parse_args(argc, argv, cmd_get_usage, &types, &object, &name, NULL);
400         if (!object) {
401                 fprintf(stderr, "ERROR: invalid arguments.\n");
402                 usage(cmd_set_usage);
403         }
404
405         if (name)
406                 ret = setget_prop(types, object, name, NULL);
407         else
408                 ret = dump_props(types, object, 0);
409
410         return ret;
411 }
412
413 static int cmd_set(int argc, char **argv)
414 {
415         int ret;
416         char *object;
417         char *name;
418         char *value;
419         int types = 0;
420
421         if (check_argc_min(argc, 4) || check_argc_max(argc, 5))
422                 usage(cmd_set_usage);
423
424         parse_args(argc, argv, cmd_set_usage, &types, &object, &name, &value);
425         if (!object || !name || !value) {
426                 fprintf(stderr, "ERROR: invalid arguments.\n");
427                 usage(cmd_set_usage);
428         }
429
430         ret = setget_prop(types, object, name, value);
431
432         return ret;
433 }
434
435 static int cmd_list(int argc, char **argv)
436 {
437         int ret;
438         char *object = NULL;
439         int types = 0;
440
441         if (check_argc_min(argc, 2) || check_argc_max(argc, 3))
442                 usage(cmd_list_usage);
443
444         parse_args(argc, argv, cmd_list_usage, &types, &object, NULL, NULL);
445         if (!object) {
446                 fprintf(stderr, "ERROR: invalid arguments.\n");
447                 usage(cmd_set_usage);
448         }
449
450         ret = dump_props(types, object, 1);
451
452         return ret;
453 }
454
455 const struct cmd_group property_cmd_group = {
456         property_cmd_group_usage, NULL, {
457                 { "get", cmd_get, cmd_get_usage, NULL, 0 },
458                 { "set", cmd_set, cmd_set_usage, NULL, 0 },
459                 { "list", cmd_list, cmd_list_usage, NULL, 0 },
460                 { 0, 0, 0, 0, 0 },
461         }
462 };
463
464 int cmd_property(int argc, char **argv)
465 {
466         return handle_command_group(&property_cmd_group, argc, argv);
467 }