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