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