btrfs-progs: subvol delete: add missing verbose option
[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         while (1) {
131                 int c = getopt(argc, argv, "c:i:v");
132                 if (c < 0)
133                         break;
134
135                 switch (c) {
136                 case 'c':
137                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
138                         if (res) {
139                                 retval = res;
140                                 goto out;
141                         }
142                         break;
143                 case 'i':
144                         res = qgroup_inherit_add_group(&inherit, optarg);
145                         if (res) {
146                                 retval = res;
147                                 goto out;
148                         }
149                         break;
150                 default:
151                         usage(cmd_subvol_create_usage);
152                 }
153         }
154
155         if (check_argc_exact(argc - optind, 1))
156                 usage(cmd_subvol_create_usage);
157
158         dst = argv[optind];
159
160         retval = 1;     /* failure */
161         res = test_isdir(dst);
162         if (res < 0 && res != -ENOENT) {
163                 error("cannot access %s: %s", dst, strerror(-res));
164                 goto out;
165         }
166         if (res >= 0) {
167                 error("target path already exists: %s", dst);
168                 goto out;
169         }
170
171         dupname = strdup(dst);
172         newname = basename(dupname);
173         dupdir = strdup(dst);
174         dstdir = dirname(dupdir);
175
176         if (!test_issubvolname(newname)) {
177                 error("invalid subvolume name: %s", newname);
178                 goto out;
179         }
180
181         len = strlen(newname);
182         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
183                 error("subvolume name too long: %s", newname);
184                 goto out;
185         }
186
187         fddst = btrfs_open_dir(dstdir, &dirstream, 1);
188         if (fddst < 0)
189                 goto out;
190
191         printf("Create subvolume '%s/%s'\n", dstdir, newname);
192         if (inherit) {
193                 struct btrfs_ioctl_vol_args_v2  args;
194
195                 memset(&args, 0, sizeof(args));
196                 strncpy_null(args.name, newname);
197                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
198                 args.size = qgroup_inherit_size(inherit);
199                 args.qgroup_inherit = inherit;
200
201                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
202         } else {
203                 struct btrfs_ioctl_vol_args     args;
204
205                 memset(&args, 0, sizeof(args));
206                 strncpy_null(args.name, newname);
207
208                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
209         }
210
211         if (res < 0) {
212                 error("cannot create subvolume: %s", strerror(errno));
213                 goto out;
214         }
215
216         retval = 0;     /* success */
217 out:
218         close_file_or_dir(fddst, dirstream);
219         free(inherit);
220         free(dupname);
221         free(dupdir);
222
223         return retval;
224 }
225
226 static int wait_for_commit(int fd)
227 {
228         int ret;
229
230         ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
231         if (ret < 0)
232                 return ret;
233         return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
234 }
235
236 static const char * const cmd_subvol_delete_usage[] = {
237         "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
238         "Delete subvolume(s)",
239         "Delete subvolumes from the filesystem. The corresponding directory",
240         "is removed instantly but the data blocks are removed later.",
241         "The deletion does not involve full commit by default due to",
242         "performance reasons (as a consequence, the subvolume may appear again",
243         "after a crash). Use one of the --commit options to wait until the",
244         "operation is safely stored on the media.",
245         "",
246         "-c|--commit-after      wait for transaction commit at the end of the operation",
247         "-C|--commit-each       wait for transaction commit after deleting each subvolume",
248         "-v|--verbose           verbose output of operations",
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         while (1) {
267                 int c;
268                 static const struct option long_options[] = {
269                         {"commit-after", no_argument, NULL, 'c'},  /* commit mode 1 */
270                         {"commit-each", no_argument, NULL, 'C'},  /* commit mode 2 */
271                         {"verbose", no_argument, NULL, 'v'},
272                         {NULL, 0, NULL, 0}
273                 };
274
275                 c = getopt_long(argc, argv, "cCv", 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         while(1) {
443                 int c;
444                 static const struct option long_options[] = {
445                         {"sort", required_argument, NULL, 'S'},
446                         {NULL, 0, NULL, 0}
447                 };
448
449                 c = getopt_long(argc, argv,
450                                     "acdgopqsurRG:C:t", long_options, NULL);
451                 if (c < 0)
452                         break;
453
454                 switch(c) {
455                 case 'p':
456                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
457                         break;
458                 case 'a':
459                         is_list_all = 1;
460                         break;
461                 case 'c':
462                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
463                         break;
464                 case 'd':
465                         btrfs_list_setup_filter(&filter_set,
466                                                 BTRFS_LIST_FILTER_DELETED,
467                                                 0);
468                         break;
469                 case 'g':
470                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
471                         break;
472                 case 'o':
473                         is_only_in_path = 1;
474                         break;
475                 case 't':
476                         is_tab_result = 1;
477                         break;
478                 case 's':
479                         btrfs_list_setup_filter(&filter_set,
480                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
481                                                 0);
482                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
483                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
484                         break;
485                 case 'u':
486                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
487                         break;
488                 case 'q':
489                         btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
490                         break;
491                 case 'R':
492                         btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
493                         break;
494                 case 'r':
495                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
496                         break;
497                 case 'G':
498                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
499                         ret = btrfs_list_parse_filter_string(optarg,
500                                                         &filter_set,
501                                                         BTRFS_LIST_FILTER_GEN);
502                         if (ret) {
503                                 uerr = 1;
504                                 goto out;
505                         }
506                         break;
507
508                 case 'C':
509                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
510                         ret = btrfs_list_parse_filter_string(optarg,
511                                                         &filter_set,
512                                                         BTRFS_LIST_FILTER_CGEN);
513                         if (ret) {
514                                 uerr = 1;
515                                 goto out;
516                         }
517                         break;
518                 case 'S':
519                         ret = btrfs_list_parse_sort_string(optarg,
520                                                            &comparer_set);
521                         if (ret) {
522                                 uerr = 1;
523                                 goto out;
524                         }
525                         break;
526
527                 default:
528                         uerr = 1;
529                         goto out;
530                 }
531         }
532
533         if (flags)
534                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
535                                         flags);
536
537         if (check_argc_exact(argc - optind, 1)) {
538                 uerr = 1;
539                 goto out;
540         }
541
542         subvol = argv[optind];
543         fd = btrfs_open_dir(subvol, &dirstream, 1);
544         if (fd < 0) {
545                 ret = -1;
546                 error("can't access '%s'", subvol);
547                 goto out;
548         }
549
550         ret = btrfs_list_get_path_rootid(fd, &top_id);
551         if (ret) {
552                 error("can't get rootid for '%s'", subvol);
553                 goto out;
554         }
555
556         if (is_list_all)
557                 btrfs_list_setup_filter(&filter_set,
558                                         BTRFS_LIST_FILTER_FULL_PATH,
559                                         top_id);
560         else if (is_only_in_path)
561                 btrfs_list_setup_filter(&filter_set,
562                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
563                                         top_id);
564
565         /* by default we shall print the following columns*/
566         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
567         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
568         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
569         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
570
571         if (is_tab_result)
572                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
573                                 BTRFS_LIST_LAYOUT_TABLE,
574                                 !is_list_all && !is_only_in_path, NULL);
575         else
576                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
577                                 BTRFS_LIST_LAYOUT_DEFAULT,
578                                 !is_list_all && !is_only_in_path, NULL);
579
580 out:
581         close_file_or_dir(fd, dirstream);
582         if (filter_set)
583                 btrfs_list_free_filter_set(filter_set);
584         if (comparer_set)
585                 btrfs_list_free_comparer_set(comparer_set);
586         if (uerr)
587                 usage(cmd_subvol_list_usage);
588         return !!ret;
589 }
590
591 static const char * const cmd_subvol_snapshot_usage[] = {
592         "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
593         "Create a snapshot of the subvolume",
594         "Create a writable/readonly snapshot of the subvolume <source> with",
595         "the name <name> in the <dest> directory.  If only <dest> is given,",
596         "the subvolume will be named the basename of <source>.",
597         "",
598         "-r             create a readonly snapshot",
599         "-i <qgroupid>  add the newly created snapshot to a qgroup. This",
600         "               option can be given multiple times.",
601         NULL
602 };
603
604 static int cmd_subvol_snapshot(int argc, char **argv)
605 {
606         char    *subvol, *dst;
607         int     res, retval;
608         int     fd = -1, fddst = -1;
609         int     len, readonly = 0;
610         char    *dupname = NULL;
611         char    *dupdir = NULL;
612         char    *newname;
613         char    *dstdir;
614         struct btrfs_ioctl_vol_args_v2  args;
615         struct btrfs_qgroup_inherit *inherit = NULL;
616         DIR *dirstream1 = NULL, *dirstream2 = NULL;
617
618         memset(&args, 0, sizeof(args));
619         while (1) {
620                 int c = getopt(argc, argv, "c:i:r");
621                 if (c < 0)
622                         break;
623
624                 switch (c) {
625                 case 'c':
626                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
627                         if (res) {
628                                 retval = res;
629                                 goto out;
630                         }
631                         break;
632                 case 'i':
633                         res = qgroup_inherit_add_group(&inherit, optarg);
634                         if (res) {
635                                 retval = res;
636                                 goto out;
637                         }
638                         break;
639                 case 'r':
640                         readonly = 1;
641                         break;
642                 case 'x':
643                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
644                         if (res) {
645                                 retval = res;
646                                 goto out;
647                         }
648                         break;
649                 default:
650                         usage(cmd_subvol_snapshot_usage);
651                 }
652         }
653
654         if (check_argc_exact(argc - optind, 2))
655                 usage(cmd_subvol_snapshot_usage);
656
657         subvol = argv[optind];
658         dst = argv[optind + 1];
659
660         retval = 1;     /* failure */
661         res = test_issubvolume(subvol);
662         if (res < 0) {
663                 error("cannot access subvolume %s: %s", subvol, strerror(-res));
664                 goto out;
665         }
666         if (!res) {
667                 error("not a subvolume: %s", subvol);
668                 goto out;
669         }
670
671         res = test_isdir(dst);
672         if (res < 0 && res != -ENOENT) {
673                 error("cannot access %s: %s", dst, strerror(-res));
674                 goto out;
675         }
676         if (res == 0) {
677                 error("'%s' exists and it is not a directory", dst);
678                 goto out;
679         }
680
681         if (res > 0) {
682                 dupname = strdup(subvol);
683                 newname = basename(dupname);
684                 dstdir = dst;
685         } else {
686                 dupname = strdup(dst);
687                 newname = basename(dupname);
688                 dupdir = strdup(dst);
689                 dstdir = dirname(dupdir);
690         }
691
692         if (!test_issubvolname(newname)) {
693                 error("invalid snapshot name '%s'", newname);
694                 goto out;
695         }
696
697         len = strlen(newname);
698         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
699                 error("snapshot name too long '%s'", newname);
700                 goto out;
701         }
702
703         fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
704         if (fddst < 0)
705                 goto out;
706
707         fd = btrfs_open_dir(subvol, &dirstream2, 1);
708         if (fd < 0)
709                 goto out;
710
711         if (readonly) {
712                 args.flags |= BTRFS_SUBVOL_RDONLY;
713                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
714                        subvol, dstdir, newname);
715         } else {
716                 printf("Create a snapshot of '%s' in '%s/%s'\n",
717                        subvol, dstdir, newname);
718         }
719
720         args.fd = fd;
721         if (inherit) {
722                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
723                 args.size = qgroup_inherit_size(inherit);
724                 args.qgroup_inherit = inherit;
725         }
726         strncpy_null(args.name, newname);
727
728         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
729
730         if (res < 0) {
731                 error("cannot snapshot '%s': %s", subvol, strerror(errno));
732                 goto out;
733         }
734
735         retval = 0;     /* success */
736
737 out:
738         close_file_or_dir(fddst, dirstream1);
739         close_file_or_dir(fd, dirstream2);
740         free(inherit);
741         free(dupname);
742         free(dupdir);
743
744         return retval;
745 }
746
747 static const char * const cmd_subvol_get_default_usage[] = {
748         "btrfs subvolume get-default <path>",
749         "Get the default subvolume of a filesystem",
750         NULL
751 };
752
753 static int cmd_subvol_get_default(int argc, char **argv)
754 {
755         int fd = -1;
756         int ret;
757         char *subvol;
758         struct btrfs_list_filter_set *filter_set;
759         u64 default_id;
760         DIR *dirstream = NULL;
761
762         clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
763
764         if (check_argc_exact(argc - optind, 1))
765                 usage(cmd_subvol_get_default_usage);
766
767         subvol = argv[1];
768         fd = btrfs_open_dir(subvol, &dirstream, 1);
769         if (fd < 0)
770                 return 1;
771
772         ret = btrfs_list_get_default_subvolume(fd, &default_id);
773         if (ret) {
774                 error("failed to look up default subvolume: %s",
775                         strerror(errno));
776                 goto out;
777         }
778
779         ret = 1;
780         if (default_id == 0) {
781                 error("'default' dir item not found");
782                 goto out;
783         }
784
785         /* no need to resolve roots if FS_TREE is default */
786         if (default_id == BTRFS_FS_TREE_OBJECTID) {
787                 printf("ID 5 (FS_TREE)\n");
788                 ret = 0;
789                 goto out;
790         }
791
792         filter_set = btrfs_list_alloc_filter_set();
793         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
794                                 default_id);
795
796         /* by default we shall print the following columns*/
797         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
798         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
799         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
800         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
801
802         ret = btrfs_list_subvols_print(fd, filter_set, NULL,
803                 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
804
805         if (filter_set)
806                 btrfs_list_free_filter_set(filter_set);
807 out:
808         close_file_or_dir(fd, dirstream);
809         return !!ret;
810 }
811
812 static const char * const cmd_subvol_set_default_usage[] = {
813         "btrfs subvolume set-default <subvolid> <path>",
814         "Set the default subvolume of a filesystem",
815         NULL
816 };
817
818 static int cmd_subvol_set_default(int argc, char **argv)
819 {
820         int     ret=0, fd, e;
821         u64     objectid;
822         char    *path;
823         char    *subvolid;
824         DIR     *dirstream = NULL;
825
826         clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
827
828         if (check_argc_exact(argc - optind, 2))
829                 usage(cmd_subvol_set_default_usage);
830
831         subvolid = argv[optind];
832         path = argv[optind + 1];
833
834         objectid = arg_strtou64(subvolid);
835
836         fd = btrfs_open_dir(path, &dirstream, 1);
837         if (fd < 0)
838                 return 1;
839
840         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
841         e = errno;
842         close_file_or_dir(fd, dirstream);
843         if (ret < 0) {
844                 error("unable to set a new default subvolume: %s",
845                         strerror(e));
846                 return 1;
847         }
848         return 0;
849 }
850
851 static const char * const cmd_subvol_find_new_usage[] = {
852         "btrfs subvolume find-new <path> <lastgen>",
853         "List the recently modified files in a filesystem",
854         NULL
855 };
856
857 static int cmd_subvol_find_new(int argc, char **argv)
858 {
859         int fd;
860         int ret;
861         char *subvol;
862         u64 last_gen;
863         DIR *dirstream = NULL;
864
865         clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
866
867         if (check_argc_exact(argc - optind, 2))
868                 usage(cmd_subvol_find_new_usage);
869
870         subvol = argv[optind];
871         last_gen = arg_strtou64(argv[optind + 1]);
872
873         ret = test_issubvolume(subvol);
874         if (ret < 0) {
875                 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
876                 return 1;
877         }
878         if (!ret) {
879                 error("not a subvolume: %s", subvol);
880                 return 1;
881         }
882
883         fd = btrfs_open_dir(subvol, &dirstream, 1);
884         if (fd < 0)
885                 return 1;
886
887         ret = ioctl(fd, BTRFS_IOC_SYNC);
888         if (ret < 0) {
889                 error("sync ioctl failed on '%s': %s",
890                         subvol, strerror(errno));
891                 close_file_or_dir(fd, dirstream);
892                 return 1;
893         }
894
895         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
896         close_file_or_dir(fd, dirstream);
897         return !!ret;
898 }
899
900 static const char * const cmd_subvol_show_usage[] = {
901         "btrfs subvolume show <subvol-path>",
902         "Show more information of the subvolume",
903         NULL
904 };
905
906 static int cmd_subvol_show(int argc, char **argv)
907 {
908         struct root_info get_ri;
909         struct btrfs_list_filter_set *filter_set = NULL;
910         char tstr[256];
911         char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
912         char *fullpath = NULL;
913         char raw_prefix[] = "\t\t\t\t";
914         int fd = -1;
915         int ret = 1;
916         DIR *dirstream1 = NULL;
917
918         clean_args_no_options(argc, argv, cmd_subvol_show_usage);
919
920         if (check_argc_exact(argc - optind, 1))
921                 usage(cmd_subvol_show_usage);
922
923         memset(&get_ri, 0, sizeof(get_ri));
924         fullpath = realpath(argv[optind], NULL);
925         if (!fullpath) {
926                 error("cannot find real path for '%s': %s",
927                         argv[optind], strerror(errno));
928                 goto out;
929         }
930
931         ret = get_subvol_info(fullpath, &get_ri);
932         if (ret == 2) {
933                 /*
934                  * Since the top level btrfs was given don't
935                  * take that as error
936                  */
937                 printf("%s is toplevel subvolume\n", fullpath);
938                 ret = 0;
939                 goto out;
940         }
941         if (ret) {
942                 if (ret < 0) {
943                         error("Failed to get subvol info %s: %s\n",
944                                         fullpath, strerror(-ret));
945                 } else {
946                         error("Failed to get subvol info %s: %d\n",
947                                         fullpath, ret);
948                 }
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 (btrfs_search_header_type(sh)
1146                             == BTRFS_ORPHAN_ITEM_KEY) {
1147                                 if (idx >= count) {
1148                                         u64 *newids;
1149
1150                                         count += SUBVOL_ID_BATCH;
1151                                         newids = (u64*)realloc(*ids,
1152                                                         count * sizeof(u64));
1153                                         if (!newids)
1154                                                 return -ENOMEM;
1155                                         *ids = newids;
1156                                 }
1157                                 (*ids)[idx] = btrfs_search_header_offset(sh);
1158                                 idx++;
1159                         }
1160                         off += btrfs_search_header_len(sh);
1161
1162                         sk->min_objectid = btrfs_search_header_objectid(sh);
1163                         sk->min_type = btrfs_search_header_type(sh);
1164                         sk->min_offset = btrfs_search_header_offset(sh);
1165                 }
1166                 if (sk->min_offset < (u64)-1)
1167                         sk->min_offset++;
1168                 else
1169                         break;
1170                 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1171                         break;
1172                 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1173                         break;
1174         }
1175
1176         return idx;
1177 }
1178
1179 static int cmd_subvol_sync(int argc, char **argv)
1180 {
1181         int fd = -1;
1182         int i;
1183         int ret = 1;
1184         DIR *dirstream = NULL;
1185         u64 *ids = NULL;
1186         int id_count;
1187         int sleep_interval = 1;
1188
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(optarg);
1198                         if (sleep_interval < 1) {
1199                                 error("invalid sleep interval %s", optarg);
1200                                 ret = 1;
1201                                 goto out;
1202                         }
1203                         break;
1204                 default:
1205                         usage(cmd_subvol_sync_usage);
1206                 }
1207         }
1208
1209         if (check_argc_min(argc - optind, 1))
1210                 usage(cmd_subvol_sync_usage);
1211
1212         fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1213         if (fd < 0) {
1214                 ret = 1;
1215                 goto out;
1216         }
1217         optind++;
1218
1219         id_count = argc - optind;
1220         if (!id_count) {
1221                 id_count = enumerate_dead_subvols(fd, &ids);
1222                 if (id_count < 0) {
1223                         error("can't enumerate dead subvolumes: %s",
1224                                         strerror(-id_count));
1225                         ret = 1;
1226                         goto out;
1227                 }
1228                 if (id_count == 0) {
1229                         ret = 0;
1230                         goto out;
1231                 }
1232         } else {
1233                 ids = (u64*)malloc(id_count * sizeof(u64));
1234                 if (!ids) {
1235                         error("not enough memory");
1236                         ret = 1;
1237                         goto out;
1238                 }
1239
1240                 for (i = 0; i < id_count; i++) {
1241                         u64 id;
1242                         const char *arg;
1243
1244                         arg = argv[optind + i];
1245                         errno = 0;
1246                         id = strtoull(arg, NULL, 10);
1247                         if (errno < 0) {
1248                                 error("unrecognized subvolume id %s", arg);
1249                                 ret = 1;
1250                                 goto out;
1251                         }
1252                         if (id < BTRFS_FIRST_FREE_OBJECTID
1253                                         || id > BTRFS_LAST_FREE_OBJECTID) {
1254                                 error("subvolume id %s out of range\n", arg);
1255                                 ret = 1;
1256                                 goto out;
1257                         }
1258                         ids[i] = id;
1259                 }
1260         }
1261
1262         ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1263
1264 out:
1265         free(ids);
1266         close_file_or_dir(fd, dirstream);
1267
1268         return !!ret;
1269 }
1270
1271 static const char subvolume_cmd_group_info[] =
1272 "manage subvolumes: create, delete, list, etc";
1273
1274 const struct cmd_group subvolume_cmd_group = {
1275         subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1276                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1277                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1278                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1279                 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1280                         NULL, 0 },
1281                 { "get-default", cmd_subvol_get_default,
1282                         cmd_subvol_get_default_usage, NULL, 0 },
1283                 { "set-default", cmd_subvol_set_default,
1284                         cmd_subvol_set_default_usage, NULL, 0 },
1285                 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1286                         NULL, 0 },
1287                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1288                 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1289                 NULL_CMD_STRUCT
1290         }
1291 };
1292
1293 int cmd_subvolume(int argc, char **argv)
1294 {
1295         return handle_command_group(&subvolume_cmd_group, argc, argv);
1296 }