Btrfs-progs: Initialize stripesize to the value of sectorsize
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blobe3d2cbce0b60f56b7057bd1387ea60be83c2a551
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 <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>
31 #include "kerncompat.h"
32 #include "ioctl.h"
33 #include "qgroup.h"
35 #include "ctree.h"
36 #include "commands.h"
37 #include "utils.h"
38 #include "btrfs-list.h"
39 #include "utils.h"
41 static int is_subvolume_cleaned(int fd, u64 subvolid)
43 int ret;
44 struct btrfs_ioctl_search_args args;
45 struct btrfs_ioctl_search_key *sk = &args.key;
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;
58 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
59 if (ret < 0)
60 return -errno;
62 if (sk->nr_items == 0)
63 return 1;
65 return 0;
68 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
69 int sleep_interval)
71 int ret;
72 int i;
74 while (1) {
75 int clean = 1;
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;
87 if (ret) {
88 printf("Subvolume id %llu is gone\n", ids[i]);
89 ids[i] = 0;
90 } else {
91 clean = 0;
94 if (clean)
95 break;
96 sleep(sleep_interval);
99 return 0;
102 static const char * const subvolume_cmd_group_usage[] = {
103 "btrfs subvolume <command> <args>",
104 NULL
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.",
113 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
114 " option can be given multiple times.",
115 NULL
118 static int cmd_subvol_create(int argc, char **argv)
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;
130 optind = 1;
131 while (1) {
132 int c = getopt(argc, argv, "c:i:v");
133 if (c < 0)
134 break;
136 switch (c) {
137 case 'c':
138 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
139 if (res) {
140 retval = res;
141 goto out;
143 break;
144 case 'i':
145 res = qgroup_inherit_add_group(&inherit, optarg);
146 if (res) {
147 retval = res;
148 goto out;
150 break;
151 default:
152 usage(cmd_subvol_create_usage);
156 if (check_argc_exact(argc - optind, 1))
157 usage(cmd_subvol_create_usage);
159 dst = argv[optind];
161 retval = 1; /* failure */
162 res = test_isdir(dst);
163 if (res < 0 && res != -ENOENT) {
164 error("cannot access %s: %s", dst, strerror(-res));
165 goto out;
167 if (res >= 0) {
168 error("target path already exists: %s", dst);
169 goto out;
172 dupname = strdup(dst);
173 newname = basename(dupname);
174 dupdir = strdup(dst);
175 dstdir = dirname(dupdir);
177 if (!test_issubvolname(newname)) {
178 error("invalid subvolume name: %s", newname);
179 goto out;
182 len = strlen(newname);
183 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
184 error("subvolume name too long: %s", newname);
185 goto out;
188 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
189 if (fddst < 0)
190 goto out;
192 printf("Create subvolume '%s/%s'\n", dstdir, newname);
193 if (inherit) {
194 struct btrfs_ioctl_vol_args_v2 args;
196 memset(&args, 0, sizeof(args));
197 strncpy_null(args.name, newname);
198 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
199 args.size = qgroup_inherit_size(inherit);
200 args.qgroup_inherit = inherit;
202 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
203 } else {
204 struct btrfs_ioctl_vol_args args;
206 memset(&args, 0, sizeof(args));
207 strncpy_null(args.name, newname);
209 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
212 if (res < 0) {
213 error("cannot create subvolume: %s", strerror(errno));
214 goto out;
217 retval = 0; /* success */
218 out:
219 close_file_or_dir(fddst, dirstream);
220 free(inherit);
221 free(dupname);
222 free(dupdir);
224 return retval;
227 static int wait_for_commit(int fd)
229 int ret;
231 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
232 if (ret < 0)
233 return ret;
234 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
237 static const char * const cmd_subvol_delete_usage[] = {
238 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
239 "Delete subvolume(s)",
240 "Delete subvolumes from the filesystem. The corresponding directory",
241 "is removed instantly but the data blocks are removed later.",
242 "The deletion does not involve full commit by default due to",
243 "performance reasons (as a consequence, the subvolume may appear again",
244 "after a crash). Use one of the --commit options to wait until the",
245 "operation is safely stored on the media.",
247 "-c|--commit-after wait for transaction commit at the end of the operation",
248 "-C|--commit-each wait for transaction commit after deleting each subvolume",
249 NULL
252 static int cmd_subvol_delete(int argc, char **argv)
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;
266 optind = 1;
267 while (1) {
268 int c;
269 static const struct option long_options[] = {
270 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
271 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
272 {NULL, 0, NULL, 0}
275 c = getopt_long(argc, argv, "cC", long_options, NULL);
276 if (c < 0)
277 break;
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);
294 if (check_argc_min(argc - optind, 1))
295 usage(cmd_subvol_delete_usage);
297 if (verbose > 0) {
298 printf("Transaction commit: %s\n",
299 !commit_mode ? "none (default)" :
300 commit_mode == 1 ? "at the end" : "after each");
303 cnt = optind;
305 again:
306 path = argv[cnt];
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;
314 if (!res) {
315 error("not a subvolume: %s", path);
316 ret = 1;
317 goto out;
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;
327 dupdname = strdup(cpath);
328 dname = dirname(dupdname);
329 dupvname = strdup(cpath);
330 vname = basename(dupvname);
331 free(cpath);
333 fd = btrfs_open_dir(dname, &dirstream, 1);
334 if (fd < 0) {
335 ret = 1;
336 goto out;
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;
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;
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;
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;
383 close_file_or_dir(fd, dirstream);
385 return ret;
389 * Naming of options:
390 * - uppercase for filters and sort options
391 * - lowercase for enabling specific items in the output
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)",
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,
425 static int cmd_subvol_list(int argc, char **argv)
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;
439 filter_set = btrfs_list_alloc_filter_set();
440 comparer_set = btrfs_list_alloc_comparer_set();
442 optind = 1;
443 while(1) {
444 int c;
445 static const struct option long_options[] = {
446 {"sort", required_argument, NULL, 'S'},
447 {NULL, 0, NULL, 0}
450 c = getopt_long(argc, argv,
451 "acdgopqsurRG:C:t", long_options, NULL);
452 if (c < 0)
453 break;
455 switch(c) {
456 case 'p':
457 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
458 break;
459 case 'a':
460 is_list_all = 1;
461 break;
462 case 'c':
463 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
464 break;
465 case 'd':
466 btrfs_list_setup_filter(&filter_set,
467 BTRFS_LIST_FILTER_DELETED,
469 break;
470 case 'g':
471 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
472 break;
473 case 'o':
474 is_only_in_path = 1;
475 break;
476 case 't':
477 is_tab_result = 1;
478 break;
479 case 's':
480 btrfs_list_setup_filter(&filter_set,
481 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
483 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
484 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
485 break;
486 case 'u':
487 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
488 break;
489 case 'q':
490 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
491 break;
492 case 'R':
493 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
494 break;
495 case 'r':
496 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
497 break;
498 case 'G':
499 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
500 ret = btrfs_list_parse_filter_string(optarg,
501 &filter_set,
502 BTRFS_LIST_FILTER_GEN);
503 if (ret) {
504 uerr = 1;
505 goto out;
507 break;
509 case 'C':
510 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
511 ret = btrfs_list_parse_filter_string(optarg,
512 &filter_set,
513 BTRFS_LIST_FILTER_CGEN);
514 if (ret) {
515 uerr = 1;
516 goto out;
518 break;
519 case 'S':
520 ret = btrfs_list_parse_sort_string(optarg,
521 &comparer_set);
522 if (ret) {
523 uerr = 1;
524 goto out;
526 break;
528 default:
529 uerr = 1;
530 goto out;
534 if (flags)
535 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
536 flags);
538 if (check_argc_exact(argc - optind, 1)) {
539 uerr = 1;
540 goto out;
543 subvol = argv[optind];
544 fd = btrfs_open_dir(subvol, &dirstream, 1);
545 if (fd < 0) {
546 ret = -1;
547 error("can't access '%s'", subvol);
548 goto out;
551 ret = btrfs_list_get_path_rootid(fd, &top_id);
552 if (ret) {
553 error("can't get rootid for '%s'", subvol);
554 goto out;
557 if (is_list_all)
558 btrfs_list_setup_filter(&filter_set,
559 BTRFS_LIST_FILTER_FULL_PATH,
560 top_id);
561 else if (is_only_in_path)
562 btrfs_list_setup_filter(&filter_set,
563 BTRFS_LIST_FILTER_TOPID_EQUAL,
564 top_id);
566 /* by default we shall print the following columns*/
567 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
568 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
569 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
570 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
572 if (is_tab_result)
573 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
574 BTRFS_LIST_LAYOUT_TABLE,
575 !is_list_all && !is_only_in_path, NULL);
576 else
577 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
578 BTRFS_LIST_LAYOUT_DEFAULT,
579 !is_list_all && !is_only_in_path, NULL);
581 out:
582 close_file_or_dir(fd, dirstream);
583 if (filter_set)
584 btrfs_list_free_filter_set(filter_set);
585 if (comparer_set)
586 btrfs_list_free_comparer_set(comparer_set);
587 if (uerr)
588 usage(cmd_subvol_list_usage);
589 return !!ret;
592 static const char * const cmd_subvol_snapshot_usage[] = {
593 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
594 "Create a snapshot of the subvolume",
595 "Create a writable/readonly snapshot of the subvolume <source> with",
596 "the name <name> in the <dest> directory. If only <dest> is given,",
597 "the subvolume will be named the basename of <source>.",
599 "-r create a readonly snapshot",
600 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
601 " option can be given multiple times.",
602 NULL
605 static int cmd_subvol_snapshot(int argc, char **argv)
607 char *subvol, *dst;
608 int res, retval;
609 int fd = -1, fddst = -1;
610 int len, readonly = 0;
611 char *dupname = NULL;
612 char *dupdir = NULL;
613 char *newname;
614 char *dstdir;
615 struct btrfs_ioctl_vol_args_v2 args;
616 struct btrfs_qgroup_inherit *inherit = NULL;
617 DIR *dirstream1 = NULL, *dirstream2 = NULL;
619 optind = 1;
620 memset(&args, 0, sizeof(args));
621 while (1) {
622 int c = getopt(argc, argv, "c:i:r");
623 if (c < 0)
624 break;
626 switch (c) {
627 case 'c':
628 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
629 if (res) {
630 retval = res;
631 goto out;
633 break;
634 case 'i':
635 res = qgroup_inherit_add_group(&inherit, optarg);
636 if (res) {
637 retval = res;
638 goto out;
640 break;
641 case 'r':
642 readonly = 1;
643 break;
644 case 'x':
645 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
646 if (res) {
647 retval = res;
648 goto out;
650 break;
651 default:
652 usage(cmd_subvol_snapshot_usage);
656 if (check_argc_exact(argc - optind, 2))
657 usage(cmd_subvol_snapshot_usage);
659 subvol = argv[optind];
660 dst = argv[optind + 1];
662 retval = 1; /* failure */
663 res = test_issubvolume(subvol);
664 if (res < 0) {
665 error("cannot access subvolume %s: %s", subvol, strerror(-res));
666 goto out;
668 if (!res) {
669 error("not a subvolume: %s", subvol);
670 goto out;
673 res = test_isdir(dst);
674 if (res < 0 && res != -ENOENT) {
675 error("cannot access %s: %s", dst, strerror(-res));
676 goto out;
678 if (res == 0) {
679 error("'%s' exists and it is not a directory", dst);
680 goto out;
683 if (res > 0) {
684 dupname = strdup(subvol);
685 newname = basename(dupname);
686 dstdir = dst;
687 } else {
688 dupname = strdup(dst);
689 newname = basename(dupname);
690 dupdir = strdup(dst);
691 dstdir = dirname(dupdir);
694 if (!test_issubvolname(newname)) {
695 error("invalid snapshot name '%s'", newname);
696 goto out;
699 len = strlen(newname);
700 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
701 error("snapshot name too long '%s'", newname);
702 goto out;
705 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
706 if (fddst < 0)
707 goto out;
709 fd = btrfs_open_dir(subvol, &dirstream2, 1);
710 if (fd < 0)
711 goto out;
713 if (readonly) {
714 args.flags |= BTRFS_SUBVOL_RDONLY;
715 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
716 subvol, dstdir, newname);
717 } else {
718 printf("Create a snapshot of '%s' in '%s/%s'\n",
719 subvol, dstdir, newname);
722 args.fd = fd;
723 if (inherit) {
724 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
725 args.size = qgroup_inherit_size(inherit);
726 args.qgroup_inherit = inherit;
728 strncpy_null(args.name, newname);
730 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
732 if (res < 0) {
733 error("cannot snapshot '%s': %s", subvol, strerror(errno));
734 goto out;
737 retval = 0; /* success */
739 out:
740 close_file_or_dir(fddst, dirstream1);
741 close_file_or_dir(fd, dirstream2);
742 free(inherit);
743 free(dupname);
744 free(dupdir);
746 return retval;
749 static const char * const cmd_subvol_get_default_usage[] = {
750 "btrfs subvolume get-default <path>",
751 "Get the default subvolume of a filesystem",
752 NULL
755 static int cmd_subvol_get_default(int argc, char **argv)
757 int fd = -1;
758 int ret;
759 char *subvol;
760 struct btrfs_list_filter_set *filter_set;
761 u64 default_id;
762 DIR *dirstream = NULL;
764 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
766 if (check_argc_exact(argc - optind, 1))
767 usage(cmd_subvol_get_default_usage);
769 subvol = argv[1];
770 fd = btrfs_open_dir(subvol, &dirstream, 1);
771 if (fd < 0)
772 return 1;
774 ret = btrfs_list_get_default_subvolume(fd, &default_id);
775 if (ret) {
776 error("failed to look up default subvolume: %s",
777 strerror(errno));
778 goto out;
781 ret = 1;
782 if (default_id == 0) {
783 error("'default' dir item not found");
784 goto out;
787 /* no need to resolve roots if FS_TREE is default */
788 if (default_id == BTRFS_FS_TREE_OBJECTID) {
789 printf("ID 5 (FS_TREE)\n");
790 ret = 0;
791 goto out;
794 filter_set = btrfs_list_alloc_filter_set();
795 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
796 default_id);
798 /* by default we shall print the following columns*/
799 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
800 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
801 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
802 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
804 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
805 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
807 if (filter_set)
808 btrfs_list_free_filter_set(filter_set);
809 out:
810 close_file_or_dir(fd, dirstream);
811 return !!ret;
814 static const char * const cmd_subvol_set_default_usage[] = {
815 "btrfs subvolume set-default <subvolid> <path>",
816 "Set the default subvolume of a filesystem",
817 NULL
820 static int cmd_subvol_set_default(int argc, char **argv)
822 int ret=0, fd, e;
823 u64 objectid;
824 char *path;
825 char *subvolid;
826 DIR *dirstream = NULL;
828 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
830 if (check_argc_exact(argc - optind, 2))
831 usage(cmd_subvol_set_default_usage);
833 subvolid = argv[optind];
834 path = argv[optind + 1];
836 objectid = arg_strtou64(subvolid);
838 fd = btrfs_open_dir(path, &dirstream, 1);
839 if (fd < 0)
840 return 1;
842 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
843 e = errno;
844 close_file_or_dir(fd, dirstream);
845 if (ret < 0) {
846 error("unable to set a new default subvolume: %s",
847 strerror(e));
848 return 1;
850 return 0;
853 static const char * const cmd_subvol_find_new_usage[] = {
854 "btrfs subvolume find-new <path> <lastgen>",
855 "List the recently modified files in a filesystem",
856 NULL
859 static int cmd_subvol_find_new(int argc, char **argv)
861 int fd;
862 int ret;
863 char *subvol;
864 u64 last_gen;
865 DIR *dirstream = NULL;
867 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
869 if (check_argc_exact(argc - optind, 2))
870 usage(cmd_subvol_find_new_usage);
872 subvol = argv[optind];
873 last_gen = arg_strtou64(argv[optind + 1]);
875 ret = test_issubvolume(subvol);
876 if (ret < 0) {
877 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
878 return 1;
880 if (!ret) {
881 error("not a subvolume: %s", subvol);
882 return 1;
885 fd = btrfs_open_dir(subvol, &dirstream, 1);
886 if (fd < 0)
887 return 1;
889 ret = ioctl(fd, BTRFS_IOC_SYNC);
890 if (ret < 0) {
891 error("sync ioctl failed on '%s': %s",
892 subvol, strerror(errno));
893 close_file_or_dir(fd, dirstream);
894 return 1;
897 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
898 close_file_or_dir(fd, dirstream);
899 return !!ret;
902 static const char * const cmd_subvol_show_usage[] = {
903 "btrfs subvolume show <subvol-path>",
904 "Show more information of the subvolume",
905 NULL
908 static int cmd_subvol_show(int argc, char **argv)
910 struct root_info get_ri;
911 struct btrfs_list_filter_set *filter_set = NULL;
912 char tstr[256];
913 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
914 char *fullpath = NULL;
915 char raw_prefix[] = "\t\t\t\t";
916 int fd = -1;
917 int ret = 1;
918 DIR *dirstream1 = NULL;
920 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
922 if (check_argc_exact(argc - optind, 1))
923 usage(cmd_subvol_show_usage);
925 memset(&get_ri, 0, sizeof(get_ri));
926 fullpath = realpath(argv[optind], NULL);
927 if (!fullpath) {
928 error("cannot find real path for '%s': %s",
929 argv[optind], strerror(errno));
930 goto out;
933 ret = get_subvol_info(fullpath, &get_ri);
934 if (ret == 2) {
936 * Since the top level btrfs was given don't
937 * take that as error
939 printf("%s is toplevel subvolume\n", fullpath);
940 ret = 0;
941 goto out;
943 if (ret) {
944 ret < 0 ?
945 error("Failed to get subvol info %s: %s\n",
946 fullpath, strerror(-ret)):
947 error("Failed to get subvol info %s: %d\n",
948 fullpath, ret);
949 return ret;
952 /* print the info */
953 printf("%s\n", fullpath);
954 printf("\tName: \t\t\t%s\n", get_ri.name);
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);
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);
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);
974 if (get_ri.otime) {
975 struct tm tm;
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);
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);
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");
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);
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;
1006 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1007 1, raw_prefix);
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);
1016 close_file_or_dir(fd, dirstream1);
1017 free(fullpath);
1018 return !!ret;
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.",
1030 "-s <N> sleep N seconds between checks (default: 1)",
1031 NULL
1034 #if 0
1036 * If we're looking for any dead subvolume, take a shortcut and look
1037 * for any ORPHAN_ITEMs in the tree root
1039 static int fs_has_dead_subvolumes(int fd)
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;
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;
1059 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1060 if (ret < 0)
1061 return -errno;
1063 if (!sk->nr_items)
1064 return 0;
1066 memcpy(&sh, args.buf, sizeof(sh));
1067 min_subvolid = sh.offset;
1070 * Verify that the root item is really there and we haven't hit
1071 * a stale orphan
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;
1084 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1085 if (ret < 0)
1086 return -errno;
1089 * Stale orphan, try the next one
1091 if (!sk->nr_items) {
1092 min_subvolid++;
1093 goto again;
1096 return 1;
1098 #endif
1100 #define SUBVOL_ID_BATCH 1024
1103 * Enumerate all dead subvolumes that exist in the filesystem.
1104 * Fill @ids and reallocate to bigger size if needed.
1106 static int enumerate_dead_subvols(int fd, u64 **ids)
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;
1114 memset(&args, 0, sizeof(args));
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;
1127 *ids = NULL;
1128 while (1) {
1129 struct btrfs_ioctl_search_header *sh;
1130 unsigned long off;
1131 int i;
1133 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1134 if (ret < 0)
1135 return -errno;
1137 if (!sk->nr_items)
1138 return idx;
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);
1145 if (btrfs_search_header_type(sh)
1146 == BTRFS_ORPHAN_ITEM_KEY) {
1147 if (idx >= count) {
1148 u64 *newids;
1150 count += SUBVOL_ID_BATCH;
1151 newids = (u64*)realloc(*ids,
1152 count * sizeof(u64));
1153 if (!newids)
1154 return -ENOMEM;
1155 *ids = newids;
1157 (*ids)[idx] = btrfs_search_header_offset(sh);
1158 idx++;
1160 off += btrfs_search_header_len(sh);
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);
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;
1176 return idx;
1179 static int cmd_subvol_sync(int argc, char **argv)
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;
1189 optind = 1;
1190 while (1) {
1191 int c = getopt(argc, argv, "s:");
1193 if (c < 0)
1194 break;
1196 switch (c) {
1197 case 's':
1198 sleep_interval = atoi(optarg);
1199 if (sleep_interval < 1) {
1200 error("invalid sleep interval %s", optarg);
1201 ret = 1;
1202 goto out;
1204 break;
1205 default:
1206 usage(cmd_subvol_sync_usage);
1210 if (check_argc_min(argc - optind, 1))
1211 usage(cmd_subvol_sync_usage);
1213 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1214 if (fd < 0) {
1215 ret = 1;
1216 goto out;
1218 optind++;
1220 id_count = argc - optind;
1221 if (!id_count) {
1222 id_count = enumerate_dead_subvols(fd, &ids);
1223 if (id_count < 0) {
1224 error("can't enumerate dead subvolumes: %s",
1225 strerror(-id_count));
1226 ret = 1;
1227 goto out;
1229 if (id_count == 0) {
1230 ret = 0;
1231 goto out;
1233 } else {
1234 ids = (u64*)malloc(id_count * sizeof(u64));
1235 if (!ids) {
1236 error("not enough memory");
1237 ret = 1;
1238 goto out;
1241 for (i = 0; i < id_count; i++) {
1242 u64 id;
1243 const char *arg;
1245 arg = argv[optind + i];
1246 errno = 0;
1247 id = strtoull(arg, NULL, 10);
1248 if (errno < 0) {
1249 error("unrecognized subvolume id %s", arg);
1250 ret = 1;
1251 goto out;
1253 if (id < BTRFS_FIRST_FREE_OBJECTID
1254 || id > BTRFS_LAST_FREE_OBJECTID) {
1255 error("subvolume id %s out of range\n", arg);
1256 ret = 1;
1257 goto out;
1259 ids[i] = id;
1263 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1265 out:
1266 free(ids);
1267 close_file_or_dir(fd, dirstream);
1269 return !!ret;
1272 static const char subvolume_cmd_group_info[] =
1273 "manage subvolumes: create, delete, list, etc";
1275 const struct cmd_group subvolume_cmd_group = {
1276 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1277 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1278 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1279 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1280 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1281 NULL, 0 },
1282 { "get-default", cmd_subvol_get_default,
1283 cmd_subvol_get_default_usage, NULL, 0 },
1284 { "set-default", cmd_subvol_set_default,
1285 cmd_subvol_set_default_usage, NULL, 0 },
1286 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1287 NULL, 0 },
1288 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1289 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1290 NULL_CMD_STRUCT
1294 int cmd_subvolume(int argc, char **argv)
1296 return handle_command_group(&subvolume_cmd_group, argc, argv);