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