6699e61510c874ed1f46080e343a599e8e2984f1
[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)
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                 fprintf(stderr, "ERROR: open %s failed. %s\n", path,
88                                 strerror(-ret));
89                 goto out;
90         }
91
92         ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
93         if (ret < 0) {
94                 ret = -errno;
95                 goto out;
96         }
97
98         memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
99         ret = 0;
100
101 out:
102         if (fd != -1)
103                 close(fd);
104         return ret;
105 }
106
107 static int check_btrfs_object(const char *object)
108 {
109         int ret;
110         u8 fsid[BTRFS_FSID_SIZE];
111
112         ret = get_fsid(object, fsid);
113         if (ret < 0)
114                 ret = 0;
115         else
116                 ret = 1;
117         return ret;
118 }
119
120 static int check_is_root(const char *object)
121 {
122         int ret;
123         u8 fsid[BTRFS_FSID_SIZE];
124         u8 fsid2[BTRFS_FSID_SIZE];
125         char *tmp;
126
127         tmp = malloc(strlen(object) + 5);
128         if (!tmp) {
129                 ret = -ENOMEM;
130                 goto out;
131         }
132         strcpy(tmp, object);
133         if (tmp[strlen(tmp) - 1] != '/')
134                 strcat(tmp, "/");
135         strcat(tmp, "..");
136
137         ret = get_fsid(object, fsid);
138         if (ret < 0) {
139                 fprintf(stderr, "ERROR: get_fsid for %s failed. %s\n", object,
140                                 strerror(-ret));
141                 goto out;
142         }
143
144         ret = get_fsid(tmp, fsid2);
145         if (ret < 0) {
146                 ret = 0;
147                 goto out;
148         }
149
150         if (!memcmp(fsid, fsid2, BTRFS_FSID_SIZE)) {
151                 ret = 0;
152                 goto out;
153         }
154
155         ret = 1;
156
157 out:
158         free(tmp);
159         return ret;
160 }
161
162 static int count_bits(int v)
163 {
164         unsigned int tmp = (unsigned int)v;
165         int cnt = 0;
166
167         while (tmp) {
168                 if (tmp & 1)
169                         cnt++;
170                 tmp >>= 1;
171         }
172         return cnt;
173 }
174
175 static int autodetect_object_types(const char *object, int *types_out)
176 {
177         int ret;
178         int is_btrfs_object;
179         int types = 0;
180         struct stat st;
181
182         is_btrfs_object = check_btrfs_object(object);
183
184         ret = lstat(object, &st);
185         if (ret < 0) {
186                 ret = -errno;
187                 goto out;
188         }
189
190         if (is_btrfs_object) {
191                 types |= prop_object_inode;
192                 if (st.st_ino == BTRFS_FIRST_FREE_OBJECTID)
193                         types |= prop_object_subvol;
194
195                 ret = check_is_root(object);
196                 if (ret < 0)
197                         goto out;
198                 if (ret)
199                         types |= prop_object_root;
200         }
201
202         if (S_ISBLK(st.st_mode))
203                 types |= prop_object_dev;
204
205         ret = 0;
206         *types_out = types;
207
208 out:
209         return ret;
210 }
211
212 static int print_prop_help(const struct prop_handler *prop)
213 {
214         fprintf(stdout, "%-20s%s\n", prop->name, prop->desc);
215         return 0;
216 }
217
218 static int dump_prop(const struct prop_handler *prop,
219                      const char *object,
220                      int types,
221                      int type,
222                      int name_and_help)
223 {
224         int ret = 0;
225
226         if ((types & type) && (prop->types & type)) {
227                 if (!name_and_help)
228                         ret = prop->handler(type, object, prop->name, NULL);
229                 else
230                         ret = print_prop_help(prop);
231         }
232         return ret;
233 }
234
235 static int dump_props(int types, const char *object, int name_and_help)
236 {
237         int ret;
238         int i;
239         int j;
240         const struct prop_handler *prop;
241
242         for (i = 0; prop_handlers[i].name; i++) {
243                 prop = &prop_handlers[i];
244                 for (j = 1; j < __prop_object_max; j <<= 1) {
245                         ret = dump_prop(prop, object, types, j, name_and_help);
246                         if (ret < 0) {
247                                 ret = 50;
248                                 goto out;
249                         }
250                 }
251         }
252
253         ret = 0;
254
255 out:
256         return ret;
257 }
258
259 static int setget_prop(int types, const char *object,
260                        const char *name, const char *value)
261 {
262         int ret;
263         const struct prop_handler *prop = NULL;
264
265         ret = parse_prop(name, prop_handlers, &prop);
266         if (ret == -1) {
267                 fprintf(stderr, "ERROR: property is unknown\n");
268                 ret = 40;
269                 goto out;
270         } else if (ret) {
271                 fprintf(stderr, "ERROR: parse_prop reported unknown error\n");
272                 ret = 42;
273                 goto out;
274         }
275
276         types &= prop->types;
277         if (!types) {
278                 fprintf(stderr,
279                         "ERROR: object is not compatible with property\n");
280                 ret = 47;
281                 goto out;
282         }
283
284         if (count_bits(types) > 1) {
285                 fprintf(stderr,
286                         "ERROR: type of object is ambiguous. Please specify a type by hand.\n");
287                 ret = 48;
288                 goto out;
289         }
290
291         if (value && prop->read_only) {
292                 fprintf(stderr, "ERROR: %s is a read-only property.\n",
293                                 prop->name);
294                 ret = 51;
295                 goto out;
296         }
297
298         ret = prop->handler(types, object, name, value);
299
300         if (ret < 0)
301                 ret = 50;
302         else
303                 ret = 0;
304
305 out:
306         return ret;
307
308 }
309
310 static void parse_args(int argc, char **argv,
311                        const char * const *usage_str,
312                        int *types, char **object,
313                        char **name, char **value)
314 {
315         int ret;
316         char *type_str = NULL;
317
318         optind = 1;
319         while (1) {
320                 int c = getopt(argc, argv, "t:");
321                 if (c < 0)
322                         break;
323
324                 switch (c) {
325                 case 't':
326                         type_str = optarg;
327                         break;
328                 default:
329                         usage(usage_str);
330                         break;
331                 }
332         }
333
334         *types = 0;
335         if (type_str) {
336                 if (!strcmp(type_str, "s") || !strcmp(type_str, "subvol")) {
337                         *types = prop_object_subvol;
338                 } else if (!strcmp(type_str, "f") ||
339                            !strcmp(type_str, "filesystem")) {
340                         *types = prop_object_root;
341                 } else if (!strcmp(type_str, "i") ||
342                            !strcmp(type_str, "inode")) {
343                         *types = prop_object_inode;
344                 } else if (!strcmp(type_str, "d") ||
345                            !strcmp(type_str, "device")) {
346                         *types = prop_object_dev;
347                 } else {
348                         fprintf(stderr, "ERROR: invalid object type.\n");
349                         usage(usage_str);
350                 }
351         }
352
353         if (object && optind < argc)
354                 *object = argv[optind++];
355         if (name && optind < argc)
356                 *name = argv[optind++];
357         if (value && optind < argc)
358                 *value = argv[optind++];
359
360         if (optind != argc) {
361                 fprintf(stderr, "ERROR: invalid arguments.\n");
362                 usage(usage_str);
363         }
364
365         if (!*types && object && *object) {
366                 ret = autodetect_object_types(*object, types);
367                 if (ret < 0) {
368                         fprintf(stderr,
369                                 "ERROR: failed to detect object type. %s\n",
370                                 strerror(-ret));
371                         usage(usage_str);
372                 }
373                 if (!*types) {
374                         fprintf(stderr,
375                                 "ERROR: object is not a btrfs object.\n");
376                         usage(usage_str);
377                 }
378         }
379 }
380
381 static int cmd_get(int argc, char **argv)
382 {
383         int ret;
384         char *object;
385         char *name = NULL;
386         int types = 0;
387
388         if (check_argc_min(argc, 2) || check_argc_max(argc, 4))
389                 usage(cmd_get_usage);
390
391         parse_args(argc, argv, cmd_get_usage, &types, &object, &name, NULL);
392         if (!object) {
393                 fprintf(stderr, "ERROR: invalid arguments.\n");
394                 usage(cmd_set_usage);
395         }
396
397         if (name)
398                 ret = setget_prop(types, object, name, NULL);
399         else
400                 ret = dump_props(types, object, 0);
401
402         return ret;
403 }
404
405 static int cmd_set(int argc, char **argv)
406 {
407         int ret;
408         char *object;
409         char *name;
410         char *value;
411         int types = 0;
412
413         if (check_argc_min(argc, 4) || check_argc_max(argc, 5))
414                 usage(cmd_set_usage);
415
416         parse_args(argc, argv, cmd_set_usage, &types, &object, &name, &value);
417         if (!object || !name || !value) {
418                 fprintf(stderr, "ERROR: invalid arguments.\n");
419                 usage(cmd_set_usage);
420         }
421
422         ret = setget_prop(types, object, name, value);
423
424         return ret;
425 }
426
427 static int cmd_list(int argc, char **argv)
428 {
429         int ret;
430         char *object = NULL;
431         int types = 0;
432
433         if (check_argc_min(argc, 2) || check_argc_max(argc, 3))
434                 usage(cmd_list_usage);
435
436         parse_args(argc, argv, cmd_list_usage, &types, &object, NULL, NULL);
437         if (!object) {
438                 fprintf(stderr, "ERROR: invalid arguments.\n");
439                 usage(cmd_set_usage);
440         }
441
442         ret = dump_props(types, object, 1);
443
444         return ret;
445 }
446
447 const struct cmd_group property_cmd_group = {
448         property_cmd_group_usage, NULL, {
449                 { "get", cmd_get, cmd_get_usage, NULL, 0 },
450                 { "set", cmd_set, cmd_set_usage, NULL, 0 },
451                 { "list", cmd_list, cmd_list_usage, NULL, 0 },
452                 { 0, 0, 0, 0, 0 },
453         }
454 };
455
456 int cmd_property(int argc, char **argv)
457 {
458         return handle_command_group(&property_cmd_group, argc, argv);
459 }