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