btrfs-progs: btrfs: Add missing btrfs_close_all_devices for btrfs command
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blobbe1a54a4f3d025cb932d1e63c783f62c6c5370c3
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.
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.
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.
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>
29 #include "kerncompat.h"
30 #include "ioctl.h"
31 #include "qgroup.h"
33 #include "ctree.h"
34 #include "commands.h"
35 #include "utils.h"
36 #include "btrfs-list.h"
37 #include "utils.h"
39 static int is_subvolume_cleaned(int fd, u64 subvolid)
41 int ret;
42 struct btrfs_ioctl_search_args args;
43 struct btrfs_ioctl_search_key *sk = &args.key;
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;
56 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
57 if (ret < 0)
58 return -errno;
60 if (sk->nr_items == 0)
61 return 1;
63 return 0;
66 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
67 int sleep_interval)
69 int ret;
70 int i;
72 while (1) {
73 int clean = 1;
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 fprintf(stderr,
81 "ERROR: can't perform the search - %s\n",
82 strerror(-ret));
83 return ret;
85 if (ret) {
86 printf("Subvolume id %llu is gone\n", ids[i]);
87 ids[i] = 0;
88 } else {
89 clean = 0;
92 if (clean)
93 break;
94 sleep(sleep_interval);
97 return 0;
100 static const char * const subvolume_cmd_group_usage[] = {
101 "btrfs subvolume <command> <args>",
102 NULL
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.",
111 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
112 " option can be given multiple times.",
113 NULL
116 static int cmd_subvol_create(int argc, char **argv)
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;
128 optind = 1;
129 while (1) {
130 int c = getopt(argc, argv, "c:i:v");
131 if (c < 0)
132 break;
134 switch (c) {
135 case 'c':
136 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
137 if (res) {
138 retval = res;
139 goto out;
141 break;
142 case 'i':
143 res = qgroup_inherit_add_group(&inherit, optarg);
144 if (res) {
145 retval = res;
146 goto out;
148 break;
149 default:
150 usage(cmd_subvol_create_usage);
154 if (check_argc_exact(argc - optind, 1))
155 usage(cmd_subvol_create_usage);
157 dst = argv[optind];
159 retval = 1; /* failure */
160 res = test_isdir(dst);
161 if (res >= 0) {
162 fprintf(stderr, "ERROR: '%s' exists\n", dst);
163 goto out;
166 dupname = strdup(dst);
167 newname = basename(dupname);
168 dupdir = strdup(dst);
169 dstdir = dirname(dupdir);
171 if (!test_issubvolname(newname)) {
172 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
173 newname);
174 goto out;
177 len = strlen(newname);
178 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
179 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
180 newname);
181 goto out;
184 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
185 if (fddst < 0)
186 goto out;
188 printf("Create subvolume '%s/%s'\n", dstdir, newname);
189 if (inherit) {
190 struct btrfs_ioctl_vol_args_v2 args;
192 memset(&args, 0, sizeof(args));
193 strncpy_null(args.name, newname);
194 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
195 args.size = qgroup_inherit_size(inherit);
196 args.qgroup_inherit = inherit;
198 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
199 } else {
200 struct btrfs_ioctl_vol_args args;
202 memset(&args, 0, sizeof(args));
203 strncpy_null(args.name, newname);
205 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
208 if (res < 0) {
209 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
210 strerror(errno));
211 goto out;
214 retval = 0; /* success */
215 out:
216 close_file_or_dir(fddst, dirstream);
217 free(inherit);
218 free(dupname);
219 free(dupdir);
221 return retval;
225 * test if path is a subvolume:
226 * this function return
227 * 0-> path exists but it is not a subvolume
228 * 1-> path exists and it is a subvolume
229 * -1 -> path is unaccessible
231 int test_issubvolume(char *path)
233 struct stat st;
234 int res;
236 res = stat(path, &st);
237 if(res < 0 )
238 return -1;
240 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
243 static int wait_for_commit(int fd)
245 int ret;
247 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
248 if (ret < 0)
249 return ret;
250 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
253 static const char * const cmd_subvol_delete_usage[] = {
254 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
255 "Delete subvolume(s)",
256 "Delete subvolumes from the filesystem. The corresponding directory",
257 "is removed instantly but the data blocks are removed later.",
258 "The deletion does not involve full commit by default due to",
259 "performance reasons (as a consequence, the subvolume may appear again",
260 "after a crash). Use one of the --commit options to wait until the",
261 "operation is safely stored on the media.",
263 "-c|--commit-after wait for transaction commit at the end of the operation",
264 "-C|--commit-each wait for transaction commit after deleting each subvolume",
265 NULL
268 static int cmd_subvol_delete(int argc, char **argv)
270 int res, e, ret = 0;
271 int cnt;
272 int fd = -1;
273 struct btrfs_ioctl_vol_args args;
274 char *dname, *vname, *cpath;
275 char *dupdname = NULL;
276 char *dupvname = NULL;
277 char *path;
278 DIR *dirstream = NULL;
279 int verbose = 0;
280 int commit_mode = 0;
282 optind = 1;
283 while (1) {
284 int c;
285 static const struct option long_options[] = {
286 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
287 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
288 {NULL, 0, NULL, 0}
291 c = getopt_long(argc, argv, "cC", long_options, NULL);
292 if (c < 0)
293 break;
295 switch(c) {
296 case 'c':
297 commit_mode = 1;
298 break;
299 case 'C':
300 commit_mode = 2;
301 break;
302 case 'v':
303 verbose++;
304 break;
305 default:
306 usage(cmd_subvol_delete_usage);
310 if (check_argc_min(argc - optind, 1))
311 usage(cmd_subvol_delete_usage);
313 if (verbose > 0) {
314 printf("Transaction commit: %s\n",
315 !commit_mode ? "none (default)" :
316 commit_mode == 1 ? "at the end" : "after each");
319 cnt = optind;
321 again:
322 path = argv[cnt];
324 res = test_issubvolume(path);
325 if (res < 0) {
326 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
327 ret = 1;
328 goto out;
330 if (!res) {
331 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
332 ret = 1;
333 goto out;
336 cpath = realpath(path, NULL);
337 if (!cpath) {
338 ret = errno;
339 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
340 path, strerror(errno));
341 goto out;
343 dupdname = strdup(cpath);
344 dname = dirname(dupdname);
345 dupvname = strdup(cpath);
346 vname = basename(dupvname);
347 free(cpath);
349 fd = btrfs_open_dir(dname, &dirstream, 1);
350 if (fd < 0) {
351 ret = 1;
352 goto out;
355 printf("Delete subvolume (%s): '%s/%s'\n",
356 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
357 ? "commit" : "no-commit", dname, vname);
358 memset(&args, 0, sizeof(args));
359 strncpy_null(args.name, vname);
360 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
361 e = errno;
363 if(res < 0 ){
364 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
365 dname, vname, strerror(e));
366 ret = 1;
367 goto out;
370 if (commit_mode == 1) {
371 res = wait_for_commit(fd);
372 if (res < 0) {
373 fprintf(stderr,
374 "ERROR: unable to wait for commit after '%s': %s\n",
375 path, strerror(errno));
376 ret = 1;
380 out:
381 free(dupdname);
382 free(dupvname);
383 dupdname = NULL;
384 dupvname = NULL;
385 cnt++;
386 if (cnt < argc) {
387 close_file_or_dir(fd, dirstream);
388 /* avoid double free */
389 fd = -1;
390 dirstream = NULL;
391 goto again;
394 if (commit_mode == 2 && fd != -1) {
395 res = wait_for_commit(fd);
396 if (res < 0) {
397 fprintf(stderr,
398 "ERROR: unable to do final sync: %s\n",
399 strerror(errno));
400 ret = 1;
403 close_file_or_dir(fd, dirstream);
405 return ret;
409 * Naming of options:
410 * - uppercase for filters and sort options
411 * - lowercase for enabling specific items in the output
413 static const char * const cmd_subvol_list_usage[] = {
414 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
415 "[--sort=gen,ogen,rootid,path] <path>",
416 "List subvolumes (and snapshots)",
418 "-p print parent ID",
419 "-a print all the subvolumes in the filesystem and",
420 " distinguish absolute and relative path with respect",
421 " to the given <path>",
422 "-c print the ogeneration of the subvolume",
423 "-g print the generation of the subvolume",
424 "-o print only subvolumes below specified path",
425 "-u print the uuid of subvolumes (and snapshots)",
426 "-q print the parent uuid of the snapshots",
427 "-R print the uuid of the received snapshots",
428 "-t print the result as a table",
429 "-s list snapshots only in the filesystem",
430 "-r list readonly subvolumes (including snapshots)",
431 "-d list deleted subvolumes that are not yet cleaned",
432 "-G [+|-]value",
433 " filter the subvolumes by generation",
434 " (+value: >= value; -value: <= value; value: = value)",
435 "-C [+|-]value",
436 " filter the subvolumes by ogeneration",
437 " (+value: >= value; -value: <= value; value: = value)",
438 "--sort=gen,ogen,rootid,path",
439 " list the subvolume in order of gen, ogen, rootid or path",
440 " you also can add '+' or '-' in front of each items.",
441 " (+:ascending, -:descending, ascending default)",
442 NULL,
445 static int cmd_subvol_list(int argc, char **argv)
447 struct btrfs_list_filter_set *filter_set;
448 struct btrfs_list_comparer_set *comparer_set;
449 u64 flags = 0;
450 int fd = -1;
451 u64 top_id;
452 int ret = -1, uerr = 0;
453 char *subvol;
454 int is_tab_result = 0;
455 int is_list_all = 0;
456 int is_only_in_path = 0;
457 DIR *dirstream = NULL;
459 filter_set = btrfs_list_alloc_filter_set();
460 comparer_set = btrfs_list_alloc_comparer_set();
462 optind = 1;
463 while(1) {
464 int c;
465 static const struct option long_options[] = {
466 {"sort", required_argument, NULL, 'S'},
467 {NULL, 0, NULL, 0}
470 c = getopt_long(argc, argv,
471 "acdgopqsurRG:C:t", long_options, NULL);
472 if (c < 0)
473 break;
475 switch(c) {
476 case 'p':
477 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
478 break;
479 case 'a':
480 is_list_all = 1;
481 break;
482 case 'c':
483 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
484 break;
485 case 'd':
486 btrfs_list_setup_filter(&filter_set,
487 BTRFS_LIST_FILTER_DELETED,
489 break;
490 case 'g':
491 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
492 break;
493 case 'o':
494 is_only_in_path = 1;
495 break;
496 case 't':
497 is_tab_result = 1;
498 break;
499 case 's':
500 btrfs_list_setup_filter(&filter_set,
501 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
503 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
504 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
505 break;
506 case 'u':
507 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
508 break;
509 case 'q':
510 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
511 break;
512 case 'R':
513 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
514 break;
515 case 'r':
516 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
517 break;
518 case 'G':
519 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
520 ret = btrfs_list_parse_filter_string(optarg,
521 &filter_set,
522 BTRFS_LIST_FILTER_GEN);
523 if (ret) {
524 uerr = 1;
525 goto out;
527 break;
529 case 'C':
530 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
531 ret = btrfs_list_parse_filter_string(optarg,
532 &filter_set,
533 BTRFS_LIST_FILTER_CGEN);
534 if (ret) {
535 uerr = 1;
536 goto out;
538 break;
539 case 'S':
540 ret = btrfs_list_parse_sort_string(optarg,
541 &comparer_set);
542 if (ret) {
543 uerr = 1;
544 goto out;
546 break;
548 default:
549 uerr = 1;
550 goto out;
554 if (flags)
555 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
556 flags);
558 if (check_argc_exact(argc - optind, 1)) {
559 uerr = 1;
560 goto out;
563 subvol = argv[optind];
564 fd = btrfs_open_dir(subvol, &dirstream, 1);
565 if (fd < 0) {
566 ret = -1;
567 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
568 goto out;
571 ret = btrfs_list_get_path_rootid(fd, &top_id);
572 if (ret) {
573 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
574 goto out;
577 if (is_list_all)
578 btrfs_list_setup_filter(&filter_set,
579 BTRFS_LIST_FILTER_FULL_PATH,
580 top_id);
581 else if (is_only_in_path)
582 btrfs_list_setup_filter(&filter_set,
583 BTRFS_LIST_FILTER_TOPID_EQUAL,
584 top_id);
586 /* by default we shall print the following columns*/
587 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
588 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
589 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
590 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
592 if (is_tab_result)
593 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
594 BTRFS_LIST_LAYOUT_TABLE,
595 !is_list_all && !is_only_in_path, NULL);
596 else
597 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
598 BTRFS_LIST_LAYOUT_DEFAULT,
599 !is_list_all && !is_only_in_path, NULL);
601 out:
602 close_file_or_dir(fd, dirstream);
603 if (filter_set)
604 btrfs_list_free_filter_set(filter_set);
605 if (comparer_set)
606 btrfs_list_free_comparer_set(comparer_set);
607 if (uerr)
608 usage(cmd_subvol_list_usage);
609 return !!ret;
612 static const char * const cmd_subvol_snapshot_usage[] = {
613 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
614 "Create a snapshot of the subvolume",
615 "Create a writable/readonly snapshot of the subvolume <source> with",
616 "the name <name> in the <dest> directory. If only <dest> is given,",
617 "the subvolume will be named the basename of <source>.",
619 "-r create a readonly snapshot",
620 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
621 " option can be given multiple times.",
622 NULL
625 static int cmd_subvol_snapshot(int argc, char **argv)
627 char *subvol, *dst;
628 int res, retval;
629 int fd = -1, fddst = -1;
630 int len, readonly = 0;
631 char *dupname = NULL;
632 char *dupdir = NULL;
633 char *newname;
634 char *dstdir;
635 struct btrfs_ioctl_vol_args_v2 args;
636 struct btrfs_qgroup_inherit *inherit = NULL;
637 DIR *dirstream1 = NULL, *dirstream2 = NULL;
639 optind = 1;
640 memset(&args, 0, sizeof(args));
641 while (1) {
642 int c = getopt(argc, argv, "c:i:r");
643 if (c < 0)
644 break;
646 switch (c) {
647 case 'c':
648 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
649 if (res) {
650 retval = res;
651 goto out;
653 break;
654 case 'i':
655 res = qgroup_inherit_add_group(&inherit, optarg);
656 if (res) {
657 retval = res;
658 goto out;
660 break;
661 case 'r':
662 readonly = 1;
663 break;
664 case 'x':
665 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
666 if (res) {
667 retval = res;
668 goto out;
670 break;
671 default:
672 usage(cmd_subvol_snapshot_usage);
676 if (check_argc_exact(argc - optind, 2))
677 usage(cmd_subvol_snapshot_usage);
679 subvol = argv[optind];
680 dst = argv[optind + 1];
682 retval = 1; /* failure */
683 res = test_issubvolume(subvol);
684 if (res < 0) {
685 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
686 goto out;
688 if (!res) {
689 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
690 goto out;
693 res = test_isdir(dst);
694 if (res == 0) {
695 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
696 goto out;
699 if (res > 0) {
700 dupname = strdup(subvol);
701 newname = basename(dupname);
702 dstdir = dst;
703 } else {
704 dupname = strdup(dst);
705 newname = basename(dupname);
706 dupdir = strdup(dst);
707 dstdir = dirname(dupdir);
710 if (!test_issubvolname(newname)) {
711 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
712 newname);
713 goto out;
716 len = strlen(newname);
717 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
718 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
719 newname);
720 goto out;
723 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
724 if (fddst < 0)
725 goto out;
727 fd = btrfs_open_dir(subvol, &dirstream2, 1);
728 if (fd < 0)
729 goto out;
731 if (readonly) {
732 args.flags |= BTRFS_SUBVOL_RDONLY;
733 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
734 subvol, dstdir, newname);
735 } else {
736 printf("Create a snapshot of '%s' in '%s/%s'\n",
737 subvol, dstdir, newname);
740 args.fd = fd;
741 if (inherit) {
742 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
743 args.size = qgroup_inherit_size(inherit);
744 args.qgroup_inherit = inherit;
746 strncpy_null(args.name, newname);
748 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
750 if (res < 0) {
751 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
752 subvol, strerror(errno));
753 goto out;
756 retval = 0; /* success */
758 out:
759 close_file_or_dir(fddst, dirstream1);
760 close_file_or_dir(fd, dirstream2);
761 free(inherit);
762 free(dupname);
763 free(dupdir);
765 return retval;
768 static const char * const cmd_subvol_get_default_usage[] = {
769 "btrfs subvolume get-default <path>",
770 "Get the default subvolume of a filesystem",
771 NULL
774 static int cmd_subvol_get_default(int argc, char **argv)
776 int fd = -1;
777 int ret;
778 char *subvol;
779 struct btrfs_list_filter_set *filter_set;
780 u64 default_id;
781 DIR *dirstream = NULL;
783 if (check_argc_exact(argc, 2))
784 usage(cmd_subvol_get_default_usage);
786 subvol = argv[1];
787 fd = btrfs_open_dir(subvol, &dirstream, 1);
788 if (fd < 0)
789 return 1;
791 ret = btrfs_list_get_default_subvolume(fd, &default_id);
792 if (ret) {
793 fprintf(stderr, "ERROR: can't perform the search - %s\n",
794 strerror(errno));
795 goto out;
798 ret = 1;
799 if (default_id == 0) {
800 fprintf(stderr, "ERROR: 'default' dir item not found\n");
801 goto out;
804 /* no need to resolve roots if FS_TREE is default */
805 if (default_id == BTRFS_FS_TREE_OBJECTID) {
806 printf("ID 5 (FS_TREE)\n");
807 ret = 0;
808 goto out;
811 filter_set = btrfs_list_alloc_filter_set();
812 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
813 default_id);
815 /* by default we shall print the following columns*/
816 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
817 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
818 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
819 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
821 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
822 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
824 if (filter_set)
825 btrfs_list_free_filter_set(filter_set);
826 out:
827 close_file_or_dir(fd, dirstream);
828 return !!ret;
831 static const char * const cmd_subvol_set_default_usage[] = {
832 "btrfs subvolume set-default <subvolid> <path>",
833 "Set the default subvolume of a filesystem",
834 NULL
837 static int cmd_subvol_set_default(int argc, char **argv)
839 int ret=0, fd, e;
840 u64 objectid;
841 char *path;
842 char *subvolid;
843 DIR *dirstream = NULL;
845 if (check_argc_exact(argc, 3))
846 usage(cmd_subvol_set_default_usage);
848 subvolid = argv[1];
849 path = argv[2];
851 objectid = arg_strtou64(subvolid);
853 fd = btrfs_open_dir(path, &dirstream, 1);
854 if (fd < 0)
855 return 1;
857 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
858 e = errno;
859 close_file_or_dir(fd, dirstream);
860 if (ret < 0) {
861 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
862 strerror(e));
863 return 1;
865 return 0;
868 static const char * const cmd_subvol_find_new_usage[] = {
869 "btrfs subvolume find-new <path> <lastgen>",
870 "List the recently modified files in a filesystem",
871 NULL
874 static int cmd_subvol_find_new(int argc, char **argv)
876 int fd;
877 int ret;
878 char *subvol;
879 u64 last_gen;
880 DIR *dirstream = NULL;
882 if (check_argc_exact(argc, 3))
883 usage(cmd_subvol_find_new_usage);
885 subvol = argv[1];
886 last_gen = arg_strtou64(argv[2]);
888 ret = test_issubvolume(subvol);
889 if (ret < 0) {
890 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
891 return 1;
893 if (!ret) {
894 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
895 return 1;
898 fd = btrfs_open_dir(subvol, &dirstream, 1);
899 if (fd < 0)
900 return 1;
902 ret = ioctl(fd, BTRFS_IOC_SYNC);
903 if (ret < 0) {
904 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
905 subvol, strerror(errno));
906 close_file_or_dir(fd, dirstream);
907 return 1;
910 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
911 close_file_or_dir(fd, dirstream);
912 return !!ret;
915 static const char * const cmd_subvol_show_usage[] = {
916 "btrfs subvolume show <subvol-path>",
917 "Show more information of the subvolume",
918 NULL
921 static int cmd_subvol_show(int argc, char **argv)
923 struct root_info get_ri;
924 struct btrfs_list_filter_set *filter_set;
925 char tstr[256];
926 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
927 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
928 char raw_prefix[] = "\t\t\t\t";
929 u64 sv_id;
930 int fd = -1, mntfd = -1;
931 int ret = 1;
932 DIR *dirstream1 = NULL, *dirstream2 = NULL;
934 if (check_argc_exact(argc, 2))
935 usage(cmd_subvol_show_usage);
937 fullpath = realpath(argv[1], NULL);
938 if (!fullpath) {
939 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
940 argv[1], strerror(errno));
941 goto out;
944 ret = test_issubvolume(fullpath);
945 if (ret < 0) {
946 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
947 goto out;
949 if (!ret) {
950 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
951 ret = 1;
952 goto out;
955 ret = find_mount_root(fullpath, &mnt);
956 if (ret < 0) {
957 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
958 "%s\n", fullpath, strerror(-ret));
959 goto out;
961 if (ret > 0) {
962 fprintf(stderr,
963 "ERROR: %s doesn't belong to btrfs mount point\n",
964 fullpath);
965 goto out;
967 ret = 1;
968 svpath = get_subvol_name(mnt, fullpath);
970 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
971 if (fd < 0)
972 goto out;
974 ret = btrfs_list_get_path_rootid(fd, &sv_id);
975 if (ret) {
976 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
977 fullpath);
978 goto out;
981 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
982 if (mntfd < 0)
983 goto out;
985 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
986 printf("%s is btrfs root\n", fullpath);
987 goto out;
990 memset(&get_ri, 0, sizeof(get_ri));
991 get_ri.root_id = sv_id;
993 ret = btrfs_get_subvol(mntfd, &get_ri);
994 if (ret) {
995 fprintf(stderr, "ERROR: can't find '%s'\n",
996 svpath);
997 goto out;
1000 /* print the info */
1001 printf("%s\n", fullpath);
1002 printf("\tName: \t\t\t%s\n", get_ri.name);
1004 if (uuid_is_null(get_ri.uuid))
1005 strcpy(uuidparse, "-");
1006 else
1007 uuid_unparse(get_ri.uuid, uuidparse);
1008 printf("\tUUID: \t\t\t%s\n", uuidparse);
1010 if (uuid_is_null(get_ri.puuid))
1011 strcpy(uuidparse, "-");
1012 else
1013 uuid_unparse(get_ri.puuid, uuidparse);
1014 printf("\tParent UUID: \t\t%s\n", uuidparse);
1016 if (uuid_is_null(get_ri.ruuid))
1017 strcpy(uuidparse, "-");
1018 else
1019 uuid_unparse(get_ri.ruuid, uuidparse);
1020 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1022 if (get_ri.otime) {
1023 struct tm tm;
1025 localtime_r(&get_ri.otime, &tm);
1026 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1027 } else
1028 strcpy(tstr, "-");
1029 printf("\tCreation time: \t\t%s\n", tstr);
1031 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1032 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1033 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1034 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1035 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1037 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1038 printf("\tFlags: \t\t\treadonly\n");
1039 else
1040 printf("\tFlags: \t\t\t-\n");
1042 /* print the snapshots of the given subvol if any*/
1043 printf("\tSnapshot(s):\n");
1044 filter_set = btrfs_list_alloc_filter_set();
1045 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1046 (u64)(unsigned long)get_ri.uuid);
1047 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1048 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1049 1, raw_prefix);
1051 /* clean up */
1052 free(get_ri.path);
1053 free(get_ri.name);
1054 free(get_ri.full_path);
1055 btrfs_list_free_filter_set(filter_set);
1057 out:
1058 close_file_or_dir(fd, dirstream1);
1059 close_file_or_dir(mntfd, dirstream2);
1060 free(mnt);
1061 free(fullpath);
1062 return !!ret;
1065 static const char * const cmd_subvol_sync_usage[] = {
1066 "btrfs subvolume sync <path> [<subvol-id>...]",
1067 "Wait until given subvolume(s) are completely removed from the filesystem.",
1068 "Wait until given subvolume(s) are completely removed from the filesystem",
1069 "after deletion.",
1070 "If no subvolume id is given, wait until all current deletion requests",
1071 "are completed, but do not wait for subvolumes deleted meanwhile.",
1072 "The status of subvolume ids is checked periodically.",
1074 "-s <N> sleep N seconds between checks (default: 1)",
1075 NULL
1078 #if 0
1080 * If we're looking for any dead subvolume, take a shortcut and look
1081 * for any ORPHAN_ITEMs in the tree root
1083 static int fs_has_dead_subvolumes(int fd)
1085 int ret;
1086 struct btrfs_ioctl_search_args args;
1087 struct btrfs_ioctl_search_key *sk = &args.key;
1088 struct btrfs_ioctl_search_header sh;
1089 u64 min_subvolid = 0;
1091 again:
1092 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1093 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1094 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1095 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1096 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1097 sk->min_offset = min_subvolid;
1098 sk->max_offset = (u64)-1;
1099 sk->min_transid = 0;
1100 sk->max_transid = (u64)-1;
1101 sk->nr_items = 1;
1103 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1104 if (ret < 0)
1105 return -errno;
1107 if (!sk->nr_items)
1108 return 0;
1110 memcpy(&sh, args.buf, sizeof(sh));
1111 min_subvolid = sh.offset;
1114 * Verify that the root item is really there and we haven't hit
1115 * a stale orphan
1117 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1118 sk->min_objectid = min_subvolid;
1119 sk->max_objectid = min_subvolid;
1120 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1121 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1122 sk->min_offset = 0;
1123 sk->max_offset = (u64)-1;
1124 sk->min_transid = 0;
1125 sk->max_transid = (u64)-1;
1126 sk->nr_items = 1;
1128 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1129 if (ret < 0)
1130 return -errno;
1133 * Stale orphan, try the next one
1135 if (!sk->nr_items) {
1136 min_subvolid++;
1137 goto again;
1140 return 1;
1142 #endif
1144 #define SUBVOL_ID_BATCH 1024
1147 * Enumerate all dead subvolumes that exist in the filesystem.
1148 * Fill @ids and reallocate to bigger size if needed.
1150 static int enumerate_dead_subvols(int fd, u64 **ids)
1152 int ret;
1153 struct btrfs_ioctl_search_args args;
1154 struct btrfs_ioctl_search_key *sk = &args.key;
1155 int idx = 0;
1156 int count = 0;
1158 memset(&args, 0, sizeof(args));
1160 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1161 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1162 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1163 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1164 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1165 sk->min_offset = 0;
1166 sk->max_offset = (u64)-1;
1167 sk->min_transid = 0;
1168 sk->max_transid = (u64)-1;
1169 sk->nr_items = 4096;
1171 *ids = NULL;
1172 while (1) {
1173 struct btrfs_ioctl_search_header *sh;
1174 unsigned long off;
1175 int i;
1177 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1178 if (ret < 0)
1179 return -errno;
1181 if (!sk->nr_items)
1182 return idx;
1184 off = 0;
1185 for (i = 0; i < sk->nr_items; i++) {
1186 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1187 off += sizeof(*sh);
1189 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1190 if (idx >= count) {
1191 u64 *newids;
1193 count += SUBVOL_ID_BATCH;
1194 newids = (u64*)realloc(*ids, count);
1195 if (!newids)
1196 return -ENOMEM;
1197 *ids = newids;
1199 (*ids)[idx] = sh->offset;
1200 idx++;
1202 off += sh->len;
1204 sk->min_objectid = sh->objectid;
1205 sk->min_type = sh->type;
1206 sk->min_offset = sh->offset;
1208 if (sk->min_offset < (u64)-1)
1209 sk->min_offset++;
1210 else
1211 break;
1212 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1213 break;
1214 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1215 break;
1218 return idx;
1221 static int cmd_subvol_sync(int argc, char **argv)
1223 int fd = -1;
1224 int i;
1225 int ret = 1;
1226 DIR *dirstream = NULL;
1227 u64 *ids = NULL;
1228 int id_count;
1229 int sleep_interval = 1;
1231 optind = 1;
1232 while (1) {
1233 int c = getopt(argc, argv, "s:");
1235 if (c < 0)
1236 break;
1238 switch (c) {
1239 case 's':
1240 sleep_interval = atoi(argv[optind]);
1241 if (sleep_interval < 1) {
1242 fprintf(stderr,
1243 "ERROR: invalid sleep interval %s\n",
1244 argv[optind]);
1245 ret = 1;
1246 goto out;
1248 break;
1249 default:
1250 usage(cmd_subvol_sync_usage);
1254 if (check_argc_min(argc - optind, 1))
1255 usage(cmd_subvol_sync_usage);
1257 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1258 if (fd < 0) {
1259 ret = 1;
1260 goto out;
1262 optind++;
1264 id_count = argc - optind;
1265 if (!id_count) {
1266 id_count = enumerate_dead_subvols(fd, &ids);
1267 if (id_count < 0) {
1268 fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
1269 strerror(-id_count));
1270 ret = 1;
1271 goto out;
1273 if (id_count == 0) {
1274 ret = 0;
1275 goto out;
1277 } else {
1278 ids = (u64*)malloc(id_count * sizeof(u64));
1279 if (!ids) {
1280 fprintf(stderr, "ERROR: not enough memory\n");
1281 ret = 1;
1282 goto out;
1285 for (i = 0; i < id_count; i++) {
1286 u64 id;
1287 const char *arg;
1289 arg = argv[optind + i];
1290 errno = 0;
1291 id = strtoull(arg, NULL, 10);
1292 if (errno < 0) {
1293 fprintf(stderr,
1294 "ERROR: unrecognized subvolume id %s\n",
1295 arg);
1296 ret = 1;
1297 goto out;
1299 if (id < BTRFS_FIRST_FREE_OBJECTID
1300 || id > BTRFS_LAST_FREE_OBJECTID) {
1301 fprintf(stderr,
1302 "ERROR: subvolume id %s out of range\n",
1303 arg);
1304 ret = 1;
1305 goto out;
1307 ids[i] = id;
1311 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1313 out:
1314 free(ids);
1315 close_file_or_dir(fd, dirstream);
1317 return !!ret;
1320 static const char subvolume_cmd_group_info[] =
1321 "manage subvolumes: create, delete, list, etc";
1323 const struct cmd_group subvolume_cmd_group = {
1324 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1325 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1326 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1327 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1328 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1329 NULL, 0 },
1330 { "get-default", cmd_subvol_get_default,
1331 cmd_subvol_get_default_usage, NULL, 0 },
1332 { "set-default", cmd_subvol_set_default,
1333 cmd_subvol_set_default_usage, NULL, 0 },
1334 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1335 NULL, 0 },
1336 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1337 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1338 NULL_CMD_STRUCT
1342 int cmd_subvolume(int argc, char **argv)
1344 return handle_command_group(&subvolume_cmd_group, argc, argv);