Btrfs-progs: introduces '-a' option into subvolume list command
[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 <name>",
191         "Delete a subvolume",
192         NULL
193 };
194
195 static int cmd_subvol_delete(int argc, char **argv)
196 {
197         int     res, fd, len, e;
198         struct btrfs_ioctl_vol_args     args;
199         char    *dname, *vname, *cpath;
200         char    *path;
201
202         if (check_argc_exact(argc, 2))
203                 usage(cmd_subvol_delete_usage);
204
205         path = argv[1];
206
207         res = test_issubvolume(path);
208         if(res<0){
209                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
210                 return 12;
211         }
212         if(!res){
213                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
214                 return 13;
215         }
216
217         cpath = realpath(path, 0);
218         dname = strdup(cpath);
219         dname = dirname(dname);
220         vname = strdup(cpath);
221         vname = basename(vname);
222         free(cpath);
223
224         if( !strcmp(vname,".") || !strcmp(vname,"..") ||
225              strchr(vname, '/') ){
226                 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
227                         vname);
228                 return 14;
229         }
230
231         len = strlen(vname);
232         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
233                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
234                         vname);
235                 return 14;
236         }
237
238         fd = open_file_or_dir(dname);
239         if (fd < 0) {
240                 close(fd);
241                 fprintf(stderr, "ERROR: can't access to '%s'\n", dname);
242                 return 12;
243         }
244
245         printf("Delete subvolume '%s/%s'\n", dname, vname);
246         strncpy(args.name, vname, BTRFS_PATH_NAME_MAX);
247         args.name[BTRFS_PATH_NAME_MAX-1] = 0;
248         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
249         e = errno;
250
251         close(fd);
252
253         if(res < 0 ){
254                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
255                         dname, vname, strerror(e));
256                 return 11;
257         }
258
259         return 0;
260 }
261
262 static const char * const cmd_subvol_list_usage[] = {
263         "btrfs subvolume list [-apurt] [-s 0|1] [-g [+|-]value] [-c [+|-]value] "
264         "[--sort=gen,ogen,rootid,path] <path>",
265         "List subvolumes (and snapshots)",
266         "",
267         "-p           print parent ID",
268         "-a           print all the subvolumes in the filesystem.",
269         "-u           print the uuid of subvolumes (and snapshots)",
270         "-t           print the result as a table",
271         "-s value     list snapshots with generation in ascending/descending order",
272         "             (1: ascending, 0: descending)",
273         "-r           list readonly subvolumes (including snapshots)",
274         "-g [+|-]value",
275         "             filter the subvolumes by generation",
276         "             (+value: >= value; -value: <= value; value: = value)",
277         "-c [+|-]value",
278         "             filter the subvolumes by ogeneration",
279         "             (+value: >= value; -value: <= value; value: = value)",
280         "--sort=gen,ogen,rootid,path",
281         "             list the subvolume in order of gen, ogen, rootid or path",
282         "             you also can add '+' or '-' in front of each items.",
283         "             (+:ascending, -:descending, ascending default)",
284         NULL,
285 };
286
287 static int cmd_subvol_list(int argc, char **argv)
288 {
289         struct btrfs_list_filter_set *filter_set;
290         struct btrfs_list_comparer_set *comparer_set;
291         u64 flags = 0;
292         int fd;
293         u64 top_id;
294         int ret;
295         int order;
296         int c;
297         char *subvol;
298         int is_tab_result = 0;
299         int is_list_all = 0;
300         struct option long_options[] = {
301                 {"sort", 1, NULL, 'S'},
302                 {0, 0, 0, 0}
303         };
304
305         filter_set = btrfs_list_alloc_filter_set();
306         comparer_set = btrfs_list_alloc_comparer_set();
307
308         optind = 1;
309         while(1) {
310                 c = getopt_long(argc, argv,
311                                     "aps:urg:c:t", long_options, NULL);
312                 if (c < 0)
313                         break;
314
315                 switch(c) {
316                 case 'p':
317                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
318                         break;
319                 case 'a':
320                         is_list_all = 1;
321                         break;
322                 case 't':
323                         is_tab_result = 1;
324                         break;
325                 case 's':
326                         order = atoi(optarg);
327                         btrfs_list_setup_filter(&filter_set,
328                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
329                                                 0);
330                         btrfs_list_setup_comparer(&comparer_set,
331                                                   BTRFS_LIST_COMP_OGEN,
332                                                   !order);
333                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
334                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
335
336                 case 'u':
337                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
338                         break;
339                 case 'r':
340                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
341                         break;
342                 case 'g':
343                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
344                         ret = btrfs_list_parse_filter_string(optarg,
345                                                         &filter_set,
346                                                         BTRFS_LIST_FILTER_GEN);
347                         if (ret)
348                                 usage(cmd_subvol_list_usage);
349                         break;
350
351                 case 'c':
352                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
353                         ret = btrfs_list_parse_filter_string(optarg,
354                                                         &filter_set,
355                                                         BTRFS_LIST_FILTER_CGEN);
356                         if (ret)
357                                 usage(cmd_subvol_list_usage);
358                         break;
359                 case 'S':
360                         ret = btrfs_list_parse_sort_string(optarg,
361                                                            &comparer_set);
362                         if (ret)
363                                 usage(cmd_subvol_list_usage);
364                         break;
365
366                 default:
367                         usage(cmd_subvol_list_usage);
368                 }
369         }
370
371         if (flags)
372                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
373                                         flags);
374
375         if (check_argc_exact(argc - optind, 1))
376                 usage(cmd_subvol_list_usage);
377
378         subvol = argv[optind];
379
380         ret = test_issubvolume(subvol);
381         if (ret < 0) {
382                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
383                 return 12;
384         }
385         if (!ret) {
386                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
387                 return 13;
388         }
389
390         fd = open_file_or_dir(subvol);
391         if (fd < 0) {
392                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
393                 return 12;
394         }
395
396         top_id = btrfs_list_get_path_rootid(fd);
397         if (!is_list_all)
398                 btrfs_list_setup_filter(&filter_set,
399                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
400                                         top_id);
401
402         ret = btrfs_list_subvols(fd, filter_set, comparer_set,
403                                 is_tab_result);
404         if (ret)
405                 return 19;
406         return 0;
407 }
408
409 static const char * const cmd_snapshot_usage[] = {
410         "btrfs subvolume snapshot [-r] <source> [<dest>/]<name>",
411         "Create a snapshot of the subvolume",
412         "Create a writable/readonly snapshot of the subvolume <source> with",
413         "the name <name> in the <dest> directory",
414         "",
415         "-r     create a readonly snapshot",
416         NULL
417 };
418
419 static int cmd_snapshot(int argc, char **argv)
420 {
421         char    *subvol, *dst;
422         int     res, fd, fddst, len, e, readonly = 0;
423         char    *newname;
424         char    *dstdir;
425         struct btrfs_ioctl_vol_args_v2  args;
426         struct btrfs_qgroup_inherit *inherit = NULL;
427
428         optind = 1;
429         memset(&args, 0, sizeof(args));
430         while (1) {
431                 int c = getopt(argc, argv, "c:i:r");
432                 if (c < 0)
433                         break;
434
435                 switch (c) {
436                 case 'c':
437                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
438                         if (res)
439                                 return res;
440                         break;
441                 case 'i':
442                         res = qgroup_inherit_add_group(&inherit, optarg);
443                         if (res)
444                                 return res;
445                         break;
446                 case 'r':
447                         readonly = 1;
448                         break;
449                 case 'x':
450                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
451                         if (res)
452                                 return res;
453                         break;
454                 default:
455                         usage(cmd_snapshot_usage);
456                 }
457         }
458
459         if (check_argc_exact(argc - optind, 2))
460                 usage(cmd_snapshot_usage);
461
462         subvol = argv[optind];
463         dst = argv[optind + 1];
464
465         res = test_issubvolume(subvol);
466         if(res<0){
467                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
468                 return 12;
469         }
470         if(!res){
471                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
472                 return 13;
473         }
474
475         res = test_isdir(dst);
476         if(res == 0 ){
477                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
478                 return 12;
479         }
480
481         if(res>0){
482                 newname = strdup(subvol);
483                 newname = basename(newname);
484                 dstdir = dst;
485         }else{
486                 newname = strdup(dst);
487                 newname = basename(newname);
488                 dstdir = strdup(dst);
489                 dstdir = dirname(dstdir);
490         }
491
492         if( !strcmp(newname,".") || !strcmp(newname,"..") ||
493              strchr(newname, '/') ){
494                 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
495                         newname);
496                 return 14;
497         }
498
499         len = strlen(newname);
500         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
501                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
502                         newname);
503                 return 14;
504         }
505
506         fddst = open_file_or_dir(dstdir);
507         if (fddst < 0) {
508                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
509                 return 12;
510         }
511
512         fd = open_file_or_dir(subvol);
513         if (fd < 0) {
514                 close(fddst);
515                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
516                 return 12;
517         }
518
519         if (readonly) {
520                 args.flags |= BTRFS_SUBVOL_RDONLY;
521                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
522                        subvol, dstdir, newname);
523         } else {
524                 printf("Create a snapshot of '%s' in '%s/%s'\n",
525                        subvol, dstdir, newname);
526         }
527
528         args.fd = fd;
529         if (inherit) {
530                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
531                 args.size = qgroup_inherit_size(inherit);
532                 args.qgroup_inherit = inherit;
533         }
534         strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
535         args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
536         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
537         e = errno;
538
539         close(fd);
540         close(fddst);
541
542         if(res < 0 ){
543                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
544                         subvol, strerror(e));
545                 return 11;
546         }
547         free(inherit);
548
549         return 0;
550 }
551
552 static const char * const cmd_subvol_get_default_usage[] = {
553         "btrfs subvolume get-default <path>",
554         "Get the default subvolume of a filesystem",
555         NULL
556 };
557
558 static int cmd_subvol_get_default(int argc, char **argv)
559 {
560         int fd;
561         int ret;
562         char *subvol;
563         struct btrfs_list_filter_set *filter_set;
564         u64 default_id;
565
566         if (check_argc_exact(argc, 2))
567                 usage(cmd_subvol_get_default_usage);
568
569         subvol = argv[1];
570
571         ret = test_issubvolume(subvol);
572         if (ret < 0) {
573                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
574                 return 12;
575         }
576         if (!ret) {
577                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
578                 return 13;
579         }
580
581         fd = open_file_or_dir(subvol);
582         if (fd < 0) {
583                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
584                 return 12;
585         }
586
587         ret = btrfs_list_get_default_subvolume(fd, &default_id);
588         if (ret) {
589                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
590                         strerror(errno));
591                 return ret;
592         }
593
594         if (default_id == 0) {
595                 fprintf(stderr, "ERROR: 'default' dir item not found\n");
596                 return ret;
597         }
598
599         /* no need to resolve roots if FS_TREE is default */
600         if (default_id == BTRFS_FS_TREE_OBJECTID) {
601                 printf("ID 5 (FS_TREE)\n");
602                 return ret;
603         }
604
605         filter_set = btrfs_list_alloc_filter_set();
606         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
607                                 default_id);
608
609         ret = btrfs_list_subvols(fd, filter_set, NULL, 0);
610         if (ret)
611                 return 19;
612         return 0;
613 }
614
615 static const char * const cmd_subvol_set_default_usage[] = {
616         "btrfs subvolume set-default <subvolid> <path>",
617         "Set the default subvolume of a filesystem",
618         NULL
619 };
620
621 static int cmd_subvol_set_default(int argc, char **argv)
622 {
623         int     ret=0, fd, e;
624         u64     objectid;
625         char    *path;
626         char    *subvolid;
627
628         if (check_argc_exact(argc, 3))
629                 usage(cmd_subvol_set_default_usage);
630
631         subvolid = argv[1];
632         path = argv[2];
633
634         fd = open_file_or_dir(path);
635         if (fd < 0) {
636                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
637                 return 12;
638         }
639
640         objectid = (unsigned long long)strtoll(subvolid, NULL, 0);
641         if (errno == ERANGE) {
642                 fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid);
643                 return 30;
644         }
645         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
646         e = errno;
647         close(fd);
648         if( ret < 0 ){
649                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
650                         strerror(e));
651                 return 30;
652         }
653         return 0;
654 }
655
656 static const char * const cmd_find_new_usage[] = {
657         "btrfs subvolume find-new <path> <lastgen>",
658         "List the recently modified files in a filesystem",
659         NULL
660 };
661
662 static int cmd_find_new(int argc, char **argv)
663 {
664         int fd;
665         int ret;
666         char *subvol;
667         u64 last_gen;
668
669         if (check_argc_exact(argc, 3))
670                 usage(cmd_find_new_usage);
671
672         subvol = argv[1];
673         last_gen = atoll(argv[2]);
674
675         ret = test_issubvolume(subvol);
676         if (ret < 0) {
677                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
678                 return 12;
679         }
680         if (!ret) {
681                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
682                 return 13;
683         }
684
685         fd = open_file_or_dir(subvol);
686         if (fd < 0) {
687                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
688                 return 12;
689         }
690         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
691         if (ret)
692                 return 19;
693         return 0;
694 }
695
696 const struct cmd_group subvolume_cmd_group = {
697         subvolume_cmd_group_usage, NULL, {
698                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
699                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
700                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
701                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
702                 { "get-default", cmd_subvol_get_default,
703                         cmd_subvol_get_default_usage, NULL, 0 },
704                 { "set-default", cmd_subvol_set_default,
705                         cmd_subvol_set_default_usage, NULL, 0 },
706                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
707                 { 0, 0, 0, 0, 0 }
708         }
709 };
710
711 int cmd_subvolume(int argc, char **argv)
712 {
713         return handle_command_group(&subvolume_cmd_group, argc, argv);
714 }