btrfs-progs: tests README: fuzzed images
[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:v");
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         NULL
249 };
250
251 static int cmd_subvol_delete(int argc, char **argv)
252 {
253         int res, ret = 0;
254         int cnt;
255         int fd = -1;
256         struct btrfs_ioctl_vol_args     args;
257         char    *dname, *vname, *cpath;
258         char    *dupdname = NULL;
259         char    *dupvname = NULL;
260         char    *path;
261         DIR     *dirstream = NULL;
262         int verbose = 0;
263         int commit_mode = 0;
264
265         while (1) {
266                 int c;
267                 static const struct option long_options[] = {
268                         {"commit-after", no_argument, NULL, 'c'},  /* commit mode 1 */
269                         {"commit-each", no_argument, NULL, 'C'},  /* commit mode 2 */
270                         {NULL, 0, NULL, 0}
271                 };
272
273                 c = getopt_long(argc, argv, "cC", long_options, NULL);
274                 if (c < 0)
275                         break;
276
277                 switch(c) {
278                 case 'c':
279                         commit_mode = 1;
280                         break;
281                 case 'C':
282                         commit_mode = 2;
283                         break;
284                 case 'v':
285                         verbose++;
286                         break;
287                 default:
288                         usage(cmd_subvol_delete_usage);
289                 }
290         }
291
292         if (check_argc_min(argc - optind, 1))
293                 usage(cmd_subvol_delete_usage);
294
295         if (verbose > 0) {
296                 printf("Transaction commit: %s\n",
297                         !commit_mode ? "none (default)" :
298                         commit_mode == 1 ? "at the end" : "after each");
299         }
300
301         cnt = optind;
302
303 again:
304         path = argv[cnt];
305
306         res = test_issubvolume(path);
307         if (res < 0) {
308                 error("cannot access subvolume %s: %s", path, strerror(-res));
309                 ret = 1;
310                 goto out;
311         }
312         if (!res) {
313                 error("not a subvolume: %s", path);
314                 ret = 1;
315                 goto out;
316         }
317
318         cpath = realpath(path, NULL);
319         if (!cpath) {
320                 ret = errno;
321                 error("cannot find real path for '%s': %s",
322                         path, strerror(errno));
323                 goto out;
324         }
325         dupdname = strdup(cpath);
326         dname = dirname(dupdname);
327         dupvname = strdup(cpath);
328         vname = basename(dupvname);
329         free(cpath);
330
331         fd = btrfs_open_dir(dname, &dirstream, 1);
332         if (fd < 0) {
333                 ret = 1;
334                 goto out;
335         }
336
337         printf("Delete subvolume (%s): '%s/%s'\n",
338                 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
339                 ? "commit" : "no-commit", dname, vname);
340         memset(&args, 0, sizeof(args));
341         strncpy_null(args.name, vname);
342         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
343         if(res < 0 ){
344                 error("cannot delete '%s/%s': %s", dname, vname,
345                         strerror(errno));
346                 ret = 1;
347                 goto out;
348         }
349
350         if (commit_mode == 1) {
351                 res = wait_for_commit(fd);
352                 if (res < 0) {
353                         error("unable to wait for commit after '%s': %s",
354                                 path, strerror(errno));
355                         ret = 1;
356                 }
357         }
358
359 out:
360         free(dupdname);
361         free(dupvname);
362         dupdname = NULL;
363         dupvname = NULL;
364         cnt++;
365         if (cnt < argc) {
366                 close_file_or_dir(fd, dirstream);
367                 /* avoid double free */
368                 fd = -1;
369                 dirstream = NULL;
370                 goto again;
371         }
372
373         if (commit_mode == 2 && fd != -1) {
374                 res = wait_for_commit(fd);
375                 if (res < 0) {
376                         error("unable to do final sync after deletion: %s",
377                                 strerror(errno));
378                         ret = 1;
379                 }
380         }
381         close_file_or_dir(fd, dirstream);
382
383         return ret;
384 }
385
386 /*
387  * Naming of options:
388  * - uppercase for filters and sort options
389  * - lowercase for enabling specific items in the output
390  */
391 static const char * const cmd_subvol_list_usage[] = {
392         "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
393         "[--sort=gen,ogen,rootid,path] <path>",
394         "List subvolumes (and snapshots)",
395         "",
396         "-p           print parent ID",
397         "-a           print all the subvolumes in the filesystem and",
398         "             distinguish absolute and relative path with respect",
399         "             to the given <path>",
400         "-c           print the ogeneration of the subvolume",
401         "-g           print the generation of the subvolume",
402         "-o           print only subvolumes below specified path",
403         "-u           print the uuid of subvolumes (and snapshots)",
404         "-q           print the parent uuid of the snapshots",
405         "-R           print the uuid of the received snapshots",
406         "-t           print the result as a table",
407         "-s           list snapshots only in the filesystem",
408         "-r           list readonly subvolumes (including snapshots)",
409         "-d           list deleted subvolumes that are not yet cleaned",
410         "-G [+|-]value",
411         "             filter the subvolumes by generation",
412         "             (+value: >= value; -value: <= value; value: = value)",
413         "-C [+|-]value",
414         "             filter the subvolumes by ogeneration",
415         "             (+value: >= value; -value: <= value; value: = value)",
416         "--sort=gen,ogen,rootid,path",
417         "             list the subvolume in order of gen, ogen, rootid or path",
418         "             you also can add '+' or '-' in front of each items.",
419         "             (+:ascending, -:descending, ascending default)",
420         NULL,
421 };
422
423 static int cmd_subvol_list(int argc, char **argv)
424 {
425         struct btrfs_list_filter_set *filter_set;
426         struct btrfs_list_comparer_set *comparer_set;
427         u64 flags = 0;
428         int fd = -1;
429         u64 top_id;
430         int ret = -1, uerr = 0;
431         char *subvol;
432         int is_tab_result = 0;
433         int is_list_all = 0;
434         int is_only_in_path = 0;
435         DIR *dirstream = NULL;
436
437         filter_set = btrfs_list_alloc_filter_set();
438         comparer_set = btrfs_list_alloc_comparer_set();
439
440         while(1) {
441                 int c;
442                 static const struct option long_options[] = {
443                         {"sort", required_argument, NULL, 'S'},
444                         {NULL, 0, NULL, 0}
445                 };
446
447                 c = getopt_long(argc, argv,
448                                     "acdgopqsurRG:C:t", long_options, NULL);
449                 if (c < 0)
450                         break;
451
452                 switch(c) {
453                 case 'p':
454                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
455                         break;
456                 case 'a':
457                         is_list_all = 1;
458                         break;
459                 case 'c':
460                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
461                         break;
462                 case 'd':
463                         btrfs_list_setup_filter(&filter_set,
464                                                 BTRFS_LIST_FILTER_DELETED,
465                                                 0);
466                         break;
467                 case 'g':
468                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
469                         break;
470                 case 'o':
471                         is_only_in_path = 1;
472                         break;
473                 case 't':
474                         is_tab_result = 1;
475                         break;
476                 case 's':
477                         btrfs_list_setup_filter(&filter_set,
478                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
479                                                 0);
480                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
481                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
482                         break;
483                 case 'u':
484                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
485                         break;
486                 case 'q':
487                         btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
488                         break;
489                 case 'R':
490                         btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
491                         break;
492                 case 'r':
493                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
494                         break;
495                 case 'G':
496                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
497                         ret = btrfs_list_parse_filter_string(optarg,
498                                                         &filter_set,
499                                                         BTRFS_LIST_FILTER_GEN);
500                         if (ret) {
501                                 uerr = 1;
502                                 goto out;
503                         }
504                         break;
505
506                 case 'C':
507                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
508                         ret = btrfs_list_parse_filter_string(optarg,
509                                                         &filter_set,
510                                                         BTRFS_LIST_FILTER_CGEN);
511                         if (ret) {
512                                 uerr = 1;
513                                 goto out;
514                         }
515                         break;
516                 case 'S':
517                         ret = btrfs_list_parse_sort_string(optarg,
518                                                            &comparer_set);
519                         if (ret) {
520                                 uerr = 1;
521                                 goto out;
522                         }
523                         break;
524
525                 default:
526                         uerr = 1;
527                         goto out;
528                 }
529         }
530
531         if (flags)
532                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
533                                         flags);
534
535         if (check_argc_exact(argc - optind, 1)) {
536                 uerr = 1;
537                 goto out;
538         }
539
540         subvol = argv[optind];
541         fd = btrfs_open_dir(subvol, &dirstream, 1);
542         if (fd < 0) {
543                 ret = -1;
544                 error("can't access '%s'", subvol);
545                 goto out;
546         }
547
548         ret = btrfs_list_get_path_rootid(fd, &top_id);
549         if (ret) {
550                 error("can't get rootid for '%s'", subvol);
551                 goto out;
552         }
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                 btrfs_list_free_filter_set(filter_set);
582         if (comparer_set)
583                 btrfs_list_free_comparer_set(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                 btrfs_list_free_filter_set(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         btrfs_list_free_filter_set(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 }