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