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