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