btrfs-progs: subvol: exchange subvol del --commit-after and --commit-each
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob8b24de37e77fc61b2315169eaa23c30749fe258d
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;
266 enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
268 while (1) {
269 int c;
270 static const struct option long_options[] = {
271 {"commit-after", no_argument, NULL, 'c'},
272 {"commit-each", no_argument, NULL, 'C'},
273 {"verbose", no_argument, NULL, 'v'},
274 {NULL, 0, NULL, 0}
277 c = getopt_long(argc, argv, "cCv", long_options, NULL);
278 if (c < 0)
279 break;
281 switch(c) {
282 case 'c':
283 commit_mode = COMMIT_AFTER;
284 break;
285 case 'C':
286 commit_mode = COMMIT_EACH;
287 break;
288 case 'v':
289 verbose++;
290 break;
291 default:
292 usage(cmd_subvol_delete_usage);
296 if (check_argc_min(argc - optind, 1))
297 usage(cmd_subvol_delete_usage);
299 if (verbose > 0) {
300 printf("Transaction commit: %s\n",
301 !commit_mode ? "none (default)" :
302 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
305 cnt = optind;
307 again:
308 path = argv[cnt];
310 res = test_issubvolume(path);
311 if (res < 0) {
312 error("cannot access subvolume %s: %s", path, strerror(-res));
313 ret = 1;
314 goto out;
316 if (!res) {
317 error("not a subvolume: %s", path);
318 ret = 1;
319 goto out;
322 cpath = realpath(path, NULL);
323 if (!cpath) {
324 ret = errno;
325 error("cannot find real path for '%s': %s",
326 path, strerror(errno));
327 goto out;
329 dupdname = strdup(cpath);
330 dname = dirname(dupdname);
331 dupvname = strdup(cpath);
332 vname = basename(dupvname);
333 free(cpath);
335 fd = btrfs_open_dir(dname, &dirstream, 1);
336 if (fd < 0) {
337 ret = 1;
338 goto out;
341 printf("Delete subvolume (%s): '%s/%s'\n",
342 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
343 ? "commit" : "no-commit", dname, vname);
344 memset(&args, 0, sizeof(args));
345 strncpy_null(args.name, vname);
346 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
347 if(res < 0 ){
348 error("cannot delete '%s/%s': %s", dname, vname,
349 strerror(errno));
350 ret = 1;
351 goto out;
354 if (commit_mode == COMMIT_EACH) {
355 res = wait_for_commit(fd);
356 if (res < 0) {
357 error("unable to wait for commit after '%s': %s",
358 path, strerror(errno));
359 ret = 1;
363 out:
364 free(dupdname);
365 free(dupvname);
366 dupdname = NULL;
367 dupvname = NULL;
368 cnt++;
369 if (cnt < argc) {
370 close_file_or_dir(fd, dirstream);
371 /* avoid double free */
372 fd = -1;
373 dirstream = NULL;
374 goto again;
377 if (commit_mode == COMMIT_AFTER && fd != -1) {
378 res = wait_for_commit(fd);
379 if (res < 0) {
380 error("unable to do final sync after deletion: %s",
381 strerror(errno));
382 ret = 1;
385 close_file_or_dir(fd, dirstream);
387 return ret;
391 * Naming of options:
392 * - uppercase for filters and sort options
393 * - lowercase for enabling specific items in the output
395 static const char * const cmd_subvol_list_usage[] = {
396 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
397 "[--sort=gen,ogen,rootid,path] <path>",
398 "List subvolumes (and snapshots)",
400 "-p print parent ID",
401 "-a print all the subvolumes in the filesystem and",
402 " distinguish absolute and relative path with respect",
403 " to the given <path>",
404 "-c print the ogeneration of the subvolume",
405 "-g print the generation of the subvolume",
406 "-o print only subvolumes below specified path",
407 "-u print the uuid of subvolumes (and snapshots)",
408 "-q print the parent uuid of the snapshots",
409 "-R print the uuid of the received snapshots",
410 "-t print the result as a table",
411 "-s list snapshots only in the filesystem",
412 "-r list readonly subvolumes (including snapshots)",
413 "-d list deleted subvolumes that are not yet cleaned",
414 "-G [+|-]value",
415 " filter the subvolumes by generation",
416 " (+value: >= value; -value: <= value; value: = value)",
417 "-C [+|-]value",
418 " filter the subvolumes by ogeneration",
419 " (+value: >= value; -value: <= value; value: = value)",
420 "--sort=gen,ogen,rootid,path",
421 " list the subvolume in order of gen, ogen, rootid or path",
422 " you also can add '+' or '-' in front of each items.",
423 " (+:ascending, -:descending, ascending default)",
424 NULL,
427 static int cmd_subvol_list(int argc, char **argv)
429 struct btrfs_list_filter_set *filter_set;
430 struct btrfs_list_comparer_set *comparer_set;
431 u64 flags = 0;
432 int fd = -1;
433 u64 top_id;
434 int ret = -1, uerr = 0;
435 char *subvol;
436 int is_list_all = 0;
437 int is_only_in_path = 0;
438 DIR *dirstream = NULL;
439 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
441 filter_set = btrfs_list_alloc_filter_set();
442 comparer_set = btrfs_list_alloc_comparer_set();
444 while(1) {
445 int c;
446 static const struct option long_options[] = {
447 {"sort", required_argument, NULL, 'S'},
448 {NULL, 0, NULL, 0}
451 c = getopt_long(argc, argv,
452 "acdgopqsurRG:C:t", long_options, NULL);
453 if (c < 0)
454 break;
456 switch(c) {
457 case 'p':
458 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
459 break;
460 case 'a':
461 is_list_all = 1;
462 break;
463 case 'c':
464 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
465 break;
466 case 'd':
467 btrfs_list_setup_filter(&filter_set,
468 BTRFS_LIST_FILTER_DELETED,
470 break;
471 case 'g':
472 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
473 break;
474 case 'o':
475 is_only_in_path = 1;
476 break;
477 case 't':
478 layout = BTRFS_LIST_LAYOUT_TABLE;
479 break;
480 case 's':
481 btrfs_list_setup_filter(&filter_set,
482 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
484 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
485 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
486 break;
487 case 'u':
488 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
489 break;
490 case 'q':
491 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
492 break;
493 case 'R':
494 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
495 break;
496 case 'r':
497 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
498 break;
499 case 'G':
500 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
501 ret = btrfs_list_parse_filter_string(optarg,
502 &filter_set,
503 BTRFS_LIST_FILTER_GEN);
504 if (ret) {
505 uerr = 1;
506 goto out;
508 break;
510 case 'C':
511 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
512 ret = btrfs_list_parse_filter_string(optarg,
513 &filter_set,
514 BTRFS_LIST_FILTER_CGEN);
515 if (ret) {
516 uerr = 1;
517 goto out;
519 break;
520 case 'S':
521 ret = btrfs_list_parse_sort_string(optarg,
522 &comparer_set);
523 if (ret) {
524 uerr = 1;
525 goto out;
527 break;
529 default:
530 uerr = 1;
531 goto out;
535 if (check_argc_exact(argc - optind, 1)) {
536 uerr = 1;
537 goto out;
540 subvol = argv[optind];
541 fd = btrfs_open_dir(subvol, &dirstream, 1);
542 if (fd < 0) {
543 ret = -1;
544 error("can't access '%s'", subvol);
545 goto out;
548 if (flags)
549 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
550 flags);
552 ret = btrfs_list_get_path_rootid(fd, &top_id);
553 if (ret)
554 goto out;
556 if (is_list_all)
557 btrfs_list_setup_filter(&filter_set,
558 BTRFS_LIST_FILTER_FULL_PATH,
559 top_id);
560 else if (is_only_in_path)
561 btrfs_list_setup_filter(&filter_set,
562 BTRFS_LIST_FILTER_TOPID_EQUAL,
563 top_id);
565 /* by default we shall print the following columns*/
566 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
567 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
568 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
569 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
571 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
572 layout, !is_list_all && !is_only_in_path, NULL);
574 out:
575 close_file_or_dir(fd, dirstream);
576 if (filter_set)
577 free(filter_set);
578 if (comparer_set)
579 free(comparer_set);
580 if (uerr)
581 usage(cmd_subvol_list_usage);
582 return !!ret;
585 static const char * const cmd_subvol_snapshot_usage[] = {
586 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
587 "Create a snapshot of the subvolume",
588 "Create a writable/readonly snapshot of the subvolume <source> with",
589 "the name <name> in the <dest> directory. If only <dest> is given,",
590 "the subvolume will be named the basename of <source>.",
592 "-r create a readonly snapshot",
593 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
594 " option can be given multiple times.",
595 NULL
598 static int cmd_subvol_snapshot(int argc, char **argv)
600 char *subvol, *dst;
601 int res, retval;
602 int fd = -1, fddst = -1;
603 int len, readonly = 0;
604 char *dupname = NULL;
605 char *dupdir = NULL;
606 char *newname;
607 char *dstdir;
608 struct btrfs_ioctl_vol_args_v2 args;
609 struct btrfs_qgroup_inherit *inherit = NULL;
610 DIR *dirstream1 = NULL, *dirstream2 = NULL;
612 memset(&args, 0, sizeof(args));
613 while (1) {
614 int c = getopt(argc, argv, "c:i:r");
615 if (c < 0)
616 break;
618 switch (c) {
619 case 'c':
620 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
621 if (res) {
622 retval = res;
623 goto out;
625 break;
626 case 'i':
627 res = qgroup_inherit_add_group(&inherit, optarg);
628 if (res) {
629 retval = res;
630 goto out;
632 break;
633 case 'r':
634 readonly = 1;
635 break;
636 case 'x':
637 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
638 if (res) {
639 retval = res;
640 goto out;
642 break;
643 default:
644 usage(cmd_subvol_snapshot_usage);
648 if (check_argc_exact(argc - optind, 2))
649 usage(cmd_subvol_snapshot_usage);
651 subvol = argv[optind];
652 dst = argv[optind + 1];
654 retval = 1; /* failure */
655 res = test_issubvolume(subvol);
656 if (res < 0) {
657 error("cannot access subvolume %s: %s", subvol, strerror(-res));
658 goto out;
660 if (!res) {
661 error("not a subvolume: %s", subvol);
662 goto out;
665 res = test_isdir(dst);
666 if (res < 0 && res != -ENOENT) {
667 error("cannot access %s: %s", dst, strerror(-res));
668 goto out;
670 if (res == 0) {
671 error("'%s' exists and it is not a directory", dst);
672 goto out;
675 if (res > 0) {
676 dupname = strdup(subvol);
677 newname = basename(dupname);
678 dstdir = dst;
679 } else {
680 dupname = strdup(dst);
681 newname = basename(dupname);
682 dupdir = strdup(dst);
683 dstdir = dirname(dupdir);
686 if (!test_issubvolname(newname)) {
687 error("invalid snapshot name '%s'", newname);
688 goto out;
691 len = strlen(newname);
692 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
693 error("snapshot name too long '%s'", newname);
694 goto out;
697 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
698 if (fddst < 0)
699 goto out;
701 fd = btrfs_open_dir(subvol, &dirstream2, 1);
702 if (fd < 0)
703 goto out;
705 if (readonly) {
706 args.flags |= BTRFS_SUBVOL_RDONLY;
707 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
708 subvol, dstdir, newname);
709 } else {
710 printf("Create a snapshot of '%s' in '%s/%s'\n",
711 subvol, dstdir, newname);
714 args.fd = fd;
715 if (inherit) {
716 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
717 args.size = qgroup_inherit_size(inherit);
718 args.qgroup_inherit = inherit;
720 strncpy_null(args.name, newname);
722 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
724 if (res < 0) {
725 error("cannot snapshot '%s': %s", subvol, strerror(errno));
726 goto out;
729 retval = 0; /* success */
731 out:
732 close_file_or_dir(fddst, dirstream1);
733 close_file_or_dir(fd, dirstream2);
734 free(inherit);
735 free(dupname);
736 free(dupdir);
738 return retval;
741 static const char * const cmd_subvol_get_default_usage[] = {
742 "btrfs subvolume get-default <path>",
743 "Get the default subvolume of a filesystem",
744 NULL
747 static int cmd_subvol_get_default(int argc, char **argv)
749 int fd = -1;
750 int ret;
751 char *subvol;
752 struct btrfs_list_filter_set *filter_set;
753 u64 default_id;
754 DIR *dirstream = NULL;
756 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
758 if (check_argc_exact(argc - optind, 1))
759 usage(cmd_subvol_get_default_usage);
761 subvol = argv[1];
762 fd = btrfs_open_dir(subvol, &dirstream, 1);
763 if (fd < 0)
764 return 1;
766 ret = btrfs_list_get_default_subvolume(fd, &default_id);
767 if (ret) {
768 error("failed to look up default subvolume: %s",
769 strerror(errno));
770 goto out;
773 ret = 1;
774 if (default_id == 0) {
775 error("'default' dir item not found");
776 goto out;
779 /* no need to resolve roots if FS_TREE is default */
780 if (default_id == BTRFS_FS_TREE_OBJECTID) {
781 printf("ID 5 (FS_TREE)\n");
782 ret = 0;
783 goto out;
786 filter_set = btrfs_list_alloc_filter_set();
787 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
788 default_id);
790 /* by default we shall print the following columns*/
791 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
792 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
793 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
794 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
796 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
797 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
799 if (filter_set)
800 free(filter_set);
801 out:
802 close_file_or_dir(fd, dirstream);
803 return !!ret;
806 static const char * const cmd_subvol_set_default_usage[] = {
807 "btrfs subvolume set-default <subvolid> <path>",
808 "Set the default subvolume of a filesystem",
809 NULL
812 static int cmd_subvol_set_default(int argc, char **argv)
814 int ret=0, fd, e;
815 u64 objectid;
816 char *path;
817 char *subvolid;
818 DIR *dirstream = NULL;
820 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
822 if (check_argc_exact(argc - optind, 2))
823 usage(cmd_subvol_set_default_usage);
825 subvolid = argv[optind];
826 path = argv[optind + 1];
828 objectid = arg_strtou64(subvolid);
830 fd = btrfs_open_dir(path, &dirstream, 1);
831 if (fd < 0)
832 return 1;
834 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
835 e = errno;
836 close_file_or_dir(fd, dirstream);
837 if (ret < 0) {
838 error("unable to set a new default subvolume: %s",
839 strerror(e));
840 return 1;
842 return 0;
845 static const char * const cmd_subvol_find_new_usage[] = {
846 "btrfs subvolume find-new <path> <lastgen>",
847 "List the recently modified files in a filesystem",
848 NULL
851 static int cmd_subvol_find_new(int argc, char **argv)
853 int fd;
854 int ret;
855 char *subvol;
856 u64 last_gen;
857 DIR *dirstream = NULL;
859 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
861 if (check_argc_exact(argc - optind, 2))
862 usage(cmd_subvol_find_new_usage);
864 subvol = argv[optind];
865 last_gen = arg_strtou64(argv[optind + 1]);
867 ret = test_issubvolume(subvol);
868 if (ret < 0) {
869 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
870 return 1;
872 if (!ret) {
873 error("not a subvolume: %s", subvol);
874 return 1;
877 fd = btrfs_open_dir(subvol, &dirstream, 1);
878 if (fd < 0)
879 return 1;
881 ret = ioctl(fd, BTRFS_IOC_SYNC);
882 if (ret < 0) {
883 error("sync ioctl failed on '%s': %s",
884 subvol, strerror(errno));
885 close_file_or_dir(fd, dirstream);
886 return 1;
889 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
890 close_file_or_dir(fd, dirstream);
891 return !!ret;
894 static const char * const cmd_subvol_show_usage[] = {
895 "btrfs subvolume show [options] <subvol-path>|<mnt>",
896 "Show more information about the subvolume",
897 "-r|--rootid rootid of the subvolume",
898 "-u|--uuid uuid of the subvolume",
900 "If no option is specified, <subvol-path> will be shown, otherwise",
901 "the rootid or uuid are resolved relative to the <mnt> path.",
902 NULL
905 static int cmd_subvol_show(int argc, char **argv)
907 struct root_info get_ri;
908 struct btrfs_list_filter_set *filter_set = NULL;
909 char tstr[256];
910 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
911 char *fullpath = NULL;
912 char raw_prefix[] = "\t\t\t\t";
913 int fd = -1;
914 int ret = 1;
915 DIR *dirstream1 = NULL;
916 int by_rootid = 0;
917 int by_uuid = 0;
918 u64 rootid_arg;
919 u8 uuid_arg[BTRFS_UUID_SIZE];
921 while (1) {
922 int c;
923 static const struct option long_options[] = {
924 { "rootid", required_argument, NULL, 'r'},
925 { "uuid", required_argument, NULL, 'u'},
926 { NULL, 0, NULL, 0 }
929 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
930 if (c < 0)
931 break;
933 switch (c) {
934 case 'r':
935 rootid_arg = arg_strtou64(optarg);
936 by_rootid = 1;
937 break;
938 case 'u':
939 uuid_parse(optarg, uuid_arg);
940 by_uuid = 1;
941 break;
942 default:
943 usage(cmd_subvol_show_usage);
947 if (check_argc_exact(argc - optind, 1))
948 usage(cmd_subvol_show_usage);
950 if (by_rootid && by_uuid) {
951 error(
952 "options --rootid and --uuid cannot be used at the same time");
953 usage(cmd_subvol_show_usage);
956 memset(&get_ri, 0, sizeof(get_ri));
957 fullpath = realpath(argv[optind], NULL);
958 if (!fullpath) {
959 error("cannot find real path for '%s': %s",
960 argv[optind], strerror(errno));
961 goto out;
964 if (by_rootid) {
965 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
966 } else if (by_uuid) {
967 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
968 } else {
969 ret = get_subvol_info(fullpath, &get_ri);
972 if (ret) {
973 if (ret < 0) {
974 error("Failed to get subvol info %s: %s",
975 fullpath, strerror(-ret));
976 } else {
977 error("Failed to get subvol info %s: %d",
978 fullpath, ret);
980 return ret;
983 /* print the info */
984 printf("%s\n", get_ri.full_path);
985 printf("\tName: \t\t\t%s\n", get_ri.name);
987 if (uuid_is_null(get_ri.uuid))
988 strcpy(uuidparse, "-");
989 else
990 uuid_unparse(get_ri.uuid, uuidparse);
991 printf("\tUUID: \t\t\t%s\n", uuidparse);
993 if (uuid_is_null(get_ri.puuid))
994 strcpy(uuidparse, "-");
995 else
996 uuid_unparse(get_ri.puuid, uuidparse);
997 printf("\tParent UUID: \t\t%s\n", uuidparse);
999 if (uuid_is_null(get_ri.ruuid))
1000 strcpy(uuidparse, "-");
1001 else
1002 uuid_unparse(get_ri.ruuid, uuidparse);
1003 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1005 if (get_ri.otime) {
1006 struct tm tm;
1008 localtime_r(&get_ri.otime, &tm);
1009 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1010 } else
1011 strcpy(tstr, "-");
1012 printf("\tCreation time: \t\t%s\n", tstr);
1014 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1015 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1016 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1017 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1018 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1020 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1021 printf("\tFlags: \t\t\treadonly\n");
1022 else
1023 printf("\tFlags: \t\t\t-\n");
1025 /* print the snapshots of the given subvol if any*/
1026 printf("\tSnapshot(s):\n");
1027 filter_set = btrfs_list_alloc_filter_set();
1028 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1029 (u64)(unsigned long)get_ri.uuid);
1030 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1032 fd = open_file_or_dir(fullpath, &dirstream1);
1033 if (fd < 0) {
1034 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1035 goto out;
1037 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1038 1, raw_prefix);
1040 out:
1041 /* clean up */
1042 free(get_ri.path);
1043 free(get_ri.name);
1044 free(get_ri.full_path);
1045 free(filter_set);
1047 close_file_or_dir(fd, dirstream1);
1048 free(fullpath);
1049 return !!ret;
1052 static const char * const cmd_subvol_sync_usage[] = {
1053 "btrfs subvolume sync <path> [<subvol-id>...]",
1054 "Wait until given subvolume(s) are completely removed from the filesystem.",
1055 "Wait until given subvolume(s) are completely removed from the filesystem",
1056 "after deletion.",
1057 "If no subvolume id is given, wait until all current deletion requests",
1058 "are completed, but do not wait for subvolumes deleted meanwhile.",
1059 "The status of subvolume ids is checked periodically.",
1061 "-s <N> sleep N seconds between checks (default: 1)",
1062 NULL
1065 #if 0
1067 * If we're looking for any dead subvolume, take a shortcut and look
1068 * for any ORPHAN_ITEMs in the tree root
1070 static int fs_has_dead_subvolumes(int fd)
1072 int ret;
1073 struct btrfs_ioctl_search_args args;
1074 struct btrfs_ioctl_search_key *sk = &args.key;
1075 struct btrfs_ioctl_search_header sh;
1076 u64 min_subvolid = 0;
1078 again:
1079 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1080 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1081 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1082 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1083 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1084 sk->min_offset = min_subvolid;
1085 sk->max_offset = (u64)-1;
1086 sk->min_transid = 0;
1087 sk->max_transid = (u64)-1;
1088 sk->nr_items = 1;
1090 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1091 if (ret < 0)
1092 return -errno;
1094 if (!sk->nr_items)
1095 return 0;
1097 memcpy(&sh, args.buf, sizeof(sh));
1098 min_subvolid = sh.offset;
1101 * Verify that the root item is really there and we haven't hit
1102 * a stale orphan
1104 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1105 sk->min_objectid = min_subvolid;
1106 sk->max_objectid = min_subvolid;
1107 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1108 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1109 sk->min_offset = 0;
1110 sk->max_offset = (u64)-1;
1111 sk->min_transid = 0;
1112 sk->max_transid = (u64)-1;
1113 sk->nr_items = 1;
1115 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1116 if (ret < 0)
1117 return -errno;
1120 * Stale orphan, try the next one
1122 if (!sk->nr_items) {
1123 min_subvolid++;
1124 goto again;
1127 return 1;
1129 #endif
1131 #define SUBVOL_ID_BATCH 1024
1134 * Enumerate all dead subvolumes that exist in the filesystem.
1135 * Fill @ids and reallocate to bigger size if needed.
1137 static int enumerate_dead_subvols(int fd, u64 **ids)
1139 int ret;
1140 struct btrfs_ioctl_search_args args;
1141 struct btrfs_ioctl_search_key *sk = &args.key;
1142 int idx = 0;
1143 int count = 0;
1145 memset(&args, 0, sizeof(args));
1147 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1148 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1149 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1150 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1151 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1152 sk->min_offset = 0;
1153 sk->max_offset = (u64)-1;
1154 sk->min_transid = 0;
1155 sk->max_transid = (u64)-1;
1156 sk->nr_items = 4096;
1158 *ids = NULL;
1159 while (1) {
1160 struct btrfs_ioctl_search_header *sh;
1161 unsigned long off;
1162 int i;
1164 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1165 if (ret < 0)
1166 return -errno;
1168 if (!sk->nr_items)
1169 return idx;
1171 off = 0;
1172 for (i = 0; i < sk->nr_items; i++) {
1173 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1174 off += sizeof(*sh);
1176 if (btrfs_search_header_type(sh)
1177 == BTRFS_ORPHAN_ITEM_KEY) {
1178 if (idx >= count) {
1179 u64 *newids;
1181 count += SUBVOL_ID_BATCH;
1182 newids = (u64*)realloc(*ids,
1183 count * sizeof(u64));
1184 if (!newids)
1185 return -ENOMEM;
1186 *ids = newids;
1188 (*ids)[idx] = btrfs_search_header_offset(sh);
1189 idx++;
1191 off += btrfs_search_header_len(sh);
1193 sk->min_objectid = btrfs_search_header_objectid(sh);
1194 sk->min_type = btrfs_search_header_type(sh);
1195 sk->min_offset = btrfs_search_header_offset(sh);
1197 if (sk->min_offset < (u64)-1)
1198 sk->min_offset++;
1199 else
1200 break;
1201 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1202 break;
1203 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1204 break;
1207 return idx;
1210 static int cmd_subvol_sync(int argc, char **argv)
1212 int fd = -1;
1213 int i;
1214 int ret = 1;
1215 DIR *dirstream = NULL;
1216 u64 *ids = NULL;
1217 int id_count;
1218 int sleep_interval = 1;
1220 while (1) {
1221 int c = getopt(argc, argv, "s:");
1223 if (c < 0)
1224 break;
1226 switch (c) {
1227 case 's':
1228 sleep_interval = atoi(optarg);
1229 if (sleep_interval < 1) {
1230 error("invalid sleep interval %s", optarg);
1231 ret = 1;
1232 goto out;
1234 break;
1235 default:
1236 usage(cmd_subvol_sync_usage);
1240 if (check_argc_min(argc - optind, 1))
1241 usage(cmd_subvol_sync_usage);
1243 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1244 if (fd < 0) {
1245 ret = 1;
1246 goto out;
1248 optind++;
1250 id_count = argc - optind;
1251 if (!id_count) {
1252 id_count = enumerate_dead_subvols(fd, &ids);
1253 if (id_count < 0) {
1254 error("can't enumerate dead subvolumes: %s",
1255 strerror(-id_count));
1256 ret = 1;
1257 goto out;
1259 if (id_count == 0) {
1260 ret = 0;
1261 goto out;
1263 } else {
1264 ids = (u64*)malloc(id_count * sizeof(u64));
1265 if (!ids) {
1266 error("not enough memory");
1267 ret = 1;
1268 goto out;
1271 for (i = 0; i < id_count; i++) {
1272 u64 id;
1273 const char *arg;
1275 arg = argv[optind + i];
1276 errno = 0;
1277 id = strtoull(arg, NULL, 10);
1278 if (errno < 0) {
1279 error("unrecognized subvolume id %s", arg);
1280 ret = 1;
1281 goto out;
1283 if (id < BTRFS_FIRST_FREE_OBJECTID
1284 || id > BTRFS_LAST_FREE_OBJECTID) {
1285 error("subvolume id %s out of range", arg);
1286 ret = 1;
1287 goto out;
1289 ids[i] = id;
1293 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1295 out:
1296 free(ids);
1297 close_file_or_dir(fd, dirstream);
1299 return !!ret;
1302 static const char subvolume_cmd_group_info[] =
1303 "manage subvolumes: create, delete, list, etc";
1305 const struct cmd_group subvolume_cmd_group = {
1306 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1307 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1308 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1309 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1310 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1311 NULL, 0 },
1312 { "get-default", cmd_subvol_get_default,
1313 cmd_subvol_get_default_usage, NULL, 0 },
1314 { "set-default", cmd_subvol_set_default,
1315 cmd_subvol_set_default_usage, NULL, 0 },
1316 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1317 NULL, 0 },
1318 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1319 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1320 NULL_CMD_STRUCT
1324 int cmd_subvolume(int argc, char **argv)
1326 return handle_command_group(&subvolume_cmd_group, argc, argv);