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