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