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