btrfs-progs: add btrfs_clear_free_space_tree() from the kernel
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob4cd2e0ec297da963f7bed8f92b6f2a1a97aa203a
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/vfs.h>
25 #include <libgen.h>
26 #include <limits.h>
27 #include <getopt.h>
28 #include <uuid/uuid.h>
29 #include <linux/magic.h>
31 #include "kerncompat.h"
32 #include "ioctl.h"
33 #include "qgroup.h"
35 #include "ctree.h"
36 #include "commands.h"
37 #include "utils.h"
38 #include "btrfs-list.h"
39 #include "utils.h"
41 static int is_subvolume_cleaned(int fd, u64 subvolid)
43 int ret;
44 struct btrfs_ioctl_search_args args;
45 struct btrfs_ioctl_search_key *sk = &args.key;
47 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
48 sk->min_objectid = subvolid;
49 sk->max_objectid = subvolid;
50 sk->min_type = BTRFS_ROOT_ITEM_KEY;
51 sk->max_type = BTRFS_ROOT_ITEM_KEY;
52 sk->min_offset = 0;
53 sk->max_offset = (u64)-1;
54 sk->min_transid = 0;
55 sk->max_transid = (u64)-1;
56 sk->nr_items = 1;
58 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
59 if (ret < 0)
60 return -errno;
62 if (sk->nr_items == 0)
63 return 1;
65 return 0;
68 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
69 int sleep_interval)
71 int ret;
72 int i;
74 while (1) {
75 int clean = 1;
77 for (i = 0; i < count; i++) {
78 if (!ids[i])
79 continue;
80 ret = is_subvolume_cleaned(fd, ids[i]);
81 if (ret < 0) {
82 error(
83 "cannot read status of dead subvolume %llu: %s",
84 (unsigned long long)ids[i], strerror(-ret));
85 return ret;
87 if (ret) {
88 printf("Subvolume id %llu is gone\n", ids[i]);
89 ids[i] = 0;
90 } else {
91 clean = 0;
94 if (clean)
95 break;
96 sleep(sleep_interval);
99 return 0;
102 static const char * const subvolume_cmd_group_usage[] = {
103 "btrfs subvolume <command> <args>",
104 NULL
107 static const char * const cmd_subvol_create_usage[] = {
108 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
109 "Create a subvolume",
110 "Create a subvolume <name> in <dest>. If <dest> is not given",
111 "subvolume <name> will be created in the current directory.",
113 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
114 " option can be given multiple times.",
115 NULL
118 static int cmd_subvol_create(int argc, char **argv)
120 int retval, res, len;
121 int fddst = -1;
122 char *dupname = NULL;
123 char *dupdir = NULL;
124 char *newname;
125 char *dstdir;
126 char *dst;
127 struct btrfs_qgroup_inherit *inherit = NULL;
128 DIR *dirstream = NULL;
130 while (1) {
131 int c = getopt(argc, argv, "c:i:");
132 if (c < 0)
133 break;
135 switch (c) {
136 case 'c':
137 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
138 if (res) {
139 retval = res;
140 goto out;
142 break;
143 case 'i':
144 res = qgroup_inherit_add_group(&inherit, optarg);
145 if (res) {
146 retval = res;
147 goto out;
149 break;
150 default:
151 usage(cmd_subvol_create_usage);
155 if (check_argc_exact(argc - optind, 1))
156 usage(cmd_subvol_create_usage);
158 dst = argv[optind];
160 retval = 1; /* failure */
161 res = test_isdir(dst);
162 if (res < 0 && res != -ENOENT) {
163 error("cannot access %s: %s", dst, strerror(-res));
164 goto out;
166 if (res >= 0) {
167 error("target path already exists: %s", dst);
168 goto out;
171 dupname = strdup(dst);
172 newname = basename(dupname);
173 dupdir = strdup(dst);
174 dstdir = dirname(dupdir);
176 if (!test_issubvolname(newname)) {
177 error("invalid subvolume name: %s", newname);
178 goto out;
181 len = strlen(newname);
182 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
183 error("subvolume name too long: %s", newname);
184 goto out;
187 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
188 if (fddst < 0)
189 goto out;
191 printf("Create subvolume '%s/%s'\n", dstdir, newname);
192 if (inherit) {
193 struct btrfs_ioctl_vol_args_v2 args;
195 memset(&args, 0, sizeof(args));
196 strncpy_null(args.name, newname);
197 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
198 args.size = qgroup_inherit_size(inherit);
199 args.qgroup_inherit = inherit;
201 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
202 } else {
203 struct btrfs_ioctl_vol_args args;
205 memset(&args, 0, sizeof(args));
206 strncpy_null(args.name, newname);
208 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
211 if (res < 0) {
212 error("cannot create subvolume: %s", strerror(errno));
213 goto out;
216 retval = 0; /* success */
217 out:
218 close_file_or_dir(fddst, dirstream);
219 free(inherit);
220 free(dupname);
221 free(dupdir);
223 return retval;
226 static int wait_for_commit(int fd)
228 int ret;
230 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
231 if (ret < 0)
232 return ret;
233 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
236 static const char * const cmd_subvol_delete_usage[] = {
237 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
238 "Delete subvolume(s)",
239 "Delete subvolumes from the filesystem. The corresponding directory",
240 "is removed instantly but the data blocks are removed later.",
241 "The deletion does not involve full commit by default due to",
242 "performance reasons (as a consequence, the subvolume may appear again",
243 "after a crash). Use one of the --commit options to wait until the",
244 "operation is safely stored on the media.",
246 "-c|--commit-after wait for transaction commit at the end of the operation",
247 "-C|--commit-each wait for transaction commit after deleting each subvolume",
248 "-v|--verbose verbose output of operations",
249 NULL
252 static int cmd_subvol_delete(int argc, char **argv)
254 int res, ret = 0;
255 int cnt;
256 int fd = -1;
257 struct btrfs_ioctl_vol_args args;
258 char *dname, *vname, *cpath;
259 char *dupdname = NULL;
260 char *dupvname = NULL;
261 char *path;
262 DIR *dirstream = NULL;
263 int verbose = 0;
264 int commit_mode = 0;
266 while (1) {
267 int c;
268 static const struct option long_options[] = {
269 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
270 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
271 {"verbose", no_argument, NULL, 'v'},
272 {NULL, 0, NULL, 0}
275 c = getopt_long(argc, argv, "cCv", long_options, NULL);
276 if (c < 0)
277 break;
279 switch(c) {
280 case 'c':
281 commit_mode = 1;
282 break;
283 case 'C':
284 commit_mode = 2;
285 break;
286 case 'v':
287 verbose++;
288 break;
289 default:
290 usage(cmd_subvol_delete_usage);
294 if (check_argc_min(argc - optind, 1))
295 usage(cmd_subvol_delete_usage);
297 if (verbose > 0) {
298 printf("Transaction commit: %s\n",
299 !commit_mode ? "none (default)" :
300 commit_mode == 1 ? "at the end" : "after each");
303 cnt = optind;
305 again:
306 path = argv[cnt];
308 res = test_issubvolume(path);
309 if (res < 0) {
310 error("cannot access subvolume %s: %s", path, strerror(-res));
311 ret = 1;
312 goto out;
314 if (!res) {
315 error("not a subvolume: %s", path);
316 ret = 1;
317 goto out;
320 cpath = realpath(path, NULL);
321 if (!cpath) {
322 ret = errno;
323 error("cannot find real path for '%s': %s",
324 path, strerror(errno));
325 goto out;
327 dupdname = strdup(cpath);
328 dname = dirname(dupdname);
329 dupvname = strdup(cpath);
330 vname = basename(dupvname);
331 free(cpath);
333 fd = btrfs_open_dir(dname, &dirstream, 1);
334 if (fd < 0) {
335 ret = 1;
336 goto out;
339 printf("Delete subvolume (%s): '%s/%s'\n",
340 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
341 ? "commit" : "no-commit", dname, vname);
342 memset(&args, 0, sizeof(args));
343 strncpy_null(args.name, vname);
344 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
345 if(res < 0 ){
346 error("cannot delete '%s/%s': %s", dname, vname,
347 strerror(errno));
348 ret = 1;
349 goto out;
352 if (commit_mode == 1) {
353 res = wait_for_commit(fd);
354 if (res < 0) {
355 error("unable to wait for commit after '%s': %s",
356 path, strerror(errno));
357 ret = 1;
361 out:
362 free(dupdname);
363 free(dupvname);
364 dupdname = NULL;
365 dupvname = NULL;
366 cnt++;
367 if (cnt < argc) {
368 close_file_or_dir(fd, dirstream);
369 /* avoid double free */
370 fd = -1;
371 dirstream = NULL;
372 goto again;
375 if (commit_mode == 2 && fd != -1) {
376 res = wait_for_commit(fd);
377 if (res < 0) {
378 error("unable to do final sync after deletion: %s",
379 strerror(errno));
380 ret = 1;
383 close_file_or_dir(fd, dirstream);
385 return ret;
389 * Naming of options:
390 * - uppercase for filters and sort options
391 * - lowercase for enabling specific items in the output
393 static const char * const cmd_subvol_list_usage[] = {
394 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
395 "[--sort=gen,ogen,rootid,path] <path>",
396 "List subvolumes (and snapshots)",
398 "-p print parent ID",
399 "-a print all the subvolumes in the filesystem and",
400 " distinguish absolute and relative path with respect",
401 " to the given <path>",
402 "-c print the ogeneration of the subvolume",
403 "-g print the generation of the subvolume",
404 "-o print only subvolumes below specified path",
405 "-u print the uuid of subvolumes (and snapshots)",
406 "-q print the parent uuid of the snapshots",
407 "-R print the uuid of the received snapshots",
408 "-t print the result as a table",
409 "-s list snapshots only in the filesystem",
410 "-r list readonly subvolumes (including snapshots)",
411 "-d list deleted subvolumes that are not yet cleaned",
412 "-G [+|-]value",
413 " filter the subvolumes by generation",
414 " (+value: >= value; -value: <= value; value: = value)",
415 "-C [+|-]value",
416 " filter the subvolumes by ogeneration",
417 " (+value: >= value; -value: <= value; value: = value)",
418 "--sort=gen,ogen,rootid,path",
419 " list the subvolume in order of gen, ogen, rootid or path",
420 " you also can add '+' or '-' in front of each items.",
421 " (+:ascending, -:descending, ascending default)",
422 NULL,
425 static int cmd_subvol_list(int argc, char **argv)
427 struct btrfs_list_filter_set *filter_set;
428 struct btrfs_list_comparer_set *comparer_set;
429 u64 flags = 0;
430 int fd = -1;
431 u64 top_id;
432 int ret = -1, uerr = 0;
433 char *subvol;
434 int is_list_all = 0;
435 int is_only_in_path = 0;
436 DIR *dirstream = NULL;
437 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
439 filter_set = btrfs_list_alloc_filter_set();
440 comparer_set = btrfs_list_alloc_comparer_set();
442 while(1) {
443 int c;
444 static const struct option long_options[] = {
445 {"sort", required_argument, NULL, 'S'},
446 {NULL, 0, NULL, 0}
449 c = getopt_long(argc, argv,
450 "acdgopqsurRG:C:t", long_options, NULL);
451 if (c < 0)
452 break;
454 switch(c) {
455 case 'p':
456 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
457 break;
458 case 'a':
459 is_list_all = 1;
460 break;
461 case 'c':
462 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
463 break;
464 case 'd':
465 btrfs_list_setup_filter(&filter_set,
466 BTRFS_LIST_FILTER_DELETED,
468 break;
469 case 'g':
470 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
471 break;
472 case 'o':
473 is_only_in_path = 1;
474 break;
475 case 't':
476 layout = BTRFS_LIST_LAYOUT_TABLE;
477 break;
478 case 's':
479 btrfs_list_setup_filter(&filter_set,
480 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
482 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
483 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
484 break;
485 case 'u':
486 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
487 break;
488 case 'q':
489 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
490 break;
491 case 'R':
492 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
493 break;
494 case 'r':
495 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
496 break;
497 case 'G':
498 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
499 ret = btrfs_list_parse_filter_string(optarg,
500 &filter_set,
501 BTRFS_LIST_FILTER_GEN);
502 if (ret) {
503 uerr = 1;
504 goto out;
506 break;
508 case 'C':
509 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
510 ret = btrfs_list_parse_filter_string(optarg,
511 &filter_set,
512 BTRFS_LIST_FILTER_CGEN);
513 if (ret) {
514 uerr = 1;
515 goto out;
517 break;
518 case 'S':
519 ret = btrfs_list_parse_sort_string(optarg,
520 &comparer_set);
521 if (ret) {
522 uerr = 1;
523 goto out;
525 break;
527 default:
528 uerr = 1;
529 goto out;
533 if (check_argc_exact(argc - optind, 1)) {
534 uerr = 1;
535 goto out;
538 subvol = argv[optind];
539 fd = btrfs_open_dir(subvol, &dirstream, 1);
540 if (fd < 0) {
541 ret = -1;
542 error("can't access '%s'", subvol);
543 goto out;
546 if (flags)
547 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
548 flags);
550 ret = btrfs_list_get_path_rootid(fd, &top_id);
551 if (ret)
552 goto out;
554 if (is_list_all)
555 btrfs_list_setup_filter(&filter_set,
556 BTRFS_LIST_FILTER_FULL_PATH,
557 top_id);
558 else if (is_only_in_path)
559 btrfs_list_setup_filter(&filter_set,
560 BTRFS_LIST_FILTER_TOPID_EQUAL,
561 top_id);
563 /* by default we shall print the following columns*/
564 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
565 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
566 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
567 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
569 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
570 layout, !is_list_all && !is_only_in_path, NULL);
572 out:
573 close_file_or_dir(fd, dirstream);
574 if (filter_set)
575 free(filter_set);
576 if (comparer_set)
577 free(comparer_set);
578 if (uerr)
579 usage(cmd_subvol_list_usage);
580 return !!ret;
583 static const char * const cmd_subvol_snapshot_usage[] = {
584 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
585 "Create a snapshot of the subvolume",
586 "Create a writable/readonly snapshot of the subvolume <source> with",
587 "the name <name> in the <dest> directory. If only <dest> is given,",
588 "the subvolume will be named the basename of <source>.",
590 "-r create a readonly snapshot",
591 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
592 " option can be given multiple times.",
593 NULL
596 static int cmd_subvol_snapshot(int argc, char **argv)
598 char *subvol, *dst;
599 int res, retval;
600 int fd = -1, fddst = -1;
601 int len, readonly = 0;
602 char *dupname = NULL;
603 char *dupdir = NULL;
604 char *newname;
605 char *dstdir;
606 struct btrfs_ioctl_vol_args_v2 args;
607 struct btrfs_qgroup_inherit *inherit = NULL;
608 DIR *dirstream1 = NULL, *dirstream2 = NULL;
610 memset(&args, 0, sizeof(args));
611 while (1) {
612 int c = getopt(argc, argv, "c:i:r");
613 if (c < 0)
614 break;
616 switch (c) {
617 case 'c':
618 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
619 if (res) {
620 retval = res;
621 goto out;
623 break;
624 case 'i':
625 res = qgroup_inherit_add_group(&inherit, optarg);
626 if (res) {
627 retval = res;
628 goto out;
630 break;
631 case 'r':
632 readonly = 1;
633 break;
634 case 'x':
635 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
636 if (res) {
637 retval = res;
638 goto out;
640 break;
641 default:
642 usage(cmd_subvol_snapshot_usage);
646 if (check_argc_exact(argc - optind, 2))
647 usage(cmd_subvol_snapshot_usage);
649 subvol = argv[optind];
650 dst = argv[optind + 1];
652 retval = 1; /* failure */
653 res = test_issubvolume(subvol);
654 if (res < 0) {
655 error("cannot access subvolume %s: %s", subvol, strerror(-res));
656 goto out;
658 if (!res) {
659 error("not a subvolume: %s", subvol);
660 goto out;
663 res = test_isdir(dst);
664 if (res < 0 && res != -ENOENT) {
665 error("cannot access %s: %s", dst, strerror(-res));
666 goto out;
668 if (res == 0) {
669 error("'%s' exists and it is not a directory", dst);
670 goto out;
673 if (res > 0) {
674 dupname = strdup(subvol);
675 newname = basename(dupname);
676 dstdir = dst;
677 } else {
678 dupname = strdup(dst);
679 newname = basename(dupname);
680 dupdir = strdup(dst);
681 dstdir = dirname(dupdir);
684 if (!test_issubvolname(newname)) {
685 error("invalid snapshot name '%s'", newname);
686 goto out;
689 len = strlen(newname);
690 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
691 error("snapshot name too long '%s'", newname);
692 goto out;
695 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
696 if (fddst < 0)
697 goto out;
699 fd = btrfs_open_dir(subvol, &dirstream2, 1);
700 if (fd < 0)
701 goto out;
703 if (readonly) {
704 args.flags |= BTRFS_SUBVOL_RDONLY;
705 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
706 subvol, dstdir, newname);
707 } else {
708 printf("Create a snapshot of '%s' in '%s/%s'\n",
709 subvol, dstdir, newname);
712 args.fd = fd;
713 if (inherit) {
714 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
715 args.size = qgroup_inherit_size(inherit);
716 args.qgroup_inherit = inherit;
718 strncpy_null(args.name, newname);
720 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
722 if (res < 0) {
723 error("cannot snapshot '%s': %s", subvol, strerror(errno));
724 goto out;
727 retval = 0; /* success */
729 out:
730 close_file_or_dir(fddst, dirstream1);
731 close_file_or_dir(fd, dirstream2);
732 free(inherit);
733 free(dupname);
734 free(dupdir);
736 return retval;
739 static const char * const cmd_subvol_get_default_usage[] = {
740 "btrfs subvolume get-default <path>",
741 "Get the default subvolume of a filesystem",
742 NULL
745 static int cmd_subvol_get_default(int argc, char **argv)
747 int fd = -1;
748 int ret;
749 char *subvol;
750 struct btrfs_list_filter_set *filter_set;
751 u64 default_id;
752 DIR *dirstream = NULL;
754 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
756 if (check_argc_exact(argc - optind, 1))
757 usage(cmd_subvol_get_default_usage);
759 subvol = argv[1];
760 fd = btrfs_open_dir(subvol, &dirstream, 1);
761 if (fd < 0)
762 return 1;
764 ret = btrfs_list_get_default_subvolume(fd, &default_id);
765 if (ret) {
766 error("failed to look up default subvolume: %s",
767 strerror(errno));
768 goto out;
771 ret = 1;
772 if (default_id == 0) {
773 error("'default' dir item not found");
774 goto out;
777 /* no need to resolve roots if FS_TREE is default */
778 if (default_id == BTRFS_FS_TREE_OBJECTID) {
779 printf("ID 5 (FS_TREE)\n");
780 ret = 0;
781 goto out;
784 filter_set = btrfs_list_alloc_filter_set();
785 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
786 default_id);
788 /* by default we shall print the following columns*/
789 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
790 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
791 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
792 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
794 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
795 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
797 if (filter_set)
798 free(filter_set);
799 out:
800 close_file_or_dir(fd, dirstream);
801 return !!ret;
804 static const char * const cmd_subvol_set_default_usage[] = {
805 "btrfs subvolume set-default <subvolid> <path>",
806 "Set the default subvolume of a filesystem",
807 NULL
810 static int cmd_subvol_set_default(int argc, char **argv)
812 int ret=0, fd, e;
813 u64 objectid;
814 char *path;
815 char *subvolid;
816 DIR *dirstream = NULL;
818 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
820 if (check_argc_exact(argc - optind, 2))
821 usage(cmd_subvol_set_default_usage);
823 subvolid = argv[optind];
824 path = argv[optind + 1];
826 objectid = arg_strtou64(subvolid);
828 fd = btrfs_open_dir(path, &dirstream, 1);
829 if (fd < 0)
830 return 1;
832 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
833 e = errno;
834 close_file_or_dir(fd, dirstream);
835 if (ret < 0) {
836 error("unable to set a new default subvolume: %s",
837 strerror(e));
838 return 1;
840 return 0;
843 static const char * const cmd_subvol_find_new_usage[] = {
844 "btrfs subvolume find-new <path> <lastgen>",
845 "List the recently modified files in a filesystem",
846 NULL
849 static int cmd_subvol_find_new(int argc, char **argv)
851 int fd;
852 int ret;
853 char *subvol;
854 u64 last_gen;
855 DIR *dirstream = NULL;
857 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
859 if (check_argc_exact(argc - optind, 2))
860 usage(cmd_subvol_find_new_usage);
862 subvol = argv[optind];
863 last_gen = arg_strtou64(argv[optind + 1]);
865 ret = test_issubvolume(subvol);
866 if (ret < 0) {
867 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
868 return 1;
870 if (!ret) {
871 error("not a subvolume: %s", subvol);
872 return 1;
875 fd = btrfs_open_dir(subvol, &dirstream, 1);
876 if (fd < 0)
877 return 1;
879 ret = ioctl(fd, BTRFS_IOC_SYNC);
880 if (ret < 0) {
881 error("sync ioctl failed on '%s': %s",
882 subvol, strerror(errno));
883 close_file_or_dir(fd, dirstream);
884 return 1;
887 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
888 close_file_or_dir(fd, dirstream);
889 return !!ret;
892 static const char * const cmd_subvol_show_usage[] = {
893 "btrfs subvolume show <subvol-path>",
894 "Show more information of the subvolume",
895 NULL
898 static int cmd_subvol_show(int argc, char **argv)
900 struct root_info get_ri;
901 struct btrfs_list_filter_set *filter_set = NULL;
902 char tstr[256];
903 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
904 char *fullpath = NULL;
905 char raw_prefix[] = "\t\t\t\t";
906 int fd = -1;
907 int ret = 1;
908 DIR *dirstream1 = NULL;
910 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
912 if (check_argc_exact(argc - optind, 1))
913 usage(cmd_subvol_show_usage);
915 memset(&get_ri, 0, sizeof(get_ri));
916 fullpath = realpath(argv[optind], NULL);
917 if (!fullpath) {
918 error("cannot find real path for '%s': %s",
919 argv[optind], strerror(errno));
920 goto out;
923 ret = get_subvol_info(fullpath, &get_ri);
924 if (ret) {
925 if (ret < 0) {
926 error("Failed to get subvol info %s: %s\n",
927 fullpath, strerror(-ret));
928 } else {
929 error("Failed to get subvol info %s: %d\n",
930 fullpath, ret);
932 return ret;
935 /* print the info */
936 printf("%s\n", fullpath);
937 printf("\tName: \t\t\t%s\n", get_ri.name);
939 if (uuid_is_null(get_ri.uuid))
940 strcpy(uuidparse, "-");
941 else
942 uuid_unparse(get_ri.uuid, uuidparse);
943 printf("\tUUID: \t\t\t%s\n", uuidparse);
945 if (uuid_is_null(get_ri.puuid))
946 strcpy(uuidparse, "-");
947 else
948 uuid_unparse(get_ri.puuid, uuidparse);
949 printf("\tParent UUID: \t\t%s\n", uuidparse);
951 if (uuid_is_null(get_ri.ruuid))
952 strcpy(uuidparse, "-");
953 else
954 uuid_unparse(get_ri.ruuid, uuidparse);
955 printf("\tReceived UUID: \t\t%s\n", uuidparse);
957 if (get_ri.otime) {
958 struct tm tm;
960 localtime_r(&get_ri.otime, &tm);
961 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
962 } else
963 strcpy(tstr, "-");
964 printf("\tCreation time: \t\t%s\n", tstr);
966 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
967 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
968 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
969 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
970 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
972 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
973 printf("\tFlags: \t\t\treadonly\n");
974 else
975 printf("\tFlags: \t\t\t-\n");
977 /* print the snapshots of the given subvol if any*/
978 printf("\tSnapshot(s):\n");
979 filter_set = btrfs_list_alloc_filter_set();
980 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
981 (u64)(unsigned long)get_ri.uuid);
982 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
984 fd = open_file_or_dir(fullpath, &dirstream1);
985 if (fd < 0) {
986 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
987 goto out;
989 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
990 1, raw_prefix);
992 out:
993 /* clean up */
994 free(get_ri.path);
995 free(get_ri.name);
996 free(get_ri.full_path);
997 free(filter_set);
999 close_file_or_dir(fd, dirstream1);
1000 free(fullpath);
1001 return !!ret;
1004 static const char * const cmd_subvol_sync_usage[] = {
1005 "btrfs subvolume sync <path> [<subvol-id>...]",
1006 "Wait until given subvolume(s) are completely removed from the filesystem.",
1007 "Wait until given subvolume(s) are completely removed from the filesystem",
1008 "after deletion.",
1009 "If no subvolume id is given, wait until all current deletion requests",
1010 "are completed, but do not wait for subvolumes deleted meanwhile.",
1011 "The status of subvolume ids is checked periodically.",
1013 "-s <N> sleep N seconds between checks (default: 1)",
1014 NULL
1017 #if 0
1019 * If we're looking for any dead subvolume, take a shortcut and look
1020 * for any ORPHAN_ITEMs in the tree root
1022 static int fs_has_dead_subvolumes(int fd)
1024 int ret;
1025 struct btrfs_ioctl_search_args args;
1026 struct btrfs_ioctl_search_key *sk = &args.key;
1027 struct btrfs_ioctl_search_header sh;
1028 u64 min_subvolid = 0;
1030 again:
1031 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1032 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1033 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1034 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1035 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1036 sk->min_offset = min_subvolid;
1037 sk->max_offset = (u64)-1;
1038 sk->min_transid = 0;
1039 sk->max_transid = (u64)-1;
1040 sk->nr_items = 1;
1042 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1043 if (ret < 0)
1044 return -errno;
1046 if (!sk->nr_items)
1047 return 0;
1049 memcpy(&sh, args.buf, sizeof(sh));
1050 min_subvolid = sh.offset;
1053 * Verify that the root item is really there and we haven't hit
1054 * a stale orphan
1056 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1057 sk->min_objectid = min_subvolid;
1058 sk->max_objectid = min_subvolid;
1059 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1060 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1061 sk->min_offset = 0;
1062 sk->max_offset = (u64)-1;
1063 sk->min_transid = 0;
1064 sk->max_transid = (u64)-1;
1065 sk->nr_items = 1;
1067 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1068 if (ret < 0)
1069 return -errno;
1072 * Stale orphan, try the next one
1074 if (!sk->nr_items) {
1075 min_subvolid++;
1076 goto again;
1079 return 1;
1081 #endif
1083 #define SUBVOL_ID_BATCH 1024
1086 * Enumerate all dead subvolumes that exist in the filesystem.
1087 * Fill @ids and reallocate to bigger size if needed.
1089 static int enumerate_dead_subvols(int fd, u64 **ids)
1091 int ret;
1092 struct btrfs_ioctl_search_args args;
1093 struct btrfs_ioctl_search_key *sk = &args.key;
1094 int idx = 0;
1095 int count = 0;
1097 memset(&args, 0, sizeof(args));
1099 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1100 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1101 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1102 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1103 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1104 sk->min_offset = 0;
1105 sk->max_offset = (u64)-1;
1106 sk->min_transid = 0;
1107 sk->max_transid = (u64)-1;
1108 sk->nr_items = 4096;
1110 *ids = NULL;
1111 while (1) {
1112 struct btrfs_ioctl_search_header *sh;
1113 unsigned long off;
1114 int i;
1116 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1117 if (ret < 0)
1118 return -errno;
1120 if (!sk->nr_items)
1121 return idx;
1123 off = 0;
1124 for (i = 0; i < sk->nr_items; i++) {
1125 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1126 off += sizeof(*sh);
1128 if (btrfs_search_header_type(sh)
1129 == BTRFS_ORPHAN_ITEM_KEY) {
1130 if (idx >= count) {
1131 u64 *newids;
1133 count += SUBVOL_ID_BATCH;
1134 newids = (u64*)realloc(*ids,
1135 count * sizeof(u64));
1136 if (!newids)
1137 return -ENOMEM;
1138 *ids = newids;
1140 (*ids)[idx] = btrfs_search_header_offset(sh);
1141 idx++;
1143 off += btrfs_search_header_len(sh);
1145 sk->min_objectid = btrfs_search_header_objectid(sh);
1146 sk->min_type = btrfs_search_header_type(sh);
1147 sk->min_offset = btrfs_search_header_offset(sh);
1149 if (sk->min_offset < (u64)-1)
1150 sk->min_offset++;
1151 else
1152 break;
1153 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1154 break;
1155 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1156 break;
1159 return idx;
1162 static int cmd_subvol_sync(int argc, char **argv)
1164 int fd = -1;
1165 int i;
1166 int ret = 1;
1167 DIR *dirstream = NULL;
1168 u64 *ids = NULL;
1169 int id_count;
1170 int sleep_interval = 1;
1172 while (1) {
1173 int c = getopt(argc, argv, "s:");
1175 if (c < 0)
1176 break;
1178 switch (c) {
1179 case 's':
1180 sleep_interval = atoi(optarg);
1181 if (sleep_interval < 1) {
1182 error("invalid sleep interval %s", optarg);
1183 ret = 1;
1184 goto out;
1186 break;
1187 default:
1188 usage(cmd_subvol_sync_usage);
1192 if (check_argc_min(argc - optind, 1))
1193 usage(cmd_subvol_sync_usage);
1195 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1196 if (fd < 0) {
1197 ret = 1;
1198 goto out;
1200 optind++;
1202 id_count = argc - optind;
1203 if (!id_count) {
1204 id_count = enumerate_dead_subvols(fd, &ids);
1205 if (id_count < 0) {
1206 error("can't enumerate dead subvolumes: %s",
1207 strerror(-id_count));
1208 ret = 1;
1209 goto out;
1211 if (id_count == 0) {
1212 ret = 0;
1213 goto out;
1215 } else {
1216 ids = (u64*)malloc(id_count * sizeof(u64));
1217 if (!ids) {
1218 error("not enough memory");
1219 ret = 1;
1220 goto out;
1223 for (i = 0; i < id_count; i++) {
1224 u64 id;
1225 const char *arg;
1227 arg = argv[optind + i];
1228 errno = 0;
1229 id = strtoull(arg, NULL, 10);
1230 if (errno < 0) {
1231 error("unrecognized subvolume id %s", arg);
1232 ret = 1;
1233 goto out;
1235 if (id < BTRFS_FIRST_FREE_OBJECTID
1236 || id > BTRFS_LAST_FREE_OBJECTID) {
1237 error("subvolume id %s out of range\n", arg);
1238 ret = 1;
1239 goto out;
1241 ids[i] = id;
1245 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1247 out:
1248 free(ids);
1249 close_file_or_dir(fd, dirstream);
1251 return !!ret;
1254 static const char subvolume_cmd_group_info[] =
1255 "manage subvolumes: create, delete, list, etc";
1257 const struct cmd_group subvolume_cmd_group = {
1258 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1259 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1260 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1261 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1262 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1263 NULL, 0 },
1264 { "get-default", cmd_subvol_get_default,
1265 cmd_subvol_get_default_usage, NULL, 0 },
1266 { "set-default", cmd_subvol_set_default,
1267 cmd_subvol_set_default_usage, NULL, 0 },
1268 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1269 NULL, 0 },
1270 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1271 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1272 NULL_CMD_STRUCT
1276 int cmd_subvolume(int argc, char **argv)
1278 return handle_command_group(&subvolume_cmd_group, argc, argv);