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