btrfs-progs: docs: update check options
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob02e1dec18ed2d2afbc26d080c69590433f08b2a9
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;
228 * Test if path is a subvolume
229 * Returns:
230 * 0 - path exists but it is not a subvolume
231 * 1 - path exists and it is a subvolume
232 * < 0 - error
234 int test_issubvolume(const char *path)
236 struct stat st;
237 struct statfs stfs;
238 int res;
240 res = stat(path, &st);
241 if (res < 0)
242 return -errno;
244 if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
245 return 0;
247 res = statfs(path, &stfs);
248 if (res < 0)
249 return -errno;
251 return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
254 static int wait_for_commit(int fd)
256 int ret;
258 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
259 if (ret < 0)
260 return ret;
261 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
264 static const char * const cmd_subvol_delete_usage[] = {
265 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
266 "Delete subvolume(s)",
267 "Delete subvolumes from the filesystem. The corresponding directory",
268 "is removed instantly but the data blocks are removed later.",
269 "The deletion does not involve full commit by default due to",
270 "performance reasons (as a consequence, the subvolume may appear again",
271 "after a crash). Use one of the --commit options to wait until the",
272 "operation is safely stored on the media.",
274 "-c|--commit-after wait for transaction commit at the end of the operation",
275 "-C|--commit-each wait for transaction commit after deleting each subvolume",
276 NULL
279 static int cmd_subvol_delete(int argc, char **argv)
281 int res, ret = 0;
282 int cnt;
283 int fd = -1;
284 struct btrfs_ioctl_vol_args args;
285 char *dname, *vname, *cpath;
286 char *dupdname = NULL;
287 char *dupvname = NULL;
288 char *path;
289 DIR *dirstream = NULL;
290 int verbose = 0;
291 int commit_mode = 0;
293 optind = 1;
294 while (1) {
295 int c;
296 static const struct option long_options[] = {
297 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
298 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
299 {NULL, 0, NULL, 0}
302 c = getopt_long(argc, argv, "cC", long_options, NULL);
303 if (c < 0)
304 break;
306 switch(c) {
307 case 'c':
308 commit_mode = 1;
309 break;
310 case 'C':
311 commit_mode = 2;
312 break;
313 case 'v':
314 verbose++;
315 break;
316 default:
317 usage(cmd_subvol_delete_usage);
321 if (check_argc_min(argc - optind, 1))
322 usage(cmd_subvol_delete_usage);
324 if (verbose > 0) {
325 printf("Transaction commit: %s\n",
326 !commit_mode ? "none (default)" :
327 commit_mode == 1 ? "at the end" : "after each");
330 cnt = optind;
332 again:
333 path = argv[cnt];
335 res = test_issubvolume(path);
336 if (res < 0) {
337 error("cannot access subvolume %s: %s", path, strerror(-res));
338 ret = 1;
339 goto out;
341 if (!res) {
342 error("not a subvolume: %s", path);
343 ret = 1;
344 goto out;
347 cpath = realpath(path, NULL);
348 if (!cpath) {
349 ret = errno;
350 error("cannot find real path for '%s': %s",
351 path, strerror(errno));
352 goto out;
354 dupdname = strdup(cpath);
355 dname = dirname(dupdname);
356 dupvname = strdup(cpath);
357 vname = basename(dupvname);
358 free(cpath);
360 fd = btrfs_open_dir(dname, &dirstream, 1);
361 if (fd < 0) {
362 ret = 1;
363 goto out;
366 printf("Delete subvolume (%s): '%s/%s'\n",
367 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
368 ? "commit" : "no-commit", dname, vname);
369 memset(&args, 0, sizeof(args));
370 strncpy_null(args.name, vname);
371 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
372 if(res < 0 ){
373 error("cannot delete '%s/%s': %s", dname, vname,
374 strerror(errno));
375 ret = 1;
376 goto out;
379 if (commit_mode == 1) {
380 res = wait_for_commit(fd);
381 if (res < 0) {
382 error("unable to wait for commit after '%s': %s",
383 path, strerror(errno));
384 ret = 1;
388 out:
389 free(dupdname);
390 free(dupvname);
391 dupdname = NULL;
392 dupvname = NULL;
393 cnt++;
394 if (cnt < argc) {
395 close_file_or_dir(fd, dirstream);
396 /* avoid double free */
397 fd = -1;
398 dirstream = NULL;
399 goto again;
402 if (commit_mode == 2 && fd != -1) {
403 res = wait_for_commit(fd);
404 if (res < 0) {
405 error("unable to do final sync after deletion: %s",
406 strerror(errno));
407 ret = 1;
410 close_file_or_dir(fd, dirstream);
412 return ret;
416 * Naming of options:
417 * - uppercase for filters and sort options
418 * - lowercase for enabling specific items in the output
420 static const char * const cmd_subvol_list_usage[] = {
421 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
422 "[--sort=gen,ogen,rootid,path] <path>",
423 "List subvolumes (and snapshots)",
425 "-p print parent ID",
426 "-a print all the subvolumes in the filesystem and",
427 " distinguish absolute and relative path with respect",
428 " to the given <path>",
429 "-c print the ogeneration of the subvolume",
430 "-g print the generation of the subvolume",
431 "-o print only subvolumes below specified path",
432 "-u print the uuid of subvolumes (and snapshots)",
433 "-q print the parent uuid of the snapshots",
434 "-R print the uuid of the received snapshots",
435 "-t print the result as a table",
436 "-s list snapshots only in the filesystem",
437 "-r list readonly subvolumes (including snapshots)",
438 "-d list deleted subvolumes that are not yet cleaned",
439 "-G [+|-]value",
440 " filter the subvolumes by generation",
441 " (+value: >= value; -value: <= value; value: = value)",
442 "-C [+|-]value",
443 " filter the subvolumes by ogeneration",
444 " (+value: >= value; -value: <= value; value: = value)",
445 "--sort=gen,ogen,rootid,path",
446 " list the subvolume in order of gen, ogen, rootid or path",
447 " you also can add '+' or '-' in front of each items.",
448 " (+:ascending, -:descending, ascending default)",
449 NULL,
452 static int cmd_subvol_list(int argc, char **argv)
454 struct btrfs_list_filter_set *filter_set;
455 struct btrfs_list_comparer_set *comparer_set;
456 u64 flags = 0;
457 int fd = -1;
458 u64 top_id;
459 int ret = -1, uerr = 0;
460 char *subvol;
461 int is_tab_result = 0;
462 int is_list_all = 0;
463 int is_only_in_path = 0;
464 DIR *dirstream = NULL;
466 filter_set = btrfs_list_alloc_filter_set();
467 comparer_set = btrfs_list_alloc_comparer_set();
469 optind = 1;
470 while(1) {
471 int c;
472 static const struct option long_options[] = {
473 {"sort", required_argument, NULL, 'S'},
474 {NULL, 0, NULL, 0}
477 c = getopt_long(argc, argv,
478 "acdgopqsurRG:C:t", long_options, NULL);
479 if (c < 0)
480 break;
482 switch(c) {
483 case 'p':
484 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
485 break;
486 case 'a':
487 is_list_all = 1;
488 break;
489 case 'c':
490 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
491 break;
492 case 'd':
493 btrfs_list_setup_filter(&filter_set,
494 BTRFS_LIST_FILTER_DELETED,
496 break;
497 case 'g':
498 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
499 break;
500 case 'o':
501 is_only_in_path = 1;
502 break;
503 case 't':
504 is_tab_result = 1;
505 break;
506 case 's':
507 btrfs_list_setup_filter(&filter_set,
508 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
510 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
511 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
512 break;
513 case 'u':
514 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
515 break;
516 case 'q':
517 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
518 break;
519 case 'R':
520 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
521 break;
522 case 'r':
523 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
524 break;
525 case 'G':
526 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
527 ret = btrfs_list_parse_filter_string(optarg,
528 &filter_set,
529 BTRFS_LIST_FILTER_GEN);
530 if (ret) {
531 uerr = 1;
532 goto out;
534 break;
536 case 'C':
537 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
538 ret = btrfs_list_parse_filter_string(optarg,
539 &filter_set,
540 BTRFS_LIST_FILTER_CGEN);
541 if (ret) {
542 uerr = 1;
543 goto out;
545 break;
546 case 'S':
547 ret = btrfs_list_parse_sort_string(optarg,
548 &comparer_set);
549 if (ret) {
550 uerr = 1;
551 goto out;
553 break;
555 default:
556 uerr = 1;
557 goto out;
561 if (flags)
562 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
563 flags);
565 if (check_argc_exact(argc - optind, 1)) {
566 uerr = 1;
567 goto out;
570 subvol = argv[optind];
571 fd = btrfs_open_dir(subvol, &dirstream, 1);
572 if (fd < 0) {
573 ret = -1;
574 error("can't access '%s'", subvol);
575 goto out;
578 ret = btrfs_list_get_path_rootid(fd, &top_id);
579 if (ret) {
580 error("can't get rootid for '%s'", subvol);
581 goto out;
584 if (is_list_all)
585 btrfs_list_setup_filter(&filter_set,
586 BTRFS_LIST_FILTER_FULL_PATH,
587 top_id);
588 else if (is_only_in_path)
589 btrfs_list_setup_filter(&filter_set,
590 BTRFS_LIST_FILTER_TOPID_EQUAL,
591 top_id);
593 /* by default we shall print the following columns*/
594 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
595 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
596 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
597 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
599 if (is_tab_result)
600 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
601 BTRFS_LIST_LAYOUT_TABLE,
602 !is_list_all && !is_only_in_path, NULL);
603 else
604 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
605 BTRFS_LIST_LAYOUT_DEFAULT,
606 !is_list_all && !is_only_in_path, NULL);
608 out:
609 close_file_or_dir(fd, dirstream);
610 if (filter_set)
611 btrfs_list_free_filter_set(filter_set);
612 if (comparer_set)
613 btrfs_list_free_comparer_set(comparer_set);
614 if (uerr)
615 usage(cmd_subvol_list_usage);
616 return !!ret;
619 static const char * const cmd_subvol_snapshot_usage[] = {
620 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
621 "Create a snapshot of the subvolume",
622 "Create a writable/readonly snapshot of the subvolume <source> with",
623 "the name <name> in the <dest> directory. If only <dest> is given,",
624 "the subvolume will be named the basename of <source>.",
626 "-r create a readonly snapshot",
627 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
628 " option can be given multiple times.",
629 NULL
632 static int cmd_subvol_snapshot(int argc, char **argv)
634 char *subvol, *dst;
635 int res, retval;
636 int fd = -1, fddst = -1;
637 int len, readonly = 0;
638 char *dupname = NULL;
639 char *dupdir = NULL;
640 char *newname;
641 char *dstdir;
642 struct btrfs_ioctl_vol_args_v2 args;
643 struct btrfs_qgroup_inherit *inherit = NULL;
644 DIR *dirstream1 = NULL, *dirstream2 = NULL;
646 optind = 1;
647 memset(&args, 0, sizeof(args));
648 while (1) {
649 int c = getopt(argc, argv, "c:i:r");
650 if (c < 0)
651 break;
653 switch (c) {
654 case 'c':
655 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
656 if (res) {
657 retval = res;
658 goto out;
660 break;
661 case 'i':
662 res = qgroup_inherit_add_group(&inherit, optarg);
663 if (res) {
664 retval = res;
665 goto out;
667 break;
668 case 'r':
669 readonly = 1;
670 break;
671 case 'x':
672 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
673 if (res) {
674 retval = res;
675 goto out;
677 break;
678 default:
679 usage(cmd_subvol_snapshot_usage);
683 if (check_argc_exact(argc - optind, 2))
684 usage(cmd_subvol_snapshot_usage);
686 subvol = argv[optind];
687 dst = argv[optind + 1];
689 retval = 1; /* failure */
690 res = test_issubvolume(subvol);
691 if (res < 0) {
692 error("cannot access subvolume %s: %s", subvol, strerror(-res));
693 goto out;
695 if (!res) {
696 error("not a subvolume: %s", subvol);
697 goto out;
700 res = test_isdir(dst);
701 if (res < 0 && res != -ENOENT) {
702 error("cannot access %s: %s", dst, strerror(-res));
703 goto out;
705 if (res == 0) {
706 error("'%s' exists and it is not a directory", dst);
707 goto out;
710 if (res > 0) {
711 dupname = strdup(subvol);
712 newname = basename(dupname);
713 dstdir = dst;
714 } else {
715 dupname = strdup(dst);
716 newname = basename(dupname);
717 dupdir = strdup(dst);
718 dstdir = dirname(dupdir);
721 if (!test_issubvolname(newname)) {
722 error("invalid snapshot name '%s'", newname);
723 goto out;
726 len = strlen(newname);
727 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
728 error("snapshot name too long '%s'", newname);
729 goto out;
732 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
733 if (fddst < 0)
734 goto out;
736 fd = btrfs_open_dir(subvol, &dirstream2, 1);
737 if (fd < 0)
738 goto out;
740 if (readonly) {
741 args.flags |= BTRFS_SUBVOL_RDONLY;
742 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
743 subvol, dstdir, newname);
744 } else {
745 printf("Create a snapshot of '%s' in '%s/%s'\n",
746 subvol, dstdir, newname);
749 args.fd = fd;
750 if (inherit) {
751 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
752 args.size = qgroup_inherit_size(inherit);
753 args.qgroup_inherit = inherit;
755 strncpy_null(args.name, newname);
757 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
759 if (res < 0) {
760 error("cannot snapshot '%s': %s", subvol, strerror(errno));
761 goto out;
764 retval = 0; /* success */
766 out:
767 close_file_or_dir(fddst, dirstream1);
768 close_file_or_dir(fd, dirstream2);
769 free(inherit);
770 free(dupname);
771 free(dupdir);
773 return retval;
776 static const char * const cmd_subvol_get_default_usage[] = {
777 "btrfs subvolume get-default <path>",
778 "Get the default subvolume of a filesystem",
779 NULL
782 static int cmd_subvol_get_default(int argc, char **argv)
784 int fd = -1;
785 int ret;
786 char *subvol;
787 struct btrfs_list_filter_set *filter_set;
788 u64 default_id;
789 DIR *dirstream = NULL;
791 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
793 if (check_argc_exact(argc - optind, 2))
794 usage(cmd_subvol_get_default_usage);
796 subvol = argv[1];
797 fd = btrfs_open_dir(subvol, &dirstream, 1);
798 if (fd < 0)
799 return 1;
801 ret = btrfs_list_get_default_subvolume(fd, &default_id);
802 if (ret) {
803 error("failed to look up default subvolume: %s",
804 strerror(errno));
805 goto out;
808 ret = 1;
809 if (default_id == 0) {
810 error("'default' dir item not found");
811 goto out;
814 /* no need to resolve roots if FS_TREE is default */
815 if (default_id == BTRFS_FS_TREE_OBJECTID) {
816 printf("ID 5 (FS_TREE)\n");
817 ret = 0;
818 goto out;
821 filter_set = btrfs_list_alloc_filter_set();
822 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
823 default_id);
825 /* by default we shall print the following columns*/
826 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
827 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
828 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
829 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
831 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
832 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
834 if (filter_set)
835 btrfs_list_free_filter_set(filter_set);
836 out:
837 close_file_or_dir(fd, dirstream);
838 return !!ret;
841 static const char * const cmd_subvol_set_default_usage[] = {
842 "btrfs subvolume set-default <subvolid> <path>",
843 "Set the default subvolume of a filesystem",
844 NULL
847 static int cmd_subvol_set_default(int argc, char **argv)
849 int ret=0, fd, e;
850 u64 objectid;
851 char *path;
852 char *subvolid;
853 DIR *dirstream = NULL;
855 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
857 if (check_argc_exact(argc - optind, 2))
858 usage(cmd_subvol_set_default_usage);
860 subvolid = argv[optind];
861 path = argv[optind + 1];
863 objectid = arg_strtou64(subvolid);
865 fd = btrfs_open_dir(path, &dirstream, 1);
866 if (fd < 0)
867 return 1;
869 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
870 e = errno;
871 close_file_or_dir(fd, dirstream);
872 if (ret < 0) {
873 error("unable to set a new default subvolume: %s",
874 strerror(e));
875 return 1;
877 return 0;
880 static const char * const cmd_subvol_find_new_usage[] = {
881 "btrfs subvolume find-new <path> <lastgen>",
882 "List the recently modified files in a filesystem",
883 NULL
886 static int cmd_subvol_find_new(int argc, char **argv)
888 int fd;
889 int ret;
890 char *subvol;
891 u64 last_gen;
892 DIR *dirstream = NULL;
894 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
896 if (check_argc_exact(argc - optind, 2))
897 usage(cmd_subvol_find_new_usage);
899 subvol = argv[optind];
900 last_gen = arg_strtou64(argv[optind + 1]);
902 ret = test_issubvolume(subvol);
903 if (ret < 0) {
904 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
905 return 1;
907 if (!ret) {
908 error("not a subvolume: %s", subvol);
909 return 1;
912 fd = btrfs_open_dir(subvol, &dirstream, 1);
913 if (fd < 0)
914 return 1;
916 ret = ioctl(fd, BTRFS_IOC_SYNC);
917 if (ret < 0) {
918 error("sync ioctl failed on '%s': %s",
919 subvol, strerror(errno));
920 close_file_or_dir(fd, dirstream);
921 return 1;
924 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
925 close_file_or_dir(fd, dirstream);
926 return !!ret;
929 static const char * const cmd_subvol_show_usage[] = {
930 "btrfs subvolume show <subvol-path>",
931 "Show more information of the subvolume",
932 NULL
935 static int cmd_subvol_show(int argc, char **argv)
937 struct root_info get_ri;
938 struct btrfs_list_filter_set *filter_set;
939 char tstr[256];
940 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
941 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
942 char raw_prefix[] = "\t\t\t\t";
943 u64 sv_id;
944 int fd = -1, mntfd = -1;
945 int ret = 1;
946 DIR *dirstream1 = NULL, *dirstream2 = NULL;
948 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
950 if (check_argc_exact(argc - optind, 1))
951 usage(cmd_subvol_show_usage);
953 fullpath = realpath(argv[optind], NULL);
954 if (!fullpath) {
955 error("cannot find real path for '%s': %s",
956 argv[optind], strerror(errno));
957 goto out;
960 ret = test_issubvolume(fullpath);
961 if (ret < 0) {
962 error("cannot access subvolume %s: %s", fullpath,
963 strerror(-ret));
964 goto out;
966 if (!ret) {
967 error("not a subvolume: %s", fullpath);
968 ret = 1;
969 goto out;
972 ret = find_mount_root(fullpath, &mnt);
973 if (ret < 0) {
974 error("find_mount_root failed on '%s': %s",
975 fullpath, strerror(-ret));
976 goto out;
978 if (ret > 0) {
979 error("%s doesn't belong to btrfs mount point", fullpath);
980 goto out;
982 ret = 1;
983 svpath = get_subvol_name(mnt, fullpath);
985 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
986 if (fd < 0)
987 goto out;
989 ret = btrfs_list_get_path_rootid(fd, &sv_id);
990 if (ret) {
991 error("can't get rootid for '%s'", fullpath);
992 goto out;
995 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
996 if (mntfd < 0)
997 goto out;
999 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
1000 printf("%s is toplevel subvolume\n", fullpath);
1001 goto out;
1004 memset(&get_ri, 0, sizeof(get_ri));
1005 get_ri.root_id = sv_id;
1007 ret = btrfs_get_subvol(mntfd, &get_ri);
1008 if (ret) {
1009 error("can't find '%s'", svpath);
1010 goto out;
1013 /* print the info */
1014 printf("%s\n", fullpath);
1015 printf("\tName: \t\t\t%s\n", get_ri.name);
1017 if (uuid_is_null(get_ri.uuid))
1018 strcpy(uuidparse, "-");
1019 else
1020 uuid_unparse(get_ri.uuid, uuidparse);
1021 printf("\tUUID: \t\t\t%s\n", uuidparse);
1023 if (uuid_is_null(get_ri.puuid))
1024 strcpy(uuidparse, "-");
1025 else
1026 uuid_unparse(get_ri.puuid, uuidparse);
1027 printf("\tParent UUID: \t\t%s\n", uuidparse);
1029 if (uuid_is_null(get_ri.ruuid))
1030 strcpy(uuidparse, "-");
1031 else
1032 uuid_unparse(get_ri.ruuid, uuidparse);
1033 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1035 if (get_ri.otime) {
1036 struct tm tm;
1038 localtime_r(&get_ri.otime, &tm);
1039 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1040 } else
1041 strcpy(tstr, "-");
1042 printf("\tCreation time: \t\t%s\n", tstr);
1044 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1045 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1046 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1047 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1048 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1050 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1051 printf("\tFlags: \t\t\treadonly\n");
1052 else
1053 printf("\tFlags: \t\t\t-\n");
1055 /* print the snapshots of the given subvol if any*/
1056 printf("\tSnapshot(s):\n");
1057 filter_set = btrfs_list_alloc_filter_set();
1058 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1059 (u64)(unsigned long)get_ri.uuid);
1060 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1061 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1062 1, raw_prefix);
1064 /* clean up */
1065 free(get_ri.path);
1066 free(get_ri.name);
1067 free(get_ri.full_path);
1068 btrfs_list_free_filter_set(filter_set);
1070 out:
1071 close_file_or_dir(fd, dirstream1);
1072 close_file_or_dir(mntfd, dirstream2);
1073 free(mnt);
1074 free(fullpath);
1075 return !!ret;
1078 static const char * const cmd_subvol_sync_usage[] = {
1079 "btrfs subvolume sync <path> [<subvol-id>...]",
1080 "Wait until given subvolume(s) are completely removed from the filesystem.",
1081 "Wait until given subvolume(s) are completely removed from the filesystem",
1082 "after deletion.",
1083 "If no subvolume id is given, wait until all current deletion requests",
1084 "are completed, but do not wait for subvolumes deleted meanwhile.",
1085 "The status of subvolume ids is checked periodically.",
1087 "-s <N> sleep N seconds between checks (default: 1)",
1088 NULL
1091 #if 0
1093 * If we're looking for any dead subvolume, take a shortcut and look
1094 * for any ORPHAN_ITEMs in the tree root
1096 static int fs_has_dead_subvolumes(int fd)
1098 int ret;
1099 struct btrfs_ioctl_search_args args;
1100 struct btrfs_ioctl_search_key *sk = &args.key;
1101 struct btrfs_ioctl_search_header sh;
1102 u64 min_subvolid = 0;
1104 again:
1105 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1106 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1107 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1108 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1109 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1110 sk->min_offset = min_subvolid;
1111 sk->max_offset = (u64)-1;
1112 sk->min_transid = 0;
1113 sk->max_transid = (u64)-1;
1114 sk->nr_items = 1;
1116 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1117 if (ret < 0)
1118 return -errno;
1120 if (!sk->nr_items)
1121 return 0;
1123 memcpy(&sh, args.buf, sizeof(sh));
1124 min_subvolid = sh.offset;
1127 * Verify that the root item is really there and we haven't hit
1128 * a stale orphan
1130 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1131 sk->min_objectid = min_subvolid;
1132 sk->max_objectid = min_subvolid;
1133 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1134 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1135 sk->min_offset = 0;
1136 sk->max_offset = (u64)-1;
1137 sk->min_transid = 0;
1138 sk->max_transid = (u64)-1;
1139 sk->nr_items = 1;
1141 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1142 if (ret < 0)
1143 return -errno;
1146 * Stale orphan, try the next one
1148 if (!sk->nr_items) {
1149 min_subvolid++;
1150 goto again;
1153 return 1;
1155 #endif
1157 #define SUBVOL_ID_BATCH 1024
1160 * Enumerate all dead subvolumes that exist in the filesystem.
1161 * Fill @ids and reallocate to bigger size if needed.
1163 static int enumerate_dead_subvols(int fd, u64 **ids)
1165 int ret;
1166 struct btrfs_ioctl_search_args args;
1167 struct btrfs_ioctl_search_key *sk = &args.key;
1168 int idx = 0;
1169 int count = 0;
1171 memset(&args, 0, sizeof(args));
1173 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1174 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1175 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1176 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1177 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1178 sk->min_offset = 0;
1179 sk->max_offset = (u64)-1;
1180 sk->min_transid = 0;
1181 sk->max_transid = (u64)-1;
1182 sk->nr_items = 4096;
1184 *ids = NULL;
1185 while (1) {
1186 struct btrfs_ioctl_search_header *sh;
1187 unsigned long off;
1188 int i;
1190 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1191 if (ret < 0)
1192 return -errno;
1194 if (!sk->nr_items)
1195 return idx;
1197 off = 0;
1198 for (i = 0; i < sk->nr_items; i++) {
1199 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1200 off += sizeof(*sh);
1202 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1203 if (idx >= count) {
1204 u64 *newids;
1206 count += SUBVOL_ID_BATCH;
1207 newids = (u64*)realloc(*ids, count);
1208 if (!newids)
1209 return -ENOMEM;
1210 *ids = newids;
1212 (*ids)[idx] = sh->offset;
1213 idx++;
1215 off += sh->len;
1217 sk->min_objectid = sh->objectid;
1218 sk->min_type = sh->type;
1219 sk->min_offset = sh->offset;
1221 if (sk->min_offset < (u64)-1)
1222 sk->min_offset++;
1223 else
1224 break;
1225 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1226 break;
1227 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1228 break;
1231 return idx;
1234 static int cmd_subvol_sync(int argc, char **argv)
1236 int fd = -1;
1237 int i;
1238 int ret = 1;
1239 DIR *dirstream = NULL;
1240 u64 *ids = NULL;
1241 int id_count;
1242 int sleep_interval = 1;
1244 optind = 1;
1245 while (1) {
1246 int c = getopt(argc, argv, "s:");
1248 if (c < 0)
1249 break;
1251 switch (c) {
1252 case 's':
1253 sleep_interval = atoi(argv[optind]);
1254 if (sleep_interval < 1) {
1255 error("invalid sleep interval %s",
1256 argv[optind]);
1257 ret = 1;
1258 goto out;
1260 break;
1261 default:
1262 usage(cmd_subvol_sync_usage);
1266 if (check_argc_min(argc - optind, 1))
1267 usage(cmd_subvol_sync_usage);
1269 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1270 if (fd < 0) {
1271 ret = 1;
1272 goto out;
1274 optind++;
1276 id_count = argc - optind;
1277 if (!id_count) {
1278 id_count = enumerate_dead_subvols(fd, &ids);
1279 if (id_count < 0) {
1280 error("can't enumerate dead subvolumes: %s",
1281 strerror(-id_count));
1282 ret = 1;
1283 goto out;
1285 if (id_count == 0) {
1286 ret = 0;
1287 goto out;
1289 } else {
1290 ids = (u64*)malloc(id_count * sizeof(u64));
1291 if (!ids) {
1292 error("not enough memory");
1293 ret = 1;
1294 goto out;
1297 for (i = 0; i < id_count; i++) {
1298 u64 id;
1299 const char *arg;
1301 arg = argv[optind + i];
1302 errno = 0;
1303 id = strtoull(arg, NULL, 10);
1304 if (errno < 0) {
1305 error("unrecognized subvolume id %s", arg);
1306 ret = 1;
1307 goto out;
1309 if (id < BTRFS_FIRST_FREE_OBJECTID
1310 || id > BTRFS_LAST_FREE_OBJECTID) {
1311 error("subvolume id %s out of range\n", arg);
1312 ret = 1;
1313 goto out;
1315 ids[i] = id;
1319 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1321 out:
1322 free(ids);
1323 close_file_or_dir(fd, dirstream);
1325 return !!ret;
1328 static const char subvolume_cmd_group_info[] =
1329 "manage subvolumes: create, delete, list, etc";
1331 const struct cmd_group subvolume_cmd_group = {
1332 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1333 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1334 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1335 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1336 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1337 NULL, 0 },
1338 { "get-default", cmd_subvol_get_default,
1339 cmd_subvol_get_default_usage, NULL, 0 },
1340 { "set-default", cmd_subvol_set_default,
1341 cmd_subvol_set_default_usage, NULL, 0 },
1342 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1343 NULL, 0 },
1344 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1345 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1346 NULL_CMD_STRUCT
1350 int cmd_subvolume(int argc, char **argv)
1352 return handle_command_group(&subvolume_cmd_group, argc, argv);