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