btrfs-progs: add getopt stubs where needed
[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 print_prop_help(const struct prop_handler *prop)
203 {
204         fprintf(stdout, "%-20s%s\n", prop->name, prop->desc);
205         return 0;
206 }
207
208 static int dump_prop(const struct prop_handler *prop,
209                      const char *object,
210                      int types,
211                      int type,
212                      int name_and_help)
213 {
214         int ret = 0;
215
216         if ((types & type) && (prop->types & type)) {
217                 if (!name_and_help)
218                         ret = prop->handler(type, object, prop->name, NULL);
219                 else
220                         ret = print_prop_help(prop);
221         }
222         return ret;
223 }
224
225 static int dump_props(int types, const char *object, int name_and_help)
226 {
227         int ret;
228         int i;
229         int j;
230         const struct prop_handler *prop;
231
232         for (i = 0; prop_handlers[i].name; i++) {
233                 prop = &prop_handlers[i];
234                 for (j = 1; j < __prop_object_max; j <<= 1) {
235                         ret = dump_prop(prop, object, types, j, name_and_help);
236                         if (ret < 0) {
237                                 ret = 50;
238                                 goto out;
239                         }
240                 }
241         }
242
243         ret = 0;
244
245 out:
246         return ret;
247 }
248
249 static int setget_prop(int types, const char *object,
250                        const char *name, const char *value)
251 {
252         int ret;
253         const struct prop_handler *prop = NULL;
254
255         ret = parse_prop(name, prop_handlers, &prop);
256         if (ret == -1) {
257                 error("unknown property: %s", name);
258                 ret = 40;
259                 goto out;
260         }
261
262         types &= prop->types;
263         if (!types) {
264                 error("object is not compatible with property: %s", prop->name);
265                 ret = 47;
266                 goto out;
267         }
268
269         if (count_bits(types) > 1) {
270                 error("type of object is ambiguous, please use option -t");
271                 ret = 48;
272                 goto out;
273         }
274
275         if (value && prop->read_only) {
276                 error("property is read-only property: %s",
277                                 prop->name);
278                 ret = 51;
279                 goto out;
280         }
281
282         ret = prop->handler(types, object, name, value);
283
284         if (ret < 0)
285                 ret = 50;
286         else
287                 ret = 0;
288
289 out:
290         return ret;
291
292 }
293
294 static void parse_args(int argc, char **argv,
295                        const char * const *usage_str,
296                        int *types, char **object,
297                        char **name, char **value)
298 {
299         int ret;
300         char *type_str = NULL;
301
302         optind = 1;
303         while (1) {
304                 int c = getopt(argc, argv, "t:");
305                 if (c < 0)
306                         break;
307
308                 switch (c) {
309                 case 't':
310                         type_str = optarg;
311                         break;
312                 default:
313                         usage(usage_str);
314                 }
315         }
316
317         *types = 0;
318         if (type_str) {
319                 if (!strcmp(type_str, "s") || !strcmp(type_str, "subvol")) {
320                         *types = prop_object_subvol;
321                 } else if (!strcmp(type_str, "f") ||
322                            !strcmp(type_str, "filesystem")) {
323                         *types = prop_object_root;
324                 } else if (!strcmp(type_str, "i") ||
325                            !strcmp(type_str, "inode")) {
326                         *types = prop_object_inode;
327                 } else if (!strcmp(type_str, "d") ||
328                            !strcmp(type_str, "device")) {
329                         *types = prop_object_dev;
330                 } else {
331                         error("invalid object type: %s", type_str);
332                         usage(usage_str);
333                 }
334         }
335
336         if (object && optind < argc)
337                 *object = argv[optind++];
338         if (name && optind < argc)
339                 *name = argv[optind++];
340         if (value && optind < argc)
341                 *value = argv[optind++];
342
343         if (optind != argc) {
344                 error("unexpected agruments found");
345                 usage(usage_str);
346         }
347
348         if (!*types && object && *object) {
349                 ret = autodetect_object_types(*object, types);
350                 if (ret < 0) {
351                         error("failed to detect object type: %s",
352                                 strerror(-ret));
353                         usage(usage_str);
354                 }
355                 if (!*types) {
356                         error("object is not a btrfs object: %s", *object);
357                         usage(usage_str);
358                 }
359         }
360 }
361
362 static const char * const cmd_property_get_usage[] = {
363         "btrfs property get [-t <type>] <object> [<name>]",
364         "Gets a property from a btrfs object.",
365         "If no name is specified, all properties for the given object are",
366         "printed.",
367         "A filesystem object can be a the filesystem itself, a subvolume,",
368         "an inode or a device. The '-t <type>' option can be used to explicitly",
369         "specify what type of object you meant. This is only needed when a",
370         "property could be set for more then one object type. Possible types",
371         "are s[ubvol], f[ilesystem], i[node] and d[evice].",
372         NULL
373 };
374
375 static int cmd_property_get(int argc, char **argv)
376 {
377         int ret;
378         char *object = NULL;
379         char *name = NULL;
380         int types = 0;
381
382         clean_args_no_options(argc, argv, cmd_property_get_usage);
383
384         if (check_argc_min(argc, 2) || check_argc_max(argc, 5))
385                 usage(cmd_property_get_usage);
386
387         parse_args(argc, argv, cmd_property_get_usage, &types, &object, &name,
388                         NULL);
389         if (!object) {
390                 error("invalid arguments");
391                 usage(cmd_property_get_usage);
392         }
393
394         if (name)
395                 ret = setget_prop(types, object, name, NULL);
396         else
397                 ret = dump_props(types, object, 0);
398
399         return ret;
400 }
401
402 static const char * const cmd_property_set_usage[] = {
403         "btrfs property set [-t <type>] <object> <name> <value>",
404         "Sets a property on a btrfs object.",
405         "Please see the help of 'btrfs property get' for a description of",
406         "objects and object types.",
407         NULL
408 };
409
410 static int cmd_property_set(int argc, char **argv)
411 {
412         int ret;
413         char *object = NULL;
414         char *name = NULL;
415         char *value = NULL;
416         int types = 0;
417
418         clean_args_no_options(argc, argv, cmd_property_set_usage);
419
420         if (check_argc_min(argc, 4) || check_argc_max(argc, 6))
421                 usage(cmd_property_set_usage);
422
423         parse_args(argc, argv, cmd_property_set_usage, &types,
424                         &object, &name, &value);
425         if (!object || !name || !value) {
426                 error("invalid arguments");
427                 usage(cmd_property_set_usage);
428         }
429
430         ret = setget_prop(types, object, name, value);
431
432         return ret;
433 }
434
435 static const char * const cmd_property_list_usage[] = {
436         "btrfs property list [-t <type>] <object>",
437         "Lists available properties with their descriptions for the given object.",
438         "Please see the help of 'btrfs property get' for a description of",
439         "objects and object types.",
440         NULL
441 };
442
443 static int cmd_property_list(int argc, char **argv)
444 {
445         int ret;
446         char *object = NULL;
447         int types = 0;
448
449         clean_args_no_options(argc, argv, cmd_property_list_usage);
450
451         if (check_argc_min(argc, 2) || check_argc_max(argc, 4))
452                 usage(cmd_property_list_usage);
453
454         parse_args(argc, argv, cmd_property_list_usage,
455                         &types, &object, NULL, NULL);
456         if (!object) {
457                 error("invalid arguments");
458                 usage(cmd_property_list_usage);
459         }
460
461         ret = dump_props(types, object, 1);
462
463         return ret;
464 }
465
466 static const char property_cmd_group_info[] =
467 "modify properties of filesystem objects";
468
469 const struct cmd_group property_cmd_group = {
470         property_cmd_group_usage, property_cmd_group_info, {
471                 { "get", cmd_property_get,
472                         cmd_property_get_usage, NULL, 0 },
473                 { "set", cmd_property_set,
474                         cmd_property_set_usage, NULL, 0 },
475                 { "list", cmd_property_list,
476                         cmd_property_list_usage, NULL, 0 },
477                 NULL_CMD_STRUCT
478         }
479 };
480
481 int cmd_property(int argc, char **argv)
482 {
483         return handle_command_group(&property_cmd_group, argc, argv);
484 }