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