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