btrfs-progs: btrfs-progs: Fix read beyond boundary bug in build_roots_info_cache()
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob8a473f7a217750df8abd4ec3d64d5f5556c4d6d0
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/vfs.h>
25 #include <libgen.h>
26 #include <limits.h>
27 #include <getopt.h>
28 #include <uuid/uuid.h>
29 #include <linux/magic.h>
31 #include "kerncompat.h"
32 #include "ioctl.h"
33 #include "qgroup.h"
35 #include "ctree.h"
36 #include "commands.h"
37 #include "utils.h"
38 #include "btrfs-list.h"
39 #include "utils.h"
40 #include "help.h"
42 static int is_subvolume_cleaned(int fd, u64 subvolid)
44 int ret;
45 struct btrfs_ioctl_search_args args;
46 struct btrfs_ioctl_search_key *sk = &args.key;
48 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
49 sk->min_objectid = subvolid;
50 sk->max_objectid = subvolid;
51 sk->min_type = BTRFS_ROOT_ITEM_KEY;
52 sk->max_type = BTRFS_ROOT_ITEM_KEY;
53 sk->min_offset = 0;
54 sk->max_offset = (u64)-1;
55 sk->min_transid = 0;
56 sk->max_transid = (u64)-1;
57 sk->nr_items = 1;
59 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
60 if (ret < 0)
61 return -errno;
63 if (sk->nr_items == 0)
64 return 1;
66 return 0;
69 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
70 int sleep_interval)
72 int ret;
73 int i;
75 while (1) {
76 int clean = 1;
78 for (i = 0; i < count; i++) {
79 if (!ids[i])
80 continue;
81 ret = is_subvolume_cleaned(fd, ids[i]);
82 if (ret < 0) {
83 error(
84 "cannot read status of dead subvolume %llu: %s",
85 (unsigned long long)ids[i], strerror(-ret));
86 return ret;
88 if (ret) {
89 printf("Subvolume id %llu is gone\n", ids[i]);
90 ids[i] = 0;
91 } else {
92 clean = 0;
95 if (clean)
96 break;
97 sleep(sleep_interval);
100 return 0;
103 static const char * const subvolume_cmd_group_usage[] = {
104 "btrfs subvolume <command> <args>",
105 NULL
108 static const char * const cmd_subvol_create_usage[] = {
109 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
110 "Create a subvolume",
111 "Create a subvolume <name> in <dest>. If <dest> is not given",
112 "subvolume <name> will be created in the current directory.",
114 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
115 " option can be given multiple times.",
116 NULL
119 static int cmd_subvol_create(int argc, char **argv)
121 int retval, res, len;
122 int fddst = -1;
123 char *dupname = NULL;
124 char *dupdir = NULL;
125 char *newname;
126 char *dstdir;
127 char *dst;
128 struct btrfs_qgroup_inherit *inherit = NULL;
129 DIR *dirstream = NULL;
131 while (1) {
132 int c = getopt(argc, argv, "c:i:");
133 if (c < 0)
134 break;
136 switch (c) {
137 case 'c':
138 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
139 if (res) {
140 retval = res;
141 goto out;
143 break;
144 case 'i':
145 res = qgroup_inherit_add_group(&inherit, optarg);
146 if (res) {
147 retval = res;
148 goto out;
150 break;
151 default:
152 usage(cmd_subvol_create_usage);
156 if (check_argc_exact(argc - optind, 1))
157 usage(cmd_subvol_create_usage);
159 dst = argv[optind];
161 retval = 1; /* failure */
162 res = test_isdir(dst);
163 if (res < 0 && res != -ENOENT) {
164 error("cannot access %s: %s", dst, strerror(-res));
165 goto out;
167 if (res >= 0) {
168 error("target path already exists: %s", dst);
169 goto out;
172 dupname = strdup(dst);
173 newname = basename(dupname);
174 dupdir = strdup(dst);
175 dstdir = dirname(dupdir);
177 if (!test_issubvolname(newname)) {
178 error("invalid subvolume name: %s", newname);
179 goto out;
182 len = strlen(newname);
183 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
184 error("subvolume name too long: %s", newname);
185 goto out;
188 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
189 if (fddst < 0)
190 goto out;
192 printf("Create subvolume '%s/%s'\n", dstdir, newname);
193 if (inherit) {
194 struct btrfs_ioctl_vol_args_v2 args;
196 memset(&args, 0, sizeof(args));
197 strncpy_null(args.name, newname);
198 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
199 args.size = qgroup_inherit_size(inherit);
200 args.qgroup_inherit = inherit;
202 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
203 } else {
204 struct btrfs_ioctl_vol_args args;
206 memset(&args, 0, sizeof(args));
207 strncpy_null(args.name, newname);
209 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
212 if (res < 0) {
213 error("cannot create subvolume: %m");
214 goto out;
217 retval = 0; /* success */
218 out:
219 close_file_or_dir(fddst, dirstream);
220 free(inherit);
221 free(dupname);
222 free(dupdir);
224 return retval;
227 static int wait_for_commit(int fd)
229 int ret;
231 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
232 if (ret < 0)
233 return ret;
234 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
237 static const char * const cmd_subvol_delete_usage[] = {
238 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
239 "Delete subvolume(s)",
240 "Delete subvolumes from the filesystem. The corresponding directory",
241 "is removed instantly but the data blocks are removed later.",
242 "The deletion does not involve full commit by default due to",
243 "performance reasons (as a consequence, the subvolume may appear again",
244 "after a crash). Use one of the --commit options to wait until the",
245 "operation is safely stored on the media.",
247 "-c|--commit-after wait for transaction commit at the end of the operation",
248 "-C|--commit-each wait for transaction commit after deleting each subvolume",
249 "-v|--verbose verbose output of operations",
250 NULL
253 static int cmd_subvol_delete(int argc, char **argv)
255 int res, ret = 0;
256 int cnt;
257 int fd = -1;
258 struct btrfs_ioctl_vol_args args;
259 char *dname, *vname, *cpath;
260 char *dupdname = NULL;
261 char *dupvname = NULL;
262 char *path;
263 DIR *dirstream = NULL;
264 int verbose = 0;
265 int commit_mode = 0;
266 u8 fsid[BTRFS_FSID_SIZE];
267 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
268 struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
269 enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
271 while (1) {
272 int c;
273 static const struct option long_options[] = {
274 {"commit-after", no_argument, NULL, 'c'},
275 {"commit-each", no_argument, NULL, 'C'},
276 {"verbose", no_argument, NULL, 'v'},
277 {NULL, 0, NULL, 0}
280 c = getopt_long(argc, argv, "cCv", long_options, NULL);
281 if (c < 0)
282 break;
284 switch(c) {
285 case 'c':
286 commit_mode = COMMIT_AFTER;
287 break;
288 case 'C':
289 commit_mode = COMMIT_EACH;
290 break;
291 case 'v':
292 verbose++;
293 break;
294 default:
295 usage(cmd_subvol_delete_usage);
299 if (check_argc_min(argc - optind, 1))
300 usage(cmd_subvol_delete_usage);
302 if (verbose > 0) {
303 printf("Transaction commit: %s\n",
304 !commit_mode ? "none (default)" :
305 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
308 cnt = optind;
310 again:
311 path = argv[cnt];
313 res = test_issubvolume(path);
314 if (res < 0) {
315 error("cannot access subvolume %s: %s", path, strerror(-res));
316 ret = 1;
317 goto out;
319 if (!res) {
320 error("not a subvolume: %s", path);
321 ret = 1;
322 goto out;
325 cpath = realpath(path, NULL);
326 if (!cpath) {
327 ret = errno;
328 error("cannot find real path for '%s': %m", path);
329 goto out;
331 dupdname = strdup(cpath);
332 dname = dirname(dupdname);
333 dupvname = strdup(cpath);
334 vname = basename(dupvname);
335 free(cpath);
337 fd = btrfs_open_dir(dname, &dirstream, 1);
338 if (fd < 0) {
339 ret = 1;
340 goto out;
343 printf("Delete subvolume (%s): '%s/%s'\n",
344 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
345 ? "commit" : "no-commit", dname, vname);
346 memset(&args, 0, sizeof(args));
347 strncpy_null(args.name, vname);
348 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
349 if(res < 0 ){
350 error("cannot delete '%s/%s': %m", dname, vname);
351 ret = 1;
352 goto out;
355 if (commit_mode == COMMIT_EACH) {
356 res = wait_for_commit(fd);
357 if (res < 0) {
358 error("unable to wait for commit after '%s': %m", path);
359 ret = 1;
361 } else if (commit_mode == COMMIT_AFTER) {
362 res = get_fsid(dname, fsid, 0);
363 if (res < 0) {
364 error("unable to get fsid for '%s': %s",
365 path, strerror(-res));
366 error(
367 "delete suceeded but commit may not be done in the end");
368 ret = 1;
369 goto out;
372 if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) {
373 if (verbose > 0) {
374 uuid_unparse(fsid, uuidbuf);
375 printf(" new fs is found for '%s', fsid: %s\n",
376 path, uuidbuf);
379 * This is the first time a subvolume on this
380 * filesystem is deleted, keep fd in order to issue
381 * SYNC ioctl in the end
383 goto keep_fd;
387 out:
388 close_file_or_dir(fd, dirstream);
389 keep_fd:
390 fd = -1;
391 dirstream = NULL;
392 free(dupdname);
393 free(dupvname);
394 dupdname = NULL;
395 dupvname = NULL;
396 cnt++;
397 if (cnt < argc)
398 goto again;
400 if (commit_mode == COMMIT_AFTER) {
401 int slot;
404 * Traverse seen_fsid_hash and issue SYNC ioctl on each
405 * filesystem
407 for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
408 struct seen_fsid *seen = seen_fsid_hash[slot];
410 while (seen) {
411 res = wait_for_commit(seen->fd);
412 if (res < 0) {
413 uuid_unparse(seen->fsid, uuidbuf);
414 error(
415 "unable to do final sync after deletion: %m, fsid: %s",
416 uuidbuf);
417 ret = 1;
418 } else if (verbose > 0) {
419 uuid_unparse(seen->fsid, uuidbuf);
420 printf("final sync is done for fsid: %s\n",
421 uuidbuf);
423 seen = seen->next;
426 /* fd will also be closed in free_seen_fsid */
427 free_seen_fsid(seen_fsid_hash);
430 return ret;
434 * Naming of options:
435 * - uppercase for filters and sort options
436 * - lowercase for enabling specific items in the output
438 static const char * const cmd_subvol_list_usage[] = {
439 "btrfs subvolume list [options] <path>",
440 "List subvolumes and snapshots in the filesystem.",
442 "Path filtering:",
443 "-o print only subvolumes below specified path",
444 "-a print all the subvolumes in the filesystem and",
445 " distinguish absolute and relative path with respect",
446 " to the given <path>",
448 "Field selection:",
449 "-p print parent ID",
450 "-c print the ogeneration of the subvolume",
451 "-g print the generation of the subvolume",
452 "-u print the uuid of subvolumes (and snapshots)",
453 "-q print the parent uuid of the snapshots",
454 "-R print the uuid of the received snapshots",
456 "Type filtering:",
457 "-s list only snapshots",
458 "-r list readonly subvolumes (including snapshots)",
459 "-d list deleted subvolumes that are not yet cleaned",
461 "Other:",
462 "-t print the result as a table",
464 "Sorting:",
465 "-G [+|-]value",
466 " filter the subvolumes by generation",
467 " (+value: >= value; -value: <= value; value: = value)",
468 "-C [+|-]value",
469 " filter the subvolumes by ogeneration",
470 " (+value: >= value; -value: <= value; value: = value)",
471 "--sort=gen,ogen,rootid,path",
472 " list the subvolume in order of gen, ogen, rootid or path",
473 " you also can add '+' or '-' in front of each items.",
474 " (+:ascending, -:descending, ascending default)",
475 NULL,
478 static int cmd_subvol_list(int argc, char **argv)
480 struct btrfs_list_filter_set *filter_set;
481 struct btrfs_list_comparer_set *comparer_set;
482 u64 flags = 0;
483 int fd = -1;
484 u64 top_id;
485 int ret = -1, uerr = 0;
486 char *subvol;
487 int is_list_all = 0;
488 int is_only_in_path = 0;
489 DIR *dirstream = NULL;
490 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
492 filter_set = btrfs_list_alloc_filter_set();
493 comparer_set = btrfs_list_alloc_comparer_set();
495 while(1) {
496 int c;
497 static const struct option long_options[] = {
498 {"sort", required_argument, NULL, 'S'},
499 {NULL, 0, NULL, 0}
502 c = getopt_long(argc, argv,
503 "acdgopqsurRG:C:t", long_options, NULL);
504 if (c < 0)
505 break;
507 switch(c) {
508 case 'p':
509 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
510 break;
511 case 'a':
512 is_list_all = 1;
513 break;
514 case 'c':
515 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
516 break;
517 case 'd':
518 btrfs_list_setup_filter(&filter_set,
519 BTRFS_LIST_FILTER_DELETED,
521 break;
522 case 'g':
523 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
524 break;
525 case 'o':
526 is_only_in_path = 1;
527 break;
528 case 't':
529 layout = BTRFS_LIST_LAYOUT_TABLE;
530 break;
531 case 's':
532 btrfs_list_setup_filter(&filter_set,
533 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
535 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
536 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
537 break;
538 case 'u':
539 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
540 break;
541 case 'q':
542 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
543 break;
544 case 'R':
545 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
546 break;
547 case 'r':
548 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
549 break;
550 case 'G':
551 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
552 ret = btrfs_list_parse_filter_string(optarg,
553 &filter_set,
554 BTRFS_LIST_FILTER_GEN);
555 if (ret) {
556 uerr = 1;
557 goto out;
559 break;
561 case 'C':
562 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
563 ret = btrfs_list_parse_filter_string(optarg,
564 &filter_set,
565 BTRFS_LIST_FILTER_CGEN);
566 if (ret) {
567 uerr = 1;
568 goto out;
570 break;
571 case 'S':
572 ret = btrfs_list_parse_sort_string(optarg,
573 &comparer_set);
574 if (ret) {
575 uerr = 1;
576 goto out;
578 break;
580 default:
581 uerr = 1;
582 goto out;
586 if (check_argc_exact(argc - optind, 1)) {
587 uerr = 1;
588 goto out;
591 subvol = argv[optind];
592 fd = btrfs_open_dir(subvol, &dirstream, 1);
593 if (fd < 0) {
594 ret = -1;
595 error("can't access '%s'", subvol);
596 goto out;
599 if (flags)
600 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
601 flags);
603 ret = btrfs_list_get_path_rootid(fd, &top_id);
604 if (ret)
605 goto out;
607 if (is_list_all)
608 btrfs_list_setup_filter(&filter_set,
609 BTRFS_LIST_FILTER_FULL_PATH,
610 top_id);
611 else if (is_only_in_path)
612 btrfs_list_setup_filter(&filter_set,
613 BTRFS_LIST_FILTER_TOPID_EQUAL,
614 top_id);
616 /* by default we shall print the following columns*/
617 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
618 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
619 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
620 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
622 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
623 layout, !is_list_all && !is_only_in_path, NULL);
625 out:
626 close_file_or_dir(fd, dirstream);
627 if (filter_set)
628 free(filter_set);
629 if (comparer_set)
630 free(comparer_set);
631 if (uerr)
632 usage(cmd_subvol_list_usage);
633 return !!ret;
636 static const char * const cmd_subvol_snapshot_usage[] = {
637 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
638 "Create a snapshot of the subvolume",
639 "Create a writable/readonly snapshot of the subvolume <source> with",
640 "the name <name> in the <dest> directory. If only <dest> is given,",
641 "the subvolume will be named the basename of <source>.",
643 "-r create a readonly snapshot",
644 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
645 " option can be given multiple times.",
646 NULL
649 static int cmd_subvol_snapshot(int argc, char **argv)
651 char *subvol, *dst;
652 int res, retval;
653 int fd = -1, fddst = -1;
654 int len, readonly = 0;
655 char *dupname = NULL;
656 char *dupdir = NULL;
657 char *newname;
658 char *dstdir;
659 struct btrfs_ioctl_vol_args_v2 args;
660 struct btrfs_qgroup_inherit *inherit = NULL;
661 DIR *dirstream1 = NULL, *dirstream2 = NULL;
663 memset(&args, 0, sizeof(args));
664 while (1) {
665 int c = getopt(argc, argv, "c:i:r");
666 if (c < 0)
667 break;
669 switch (c) {
670 case 'c':
671 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
672 if (res) {
673 retval = res;
674 goto out;
676 break;
677 case 'i':
678 res = qgroup_inherit_add_group(&inherit, optarg);
679 if (res) {
680 retval = res;
681 goto out;
683 break;
684 case 'r':
685 readonly = 1;
686 break;
687 case 'x':
688 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
689 if (res) {
690 retval = res;
691 goto out;
693 break;
694 default:
695 usage(cmd_subvol_snapshot_usage);
699 if (check_argc_exact(argc - optind, 2))
700 usage(cmd_subvol_snapshot_usage);
702 subvol = argv[optind];
703 dst = argv[optind + 1];
705 retval = 1; /* failure */
706 res = test_issubvolume(subvol);
707 if (res < 0) {
708 error("cannot access subvolume %s: %s", subvol, strerror(-res));
709 goto out;
711 if (!res) {
712 error("not a subvolume: %s", subvol);
713 goto out;
716 res = test_isdir(dst);
717 if (res < 0 && res != -ENOENT) {
718 error("cannot access %s: %s", dst, strerror(-res));
719 goto out;
721 if (res == 0) {
722 error("'%s' exists and it is not a directory", dst);
723 goto out;
726 if (res > 0) {
727 dupname = strdup(subvol);
728 newname = basename(dupname);
729 dstdir = dst;
730 } else {
731 dupname = strdup(dst);
732 newname = basename(dupname);
733 dupdir = strdup(dst);
734 dstdir = dirname(dupdir);
737 if (!test_issubvolname(newname)) {
738 error("invalid snapshot name '%s'", newname);
739 goto out;
742 len = strlen(newname);
743 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
744 error("snapshot name too long '%s'", newname);
745 goto out;
748 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
749 if (fddst < 0)
750 goto out;
752 fd = btrfs_open_dir(subvol, &dirstream2, 1);
753 if (fd < 0)
754 goto out;
756 if (readonly) {
757 args.flags |= BTRFS_SUBVOL_RDONLY;
758 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
759 subvol, dstdir, newname);
760 } else {
761 printf("Create a snapshot of '%s' in '%s/%s'\n",
762 subvol, dstdir, newname);
765 args.fd = fd;
766 if (inherit) {
767 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
768 args.size = qgroup_inherit_size(inherit);
769 args.qgroup_inherit = inherit;
771 strncpy_null(args.name, newname);
773 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
775 if (res < 0) {
776 error("cannot snapshot '%s': %m", subvol);
777 goto out;
780 retval = 0; /* success */
782 out:
783 close_file_or_dir(fddst, dirstream1);
784 close_file_or_dir(fd, dirstream2);
785 free(inherit);
786 free(dupname);
787 free(dupdir);
789 return retval;
792 static const char * const cmd_subvol_get_default_usage[] = {
793 "btrfs subvolume get-default <path>",
794 "Get the default subvolume of a filesystem",
795 NULL
798 static int cmd_subvol_get_default(int argc, char **argv)
800 int fd = -1;
801 int ret;
802 char *subvol;
803 struct btrfs_list_filter_set *filter_set;
804 u64 default_id;
805 DIR *dirstream = NULL;
807 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
809 if (check_argc_exact(argc - optind, 1))
810 usage(cmd_subvol_get_default_usage);
812 subvol = argv[1];
813 fd = btrfs_open_dir(subvol, &dirstream, 1);
814 if (fd < 0)
815 return 1;
817 ret = btrfs_list_get_default_subvolume(fd, &default_id);
818 if (ret) {
819 error("failed to look up default subvolume: %m");
820 goto out;
823 ret = 1;
824 if (default_id == 0) {
825 error("'default' dir item not found");
826 goto out;
829 /* no need to resolve roots if FS_TREE is default */
830 if (default_id == BTRFS_FS_TREE_OBJECTID) {
831 printf("ID 5 (FS_TREE)\n");
832 ret = 0;
833 goto out;
836 filter_set = btrfs_list_alloc_filter_set();
837 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
838 default_id);
840 /* by default we shall print the following columns*/
841 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
842 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
843 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
844 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
846 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
847 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
849 if (filter_set)
850 free(filter_set);
851 out:
852 close_file_or_dir(fd, dirstream);
853 return !!ret;
856 static const char * const cmd_subvol_set_default_usage[] = {
857 "btrfs subvolume set-default <subvolume>\n"
858 "btrfs subvolume set-default <subvolid> <path>",
859 "Set the default subvolume of the filesystem mounted as default.",
860 "The subvolume can be specified by its path,",
861 "or the pair of subvolume id and path to the filesystem.",
862 NULL
865 static int cmd_subvol_set_default(int argc, char **argv)
867 int ret=0, fd;
868 u64 objectid;
869 char *path;
870 char *subvolid;
871 DIR *dirstream = NULL;
873 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
875 if (check_argc_min(argc - optind, 1) ||
876 check_argc_max(argc - optind, 2))
877 usage(cmd_subvol_set_default_usage);
879 if (argc - optind == 1) {
880 /* path to the subvolume is specified */
881 path = argv[optind];
883 ret = test_issubvolume(path);
884 if (ret < 0) {
885 error("stat error: %s", strerror(-ret));
886 return 1;
887 } else if (!ret) {
888 error("'%s' is not a subvolume", path);
889 return 1;
892 fd = btrfs_open_dir(path, &dirstream, 1);
893 if (fd < 0)
894 return 1;
896 ret = lookup_path_rootid(fd, &objectid);
897 if (ret) {
898 error("unable to get subvol id: %s", strerror(-ret));
899 close_file_or_dir(fd, dirstream);
900 return 1;
902 } else {
903 /* subvol id and path to the filesystem are specified */
904 subvolid = argv[optind];
905 path = argv[optind + 1];
906 objectid = arg_strtou64(subvolid);
908 fd = btrfs_open_dir(path, &dirstream, 1);
909 if (fd < 0)
910 return 1;
913 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
914 close_file_or_dir(fd, dirstream);
915 if (ret < 0) {
916 error("unable to set a new default subvolume: %m");
917 return 1;
919 return 0;
922 static const char * const cmd_subvol_find_new_usage[] = {
923 "btrfs subvolume find-new <path> <lastgen>",
924 "List the recently modified files in a filesystem",
925 NULL
928 static int cmd_subvol_find_new(int argc, char **argv)
930 int fd;
931 int ret;
932 char *subvol;
933 u64 last_gen;
934 DIR *dirstream = NULL;
936 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
938 if (check_argc_exact(argc - optind, 2))
939 usage(cmd_subvol_find_new_usage);
941 subvol = argv[optind];
942 last_gen = arg_strtou64(argv[optind + 1]);
944 ret = test_issubvolume(subvol);
945 if (ret < 0) {
946 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
947 return 1;
949 if (!ret) {
950 error("not a subvolume: %s", subvol);
951 return 1;
954 fd = btrfs_open_dir(subvol, &dirstream, 1);
955 if (fd < 0)
956 return 1;
958 ret = ioctl(fd, BTRFS_IOC_SYNC);
959 if (ret < 0) {
960 error("sync ioctl failed on '%s': %m", subvol);
961 close_file_or_dir(fd, dirstream);
962 return 1;
965 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
966 close_file_or_dir(fd, dirstream);
967 return !!ret;
970 static const char * const cmd_subvol_show_usage[] = {
971 "btrfs subvolume show [options] <subvol-path>|<mnt>",
972 "Show more information about the subvolume",
973 "-r|--rootid rootid of the subvolume",
974 "-u|--uuid uuid of the subvolume",
976 "If no option is specified, <subvol-path> will be shown, otherwise",
977 "the rootid or uuid are resolved relative to the <mnt> path.",
978 NULL
981 static int cmd_subvol_show(int argc, char **argv)
983 struct root_info get_ri;
984 struct btrfs_list_filter_set *filter_set = NULL;
985 char tstr[256];
986 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
987 char *fullpath = NULL;
988 char raw_prefix[] = "\t\t\t\t";
989 int fd = -1;
990 int ret = 1;
991 DIR *dirstream1 = NULL;
992 int by_rootid = 0;
993 int by_uuid = 0;
994 u64 rootid_arg;
995 u8 uuid_arg[BTRFS_UUID_SIZE];
997 while (1) {
998 int c;
999 static const struct option long_options[] = {
1000 { "rootid", required_argument, NULL, 'r'},
1001 { "uuid", required_argument, NULL, 'u'},
1002 { NULL, 0, NULL, 0 }
1005 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
1006 if (c < 0)
1007 break;
1009 switch (c) {
1010 case 'r':
1011 rootid_arg = arg_strtou64(optarg);
1012 by_rootid = 1;
1013 break;
1014 case 'u':
1015 uuid_parse(optarg, uuid_arg);
1016 by_uuid = 1;
1017 break;
1018 default:
1019 usage(cmd_subvol_show_usage);
1023 if (check_argc_exact(argc - optind, 1))
1024 usage(cmd_subvol_show_usage);
1026 if (by_rootid && by_uuid) {
1027 error(
1028 "options --rootid and --uuid cannot be used at the same time");
1029 usage(cmd_subvol_show_usage);
1032 memset(&get_ri, 0, sizeof(get_ri));
1033 fullpath = realpath(argv[optind], NULL);
1034 if (!fullpath) {
1035 error("cannot find real path for '%s': %m", argv[optind]);
1036 goto out;
1039 if (by_rootid) {
1040 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
1041 } else if (by_uuid) {
1042 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
1043 } else {
1044 ret = get_subvol_info(fullpath, &get_ri);
1047 if (ret) {
1048 if (ret < 0) {
1049 error("Failed to get subvol info %s: %s",
1050 fullpath, strerror(-ret));
1051 } else {
1052 error("Failed to get subvol info %s: %d",
1053 fullpath, ret);
1055 return ret;
1058 /* print the info */
1059 printf("%s\n", get_ri.full_path);
1060 printf("\tName: \t\t\t%s\n", get_ri.name);
1062 if (uuid_is_null(get_ri.uuid))
1063 strcpy(uuidparse, "-");
1064 else
1065 uuid_unparse(get_ri.uuid, uuidparse);
1066 printf("\tUUID: \t\t\t%s\n", uuidparse);
1068 if (uuid_is_null(get_ri.puuid))
1069 strcpy(uuidparse, "-");
1070 else
1071 uuid_unparse(get_ri.puuid, uuidparse);
1072 printf("\tParent UUID: \t\t%s\n", uuidparse);
1074 if (uuid_is_null(get_ri.ruuid))
1075 strcpy(uuidparse, "-");
1076 else
1077 uuid_unparse(get_ri.ruuid, uuidparse);
1078 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1080 if (get_ri.otime) {
1081 struct tm tm;
1083 localtime_r(&get_ri.otime, &tm);
1084 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1085 } else
1086 strcpy(tstr, "-");
1087 printf("\tCreation time: \t\t%s\n", tstr);
1089 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1090 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1091 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1092 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1093 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1095 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1096 printf("\tFlags: \t\t\treadonly\n");
1097 else
1098 printf("\tFlags: \t\t\t-\n");
1100 /* print the snapshots of the given subvol if any*/
1101 printf("\tSnapshot(s):\n");
1102 filter_set = btrfs_list_alloc_filter_set();
1103 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1104 (u64)(unsigned long)get_ri.uuid);
1105 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1107 fd = open_file_or_dir(fullpath, &dirstream1);
1108 if (fd < 0) {
1109 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1110 goto out;
1112 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1113 1, raw_prefix);
1115 out:
1116 /* clean up */
1117 free(get_ri.path);
1118 free(get_ri.name);
1119 free(get_ri.full_path);
1120 free(filter_set);
1122 close_file_or_dir(fd, dirstream1);
1123 free(fullpath);
1124 return !!ret;
1127 static const char * const cmd_subvol_sync_usage[] = {
1128 "btrfs subvolume sync <path> [<subvol-id>...]",
1129 "Wait until given subvolume(s) are completely removed from the filesystem.",
1130 "Wait until given subvolume(s) are completely removed from the filesystem",
1131 "after deletion.",
1132 "If no subvolume id is given, wait until all current deletion requests",
1133 "are completed, but do not wait for subvolumes deleted meanwhile.",
1134 "The status of subvolume ids is checked periodically.",
1136 "-s <N> sleep N seconds between checks (default: 1)",
1137 NULL
1140 #if 0
1142 * If we're looking for any dead subvolume, take a shortcut and look
1143 * for any ORPHAN_ITEMs in the tree root
1145 static int fs_has_dead_subvolumes(int fd)
1147 int ret;
1148 struct btrfs_ioctl_search_args args;
1149 struct btrfs_ioctl_search_key *sk = &args.key;
1150 struct btrfs_ioctl_search_header sh;
1151 u64 min_subvolid = 0;
1153 again:
1154 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1155 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1156 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1157 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1158 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1159 sk->min_offset = min_subvolid;
1160 sk->max_offset = (u64)-1;
1161 sk->min_transid = 0;
1162 sk->max_transid = (u64)-1;
1163 sk->nr_items = 1;
1165 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1166 if (ret < 0)
1167 return -errno;
1169 if (!sk->nr_items)
1170 return 0;
1172 memcpy(&sh, args.buf, sizeof(sh));
1173 min_subvolid = sh.offset;
1176 * Verify that the root item is really there and we haven't hit
1177 * a stale orphan
1179 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1180 sk->min_objectid = min_subvolid;
1181 sk->max_objectid = min_subvolid;
1182 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1183 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1184 sk->min_offset = 0;
1185 sk->max_offset = (u64)-1;
1186 sk->min_transid = 0;
1187 sk->max_transid = (u64)-1;
1188 sk->nr_items = 1;
1190 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1191 if (ret < 0)
1192 return -errno;
1195 * Stale orphan, try the next one
1197 if (!sk->nr_items) {
1198 min_subvolid++;
1199 goto again;
1202 return 1;
1204 #endif
1206 #define SUBVOL_ID_BATCH 1024
1209 * Enumerate all dead subvolumes that exist in the filesystem.
1210 * Fill @ids and reallocate to bigger size if needed.
1212 static int enumerate_dead_subvols(int fd, u64 **ids)
1214 int ret;
1215 struct btrfs_ioctl_search_args args;
1216 struct btrfs_ioctl_search_key *sk = &args.key;
1217 int idx = 0;
1218 int count = 0;
1220 memset(&args, 0, sizeof(args));
1222 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1223 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1224 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1225 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1226 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1227 sk->min_offset = 0;
1228 sk->max_offset = (u64)-1;
1229 sk->min_transid = 0;
1230 sk->max_transid = (u64)-1;
1231 sk->nr_items = 4096;
1233 *ids = NULL;
1234 while (1) {
1235 struct btrfs_ioctl_search_header *sh;
1236 unsigned long off;
1237 int i;
1239 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1240 if (ret < 0)
1241 return -errno;
1243 if (!sk->nr_items)
1244 return idx;
1246 off = 0;
1247 for (i = 0; i < sk->nr_items; i++) {
1248 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1249 off += sizeof(*sh);
1251 if (btrfs_search_header_type(sh)
1252 == BTRFS_ORPHAN_ITEM_KEY) {
1253 if (idx >= count) {
1254 u64 *newids;
1256 count += SUBVOL_ID_BATCH;
1257 newids = (u64*)realloc(*ids,
1258 count * sizeof(u64));
1259 if (!newids)
1260 return -ENOMEM;
1261 *ids = newids;
1263 (*ids)[idx] = btrfs_search_header_offset(sh);
1264 idx++;
1266 off += btrfs_search_header_len(sh);
1268 sk->min_objectid = btrfs_search_header_objectid(sh);
1269 sk->min_type = btrfs_search_header_type(sh);
1270 sk->min_offset = btrfs_search_header_offset(sh);
1272 if (sk->min_offset < (u64)-1)
1273 sk->min_offset++;
1274 else
1275 break;
1276 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1277 break;
1278 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1279 break;
1282 return idx;
1285 static int cmd_subvol_sync(int argc, char **argv)
1287 int fd = -1;
1288 int i;
1289 int ret = 1;
1290 DIR *dirstream = NULL;
1291 u64 *ids = NULL;
1292 int id_count;
1293 int sleep_interval = 1;
1295 while (1) {
1296 int c = getopt(argc, argv, "s:");
1298 if (c < 0)
1299 break;
1301 switch (c) {
1302 case 's':
1303 sleep_interval = atoi(optarg);
1304 if (sleep_interval < 1) {
1305 error("invalid sleep interval %s", optarg);
1306 ret = 1;
1307 goto out;
1309 break;
1310 default:
1311 usage(cmd_subvol_sync_usage);
1315 if (check_argc_min(argc - optind, 1))
1316 usage(cmd_subvol_sync_usage);
1318 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1319 if (fd < 0) {
1320 ret = 1;
1321 goto out;
1323 optind++;
1325 id_count = argc - optind;
1326 if (!id_count) {
1327 id_count = enumerate_dead_subvols(fd, &ids);
1328 if (id_count < 0) {
1329 error("can't enumerate dead subvolumes: %s",
1330 strerror(-id_count));
1331 ret = 1;
1332 goto out;
1334 if (id_count == 0) {
1335 ret = 0;
1336 goto out;
1338 } else {
1339 ids = (u64*)malloc(id_count * sizeof(u64));
1340 if (!ids) {
1341 error("not enough memory");
1342 ret = 1;
1343 goto out;
1346 for (i = 0; i < id_count; i++) {
1347 u64 id;
1348 const char *arg;
1350 arg = argv[optind + i];
1351 errno = 0;
1352 id = strtoull(arg, NULL, 10);
1353 if (errno < 0) {
1354 error("unrecognized subvolume id %s", arg);
1355 ret = 1;
1356 goto out;
1358 if (id < BTRFS_FIRST_FREE_OBJECTID
1359 || id > BTRFS_LAST_FREE_OBJECTID) {
1360 error("subvolume id %s out of range", arg);
1361 ret = 1;
1362 goto out;
1364 ids[i] = id;
1368 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1370 out:
1371 free(ids);
1372 close_file_or_dir(fd, dirstream);
1374 return !!ret;
1377 static const char subvolume_cmd_group_info[] =
1378 "manage subvolumes: create, delete, list, etc";
1380 const struct cmd_group subvolume_cmd_group = {
1381 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1382 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1383 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1384 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1385 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1386 NULL, 0 },
1387 { "get-default", cmd_subvol_get_default,
1388 cmd_subvol_get_default_usage, NULL, 0 },
1389 { "set-default", cmd_subvol_set_default,
1390 cmd_subvol_set_default_usage, NULL, 0 },
1391 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1392 NULL, 0 },
1393 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1394 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1395 NULL_CMD_STRUCT
1399 int cmd_subvolume(int argc, char **argv)
1401 return handle_command_group(&subvolume_cmd_group, argc, argv);