btrfs-progs: print qgroup excl as unsigned
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob5e821c712e74f1bd767c45a62363ee4912f9d610
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
45 * test if path is a directory
46 * this function return
47 * 0-> path exists but it is not a directory
48 * 1-> path exists and it is a directory
49 * -1 -> path is unaccessible
51 static int test_isdir(char *path)
53 struct stat st;
54 int res;
56 res = stat(path, &st);
57 if(res < 0 )
58 return -1;
60 return S_ISDIR(st.st_mode);
63 static const char * const cmd_subvol_create_usage[] = {
64 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
65 "Create a subvolume",
66 "Create a subvolume <name> in <dest>. If <dest> is not given",
67 "subvolume <name> will be created in the current directory.",
68 "",
69 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
70 " option can be given multiple times.",
71 NULL
74 static int cmd_subvol_create(int argc, char **argv)
76 int retval, res, len;
77 int fddst = -1;
78 char *dupname = NULL;
79 char *dupdir = NULL;
80 char *newname;
81 char *dstdir;
82 char *dst;
83 struct btrfs_qgroup_inherit *inherit = NULL;
84 DIR *dirstream = NULL;
86 optind = 1;
87 while (1) {
88 int c = getopt(argc, argv, "c:i:");
89 if (c < 0)
90 break;
92 switch (c) {
93 case 'c':
94 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
95 if (res) {
96 retval = res;
97 goto out;
99 break;
100 case 'i':
101 res = qgroup_inherit_add_group(&inherit, optarg);
102 if (res) {
103 retval = res;
104 goto out;
106 break;
107 default:
108 usage(cmd_subvol_create_usage);
112 if (check_argc_exact(argc - optind, 1))
113 usage(cmd_subvol_create_usage);
115 dst = argv[optind];
117 retval = 1; /* failure */
118 res = test_isdir(dst);
119 if (res >= 0) {
120 fprintf(stderr, "ERROR: '%s' exists\n", dst);
121 goto out;
124 dupname = strdup(dst);
125 newname = basename(dupname);
126 dupdir = strdup(dst);
127 dstdir = dirname(dupdir);
129 if (!strcmp(newname, ".") || !strcmp(newname, "..") ||
130 strchr(newname, '/') ){
131 fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
132 newname);
133 goto out;
136 len = strlen(newname);
137 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
138 fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
139 newname);
140 goto out;
143 fddst = open_file_or_dir(dstdir, &dirstream);
144 if (fddst < 0) {
145 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
146 goto out;
149 printf("Create subvolume '%s/%s'\n", dstdir, newname);
150 if (inherit) {
151 struct btrfs_ioctl_vol_args_v2 args;
153 memset(&args, 0, sizeof(args));
154 strncpy_null(args.name, newname);
155 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
156 args.size = qgroup_inherit_size(inherit);
157 args.qgroup_inherit = inherit;
159 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
160 } else {
161 struct btrfs_ioctl_vol_args args;
163 memset(&args, 0, sizeof(args));
164 strncpy_null(args.name, newname);
166 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
169 if (res < 0) {
170 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
171 strerror(errno));
172 goto out;
175 retval = 0; /* success */
176 out:
177 close_file_or_dir(fddst, dirstream);
178 free(inherit);
179 free(dupname);
180 free(dupdir);
182 return retval;
186 * test if path is a subvolume:
187 * this function return
188 * 0-> path exists but it is not a subvolume
189 * 1-> path exists and it is a subvolume
190 * -1 -> path is unaccessible
192 int test_issubvolume(char *path)
194 struct stat st;
195 int res;
197 res = stat(path, &st);
198 if(res < 0 )
199 return -1;
201 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
204 static int wait_for_commit(int fd)
206 int ret;
208 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
209 if (ret < 0)
210 return ret;
211 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
214 static const char * const cmd_subvol_delete_usage[] = {
215 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
216 "Delete subvolume(s)",
217 "Delete subvolumes from the filesystem. The corresponding directory",
218 "is removed instantly but the data blocks are removed later.",
219 "The deletion does not involve full commit by default due to",
220 "performance reasons (as a consequence, the subvolume may appear again",
221 "after a crash). Use one of the --commit options to wait until the",
222 "operation is safely stored on the media.",
224 "-c|--commit-after wait for transaction commit at the end of the operation",
225 "-C|--commit-each wait for transaction commit after deleting each subvolume",
226 NULL
229 static int cmd_subvol_delete(int argc, char **argv)
231 int res, len, e, ret = 0;
232 int cnt;
233 int fd = -1;
234 struct btrfs_ioctl_vol_args args;
235 char *dname, *vname, *cpath;
236 char *dupdname = NULL;
237 char *dupvname = NULL;
238 char *path;
239 DIR *dirstream = NULL;
240 int sync_mode = 0;
241 struct option long_options[] = {
242 {"commit-after", no_argument, NULL, 'c'}, /* sync mode 1 */
243 {"commit-each", no_argument, NULL, 'C'}, /* sync mode 2 */
244 {NULL, 0, NULL, 0}
247 optind = 1;
248 while (1) {
249 int c;
251 c = getopt_long(argc, argv, "cC", long_options, NULL);
252 if (c < 0)
253 break;
255 switch(c) {
256 case 'c':
257 sync_mode = 1;
258 break;
259 case 'C':
260 sync_mode = 2;
261 break;
262 default:
263 usage(cmd_subvol_delete_usage);
267 if (check_argc_min(argc - optind, 1))
268 usage(cmd_subvol_delete_usage);
270 printf("Transaction commit: %s\n",
271 !sync_mode ? "none (default)" :
272 sync_mode == 1 ? "at the end" : "after each");
274 cnt = optind;
276 again:
277 path = argv[cnt];
279 res = test_issubvolume(path);
280 if (res < 0) {
281 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
282 ret = 1;
283 goto out;
285 if (!res) {
286 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
287 ret = 1;
288 goto out;
291 cpath = realpath(path, NULL);
292 if (!cpath) {
293 ret = errno;
294 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
295 path, strerror(errno));
296 goto out;
298 dupdname = strdup(cpath);
299 dname = dirname(dupdname);
300 dupvname = strdup(cpath);
301 vname = basename(dupvname);
302 free(cpath);
304 if (!strcmp(vname, ".") || !strcmp(vname, "..") ||
305 strchr(vname, '/')) {
306 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
307 vname);
308 ret = 1;
309 goto out;
312 len = strlen(vname);
313 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
314 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
315 vname);
316 ret = 1;
317 goto out;
320 fd = open_file_or_dir(dname, &dirstream);
321 if (fd < 0) {
322 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
323 ret = 1;
324 goto out;
327 printf("Delete subvolume '%s/%s'\n", dname, vname);
328 strncpy_null(args.name, vname);
329 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
330 e = errno;
332 if(res < 0 ){
333 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
334 dname, vname, strerror(e));
335 ret = 1;
336 goto out;
339 if (sync_mode == 1) {
340 res = wait_for_commit(fd);
341 if (res < 0) {
342 fprintf(stderr,
343 "ERROR: unable to wait for commit after '%s': %s\n",
344 path, strerror(errno));
345 ret = 1;
349 out:
350 free(dupdname);
351 free(dupvname);
352 dupdname = NULL;
353 dupvname = NULL;
354 cnt++;
355 if (cnt < argc) {
356 close_file_or_dir(fd, dirstream);
357 /* avoid double free */
358 fd = -1;
359 dirstream = NULL;
360 goto again;
363 if (sync_mode == 2 && fd != -1) {
364 res = wait_for_commit(fd);
365 if (res < 0) {
366 fprintf(stderr,
367 "ERROR: unable to do final sync: %s\n",
368 strerror(errno));
369 ret = 1;
372 close_file_or_dir(fd, dirstream);
374 return ret;
378 * Naming of options:
379 * - uppercase for filters and sort options
380 * - lowercase for enabling specific items in the output
382 static const char * const cmd_subvol_list_usage[] = {
383 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
384 "[--sort=gen,ogen,rootid,path] <path>",
385 "List subvolumes (and snapshots)",
387 "-p print parent ID",
388 "-a print all the subvolumes in the filesystem and",
389 " distinguish absolute and relative path with respect",
390 " to the given <path>",
391 "-c print the ogeneration of the subvolume",
392 "-g print the generation of the subvolume",
393 "-o print only subvolumes bellow specified path",
394 "-u print the uuid of subvolumes (and snapshots)",
395 "-q print the parent uuid of the snapshots",
396 "-t print the result as a table",
397 "-s list snapshots only in the filesystem",
398 "-r list readonly subvolumes (including snapshots)",
399 "-d list deleted subvolumes that are not yet cleaned",
400 "-G [+|-]value",
401 " filter the subvolumes by generation",
402 " (+value: >= value; -value: <= value; value: = value)",
403 "-C [+|-]value",
404 " filter the subvolumes by ogeneration",
405 " (+value: >= value; -value: <= value; value: = value)",
406 "--sort=gen,ogen,rootid,path",
407 " list the subvolume in order of gen, ogen, rootid or path",
408 " you also can add '+' or '-' in front of each items.",
409 " (+:ascending, -:descending, ascending default)",
410 NULL,
413 static int cmd_subvol_list(int argc, char **argv)
415 struct btrfs_list_filter_set *filter_set;
416 struct btrfs_list_comparer_set *comparer_set;
417 u64 flags = 0;
418 int fd = -1;
419 u64 top_id;
420 int ret = -1, uerr = 0;
421 int c;
422 char *subvol;
423 int is_tab_result = 0;
424 int is_list_all = 0;
425 int is_only_in_path = 0;
426 struct option long_options[] = {
427 {"sort", 1, NULL, 'S'},
428 {NULL, 0, NULL, 0}
430 DIR *dirstream = NULL;
432 filter_set = btrfs_list_alloc_filter_set();
433 comparer_set = btrfs_list_alloc_comparer_set();
435 optind = 1;
436 while(1) {
437 c = getopt_long(argc, argv,
438 "acdgopqsurG:C:t", long_options, NULL);
439 if (c < 0)
440 break;
442 switch(c) {
443 case 'p':
444 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
445 break;
446 case 'a':
447 is_list_all = 1;
448 break;
449 case 'c':
450 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
451 break;
452 case 'd':
453 btrfs_list_setup_filter(&filter_set,
454 BTRFS_LIST_FILTER_DELETED,
456 break;
457 case 'g':
458 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
459 break;
460 case 'o':
461 is_only_in_path = 1;
462 break;
463 case 't':
464 is_tab_result = 1;
465 break;
466 case 's':
467 btrfs_list_setup_filter(&filter_set,
468 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
470 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
471 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
472 break;
473 case 'u':
474 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
475 break;
476 case 'q':
477 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
478 break;
479 case 'r':
480 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
481 break;
482 case 'G':
483 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
484 ret = btrfs_list_parse_filter_string(optarg,
485 &filter_set,
486 BTRFS_LIST_FILTER_GEN);
487 if (ret) {
488 uerr = 1;
489 goto out;
491 break;
493 case 'C':
494 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
495 ret = btrfs_list_parse_filter_string(optarg,
496 &filter_set,
497 BTRFS_LIST_FILTER_CGEN);
498 if (ret) {
499 uerr = 1;
500 goto out;
502 break;
503 case 'S':
504 ret = btrfs_list_parse_sort_string(optarg,
505 &comparer_set);
506 if (ret) {
507 uerr = 1;
508 goto out;
510 break;
512 default:
513 uerr = 1;
514 goto out;
518 if (flags)
519 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
520 flags);
522 if (check_argc_exact(argc - optind, 1)) {
523 uerr = 1;
524 goto out;
527 subvol = argv[optind];
528 fd = open_file_or_dir(subvol, &dirstream);
529 if (fd < 0) {
530 ret = -1;
531 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
532 goto out;
535 ret = btrfs_list_get_path_rootid(fd, &top_id);
536 if (ret) {
537 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
538 goto out;
541 if (is_list_all)
542 btrfs_list_setup_filter(&filter_set,
543 BTRFS_LIST_FILTER_FULL_PATH,
544 top_id);
545 else if (is_only_in_path)
546 btrfs_list_setup_filter(&filter_set,
547 BTRFS_LIST_FILTER_TOPID_EQUAL,
548 top_id);
550 /* by default we shall print the following columns*/
551 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
552 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
553 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
554 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
556 if (is_tab_result)
557 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
558 BTRFS_LIST_LAYOUT_TABLE,
559 !is_list_all && !is_only_in_path, NULL);
560 else
561 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
562 BTRFS_LIST_LAYOUT_DEFAULT,
563 !is_list_all && !is_only_in_path, NULL);
565 out:
566 close_file_or_dir(fd, dirstream);
567 if (filter_set)
568 btrfs_list_free_filter_set(filter_set);
569 if (comparer_set)
570 btrfs_list_free_comparer_set(comparer_set);
571 if (uerr)
572 usage(cmd_subvol_list_usage);
573 return !!ret;
576 static const char * const cmd_snapshot_usage[] = {
577 "btrfs subvolume snapshot [-r] <source> <dest>|[<dest>/]<name>",
578 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
579 "Create a snapshot of the subvolume",
580 "Create a writable/readonly snapshot of the subvolume <source> with",
581 "the name <name> in the <dest> directory. If only <dest> is given,",
582 "the subvolume will be named the basename of <source>.",
584 "-r create a readonly snapshot",
585 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
586 " option can be given multiple times.",
587 NULL
590 static int cmd_snapshot(int argc, char **argv)
592 char *subvol, *dst;
593 int res, retval;
594 int fd = -1, fddst = -1;
595 int len, readonly = 0;
596 char *dupname = NULL;
597 char *dupdir = NULL;
598 char *newname;
599 char *dstdir;
600 struct btrfs_ioctl_vol_args_v2 args;
601 struct btrfs_qgroup_inherit *inherit = NULL;
602 DIR *dirstream1 = NULL, *dirstream2 = NULL;
604 optind = 1;
605 memset(&args, 0, sizeof(args));
606 while (1) {
607 int c = getopt(argc, argv, "c:i:r");
608 if (c < 0)
609 break;
611 switch (c) {
612 case 'c':
613 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
614 if (res) {
615 retval = res;
616 goto out;
618 break;
619 case 'i':
620 res = qgroup_inherit_add_group(&inherit, optarg);
621 if (res) {
622 retval = res;
623 goto out;
625 break;
626 case 'r':
627 readonly = 1;
628 break;
629 case 'x':
630 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
631 if (res) {
632 retval = res;
633 goto out;
635 break;
636 default:
637 usage(cmd_snapshot_usage);
641 if (check_argc_exact(argc - optind, 2))
642 usage(cmd_snapshot_usage);
644 subvol = argv[optind];
645 dst = argv[optind + 1];
647 retval = 1; /* failure */
648 res = test_issubvolume(subvol);
649 if (res < 0) {
650 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
651 goto out;
653 if (!res) {
654 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
655 goto out;
658 res = test_isdir(dst);
659 if (res == 0) {
660 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
661 goto out;
664 if (res > 0) {
665 dupname = strdup(subvol);
666 newname = basename(dupname);
667 dstdir = dst;
668 } else {
669 dupname = strdup(dst);
670 newname = basename(dupname);
671 dupdir = strdup(dst);
672 dstdir = dirname(dupdir);
675 if (!strcmp(newname, ".") || !strcmp(newname, "..") ||
676 strchr(newname, '/') ){
677 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
678 newname);
679 goto out;
682 len = strlen(newname);
683 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
684 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
685 newname);
686 goto out;
689 fddst = open_file_or_dir(dstdir, &dirstream1);
690 if (fddst < 0) {
691 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
692 goto out;
695 fd = open_file_or_dir(subvol, &dirstream2);
696 if (fd < 0) {
697 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
698 goto out;
701 if (readonly) {
702 args.flags |= BTRFS_SUBVOL_RDONLY;
703 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
704 subvol, dstdir, newname);
705 } else {
706 printf("Create a snapshot of '%s' in '%s/%s'\n",
707 subvol, dstdir, newname);
710 args.fd = fd;
711 if (inherit) {
712 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
713 args.size = qgroup_inherit_size(inherit);
714 args.qgroup_inherit = inherit;
716 strncpy_null(args.name, newname);
718 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
720 if (res < 0) {
721 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
722 subvol, strerror(errno));
723 goto out;
726 retval = 0; /* success */
728 out:
729 close_file_or_dir(fddst, dirstream1);
730 close_file_or_dir(fd, dirstream2);
731 free(inherit);
732 free(dupname);
733 free(dupdir);
735 return retval;
738 static const char * const cmd_subvol_get_default_usage[] = {
739 "btrfs subvolume get-default <path>",
740 "Get the default subvolume of a filesystem",
741 NULL
744 static int cmd_subvol_get_default(int argc, char **argv)
746 int fd = -1;
747 int ret;
748 char *subvol;
749 struct btrfs_list_filter_set *filter_set;
750 u64 default_id;
751 DIR *dirstream = NULL;
753 if (check_argc_exact(argc, 2))
754 usage(cmd_subvol_get_default_usage);
756 subvol = argv[1];
757 fd = open_file_or_dir(subvol, &dirstream);
758 if (fd < 0) {
759 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
760 return 1;
763 ret = btrfs_list_get_default_subvolume(fd, &default_id);
764 if (ret) {
765 fprintf(stderr, "ERROR: can't perform the search - %s\n",
766 strerror(errno));
767 goto out;
770 ret = 1;
771 if (default_id == 0) {
772 fprintf(stderr, "ERROR: 'default' dir item not found\n");
773 goto out;
776 /* no need to resolve roots if FS_TREE is default */
777 if (default_id == BTRFS_FS_TREE_OBJECTID) {
778 printf("ID 5 (FS_TREE)\n");
779 ret = 0;
780 goto out;
783 filter_set = btrfs_list_alloc_filter_set();
784 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
785 default_id);
787 /* by default we shall print the following columns*/
788 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
789 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
790 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
791 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
793 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
794 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
796 if (filter_set)
797 btrfs_list_free_filter_set(filter_set);
798 out:
799 close_file_or_dir(fd, dirstream);
800 return !!ret;
803 static const char * const cmd_subvol_set_default_usage[] = {
804 "btrfs subvolume set-default <subvolid> <path>",
805 "Set the default subvolume of a filesystem",
806 NULL
809 static int cmd_subvol_set_default(int argc, char **argv)
811 int ret=0, fd, e;
812 u64 objectid;
813 char *path;
814 char *subvolid;
815 DIR *dirstream = NULL;
817 if (check_argc_exact(argc, 3))
818 usage(cmd_subvol_set_default_usage);
820 subvolid = argv[1];
821 path = argv[2];
823 objectid = arg_strtou64(subvolid);
825 fd = open_file_or_dir(path, &dirstream);
826 if (fd < 0) {
827 fprintf(stderr, "ERROR: can't access '%s'\n", path);
828 return 1;
831 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
832 e = errno;
833 close_file_or_dir(fd, dirstream);
834 if (ret < 0) {
835 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
836 strerror(e));
837 return 1;
839 return 0;
842 static const char * const cmd_find_new_usage[] = {
843 "btrfs subvolume find-new <path> <lastgen>",
844 "List the recently modified files in a filesystem",
845 NULL
848 static int cmd_find_new(int argc, char **argv)
850 int fd;
851 int ret;
852 char *subvol;
853 u64 last_gen;
854 DIR *dirstream = NULL;
856 if (check_argc_exact(argc, 3))
857 usage(cmd_find_new_usage);
859 subvol = argv[1];
860 last_gen = arg_strtou64(argv[2]);
862 ret = test_issubvolume(subvol);
863 if (ret < 0) {
864 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
865 return 1;
867 if (!ret) {
868 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
869 return 1;
872 fd = open_file_or_dir(subvol, &dirstream);
873 if (fd < 0) {
874 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
875 return 1;
878 ret = ioctl(fd, BTRFS_IOC_SYNC);
879 if (ret < 0) {
880 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
881 subvol, strerror(errno));
882 close_file_or_dir(fd, dirstream);
883 return 1;
886 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
887 close_file_or_dir(fd, dirstream);
888 return !!ret;
891 static const char * const cmd_subvol_show_usage[] = {
892 "btrfs subvolume show <subvol-path>",
893 "Show more information of the subvolume",
894 NULL
897 static int cmd_subvol_show(int argc, char **argv)
899 struct root_info get_ri;
900 struct btrfs_list_filter_set *filter_set;
901 char tstr[256];
902 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
903 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
904 char raw_prefix[] = "\t\t\t\t";
905 u64 sv_id, mntid;
906 int fd = -1, mntfd = -1;
907 int ret = 1;
908 DIR *dirstream1 = NULL, *dirstream2 = NULL;
910 if (check_argc_exact(argc, 2))
911 usage(cmd_subvol_show_usage);
913 fullpath = realpath(argv[1], NULL);
914 if (!fullpath) {
915 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
916 argv[1], strerror(errno));
917 goto out;
920 ret = test_issubvolume(fullpath);
921 if (ret < 0) {
922 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
923 goto out;
925 if (!ret) {
926 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
927 goto out;
930 ret = find_mount_root(fullpath, &mnt);
931 if (ret < 0) {
932 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
933 "%s\n", fullpath, strerror(-ret));
934 goto out;
936 ret = 1;
937 svpath = get_subvol_name(mnt, fullpath);
939 fd = open_file_or_dir(fullpath, &dirstream1);
940 if (fd < 0) {
941 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
942 goto out;
945 ret = btrfs_list_get_path_rootid(fd, &sv_id);
946 if (ret) {
947 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
948 fullpath);
949 goto out;
952 mntfd = open_file_or_dir(mnt, &dirstream2);
953 if (mntfd < 0) {
954 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
955 goto out;
958 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
959 if (ret) {
960 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
961 goto out;
964 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
965 printf("%s is btrfs root\n", fullpath);
966 goto out;
969 memset(&get_ri, 0, sizeof(get_ri));
970 get_ri.root_id = sv_id;
972 if (btrfs_get_subvol(mntfd, &get_ri)) {
973 fprintf(stderr, "ERROR: can't find '%s'\n",
974 svpath);
975 goto out;
978 ret = 0;
979 /* print the info */
980 printf("%s\n", fullpath);
981 printf("\tName: \t\t\t%s\n", get_ri.name);
983 if (uuid_is_null(get_ri.uuid))
984 strcpy(uuidparse, "-");
985 else
986 uuid_unparse(get_ri.uuid, uuidparse);
987 printf("\tuuid: \t\t\t%s\n", uuidparse);
989 if (uuid_is_null(get_ri.puuid))
990 strcpy(uuidparse, "-");
991 else
992 uuid_unparse(get_ri.puuid, uuidparse);
993 printf("\tParent uuid: \t\t%s\n", uuidparse);
995 if (get_ri.otime) {
996 struct tm tm;
998 localtime_r(&get_ri.otime, &tm);
999 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
1000 } else
1001 strcpy(tstr, "-");
1002 printf("\tCreation time: \t\t%s\n", tstr);
1004 printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
1005 printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
1006 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1007 printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
1008 printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
1010 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1011 printf("\tFlags: \t\t\treadonly\n");
1012 else
1013 printf("\tFlags: \t\t\t-\n");
1015 /* print the snapshots of the given subvol if any*/
1016 printf("\tSnapshot(s):\n");
1017 filter_set = btrfs_list_alloc_filter_set();
1018 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1019 (u64)(unsigned long)get_ri.uuid);
1020 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1021 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1022 1, raw_prefix);
1024 /* clean up */
1025 free(get_ri.path);
1026 free(get_ri.name);
1027 free(get_ri.full_path);
1028 btrfs_list_free_filter_set(filter_set);
1030 out:
1031 close_file_or_dir(fd, dirstream1);
1032 close_file_or_dir(mntfd, dirstream2);
1033 free(mnt);
1034 free(fullpath);
1035 return !!ret;
1038 const struct cmd_group subvolume_cmd_group = {
1039 subvolume_cmd_group_usage, NULL, {
1040 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1041 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1042 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1043 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1044 { "get-default", cmd_subvol_get_default,
1045 cmd_subvol_get_default_usage, NULL, 0 },
1046 { "set-default", cmd_subvol_set_default,
1047 cmd_subvol_set_default_usage, NULL, 0 },
1048 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1049 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1050 NULL_CMD_STRUCT
1054 int cmd_subvolume(int argc, char **argv)
1056 return handle_command_group(&subvolume_cmd_group, argc, argv);