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