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