btrfs-progs: check: Remove ext_ref param from walk_down_tree
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob45363a5ab3f80cb2a04fa6a1af153498094884da
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 <inttypes.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <sys/ioctl.h>
23 #include <errno.h>
24 #include <sys/stat.h>
25 #include <sys/vfs.h>
26 #include <libgen.h>
27 #include <limits.h>
28 #include <getopt.h>
29 #include <uuid/uuid.h>
30 #include <linux/magic.h>
32 #include <btrfsutil.h>
34 #include "kerncompat.h"
35 #include "ioctl.h"
36 #include "qgroup.h"
38 #include "ctree.h"
39 #include "commands.h"
40 #include "utils.h"
41 #include "btrfs-list.h"
42 #include "utils.h"
43 #include "help.h"
45 static int wait_for_subvolume_cleaning(int fd, size_t count, uint64_t *ids,
46 int sleep_interval)
48 size_t i;
49 enum btrfs_util_error err;
51 while (1) {
52 int clean = 1;
54 for (i = 0; i < count; i++) {
55 if (!ids[i])
56 continue;
57 err = btrfs_util_subvolume_info_fd(fd, ids[i], NULL);
58 if (err == BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
59 printf("Subvolume id %" PRIu64 " is gone\n",
60 ids[i]);
61 ids[i] = 0;
62 } else if (err) {
63 error_btrfs_util(err);
64 return -errno;
65 } else {
66 clean = 0;
69 if (clean)
70 break;
71 sleep(sleep_interval);
74 return 0;
77 static const char * const subvolume_cmd_group_usage[] = {
78 "btrfs subvolume <command> <args>",
79 NULL
82 static const char * const cmd_subvol_create_usage[] = {
83 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
84 "Create a subvolume",
85 "Create a subvolume <name> in <dest>. If <dest> is not given",
86 "subvolume <name> will be created in the current directory.",
87 "",
88 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
89 " option can be given multiple times.",
90 NULL
93 static int cmd_subvol_create(int argc, char **argv)
95 int retval, res, len;
96 int fddst = -1;
97 char *dupname = NULL;
98 char *dupdir = NULL;
99 char *newname;
100 char *dstdir;
101 char *dst;
102 struct btrfs_qgroup_inherit *inherit = NULL;
103 DIR *dirstream = NULL;
105 while (1) {
106 int c = getopt(argc, argv, "c:i:");
107 if (c < 0)
108 break;
110 switch (c) {
111 case 'c':
112 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
113 if (res) {
114 retval = res;
115 goto out;
117 break;
118 case 'i':
119 res = qgroup_inherit_add_group(&inherit, optarg);
120 if (res) {
121 retval = res;
122 goto out;
124 break;
125 default:
126 usage(cmd_subvol_create_usage);
130 if (check_argc_exact(argc - optind, 1))
131 usage(cmd_subvol_create_usage);
133 dst = argv[optind];
135 retval = 1; /* failure */
136 res = test_isdir(dst);
137 if (res < 0 && res != -ENOENT) {
138 error("cannot access %s: %s", dst, strerror(-res));
139 goto out;
141 if (res >= 0) {
142 error("target path already exists: %s", dst);
143 goto out;
146 dupname = strdup(dst);
147 newname = basename(dupname);
148 dupdir = strdup(dst);
149 dstdir = dirname(dupdir);
151 if (!test_issubvolname(newname)) {
152 error("invalid subvolume name: %s", newname);
153 goto out;
156 len = strlen(newname);
157 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
158 error("subvolume name too long: %s", newname);
159 goto out;
162 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
163 if (fddst < 0)
164 goto out;
166 printf("Create subvolume '%s/%s'\n", dstdir, newname);
167 if (inherit) {
168 struct btrfs_ioctl_vol_args_v2 args;
170 memset(&args, 0, sizeof(args));
171 strncpy_null(args.name, newname);
172 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
173 args.size = qgroup_inherit_size(inherit);
174 args.qgroup_inherit = inherit;
176 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
177 } else {
178 struct btrfs_ioctl_vol_args args;
180 memset(&args, 0, sizeof(args));
181 strncpy_null(args.name, newname);
183 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
186 if (res < 0) {
187 error("cannot create subvolume: %m");
188 goto out;
191 retval = 0; /* success */
192 out:
193 close_file_or_dir(fddst, dirstream);
194 free(inherit);
195 free(dupname);
196 free(dupdir);
198 return retval;
201 static int wait_for_commit(int fd)
203 enum btrfs_util_error err;
204 uint64_t transid;
206 err = btrfs_util_start_sync_fd(fd, &transid);
207 if (err)
208 return -1;
210 err = btrfs_util_wait_sync_fd(fd, transid);
211 if (err)
212 return -1;
214 return 0;
217 static const char * const cmd_subvol_delete_usage[] = {
218 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
219 "Delete subvolume(s)",
220 "Delete subvolumes from the filesystem. The corresponding directory",
221 "is removed instantly but the data blocks are removed later.",
222 "The deletion does not involve full commit by default due to",
223 "performance reasons (as a consequence, the subvolume may appear again",
224 "after a crash). Use one of the --commit options to wait until the",
225 "operation is safely stored on the media.",
227 "-c|--commit-after wait for transaction commit at the end of the operation",
228 "-C|--commit-each wait for transaction commit after deleting each subvolume",
229 "-v|--verbose verbose output of operations",
230 NULL
233 static int cmd_subvol_delete(int argc, char **argv)
235 int res, ret = 0;
236 int cnt;
237 int fd = -1;
238 char *dname, *vname, *cpath;
239 char *dupdname = NULL;
240 char *dupvname = NULL;
241 char *path;
242 DIR *dirstream = NULL;
243 int verbose = 0;
244 int commit_mode = 0;
245 u8 fsid[BTRFS_FSID_SIZE];
246 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
247 struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
248 enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
249 enum btrfs_util_error err;
251 while (1) {
252 int c;
253 static const struct option long_options[] = {
254 {"commit-after", no_argument, NULL, 'c'},
255 {"commit-each", no_argument, NULL, 'C'},
256 {"verbose", no_argument, NULL, 'v'},
257 {NULL, 0, NULL, 0}
260 c = getopt_long(argc, argv, "cCv", long_options, NULL);
261 if (c < 0)
262 break;
264 switch(c) {
265 case 'c':
266 commit_mode = COMMIT_AFTER;
267 break;
268 case 'C':
269 commit_mode = COMMIT_EACH;
270 break;
271 case 'v':
272 verbose++;
273 break;
274 default:
275 usage(cmd_subvol_delete_usage);
279 if (check_argc_min(argc - optind, 1))
280 usage(cmd_subvol_delete_usage);
282 if (verbose > 0) {
283 printf("Transaction commit: %s\n",
284 !commit_mode ? "none (default)" :
285 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
288 cnt = optind;
290 again:
291 path = argv[cnt];
293 err = btrfs_util_is_subvolume(path);
294 if (err) {
295 error_btrfs_util(err);
296 ret = 1;
297 goto out;
300 cpath = realpath(path, NULL);
301 if (!cpath) {
302 ret = errno;
303 error("cannot find real path for '%s': %m", path);
304 goto out;
306 dupdname = strdup(cpath);
307 dname = dirname(dupdname);
308 dupvname = strdup(cpath);
309 vname = basename(dupvname);
310 free(cpath);
312 fd = btrfs_open_dir(dname, &dirstream, 1);
313 if (fd < 0) {
314 ret = 1;
315 goto out;
318 printf("Delete subvolume (%s): '%s/%s'\n",
319 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
320 ? "commit" : "no-commit", dname, vname);
322 err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
323 if (err) {
324 error_btrfs_util(err);
325 ret = 1;
326 goto out;
329 if (commit_mode == COMMIT_EACH) {
330 res = wait_for_commit(fd);
331 if (res < 0) {
332 error("unable to wait for commit after '%s': %m", path);
333 ret = 1;
335 } else if (commit_mode == COMMIT_AFTER) {
336 res = get_fsid(dname, fsid, 0);
337 if (res < 0) {
338 error("unable to get fsid for '%s': %s",
339 path, strerror(-res));
340 error(
341 "delete succeeded but commit may not be done in the end");
342 ret = 1;
343 goto out;
346 if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) {
347 if (verbose > 0) {
348 uuid_unparse(fsid, uuidbuf);
349 printf(" new fs is found for '%s', fsid: %s\n",
350 path, uuidbuf);
353 * This is the first time a subvolume on this
354 * filesystem is deleted, keep fd in order to issue
355 * SYNC ioctl in the end
357 goto keep_fd;
361 out:
362 close_file_or_dir(fd, dirstream);
363 keep_fd:
364 fd = -1;
365 dirstream = NULL;
366 free(dupdname);
367 free(dupvname);
368 dupdname = NULL;
369 dupvname = NULL;
370 cnt++;
371 if (cnt < argc)
372 goto again;
374 if (commit_mode == COMMIT_AFTER) {
375 int slot;
378 * Traverse seen_fsid_hash and issue SYNC ioctl on each
379 * filesystem
381 for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
382 struct seen_fsid *seen = seen_fsid_hash[slot];
384 while (seen) {
385 res = wait_for_commit(seen->fd);
386 if (res < 0) {
387 uuid_unparse(seen->fsid, uuidbuf);
388 error(
389 "unable to do final sync after deletion: %m, fsid: %s",
390 uuidbuf);
391 ret = 1;
392 } else if (verbose > 0) {
393 uuid_unparse(seen->fsid, uuidbuf);
394 printf("final sync is done for fsid: %s\n",
395 uuidbuf);
397 seen = seen->next;
400 /* fd will also be closed in free_seen_fsid */
401 free_seen_fsid(seen_fsid_hash);
404 return ret;
408 * Naming of options:
409 * - uppercase for filters and sort options
410 * - lowercase for enabling specific items in the output
412 static const char * const cmd_subvol_list_usage[] = {
413 "btrfs subvolume list [options] <path>",
414 "List subvolumes and snapshots in the filesystem.",
416 "Path filtering:",
417 "-o print only subvolumes below specified path",
418 "-a print all the subvolumes in the filesystem and",
419 " distinguish absolute and relative path with respect",
420 " to the given <path>",
422 "Field selection:",
423 "-p print parent ID",
424 "-c print the ogeneration of the subvolume",
425 "-g print the generation of the subvolume",
426 "-u print the uuid of subvolumes (and snapshots)",
427 "-q print the parent uuid of the snapshots",
428 "-R print the uuid of the received snapshots",
430 "Type filtering:",
431 "-s list only snapshots",
432 "-r list readonly subvolumes (including snapshots)",
433 "-d list deleted subvolumes that are not yet cleaned",
435 "Other:",
436 "-t print the result as a table",
438 "Sorting:",
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_list_all = 0;
462 int is_only_in_path = 0;
463 DIR *dirstream = NULL;
464 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
466 filter_set = btrfs_list_alloc_filter_set();
467 comparer_set = btrfs_list_alloc_comparer_set();
469 while(1) {
470 int c;
471 static const struct option long_options[] = {
472 {"sort", required_argument, NULL, 'S'},
473 {NULL, 0, NULL, 0}
476 c = getopt_long(argc, argv,
477 "acdgopqsurRG:C:t", long_options, NULL);
478 if (c < 0)
479 break;
481 switch(c) {
482 case 'p':
483 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
484 break;
485 case 'a':
486 is_list_all = 1;
487 break;
488 case 'c':
489 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
490 break;
491 case 'd':
492 btrfs_list_setup_filter(&filter_set,
493 BTRFS_LIST_FILTER_DELETED,
495 break;
496 case 'g':
497 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
498 break;
499 case 'o':
500 is_only_in_path = 1;
501 break;
502 case 't':
503 layout = BTRFS_LIST_LAYOUT_TABLE;
504 break;
505 case 's':
506 btrfs_list_setup_filter(&filter_set,
507 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
509 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
510 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
511 break;
512 case 'u':
513 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
514 break;
515 case 'q':
516 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
517 break;
518 case 'R':
519 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
520 break;
521 case 'r':
522 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
523 break;
524 case 'G':
525 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
526 ret = btrfs_list_parse_filter_string(optarg,
527 &filter_set,
528 BTRFS_LIST_FILTER_GEN);
529 if (ret) {
530 uerr = 1;
531 goto out;
533 break;
535 case 'C':
536 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
537 ret = btrfs_list_parse_filter_string(optarg,
538 &filter_set,
539 BTRFS_LIST_FILTER_CGEN);
540 if (ret) {
541 uerr = 1;
542 goto out;
544 break;
545 case 'S':
546 ret = btrfs_list_parse_sort_string(optarg,
547 &comparer_set);
548 if (ret) {
549 uerr = 1;
550 goto out;
552 break;
554 default:
555 uerr = 1;
556 goto out;
560 if (check_argc_exact(argc - optind, 1)) {
561 uerr = 1;
562 goto out;
565 subvol = argv[optind];
566 fd = btrfs_open_dir(subvol, &dirstream, 1);
567 if (fd < 0) {
568 ret = -1;
569 error("can't access '%s'", subvol);
570 goto out;
573 if (flags)
574 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
575 flags);
577 ret = btrfs_list_get_path_rootid(fd, &top_id);
578 if (ret)
579 goto out;
581 if (is_list_all)
582 btrfs_list_setup_filter(&filter_set,
583 BTRFS_LIST_FILTER_FULL_PATH,
584 top_id);
585 else if (is_only_in_path)
586 btrfs_list_setup_filter(&filter_set,
587 BTRFS_LIST_FILTER_TOPID_EQUAL,
588 top_id);
590 /* by default we shall print the following columns*/
591 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
592 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
593 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
594 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
596 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
597 layout, !is_list_all && !is_only_in_path, NULL);
599 out:
600 close_file_or_dir(fd, dirstream);
601 if (filter_set)
602 free(filter_set);
603 if (comparer_set)
604 free(comparer_set);
605 if (uerr)
606 usage(cmd_subvol_list_usage);
607 return !!ret;
610 static const char * const cmd_subvol_snapshot_usage[] = {
611 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
612 "Create a snapshot of the subvolume",
613 "Create a writable/readonly snapshot of the subvolume <source> with",
614 "the name <name> in the <dest> directory. If only <dest> is given,",
615 "the subvolume will be named the basename of <source>.",
617 "-r create a readonly snapshot",
618 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
619 " option can be given multiple times.",
620 NULL
623 static int cmd_subvol_snapshot(int argc, char **argv)
625 char *subvol, *dst;
626 int res, retval;
627 int fd = -1, fddst = -1;
628 int len, readonly = 0;
629 char *dupname = NULL;
630 char *dupdir = NULL;
631 char *newname;
632 char *dstdir;
633 enum btrfs_util_error err;
634 struct btrfs_ioctl_vol_args_v2 args;
635 struct btrfs_qgroup_inherit *inherit = NULL;
636 DIR *dirstream1 = NULL, *dirstream2 = NULL;
638 memset(&args, 0, sizeof(args));
639 while (1) {
640 int c = getopt(argc, argv, "c:i:r");
641 if (c < 0)
642 break;
644 switch (c) {
645 case 'c':
646 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
647 if (res) {
648 retval = res;
649 goto out;
651 break;
652 case 'i':
653 res = qgroup_inherit_add_group(&inherit, optarg);
654 if (res) {
655 retval = res;
656 goto out;
658 break;
659 case 'r':
660 readonly = 1;
661 break;
662 case 'x':
663 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
664 if (res) {
665 retval = res;
666 goto out;
668 break;
669 default:
670 usage(cmd_subvol_snapshot_usage);
674 if (check_argc_exact(argc - optind, 2))
675 usage(cmd_subvol_snapshot_usage);
677 subvol = argv[optind];
678 dst = argv[optind + 1];
680 retval = 1; /* failure */
681 err = btrfs_util_is_subvolume(subvol);
682 if (err) {
683 error_btrfs_util(err);
684 goto out;
687 res = test_isdir(dst);
688 if (res < 0 && res != -ENOENT) {
689 error("cannot access %s: %s", dst, strerror(-res));
690 goto out;
692 if (res == 0) {
693 error("'%s' exists and it is not a directory", dst);
694 goto out;
697 if (res > 0) {
698 dupname = strdup(subvol);
699 newname = basename(dupname);
700 dstdir = dst;
701 } else {
702 dupname = strdup(dst);
703 newname = basename(dupname);
704 dupdir = strdup(dst);
705 dstdir = dirname(dupdir);
708 if (!test_issubvolname(newname)) {
709 error("invalid snapshot name '%s'", newname);
710 goto out;
713 len = strlen(newname);
714 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
715 error("snapshot name too long '%s'", newname);
716 goto out;
719 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
720 if (fddst < 0)
721 goto out;
723 fd = btrfs_open_dir(subvol, &dirstream2, 1);
724 if (fd < 0)
725 goto out;
727 if (readonly) {
728 args.flags |= BTRFS_SUBVOL_RDONLY;
729 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
730 subvol, dstdir, newname);
731 } else {
732 printf("Create a snapshot of '%s' in '%s/%s'\n",
733 subvol, dstdir, newname);
736 args.fd = fd;
737 if (inherit) {
738 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
739 args.size = qgroup_inherit_size(inherit);
740 args.qgroup_inherit = inherit;
742 strncpy_null(args.name, newname);
744 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
746 if (res < 0) {
747 error("cannot snapshot '%s': %m", subvol);
748 goto out;
751 retval = 0; /* success */
753 out:
754 close_file_or_dir(fddst, dirstream1);
755 close_file_or_dir(fd, dirstream2);
756 free(inherit);
757 free(dupname);
758 free(dupdir);
760 return retval;
763 static const char * const cmd_subvol_get_default_usage[] = {
764 "btrfs subvolume get-default <path>",
765 "Get the default subvolume of a filesystem",
766 NULL
769 static int cmd_subvol_get_default(int argc, char **argv)
771 int fd = -1;
772 int ret = 1;
773 uint64_t default_id;
774 DIR *dirstream = NULL;
775 enum btrfs_util_error err;
776 struct btrfs_util_subvolume_info subvol;
777 char *path;
779 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
781 if (check_argc_exact(argc - optind, 1))
782 usage(cmd_subvol_get_default_usage);
784 fd = btrfs_open_dir(argv[1], &dirstream, 1);
785 if (fd < 0)
786 return 1;
788 err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
789 if (err) {
790 error_btrfs_util(err);
791 goto out;
794 /* no need to resolve roots if FS_TREE is default */
795 if (default_id == BTRFS_FS_TREE_OBJECTID) {
796 printf("ID 5 (FS_TREE)\n");
797 ret = 0;
798 goto out;
801 err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol);
802 if (err) {
803 error_btrfs_util(err);
804 goto out;
807 err = btrfs_util_subvolume_path_fd(fd, default_id, &path);
808 if (err) {
809 error_btrfs_util(err);
810 goto out;
813 printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
814 subvol.id, subvol.generation, subvol.parent_id, path);
816 free(path);
818 ret = 0;
819 out:
820 close_file_or_dir(fd, dirstream);
821 return ret;
824 static const char * const cmd_subvol_set_default_usage[] = {
825 "btrfs subvolume set-default <subvolume>\n"
826 "btrfs subvolume set-default <subvolid> <path>",
827 "Set the default subvolume of the filesystem mounted as default.",
828 "The subvolume can be specified by its path,",
829 "or the pair of subvolume id and path to the filesystem.",
830 NULL
833 static int cmd_subvol_set_default(int argc, char **argv)
835 u64 objectid;
836 char *path;
837 enum btrfs_util_error err;
839 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
841 if (check_argc_min(argc - optind, 1) ||
842 check_argc_max(argc - optind, 2))
843 usage(cmd_subvol_set_default_usage);
845 if (argc - optind == 1) {
846 /* path to the subvolume is specified */
847 objectid = 0;
848 path = argv[optind];
849 } else {
850 /* subvol id and path to the filesystem are specified */
851 objectid = arg_strtou64(argv[optind]);
852 path = argv[optind + 1];
855 err = btrfs_util_set_default_subvolume(path, objectid);
856 if (err) {
857 error_btrfs_util(err);
858 return 1;
860 return 0;
863 static const char * const cmd_subvol_find_new_usage[] = {
864 "btrfs subvolume find-new <path> <lastgen>",
865 "List the recently modified files in a filesystem",
866 NULL
869 static int cmd_subvol_find_new(int argc, char **argv)
871 int fd;
872 int ret;
873 char *subvol;
874 u64 last_gen;
875 DIR *dirstream = NULL;
876 enum btrfs_util_error err;
878 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
880 if (check_argc_exact(argc - optind, 2))
881 usage(cmd_subvol_find_new_usage);
883 subvol = argv[optind];
884 last_gen = arg_strtou64(argv[optind + 1]);
886 err = btrfs_util_is_subvolume(subvol);
887 if (err) {
888 error_btrfs_util(err);
889 return 1;
892 fd = btrfs_open_dir(subvol, &dirstream, 1);
893 if (fd < 0)
894 return 1;
896 err = btrfs_util_sync_fd(fd);
897 if (err) {
898 error_btrfs_util(err);
899 close_file_or_dir(fd, dirstream);
900 return 1;
903 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
904 close_file_or_dir(fd, dirstream);
905 return !!ret;
908 static const char * const cmd_subvol_show_usage[] = {
909 "btrfs subvolume show [options] <subvol-path>|<mnt>",
910 "Show more information about the subvolume",
911 "-r|--rootid rootid of the subvolume",
912 "-u|--uuid uuid of the subvolume",
914 "If no option is specified, <subvol-path> will be shown, otherwise",
915 "the rootid or uuid are resolved relative to the <mnt> path.",
916 NULL
919 static int cmd_subvol_show(int argc, char **argv)
921 char tstr[256];
922 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
923 char *fullpath = NULL;
924 int fd = -1;
925 int ret = 1;
926 DIR *dirstream1 = NULL;
927 int by_rootid = 0;
928 int by_uuid = 0;
929 u64 rootid_arg = 0;
930 u8 uuid_arg[BTRFS_UUID_SIZE];
931 struct btrfs_util_subvolume_iterator *iter;
932 struct btrfs_util_subvolume_info subvol;
933 char *subvol_path = NULL;
934 enum btrfs_util_error err;
936 while (1) {
937 int c;
938 static const struct option long_options[] = {
939 { "rootid", required_argument, NULL, 'r'},
940 { "uuid", required_argument, NULL, 'u'},
941 { NULL, 0, NULL, 0 }
944 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
945 if (c < 0)
946 break;
948 switch (c) {
949 case 'r':
950 rootid_arg = arg_strtou64(optarg);
951 by_rootid = 1;
952 break;
953 case 'u':
954 uuid_parse(optarg, uuid_arg);
955 by_uuid = 1;
956 break;
957 default:
958 usage(cmd_subvol_show_usage);
962 if (check_argc_exact(argc - optind, 1))
963 usage(cmd_subvol_show_usage);
965 if (by_rootid && by_uuid) {
966 error(
967 "options --rootid and --uuid cannot be used at the same time");
968 usage(cmd_subvol_show_usage);
971 fullpath = realpath(argv[optind], NULL);
972 if (!fullpath) {
973 error("cannot find real path for '%s': %m", argv[optind]);
974 goto out;
977 fd = open_file_or_dir(fullpath, &dirstream1);
978 if (fd < 0) {
979 error("can't access '%s'", fullpath);
980 goto out;
983 if (by_uuid) {
984 err = btrfs_util_create_subvolume_iterator_fd(fd,
985 BTRFS_FS_TREE_OBJECTID,
986 0, &iter);
987 if (err) {
988 error_btrfs_util(err);
989 goto out;
992 for (;;) {
993 err = btrfs_util_subvolume_iterator_next_info(iter,
994 &subvol_path,
995 &subvol);
996 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
997 uuid_unparse(uuid_arg, uuidparse);
998 error("can't find uuid '%s' on '%s'", uuidparse,
999 fullpath);
1000 btrfs_util_destroy_subvolume_iterator(iter);
1001 goto out;
1002 } else if (err) {
1003 error_btrfs_util(err);
1004 btrfs_util_destroy_subvolume_iterator(iter);
1005 goto out;
1008 if (uuid_compare(subvol.uuid, uuid_arg) == 0)
1009 break;
1011 free(subvol_path);
1013 btrfs_util_destroy_subvolume_iterator(iter);
1014 } else {
1016 * If !by_rootid, rootid_arg = 0, which means find the
1017 * subvolume ID of the fd and use that.
1019 err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
1020 if (err) {
1021 error_btrfs_util(err);
1022 goto out;
1025 err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
1026 if (err) {
1027 error_btrfs_util(err);
1028 goto out;
1033 /* print the info */
1034 printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
1035 printf("\tName: \t\t\t%s\n",
1036 (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
1037 basename(subvol_path)));
1039 if (uuid_is_null(subvol.uuid))
1040 strcpy(uuidparse, "-");
1041 else
1042 uuid_unparse(subvol.uuid, uuidparse);
1043 printf("\tUUID: \t\t\t%s\n", uuidparse);
1045 if (uuid_is_null(subvol.parent_uuid))
1046 strcpy(uuidparse, "-");
1047 else
1048 uuid_unparse(subvol.parent_uuid, uuidparse);
1049 printf("\tParent UUID: \t\t%s\n", uuidparse);
1051 if (uuid_is_null(subvol.received_uuid))
1052 strcpy(uuidparse, "-");
1053 else
1054 uuid_unparse(subvol.received_uuid, uuidparse);
1055 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1057 if (subvol.otime.tv_sec) {
1058 struct tm tm;
1060 localtime_r(&subvol.otime.tv_sec, &tm);
1061 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1062 } else
1063 strcpy(tstr, "-");
1064 printf("\tCreation time: \t\t%s\n", tstr);
1066 printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
1067 printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
1068 printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
1069 printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1070 printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1072 if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1073 printf("\tFlags: \t\t\treadonly\n");
1074 else
1075 printf("\tFlags: \t\t\t-\n");
1077 /* print the snapshots of the given subvol if any*/
1078 printf("\tSnapshot(s):\n");
1080 err = btrfs_util_create_subvolume_iterator_fd(fd,
1081 BTRFS_FS_TREE_OBJECTID, 0,
1082 &iter);
1084 for (;;) {
1085 struct btrfs_util_subvolume_info subvol2;
1086 char *path;
1088 err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
1089 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1090 break;
1091 } else if (err) {
1092 error_btrfs_util(err);
1093 btrfs_util_destroy_subvolume_iterator(iter);
1094 goto out;
1097 if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
1098 printf("\t\t\t\t%s\n", path);
1100 free(path);
1102 btrfs_util_destroy_subvolume_iterator(iter);
1104 ret = 0;
1105 out:
1106 free(subvol_path);
1107 close_file_or_dir(fd, dirstream1);
1108 free(fullpath);
1109 return !!ret;
1112 static const char * const cmd_subvol_sync_usage[] = {
1113 "btrfs subvolume sync <path> [<subvol-id>...]",
1114 "Wait until given subvolume(s) are completely removed from the filesystem.",
1115 "Wait until given subvolume(s) are completely removed from the filesystem",
1116 "after deletion.",
1117 "If no subvolume id is given, wait until all current deletion requests",
1118 "are completed, but do not wait for subvolumes deleted meanwhile.",
1119 "The status of subvolume ids is checked periodically.",
1121 "-s <N> sleep N seconds between checks (default: 1)",
1122 NULL
1125 static int cmd_subvol_sync(int argc, char **argv)
1127 int fd = -1;
1128 int ret = 1;
1129 DIR *dirstream = NULL;
1130 uint64_t *ids = NULL;
1131 size_t id_count, i;
1132 int sleep_interval = 1;
1133 enum btrfs_util_error err;
1135 while (1) {
1136 int c = getopt(argc, argv, "s:");
1138 if (c < 0)
1139 break;
1141 switch (c) {
1142 case 's':
1143 sleep_interval = atoi(optarg);
1144 if (sleep_interval < 1) {
1145 error("invalid sleep interval %s", optarg);
1146 ret = 1;
1147 goto out;
1149 break;
1150 default:
1151 usage(cmd_subvol_sync_usage);
1155 if (check_argc_min(argc - optind, 1))
1156 usage(cmd_subvol_sync_usage);
1158 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1159 if (fd < 0) {
1160 ret = 1;
1161 goto out;
1163 optind++;
1165 id_count = argc - optind;
1166 if (!id_count) {
1167 err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count);
1168 if (err) {
1169 error_btrfs_util(err);
1170 ret = 1;
1171 goto out;
1173 if (id_count == 0) {
1174 ret = 0;
1175 goto out;
1177 } else {
1178 ids = malloc(id_count * sizeof(uint64_t));
1179 if (!ids) {
1180 error("not enough memory");
1181 ret = 1;
1182 goto out;
1185 for (i = 0; i < id_count; i++) {
1186 u64 id;
1187 const char *arg;
1189 arg = argv[optind + i];
1190 errno = 0;
1191 id = strtoull(arg, NULL, 10);
1192 if (errno) {
1193 error("unrecognized subvolume id %s", arg);
1194 ret = 1;
1195 goto out;
1197 if (id < BTRFS_FIRST_FREE_OBJECTID ||
1198 id > BTRFS_LAST_FREE_OBJECTID) {
1199 error("subvolume id %s out of range", arg);
1200 ret = 1;
1201 goto out;
1203 ids[i] = id;
1207 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1209 out:
1210 free(ids);
1211 close_file_or_dir(fd, dirstream);
1213 return !!ret;
1216 static const char subvolume_cmd_group_info[] =
1217 "manage subvolumes: create, delete, list, etc";
1219 const struct cmd_group subvolume_cmd_group = {
1220 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1221 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1222 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1223 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1224 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1225 NULL, 0 },
1226 { "get-default", cmd_subvol_get_default,
1227 cmd_subvol_get_default_usage, NULL, 0 },
1228 { "set-default", cmd_subvol_set_default,
1229 cmd_subvol_set_default_usage, NULL, 0 },
1230 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1231 NULL, 0 },
1232 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1233 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1234 NULL_CMD_STRUCT
1238 int cmd_subvolume(int argc, char **argv)
1240 return handle_command_group(&subvolume_cmd_group, argc, argv);