3895d1169e5cdedbffe596f990af38a005bb41be
[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:v");
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, 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 verbose = 0;
221         int commit_mode = 0;
222         struct option long_options[] = {
223                 {"commit-after", no_argument, NULL, 'c'},  /* commit mode 1 */
224                 {"commit-each", no_argument, NULL, 'C'},  /* commit mode 2 */
225                 {NULL, 0, NULL, 0}
226         };
227
228         optind = 1;
229         while (1) {
230                 int c;
231
232                 c = getopt_long(argc, argv, "cC", long_options, NULL);
233                 if (c < 0)
234                         break;
235
236                 switch(c) {
237                 case 'c':
238                         commit_mode = 1;
239                         break;
240                 case 'C':
241                         commit_mode = 2;
242                         break;
243                 case 'v':
244                         verbose++;
245                         break;
246                 default:
247                         usage(cmd_subvol_delete_usage);
248                 }
249         }
250
251         if (check_argc_min(argc - optind, 1))
252                 usage(cmd_subvol_delete_usage);
253
254         if (verbose > 0) {
255                 printf("Transaction commit: %s\n",
256                         !commit_mode ? "none (default)" :
257                         commit_mode == 1 ? "at the end" : "after each");
258         }
259
260         cnt = optind;
261
262 again:
263         path = argv[cnt];
264
265         res = test_issubvolume(path);
266         if (res < 0) {
267                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
268                 ret = 1;
269                 goto out;
270         }
271         if (!res) {
272                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
273                 ret = 1;
274                 goto out;
275         }
276
277         cpath = realpath(path, NULL);
278         if (!cpath) {
279                 ret = errno;
280                 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
281                         path, strerror(errno));
282                 goto out;
283         }
284         dupdname = strdup(cpath);
285         dname = dirname(dupdname);
286         dupvname = strdup(cpath);
287         vname = basename(dupvname);
288         free(cpath);
289
290         fd = open_file_or_dir(dname, &dirstream);
291         if (fd < 0) {
292                 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
293                 ret = 1;
294                 goto out;
295         }
296
297         printf("Delete subvolume (%s): '%s/%s'\n",
298                 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
299                 ? "commit" : "no-commit", dname, vname);
300         strncpy_null(args.name, vname);
301         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
302         e = errno;
303
304         if(res < 0 ){
305                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
306                         dname, vname, strerror(e));
307                 ret = 1;
308                 goto out;
309         }
310
311         if (commit_mode == 1) {
312                 res = wait_for_commit(fd);
313                 if (res < 0) {
314                         fprintf(stderr,
315                                 "ERROR: unable to wait for commit after '%s': %s\n",
316                                 path, strerror(errno));
317                         ret = 1;
318                 }
319         }
320
321 out:
322         free(dupdname);
323         free(dupvname);
324         dupdname = NULL;
325         dupvname = NULL;
326         cnt++;
327         if (cnt < argc) {
328                 close_file_or_dir(fd, dirstream);
329                 /* avoid double free */
330                 fd = -1;
331                 dirstream = NULL;
332                 goto again;
333         }
334
335         if (commit_mode == 2 && fd != -1) {
336                 res = wait_for_commit(fd);
337                 if (res < 0) {
338                         fprintf(stderr,
339                                 "ERROR: unable to do final sync: %s\n",
340                                 strerror(errno));
341                         ret = 1;
342                 }
343         }
344         close_file_or_dir(fd, dirstream);
345
346         return ret;
347 }
348
349 /*
350  * Naming of options:
351  * - uppercase for filters and sort options
352  * - lowercase for enabling specific items in the output
353  */
354 static const char * const cmd_subvol_list_usage[] = {
355         "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
356         "[--sort=gen,ogen,rootid,path] <path>",
357         "List subvolumes (and snapshots)",
358         "",
359         "-p           print parent ID",
360         "-a           print all the subvolumes in the filesystem and",
361         "             distinguish absolute and relative path with respect",
362         "             to the given <path>",
363         "-c           print the ogeneration of the subvolume",
364         "-g           print the generation of the subvolume",
365         "-o           print only subvolumes below specified path",
366         "-u           print the uuid of subvolumes (and snapshots)",
367         "-q           print the parent uuid of the snapshots",
368         "-R           print the uuid of the received snapshots",
369         "-t           print the result as a table",
370         "-s           list snapshots only in the filesystem",
371         "-r           list readonly subvolumes (including snapshots)",
372         "-d           list deleted subvolumes that are not yet cleaned",
373         "-G [+|-]value",
374         "             filter the subvolumes by generation",
375         "             (+value: >= value; -value: <= value; value: = value)",
376         "-C [+|-]value",
377         "             filter the subvolumes by ogeneration",
378         "             (+value: >= value; -value: <= value; value: = value)",
379         "--sort=gen,ogen,rootid,path",
380         "             list the subvolume in order of gen, ogen, rootid or path",
381         "             you also can add '+' or '-' in front of each items.",
382         "             (+:ascending, -:descending, ascending default)",
383         NULL,
384 };
385
386 static int cmd_subvol_list(int argc, char **argv)
387 {
388         struct btrfs_list_filter_set *filter_set;
389         struct btrfs_list_comparer_set *comparer_set;
390         u64 flags = 0;
391         int fd = -1;
392         u64 top_id;
393         int ret = -1, uerr = 0;
394         int c;
395         char *subvol;
396         int is_tab_result = 0;
397         int is_list_all = 0;
398         int is_only_in_path = 0;
399         struct option long_options[] = {
400                 {"sort", 1, NULL, 'S'},
401                 {NULL, 0, NULL, 0}
402         };
403         DIR *dirstream = NULL;
404
405         filter_set = btrfs_list_alloc_filter_set();
406         comparer_set = btrfs_list_alloc_comparer_set();
407
408         optind = 1;
409         while(1) {
410                 c = getopt_long(argc, argv,
411                                     "acdgopqsurRG:C:t", long_options, NULL);
412                 if (c < 0)
413                         break;
414
415                 switch(c) {
416                 case 'p':
417                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
418                         break;
419                 case 'a':
420                         is_list_all = 1;
421                         break;
422                 case 'c':
423                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
424                         break;
425                 case 'd':
426                         btrfs_list_setup_filter(&filter_set,
427                                                 BTRFS_LIST_FILTER_DELETED,
428                                                 0);
429                         break;
430                 case 'g':
431                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
432                         break;
433                 case 'o':
434                         is_only_in_path = 1;
435                         break;
436                 case 't':
437                         is_tab_result = 1;
438                         break;
439                 case 's':
440                         btrfs_list_setup_filter(&filter_set,
441                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
442                                                 0);
443                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
444                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
445                         break;
446                 case 'u':
447                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
448                         break;
449                 case 'q':
450                         btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
451                         break;
452                 case 'R':
453                         btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
454                         break;
455                 case 'r':
456                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
457                         break;
458                 case 'G':
459                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
460                         ret = btrfs_list_parse_filter_string(optarg,
461                                                         &filter_set,
462                                                         BTRFS_LIST_FILTER_GEN);
463                         if (ret) {
464                                 uerr = 1;
465                                 goto out;
466                         }
467                         break;
468
469                 case 'C':
470                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
471                         ret = btrfs_list_parse_filter_string(optarg,
472                                                         &filter_set,
473                                                         BTRFS_LIST_FILTER_CGEN);
474                         if (ret) {
475                                 uerr = 1;
476                                 goto out;
477                         }
478                         break;
479                 case 'S':
480                         ret = btrfs_list_parse_sort_string(optarg,
481                                                            &comparer_set);
482                         if (ret) {
483                                 uerr = 1;
484                                 goto out;
485                         }
486                         break;
487
488                 default:
489                         uerr = 1;
490                         goto out;
491                 }
492         }
493
494         if (flags)
495                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
496                                         flags);
497
498         if (check_argc_exact(argc - optind, 1)) {
499                 uerr = 1;
500                 goto out;
501         }
502
503         subvol = argv[optind];
504         fd = open_file_or_dir(subvol, &dirstream);
505         if (fd < 0) {
506                 ret = -1;
507                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
508                 goto out;
509         }
510
511         ret = btrfs_list_get_path_rootid(fd, &top_id);
512         if (ret) {
513                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
514                 goto out;
515         }
516
517         if (is_list_all)
518                 btrfs_list_setup_filter(&filter_set,
519                                         BTRFS_LIST_FILTER_FULL_PATH,
520                                         top_id);
521         else if (is_only_in_path)
522                 btrfs_list_setup_filter(&filter_set,
523                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
524                                         top_id);
525
526         /* by default we shall print the following columns*/
527         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
528         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
529         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
530         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
531
532         if (is_tab_result)
533                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
534                                 BTRFS_LIST_LAYOUT_TABLE,
535                                 !is_list_all && !is_only_in_path, NULL);
536         else
537                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
538                                 BTRFS_LIST_LAYOUT_DEFAULT,
539                                 !is_list_all && !is_only_in_path, NULL);
540
541 out:
542         close_file_or_dir(fd, dirstream);
543         if (filter_set)
544                 btrfs_list_free_filter_set(filter_set);
545         if (comparer_set)
546                 btrfs_list_free_comparer_set(comparer_set);
547         if (uerr)
548                 usage(cmd_subvol_list_usage);
549         return !!ret;
550 }
551
552 static const char * const cmd_snapshot_usage[] = {
553         "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
554         "Create a snapshot of the subvolume",
555         "Create a writable/readonly snapshot of the subvolume <source> with",
556         "the name <name> in the <dest> directory.  If only <dest> is given,",
557         "the subvolume will be named the basename of <source>.",
558         "",
559         "-r             create a readonly snapshot",
560         "-i <qgroupid>  add the newly created snapshot to a qgroup. This",
561         "               option can be given multiple times.",
562         NULL
563 };
564
565 static int cmd_snapshot(int argc, char **argv)
566 {
567         char    *subvol, *dst;
568         int     res, retval;
569         int     fd = -1, fddst = -1;
570         int     len, readonly = 0;
571         char    *dupname = NULL;
572         char    *dupdir = NULL;
573         char    *newname;
574         char    *dstdir;
575         struct btrfs_ioctl_vol_args_v2  args;
576         struct btrfs_qgroup_inherit *inherit = NULL;
577         DIR *dirstream1 = NULL, *dirstream2 = NULL;
578
579         optind = 1;
580         memset(&args, 0, sizeof(args));
581         while (1) {
582                 int c = getopt(argc, argv, "c:i:r");
583                 if (c < 0)
584                         break;
585
586                 switch (c) {
587                 case 'c':
588                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
589                         if (res) {
590                                 retval = res;
591                                 goto out;
592                         }
593                         break;
594                 case 'i':
595                         res = qgroup_inherit_add_group(&inherit, optarg);
596                         if (res) {
597                                 retval = res;
598                                 goto out;
599                         }
600                         break;
601                 case 'r':
602                         readonly = 1;
603                         break;
604                 case 'x':
605                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
606                         if (res) {
607                                 retval = res;
608                                 goto out;
609                         }
610                         break;
611                 default:
612                         usage(cmd_snapshot_usage);
613                 }
614         }
615
616         if (check_argc_exact(argc - optind, 2))
617                 usage(cmd_snapshot_usage);
618
619         subvol = argv[optind];
620         dst = argv[optind + 1];
621
622         retval = 1;     /* failure */
623         res = test_issubvolume(subvol);
624         if (res < 0) {
625                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
626                 goto out;
627         }
628         if (!res) {
629                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
630                 goto out;
631         }
632
633         res = test_isdir(dst);
634         if (res == 0) {
635                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
636                 goto out;
637         }
638
639         if (res > 0) {
640                 dupname = strdup(subvol);
641                 newname = basename(dupname);
642                 dstdir = dst;
643         } else {
644                 dupname = strdup(dst);
645                 newname = basename(dupname);
646                 dupdir = strdup(dst);
647                 dstdir = dirname(dupdir);
648         }
649
650         if (!test_issubvolname(newname)) {
651                 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
652                         newname);
653                 goto out;
654         }
655
656         len = strlen(newname);
657         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
658                 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
659                         newname);
660                 goto out;
661         }
662
663         fddst = open_file_or_dir(dstdir, &dirstream1);
664         if (fddst < 0) {
665                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
666                 goto out;
667         }
668
669         fd = open_file_or_dir(subvol, &dirstream2);
670         if (fd < 0) {
671                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
672                 goto out;
673         }
674
675         if (readonly) {
676                 args.flags |= BTRFS_SUBVOL_RDONLY;
677                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
678                        subvol, dstdir, newname);
679         } else {
680                 printf("Create a snapshot of '%s' in '%s/%s'\n",
681                        subvol, dstdir, newname);
682         }
683
684         args.fd = fd;
685         if (inherit) {
686                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
687                 args.size = qgroup_inherit_size(inherit);
688                 args.qgroup_inherit = inherit;
689         }
690         strncpy_null(args.name, newname);
691
692         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
693
694         if (res < 0) {
695                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
696                         subvol, strerror(errno));
697                 goto out;
698         }
699
700         retval = 0;     /* success */
701
702 out:
703         close_file_or_dir(fddst, dirstream1);
704         close_file_or_dir(fd, dirstream2);
705         free(inherit);
706         free(dupname);
707         free(dupdir);
708
709         return retval;
710 }
711
712 static const char * const cmd_subvol_get_default_usage[] = {
713         "btrfs subvolume get-default <path>",
714         "Get the default subvolume of a filesystem",
715         NULL
716 };
717
718 static int cmd_subvol_get_default(int argc, char **argv)
719 {
720         int fd = -1;
721         int ret;
722         char *subvol;
723         struct btrfs_list_filter_set *filter_set;
724         u64 default_id;
725         DIR *dirstream = NULL;
726
727         if (check_argc_exact(argc, 2))
728                 usage(cmd_subvol_get_default_usage);
729
730         subvol = argv[1];
731         fd = open_file_or_dir(subvol, &dirstream);
732         if (fd < 0) {
733                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
734                 return 1;
735         }
736
737         ret = btrfs_list_get_default_subvolume(fd, &default_id);
738         if (ret) {
739                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
740                         strerror(errno));
741                 goto out;
742         }
743
744         ret = 1;
745         if (default_id == 0) {
746                 fprintf(stderr, "ERROR: 'default' dir item not found\n");
747                 goto out;
748         }
749
750         /* no need to resolve roots if FS_TREE is default */
751         if (default_id == BTRFS_FS_TREE_OBJECTID) {
752                 printf("ID 5 (FS_TREE)\n");
753                 ret = 0;
754                 goto out;
755         }
756
757         filter_set = btrfs_list_alloc_filter_set();
758         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
759                                 default_id);
760
761         /* by default we shall print the following columns*/
762         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
763         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
764         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
765         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
766
767         ret = btrfs_list_subvols_print(fd, filter_set, NULL,
768                 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
769
770         if (filter_set)
771                 btrfs_list_free_filter_set(filter_set);
772 out:
773         close_file_or_dir(fd, dirstream);
774         return !!ret;
775 }
776
777 static const char * const cmd_subvol_set_default_usage[] = {
778         "btrfs subvolume set-default <subvolid> <path>",
779         "Set the default subvolume of a filesystem",
780         NULL
781 };
782
783 static int cmd_subvol_set_default(int argc, char **argv)
784 {
785         int     ret=0, fd, e;
786         u64     objectid;
787         char    *path;
788         char    *subvolid;
789         DIR     *dirstream = NULL;
790
791         if (check_argc_exact(argc, 3))
792                 usage(cmd_subvol_set_default_usage);
793
794         subvolid = argv[1];
795         path = argv[2];
796
797         objectid = arg_strtou64(subvolid);
798
799         fd = open_file_or_dir(path, &dirstream);
800         if (fd < 0) {
801                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
802                 return 1;
803         }
804
805         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
806         e = errno;
807         close_file_or_dir(fd, dirstream);
808         if (ret < 0) {
809                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
810                         strerror(e));
811                 return 1;
812         }
813         return 0;
814 }
815
816 static const char * const cmd_find_new_usage[] = {
817         "btrfs subvolume find-new <path> <lastgen>",
818         "List the recently modified files in a filesystem",
819         NULL
820 };
821
822 static int cmd_find_new(int argc, char **argv)
823 {
824         int fd;
825         int ret;
826         char *subvol;
827         u64 last_gen;
828         DIR *dirstream = NULL;
829
830         if (check_argc_exact(argc, 3))
831                 usage(cmd_find_new_usage);
832
833         subvol = argv[1];
834         last_gen = arg_strtou64(argv[2]);
835
836         ret = test_issubvolume(subvol);
837         if (ret < 0) {
838                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
839                 return 1;
840         }
841         if (!ret) {
842                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
843                 return 1;
844         }
845
846         fd = open_file_or_dir(subvol, &dirstream);
847         if (fd < 0) {
848                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
849                 return 1;
850         }
851
852         ret = ioctl(fd, BTRFS_IOC_SYNC);
853         if (ret < 0) {
854                 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
855                         subvol, strerror(errno));
856                 close_file_or_dir(fd, dirstream);
857                 return 1;
858         }
859
860         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
861         close_file_or_dir(fd, dirstream);
862         return !!ret;
863 }
864
865 static const char * const cmd_subvol_show_usage[] = {
866         "btrfs subvolume show <subvol-path>",
867         "Show more information of the subvolume",
868         NULL
869 };
870
871 static int cmd_subvol_show(int argc, char **argv)
872 {
873         struct root_info get_ri;
874         struct btrfs_list_filter_set *filter_set;
875         char tstr[256];
876         char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
877         char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
878         char raw_prefix[] = "\t\t\t\t";
879         u64 sv_id, mntid;
880         int fd = -1, mntfd = -1;
881         int ret = 1;
882         DIR *dirstream1 = NULL, *dirstream2 = NULL;
883
884         if (check_argc_exact(argc, 2))
885                 usage(cmd_subvol_show_usage);
886
887         fullpath = realpath(argv[1], NULL);
888         if (!fullpath) {
889                 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
890                         argv[1], strerror(errno));
891                 goto out;
892         }
893
894         ret = test_issubvolume(fullpath);
895         if (ret < 0) {
896                 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
897                 goto out;
898         }
899         if (!ret) {
900                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
901                 ret = 1;
902                 goto out;
903         }
904
905         ret = find_mount_root(fullpath, &mnt);
906         if (ret < 0) {
907                 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
908                                 "%s\n", fullpath, strerror(-ret));
909                 goto out;
910         }
911         if (ret > 0) {
912                 fprintf(stderr,
913                         "ERROR: %s doesn't belong to btrfs mount point\n",
914                         fullpath);
915                 goto out;
916         }
917         ret = 1;
918         svpath = get_subvol_name(mnt, fullpath);
919
920         fd = open_file_or_dir(fullpath, &dirstream1);
921         if (fd < 0) {
922                 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
923                 goto out;
924         }
925
926         ret = btrfs_list_get_path_rootid(fd, &sv_id);
927         if (ret) {
928                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
929                         fullpath);
930                 goto out;
931         }
932
933         mntfd = open_file_or_dir(mnt, &dirstream2);
934         if (mntfd < 0) {
935                 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
936                 goto out;
937         }
938
939         ret = btrfs_list_get_path_rootid(mntfd, &mntid);
940         if (ret) {
941                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
942                 goto out;
943         }
944
945         if (sv_id == BTRFS_FS_TREE_OBJECTID) {
946                 printf("%s is btrfs root\n", fullpath);
947                 goto out;
948         }
949
950         memset(&get_ri, 0, sizeof(get_ri));
951         get_ri.root_id = sv_id;
952
953         ret = btrfs_get_subvol(mntfd, &get_ri);
954         if (ret) {
955                 fprintf(stderr, "ERROR: can't find '%s'\n",
956                         svpath);
957                 goto out;
958         }
959
960         /* print the info */
961         printf("%s\n", fullpath);
962         printf("\tName: \t\t\t%s\n", get_ri.name);
963
964         if (uuid_is_null(get_ri.uuid))
965                 strcpy(uuidparse, "-");
966         else
967                 uuid_unparse(get_ri.uuid, uuidparse);
968         printf("\tuuid: \t\t\t%s\n", uuidparse);
969
970         if (uuid_is_null(get_ri.puuid))
971                 strcpy(uuidparse, "-");
972         else
973                 uuid_unparse(get_ri.puuid, uuidparse);
974         printf("\tParent uuid: \t\t%s\n", uuidparse);
975
976         if (get_ri.otime) {
977                 struct tm tm;
978
979                 localtime_r(&get_ri.otime, &tm);
980                 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
981         } else
982                 strcpy(tstr, "-");
983         printf("\tCreation time: \t\t%s\n", tstr);
984
985         printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
986         printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
987         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
988         printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
989         printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
990
991         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
992                 printf("\tFlags: \t\t\treadonly\n");
993         else
994                 printf("\tFlags: \t\t\t-\n");
995
996         /* print the snapshots of the given subvol if any*/
997         printf("\tSnapshot(s):\n");
998         filter_set = btrfs_list_alloc_filter_set();
999         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1000                                 (u64)(unsigned long)get_ri.uuid);
1001         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1002         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1003                         1, raw_prefix);
1004
1005         /* clean up */
1006         free(get_ri.path);
1007         free(get_ri.name);
1008         free(get_ri.full_path);
1009         btrfs_list_free_filter_set(filter_set);
1010
1011 out:
1012         close_file_or_dir(fd, dirstream1);
1013         close_file_or_dir(mntfd, dirstream2);
1014         free(mnt);
1015         free(fullpath);
1016         return !!ret;
1017 }
1018
1019 static const char * const cmd_subvol_sync_usage[] = {
1020         "btrfs subvolume sync <path> [<subvol-id>...]",
1021         "Wait until given subvolume(s) are completely removed from the filesystem.",
1022         "Wait until given subvolume(s) are completely removed from the filesystem",
1023         "after deletion.",
1024         "If no subvolume id is given, wait until all ongoing deletion requests",
1025         "are complete. This may take long if new deleted subvolumes appear during",
1026         "the sleep interval.",
1027         "",
1028         "-s <N>       sleep N seconds between checks (default: 1)",
1029         NULL
1030 };
1031
1032 static int is_subvolume_cleaned(int fd, u64 subvolid)
1033 {
1034         int ret;
1035         struct btrfs_ioctl_search_args args;
1036         struct btrfs_ioctl_search_key *sk = &args.key;
1037
1038         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1039         sk->min_objectid = subvolid;
1040         sk->max_objectid = subvolid;
1041         sk->min_type = BTRFS_ROOT_ITEM_KEY;
1042         sk->max_type = BTRFS_ROOT_ITEM_KEY;
1043         sk->min_offset = 0;
1044         sk->max_offset = (u64)-1;
1045         sk->min_transid = 0;
1046         sk->max_transid = (u64)-1;
1047         sk->nr_items = 1;
1048
1049         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1050         if (ret < 0)
1051                 return -errno;
1052
1053         if (sk->nr_items == 0)
1054                 return 1;
1055
1056         return 0;
1057 }
1058
1059 /*
1060  * If we're looking for any dead subvolume, take a shortcut and look
1061  * for any ORPHAN_ITEMs in the tree root
1062  */
1063 static int fs_has_dead_subvolumes(int fd)
1064 {
1065         int ret;
1066         struct btrfs_ioctl_search_args args;
1067         struct btrfs_ioctl_search_key *sk = &args.key;
1068         struct btrfs_ioctl_search_header sh;
1069         u64 min_subvolid = 0;
1070
1071 again:
1072         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1073         sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1074         sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1075         sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1076         sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1077         sk->min_offset = min_subvolid;
1078         sk->max_offset = (u64)-1;
1079         sk->min_transid = 0;
1080         sk->max_transid = (u64)-1;
1081         sk->nr_items = 1;
1082
1083         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1084         if (ret < 0)
1085                 return -errno;
1086
1087         if (!sk->nr_items)
1088                 return 0;
1089
1090         memcpy(&sh, args.buf, sizeof(sh));
1091         min_subvolid = sh.offset;
1092
1093         /*
1094          * Verify that the root item is really there and we haven't hit
1095          * a stale orphan
1096          */
1097         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1098         sk->min_objectid = min_subvolid;
1099         sk->max_objectid = min_subvolid;
1100         sk->min_type = BTRFS_ROOT_ITEM_KEY;
1101         sk->max_type = BTRFS_ROOT_ITEM_KEY;
1102         sk->min_offset = 0;
1103         sk->max_offset = (u64)-1;
1104         sk->min_transid = 0;
1105         sk->max_transid = (u64)-1;
1106         sk->nr_items = 1;
1107
1108         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1109         if (ret < 0)
1110                 return -errno;
1111
1112         /*
1113          * Stale orphan, try the next one
1114          */
1115         if (!sk->nr_items) {
1116                 min_subvolid++;
1117                 goto again;
1118         }
1119
1120         return 1;
1121 }
1122
1123 static int cmd_subvol_sync(int argc, char **argv)
1124 {
1125         int fd = -1;
1126         int i;
1127         int ret = 1;
1128         DIR *dirstream = NULL;
1129         u64 *ids = NULL;
1130         int id_count;
1131         int remaining;
1132         int sleep_interval = 1;
1133
1134         optind = 1;
1135         while (1) {
1136                 int c = getopt(argc, argv, "s:");
1137
1138                 if (c < 0)
1139                         break;
1140
1141                 switch (c) {
1142                 case 's':
1143                         sleep_interval = atoi(argv[optind]);
1144                         if (sleep_interval < 1) {
1145                                 fprintf(stderr,
1146                                         "ERROR: invalid sleep interval %s\n",
1147                                         argv[optind]);
1148                                 ret = 1;
1149                                 goto out;
1150                         }
1151                         break;
1152                 default:
1153                         usage(cmd_subvol_sync_usage);
1154                 }
1155         }
1156
1157         if (check_argc_min(argc - optind, 1))
1158                 usage(cmd_subvol_sync_usage);
1159
1160         fd = open_file_or_dir(argv[optind], &dirstream);
1161         if (fd < 0) {
1162                 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1163                 ret = 1;
1164                 goto out;
1165         }
1166         optind++;
1167
1168         id_count = argc - optind;
1169
1170         /*
1171          * Wait for all
1172          */
1173         if (!id_count) {
1174                 while (1) {
1175                         ret = fs_has_dead_subvolumes(fd);
1176                         if (ret < 0) {
1177                                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1178                                                 strerror(-ret));
1179                                 ret = 1;
1180                                 goto out;
1181                         }
1182                         if (!ret)
1183                                 goto out;
1184                         sleep(sleep_interval);
1185                 }
1186         }
1187
1188         /*
1189          * Wait only for the requested ones
1190          */
1191         ids = (u64*)malloc(sizeof(u64) * id_count);
1192
1193         if (!ids) {
1194                 fprintf(stderr, "ERROR: not enough memory\n");
1195                 ret = 1;
1196                 goto out;
1197         }
1198
1199         for (i = 0; i < id_count; i++) {
1200                 u64 id;
1201                 const char *arg;
1202
1203                 arg = argv[optind + i];
1204                 errno = 0;
1205                 id = strtoull(arg, NULL, 10);
1206                 if (errno < 0) {
1207                         fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1208                                 arg);
1209                         ret = 1;
1210                         goto out;
1211                 }
1212                 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1213                         fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1214                                 arg);
1215                         ret = 1;
1216                         goto out;
1217                 }
1218                 ids[i] = id;
1219         }
1220
1221         remaining = id_count;
1222         while (1) {
1223                 for (i = 0; i < id_count; i++) {
1224                         if (!ids[i])
1225                                 continue;
1226                         ret = is_subvolume_cleaned(fd, ids[i]);
1227                         if (ret < 0) {
1228                                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1229                                                 strerror(-ret));
1230                                 goto out;
1231                         }
1232                         if (ret) {
1233                                 printf("Subvolume id %llu is gone\n", ids[i]);
1234                                 ids[i] = 0;
1235                                 remaining--;
1236                         }
1237                 }
1238                 if (!remaining)
1239                         break;
1240                 sleep(sleep_interval);
1241         }
1242
1243 out:
1244         free(ids);
1245         close_file_or_dir(fd, dirstream);
1246
1247         return !!ret;
1248 }
1249
1250 const struct cmd_group subvolume_cmd_group = {
1251         subvolume_cmd_group_usage, NULL, {
1252                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1253                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1254                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1255                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1256                 { "get-default", cmd_subvol_get_default,
1257                         cmd_subvol_get_default_usage, NULL, 0 },
1258                 { "set-default", cmd_subvol_set_default,
1259                         cmd_subvol_set_default_usage, NULL, 0 },
1260                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1261                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1262                 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1263                 NULL_CMD_STRUCT
1264         }
1265 };
1266
1267 int cmd_subvolume(int argc, char **argv)
1268 {
1269         return handle_command_group(&subvolume_cmd_group, argc, argv);
1270 }