btrfs-progs: let test_issubvolume return the exact error
[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", path);
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", subvol);
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", subvol);
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", fullpath);
961                 goto out;
962         }
963         if (!ret) {
964                 error("not a subvolume: %s", fullpath);
965                 ret = 1;
966                 goto out;
967         }
968
969         ret = find_mount_root(fullpath, &mnt);
970         if (ret < 0) {
971                 error("find_mount_root failed on '%s': %s",
972                         fullpath, strerror(-ret));
973                 goto out;
974         }
975         if (ret > 0) {
976                 error("%s doesn't belong to btrfs mount point", fullpath);
977                 goto out;
978         }
979         ret = 1;
980         svpath = get_subvol_name(mnt, fullpath);
981
982         fd = btrfs_open_dir(fullpath, &dirstream1, 1);
983         if (fd < 0)
984                 goto out;
985
986         ret = btrfs_list_get_path_rootid(fd, &sv_id);
987         if (ret) {
988                 error("can't get rootid for '%s'", fullpath);
989                 goto out;
990         }
991
992         mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
993         if (mntfd < 0)
994                 goto out;
995
996         if (sv_id == BTRFS_FS_TREE_OBJECTID) {
997                 printf("%s is toplevel subvolume\n", fullpath);
998                 goto out;
999         }
1000
1001         memset(&get_ri, 0, sizeof(get_ri));
1002         get_ri.root_id = sv_id;
1003
1004         ret = btrfs_get_subvol(mntfd, &get_ri);
1005         if (ret) {
1006                 error("can't find '%s'", svpath);
1007                 goto out;
1008         }
1009
1010         /* print the info */
1011         printf("%s\n", fullpath);
1012         printf("\tName: \t\t\t%s\n", get_ri.name);
1013
1014         if (uuid_is_null(get_ri.uuid))
1015                 strcpy(uuidparse, "-");
1016         else
1017                 uuid_unparse(get_ri.uuid, uuidparse);
1018         printf("\tUUID: \t\t\t%s\n", uuidparse);
1019
1020         if (uuid_is_null(get_ri.puuid))
1021                 strcpy(uuidparse, "-");
1022         else
1023                 uuid_unparse(get_ri.puuid, uuidparse);
1024         printf("\tParent UUID: \t\t%s\n", uuidparse);
1025
1026         if (uuid_is_null(get_ri.ruuid))
1027                 strcpy(uuidparse, "-");
1028         else
1029                 uuid_unparse(get_ri.ruuid, uuidparse);
1030         printf("\tReceived UUID: \t\t%s\n", uuidparse);
1031
1032         if (get_ri.otime) {
1033                 struct tm tm;
1034
1035                 localtime_r(&get_ri.otime, &tm);
1036                 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1037         } else
1038                 strcpy(tstr, "-");
1039         printf("\tCreation time: \t\t%s\n", tstr);
1040
1041         printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1042         printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1043         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1044         printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1045         printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1046
1047         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1048                 printf("\tFlags: \t\t\treadonly\n");
1049         else
1050                 printf("\tFlags: \t\t\t-\n");
1051
1052         /* print the snapshots of the given subvol if any*/
1053         printf("\tSnapshot(s):\n");
1054         filter_set = btrfs_list_alloc_filter_set();
1055         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1056                                 (u64)(unsigned long)get_ri.uuid);
1057         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1058         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1059                         1, raw_prefix);
1060
1061         /* clean up */
1062         free(get_ri.path);
1063         free(get_ri.name);
1064         free(get_ri.full_path);
1065         btrfs_list_free_filter_set(filter_set);
1066
1067 out:
1068         close_file_or_dir(fd, dirstream1);
1069         close_file_or_dir(mntfd, dirstream2);
1070         free(mnt);
1071         free(fullpath);
1072         return !!ret;
1073 }
1074
1075 static const char * const cmd_subvol_sync_usage[] = {
1076         "btrfs subvolume sync <path> [<subvol-id>...]",
1077         "Wait until given subvolume(s) are completely removed from the filesystem.",
1078         "Wait until given subvolume(s) are completely removed from the filesystem",
1079         "after deletion.",
1080         "If no subvolume id is given, wait until all current deletion requests",
1081         "are completed, but do not wait for subvolumes deleted meanwhile.",
1082         "The status of subvolume ids is checked periodically.",
1083         "",
1084         "-s <N>       sleep N seconds between checks (default: 1)",
1085         NULL
1086 };
1087
1088 #if 0
1089 /*
1090  * If we're looking for any dead subvolume, take a shortcut and look
1091  * for any ORPHAN_ITEMs in the tree root
1092  */
1093 static int fs_has_dead_subvolumes(int fd)
1094 {
1095         int ret;
1096         struct btrfs_ioctl_search_args args;
1097         struct btrfs_ioctl_search_key *sk = &args.key;
1098         struct btrfs_ioctl_search_header sh;
1099         u64 min_subvolid = 0;
1100
1101 again:
1102         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1103         sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1104         sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1105         sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1106         sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1107         sk->min_offset = min_subvolid;
1108         sk->max_offset = (u64)-1;
1109         sk->min_transid = 0;
1110         sk->max_transid = (u64)-1;
1111         sk->nr_items = 1;
1112
1113         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1114         if (ret < 0)
1115                 return -errno;
1116
1117         if (!sk->nr_items)
1118                 return 0;
1119
1120         memcpy(&sh, args.buf, sizeof(sh));
1121         min_subvolid = sh.offset;
1122
1123         /*
1124          * Verify that the root item is really there and we haven't hit
1125          * a stale orphan
1126          */
1127         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1128         sk->min_objectid = min_subvolid;
1129         sk->max_objectid = min_subvolid;
1130         sk->min_type = BTRFS_ROOT_ITEM_KEY;
1131         sk->max_type = BTRFS_ROOT_ITEM_KEY;
1132         sk->min_offset = 0;
1133         sk->max_offset = (u64)-1;
1134         sk->min_transid = 0;
1135         sk->max_transid = (u64)-1;
1136         sk->nr_items = 1;
1137
1138         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1139         if (ret < 0)
1140                 return -errno;
1141
1142         /*
1143          * Stale orphan, try the next one
1144          */
1145         if (!sk->nr_items) {
1146                 min_subvolid++;
1147                 goto again;
1148         }
1149
1150         return 1;
1151 }
1152 #endif
1153
1154 #define SUBVOL_ID_BATCH         1024
1155
1156 /*
1157  * Enumerate all dead subvolumes that exist in the filesystem.
1158  * Fill @ids and reallocate to bigger size if needed.
1159  */
1160 static int enumerate_dead_subvols(int fd, u64 **ids)
1161 {
1162         int ret;
1163         struct btrfs_ioctl_search_args args;
1164         struct btrfs_ioctl_search_key *sk = &args.key;
1165         int idx = 0;
1166         int count = 0;
1167
1168         memset(&args, 0, sizeof(args));
1169
1170         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1171         sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1172         sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1173         sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1174         sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1175         sk->min_offset = 0;
1176         sk->max_offset = (u64)-1;
1177         sk->min_transid = 0;
1178         sk->max_transid = (u64)-1;
1179         sk->nr_items = 4096;
1180
1181         *ids = NULL;
1182         while (1) {
1183                 struct btrfs_ioctl_search_header *sh;
1184                 unsigned long off;
1185                 int i;
1186
1187                 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1188                 if (ret < 0)
1189                         return -errno;
1190
1191                 if (!sk->nr_items)
1192                         return idx;
1193
1194                 off = 0;
1195                 for (i = 0; i < sk->nr_items; i++) {
1196                         sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1197                         off += sizeof(*sh);
1198
1199                         if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1200                                 if (idx >= count) {
1201                                         u64 *newids;
1202
1203                                         count += SUBVOL_ID_BATCH;
1204                                         newids = (u64*)realloc(*ids, count);
1205                                         if (!newids)
1206                                                 return -ENOMEM;
1207                                         *ids = newids;
1208                                 }
1209                                 (*ids)[idx] = sh->offset;
1210                                 idx++;
1211                         }
1212                         off += sh->len;
1213
1214                         sk->min_objectid = sh->objectid;
1215                         sk->min_type = sh->type;
1216                         sk->min_offset = sh->offset;
1217                 }
1218                 if (sk->min_offset < (u64)-1)
1219                         sk->min_offset++;
1220                 else
1221                         break;
1222                 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1223                         break;
1224                 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1225                         break;
1226         }
1227
1228         return idx;
1229 }
1230
1231 static int cmd_subvol_sync(int argc, char **argv)
1232 {
1233         int fd = -1;
1234         int i;
1235         int ret = 1;
1236         DIR *dirstream = NULL;
1237         u64 *ids = NULL;
1238         int id_count;
1239         int sleep_interval = 1;
1240
1241         optind = 1;
1242         while (1) {
1243                 int c = getopt(argc, argv, "s:");
1244
1245                 if (c < 0)
1246                         break;
1247
1248                 switch (c) {
1249                 case 's':
1250                         sleep_interval = atoi(argv[optind]);
1251                         if (sleep_interval < 1) {
1252                                 error("invalid sleep interval %s",
1253                                         argv[optind]);
1254                                 ret = 1;
1255                                 goto out;
1256                         }
1257                         break;
1258                 default:
1259                         usage(cmd_subvol_sync_usage);
1260                 }
1261         }
1262
1263         if (check_argc_min(argc - optind, 1))
1264                 usage(cmd_subvol_sync_usage);
1265
1266         fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1267         if (fd < 0) {
1268                 ret = 1;
1269                 goto out;
1270         }
1271         optind++;
1272
1273         id_count = argc - optind;
1274         if (!id_count) {
1275                 id_count = enumerate_dead_subvols(fd, &ids);
1276                 if (id_count < 0) {
1277                         error("can't enumerate dead subvolumes: %s",
1278                                         strerror(-id_count));
1279                         ret = 1;
1280                         goto out;
1281                 }
1282                 if (id_count == 0) {
1283                         ret = 0;
1284                         goto out;
1285                 }
1286         } else {
1287                 ids = (u64*)malloc(id_count * sizeof(u64));
1288                 if (!ids) {
1289                         error("not enough memory");
1290                         ret = 1;
1291                         goto out;
1292                 }
1293
1294                 for (i = 0; i < id_count; i++) {
1295                         u64 id;
1296                         const char *arg;
1297
1298                         arg = argv[optind + i];
1299                         errno = 0;
1300                         id = strtoull(arg, NULL, 10);
1301                         if (errno < 0) {
1302                                 error("unrecognized subvolume id %s", arg);
1303                                 ret = 1;
1304                                 goto out;
1305                         }
1306                         if (id < BTRFS_FIRST_FREE_OBJECTID
1307                                         || id > BTRFS_LAST_FREE_OBJECTID) {
1308                                 error("subvolume id %s out of range\n", arg);
1309                                 ret = 1;
1310                                 goto out;
1311                         }
1312                         ids[i] = id;
1313                 }
1314         }
1315
1316         ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1317
1318 out:
1319         free(ids);
1320         close_file_or_dir(fd, dirstream);
1321
1322         return !!ret;
1323 }
1324
1325 static const char subvolume_cmd_group_info[] =
1326 "manage subvolumes: create, delete, list, etc";
1327
1328 const struct cmd_group subvolume_cmd_group = {
1329         subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1330                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1331                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1332                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1333                 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1334                         NULL, 0 },
1335                 { "get-default", cmd_subvol_get_default,
1336                         cmd_subvol_get_default_usage, NULL, 0 },
1337                 { "set-default", cmd_subvol_set_default,
1338                         cmd_subvol_set_default_usage, NULL, 0 },
1339                 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1340                         NULL, 0 },
1341                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1342                 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1343                 NULL_CMD_STRUCT
1344         }
1345 };
1346
1347 int cmd_subvolume(int argc, char **argv)
1348 {
1349         return handle_command_group(&subvolume_cmd_group, argc, argv);
1350 }