Btrfs-progs: bugfix for subvolume parent determination in btrfs send
[platform/upstream/btrfs-progs.git] / cmds-subvolume.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 <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <libgen.h>
25 #include <limits.h>
26 #include <getopt.h>
27
28 #include "kerncompat.h"
29 #include "ioctl.h"
30 #include "qgroup.h"
31
32 #include "ctree.h"
33 #include "commands.h"
34 #include "btrfs-list.h"
35
36 static const char * const subvolume_cmd_group_usage[] = {
37         "btrfs subvolume <command> <args>",
38         NULL
39 };
40
41 /*
42  * test if path is a directory
43  * this function return
44  * 0-> path exists but it is not a directory
45  * 1-> path exists and it is  a directory
46  * -1 -> path is unaccessible
47  */
48 static int test_isdir(char *path)
49 {
50         struct stat     st;
51         int             res;
52
53         res = stat(path, &st);
54         if(res < 0 )
55                 return -1;
56
57         return S_ISDIR(st.st_mode);
58 }
59
60 static const char * const cmd_subvol_create_usage[] = {
61         "btrfs subvolume create [<dest>/]<name>",
62         "Create a subvolume",
63         "Create a subvolume <name> in <dest>.  If <dest> is not given",
64         "subvolume <name> will be created in the current directory.",
65         NULL
66 };
67
68 static int cmd_subvol_create(int argc, char **argv)
69 {
70         int     res, fddst, len, e;
71         char    *newname;
72         char    *dstdir;
73         char    *dst;
74         struct btrfs_qgroup_inherit *inherit = NULL;
75
76         optind = 1;
77         while (1) {
78                 int c = getopt(argc, argv, "c:i:r");
79                 if (c < 0)
80                         break;
81
82                 switch (c) {
83                 case 'c':
84                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
85                         if (res)
86                                 return res;
87                         break;
88                 case 'i':
89                         res = qgroup_inherit_add_group(&inherit, optarg);
90                         if (res)
91                                 return res;
92                         break;
93                 default:
94                         usage(cmd_subvol_create_usage);
95                 }
96         }
97
98         if (check_argc_exact(argc - optind, 1))
99                 usage(cmd_subvol_create_usage);
100
101         dst = argv[optind];
102
103         res = test_isdir(dst);
104         if(res >= 0 ){
105                 fprintf(stderr, "ERROR: '%s' exists\n", dst);
106                 return 12;
107         }
108
109         newname = strdup(dst);
110         newname = basename(newname);
111         dstdir = strdup(dst);
112         dstdir = dirname(dstdir);
113
114         if( !strcmp(newname,".") || !strcmp(newname,"..") ||
115              strchr(newname, '/') ){
116                 fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
117                         newname);
118                 return 14;
119         }
120
121         len = strlen(newname);
122         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
123                 fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
124                         newname);
125                 return 14;
126         }
127
128         fddst = open_file_or_dir(dstdir);
129         if (fddst < 0) {
130                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
131                 return 12;
132         }
133
134         printf("Create subvolume '%s/%s'\n", dstdir, newname);
135         if (inherit) {
136                 struct btrfs_ioctl_vol_args_v2  args;
137
138                 memset(&args, 0, sizeof(args));
139                 strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
140                 args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
141                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
142                 args.size = qgroup_inherit_size(inherit);
143                 args.qgroup_inherit = inherit;
144
145                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
146         } else {
147                 struct btrfs_ioctl_vol_args     args;
148
149                 memset(&args, 0, sizeof(args));
150                 strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
151                 args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
152
153                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
154         }
155
156         e = errno;
157
158         close(fddst);
159
160         if(res < 0 ){
161                 fprintf( stderr, "ERROR: cannot create subvolume - %s\n",
162                         strerror(e));
163                 return 11;
164         }
165         free(inherit);
166
167         return 0;
168 }
169
170 /*
171  * test if path is a subvolume:
172  * this function return
173  * 0-> path exists but it is not a subvolume
174  * 1-> path exists and it is  a subvolume
175  * -1 -> path is unaccessible
176  */
177 int test_issubvolume(char *path)
178 {
179         struct stat     st;
180         int             res;
181
182         res = stat(path, &st);
183         if(res < 0 )
184                 return -1;
185
186         return (st.st_ino == 256) && S_ISDIR(st.st_mode);
187 }
188
189 static const char * const cmd_subvol_delete_usage[] = {
190         "btrfs subvolume delete <subvolume> [<subvolume>...]",
191         "Delete subvolume(s)",
192         NULL
193 };
194
195 static int cmd_subvol_delete(int argc, char **argv)
196 {
197         int     res, fd, len, e, cnt = 1, ret = 0;
198         struct btrfs_ioctl_vol_args     args;
199         char    *dname, *vname, *cpath;
200         char    *path;
201
202         if (argc < 2)
203                 usage(cmd_subvol_delete_usage);
204
205 again:
206         path = argv[cnt];
207
208         res = test_issubvolume(path);
209         if(res<0){
210                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
211                 ret = 12;
212                 goto out;
213         }
214         if(!res){
215                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
216                 ret = 13;
217                 goto out;
218         }
219
220         cpath = realpath(path, 0);
221         dname = strdup(cpath);
222         dname = dirname(dname);
223         vname = strdup(cpath);
224         vname = basename(vname);
225         free(cpath);
226
227         if( !strcmp(vname,".") || !strcmp(vname,"..") ||
228              strchr(vname, '/') ){
229                 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
230                         vname);
231                 ret = 14;
232                 goto out;
233         }
234
235         len = strlen(vname);
236         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
237                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
238                         vname);
239                 ret = 14;
240                 goto out;
241         }
242
243         fd = open_file_or_dir(dname);
244         if (fd < 0) {
245                 close(fd);
246                 fprintf(stderr, "ERROR: can't access to '%s'\n", dname);
247                 ret = 12;
248                 goto out;
249         }
250
251         printf("Delete subvolume '%s/%s'\n", dname, vname);
252         strncpy(args.name, vname, BTRFS_PATH_NAME_MAX);
253         args.name[BTRFS_PATH_NAME_MAX-1] = 0;
254         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
255         e = errno;
256
257         close(fd);
258
259         if(res < 0 ){
260                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
261                         dname, vname, strerror(e));
262                 ret = 11;
263                 goto out;
264         }
265
266 out:
267         cnt++;
268         if (cnt < argc)
269                 goto again;
270
271         return ret;
272 }
273
274 static const char * const cmd_subvol_list_usage[] = {
275         "btrfs subvolume list [-apurts] [-g [+|-]value] [-c [+|-]value] "
276         "[--sort=gen,ogen,rootid,path] <path>",
277         "List subvolumes (and snapshots)",
278         "",
279         "-p           print parent ID",
280         "-a           print all the subvolumes in the filesystem.",
281         "-u           print the uuid of subvolumes (and snapshots)",
282         "-t           print the result as a table",
283         "-s           list snapshots only in the filesystem",
284         "-r           list readonly subvolumes (including snapshots)",
285         "-g [+|-]value",
286         "             filter the subvolumes by generation",
287         "             (+value: >= value; -value: <= value; value: = value)",
288         "-c [+|-]value",
289         "             filter the subvolumes by ogeneration",
290         "             (+value: >= value; -value: <= value; value: = value)",
291         "--sort=gen,ogen,rootid,path",
292         "             list the subvolume in order of gen, ogen, rootid or path",
293         "             you also can add '+' or '-' in front of each items.",
294         "             (+:ascending, -:descending, ascending default)",
295         NULL,
296 };
297
298 static int cmd_subvol_list(int argc, char **argv)
299 {
300         struct btrfs_list_filter_set *filter_set;
301         struct btrfs_list_comparer_set *comparer_set;
302         u64 flags = 0;
303         int fd;
304         u64 top_id;
305         int ret;
306         int c;
307         char *subvol;
308         int is_tab_result = 0;
309         int is_list_all = 0;
310         struct option long_options[] = {
311                 {"sort", 1, NULL, 'S'},
312                 {0, 0, 0, 0}
313         };
314
315         filter_set = btrfs_list_alloc_filter_set();
316         comparer_set = btrfs_list_alloc_comparer_set();
317
318         optind = 1;
319         while(1) {
320                 c = getopt_long(argc, argv,
321                                     "apsurg:c:t", long_options, NULL);
322                 if (c < 0)
323                         break;
324
325                 switch(c) {
326                 case 'p':
327                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
328                         break;
329                 case 'a':
330                         is_list_all = 1;
331                         break;
332                 case 't':
333                         is_tab_result = 1;
334                         break;
335                 case 's':
336                         btrfs_list_setup_filter(&filter_set,
337                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
338                                                 0);
339                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
340                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
341
342                 case 'u':
343                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
344                         break;
345                 case 'r':
346                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
347                         break;
348                 case 'g':
349                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
350                         ret = btrfs_list_parse_filter_string(optarg,
351                                                         &filter_set,
352                                                         BTRFS_LIST_FILTER_GEN);
353                         if (ret)
354                                 usage(cmd_subvol_list_usage);
355                         break;
356
357                 case 'c':
358                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
359                         ret = btrfs_list_parse_filter_string(optarg,
360                                                         &filter_set,
361                                                         BTRFS_LIST_FILTER_CGEN);
362                         if (ret)
363                                 usage(cmd_subvol_list_usage);
364                         break;
365                 case 'S':
366                         ret = btrfs_list_parse_sort_string(optarg,
367                                                            &comparer_set);
368                         if (ret)
369                                 usage(cmd_subvol_list_usage);
370                         break;
371
372                 default:
373                         usage(cmd_subvol_list_usage);
374                 }
375         }
376
377         if (flags)
378                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
379                                         flags);
380
381         if (check_argc_exact(argc - optind, 1))
382                 usage(cmd_subvol_list_usage);
383
384         subvol = argv[optind];
385
386         ret = test_issubvolume(subvol);
387         if (ret < 0) {
388                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
389                 return 12;
390         }
391         if (!ret) {
392                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
393                 return 13;
394         }
395
396         fd = open_file_or_dir(subvol);
397         if (fd < 0) {
398                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
399                 return 12;
400         }
401
402         top_id = btrfs_list_get_path_rootid(fd);
403         if (!is_list_all)
404                 btrfs_list_setup_filter(&filter_set,
405                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
406                                         top_id);
407
408         ret = btrfs_list_subvols(fd, filter_set, comparer_set,
409                                 is_tab_result);
410         if (ret)
411                 return 19;
412         return 0;
413 }
414
415 static const char * const cmd_snapshot_usage[] = {
416         "btrfs subvolume snapshot [-r] <source> [<dest>/]<name>",
417         "Create a snapshot of the subvolume",
418         "Create a writable/readonly snapshot of the subvolume <source> with",
419         "the name <name> in the <dest> directory",
420         "",
421         "-r     create a readonly snapshot",
422         NULL
423 };
424
425 static int cmd_snapshot(int argc, char **argv)
426 {
427         char    *subvol, *dst;
428         int     res, fd, fddst, len, e, readonly = 0;
429         char    *newname;
430         char    *dstdir;
431         struct btrfs_ioctl_vol_args_v2  args;
432         struct btrfs_qgroup_inherit *inherit = NULL;
433
434         optind = 1;
435         memset(&args, 0, sizeof(args));
436         while (1) {
437                 int c = getopt(argc, argv, "c:i:r");
438                 if (c < 0)
439                         break;
440
441                 switch (c) {
442                 case 'c':
443                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
444                         if (res)
445                                 return res;
446                         break;
447                 case 'i':
448                         res = qgroup_inherit_add_group(&inherit, optarg);
449                         if (res)
450                                 return res;
451                         break;
452                 case 'r':
453                         readonly = 1;
454                         break;
455                 case 'x':
456                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
457                         if (res)
458                                 return res;
459                         break;
460                 default:
461                         usage(cmd_snapshot_usage);
462                 }
463         }
464
465         if (check_argc_exact(argc - optind, 2))
466                 usage(cmd_snapshot_usage);
467
468         subvol = argv[optind];
469         dst = argv[optind + 1];
470
471         res = test_issubvolume(subvol);
472         if(res<0){
473                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
474                 return 12;
475         }
476         if(!res){
477                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
478                 return 13;
479         }
480
481         res = test_isdir(dst);
482         if(res == 0 ){
483                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
484                 return 12;
485         }
486
487         if(res>0){
488                 newname = strdup(subvol);
489                 newname = basename(newname);
490                 dstdir = dst;
491         }else{
492                 newname = strdup(dst);
493                 newname = basename(newname);
494                 dstdir = strdup(dst);
495                 dstdir = dirname(dstdir);
496         }
497
498         if( !strcmp(newname,".") || !strcmp(newname,"..") ||
499              strchr(newname, '/') ){
500                 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
501                         newname);
502                 return 14;
503         }
504
505         len = strlen(newname);
506         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
507                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
508                         newname);
509                 return 14;
510         }
511
512         fddst = open_file_or_dir(dstdir);
513         if (fddst < 0) {
514                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
515                 return 12;
516         }
517
518         fd = open_file_or_dir(subvol);
519         if (fd < 0) {
520                 close(fddst);
521                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
522                 return 12;
523         }
524
525         if (readonly) {
526                 args.flags |= BTRFS_SUBVOL_RDONLY;
527                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
528                        subvol, dstdir, newname);
529         } else {
530                 printf("Create a snapshot of '%s' in '%s/%s'\n",
531                        subvol, dstdir, newname);
532         }
533
534         args.fd = fd;
535         if (inherit) {
536                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
537                 args.size = qgroup_inherit_size(inherit);
538                 args.qgroup_inherit = inherit;
539         }
540         strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
541         args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
542         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
543         e = errno;
544
545         close(fd);
546         close(fddst);
547
548         if(res < 0 ){
549                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
550                         subvol, strerror(e));
551                 return 11;
552         }
553         free(inherit);
554
555         return 0;
556 }
557
558 static const char * const cmd_subvol_get_default_usage[] = {
559         "btrfs subvolume get-default <path>",
560         "Get the default subvolume of a filesystem",
561         NULL
562 };
563
564 static int cmd_subvol_get_default(int argc, char **argv)
565 {
566         int fd;
567         int ret;
568         char *subvol;
569         struct btrfs_list_filter_set *filter_set;
570         u64 default_id;
571
572         if (check_argc_exact(argc, 2))
573                 usage(cmd_subvol_get_default_usage);
574
575         subvol = argv[1];
576
577         ret = test_issubvolume(subvol);
578         if (ret < 0) {
579                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
580                 return 12;
581         }
582         if (!ret) {
583                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
584                 return 13;
585         }
586
587         fd = open_file_or_dir(subvol);
588         if (fd < 0) {
589                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
590                 return 12;
591         }
592
593         ret = btrfs_list_get_default_subvolume(fd, &default_id);
594         if (ret) {
595                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
596                         strerror(errno));
597                 return ret;
598         }
599
600         if (default_id == 0) {
601                 fprintf(stderr, "ERROR: 'default' dir item not found\n");
602                 return ret;
603         }
604
605         /* no need to resolve roots if FS_TREE is default */
606         if (default_id == BTRFS_FS_TREE_OBJECTID) {
607                 printf("ID 5 (FS_TREE)\n");
608                 return ret;
609         }
610
611         filter_set = btrfs_list_alloc_filter_set();
612         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
613                                 default_id);
614
615         ret = btrfs_list_subvols(fd, filter_set, NULL, 0);
616         if (ret)
617                 return 19;
618         return 0;
619 }
620
621 static const char * const cmd_subvol_set_default_usage[] = {
622         "btrfs subvolume set-default <subvolid> <path>",
623         "Set the default subvolume of a filesystem",
624         NULL
625 };
626
627 static int cmd_subvol_set_default(int argc, char **argv)
628 {
629         int     ret=0, fd, e;
630         u64     objectid;
631         char    *path;
632         char    *subvolid;
633
634         if (check_argc_exact(argc, 3))
635                 usage(cmd_subvol_set_default_usage);
636
637         subvolid = argv[1];
638         path = argv[2];
639
640         fd = open_file_or_dir(path);
641         if (fd < 0) {
642                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
643                 return 12;
644         }
645
646         objectid = (unsigned long long)strtoll(subvolid, NULL, 0);
647         if (errno == ERANGE) {
648                 fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid);
649                 return 30;
650         }
651         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
652         e = errno;
653         close(fd);
654         if( ret < 0 ){
655                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
656                         strerror(e));
657                 return 30;
658         }
659         return 0;
660 }
661
662 static const char * const cmd_find_new_usage[] = {
663         "btrfs subvolume find-new <path> <lastgen>",
664         "List the recently modified files in a filesystem",
665         NULL
666 };
667
668 static int cmd_find_new(int argc, char **argv)
669 {
670         int fd;
671         int ret;
672         char *subvol;
673         u64 last_gen;
674
675         if (check_argc_exact(argc, 3))
676                 usage(cmd_find_new_usage);
677
678         subvol = argv[1];
679         last_gen = atoll(argv[2]);
680
681         ret = test_issubvolume(subvol);
682         if (ret < 0) {
683                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
684                 return 12;
685         }
686         if (!ret) {
687                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
688                 return 13;
689         }
690
691         fd = open_file_or_dir(subvol);
692         if (fd < 0) {
693                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
694                 return 12;
695         }
696         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
697         if (ret)
698                 return 19;
699         return 0;
700 }
701
702 const struct cmd_group subvolume_cmd_group = {
703         subvolume_cmd_group_usage, NULL, {
704                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
705                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
706                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
707                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
708                 { "get-default", cmd_subvol_get_default,
709                         cmd_subvol_get_default_usage, NULL, 0 },
710                 { "set-default", cmd_subvol_set_default,
711                         cmd_subvol_set_default_usage, NULL, 0 },
712                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
713                 { 0, 0, 0, 0, 0 }
714         }
715 };
716
717 int cmd_subvolume(int argc, char **argv)
718 {
719         return handle_command_group(&subvolume_cmd_group, argc, argv);
720 }