btrfs-progs: use the correct SI prefixes
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blobfa58a24426285e5ec08d8f03836eabc5320e0915
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 <libgen.h>
25 #include <limits.h>
26 #include <getopt.h>
27 #include <uuid/uuid.h>
29 #include "kerncompat.h"
30 #include "ioctl.h"
31 #include "qgroup.h"
33 #include "ctree.h"
34 #include "commands.h"
35 #include "utils.h"
36 #include "btrfs-list.h"
37 #include "utils.h"
39 static const char * const subvolume_cmd_group_usage[] = {
40 "btrfs subvolume <command> <args>",
41 NULL
44 static const char * const cmd_subvol_create_usage[] = {
45 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
46 "Create a subvolume",
47 "Create a subvolume <name> in <dest>. If <dest> is not given",
48 "subvolume <name> will be created in the current directory.",
49 "",
50 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
51 " option can be given multiple times.",
52 NULL
55 static int cmd_subvol_create(int argc, char **argv)
57 int retval, res, len;
58 int fddst = -1;
59 char *dupname = NULL;
60 char *dupdir = NULL;
61 char *newname;
62 char *dstdir;
63 char *dst;
64 struct btrfs_qgroup_inherit *inherit = NULL;
65 DIR *dirstream = NULL;
67 optind = 1;
68 while (1) {
69 int c = getopt(argc, argv, "c:i:");
70 if (c < 0)
71 break;
73 switch (c) {
74 case 'c':
75 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
76 if (res) {
77 retval = res;
78 goto out;
80 break;
81 case 'i':
82 res = qgroup_inherit_add_group(&inherit, optarg);
83 if (res) {
84 retval = res;
85 goto out;
87 break;
88 default:
89 usage(cmd_subvol_create_usage);
93 if (check_argc_exact(argc - optind, 1))
94 usage(cmd_subvol_create_usage);
96 dst = argv[optind];
98 retval = 1; /* failure */
99 res = test_isdir(dst);
100 if (res >= 0) {
101 fprintf(stderr, "ERROR: '%s' exists\n", dst);
102 goto out;
105 dupname = strdup(dst);
106 newname = basename(dupname);
107 dupdir = strdup(dst);
108 dstdir = dirname(dupdir);
110 if (!test_issubvolname(newname)) {
111 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
112 newname);
113 goto out;
116 len = strlen(newname);
117 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
118 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
119 newname);
120 goto out;
123 fddst = open_file_or_dir(dstdir, &dirstream);
124 if (fddst < 0) {
125 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
126 goto out;
129 printf("Create subvolume '%s/%s'\n", dstdir, newname);
130 if (inherit) {
131 struct btrfs_ioctl_vol_args_v2 args;
133 memset(&args, 0, sizeof(args));
134 strncpy_null(args.name, newname);
135 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
136 args.size = qgroup_inherit_size(inherit);
137 args.qgroup_inherit = inherit;
139 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
140 } else {
141 struct btrfs_ioctl_vol_args args;
143 memset(&args, 0, sizeof(args));
144 strncpy_null(args.name, newname);
146 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
149 if (res < 0) {
150 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
151 strerror(errno));
152 goto out;
155 retval = 0; /* success */
156 out:
157 close_file_or_dir(fddst, dirstream);
158 free(inherit);
159 free(dupname);
160 free(dupdir);
162 return retval;
166 * test if path is a subvolume:
167 * this function return
168 * 0-> path exists but it is not a subvolume
169 * 1-> path exists and it is a subvolume
170 * -1 -> path is unaccessible
172 int test_issubvolume(char *path)
174 struct stat st;
175 int res;
177 res = stat(path, &st);
178 if(res < 0 )
179 return -1;
181 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
184 static int wait_for_commit(int fd)
186 int ret;
188 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
189 if (ret < 0)
190 return ret;
191 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
194 static const char * const cmd_subvol_delete_usage[] = {
195 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
196 "Delete subvolume(s)",
197 "Delete subvolumes from the filesystem. The corresponding directory",
198 "is removed instantly but the data blocks are removed later.",
199 "The deletion does not involve full commit by default due to",
200 "performance reasons (as a consequence, the subvolume may appear again",
201 "after a crash). Use one of the --commit options to wait until the",
202 "operation is safely stored on the media.",
204 "-c|--commit-after wait for transaction commit at the end of the operation",
205 "-C|--commit-each wait for transaction commit after deleting each subvolume",
206 NULL
209 static int cmd_subvol_delete(int argc, char **argv)
211 int res, len, e, ret = 0;
212 int cnt;
213 int fd = -1;
214 struct btrfs_ioctl_vol_args args;
215 char *dname, *vname, *cpath;
216 char *dupdname = NULL;
217 char *dupvname = NULL;
218 char *path;
219 DIR *dirstream = NULL;
220 int sync_mode = 0;
221 struct option long_options[] = {
222 {"commit-after", no_argument, NULL, 'c'}, /* sync mode 1 */
223 {"commit-each", no_argument, NULL, 'C'}, /* sync mode 2 */
224 {NULL, 0, NULL, 0}
227 optind = 1;
228 while (1) {
229 int c;
231 c = getopt_long(argc, argv, "cC", long_options, NULL);
232 if (c < 0)
233 break;
235 switch(c) {
236 case 'c':
237 sync_mode = 1;
238 break;
239 case 'C':
240 sync_mode = 2;
241 break;
242 default:
243 usage(cmd_subvol_delete_usage);
247 if (check_argc_min(argc - optind, 1))
248 usage(cmd_subvol_delete_usage);
250 printf("Transaction commit: %s\n",
251 !sync_mode ? "none (default)" :
252 sync_mode == 1 ? "at the end" : "after each");
254 cnt = optind;
256 again:
257 path = argv[cnt];
259 res = test_issubvolume(path);
260 if (res < 0) {
261 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
262 ret = 1;
263 goto out;
265 if (!res) {
266 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
267 ret = 1;
268 goto out;
271 cpath = realpath(path, NULL);
272 if (!cpath) {
273 ret = errno;
274 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
275 path, strerror(errno));
276 goto out;
278 dupdname = strdup(cpath);
279 dname = dirname(dupdname);
280 dupvname = strdup(cpath);
281 vname = basename(dupvname);
282 free(cpath);
284 if (!test_issubvolname(vname)) {
285 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
286 vname);
287 ret = 1;
288 goto out;
291 len = strlen(vname);
292 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
293 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
294 vname);
295 ret = 1;
296 goto out;
299 fd = open_file_or_dir(dname, &dirstream);
300 if (fd < 0) {
301 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
302 ret = 1;
303 goto out;
306 printf("Delete subvolume '%s/%s'\n", dname, vname);
307 strncpy_null(args.name, vname);
308 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
309 e = errno;
311 if(res < 0 ){
312 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
313 dname, vname, strerror(e));
314 ret = 1;
315 goto out;
318 if (sync_mode == 1) {
319 res = wait_for_commit(fd);
320 if (res < 0) {
321 fprintf(stderr,
322 "ERROR: unable to wait for commit after '%s': %s\n",
323 path, strerror(errno));
324 ret = 1;
328 out:
329 free(dupdname);
330 free(dupvname);
331 dupdname = NULL;
332 dupvname = NULL;
333 cnt++;
334 if (cnt < argc) {
335 close_file_or_dir(fd, dirstream);
336 /* avoid double free */
337 fd = -1;
338 dirstream = NULL;
339 goto again;
342 if (sync_mode == 2 && fd != -1) {
343 res = wait_for_commit(fd);
344 if (res < 0) {
345 fprintf(stderr,
346 "ERROR: unable to do final sync: %s\n",
347 strerror(errno));
348 ret = 1;
351 close_file_or_dir(fd, dirstream);
353 return ret;
357 * Naming of options:
358 * - uppercase for filters and sort options
359 * - lowercase for enabling specific items in the output
361 static const char * const cmd_subvol_list_usage[] = {
362 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
363 "[--sort=gen,ogen,rootid,path] <path>",
364 "List subvolumes (and snapshots)",
366 "-p print parent ID",
367 "-a print all the subvolumes in the filesystem and",
368 " distinguish absolute and relative path with respect",
369 " to the given <path>",
370 "-c print the ogeneration of the subvolume",
371 "-g print the generation of the subvolume",
372 "-o print only subvolumes below specified path",
373 "-u print the uuid of subvolumes (and snapshots)",
374 "-q print the parent uuid of the snapshots",
375 "-R print the uuid of the received snapshots",
376 "-t print the result as a table",
377 "-s list snapshots only in the filesystem",
378 "-r list readonly subvolumes (including snapshots)",
379 "-d list deleted subvolumes that are not yet cleaned",
380 "-G [+|-]value",
381 " filter the subvolumes by generation",
382 " (+value: >= value; -value: <= value; value: = value)",
383 "-C [+|-]value",
384 " filter the subvolumes by ogeneration",
385 " (+value: >= value; -value: <= value; value: = value)",
386 "--sort=gen,ogen,rootid,path",
387 " list the subvolume in order of gen, ogen, rootid or path",
388 " you also can add '+' or '-' in front of each items.",
389 " (+:ascending, -:descending, ascending default)",
390 NULL,
393 static int cmd_subvol_list(int argc, char **argv)
395 struct btrfs_list_filter_set *filter_set;
396 struct btrfs_list_comparer_set *comparer_set;
397 u64 flags = 0;
398 int fd = -1;
399 u64 top_id;
400 int ret = -1, uerr = 0;
401 int c;
402 char *subvol;
403 int is_tab_result = 0;
404 int is_list_all = 0;
405 int is_only_in_path = 0;
406 struct option long_options[] = {
407 {"sort", 1, NULL, 'S'},
408 {NULL, 0, NULL, 0}
410 DIR *dirstream = NULL;
412 filter_set = btrfs_list_alloc_filter_set();
413 comparer_set = btrfs_list_alloc_comparer_set();
415 optind = 1;
416 while(1) {
417 c = getopt_long(argc, argv,
418 "acdgopqsurRG:C:t", long_options, NULL);
419 if (c < 0)
420 break;
422 switch(c) {
423 case 'p':
424 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
425 break;
426 case 'a':
427 is_list_all = 1;
428 break;
429 case 'c':
430 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
431 break;
432 case 'd':
433 btrfs_list_setup_filter(&filter_set,
434 BTRFS_LIST_FILTER_DELETED,
436 break;
437 case 'g':
438 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
439 break;
440 case 'o':
441 is_only_in_path = 1;
442 break;
443 case 't':
444 is_tab_result = 1;
445 break;
446 case 's':
447 btrfs_list_setup_filter(&filter_set,
448 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
450 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
451 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
452 break;
453 case 'u':
454 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
455 break;
456 case 'q':
457 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
458 break;
459 case 'R':
460 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
461 break;
462 case 'r':
463 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
464 break;
465 case 'G':
466 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
467 ret = btrfs_list_parse_filter_string(optarg,
468 &filter_set,
469 BTRFS_LIST_FILTER_GEN);
470 if (ret) {
471 uerr = 1;
472 goto out;
474 break;
476 case 'C':
477 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
478 ret = btrfs_list_parse_filter_string(optarg,
479 &filter_set,
480 BTRFS_LIST_FILTER_CGEN);
481 if (ret) {
482 uerr = 1;
483 goto out;
485 break;
486 case 'S':
487 ret = btrfs_list_parse_sort_string(optarg,
488 &comparer_set);
489 if (ret) {
490 uerr = 1;
491 goto out;
493 break;
495 default:
496 uerr = 1;
497 goto out;
501 if (flags)
502 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
503 flags);
505 if (check_argc_exact(argc - optind, 1)) {
506 uerr = 1;
507 goto out;
510 subvol = argv[optind];
511 fd = open_file_or_dir(subvol, &dirstream);
512 if (fd < 0) {
513 ret = -1;
514 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
515 goto out;
518 ret = btrfs_list_get_path_rootid(fd, &top_id);
519 if (ret) {
520 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
521 goto out;
524 if (is_list_all)
525 btrfs_list_setup_filter(&filter_set,
526 BTRFS_LIST_FILTER_FULL_PATH,
527 top_id);
528 else if (is_only_in_path)
529 btrfs_list_setup_filter(&filter_set,
530 BTRFS_LIST_FILTER_TOPID_EQUAL,
531 top_id);
533 /* by default we shall print the following columns*/
534 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
535 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
536 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
537 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
539 if (is_tab_result)
540 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
541 BTRFS_LIST_LAYOUT_TABLE,
542 !is_list_all && !is_only_in_path, NULL);
543 else
544 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
545 BTRFS_LIST_LAYOUT_DEFAULT,
546 !is_list_all && !is_only_in_path, NULL);
548 out:
549 close_file_or_dir(fd, dirstream);
550 if (filter_set)
551 btrfs_list_free_filter_set(filter_set);
552 if (comparer_set)
553 btrfs_list_free_comparer_set(comparer_set);
554 if (uerr)
555 usage(cmd_subvol_list_usage);
556 return !!ret;
559 static const char * const cmd_snapshot_usage[] = {
560 "btrfs subvolume snapshot [-r] <source> <dest>|[<dest>/]<name>",
561 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
562 "Create a snapshot of the subvolume",
563 "Create a writable/readonly snapshot of the subvolume <source> with",
564 "the name <name> in the <dest> directory. If only <dest> is given,",
565 "the subvolume will be named the basename of <source>.",
567 "-r create a readonly snapshot",
568 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
569 " option can be given multiple times.",
570 NULL
573 static int cmd_snapshot(int argc, char **argv)
575 char *subvol, *dst;
576 int res, retval;
577 int fd = -1, fddst = -1;
578 int len, readonly = 0;
579 char *dupname = NULL;
580 char *dupdir = NULL;
581 char *newname;
582 char *dstdir;
583 struct btrfs_ioctl_vol_args_v2 args;
584 struct btrfs_qgroup_inherit *inherit = NULL;
585 DIR *dirstream1 = NULL, *dirstream2 = NULL;
587 optind = 1;
588 memset(&args, 0, sizeof(args));
589 while (1) {
590 int c = getopt(argc, argv, "c:i:r");
591 if (c < 0)
592 break;
594 switch (c) {
595 case 'c':
596 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
597 if (res) {
598 retval = res;
599 goto out;
601 break;
602 case 'i':
603 res = qgroup_inherit_add_group(&inherit, optarg);
604 if (res) {
605 retval = res;
606 goto out;
608 break;
609 case 'r':
610 readonly = 1;
611 break;
612 case 'x':
613 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
614 if (res) {
615 retval = res;
616 goto out;
618 break;
619 default:
620 usage(cmd_snapshot_usage);
624 if (check_argc_exact(argc - optind, 2))
625 usage(cmd_snapshot_usage);
627 subvol = argv[optind];
628 dst = argv[optind + 1];
630 retval = 1; /* failure */
631 res = test_issubvolume(subvol);
632 if (res < 0) {
633 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
634 goto out;
636 if (!res) {
637 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
638 goto out;
641 res = test_isdir(dst);
642 if (res == 0) {
643 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
644 goto out;
647 if (res > 0) {
648 dupname = strdup(subvol);
649 newname = basename(dupname);
650 dstdir = dst;
651 } else {
652 dupname = strdup(dst);
653 newname = basename(dupname);
654 dupdir = strdup(dst);
655 dstdir = dirname(dupdir);
658 if (!test_issubvolname(newname)) {
659 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
660 newname);
661 goto out;
664 len = strlen(newname);
665 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
666 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
667 newname);
668 goto out;
671 fddst = open_file_or_dir(dstdir, &dirstream1);
672 if (fddst < 0) {
673 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
674 goto out;
677 fd = open_file_or_dir(subvol, &dirstream2);
678 if (fd < 0) {
679 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
680 goto out;
683 if (readonly) {
684 args.flags |= BTRFS_SUBVOL_RDONLY;
685 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
686 subvol, dstdir, newname);
687 } else {
688 printf("Create a snapshot of '%s' in '%s/%s'\n",
689 subvol, dstdir, newname);
692 args.fd = fd;
693 if (inherit) {
694 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
695 args.size = qgroup_inherit_size(inherit);
696 args.qgroup_inherit = inherit;
698 strncpy_null(args.name, newname);
700 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
702 if (res < 0) {
703 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
704 subvol, strerror(errno));
705 goto out;
708 retval = 0; /* success */
710 out:
711 close_file_or_dir(fddst, dirstream1);
712 close_file_or_dir(fd, dirstream2);
713 free(inherit);
714 free(dupname);
715 free(dupdir);
717 return retval;
720 static const char * const cmd_subvol_get_default_usage[] = {
721 "btrfs subvolume get-default <path>",
722 "Get the default subvolume of a filesystem",
723 NULL
726 static int cmd_subvol_get_default(int argc, char **argv)
728 int fd = -1;
729 int ret;
730 char *subvol;
731 struct btrfs_list_filter_set *filter_set;
732 u64 default_id;
733 DIR *dirstream = NULL;
735 if (check_argc_exact(argc, 2))
736 usage(cmd_subvol_get_default_usage);
738 subvol = argv[1];
739 fd = open_file_or_dir(subvol, &dirstream);
740 if (fd < 0) {
741 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
742 return 1;
745 ret = btrfs_list_get_default_subvolume(fd, &default_id);
746 if (ret) {
747 fprintf(stderr, "ERROR: can't perform the search - %s\n",
748 strerror(errno));
749 goto out;
752 ret = 1;
753 if (default_id == 0) {
754 fprintf(stderr, "ERROR: 'default' dir item not found\n");
755 goto out;
758 /* no need to resolve roots if FS_TREE is default */
759 if (default_id == BTRFS_FS_TREE_OBJECTID) {
760 printf("ID 5 (FS_TREE)\n");
761 ret = 0;
762 goto out;
765 filter_set = btrfs_list_alloc_filter_set();
766 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
767 default_id);
769 /* by default we shall print the following columns*/
770 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
771 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
772 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
773 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
775 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
776 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
778 if (filter_set)
779 btrfs_list_free_filter_set(filter_set);
780 out:
781 close_file_or_dir(fd, dirstream);
782 return !!ret;
785 static const char * const cmd_subvol_set_default_usage[] = {
786 "btrfs subvolume set-default <subvolid> <path>",
787 "Set the default subvolume of a filesystem",
788 NULL
791 static int cmd_subvol_set_default(int argc, char **argv)
793 int ret=0, fd, e;
794 u64 objectid;
795 char *path;
796 char *subvolid;
797 DIR *dirstream = NULL;
799 if (check_argc_exact(argc, 3))
800 usage(cmd_subvol_set_default_usage);
802 subvolid = argv[1];
803 path = argv[2];
805 objectid = arg_strtou64(subvolid);
807 fd = open_file_or_dir(path, &dirstream);
808 if (fd < 0) {
809 fprintf(stderr, "ERROR: can't access '%s'\n", path);
810 return 1;
813 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
814 e = errno;
815 close_file_or_dir(fd, dirstream);
816 if (ret < 0) {
817 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
818 strerror(e));
819 return 1;
821 return 0;
824 static const char * const cmd_find_new_usage[] = {
825 "btrfs subvolume find-new <path> <lastgen>",
826 "List the recently modified files in a filesystem",
827 NULL
830 static int cmd_find_new(int argc, char **argv)
832 int fd;
833 int ret;
834 char *subvol;
835 u64 last_gen;
836 DIR *dirstream = NULL;
838 if (check_argc_exact(argc, 3))
839 usage(cmd_find_new_usage);
841 subvol = argv[1];
842 last_gen = arg_strtou64(argv[2]);
844 ret = test_issubvolume(subvol);
845 if (ret < 0) {
846 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
847 return 1;
849 if (!ret) {
850 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
851 return 1;
854 fd = open_file_or_dir(subvol, &dirstream);
855 if (fd < 0) {
856 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
857 return 1;
860 ret = ioctl(fd, BTRFS_IOC_SYNC);
861 if (ret < 0) {
862 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
863 subvol, strerror(errno));
864 close_file_or_dir(fd, dirstream);
865 return 1;
868 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
869 close_file_or_dir(fd, dirstream);
870 return !!ret;
873 static const char * const cmd_subvol_show_usage[] = {
874 "btrfs subvolume show <subvol-path>",
875 "Show more information of the subvolume",
876 NULL
879 static int cmd_subvol_show(int argc, char **argv)
881 struct root_info get_ri;
882 struct btrfs_list_filter_set *filter_set;
883 char tstr[256];
884 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
885 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
886 char raw_prefix[] = "\t\t\t\t";
887 u64 sv_id, mntid;
888 int fd = -1, mntfd = -1;
889 int ret = 1;
890 DIR *dirstream1 = NULL, *dirstream2 = NULL;
892 if (check_argc_exact(argc, 2))
893 usage(cmd_subvol_show_usage);
895 fullpath = realpath(argv[1], NULL);
896 if (!fullpath) {
897 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
898 argv[1], strerror(errno));
899 goto out;
902 ret = test_issubvolume(fullpath);
903 if (ret < 0) {
904 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
905 goto out;
907 if (!ret) {
908 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
909 goto out;
912 ret = find_mount_root(fullpath, &mnt);
913 if (ret < 0) {
914 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
915 "%s\n", fullpath, strerror(-ret));
916 goto out;
918 if (ret > 0) {
919 fprintf(stderr,
920 "ERROR: %s doesn't belong to btrfs mount point\n",
921 fullpath);
922 ret = -EINVAL;
923 goto out;
925 ret = 1;
926 svpath = get_subvol_name(mnt, fullpath);
928 fd = open_file_or_dir(fullpath, &dirstream1);
929 if (fd < 0) {
930 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
931 goto out;
934 ret = btrfs_list_get_path_rootid(fd, &sv_id);
935 if (ret) {
936 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
937 fullpath);
938 goto out;
941 mntfd = open_file_or_dir(mnt, &dirstream2);
942 if (mntfd < 0) {
943 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
944 goto out;
947 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
948 if (ret) {
949 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
950 goto out;
953 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
954 printf("%s is btrfs root\n", fullpath);
955 goto out;
958 memset(&get_ri, 0, sizeof(get_ri));
959 get_ri.root_id = sv_id;
961 if (btrfs_get_subvol(mntfd, &get_ri)) {
962 fprintf(stderr, "ERROR: can't find '%s'\n",
963 svpath);
964 goto out;
967 ret = 0;
968 /* print the info */
969 printf("%s\n", fullpath);
970 printf("\tName: \t\t\t%s\n", get_ri.name);
972 if (uuid_is_null(get_ri.uuid))
973 strcpy(uuidparse, "-");
974 else
975 uuid_unparse(get_ri.uuid, uuidparse);
976 printf("\tuuid: \t\t\t%s\n", uuidparse);
978 if (uuid_is_null(get_ri.puuid))
979 strcpy(uuidparse, "-");
980 else
981 uuid_unparse(get_ri.puuid, uuidparse);
982 printf("\tParent uuid: \t\t%s\n", uuidparse);
984 if (get_ri.otime) {
985 struct tm tm;
987 localtime_r(&get_ri.otime, &tm);
988 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
989 } else
990 strcpy(tstr, "-");
991 printf("\tCreation time: \t\t%s\n", tstr);
993 printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
994 printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
995 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
996 printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
997 printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
999 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1000 printf("\tFlags: \t\t\treadonly\n");
1001 else
1002 printf("\tFlags: \t\t\t-\n");
1004 /* print the snapshots of the given subvol if any*/
1005 printf("\tSnapshot(s):\n");
1006 filter_set = btrfs_list_alloc_filter_set();
1007 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1008 (u64)(unsigned long)get_ri.uuid);
1009 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1010 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1011 1, raw_prefix);
1013 /* clean up */
1014 free(get_ri.path);
1015 free(get_ri.name);
1016 free(get_ri.full_path);
1017 btrfs_list_free_filter_set(filter_set);
1019 out:
1020 close_file_or_dir(fd, dirstream1);
1021 close_file_or_dir(mntfd, dirstream2);
1022 free(mnt);
1023 free(fullpath);
1024 return !!ret;
1027 static const char * const cmd_subvol_sync_usage[] = {
1028 "btrfs subvolume sync <path> [<subvol-id>...]",
1029 "Wait until given subvolume(s) are completely removed from the filesystem.",
1030 "Wait until given subvolume(s) are completely removed from the filesystem",
1031 "after deletion.",
1032 "If no subvolume id is given, wait until all ongoing deletion requests",
1033 "are complete. This may take long if new deleted subvolumes appear during",
1034 "the sleep interval.",
1036 "-s <N> sleep N seconds between checks (default: 1)",
1037 NULL
1040 static int is_subvolume_cleaned(int fd, u64 subvolid)
1042 int ret;
1043 struct btrfs_ioctl_search_args args;
1044 struct btrfs_ioctl_search_key *sk = &args.key;
1046 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1047 sk->min_objectid = subvolid;
1048 sk->max_objectid = subvolid;
1049 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1050 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1051 sk->min_offset = 0;
1052 sk->max_offset = (u64)-1;
1053 sk->min_transid = 0;
1054 sk->max_transid = (u64)-1;
1055 sk->nr_items = 1;
1057 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1058 if (ret < 0)
1059 return -errno;
1061 if (sk->nr_items == 0)
1062 return 1;
1064 return 0;
1068 * If we're looking for any dead subvolume, take a shortcut and look
1069 * for any ORPHAN_ITEMs in the tree root
1071 static int fs_has_dead_subvolumes(int fd)
1073 int ret;
1074 struct btrfs_ioctl_search_args args;
1075 struct btrfs_ioctl_search_key *sk = &args.key;
1076 struct btrfs_ioctl_search_header sh;
1077 u64 min_subvolid = 0;
1079 again:
1080 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1081 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1082 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1083 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1084 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1085 sk->min_offset = min_subvolid;
1086 sk->max_offset = (u64)-1;
1087 sk->min_transid = 0;
1088 sk->max_transid = (u64)-1;
1089 sk->nr_items = 1;
1091 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1092 if (ret < 0)
1093 return -errno;
1095 if (!sk->nr_items)
1096 return 0;
1098 memcpy(&sh, args.buf, sizeof(sh));
1099 min_subvolid = sh.offset;
1102 * Verify that the root item is really there and we haven't hit
1103 * a stale orphan
1105 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1106 sk->min_objectid = min_subvolid;
1107 sk->max_objectid = min_subvolid;
1108 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1109 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1110 sk->min_offset = 0;
1111 sk->max_offset = (u64)-1;
1112 sk->min_transid = 0;
1113 sk->max_transid = (u64)-1;
1114 sk->nr_items = 1;
1116 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1117 if (ret < 0)
1118 return -errno;
1121 * Stale orphan, try the next one
1123 if (!sk->nr_items) {
1124 min_subvolid++;
1125 goto again;
1128 return 1;
1131 static int cmd_subvol_sync(int argc, char **argv)
1133 int fd = -1;
1134 int i;
1135 int ret = 1;
1136 DIR *dirstream = NULL;
1137 u64 *ids = NULL;
1138 int id_count;
1139 int remaining;
1140 int sleep_interval = 1;
1142 optind = 1;
1143 while (1) {
1144 int c = getopt(argc, argv, "s:");
1146 if (c < 0)
1147 break;
1149 switch (c) {
1150 case 's':
1151 sleep_interval = atoi(argv[optind]);
1152 if (sleep_interval < 1) {
1153 fprintf(stderr,
1154 "ERROR: invalid sleep interval %s\n",
1155 argv[optind]);
1156 ret = 1;
1157 goto out;
1159 break;
1160 default:
1161 usage(cmd_subvol_sync_usage);
1165 if (check_argc_min(argc - optind, 1))
1166 usage(cmd_subvol_sync_usage);
1168 fd = open_file_or_dir(argv[optind], &dirstream);
1169 if (fd < 0) {
1170 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1171 ret = 1;
1172 goto out;
1174 optind++;
1176 id_count = argc - optind;
1179 * Wait for all
1181 if (!id_count) {
1182 while (1) {
1183 ret = fs_has_dead_subvolumes(fd);
1184 if (ret < 0) {
1185 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1186 strerror(-ret));
1187 ret = 1;
1188 goto out;
1190 if (!ret)
1191 goto out;
1192 sleep(sleep_interval);
1197 * Wait only for the requested ones
1199 ids = (u64*)malloc(sizeof(u64) * id_count);
1201 if (!ids) {
1202 fprintf(stderr, "ERROR: not enough memory\n");
1203 ret = 1;
1204 goto out;
1207 for (i = 0; i < id_count; i++) {
1208 u64 id;
1209 const char *arg;
1211 arg = argv[optind + i];
1212 errno = 0;
1213 id = strtoull(arg, NULL, 10);
1214 if (errno < 0) {
1215 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1216 arg);
1217 ret = 1;
1218 goto out;
1220 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1221 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1222 arg);
1223 ret = 1;
1224 goto out;
1226 ids[i] = id;
1229 remaining = id_count;
1230 while (1) {
1231 for (i = 0; i < id_count; i++) {
1232 if (!ids[i])
1233 continue;
1234 ret = is_subvolume_cleaned(fd, ids[i]);
1235 if (ret < 0) {
1236 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1237 strerror(-ret));
1238 goto out;
1240 if (ret) {
1241 printf("Subvolume id %llu is gone\n", ids[i]);
1242 ids[i] = 0;
1243 remaining--;
1246 if (!remaining)
1247 break;
1248 sleep(sleep_interval);
1251 out:
1252 free(ids);
1253 close_file_or_dir(fd, dirstream);
1255 return !!ret;
1258 const struct cmd_group subvolume_cmd_group = {
1259 subvolume_cmd_group_usage, NULL, {
1260 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1261 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1262 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1263 { "snapshot", cmd_snapshot, cmd_snapshot_usage, 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_find_new, cmd_find_new_usage, NULL, 0 },
1269 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1270 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1271 NULL_CMD_STRUCT
1275 int cmd_subvolume(int argc, char **argv)
1277 return handle_command_group(&subvolume_cmd_group, argc, argv);