Btrfs progs v4.13.1
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob666f6e05029374b80165cc0fd50c261f9c037f6b
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"
40 #include "help.h"
42 static int is_subvolume_cleaned(int fd, u64 subvolid)
44 int ret;
45 struct btrfs_ioctl_search_args args;
46 struct btrfs_ioctl_search_key *sk = &args.key;
48 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
49 sk->min_objectid = subvolid;
50 sk->max_objectid = subvolid;
51 sk->min_type = BTRFS_ROOT_ITEM_KEY;
52 sk->max_type = BTRFS_ROOT_ITEM_KEY;
53 sk->min_offset = 0;
54 sk->max_offset = (u64)-1;
55 sk->min_transid = 0;
56 sk->max_transid = (u64)-1;
57 sk->nr_items = 1;
59 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
60 if (ret < 0)
61 return -errno;
63 if (sk->nr_items == 0)
64 return 1;
66 return 0;
69 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
70 int sleep_interval)
72 int ret;
73 int i;
75 while (1) {
76 int clean = 1;
78 for (i = 0; i < count; i++) {
79 if (!ids[i])
80 continue;
81 ret = is_subvolume_cleaned(fd, ids[i]);
82 if (ret < 0) {
83 error(
84 "cannot read status of dead subvolume %llu: %s",
85 (unsigned long long)ids[i], strerror(-ret));
86 return ret;
88 if (ret) {
89 printf("Subvolume id %llu is gone\n", ids[i]);
90 ids[i] = 0;
91 } else {
92 clean = 0;
95 if (clean)
96 break;
97 sleep(sleep_interval);
100 return 0;
103 static const char * const subvolume_cmd_group_usage[] = {
104 "btrfs subvolume <command> <args>",
105 NULL
108 static const char * const cmd_subvol_create_usage[] = {
109 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
110 "Create a subvolume",
111 "Create a subvolume <name> in <dest>. If <dest> is not given",
112 "subvolume <name> will be created in the current directory.",
114 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
115 " option can be given multiple times.",
116 NULL
119 static int cmd_subvol_create(int argc, char **argv)
121 int retval, res, len;
122 int fddst = -1;
123 char *dupname = NULL;
124 char *dupdir = NULL;
125 char *newname;
126 char *dstdir;
127 char *dst;
128 struct btrfs_qgroup_inherit *inherit = NULL;
129 DIR *dirstream = NULL;
131 while (1) {
132 int c = getopt(argc, argv, "c:i:");
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 "-v|--verbose verbose output of operations",
250 NULL
253 static int cmd_subvol_delete(int argc, char **argv)
255 int res, ret = 0;
256 int cnt;
257 int fd = -1;
258 struct btrfs_ioctl_vol_args args;
259 char *dname, *vname, *cpath;
260 char *dupdname = NULL;
261 char *dupvname = NULL;
262 char *path;
263 DIR *dirstream = NULL;
264 int verbose = 0;
265 int commit_mode = 0;
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 {"verbose", no_argument, NULL, 'v'},
273 {NULL, 0, NULL, 0}
276 c = getopt_long(argc, argv, "cCv", long_options, NULL);
277 if (c < 0)
278 break;
280 switch(c) {
281 case 'c':
282 commit_mode = 1;
283 break;
284 case 'C':
285 commit_mode = 2;
286 break;
287 case 'v':
288 verbose++;
289 break;
290 default:
291 usage(cmd_subvol_delete_usage);
295 if (check_argc_min(argc - optind, 1))
296 usage(cmd_subvol_delete_usage);
298 if (verbose > 0) {
299 printf("Transaction commit: %s\n",
300 !commit_mode ? "none (default)" :
301 commit_mode == 1 ? "at the end" : "after each");
304 cnt = optind;
306 again:
307 path = argv[cnt];
309 res = test_issubvolume(path);
310 if (res < 0) {
311 error("cannot access subvolume %s: %s", path, strerror(-res));
312 ret = 1;
313 goto out;
315 if (!res) {
316 error("not a subvolume: %s", path);
317 ret = 1;
318 goto out;
321 cpath = realpath(path, NULL);
322 if (!cpath) {
323 ret = errno;
324 error("cannot find real path for '%s': %s",
325 path, strerror(errno));
326 goto out;
328 dupdname = strdup(cpath);
329 dname = dirname(dupdname);
330 dupvname = strdup(cpath);
331 vname = basename(dupvname);
332 free(cpath);
334 fd = btrfs_open_dir(dname, &dirstream, 1);
335 if (fd < 0) {
336 ret = 1;
337 goto out;
340 printf("Delete subvolume (%s): '%s/%s'\n",
341 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
342 ? "commit" : "no-commit", dname, vname);
343 memset(&args, 0, sizeof(args));
344 strncpy_null(args.name, vname);
345 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
346 if(res < 0 ){
347 error("cannot delete '%s/%s': %s", dname, vname,
348 strerror(errno));
349 ret = 1;
350 goto out;
353 if (commit_mode == 1) {
354 res = wait_for_commit(fd);
355 if (res < 0) {
356 error("unable to wait for commit after '%s': %s",
357 path, strerror(errno));
358 ret = 1;
362 out:
363 free(dupdname);
364 free(dupvname);
365 dupdname = NULL;
366 dupvname = NULL;
367 cnt++;
368 if (cnt < argc) {
369 close_file_or_dir(fd, dirstream);
370 /* avoid double free */
371 fd = -1;
372 dirstream = NULL;
373 goto again;
376 if (commit_mode == 2 && fd != -1) {
377 res = wait_for_commit(fd);
378 if (res < 0) {
379 error("unable to do final sync after deletion: %s",
380 strerror(errno));
381 ret = 1;
384 close_file_or_dir(fd, dirstream);
386 return ret;
390 * Naming of options:
391 * - uppercase for filters and sort options
392 * - lowercase for enabling specific items in the output
394 static const char * const cmd_subvol_list_usage[] = {
395 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
396 "[--sort=gen,ogen,rootid,path] <path>",
397 "List subvolumes (and snapshots)",
399 "-p print parent ID",
400 "-a print all the subvolumes in the filesystem and",
401 " distinguish absolute and relative path with respect",
402 " to the given <path>",
403 "-c print the ogeneration of the subvolume",
404 "-g print the generation of the subvolume",
405 "-o print only subvolumes below specified path",
406 "-u print the uuid of subvolumes (and snapshots)",
407 "-q print the parent uuid of the snapshots",
408 "-R print the uuid of the received snapshots",
409 "-t print the result as a table",
410 "-s list snapshots only in the filesystem",
411 "-r list readonly subvolumes (including snapshots)",
412 "-d list deleted subvolumes that are not yet cleaned",
413 "-G [+|-]value",
414 " filter the subvolumes by generation",
415 " (+value: >= value; -value: <= value; value: = value)",
416 "-C [+|-]value",
417 " filter the subvolumes by ogeneration",
418 " (+value: >= value; -value: <= value; value: = value)",
419 "--sort=gen,ogen,rootid,path",
420 " list the subvolume in order of gen, ogen, rootid or path",
421 " you also can add '+' or '-' in front of each items.",
422 " (+:ascending, -:descending, ascending default)",
423 NULL,
426 static int cmd_subvol_list(int argc, char **argv)
428 struct btrfs_list_filter_set *filter_set;
429 struct btrfs_list_comparer_set *comparer_set;
430 u64 flags = 0;
431 int fd = -1;
432 u64 top_id;
433 int ret = -1, uerr = 0;
434 char *subvol;
435 int is_list_all = 0;
436 int is_only_in_path = 0;
437 DIR *dirstream = NULL;
438 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
440 filter_set = btrfs_list_alloc_filter_set();
441 comparer_set = btrfs_list_alloc_comparer_set();
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 layout = BTRFS_LIST_LAYOUT_TABLE;
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 (check_argc_exact(argc - optind, 1)) {
535 uerr = 1;
536 goto out;
539 subvol = argv[optind];
540 fd = btrfs_open_dir(subvol, &dirstream, 1);
541 if (fd < 0) {
542 ret = -1;
543 error("can't access '%s'", subvol);
544 goto out;
547 if (flags)
548 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
549 flags);
551 ret = btrfs_list_get_path_rootid(fd, &top_id);
552 if (ret)
553 goto out;
555 if (is_list_all)
556 btrfs_list_setup_filter(&filter_set,
557 BTRFS_LIST_FILTER_FULL_PATH,
558 top_id);
559 else if (is_only_in_path)
560 btrfs_list_setup_filter(&filter_set,
561 BTRFS_LIST_FILTER_TOPID_EQUAL,
562 top_id);
564 /* by default we shall print the following columns*/
565 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
566 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
567 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
568 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
570 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
571 layout, !is_list_all && !is_only_in_path, NULL);
573 out:
574 close_file_or_dir(fd, dirstream);
575 if (filter_set)
576 free(filter_set);
577 if (comparer_set)
578 free(comparer_set);
579 if (uerr)
580 usage(cmd_subvol_list_usage);
581 return !!ret;
584 static const char * const cmd_subvol_snapshot_usage[] = {
585 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
586 "Create a snapshot of the subvolume",
587 "Create a writable/readonly snapshot of the subvolume <source> with",
588 "the name <name> in the <dest> directory. If only <dest> is given,",
589 "the subvolume will be named the basename of <source>.",
591 "-r create a readonly snapshot",
592 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
593 " option can be given multiple times.",
594 NULL
597 static int cmd_subvol_snapshot(int argc, char **argv)
599 char *subvol, *dst;
600 int res, retval;
601 int fd = -1, fddst = -1;
602 int len, readonly = 0;
603 char *dupname = NULL;
604 char *dupdir = NULL;
605 char *newname;
606 char *dstdir;
607 struct btrfs_ioctl_vol_args_v2 args;
608 struct btrfs_qgroup_inherit *inherit = NULL;
609 DIR *dirstream1 = NULL, *dirstream2 = NULL;
611 memset(&args, 0, sizeof(args));
612 while (1) {
613 int c = getopt(argc, argv, "c:i:r");
614 if (c < 0)
615 break;
617 switch (c) {
618 case 'c':
619 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
620 if (res) {
621 retval = res;
622 goto out;
624 break;
625 case 'i':
626 res = qgroup_inherit_add_group(&inherit, optarg);
627 if (res) {
628 retval = res;
629 goto out;
631 break;
632 case 'r':
633 readonly = 1;
634 break;
635 case 'x':
636 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
637 if (res) {
638 retval = res;
639 goto out;
641 break;
642 default:
643 usage(cmd_subvol_snapshot_usage);
647 if (check_argc_exact(argc - optind, 2))
648 usage(cmd_subvol_snapshot_usage);
650 subvol = argv[optind];
651 dst = argv[optind + 1];
653 retval = 1; /* failure */
654 res = test_issubvolume(subvol);
655 if (res < 0) {
656 error("cannot access subvolume %s: %s", subvol, strerror(-res));
657 goto out;
659 if (!res) {
660 error("not a subvolume: %s", subvol);
661 goto out;
664 res = test_isdir(dst);
665 if (res < 0 && res != -ENOENT) {
666 error("cannot access %s: %s", dst, strerror(-res));
667 goto out;
669 if (res == 0) {
670 error("'%s' exists and it is not a directory", dst);
671 goto out;
674 if (res > 0) {
675 dupname = strdup(subvol);
676 newname = basename(dupname);
677 dstdir = dst;
678 } else {
679 dupname = strdup(dst);
680 newname = basename(dupname);
681 dupdir = strdup(dst);
682 dstdir = dirname(dupdir);
685 if (!test_issubvolname(newname)) {
686 error("invalid snapshot name '%s'", newname);
687 goto out;
690 len = strlen(newname);
691 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
692 error("snapshot name too long '%s'", newname);
693 goto out;
696 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
697 if (fddst < 0)
698 goto out;
700 fd = btrfs_open_dir(subvol, &dirstream2, 1);
701 if (fd < 0)
702 goto out;
704 if (readonly) {
705 args.flags |= BTRFS_SUBVOL_RDONLY;
706 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
707 subvol, dstdir, newname);
708 } else {
709 printf("Create a snapshot of '%s' in '%s/%s'\n",
710 subvol, dstdir, newname);
713 args.fd = fd;
714 if (inherit) {
715 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
716 args.size = qgroup_inherit_size(inherit);
717 args.qgroup_inherit = inherit;
719 strncpy_null(args.name, newname);
721 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
723 if (res < 0) {
724 error("cannot snapshot '%s': %s", subvol, strerror(errno));
725 goto out;
728 retval = 0; /* success */
730 out:
731 close_file_or_dir(fddst, dirstream1);
732 close_file_or_dir(fd, dirstream2);
733 free(inherit);
734 free(dupname);
735 free(dupdir);
737 return retval;
740 static const char * const cmd_subvol_get_default_usage[] = {
741 "btrfs subvolume get-default <path>",
742 "Get the default subvolume of a filesystem",
743 NULL
746 static int cmd_subvol_get_default(int argc, char **argv)
748 int fd = -1;
749 int ret;
750 char *subvol;
751 struct btrfs_list_filter_set *filter_set;
752 u64 default_id;
753 DIR *dirstream = NULL;
755 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
757 if (check_argc_exact(argc - optind, 1))
758 usage(cmd_subvol_get_default_usage);
760 subvol = argv[1];
761 fd = btrfs_open_dir(subvol, &dirstream, 1);
762 if (fd < 0)
763 return 1;
765 ret = btrfs_list_get_default_subvolume(fd, &default_id);
766 if (ret) {
767 error("failed to look up default subvolume: %s",
768 strerror(errno));
769 goto out;
772 ret = 1;
773 if (default_id == 0) {
774 error("'default' dir item not found");
775 goto out;
778 /* no need to resolve roots if FS_TREE is default */
779 if (default_id == BTRFS_FS_TREE_OBJECTID) {
780 printf("ID 5 (FS_TREE)\n");
781 ret = 0;
782 goto out;
785 filter_set = btrfs_list_alloc_filter_set();
786 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
787 default_id);
789 /* by default we shall print the following columns*/
790 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
791 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
792 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
793 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
795 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
796 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
798 if (filter_set)
799 free(filter_set);
800 out:
801 close_file_or_dir(fd, dirstream);
802 return !!ret;
805 static const char * const cmd_subvol_set_default_usage[] = {
806 "btrfs subvolume set-default <subvolid> <path>",
807 "Set the default subvolume of a filesystem",
808 NULL
811 static int cmd_subvol_set_default(int argc, char **argv)
813 int ret=0, fd, e;
814 u64 objectid;
815 char *path;
816 char *subvolid;
817 DIR *dirstream = NULL;
819 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
821 if (check_argc_exact(argc - optind, 2))
822 usage(cmd_subvol_set_default_usage);
824 subvolid = argv[optind];
825 path = argv[optind + 1];
827 objectid = arg_strtou64(subvolid);
829 fd = btrfs_open_dir(path, &dirstream, 1);
830 if (fd < 0)
831 return 1;
833 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
834 e = errno;
835 close_file_or_dir(fd, dirstream);
836 if (ret < 0) {
837 error("unable to set a new default subvolume: %s",
838 strerror(e));
839 return 1;
841 return 0;
844 static const char * const cmd_subvol_find_new_usage[] = {
845 "btrfs subvolume find-new <path> <lastgen>",
846 "List the recently modified files in a filesystem",
847 NULL
850 static int cmd_subvol_find_new(int argc, char **argv)
852 int fd;
853 int ret;
854 char *subvol;
855 u64 last_gen;
856 DIR *dirstream = NULL;
858 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
860 if (check_argc_exact(argc - optind, 2))
861 usage(cmd_subvol_find_new_usage);
863 subvol = argv[optind];
864 last_gen = arg_strtou64(argv[optind + 1]);
866 ret = test_issubvolume(subvol);
867 if (ret < 0) {
868 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
869 return 1;
871 if (!ret) {
872 error("not a subvolume: %s", subvol);
873 return 1;
876 fd = btrfs_open_dir(subvol, &dirstream, 1);
877 if (fd < 0)
878 return 1;
880 ret = ioctl(fd, BTRFS_IOC_SYNC);
881 if (ret < 0) {
882 error("sync ioctl failed on '%s': %s",
883 subvol, strerror(errno));
884 close_file_or_dir(fd, dirstream);
885 return 1;
888 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
889 close_file_or_dir(fd, dirstream);
890 return !!ret;
893 static const char * const cmd_subvol_show_usage[] = {
894 "btrfs subvolume show [options] <subvol-path>|<mnt>",
895 "Show more information about the subvolume",
896 "-r|--rootid rootid of the subvolume",
897 "-u|--uuid uuid of the subvolume",
899 "If no option is specified, <subvol-path> will be shown, otherwise",
900 "the rootid or uuid are resolved relative to the <mnt> path.",
901 NULL
904 static int cmd_subvol_show(int argc, char **argv)
906 struct root_info get_ri;
907 struct btrfs_list_filter_set *filter_set = NULL;
908 char tstr[256];
909 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
910 char *fullpath = NULL;
911 char raw_prefix[] = "\t\t\t\t";
912 int fd = -1;
913 int ret = 1;
914 DIR *dirstream1 = NULL;
915 int by_rootid = 0;
916 int by_uuid = 0;
917 u64 rootid_arg;
918 u8 uuid_arg[BTRFS_UUID_SIZE];
920 while (1) {
921 int c;
922 static const struct option long_options[] = {
923 { "rootid", required_argument, NULL, 'r'},
924 { "uuid", required_argument, NULL, 'u'},
925 { NULL, 0, NULL, 0 }
928 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
929 if (c < 0)
930 break;
932 switch (c) {
933 case 'r':
934 rootid_arg = arg_strtou64(optarg);
935 by_rootid = 1;
936 break;
937 case 'u':
938 uuid_parse(optarg, uuid_arg);
939 by_uuid = 1;
940 break;
941 default:
942 usage(cmd_subvol_show_usage);
946 if (check_argc_exact(argc - optind, 1))
947 usage(cmd_subvol_show_usage);
949 if (by_rootid && by_uuid) {
950 error(
951 "options --rootid and --uuid cannot be used at the same time");
952 usage(cmd_subvol_show_usage);
955 memset(&get_ri, 0, sizeof(get_ri));
956 fullpath = realpath(argv[optind], NULL);
957 if (!fullpath) {
958 error("cannot find real path for '%s': %s",
959 argv[optind], strerror(errno));
960 goto out;
963 if (by_rootid) {
964 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
965 } else if (by_uuid) {
966 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
967 } else {
968 ret = get_subvol_info(fullpath, &get_ri);
971 if (ret) {
972 if (ret < 0) {
973 error("Failed to get subvol info %s: %s",
974 fullpath, strerror(-ret));
975 } else {
976 error("Failed to get subvol info %s: %d",
977 fullpath, ret);
979 return ret;
982 /* print the info */
983 printf("%s\n", get_ri.full_path);
984 printf("\tName: \t\t\t%s\n", get_ri.name);
986 if (uuid_is_null(get_ri.uuid))
987 strcpy(uuidparse, "-");
988 else
989 uuid_unparse(get_ri.uuid, uuidparse);
990 printf("\tUUID: \t\t\t%s\n", uuidparse);
992 if (uuid_is_null(get_ri.puuid))
993 strcpy(uuidparse, "-");
994 else
995 uuid_unparse(get_ri.puuid, uuidparse);
996 printf("\tParent UUID: \t\t%s\n", uuidparse);
998 if (uuid_is_null(get_ri.ruuid))
999 strcpy(uuidparse, "-");
1000 else
1001 uuid_unparse(get_ri.ruuid, uuidparse);
1002 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1004 if (get_ri.otime) {
1005 struct tm tm;
1007 localtime_r(&get_ri.otime, &tm);
1008 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1009 } else
1010 strcpy(tstr, "-");
1011 printf("\tCreation time: \t\t%s\n", tstr);
1013 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1014 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1015 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1016 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1017 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1019 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1020 printf("\tFlags: \t\t\treadonly\n");
1021 else
1022 printf("\tFlags: \t\t\t-\n");
1024 /* print the snapshots of the given subvol if any*/
1025 printf("\tSnapshot(s):\n");
1026 filter_set = btrfs_list_alloc_filter_set();
1027 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1028 (u64)(unsigned long)get_ri.uuid);
1029 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1031 fd = open_file_or_dir(fullpath, &dirstream1);
1032 if (fd < 0) {
1033 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1034 goto out;
1036 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1037 1, raw_prefix);
1039 out:
1040 /* clean up */
1041 free(get_ri.path);
1042 free(get_ri.name);
1043 free(get_ri.full_path);
1044 free(filter_set);
1046 close_file_or_dir(fd, dirstream1);
1047 free(fullpath);
1048 return !!ret;
1051 static const char * const cmd_subvol_sync_usage[] = {
1052 "btrfs subvolume sync <path> [<subvol-id>...]",
1053 "Wait until given subvolume(s) are completely removed from the filesystem.",
1054 "Wait until given subvolume(s) are completely removed from the filesystem",
1055 "after deletion.",
1056 "If no subvolume id is given, wait until all current deletion requests",
1057 "are completed, but do not wait for subvolumes deleted meanwhile.",
1058 "The status of subvolume ids is checked periodically.",
1060 "-s <N> sleep N seconds between checks (default: 1)",
1061 NULL
1064 #if 0
1066 * If we're looking for any dead subvolume, take a shortcut and look
1067 * for any ORPHAN_ITEMs in the tree root
1069 static int fs_has_dead_subvolumes(int fd)
1071 int ret;
1072 struct btrfs_ioctl_search_args args;
1073 struct btrfs_ioctl_search_key *sk = &args.key;
1074 struct btrfs_ioctl_search_header sh;
1075 u64 min_subvolid = 0;
1077 again:
1078 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1079 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1080 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1081 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1082 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1083 sk->min_offset = min_subvolid;
1084 sk->max_offset = (u64)-1;
1085 sk->min_transid = 0;
1086 sk->max_transid = (u64)-1;
1087 sk->nr_items = 1;
1089 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1090 if (ret < 0)
1091 return -errno;
1093 if (!sk->nr_items)
1094 return 0;
1096 memcpy(&sh, args.buf, sizeof(sh));
1097 min_subvolid = sh.offset;
1100 * Verify that the root item is really there and we haven't hit
1101 * a stale orphan
1103 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1104 sk->min_objectid = min_subvolid;
1105 sk->max_objectid = min_subvolid;
1106 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1107 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1108 sk->min_offset = 0;
1109 sk->max_offset = (u64)-1;
1110 sk->min_transid = 0;
1111 sk->max_transid = (u64)-1;
1112 sk->nr_items = 1;
1114 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1115 if (ret < 0)
1116 return -errno;
1119 * Stale orphan, try the next one
1121 if (!sk->nr_items) {
1122 min_subvolid++;
1123 goto again;
1126 return 1;
1128 #endif
1130 #define SUBVOL_ID_BATCH 1024
1133 * Enumerate all dead subvolumes that exist in the filesystem.
1134 * Fill @ids and reallocate to bigger size if needed.
1136 static int enumerate_dead_subvols(int fd, u64 **ids)
1138 int ret;
1139 struct btrfs_ioctl_search_args args;
1140 struct btrfs_ioctl_search_key *sk = &args.key;
1141 int idx = 0;
1142 int count = 0;
1144 memset(&args, 0, sizeof(args));
1146 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1147 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1148 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1149 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1150 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1151 sk->min_offset = 0;
1152 sk->max_offset = (u64)-1;
1153 sk->min_transid = 0;
1154 sk->max_transid = (u64)-1;
1155 sk->nr_items = 4096;
1157 *ids = NULL;
1158 while (1) {
1159 struct btrfs_ioctl_search_header *sh;
1160 unsigned long off;
1161 int i;
1163 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1164 if (ret < 0)
1165 return -errno;
1167 if (!sk->nr_items)
1168 return idx;
1170 off = 0;
1171 for (i = 0; i < sk->nr_items; i++) {
1172 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1173 off += sizeof(*sh);
1175 if (btrfs_search_header_type(sh)
1176 == BTRFS_ORPHAN_ITEM_KEY) {
1177 if (idx >= count) {
1178 u64 *newids;
1180 count += SUBVOL_ID_BATCH;
1181 newids = (u64*)realloc(*ids,
1182 count * sizeof(u64));
1183 if (!newids)
1184 return -ENOMEM;
1185 *ids = newids;
1187 (*ids)[idx] = btrfs_search_header_offset(sh);
1188 idx++;
1190 off += btrfs_search_header_len(sh);
1192 sk->min_objectid = btrfs_search_header_objectid(sh);
1193 sk->min_type = btrfs_search_header_type(sh);
1194 sk->min_offset = btrfs_search_header_offset(sh);
1196 if (sk->min_offset < (u64)-1)
1197 sk->min_offset++;
1198 else
1199 break;
1200 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1201 break;
1202 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1203 break;
1206 return idx;
1209 static int cmd_subvol_sync(int argc, char **argv)
1211 int fd = -1;
1212 int i;
1213 int ret = 1;
1214 DIR *dirstream = NULL;
1215 u64 *ids = NULL;
1216 int id_count;
1217 int sleep_interval = 1;
1219 while (1) {
1220 int c = getopt(argc, argv, "s:");
1222 if (c < 0)
1223 break;
1225 switch (c) {
1226 case 's':
1227 sleep_interval = atoi(optarg);
1228 if (sleep_interval < 1) {
1229 error("invalid sleep interval %s", optarg);
1230 ret = 1;
1231 goto out;
1233 break;
1234 default:
1235 usage(cmd_subvol_sync_usage);
1239 if (check_argc_min(argc - optind, 1))
1240 usage(cmd_subvol_sync_usage);
1242 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1243 if (fd < 0) {
1244 ret = 1;
1245 goto out;
1247 optind++;
1249 id_count = argc - optind;
1250 if (!id_count) {
1251 id_count = enumerate_dead_subvols(fd, &ids);
1252 if (id_count < 0) {
1253 error("can't enumerate dead subvolumes: %s",
1254 strerror(-id_count));
1255 ret = 1;
1256 goto out;
1258 if (id_count == 0) {
1259 ret = 0;
1260 goto out;
1262 } else {
1263 ids = (u64*)malloc(id_count * sizeof(u64));
1264 if (!ids) {
1265 error("not enough memory");
1266 ret = 1;
1267 goto out;
1270 for (i = 0; i < id_count; i++) {
1271 u64 id;
1272 const char *arg;
1274 arg = argv[optind + i];
1275 errno = 0;
1276 id = strtoull(arg, NULL, 10);
1277 if (errno < 0) {
1278 error("unrecognized subvolume id %s", arg);
1279 ret = 1;
1280 goto out;
1282 if (id < BTRFS_FIRST_FREE_OBJECTID
1283 || id > BTRFS_LAST_FREE_OBJECTID) {
1284 error("subvolume id %s out of range", arg);
1285 ret = 1;
1286 goto out;
1288 ids[i] = id;
1292 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1294 out:
1295 free(ids);
1296 close_file_or_dir(fd, dirstream);
1298 return !!ret;
1301 static const char subvolume_cmd_group_info[] =
1302 "manage subvolumes: create, delete, list, etc";
1304 const struct cmd_group subvolume_cmd_group = {
1305 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1306 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1307 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1308 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1309 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1310 NULL, 0 },
1311 { "get-default", cmd_subvol_get_default,
1312 cmd_subvol_get_default_usage, NULL, 0 },
1313 { "set-default", cmd_subvol_set_default,
1314 cmd_subvol_set_default_usage, NULL, 0 },
1315 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1316 NULL, 0 },
1317 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1318 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1319 NULL_CMD_STRUCT
1323 int cmd_subvolume(int argc, char **argv)
1325 return handle_command_group(&subvolume_cmd_group, argc, argv);