0d4a52ea981303b2c86c34e1f057870b9862ff16
[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 #include <uuid/uuid.h>
28
29 #include "kerncompat.h"
30 #include "ioctl.h"
31 #include "qgroup.h"
32
33 #include "ctree.h"
34 #include "commands.h"
35 #include "utils.h"
36 #include "btrfs-list.h"
37 #include "utils.h"
38
39 static const char * const subvolume_cmd_group_usage[] = {
40         "btrfs subvolume <command> <args>",
41         NULL
42 };
43
44 static const char * const cmd_subvol_create_usage[] = {
45         "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
46         "Create a subvolume",
47         "Create a subvolume <name> in <dest>.  If <dest> is not given",
48         "subvolume <name> will be created in the current directory.",
49         "",
50         "-i <qgroupid>  add the newly created subvolume to a qgroup. This",
51         "               option can be given multiple times.",
52         NULL
53 };
54
55 static int cmd_subvol_create(int argc, char **argv)
56 {
57         int     retval, res, len;
58         int     fddst = -1;
59         char    *dupname = NULL;
60         char    *dupdir = NULL;
61         char    *newname;
62         char    *dstdir;
63         char    *dst;
64         struct btrfs_qgroup_inherit *inherit = NULL;
65         DIR     *dirstream = NULL;
66
67         optind = 1;
68         while (1) {
69                 int c = getopt(argc, argv, "c:i:");
70                 if (c < 0)
71                         break;
72
73                 switch (c) {
74                 case 'c':
75                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
76                         if (res) {
77                                 retval = res;
78                                 goto out;
79                         }
80                         break;
81                 case 'i':
82                         res = qgroup_inherit_add_group(&inherit, optarg);
83                         if (res) {
84                                 retval = res;
85                                 goto out;
86                         }
87                         break;
88                 default:
89                         usage(cmd_subvol_create_usage);
90                 }
91         }
92
93         if (check_argc_exact(argc - optind, 1))
94                 usage(cmd_subvol_create_usage);
95
96         dst = argv[optind];
97
98         retval = 1;     /* failure */
99         res = test_isdir(dst);
100         if (res >= 0) {
101                 fprintf(stderr, "ERROR: '%s' exists\n", dst);
102                 goto out;
103         }
104
105         dupname = strdup(dst);
106         newname = basename(dupname);
107         dupdir = strdup(dst);
108         dstdir = dirname(dupdir);
109
110         if (!test_issubvolname(newname)) {
111                 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
112                         newname);
113                 goto out;
114         }
115
116         len = strlen(newname);
117         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
118                 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
119                         newname);
120                 goto out;
121         }
122
123         fddst = open_file_or_dir(dstdir, &dirstream);
124         if (fddst < 0) {
125                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
126                 goto out;
127         }
128
129         printf("Create subvolume '%s/%s'\n", dstdir, newname);
130         if (inherit) {
131                 struct btrfs_ioctl_vol_args_v2  args;
132
133                 memset(&args, 0, sizeof(args));
134                 strncpy_null(args.name, newname);
135                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
136                 args.size = qgroup_inherit_size(inherit);
137                 args.qgroup_inherit = inherit;
138
139                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
140         } else {
141                 struct btrfs_ioctl_vol_args     args;
142
143                 memset(&args, 0, sizeof(args));
144                 strncpy_null(args.name, newname);
145
146                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
147         }
148
149         if (res < 0) {
150                 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
151                         strerror(errno));
152                 goto out;
153         }
154
155         retval = 0;     /* success */
156 out:
157         close_file_or_dir(fddst, dirstream);
158         free(inherit);
159         free(dupname);
160         free(dupdir);
161
162         return retval;
163 }
164
165 /*
166  * test if path is a subvolume:
167  * this function return
168  * 0-> path exists but it is not a subvolume
169  * 1-> path exists and it is  a subvolume
170  * -1 -> path is unaccessible
171  */
172 int test_issubvolume(char *path)
173 {
174         struct stat     st;
175         int             res;
176
177         res = stat(path, &st);
178         if(res < 0 )
179                 return -1;
180
181         return (st.st_ino == 256) && S_ISDIR(st.st_mode);
182 }
183
184 static int wait_for_commit(int fd)
185 {
186         int ret;
187
188         ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
189         if (ret < 0)
190                 return ret;
191         return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
192 }
193
194 static const char * const cmd_subvol_delete_usage[] = {
195         "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
196         "Delete subvolume(s)",
197         "Delete subvolumes from the filesystem. The corresponding directory",
198         "is removed instantly but the data blocks are removed later.",
199         "The deletion does not involve full commit by default due to",
200         "performance reasons (as a consequence, the subvolume may appear again",
201         "after a crash). Use one of the --commit options to wait until the",
202         "operation is safely stored on the media.",
203         "",
204         "-c|--commit-after      wait for transaction commit at the end of the operation",
205         "-C|--commit-each       wait for transaction commit after deleting each subvolume",
206         NULL
207 };
208
209 static int cmd_subvol_delete(int argc, char **argv)
210 {
211         int     res, len, e, ret = 0;
212         int cnt;
213         int fd = -1;
214         struct btrfs_ioctl_vol_args     args;
215         char    *dname, *vname, *cpath;
216         char    *dupdname = NULL;
217         char    *dupvname = NULL;
218         char    *path;
219         DIR     *dirstream = NULL;
220         int sync_mode = 0;
221         struct option long_options[] = {
222                 {"commit-after", no_argument, NULL, 'c'},  /* sync mode 1 */
223                 {"commit-each", no_argument, NULL, 'C'},  /* sync mode 2 */
224                 {NULL, 0, NULL, 0}
225         };
226
227         optind = 1;
228         while (1) {
229                 int c;
230
231                 c = getopt_long(argc, argv, "cC", long_options, NULL);
232                 if (c < 0)
233                         break;
234
235                 switch(c) {
236                 case 'c':
237                         sync_mode = 1;
238                         break;
239                 case 'C':
240                         sync_mode = 2;
241                         break;
242                 default:
243                         usage(cmd_subvol_delete_usage);
244                 }
245         }
246
247         if (check_argc_min(argc - optind, 1))
248                 usage(cmd_subvol_delete_usage);
249
250         printf("Transaction commit: %s\n",
251                 !sync_mode ? "none (default)" :
252                 sync_mode == 1 ? "at the end" : "after each");
253
254         cnt = optind;
255
256 again:
257         path = argv[cnt];
258
259         res = test_issubvolume(path);
260         if (res < 0) {
261                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
262                 ret = 1;
263                 goto out;
264         }
265         if (!res) {
266                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
267                 ret = 1;
268                 goto out;
269         }
270
271         cpath = realpath(path, NULL);
272         if (!cpath) {
273                 ret = errno;
274                 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
275                         path, strerror(errno));
276                 goto out;
277         }
278         dupdname = strdup(cpath);
279         dname = dirname(dupdname);
280         dupvname = strdup(cpath);
281         vname = basename(dupvname);
282         free(cpath);
283
284         if (!test_issubvolname(vname)) {
285                 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
286                         vname);
287                 ret = 1;
288                 goto out;
289         }
290
291         len = strlen(vname);
292         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
293                 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
294                         vname);
295                 ret = 1;
296                 goto out;
297         }
298
299         fd = open_file_or_dir(dname, &dirstream);
300         if (fd < 0) {
301                 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
302                 ret = 1;
303                 goto out;
304         }
305
306         printf("Delete subvolume '%s/%s'\n", dname, vname);
307         strncpy_null(args.name, vname);
308         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
309         e = errno;
310
311         if(res < 0 ){
312                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
313                         dname, vname, strerror(e));
314                 ret = 1;
315                 goto out;
316         }
317
318         if (sync_mode == 1) {
319                 res = wait_for_commit(fd);
320                 if (res < 0) {
321                         fprintf(stderr,
322                                 "ERROR: unable to wait for commit after '%s': %s\n",
323                                 path, strerror(errno));
324                         ret = 1;
325                 }
326         }
327
328 out:
329         free(dupdname);
330         free(dupvname);
331         dupdname = NULL;
332         dupvname = NULL;
333         cnt++;
334         if (cnt < argc) {
335                 close_file_or_dir(fd, dirstream);
336                 /* avoid double free */
337                 fd = -1;
338                 dirstream = NULL;
339                 goto again;
340         }
341
342         if (sync_mode == 2 && fd != -1) {
343                 res = wait_for_commit(fd);
344                 if (res < 0) {
345                         fprintf(stderr,
346                                 "ERROR: unable to do final sync: %s\n",
347                                 strerror(errno));
348                         ret = 1;
349                 }
350         }
351         close_file_or_dir(fd, dirstream);
352
353         return ret;
354 }
355
356 /*
357  * Naming of options:
358  * - uppercase for filters and sort options
359  * - lowercase for enabling specific items in the output
360  */
361 static const char * const cmd_subvol_list_usage[] = {
362         "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
363         "[--sort=gen,ogen,rootid,path] <path>",
364         "List subvolumes (and snapshots)",
365         "",
366         "-p           print parent ID",
367         "-a           print all the subvolumes in the filesystem and",
368         "             distinguish absolute and relative path with respect",
369         "             to the given <path>",
370         "-c           print the ogeneration of the subvolume",
371         "-g           print the generation of the subvolume",
372         "-o           print only subvolumes bellow specified path",
373         "-u           print the uuid of subvolumes (and snapshots)",
374         "-q           print the parent uuid of the snapshots",
375         "-t           print the result as a table",
376         "-s           list snapshots only in the filesystem",
377         "-r           list readonly subvolumes (including snapshots)",
378         "-d           list deleted subvolumes that are not yet cleaned",
379         "-G [+|-]value",
380         "             filter the subvolumes by generation",
381         "             (+value: >= value; -value: <= value; value: = value)",
382         "-C [+|-]value",
383         "             filter the subvolumes by ogeneration",
384         "             (+value: >= value; -value: <= value; value: = value)",
385         "--sort=gen,ogen,rootid,path",
386         "             list the subvolume in order of gen, ogen, rootid or path",
387         "             you also can add '+' or '-' in front of each items.",
388         "             (+:ascending, -:descending, ascending default)",
389         NULL,
390 };
391
392 static int cmd_subvol_list(int argc, char **argv)
393 {
394         struct btrfs_list_filter_set *filter_set;
395         struct btrfs_list_comparer_set *comparer_set;
396         u64 flags = 0;
397         int fd = -1;
398         u64 top_id;
399         int ret = -1, uerr = 0;
400         int c;
401         char *subvol;
402         int is_tab_result = 0;
403         int is_list_all = 0;
404         int is_only_in_path = 0;
405         struct option long_options[] = {
406                 {"sort", 1, NULL, 'S'},
407                 {NULL, 0, NULL, 0}
408         };
409         DIR *dirstream = NULL;
410
411         filter_set = btrfs_list_alloc_filter_set();
412         comparer_set = btrfs_list_alloc_comparer_set();
413
414         optind = 1;
415         while(1) {
416                 c = getopt_long(argc, argv,
417                                     "acdgopqsurG:C:t", long_options, NULL);
418                 if (c < 0)
419                         break;
420
421                 switch(c) {
422                 case 'p':
423                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
424                         break;
425                 case 'a':
426                         is_list_all = 1;
427                         break;
428                 case 'c':
429                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
430                         break;
431                 case 'd':
432                         btrfs_list_setup_filter(&filter_set,
433                                                 BTRFS_LIST_FILTER_DELETED,
434                                                 0);
435                         break;
436                 case 'g':
437                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
438                         break;
439                 case 'o':
440                         is_only_in_path = 1;
441                         break;
442                 case 't':
443                         is_tab_result = 1;
444                         break;
445                 case 's':
446                         btrfs_list_setup_filter(&filter_set,
447                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
448                                                 0);
449                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
450                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
451                         break;
452                 case 'u':
453                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
454                         break;
455                 case 'q':
456                         btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
457                         break;
458                 case 'r':
459                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
460                         break;
461                 case 'G':
462                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
463                         ret = btrfs_list_parse_filter_string(optarg,
464                                                         &filter_set,
465                                                         BTRFS_LIST_FILTER_GEN);
466                         if (ret) {
467                                 uerr = 1;
468                                 goto out;
469                         }
470                         break;
471
472                 case 'C':
473                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
474                         ret = btrfs_list_parse_filter_string(optarg,
475                                                         &filter_set,
476                                                         BTRFS_LIST_FILTER_CGEN);
477                         if (ret) {
478                                 uerr = 1;
479                                 goto out;
480                         }
481                         break;
482                 case 'S':
483                         ret = btrfs_list_parse_sort_string(optarg,
484                                                            &comparer_set);
485                         if (ret) {
486                                 uerr = 1;
487                                 goto out;
488                         }
489                         break;
490
491                 default:
492                         uerr = 1;
493                         goto out;
494                 }
495         }
496
497         if (flags)
498                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
499                                         flags);
500
501         if (check_argc_exact(argc - optind, 1)) {
502                 uerr = 1;
503                 goto out;
504         }
505
506         subvol = argv[optind];
507         fd = open_file_or_dir(subvol, &dirstream);
508         if (fd < 0) {
509                 ret = -1;
510                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
511                 goto out;
512         }
513
514         ret = btrfs_list_get_path_rootid(fd, &top_id);
515         if (ret) {
516                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
517                 goto out;
518         }
519
520         if (is_list_all)
521                 btrfs_list_setup_filter(&filter_set,
522                                         BTRFS_LIST_FILTER_FULL_PATH,
523                                         top_id);
524         else if (is_only_in_path)
525                 btrfs_list_setup_filter(&filter_set,
526                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
527                                         top_id);
528
529         /* by default we shall print the following columns*/
530         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
531         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
532         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
533         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
534
535         if (is_tab_result)
536                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
537                                 BTRFS_LIST_LAYOUT_TABLE,
538                                 !is_list_all && !is_only_in_path, NULL);
539         else
540                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
541                                 BTRFS_LIST_LAYOUT_DEFAULT,
542                                 !is_list_all && !is_only_in_path, NULL);
543
544 out:
545         close_file_or_dir(fd, dirstream);
546         if (filter_set)
547                 btrfs_list_free_filter_set(filter_set);
548         if (comparer_set)
549                 btrfs_list_free_comparer_set(comparer_set);
550         if (uerr)
551                 usage(cmd_subvol_list_usage);
552         return !!ret;
553 }
554
555 static const char * const cmd_snapshot_usage[] = {
556         "btrfs subvolume snapshot [-r] <source> <dest>|[<dest>/]<name>",
557         "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
558         "Create a snapshot of the subvolume",
559         "Create a writable/readonly snapshot of the subvolume <source> with",
560         "the name <name> in the <dest> directory.  If only <dest> is given,",
561         "the subvolume will be named the basename of <source>.",
562         "",
563         "-r             create a readonly snapshot",
564         "-i <qgroupid>  add the newly created snapshot to a qgroup. This",
565         "               option can be given multiple times.",
566         NULL
567 };
568
569 static int cmd_snapshot(int argc, char **argv)
570 {
571         char    *subvol, *dst;
572         int     res, retval;
573         int     fd = -1, fddst = -1;
574         int     len, readonly = 0;
575         char    *dupname = NULL;
576         char    *dupdir = NULL;
577         char    *newname;
578         char    *dstdir;
579         struct btrfs_ioctl_vol_args_v2  args;
580         struct btrfs_qgroup_inherit *inherit = NULL;
581         DIR *dirstream1 = NULL, *dirstream2 = NULL;
582
583         optind = 1;
584         memset(&args, 0, sizeof(args));
585         while (1) {
586                 int c = getopt(argc, argv, "c:i:r");
587                 if (c < 0)
588                         break;
589
590                 switch (c) {
591                 case 'c':
592                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
593                         if (res) {
594                                 retval = res;
595                                 goto out;
596                         }
597                         break;
598                 case 'i':
599                         res = qgroup_inherit_add_group(&inherit, optarg);
600                         if (res) {
601                                 retval = res;
602                                 goto out;
603                         }
604                         break;
605                 case 'r':
606                         readonly = 1;
607                         break;
608                 case 'x':
609                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
610                         if (res) {
611                                 retval = res;
612                                 goto out;
613                         }
614                         break;
615                 default:
616                         usage(cmd_snapshot_usage);
617                 }
618         }
619
620         if (check_argc_exact(argc - optind, 2))
621                 usage(cmd_snapshot_usage);
622
623         subvol = argv[optind];
624         dst = argv[optind + 1];
625
626         retval = 1;     /* failure */
627         res = test_issubvolume(subvol);
628         if (res < 0) {
629                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
630                 goto out;
631         }
632         if (!res) {
633                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
634                 goto out;
635         }
636
637         res = test_isdir(dst);
638         if (res == 0) {
639                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
640                 goto out;
641         }
642
643         if (res > 0) {
644                 dupname = strdup(subvol);
645                 newname = basename(dupname);
646                 dstdir = dst;
647         } else {
648                 dupname = strdup(dst);
649                 newname = basename(dupname);
650                 dupdir = strdup(dst);
651                 dstdir = dirname(dupdir);
652         }
653
654         if (!test_issubvolname(newname)) {
655                 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
656                         newname);
657                 goto out;
658         }
659
660         len = strlen(newname);
661         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
662                 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
663                         newname);
664                 goto out;
665         }
666
667         fddst = open_file_or_dir(dstdir, &dirstream1);
668         if (fddst < 0) {
669                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
670                 goto out;
671         }
672
673         fd = open_file_or_dir(subvol, &dirstream2);
674         if (fd < 0) {
675                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
676                 goto out;
677         }
678
679         if (readonly) {
680                 args.flags |= BTRFS_SUBVOL_RDONLY;
681                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
682                        subvol, dstdir, newname);
683         } else {
684                 printf("Create a snapshot of '%s' in '%s/%s'\n",
685                        subvol, dstdir, newname);
686         }
687
688         args.fd = fd;
689         if (inherit) {
690                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
691                 args.size = qgroup_inherit_size(inherit);
692                 args.qgroup_inherit = inherit;
693         }
694         strncpy_null(args.name, newname);
695
696         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
697
698         if (res < 0) {
699                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
700                         subvol, strerror(errno));
701                 goto out;
702         }
703
704         retval = 0;     /* success */
705
706 out:
707         close_file_or_dir(fddst, dirstream1);
708         close_file_or_dir(fd, dirstream2);
709         free(inherit);
710         free(dupname);
711         free(dupdir);
712
713         return retval;
714 }
715
716 static const char * const cmd_subvol_get_default_usage[] = {
717         "btrfs subvolume get-default <path>",
718         "Get the default subvolume of a filesystem",
719         NULL
720 };
721
722 static int cmd_subvol_get_default(int argc, char **argv)
723 {
724         int fd = -1;
725         int ret;
726         char *subvol;
727         struct btrfs_list_filter_set *filter_set;
728         u64 default_id;
729         DIR *dirstream = NULL;
730
731         if (check_argc_exact(argc, 2))
732                 usage(cmd_subvol_get_default_usage);
733
734         subvol = argv[1];
735         fd = open_file_or_dir(subvol, &dirstream);
736         if (fd < 0) {
737                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
738                 return 1;
739         }
740
741         ret = btrfs_list_get_default_subvolume(fd, &default_id);
742         if (ret) {
743                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
744                         strerror(errno));
745                 goto out;
746         }
747
748         ret = 1;
749         if (default_id == 0) {
750                 fprintf(stderr, "ERROR: 'default' dir item not found\n");
751                 goto out;
752         }
753
754         /* no need to resolve roots if FS_TREE is default */
755         if (default_id == BTRFS_FS_TREE_OBJECTID) {
756                 printf("ID 5 (FS_TREE)\n");
757                 ret = 0;
758                 goto out;
759         }
760
761         filter_set = btrfs_list_alloc_filter_set();
762         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
763                                 default_id);
764
765         /* by default we shall print the following columns*/
766         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
767         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
768         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
769         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
770
771         ret = btrfs_list_subvols_print(fd, filter_set, NULL,
772                 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
773
774         if (filter_set)
775                 btrfs_list_free_filter_set(filter_set);
776 out:
777         close_file_or_dir(fd, dirstream);
778         return !!ret;
779 }
780
781 static const char * const cmd_subvol_set_default_usage[] = {
782         "btrfs subvolume set-default <subvolid> <path>",
783         "Set the default subvolume of a filesystem",
784         NULL
785 };
786
787 static int cmd_subvol_set_default(int argc, char **argv)
788 {
789         int     ret=0, fd, e;
790         u64     objectid;
791         char    *path;
792         char    *subvolid;
793         DIR     *dirstream = NULL;
794
795         if (check_argc_exact(argc, 3))
796                 usage(cmd_subvol_set_default_usage);
797
798         subvolid = argv[1];
799         path = argv[2];
800
801         objectid = arg_strtou64(subvolid);
802
803         fd = open_file_or_dir(path, &dirstream);
804         if (fd < 0) {
805                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
806                 return 1;
807         }
808
809         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
810         e = errno;
811         close_file_or_dir(fd, dirstream);
812         if (ret < 0) {
813                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
814                         strerror(e));
815                 return 1;
816         }
817         return 0;
818 }
819
820 static const char * const cmd_find_new_usage[] = {
821         "btrfs subvolume find-new <path> <lastgen>",
822         "List the recently modified files in a filesystem",
823         NULL
824 };
825
826 static int cmd_find_new(int argc, char **argv)
827 {
828         int fd;
829         int ret;
830         char *subvol;
831         u64 last_gen;
832         DIR *dirstream = NULL;
833
834         if (check_argc_exact(argc, 3))
835                 usage(cmd_find_new_usage);
836
837         subvol = argv[1];
838         last_gen = arg_strtou64(argv[2]);
839
840         ret = test_issubvolume(subvol);
841         if (ret < 0) {
842                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
843                 return 1;
844         }
845         if (!ret) {
846                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
847                 return 1;
848         }
849
850         fd = open_file_or_dir(subvol, &dirstream);
851         if (fd < 0) {
852                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
853                 return 1;
854         }
855
856         ret = ioctl(fd, BTRFS_IOC_SYNC);
857         if (ret < 0) {
858                 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
859                         subvol, strerror(errno));
860                 close_file_or_dir(fd, dirstream);
861                 return 1;
862         }
863
864         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
865         close_file_or_dir(fd, dirstream);
866         return !!ret;
867 }
868
869 static const char * const cmd_subvol_show_usage[] = {
870         "btrfs subvolume show <subvol-path>",
871         "Show more information of the subvolume",
872         NULL
873 };
874
875 static int cmd_subvol_show(int argc, char **argv)
876 {
877         struct root_info get_ri;
878         struct btrfs_list_filter_set *filter_set;
879         char tstr[256];
880         char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
881         char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
882         char raw_prefix[] = "\t\t\t\t";
883         u64 sv_id, mntid;
884         int fd = -1, mntfd = -1;
885         int ret = 1;
886         DIR *dirstream1 = NULL, *dirstream2 = NULL;
887
888         if (check_argc_exact(argc, 2))
889                 usage(cmd_subvol_show_usage);
890
891         fullpath = realpath(argv[1], NULL);
892         if (!fullpath) {
893                 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
894                         argv[1], strerror(errno));
895                 goto out;
896         }
897
898         ret = test_issubvolume(fullpath);
899         if (ret < 0) {
900                 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
901                 goto out;
902         }
903         if (!ret) {
904                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
905                 goto out;
906         }
907
908         ret = find_mount_root(fullpath, &mnt);
909         if (ret < 0) {
910                 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
911                                 "%s\n", fullpath, strerror(-ret));
912                 goto out;
913         }
914         if (ret > 0) {
915                 fprintf(stderr,
916                         "ERROR: %s doesn't belong to btrfs mount point\n",
917                         fullpath);
918                 ret = -EINVAL;
919                 goto out;
920         }
921         ret = 1;
922         svpath = get_subvol_name(mnt, fullpath);
923
924         fd = open_file_or_dir(fullpath, &dirstream1);
925         if (fd < 0) {
926                 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
927                 goto out;
928         }
929
930         ret = btrfs_list_get_path_rootid(fd, &sv_id);
931         if (ret) {
932                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
933                         fullpath);
934                 goto out;
935         }
936
937         mntfd = open_file_or_dir(mnt, &dirstream2);
938         if (mntfd < 0) {
939                 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
940                 goto out;
941         }
942
943         ret = btrfs_list_get_path_rootid(mntfd, &mntid);
944         if (ret) {
945                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
946                 goto out;
947         }
948
949         if (sv_id == BTRFS_FS_TREE_OBJECTID) {
950                 printf("%s is btrfs root\n", fullpath);
951                 goto out;
952         }
953
954         memset(&get_ri, 0, sizeof(get_ri));
955         get_ri.root_id = sv_id;
956
957         if (btrfs_get_subvol(mntfd, &get_ri)) {
958                 fprintf(stderr, "ERROR: can't find '%s'\n",
959                         svpath);
960                 goto out;
961         }
962
963         ret = 0;
964         /* print the info */
965         printf("%s\n", fullpath);
966         printf("\tName: \t\t\t%s\n", get_ri.name);
967
968         if (uuid_is_null(get_ri.uuid))
969                 strcpy(uuidparse, "-");
970         else
971                 uuid_unparse(get_ri.uuid, uuidparse);
972         printf("\tuuid: \t\t\t%s\n", uuidparse);
973
974         if (uuid_is_null(get_ri.puuid))
975                 strcpy(uuidparse, "-");
976         else
977                 uuid_unparse(get_ri.puuid, uuidparse);
978         printf("\tParent uuid: \t\t%s\n", uuidparse);
979
980         if (get_ri.otime) {
981                 struct tm tm;
982
983                 localtime_r(&get_ri.otime, &tm);
984                 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
985         } else
986                 strcpy(tstr, "-");
987         printf("\tCreation time: \t\t%s\n", tstr);
988
989         printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
990         printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
991         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
992         printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
993         printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
994
995         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
996                 printf("\tFlags: \t\t\treadonly\n");
997         else
998                 printf("\tFlags: \t\t\t-\n");
999
1000         /* print the snapshots of the given subvol if any*/
1001         printf("\tSnapshot(s):\n");
1002         filter_set = btrfs_list_alloc_filter_set();
1003         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1004                                 (u64)(unsigned long)get_ri.uuid);
1005         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1006         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1007                         1, raw_prefix);
1008
1009         /* clean up */
1010         free(get_ri.path);
1011         free(get_ri.name);
1012         free(get_ri.full_path);
1013         btrfs_list_free_filter_set(filter_set);
1014
1015 out:
1016         close_file_or_dir(fd, dirstream1);
1017         close_file_or_dir(mntfd, dirstream2);
1018         free(mnt);
1019         free(fullpath);
1020         return !!ret;
1021 }
1022
1023 const struct cmd_group subvolume_cmd_group = {
1024         subvolume_cmd_group_usage, NULL, {
1025                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1026                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1027                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1028                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1029                 { "get-default", cmd_subvol_get_default,
1030                         cmd_subvol_get_default_usage, NULL, 0 },
1031                 { "set-default", cmd_subvol_set_default,
1032                         cmd_subvol_set_default_usage, NULL, 0 },
1033                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1034                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1035                 NULL_CMD_STRUCT
1036         }
1037 };
1038
1039 int cmd_subvolume(int argc, char **argv)
1040 {
1041         return handle_command_group(&subvolume_cmd_group, argc, argv);
1042 }