btrfs-progs: btrfstune: rework option handling
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blobc4c61ea4bf6698aee35900333e44e3b50cbbf063
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:v");
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, 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 verbose = 0;
221 int commit_mode = 0;
223 optind = 1;
224 while (1) {
225 int c;
226 static const struct option long_options[] = {
227 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
228 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
229 {NULL, 0, NULL, 0}
232 c = getopt_long(argc, argv, "cC", long_options, NULL);
233 if (c < 0)
234 break;
236 switch(c) {
237 case 'c':
238 commit_mode = 1;
239 break;
240 case 'C':
241 commit_mode = 2;
242 break;
243 case 'v':
244 verbose++;
245 break;
246 default:
247 usage(cmd_subvol_delete_usage);
251 if (check_argc_min(argc - optind, 1))
252 usage(cmd_subvol_delete_usage);
254 if (verbose > 0) {
255 printf("Transaction commit: %s\n",
256 !commit_mode ? "none (default)" :
257 commit_mode == 1 ? "at the end" : "after each");
260 cnt = optind;
262 again:
263 path = argv[cnt];
265 res = test_issubvolume(path);
266 if (res < 0) {
267 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
268 ret = 1;
269 goto out;
271 if (!res) {
272 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
273 ret = 1;
274 goto out;
277 cpath = realpath(path, NULL);
278 if (!cpath) {
279 ret = errno;
280 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
281 path, strerror(errno));
282 goto out;
284 dupdname = strdup(cpath);
285 dname = dirname(dupdname);
286 dupvname = strdup(cpath);
287 vname = basename(dupvname);
288 free(cpath);
290 fd = open_file_or_dir(dname, &dirstream);
291 if (fd < 0) {
292 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
293 ret = 1;
294 goto out;
297 printf("Delete subvolume (%s): '%s/%s'\n",
298 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
299 ? "commit" : "no-commit", dname, vname);
300 strncpy_null(args.name, vname);
301 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
302 e = errno;
304 if(res < 0 ){
305 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
306 dname, vname, strerror(e));
307 ret = 1;
308 goto out;
311 if (commit_mode == 1) {
312 res = wait_for_commit(fd);
313 if (res < 0) {
314 fprintf(stderr,
315 "ERROR: unable to wait for commit after '%s': %s\n",
316 path, strerror(errno));
317 ret = 1;
321 out:
322 free(dupdname);
323 free(dupvname);
324 dupdname = NULL;
325 dupvname = NULL;
326 cnt++;
327 if (cnt < argc) {
328 close_file_or_dir(fd, dirstream);
329 /* avoid double free */
330 fd = -1;
331 dirstream = NULL;
332 goto again;
335 if (commit_mode == 2 && fd != -1) {
336 res = wait_for_commit(fd);
337 if (res < 0) {
338 fprintf(stderr,
339 "ERROR: unable to do final sync: %s\n",
340 strerror(errno));
341 ret = 1;
344 close_file_or_dir(fd, dirstream);
346 return ret;
350 * Naming of options:
351 * - uppercase for filters and sort options
352 * - lowercase for enabling specific items in the output
354 static const char * const cmd_subvol_list_usage[] = {
355 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
356 "[--sort=gen,ogen,rootid,path] <path>",
357 "List subvolumes (and snapshots)",
359 "-p print parent ID",
360 "-a print all the subvolumes in the filesystem and",
361 " distinguish absolute and relative path with respect",
362 " to the given <path>",
363 "-c print the ogeneration of the subvolume",
364 "-g print the generation of the subvolume",
365 "-o print only subvolumes below specified path",
366 "-u print the uuid of subvolumes (and snapshots)",
367 "-q print the parent uuid of the snapshots",
368 "-R print the uuid of the received snapshots",
369 "-t print the result as a table",
370 "-s list snapshots only in the filesystem",
371 "-r list readonly subvolumes (including snapshots)",
372 "-d list deleted subvolumes that are not yet cleaned",
373 "-G [+|-]value",
374 " filter the subvolumes by generation",
375 " (+value: >= value; -value: <= value; value: = value)",
376 "-C [+|-]value",
377 " filter the subvolumes by ogeneration",
378 " (+value: >= value; -value: <= value; value: = value)",
379 "--sort=gen,ogen,rootid,path",
380 " list the subvolume in order of gen, ogen, rootid or path",
381 " you also can add '+' or '-' in front of each items.",
382 " (+:ascending, -:descending, ascending default)",
383 NULL,
386 static int cmd_subvol_list(int argc, char **argv)
388 struct btrfs_list_filter_set *filter_set;
389 struct btrfs_list_comparer_set *comparer_set;
390 u64 flags = 0;
391 int fd = -1;
392 u64 top_id;
393 int ret = -1, uerr = 0;
394 char *subvol;
395 int is_tab_result = 0;
396 int is_list_all = 0;
397 int is_only_in_path = 0;
398 DIR *dirstream = NULL;
400 filter_set = btrfs_list_alloc_filter_set();
401 comparer_set = btrfs_list_alloc_comparer_set();
403 optind = 1;
404 while(1) {
405 int c;
406 static const struct option long_options[] = {
407 {"sort", required_argument, NULL, 'S'},
408 {NULL, 0, NULL, 0}
411 c = getopt_long(argc, argv,
412 "acdgopqsurRG:C:t", long_options, NULL);
413 if (c < 0)
414 break;
416 switch(c) {
417 case 'p':
418 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
419 break;
420 case 'a':
421 is_list_all = 1;
422 break;
423 case 'c':
424 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
425 break;
426 case 'd':
427 btrfs_list_setup_filter(&filter_set,
428 BTRFS_LIST_FILTER_DELETED,
430 break;
431 case 'g':
432 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
433 break;
434 case 'o':
435 is_only_in_path = 1;
436 break;
437 case 't':
438 is_tab_result = 1;
439 break;
440 case 's':
441 btrfs_list_setup_filter(&filter_set,
442 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
444 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
445 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
446 break;
447 case 'u':
448 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
449 break;
450 case 'q':
451 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
452 break;
453 case 'R':
454 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
455 break;
456 case 'r':
457 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
458 break;
459 case 'G':
460 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
461 ret = btrfs_list_parse_filter_string(optarg,
462 &filter_set,
463 BTRFS_LIST_FILTER_GEN);
464 if (ret) {
465 uerr = 1;
466 goto out;
468 break;
470 case 'C':
471 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
472 ret = btrfs_list_parse_filter_string(optarg,
473 &filter_set,
474 BTRFS_LIST_FILTER_CGEN);
475 if (ret) {
476 uerr = 1;
477 goto out;
479 break;
480 case 'S':
481 ret = btrfs_list_parse_sort_string(optarg,
482 &comparer_set);
483 if (ret) {
484 uerr = 1;
485 goto out;
487 break;
489 default:
490 uerr = 1;
491 goto out;
495 if (flags)
496 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
497 flags);
499 if (check_argc_exact(argc - optind, 1)) {
500 uerr = 1;
501 goto out;
504 subvol = argv[optind];
505 fd = open_file_or_dir(subvol, &dirstream);
506 if (fd < 0) {
507 ret = -1;
508 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
509 goto out;
512 ret = btrfs_list_get_path_rootid(fd, &top_id);
513 if (ret) {
514 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
515 goto out;
518 if (is_list_all)
519 btrfs_list_setup_filter(&filter_set,
520 BTRFS_LIST_FILTER_FULL_PATH,
521 top_id);
522 else if (is_only_in_path)
523 btrfs_list_setup_filter(&filter_set,
524 BTRFS_LIST_FILTER_TOPID_EQUAL,
525 top_id);
527 /* by default we shall print the following columns*/
528 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
529 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
530 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
531 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
533 if (is_tab_result)
534 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
535 BTRFS_LIST_LAYOUT_TABLE,
536 !is_list_all && !is_only_in_path, NULL);
537 else
538 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
539 BTRFS_LIST_LAYOUT_DEFAULT,
540 !is_list_all && !is_only_in_path, NULL);
542 out:
543 close_file_or_dir(fd, dirstream);
544 if (filter_set)
545 btrfs_list_free_filter_set(filter_set);
546 if (comparer_set)
547 btrfs_list_free_comparer_set(comparer_set);
548 if (uerr)
549 usage(cmd_subvol_list_usage);
550 return !!ret;
553 static const char * const cmd_snapshot_usage[] = {
554 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
555 "Create a snapshot of the subvolume",
556 "Create a writable/readonly snapshot of the subvolume <source> with",
557 "the name <name> in the <dest> directory. If only <dest> is given,",
558 "the subvolume will be named the basename of <source>.",
560 "-r create a readonly snapshot",
561 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
562 " option can be given multiple times.",
563 NULL
566 static int cmd_snapshot(int argc, char **argv)
568 char *subvol, *dst;
569 int res, retval;
570 int fd = -1, fddst = -1;
571 int len, readonly = 0;
572 char *dupname = NULL;
573 char *dupdir = NULL;
574 char *newname;
575 char *dstdir;
576 struct btrfs_ioctl_vol_args_v2 args;
577 struct btrfs_qgroup_inherit *inherit = NULL;
578 DIR *dirstream1 = NULL, *dirstream2 = NULL;
580 optind = 1;
581 memset(&args, 0, sizeof(args));
582 while (1) {
583 int c = getopt(argc, argv, "c:i:r");
584 if (c < 0)
585 break;
587 switch (c) {
588 case 'c':
589 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
590 if (res) {
591 retval = res;
592 goto out;
594 break;
595 case 'i':
596 res = qgroup_inherit_add_group(&inherit, optarg);
597 if (res) {
598 retval = res;
599 goto out;
601 break;
602 case 'r':
603 readonly = 1;
604 break;
605 case 'x':
606 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
607 if (res) {
608 retval = res;
609 goto out;
611 break;
612 default:
613 usage(cmd_snapshot_usage);
617 if (check_argc_exact(argc - optind, 2))
618 usage(cmd_snapshot_usage);
620 subvol = argv[optind];
621 dst = argv[optind + 1];
623 retval = 1; /* failure */
624 res = test_issubvolume(subvol);
625 if (res < 0) {
626 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
627 goto out;
629 if (!res) {
630 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
631 goto out;
634 res = test_isdir(dst);
635 if (res == 0) {
636 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
637 goto out;
640 if (res > 0) {
641 dupname = strdup(subvol);
642 newname = basename(dupname);
643 dstdir = dst;
644 } else {
645 dupname = strdup(dst);
646 newname = basename(dupname);
647 dupdir = strdup(dst);
648 dstdir = dirname(dupdir);
651 if (!test_issubvolname(newname)) {
652 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
653 newname);
654 goto out;
657 len = strlen(newname);
658 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
659 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
660 newname);
661 goto out;
664 fddst = open_file_or_dir(dstdir, &dirstream1);
665 if (fddst < 0) {
666 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
667 goto out;
670 fd = open_file_or_dir(subvol, &dirstream2);
671 if (fd < 0) {
672 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
673 goto out;
676 if (readonly) {
677 args.flags |= BTRFS_SUBVOL_RDONLY;
678 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
679 subvol, dstdir, newname);
680 } else {
681 printf("Create a snapshot of '%s' in '%s/%s'\n",
682 subvol, dstdir, newname);
685 args.fd = fd;
686 if (inherit) {
687 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
688 args.size = qgroup_inherit_size(inherit);
689 args.qgroup_inherit = inherit;
691 strncpy_null(args.name, newname);
693 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
695 if (res < 0) {
696 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
697 subvol, strerror(errno));
698 goto out;
701 retval = 0; /* success */
703 out:
704 close_file_or_dir(fddst, dirstream1);
705 close_file_or_dir(fd, dirstream2);
706 free(inherit);
707 free(dupname);
708 free(dupdir);
710 return retval;
713 static const char * const cmd_subvol_get_default_usage[] = {
714 "btrfs subvolume get-default <path>",
715 "Get the default subvolume of a filesystem",
716 NULL
719 static int cmd_subvol_get_default(int argc, char **argv)
721 int fd = -1;
722 int ret;
723 char *subvol;
724 struct btrfs_list_filter_set *filter_set;
725 u64 default_id;
726 DIR *dirstream = NULL;
728 if (check_argc_exact(argc, 2))
729 usage(cmd_subvol_get_default_usage);
731 subvol = argv[1];
732 fd = open_file_or_dir(subvol, &dirstream);
733 if (fd < 0) {
734 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
735 return 1;
738 ret = btrfs_list_get_default_subvolume(fd, &default_id);
739 if (ret) {
740 fprintf(stderr, "ERROR: can't perform the search - %s\n",
741 strerror(errno));
742 goto out;
745 ret = 1;
746 if (default_id == 0) {
747 fprintf(stderr, "ERROR: 'default' dir item not found\n");
748 goto out;
751 /* no need to resolve roots if FS_TREE is default */
752 if (default_id == BTRFS_FS_TREE_OBJECTID) {
753 printf("ID 5 (FS_TREE)\n");
754 ret = 0;
755 goto out;
758 filter_set = btrfs_list_alloc_filter_set();
759 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
760 default_id);
762 /* by default we shall print the following columns*/
763 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
764 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
765 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
766 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
768 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
769 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
771 if (filter_set)
772 btrfs_list_free_filter_set(filter_set);
773 out:
774 close_file_or_dir(fd, dirstream);
775 return !!ret;
778 static const char * const cmd_subvol_set_default_usage[] = {
779 "btrfs subvolume set-default <subvolid> <path>",
780 "Set the default subvolume of a filesystem",
781 NULL
784 static int cmd_subvol_set_default(int argc, char **argv)
786 int ret=0, fd, e;
787 u64 objectid;
788 char *path;
789 char *subvolid;
790 DIR *dirstream = NULL;
792 if (check_argc_exact(argc, 3))
793 usage(cmd_subvol_set_default_usage);
795 subvolid = argv[1];
796 path = argv[2];
798 objectid = arg_strtou64(subvolid);
800 fd = open_file_or_dir(path, &dirstream);
801 if (fd < 0) {
802 fprintf(stderr, "ERROR: can't access '%s'\n", path);
803 return 1;
806 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
807 e = errno;
808 close_file_or_dir(fd, dirstream);
809 if (ret < 0) {
810 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
811 strerror(e));
812 return 1;
814 return 0;
817 static const char * const cmd_find_new_usage[] = {
818 "btrfs subvolume find-new <path> <lastgen>",
819 "List the recently modified files in a filesystem",
820 NULL
823 static int cmd_find_new(int argc, char **argv)
825 int fd;
826 int ret;
827 char *subvol;
828 u64 last_gen;
829 DIR *dirstream = NULL;
831 if (check_argc_exact(argc, 3))
832 usage(cmd_find_new_usage);
834 subvol = argv[1];
835 last_gen = arg_strtou64(argv[2]);
837 ret = test_issubvolume(subvol);
838 if (ret < 0) {
839 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
840 return 1;
842 if (!ret) {
843 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
844 return 1;
847 fd = open_file_or_dir(subvol, &dirstream);
848 if (fd < 0) {
849 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
850 return 1;
853 ret = ioctl(fd, BTRFS_IOC_SYNC);
854 if (ret < 0) {
855 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
856 subvol, strerror(errno));
857 close_file_or_dir(fd, dirstream);
858 return 1;
861 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
862 close_file_or_dir(fd, dirstream);
863 return !!ret;
866 static const char * const cmd_subvol_show_usage[] = {
867 "btrfs subvolume show <subvol-path>",
868 "Show more information of the subvolume",
869 NULL
872 static int cmd_subvol_show(int argc, char **argv)
874 struct root_info get_ri;
875 struct btrfs_list_filter_set *filter_set;
876 char tstr[256];
877 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
878 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
879 char raw_prefix[] = "\t\t\t\t";
880 u64 sv_id, mntid;
881 int fd = -1, mntfd = -1;
882 int ret = 1;
883 DIR *dirstream1 = NULL, *dirstream2 = NULL;
885 if (check_argc_exact(argc, 2))
886 usage(cmd_subvol_show_usage);
888 fullpath = realpath(argv[1], NULL);
889 if (!fullpath) {
890 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
891 argv[1], strerror(errno));
892 goto out;
895 ret = test_issubvolume(fullpath);
896 if (ret < 0) {
897 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
898 goto out;
900 if (!ret) {
901 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
902 ret = 1;
903 goto out;
906 ret = find_mount_root(fullpath, &mnt);
907 if (ret < 0) {
908 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
909 "%s\n", fullpath, strerror(-ret));
910 goto out;
912 if (ret > 0) {
913 fprintf(stderr,
914 "ERROR: %s doesn't belong to btrfs mount point\n",
915 fullpath);
916 goto out;
918 ret = 1;
919 svpath = get_subvol_name(mnt, fullpath);
921 fd = open_file_or_dir(fullpath, &dirstream1);
922 if (fd < 0) {
923 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
924 goto out;
927 ret = btrfs_list_get_path_rootid(fd, &sv_id);
928 if (ret) {
929 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
930 fullpath);
931 goto out;
934 mntfd = open_file_or_dir(mnt, &dirstream2);
935 if (mntfd < 0) {
936 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
937 goto out;
940 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
941 if (ret) {
942 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
943 goto out;
946 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
947 printf("%s is btrfs root\n", fullpath);
948 goto out;
951 memset(&get_ri, 0, sizeof(get_ri));
952 get_ri.root_id = sv_id;
954 ret = btrfs_get_subvol(mntfd, &get_ri);
955 if (ret) {
956 fprintf(stderr, "ERROR: can't find '%s'\n",
957 svpath);
958 goto out;
961 /* print the info */
962 printf("%s\n", fullpath);
963 printf("\tName: \t\t\t%s\n", get_ri.name);
965 if (uuid_is_null(get_ri.uuid))
966 strcpy(uuidparse, "-");
967 else
968 uuid_unparse(get_ri.uuid, uuidparse);
969 printf("\tUUID: \t\t\t%s\n", uuidparse);
971 if (uuid_is_null(get_ri.puuid))
972 strcpy(uuidparse, "-");
973 else
974 uuid_unparse(get_ri.puuid, uuidparse);
975 printf("\tParent UUID: \t\t%s\n", uuidparse);
977 if (uuid_is_null(get_ri.ruuid))
978 strcpy(uuidparse, "-");
979 else
980 uuid_unparse(get_ri.ruuid, uuidparse);
981 printf("\tReceived UUID: \t\t%s\n", uuidparse);
983 if (get_ri.otime) {
984 struct tm tm;
986 localtime_r(&get_ri.otime, &tm);
987 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
988 } else
989 strcpy(tstr, "-");
990 printf("\tCreation time: \t\t%s\n", tstr);
992 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
993 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
994 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
995 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
996 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
998 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
999 printf("\tFlags: \t\t\treadonly\n");
1000 else
1001 printf("\tFlags: \t\t\t-\n");
1003 /* print the snapshots of the given subvol if any*/
1004 printf("\tSnapshot(s):\n");
1005 filter_set = btrfs_list_alloc_filter_set();
1006 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1007 (u64)(unsigned long)get_ri.uuid);
1008 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1009 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1010 1, raw_prefix);
1012 /* clean up */
1013 free(get_ri.path);
1014 free(get_ri.name);
1015 free(get_ri.full_path);
1016 btrfs_list_free_filter_set(filter_set);
1018 out:
1019 close_file_or_dir(fd, dirstream1);
1020 close_file_or_dir(mntfd, dirstream2);
1021 free(mnt);
1022 free(fullpath);
1023 return !!ret;
1026 static const char * const cmd_subvol_sync_usage[] = {
1027 "btrfs subvolume sync <path> [<subvol-id>...]",
1028 "Wait until given subvolume(s) are completely removed from the filesystem.",
1029 "Wait until given subvolume(s) are completely removed from the filesystem",
1030 "after deletion.",
1031 "If no subvolume id is given, wait until all ongoing deletion requests",
1032 "are complete. This may take long if new deleted subvolumes appear during",
1033 "the sleep interval.",
1035 "-s <N> sleep N seconds between checks (default: 1)",
1036 NULL
1039 static int is_subvolume_cleaned(int fd, u64 subvolid)
1041 int ret;
1042 struct btrfs_ioctl_search_args args;
1043 struct btrfs_ioctl_search_key *sk = &args.key;
1045 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1046 sk->min_objectid = subvolid;
1047 sk->max_objectid = subvolid;
1048 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1049 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1050 sk->min_offset = 0;
1051 sk->max_offset = (u64)-1;
1052 sk->min_transid = 0;
1053 sk->max_transid = (u64)-1;
1054 sk->nr_items = 1;
1056 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1057 if (ret < 0)
1058 return -errno;
1060 if (sk->nr_items == 0)
1061 return 1;
1063 return 0;
1067 * If we're looking for any dead subvolume, take a shortcut and look
1068 * for any ORPHAN_ITEMs in the tree root
1070 static int fs_has_dead_subvolumes(int fd)
1072 int ret;
1073 struct btrfs_ioctl_search_args args;
1074 struct btrfs_ioctl_search_key *sk = &args.key;
1075 struct btrfs_ioctl_search_header sh;
1076 u64 min_subvolid = 0;
1078 again:
1079 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1080 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1081 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1082 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1083 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1084 sk->min_offset = min_subvolid;
1085 sk->max_offset = (u64)-1;
1086 sk->min_transid = 0;
1087 sk->max_transid = (u64)-1;
1088 sk->nr_items = 1;
1090 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1091 if (ret < 0)
1092 return -errno;
1094 if (!sk->nr_items)
1095 return 0;
1097 memcpy(&sh, args.buf, sizeof(sh));
1098 min_subvolid = sh.offset;
1101 * Verify that the root item is really there and we haven't hit
1102 * a stale orphan
1104 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1105 sk->min_objectid = min_subvolid;
1106 sk->max_objectid = min_subvolid;
1107 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1108 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1109 sk->min_offset = 0;
1110 sk->max_offset = (u64)-1;
1111 sk->min_transid = 0;
1112 sk->max_transid = (u64)-1;
1113 sk->nr_items = 1;
1115 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1116 if (ret < 0)
1117 return -errno;
1120 * Stale orphan, try the next one
1122 if (!sk->nr_items) {
1123 min_subvolid++;
1124 goto again;
1127 return 1;
1130 static int cmd_subvol_sync(int argc, char **argv)
1132 int fd = -1;
1133 int i;
1134 int ret = 1;
1135 DIR *dirstream = NULL;
1136 u64 *ids = NULL;
1137 int id_count;
1138 int remaining;
1139 int sleep_interval = 1;
1141 optind = 1;
1142 while (1) {
1143 int c = getopt(argc, argv, "s:");
1145 if (c < 0)
1146 break;
1148 switch (c) {
1149 case 's':
1150 sleep_interval = atoi(argv[optind]);
1151 if (sleep_interval < 1) {
1152 fprintf(stderr,
1153 "ERROR: invalid sleep interval %s\n",
1154 argv[optind]);
1155 ret = 1;
1156 goto out;
1158 break;
1159 default:
1160 usage(cmd_subvol_sync_usage);
1164 if (check_argc_min(argc - optind, 1))
1165 usage(cmd_subvol_sync_usage);
1167 fd = open_file_or_dir(argv[optind], &dirstream);
1168 if (fd < 0) {
1169 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1170 ret = 1;
1171 goto out;
1173 optind++;
1175 id_count = argc - optind;
1178 * Wait for all
1180 if (!id_count) {
1181 while (1) {
1182 ret = fs_has_dead_subvolumes(fd);
1183 if (ret < 0) {
1184 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1185 strerror(-ret));
1186 ret = 1;
1187 goto out;
1189 if (!ret)
1190 goto out;
1191 sleep(sleep_interval);
1196 * Wait only for the requested ones
1198 ids = (u64*)malloc(sizeof(u64) * id_count);
1200 if (!ids) {
1201 fprintf(stderr, "ERROR: not enough memory\n");
1202 ret = 1;
1203 goto out;
1206 for (i = 0; i < id_count; i++) {
1207 u64 id;
1208 const char *arg;
1210 arg = argv[optind + i];
1211 errno = 0;
1212 id = strtoull(arg, NULL, 10);
1213 if (errno < 0) {
1214 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1215 arg);
1216 ret = 1;
1217 goto out;
1219 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1220 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1221 arg);
1222 ret = 1;
1223 goto out;
1225 ids[i] = id;
1228 remaining = id_count;
1229 while (1) {
1230 for (i = 0; i < id_count; i++) {
1231 if (!ids[i])
1232 continue;
1233 ret = is_subvolume_cleaned(fd, ids[i]);
1234 if (ret < 0) {
1235 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1236 strerror(-ret));
1237 goto out;
1239 if (ret) {
1240 printf("Subvolume id %llu is gone\n", ids[i]);
1241 ids[i] = 0;
1242 remaining--;
1245 if (!remaining)
1246 break;
1247 sleep(sleep_interval);
1250 out:
1251 free(ids);
1252 close_file_or_dir(fd, dirstream);
1254 return !!ret;
1257 const struct cmd_group subvolume_cmd_group = {
1258 subvolume_cmd_group_usage, NULL, {
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_snapshot, cmd_snapshot_usage, NULL, 0 },
1263 { "get-default", cmd_subvol_get_default,
1264 cmd_subvol_get_default_usage, NULL, 0 },
1265 { "set-default", cmd_subvol_set_default,
1266 cmd_subvol_set_default_usage, NULL, 0 },
1267 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1268 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1269 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1270 NULL_CMD_STRUCT
1274 int cmd_subvolume(int argc, char **argv)
1276 return handle_command_group(&subvolume_cmd_group, argc, argv);