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