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