Btrfs progs v4.17.1
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blobe7a884af1f5ded892f9ba82bbd72dd66e42eed49
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 optind = 0;
106 while (1) {
107 int c = getopt(argc, argv, "c:i:");
108 if (c < 0)
109 break;
111 switch (c) {
112 case 'c':
113 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
114 if (res) {
115 retval = res;
116 goto out;
118 break;
119 case 'i':
120 res = qgroup_inherit_add_group(&inherit, optarg);
121 if (res) {
122 retval = res;
123 goto out;
125 break;
126 default:
127 usage(cmd_subvol_create_usage);
131 if (check_argc_exact(argc - optind, 1))
132 usage(cmd_subvol_create_usage);
134 dst = argv[optind];
136 retval = 1; /* failure */
137 res = test_isdir(dst);
138 if (res < 0 && res != -ENOENT) {
139 error("cannot access %s: %s", dst, strerror(-res));
140 goto out;
142 if (res >= 0) {
143 error("target path already exists: %s", dst);
144 goto out;
147 dupname = strdup(dst);
148 newname = basename(dupname);
149 dupdir = strdup(dst);
150 dstdir = dirname(dupdir);
152 if (!test_issubvolname(newname)) {
153 error("invalid subvolume name: %s", newname);
154 goto out;
157 len = strlen(newname);
158 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
159 error("subvolume name too long: %s", newname);
160 goto out;
163 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
164 if (fddst < 0)
165 goto out;
167 printf("Create subvolume '%s/%s'\n", dstdir, newname);
168 if (inherit) {
169 struct btrfs_ioctl_vol_args_v2 args;
171 memset(&args, 0, sizeof(args));
172 strncpy_null(args.name, newname);
173 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
174 args.size = qgroup_inherit_size(inherit);
175 args.qgroup_inherit = inherit;
177 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
178 } else {
179 struct btrfs_ioctl_vol_args args;
181 memset(&args, 0, sizeof(args));
182 strncpy_null(args.name, newname);
184 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
187 if (res < 0) {
188 error("cannot create subvolume: %m");
189 goto out;
192 retval = 0; /* success */
193 out:
194 close_file_or_dir(fddst, dirstream);
195 free(inherit);
196 free(dupname);
197 free(dupdir);
199 return retval;
202 static int wait_for_commit(int fd)
204 enum btrfs_util_error err;
205 uint64_t transid;
207 err = btrfs_util_start_sync_fd(fd, &transid);
208 if (err)
209 return -1;
211 err = btrfs_util_wait_sync_fd(fd, transid);
212 if (err)
213 return -1;
215 return 0;
218 static const char * const cmd_subvol_delete_usage[] = {
219 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
220 "Delete subvolume(s)",
221 "Delete subvolumes from the filesystem. The corresponding directory",
222 "is removed instantly but the data blocks are removed later.",
223 "The deletion does not involve full commit by default due to",
224 "performance reasons (as a consequence, the subvolume may appear again",
225 "after a crash). Use one of the --commit options to wait until the",
226 "operation is safely stored on the media.",
228 "-c|--commit-after wait for transaction commit at the end of the operation",
229 "-C|--commit-each wait for transaction commit after deleting each subvolume",
230 "-v|--verbose verbose output of operations",
231 NULL
234 static int cmd_subvol_delete(int argc, char **argv)
236 int res, ret = 0;
237 int cnt;
238 int fd = -1;
239 char *dname, *vname, *cpath;
240 char *dupdname = NULL;
241 char *dupvname = NULL;
242 char *path;
243 DIR *dirstream = NULL;
244 int verbose = 0;
245 int commit_mode = 0;
246 u8 fsid[BTRFS_FSID_SIZE];
247 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
248 struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
249 enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
250 enum btrfs_util_error err;
252 optind = 0;
253 while (1) {
254 int c;
255 static const struct option long_options[] = {
256 {"commit-after", no_argument, NULL, 'c'},
257 {"commit-each", no_argument, NULL, 'C'},
258 {"verbose", no_argument, NULL, 'v'},
259 {NULL, 0, NULL, 0}
262 c = getopt_long(argc, argv, "cCv", long_options, NULL);
263 if (c < 0)
264 break;
266 switch(c) {
267 case 'c':
268 commit_mode = COMMIT_AFTER;
269 break;
270 case 'C':
271 commit_mode = COMMIT_EACH;
272 break;
273 case 'v':
274 verbose++;
275 break;
276 default:
277 usage(cmd_subvol_delete_usage);
281 if (check_argc_min(argc - optind, 1))
282 usage(cmd_subvol_delete_usage);
284 if (verbose > 0) {
285 printf("Transaction commit: %s\n",
286 !commit_mode ? "none (default)" :
287 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
290 cnt = optind;
292 again:
293 path = argv[cnt];
295 err = btrfs_util_is_subvolume(path);
296 if (err) {
297 error_btrfs_util(err);
298 ret = 1;
299 goto out;
302 cpath = realpath(path, NULL);
303 if (!cpath) {
304 ret = errno;
305 error("cannot find real path for '%s': %m", path);
306 goto out;
308 dupdname = strdup(cpath);
309 dname = dirname(dupdname);
310 dupvname = strdup(cpath);
311 vname = basename(dupvname);
312 free(cpath);
314 fd = btrfs_open_dir(dname, &dirstream, 1);
315 if (fd < 0) {
316 ret = 1;
317 goto out;
320 printf("Delete subvolume (%s): '%s/%s'\n",
321 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
322 ? "commit" : "no-commit", dname, vname);
324 err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
325 if (err) {
326 error_btrfs_util(err);
327 ret = 1;
328 goto out;
331 if (commit_mode == COMMIT_EACH) {
332 res = wait_for_commit(fd);
333 if (res < 0) {
334 error("unable to wait for commit after '%s': %m", path);
335 ret = 1;
337 } else if (commit_mode == COMMIT_AFTER) {
338 res = get_fsid(dname, fsid, 0);
339 if (res < 0) {
340 error("unable to get fsid for '%s': %s",
341 path, strerror(-res));
342 error(
343 "delete succeeded but commit may not be done in the end");
344 ret = 1;
345 goto out;
348 if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) {
349 if (verbose > 0) {
350 uuid_unparse(fsid, uuidbuf);
351 printf(" new fs is found for '%s', fsid: %s\n",
352 path, uuidbuf);
355 * This is the first time a subvolume on this
356 * filesystem is deleted, keep fd in order to issue
357 * SYNC ioctl in the end
359 goto keep_fd;
363 out:
364 close_file_or_dir(fd, dirstream);
365 keep_fd:
366 fd = -1;
367 dirstream = NULL;
368 free(dupdname);
369 free(dupvname);
370 dupdname = NULL;
371 dupvname = NULL;
372 cnt++;
373 if (cnt < argc)
374 goto again;
376 if (commit_mode == COMMIT_AFTER) {
377 int slot;
380 * Traverse seen_fsid_hash and issue SYNC ioctl on each
381 * filesystem
383 for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
384 struct seen_fsid *seen = seen_fsid_hash[slot];
386 while (seen) {
387 res = wait_for_commit(seen->fd);
388 if (res < 0) {
389 uuid_unparse(seen->fsid, uuidbuf);
390 error(
391 "unable to do final sync after deletion: %m, fsid: %s",
392 uuidbuf);
393 ret = 1;
394 } else if (verbose > 0) {
395 uuid_unparse(seen->fsid, uuidbuf);
396 printf("final sync is done for fsid: %s\n",
397 uuidbuf);
399 seen = seen->next;
402 /* fd will also be closed in free_seen_fsid */
403 free_seen_fsid(seen_fsid_hash);
406 return ret;
410 * Naming of options:
411 * - uppercase for filters and sort options
412 * - lowercase for enabling specific items in the output
414 static const char * const cmd_subvol_list_usage[] = {
415 "btrfs subvolume list [options] <path>",
416 "List subvolumes and snapshots in the filesystem.",
418 "Path filtering:",
419 "-o print only subvolumes below specified path",
420 "-a print all the subvolumes in the filesystem and",
421 " distinguish absolute and relative path with respect",
422 " to the given <path>",
424 "Field selection:",
425 "-p print parent ID",
426 "-c print the ogeneration of the subvolume",
427 "-g print the generation of the subvolume",
428 "-u print the uuid of subvolumes (and snapshots)",
429 "-q print the parent uuid of the snapshots",
430 "-R print the uuid of the received snapshots",
432 "Type filtering:",
433 "-s list only snapshots",
434 "-r list readonly subvolumes (including snapshots)",
435 "-d list deleted subvolumes that are not yet cleaned",
437 "Other:",
438 "-t print the result as a table",
440 "Sorting:",
441 "-G [+|-]value",
442 " filter the subvolumes by generation",
443 " (+value: >= value; -value: <= value; value: = value)",
444 "-C [+|-]value",
445 " filter the subvolumes by ogeneration",
446 " (+value: >= value; -value: <= value; value: = value)",
447 "--sort=gen,ogen,rootid,path",
448 " list the subvolume in order of gen, ogen, rootid or path",
449 " you also can add '+' or '-' in front of each items.",
450 " (+:ascending, -:descending, ascending default)",
451 NULL,
454 static int cmd_subvol_list(int argc, char **argv)
456 struct btrfs_list_filter_set *filter_set;
457 struct btrfs_list_comparer_set *comparer_set;
458 u64 flags = 0;
459 int fd = -1;
460 u64 top_id;
461 int ret = -1, uerr = 0;
462 char *subvol;
463 int is_list_all = 0;
464 int is_only_in_path = 0;
465 DIR *dirstream = NULL;
466 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
468 filter_set = btrfs_list_alloc_filter_set();
469 comparer_set = btrfs_list_alloc_comparer_set();
471 optind = 0;
472 while(1) {
473 int c;
474 static const struct option long_options[] = {
475 {"sort", required_argument, NULL, 'S'},
476 {NULL, 0, NULL, 0}
479 c = getopt_long(argc, argv,
480 "acdgopqsurRG:C:t", long_options, NULL);
481 if (c < 0)
482 break;
484 switch(c) {
485 case 'p':
486 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
487 break;
488 case 'a':
489 is_list_all = 1;
490 break;
491 case 'c':
492 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
493 break;
494 case 'd':
495 btrfs_list_setup_filter(&filter_set,
496 BTRFS_LIST_FILTER_DELETED,
498 break;
499 case 'g':
500 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
501 break;
502 case 'o':
503 is_only_in_path = 1;
504 break;
505 case 't':
506 layout = BTRFS_LIST_LAYOUT_TABLE;
507 break;
508 case 's':
509 btrfs_list_setup_filter(&filter_set,
510 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
512 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
513 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
514 break;
515 case 'u':
516 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
517 break;
518 case 'q':
519 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
520 break;
521 case 'R':
522 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
523 break;
524 case 'r':
525 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
526 break;
527 case 'G':
528 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
529 ret = btrfs_list_parse_filter_string(optarg,
530 &filter_set,
531 BTRFS_LIST_FILTER_GEN);
532 if (ret) {
533 uerr = 1;
534 goto out;
536 break;
538 case 'C':
539 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
540 ret = btrfs_list_parse_filter_string(optarg,
541 &filter_set,
542 BTRFS_LIST_FILTER_CGEN);
543 if (ret) {
544 uerr = 1;
545 goto out;
547 break;
548 case 'S':
549 ret = btrfs_list_parse_sort_string(optarg,
550 &comparer_set);
551 if (ret) {
552 uerr = 1;
553 goto out;
555 break;
557 default:
558 uerr = 1;
559 goto out;
563 if (check_argc_exact(argc - optind, 1)) {
564 uerr = 1;
565 goto out;
568 subvol = argv[optind];
569 fd = btrfs_open_dir(subvol, &dirstream, 1);
570 if (fd < 0) {
571 ret = -1;
572 error("can't access '%s'", subvol);
573 goto out;
576 if (flags)
577 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
578 flags);
580 ret = btrfs_list_get_path_rootid(fd, &top_id);
581 if (ret)
582 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 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
600 layout, !is_list_all && !is_only_in_path, NULL);
602 out:
603 close_file_or_dir(fd, dirstream);
604 if (filter_set)
605 free(filter_set);
606 if (comparer_set)
607 free(comparer_set);
608 if (uerr)
609 usage(cmd_subvol_list_usage);
610 return !!ret;
613 static const char * const cmd_subvol_snapshot_usage[] = {
614 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
615 "Create a snapshot of the subvolume",
616 "Create a writable/readonly snapshot of the subvolume <source> with",
617 "the name <name> in the <dest> directory. If only <dest> is given,",
618 "the subvolume will be named the basename of <source>.",
620 "-r create a readonly snapshot",
621 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
622 " option can be given multiple times.",
623 NULL
626 static int cmd_subvol_snapshot(int argc, char **argv)
628 char *subvol, *dst;
629 int res, retval;
630 int fd = -1, fddst = -1;
631 int len, readonly = 0;
632 char *dupname = NULL;
633 char *dupdir = NULL;
634 char *newname;
635 char *dstdir;
636 enum btrfs_util_error err;
637 struct btrfs_ioctl_vol_args_v2 args;
638 struct btrfs_qgroup_inherit *inherit = NULL;
639 DIR *dirstream1 = NULL, *dirstream2 = NULL;
641 memset(&args, 0, sizeof(args));
642 optind = 0;
643 while (1) {
644 int c = getopt(argc, argv, "c:i:r");
645 if (c < 0)
646 break;
648 switch (c) {
649 case 'c':
650 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
651 if (res) {
652 retval = res;
653 goto out;
655 break;
656 case 'i':
657 res = qgroup_inherit_add_group(&inherit, optarg);
658 if (res) {
659 retval = res;
660 goto out;
662 break;
663 case 'r':
664 readonly = 1;
665 break;
666 case 'x':
667 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
668 if (res) {
669 retval = res;
670 goto out;
672 break;
673 default:
674 usage(cmd_subvol_snapshot_usage);
678 if (check_argc_exact(argc - optind, 2))
679 usage(cmd_subvol_snapshot_usage);
681 subvol = argv[optind];
682 dst = argv[optind + 1];
684 retval = 1; /* failure */
685 err = btrfs_util_is_subvolume(subvol);
686 if (err) {
687 error_btrfs_util(err);
688 goto out;
691 res = test_isdir(dst);
692 if (res < 0 && res != -ENOENT) {
693 error("cannot access %s: %s", dst, strerror(-res));
694 goto out;
696 if (res == 0) {
697 error("'%s' exists and it is not a directory", dst);
698 goto out;
701 if (res > 0) {
702 dupname = strdup(subvol);
703 newname = basename(dupname);
704 dstdir = dst;
705 } else {
706 dupname = strdup(dst);
707 newname = basename(dupname);
708 dupdir = strdup(dst);
709 dstdir = dirname(dupdir);
712 if (!test_issubvolname(newname)) {
713 error("invalid snapshot name '%s'", newname);
714 goto out;
717 len = strlen(newname);
718 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
719 error("snapshot name too long '%s'", newname);
720 goto out;
723 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
724 if (fddst < 0)
725 goto out;
727 fd = btrfs_open_dir(subvol, &dirstream2, 1);
728 if (fd < 0)
729 goto out;
731 if (readonly) {
732 args.flags |= BTRFS_SUBVOL_RDONLY;
733 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
734 subvol, dstdir, newname);
735 } else {
736 printf("Create a snapshot of '%s' in '%s/%s'\n",
737 subvol, dstdir, newname);
740 args.fd = fd;
741 if (inherit) {
742 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
743 args.size = qgroup_inherit_size(inherit);
744 args.qgroup_inherit = inherit;
746 strncpy_null(args.name, newname);
748 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
750 if (res < 0) {
751 error("cannot snapshot '%s': %m", subvol);
752 goto out;
755 retval = 0; /* success */
757 out:
758 close_file_or_dir(fddst, dirstream1);
759 close_file_or_dir(fd, dirstream2);
760 free(inherit);
761 free(dupname);
762 free(dupdir);
764 return retval;
767 static const char * const cmd_subvol_get_default_usage[] = {
768 "btrfs subvolume get-default <path>",
769 "Get the default subvolume of a filesystem",
770 NULL
773 static int cmd_subvol_get_default(int argc, char **argv)
775 int fd = -1;
776 int ret = 1;
777 uint64_t default_id;
778 DIR *dirstream = NULL;
779 enum btrfs_util_error err;
780 struct btrfs_util_subvolume_info subvol;
781 char *path;
783 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
785 if (check_argc_exact(argc - optind, 1))
786 usage(cmd_subvol_get_default_usage);
788 fd = btrfs_open_dir(argv[1], &dirstream, 1);
789 if (fd < 0)
790 return 1;
792 err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
793 if (err) {
794 error_btrfs_util(err);
795 goto out;
798 /* no need to resolve roots if FS_TREE is default */
799 if (default_id == BTRFS_FS_TREE_OBJECTID) {
800 printf("ID 5 (FS_TREE)\n");
801 ret = 0;
802 goto out;
805 err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol);
806 if (err) {
807 error_btrfs_util(err);
808 goto out;
811 err = btrfs_util_subvolume_path_fd(fd, default_id, &path);
812 if (err) {
813 error_btrfs_util(err);
814 goto out;
817 printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
818 subvol.id, subvol.generation, subvol.parent_id, path);
820 free(path);
822 ret = 0;
823 out:
824 close_file_or_dir(fd, dirstream);
825 return ret;
828 static const char * const cmd_subvol_set_default_usage[] = {
829 "btrfs subvolume set-default <subvolume>\n"
830 "btrfs subvolume set-default <subvolid> <path>",
831 "Set the default subvolume of the filesystem mounted as default.",
832 "The subvolume can be specified by its path,",
833 "or the pair of subvolume id and path to the filesystem.",
834 NULL
837 static int cmd_subvol_set_default(int argc, char **argv)
839 u64 objectid;
840 char *path;
841 enum btrfs_util_error err;
843 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
845 if (check_argc_min(argc - optind, 1) ||
846 check_argc_max(argc - optind, 2))
847 usage(cmd_subvol_set_default_usage);
849 if (argc - optind == 1) {
850 /* path to the subvolume is specified */
851 objectid = 0;
852 path = argv[optind];
853 } else {
854 /* subvol id and path to the filesystem are specified */
855 objectid = arg_strtou64(argv[optind]);
856 path = argv[optind + 1];
859 err = btrfs_util_set_default_subvolume(path, objectid);
860 if (err) {
861 error_btrfs_util(err);
862 return 1;
864 return 0;
867 static const char * const cmd_subvol_find_new_usage[] = {
868 "btrfs subvolume find-new <path> <lastgen>",
869 "List the recently modified files in a filesystem",
870 NULL
873 static int cmd_subvol_find_new(int argc, char **argv)
875 int fd;
876 int ret;
877 char *subvol;
878 u64 last_gen;
879 DIR *dirstream = NULL;
880 enum btrfs_util_error err;
882 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
884 if (check_argc_exact(argc - optind, 2))
885 usage(cmd_subvol_find_new_usage);
887 subvol = argv[optind];
888 last_gen = arg_strtou64(argv[optind + 1]);
890 err = btrfs_util_is_subvolume(subvol);
891 if (err) {
892 error_btrfs_util(err);
893 return 1;
896 fd = btrfs_open_dir(subvol, &dirstream, 1);
897 if (fd < 0)
898 return 1;
900 err = btrfs_util_sync_fd(fd);
901 if (err) {
902 error_btrfs_util(err);
903 close_file_or_dir(fd, dirstream);
904 return 1;
907 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
908 close_file_or_dir(fd, dirstream);
909 return !!ret;
912 static const char * const cmd_subvol_show_usage[] = {
913 "btrfs subvolume show [options] <subvol-path>|<mnt>",
914 "Show more information about the subvolume",
915 "-r|--rootid rootid of the subvolume",
916 "-u|--uuid uuid of the subvolume",
918 "If no option is specified, <subvol-path> will be shown, otherwise",
919 "the rootid or uuid are resolved relative to the <mnt> path.",
920 NULL
923 static int cmd_subvol_show(int argc, char **argv)
925 char tstr[256];
926 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
927 char *fullpath = NULL;
928 int fd = -1;
929 int ret = 1;
930 DIR *dirstream1 = NULL;
931 int by_rootid = 0;
932 int by_uuid = 0;
933 u64 rootid_arg = 0;
934 u8 uuid_arg[BTRFS_UUID_SIZE];
935 struct btrfs_util_subvolume_iterator *iter;
936 struct btrfs_util_subvolume_info subvol;
937 char *subvol_path = NULL;
938 enum btrfs_util_error err;
940 optind = 0;
941 while (1) {
942 int c;
943 static const struct option long_options[] = {
944 { "rootid", required_argument, NULL, 'r'},
945 { "uuid", required_argument, NULL, 'u'},
946 { NULL, 0, NULL, 0 }
949 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
950 if (c < 0)
951 break;
953 switch (c) {
954 case 'r':
955 rootid_arg = arg_strtou64(optarg);
956 by_rootid = 1;
957 break;
958 case 'u':
959 uuid_parse(optarg, uuid_arg);
960 by_uuid = 1;
961 break;
962 default:
963 usage(cmd_subvol_show_usage);
967 if (check_argc_exact(argc - optind, 1))
968 usage(cmd_subvol_show_usage);
970 if (by_rootid && by_uuid) {
971 error(
972 "options --rootid and --uuid cannot be used at the same time");
973 usage(cmd_subvol_show_usage);
976 fullpath = realpath(argv[optind], NULL);
977 if (!fullpath) {
978 error("cannot find real path for '%s': %m", argv[optind]);
979 goto out;
982 fd = open_file_or_dir(fullpath, &dirstream1);
983 if (fd < 0) {
984 error("can't access '%s'", fullpath);
985 goto out;
988 if (by_uuid) {
989 err = btrfs_util_create_subvolume_iterator_fd(fd,
990 BTRFS_FS_TREE_OBJECTID,
991 0, &iter);
992 if (err) {
993 error_btrfs_util(err);
994 goto out;
997 for (;;) {
998 err = btrfs_util_subvolume_iterator_next_info(iter,
999 &subvol_path,
1000 &subvol);
1001 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1002 uuid_unparse(uuid_arg, uuidparse);
1003 error("can't find uuid '%s' on '%s'", uuidparse,
1004 fullpath);
1005 btrfs_util_destroy_subvolume_iterator(iter);
1006 goto out;
1007 } else if (err) {
1008 error_btrfs_util(err);
1009 btrfs_util_destroy_subvolume_iterator(iter);
1010 goto out;
1013 if (uuid_compare(subvol.uuid, uuid_arg) == 0)
1014 break;
1016 free(subvol_path);
1018 btrfs_util_destroy_subvolume_iterator(iter);
1019 } else {
1021 * If !by_rootid, rootid_arg = 0, which means find the
1022 * subvolume ID of the fd and use that.
1024 err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
1025 if (err) {
1026 error_btrfs_util(err);
1027 goto out;
1030 err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
1031 if (err) {
1032 error_btrfs_util(err);
1033 goto out;
1038 /* print the info */
1039 printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
1040 printf("\tName: \t\t\t%s\n",
1041 (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
1042 basename(subvol_path)));
1044 if (uuid_is_null(subvol.uuid))
1045 strcpy(uuidparse, "-");
1046 else
1047 uuid_unparse(subvol.uuid, uuidparse);
1048 printf("\tUUID: \t\t\t%s\n", uuidparse);
1050 if (uuid_is_null(subvol.parent_uuid))
1051 strcpy(uuidparse, "-");
1052 else
1053 uuid_unparse(subvol.parent_uuid, uuidparse);
1054 printf("\tParent UUID: \t\t%s\n", uuidparse);
1056 if (uuid_is_null(subvol.received_uuid))
1057 strcpy(uuidparse, "-");
1058 else
1059 uuid_unparse(subvol.received_uuid, uuidparse);
1060 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1062 if (subvol.otime.tv_sec) {
1063 struct tm tm;
1065 localtime_r(&subvol.otime.tv_sec, &tm);
1066 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1067 } else
1068 strcpy(tstr, "-");
1069 printf("\tCreation time: \t\t%s\n", tstr);
1071 printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
1072 printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
1073 printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
1074 printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1075 printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1077 if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1078 printf("\tFlags: \t\t\treadonly\n");
1079 else
1080 printf("\tFlags: \t\t\t-\n");
1082 /* print the snapshots of the given subvol if any*/
1083 printf("\tSnapshot(s):\n");
1085 err = btrfs_util_create_subvolume_iterator_fd(fd,
1086 BTRFS_FS_TREE_OBJECTID, 0,
1087 &iter);
1089 for (;;) {
1090 struct btrfs_util_subvolume_info subvol2;
1091 char *path;
1093 err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
1094 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1095 break;
1096 } else if (err) {
1097 error_btrfs_util(err);
1098 btrfs_util_destroy_subvolume_iterator(iter);
1099 goto out;
1102 if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
1103 printf("\t\t\t\t%s\n", path);
1105 free(path);
1107 btrfs_util_destroy_subvolume_iterator(iter);
1109 ret = 0;
1110 out:
1111 free(subvol_path);
1112 close_file_or_dir(fd, dirstream1);
1113 free(fullpath);
1114 return !!ret;
1117 static const char * const cmd_subvol_sync_usage[] = {
1118 "btrfs subvolume sync <path> [<subvol-id>...]",
1119 "Wait until given subvolume(s) are completely removed from the filesystem.",
1120 "Wait until given subvolume(s) are completely removed from the filesystem",
1121 "after deletion.",
1122 "If no subvolume id is given, wait until all current deletion requests",
1123 "are completed, but do not wait for subvolumes deleted meanwhile.",
1124 "The status of subvolume ids is checked periodically.",
1126 "-s <N> sleep N seconds between checks (default: 1)",
1127 NULL
1130 static int cmd_subvol_sync(int argc, char **argv)
1132 int fd = -1;
1133 int ret = 1;
1134 DIR *dirstream = NULL;
1135 uint64_t *ids = NULL;
1136 size_t id_count, i;
1137 int sleep_interval = 1;
1138 enum btrfs_util_error err;
1140 optind = 0;
1141 while (1) {
1142 int c = getopt(argc, argv, "s:");
1144 if (c < 0)
1145 break;
1147 switch (c) {
1148 case 's':
1149 sleep_interval = atoi(optarg);
1150 if (sleep_interval < 1) {
1151 error("invalid sleep interval %s", optarg);
1152 ret = 1;
1153 goto out;
1155 break;
1156 default:
1157 usage(cmd_subvol_sync_usage);
1161 if (check_argc_min(argc - optind, 1))
1162 usage(cmd_subvol_sync_usage);
1164 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1165 if (fd < 0) {
1166 ret = 1;
1167 goto out;
1169 optind++;
1171 id_count = argc - optind;
1172 if (!id_count) {
1173 err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count);
1174 if (err) {
1175 error_btrfs_util(err);
1176 ret = 1;
1177 goto out;
1179 if (id_count == 0) {
1180 ret = 0;
1181 goto out;
1183 } else {
1184 ids = malloc(id_count * sizeof(uint64_t));
1185 if (!ids) {
1186 error("not enough memory");
1187 ret = 1;
1188 goto out;
1191 for (i = 0; i < id_count; i++) {
1192 u64 id;
1193 const char *arg;
1195 arg = argv[optind + i];
1196 errno = 0;
1197 id = strtoull(arg, NULL, 10);
1198 if (errno) {
1199 error("unrecognized subvolume id %s", arg);
1200 ret = 1;
1201 goto out;
1203 if (id < BTRFS_FIRST_FREE_OBJECTID ||
1204 id > BTRFS_LAST_FREE_OBJECTID) {
1205 error("subvolume id %s out of range", arg);
1206 ret = 1;
1207 goto out;
1209 ids[i] = id;
1213 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1215 out:
1216 free(ids);
1217 close_file_or_dir(fd, dirstream);
1219 return !!ret;
1222 static const char subvolume_cmd_group_info[] =
1223 "manage subvolumes: create, delete, list, etc";
1225 const struct cmd_group subvolume_cmd_group = {
1226 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1227 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1228 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1229 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1230 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1231 NULL, 0 },
1232 { "get-default", cmd_subvol_get_default,
1233 cmd_subvol_get_default_usage, NULL, 0 },
1234 { "set-default", cmd_subvol_set_default,
1235 cmd_subvol_set_default_usage, NULL, 0 },
1236 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1237 NULL, 0 },
1238 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1239 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1240 NULL_CMD_STRUCT
1244 int cmd_subvolume(int argc, char **argv)
1246 return handle_command_group(&subvolume_cmd_group, argc, argv);