btrfs-progs: mkfs should first check all disks before writing to a disk
[platform/upstream/btrfs-progs.git] / cmds-subvolume.c
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.
5  *
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.
10  *
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.
15  */
16
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>
28
29 #include "kerncompat.h"
30 #include "ioctl.h"
31 #include "qgroup.h"
32
33 #include "ctree.h"
34 #include "commands.h"
35 #include "utils.h"
36 #include "btrfs-list.h"
37 #include "utils.h"
38
39 static const char * const subvolume_cmd_group_usage[] = {
40         "btrfs subvolume <command> <args>",
41         NULL
42 };
43
44 /*
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
50  */
51 static int test_isdir(char *path)
52 {
53         struct stat     st;
54         int             res;
55
56         res = stat(path, &st);
57         if(res < 0 )
58                 return -1;
59
60         return S_ISDIR(st.st_mode);
61 }
62
63 static const char * const cmd_subvol_create_usage[] = {
64         "btrfs subvolume create [<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         NULL
69 };
70
71 static int cmd_subvol_create(int argc, char **argv)
72 {
73         int     retval, res, len;
74         int     fddst = -1;
75         char    *newname;
76         char    *dstdir;
77         char    *dst;
78         struct btrfs_qgroup_inherit *inherit = NULL;
79
80         optind = 1;
81         while (1) {
82                 int c = getopt(argc, argv, "c:i:r");
83                 if (c < 0)
84                         break;
85
86                 switch (c) {
87                 case 'c':
88                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
89                         if (res)
90                                 return res;
91                         break;
92                 case 'i':
93                         res = qgroup_inherit_add_group(&inherit, optarg);
94                         if (res)
95                                 return res;
96                         break;
97                 default:
98                         usage(cmd_subvol_create_usage);
99                 }
100         }
101
102         if (check_argc_exact(argc - optind, 1))
103                 usage(cmd_subvol_create_usage);
104
105         dst = argv[optind];
106
107         retval = 1;     /* failure */
108         res = test_isdir(dst);
109         if (res >= 0) {
110                 fprintf(stderr, "ERROR: '%s' exists\n", dst);
111                 goto out;
112         }
113
114         newname = strdup(dst);
115         newname = basename(newname);
116         dstdir = strdup(dst);
117         dstdir = dirname(dstdir);
118
119         if (!strcmp(newname, ".") || !strcmp(newname, "..") ||
120              strchr(newname, '/') ){
121                 fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
122                         newname);
123                 goto out;
124         }
125
126         len = strlen(newname);
127         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
128                 fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
129                         newname);
130                 goto out;
131         }
132
133         fddst = open_file_or_dir(dstdir);
134         if (fddst < 0) {
135                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
136                 goto out;
137         }
138
139         printf("Create subvolume '%s/%s'\n", dstdir, newname);
140         if (inherit) {
141                 struct btrfs_ioctl_vol_args_v2  args;
142
143                 memset(&args, 0, sizeof(args));
144                 strncpy_null(args.name, newname);
145                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
146                 args.size = qgroup_inherit_size(inherit);
147                 args.qgroup_inherit = inherit;
148
149                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
150         } else {
151                 struct btrfs_ioctl_vol_args     args;
152
153                 memset(&args, 0, sizeof(args));
154                 strncpy_null(args.name, newname);
155
156                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
157         }
158
159         if (res < 0) {
160                 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
161                         strerror(errno));
162                 goto out;
163         }
164
165         retval = 0;     /* success */
166 out:
167         if (fddst != -1)
168                 close(fddst);
169         free(inherit);
170
171         return retval;
172 }
173
174 /*
175  * test if path is a subvolume:
176  * this function return
177  * 0-> path exists but it is not a subvolume
178  * 1-> path exists and it is  a subvolume
179  * -1 -> path is unaccessible
180  */
181 int test_issubvolume(char *path)
182 {
183         struct stat     st;
184         int             res;
185
186         res = stat(path, &st);
187         if(res < 0 )
188                 return -1;
189
190         return (st.st_ino == 256) && S_ISDIR(st.st_mode);
191 }
192
193 static const char * const cmd_subvol_delete_usage[] = {
194         "btrfs subvolume delete <subvolume> [<subvolume>...]",
195         "Delete subvolume(s)",
196         NULL
197 };
198
199 static int cmd_subvol_delete(int argc, char **argv)
200 {
201         int     res, fd, len, e, cnt = 1, ret = 0;
202         struct btrfs_ioctl_vol_args     args;
203         char    *dname, *vname, *cpath;
204         char    *path;
205
206         if (argc < 2)
207                 usage(cmd_subvol_delete_usage);
208
209 again:
210         path = argv[cnt];
211
212         res = test_issubvolume(path);
213         if(res<0){
214                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
215                 ret = 12;
216                 goto out;
217         }
218         if(!res){
219                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
220                 ret = 13;
221                 goto out;
222         }
223
224         cpath = realpath(path, 0);
225         dname = strdup(cpath);
226         dname = dirname(dname);
227         vname = strdup(cpath);
228         vname = basename(vname);
229         free(cpath);
230
231         if( !strcmp(vname,".") || !strcmp(vname,"..") ||
232              strchr(vname, '/') ){
233                 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
234                         vname);
235                 ret = 14;
236                 goto out;
237         }
238
239         len = strlen(vname);
240         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
241                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
242                         vname);
243                 ret = 14;
244                 goto out;
245         }
246
247         fd = open_file_or_dir(dname);
248         if (fd < 0) {
249                 fprintf(stderr, "ERROR: can't access to '%s'\n", dname);
250                 ret = 12;
251                 goto out;
252         }
253
254         printf("Delete subvolume '%s/%s'\n", dname, vname);
255         strncpy_null(args.name, vname);
256         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
257         e = errno;
258
259         close(fd);
260
261         if(res < 0 ){
262                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
263                         dname, vname, strerror(e));
264                 ret = 11;
265                 goto out;
266         }
267
268 out:
269         cnt++;
270         if (cnt < argc)
271                 goto again;
272
273         return ret;
274 }
275
276 /*
277  * Naming of options:
278  * - uppercase for filters and sort options
279  * - lowercase for enabling specific items in the output
280  */
281 static const char * const cmd_subvol_list_usage[] = {
282         "btrfs subvolume list [-agopurts] [-G [+|-]value] [-C [+|-]value] "
283         "[--sort=gen,ogen,rootid,path] <path>",
284         "List subvolumes (and snapshots)",
285         "",
286         "-p           print parent ID",
287         "-a           print all the subvolumes in the filesystem and",
288         "             distinguish absolute and relative path with respect",
289         "             to the given <path>",
290         "-c           print the ogeneration of the subvolume",
291         "-g           print the generation of the subvolume",
292         "-o           print only subvolumes bellow specified path",
293         "-u           print the uuid of subvolumes (and snapshots)",
294         "-q           print the parent uuid of the snapshots",
295         "-t           print the result as a table",
296         "-s           list snapshots only in the filesystem",
297         "-r           list readonly subvolumes (including snapshots)",
298         "-G [+|-]value",
299         "             filter the subvolumes by generation",
300         "             (+value: >= value; -value: <= value; value: = value)",
301         "-C [+|-]value",
302         "             filter the subvolumes by ogeneration",
303         "             (+value: >= value; -value: <= value; value: = value)",
304         "--sort=gen,ogen,rootid,path",
305         "             list the subvolume in order of gen, ogen, rootid or path",
306         "             you also can add '+' or '-' in front of each items.",
307         "             (+:ascending, -:descending, ascending default)",
308         NULL,
309 };
310
311 static int cmd_subvol_list(int argc, char **argv)
312 {
313         struct btrfs_list_filter_set *filter_set;
314         struct btrfs_list_comparer_set *comparer_set;
315         u64 flags = 0;
316         int fd = -1;
317         u64 top_id;
318         int ret = -1, uerr = 0;
319         int c;
320         char *subvol;
321         int is_tab_result = 0;
322         int is_list_all = 0;
323         int is_only_in_path = 0;
324         struct option long_options[] = {
325                 {"sort", 1, NULL, 'S'},
326                 {0, 0, 0, 0}
327         };
328
329         filter_set = btrfs_list_alloc_filter_set();
330         comparer_set = btrfs_list_alloc_comparer_set();
331
332         optind = 1;
333         while(1) {
334                 c = getopt_long(argc, argv,
335                                     "acgopqsurG:C:t", long_options, NULL);
336                 if (c < 0)
337                         break;
338
339                 switch(c) {
340                 case 'p':
341                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
342                         break;
343                 case 'a':
344                         is_list_all = 1;
345                         break;
346                 case 'c':
347                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
348                         break;
349                 case 'g':
350                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
351                         break;
352                 case 'o':
353                         is_only_in_path = 1;
354                         break;
355                 case 't':
356                         is_tab_result = 1;
357                         break;
358                 case 's':
359                         btrfs_list_setup_filter(&filter_set,
360                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
361                                                 0);
362                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
363                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
364                         break;
365                 case 'u':
366                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
367                         break;
368                 case 'q':
369                         btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
370                         break;
371                 case 'r':
372                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
373                         break;
374                 case 'G':
375                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
376                         ret = btrfs_list_parse_filter_string(optarg,
377                                                         &filter_set,
378                                                         BTRFS_LIST_FILTER_GEN);
379                         if (ret) {
380                                 uerr = 1;
381                                 goto out;
382                         }
383                         break;
384
385                 case 'C':
386                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
387                         ret = btrfs_list_parse_filter_string(optarg,
388                                                         &filter_set,
389                                                         BTRFS_LIST_FILTER_CGEN);
390                         if (ret) {
391                                 uerr = 1;
392                                 goto out;
393                         }
394                         break;
395                 case 'S':
396                         ret = btrfs_list_parse_sort_string(optarg,
397                                                            &comparer_set);
398                         if (ret) {
399                                 uerr = 1;
400                                 goto out;
401                         }
402                         break;
403
404                 default:
405                         uerr = 1;
406                         goto out;
407                 }
408         }
409
410         if (flags)
411                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
412                                         flags);
413
414         if (check_argc_exact(argc - optind, 1)) {
415                 uerr = 1;
416                 goto out;
417         }
418
419         subvol = argv[optind];
420
421         ret = test_issubvolume(subvol);
422         if (ret < 0) {
423                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
424                 goto out;
425         }
426         if (!ret) {
427                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
428                 ret = -1;
429                 goto out;
430         }
431
432         fd = open_file_or_dir(subvol);
433         if (fd < 0) {
434                 ret = -1;
435                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
436                 goto out;
437         }
438
439         ret = btrfs_list_get_path_rootid(fd, &top_id);
440         if (ret) {
441                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
442                 goto out;
443         }
444
445         if (is_list_all)
446                 btrfs_list_setup_filter(&filter_set,
447                                         BTRFS_LIST_FILTER_FULL_PATH,
448                                         top_id);
449         else if (is_only_in_path)
450                 btrfs_list_setup_filter(&filter_set,
451                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
452                                         top_id);
453
454         /* by default we shall print the following columns*/
455         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
456         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
457         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
458         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
459
460         if (is_tab_result)
461                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
462                                 BTRFS_LIST_LAYOUT_TABLE,
463                                 !is_list_all && !is_only_in_path, NULL);
464         else
465                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
466                                 BTRFS_LIST_LAYOUT_DEFAULT,
467                                 !is_list_all && !is_only_in_path, NULL);
468
469 out:
470         if (fd != -1)
471                 close(fd);
472         if (filter_set)
473                 btrfs_list_free_filter_set(filter_set);
474         if (comparer_set)
475                 btrfs_list_free_comparer_set(comparer_set);
476         if (uerr)
477                 usage(cmd_subvol_list_usage);
478
479         return ret;
480 }
481
482 static const char * const cmd_snapshot_usage[] = {
483         "btrfs subvolume snapshot [-r] <source> [<dest>/]<name>",
484         "Create a snapshot of the subvolume",
485         "Create a writable/readonly snapshot of the subvolume <source> with",
486         "the name <name> in the <dest> directory",
487         "",
488         "-r     create a readonly snapshot",
489         NULL
490 };
491
492 static int cmd_snapshot(int argc, char **argv)
493 {
494         char    *subvol, *dst;
495         int     res, retval;
496         int     fd = -1, fddst = -1;
497         int     len, readonly = 0;
498         char    *newname;
499         char    *dstdir;
500         struct btrfs_ioctl_vol_args_v2  args;
501         struct btrfs_qgroup_inherit *inherit = NULL;
502
503         optind = 1;
504         memset(&args, 0, sizeof(args));
505         while (1) {
506                 int c = getopt(argc, argv, "c:i:r");
507                 if (c < 0)
508                         break;
509
510                 switch (c) {
511                 case 'c':
512                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
513                         if (res)
514                                 return res;
515                         break;
516                 case 'i':
517                         res = qgroup_inherit_add_group(&inherit, optarg);
518                         if (res)
519                                 return res;
520                         break;
521                 case 'r':
522                         readonly = 1;
523                         break;
524                 case 'x':
525                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
526                         if (res)
527                                 return res;
528                         break;
529                 default:
530                         usage(cmd_snapshot_usage);
531                 }
532         }
533
534         if (check_argc_exact(argc - optind, 2))
535                 usage(cmd_snapshot_usage);
536
537         subvol = argv[optind];
538         dst = argv[optind + 1];
539
540         retval = 1;     /* failure */
541         res = test_issubvolume(subvol);
542         if (res < 0) {
543                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
544                 goto out;
545         }
546         if (!res) {
547                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
548                 goto out;
549         }
550
551         res = test_isdir(dst);
552         if (res == 0) {
553                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
554                 goto out;
555         }
556
557         if (res > 0) {
558                 newname = strdup(subvol);
559                 newname = basename(newname);
560                 dstdir = dst;
561         } else {
562                 newname = strdup(dst);
563                 newname = basename(newname);
564                 dstdir = strdup(dst);
565                 dstdir = dirname(dstdir);
566         }
567
568         if (!strcmp(newname, ".") || !strcmp(newname, "..") ||
569              strchr(newname, '/') ){
570                 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
571                         newname);
572                 goto out;
573         }
574
575         len = strlen(newname);
576         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
577                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
578                         newname);
579                 goto out;
580         }
581
582         fddst = open_file_or_dir(dstdir);
583         if (fddst < 0) {
584                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
585                 goto out;
586         }
587
588         fd = open_file_or_dir(subvol);
589         if (fd < 0) {
590                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
591                 goto out;
592         }
593
594         if (readonly) {
595                 args.flags |= BTRFS_SUBVOL_RDONLY;
596                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
597                        subvol, dstdir, newname);
598         } else {
599                 printf("Create a snapshot of '%s' in '%s/%s'\n",
600                        subvol, dstdir, newname);
601         }
602
603         args.fd = fd;
604         if (inherit) {
605                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
606                 args.size = qgroup_inherit_size(inherit);
607                 args.qgroup_inherit = inherit;
608         }
609         strncpy_null(args.name, newname);
610
611         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
612
613         if (res < 0) {
614                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
615                         subvol, strerror(errno));
616                 goto out;
617         }
618
619         retval = 0;     /* success */
620
621 out:
622         if (fd != -1)
623                 close(fd);
624         if (fddst != -1)
625                 close(fddst);
626         free(inherit);
627
628         return retval;
629 }
630
631 static const char * const cmd_subvol_get_default_usage[] = {
632         "btrfs subvolume get-default <path>",
633         "Get the default subvolume of a filesystem",
634         NULL
635 };
636
637 static int cmd_subvol_get_default(int argc, char **argv)
638 {
639         int fd = -1;
640         int ret;
641         char *subvol;
642         struct btrfs_list_filter_set *filter_set;
643         u64 default_id;
644
645         if (check_argc_exact(argc, 2))
646                 usage(cmd_subvol_get_default_usage);
647
648         subvol = argv[1];
649
650         ret = test_issubvolume(subvol);
651         if (ret < 0) {
652                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
653                 return 1;
654         }
655         if (!ret) {
656                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
657                 return 1;
658         }
659
660         fd = open_file_or_dir(subvol);
661         if (fd < 0) {
662                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
663                 return 1;
664         }
665
666         ret = btrfs_list_get_default_subvolume(fd, &default_id);
667         if (ret) {
668                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
669                         strerror(errno));
670                 goto out;
671         }
672
673         ret = 1;
674         if (default_id == 0) {
675                 fprintf(stderr, "ERROR: 'default' dir item not found\n");
676                 goto out;
677         }
678
679         /* no need to resolve roots if FS_TREE is default */
680         if (default_id == BTRFS_FS_TREE_OBJECTID) {
681                 printf("ID 5 (FS_TREE)\n");
682                 goto out;
683         }
684
685         filter_set = btrfs_list_alloc_filter_set();
686         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
687                                 default_id);
688
689         /* by default we shall print the following columns*/
690         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
691         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
692         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
693         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
694
695         ret = btrfs_list_subvols_print(fd, filter_set, NULL,
696                 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
697
698         if (filter_set)
699                 btrfs_list_free_filter_set(filter_set);
700 out:
701         if (fd != -1)
702                 close(fd);
703         if (ret)
704                 return 1;
705         return 0;
706 }
707
708 static const char * const cmd_subvol_set_default_usage[] = {
709         "btrfs subvolume set-default <subvolid> <path>",
710         "Set the default subvolume of a filesystem",
711         NULL
712 };
713
714 static int cmd_subvol_set_default(int argc, char **argv)
715 {
716         int     ret=0, fd, e;
717         u64     objectid;
718         char    *path;
719         char    *subvolid;
720
721         if (check_argc_exact(argc, 3))
722                 usage(cmd_subvol_set_default_usage);
723
724         subvolid = argv[1];
725         path = argv[2];
726
727         objectid = (unsigned long long)strtoll(subvolid, NULL, 0);
728         if (errno == ERANGE) {
729                 fprintf(stderr, "ERROR: invalid tree id (%s)\n", subvolid);
730                 return 1;
731         }
732
733         fd = open_file_or_dir(path);
734         if (fd < 0) {
735                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
736                 return 1;
737         }
738
739         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
740         e = errno;
741         close(fd);
742         if (ret < 0) {
743                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
744                         strerror(e));
745                 return 1;
746         }
747         return 0;
748 }
749
750 static const char * const cmd_find_new_usage[] = {
751         "btrfs subvolume find-new <path> <lastgen>",
752         "List the recently modified files in a filesystem",
753         NULL
754 };
755
756 static int cmd_find_new(int argc, char **argv)
757 {
758         int fd;
759         int ret;
760         char *subvol;
761         u64 last_gen;
762
763         if (check_argc_exact(argc, 3))
764                 usage(cmd_find_new_usage);
765
766         subvol = argv[1];
767         last_gen = atoll(argv[2]);
768
769         ret = test_issubvolume(subvol);
770         if (ret < 0) {
771                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
772                 return 12;
773         }
774         if (!ret) {
775                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
776                 return 13;
777         }
778
779         fd = open_file_or_dir(subvol);
780         if (fd < 0) {
781                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
782                 return 12;
783         }
784         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
785         close(fd);
786         if (ret)
787                 return 19;
788         return 0;
789 }
790
791 static const char * const cmd_subvol_show_usage[] = {
792         "btrfs subvolume show <subvol-path>",
793         "Show more information of the subvolume",
794         NULL
795 };
796
797 static int cmd_subvol_show(int argc, char **argv)
798 {
799         struct root_info get_ri;
800         struct btrfs_list_filter_set *filter_set;
801         char tstr[256];
802         char uuidparse[37];
803         char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
804         char raw_prefix[] = "\t\t\t\t";
805         u64 sv_id, mntid;
806         int fd = -1, mntfd = -1;
807         int ret = -1;
808
809         if (check_argc_exact(argc, 2))
810                 usage(cmd_subvol_show_usage);
811
812         fullpath = realpath(argv[1], 0);
813         if (!fullpath) {
814                 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
815                         argv[1], strerror(errno));
816                 goto out;
817         }
818
819         ret = test_issubvolume(fullpath);
820         if (ret < 0) {
821                 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
822                 goto out;
823         }
824         if (!ret) {
825                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
826                 ret = -1;
827                 goto out;
828         }
829
830         ret = find_mount_root(fullpath, &mnt);
831         if (ret < 0) {
832                 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
833                                 "%s\n", fullpath, strerror(-ret));
834                 goto out;
835         }
836         ret = -1;
837         svpath = get_subvol_name(mnt, fullpath);
838
839         fd = open_file_or_dir(fullpath);
840         if (fd < 0) {
841                 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
842                 goto out;
843         }
844
845         ret = btrfs_list_get_path_rootid(fd, &sv_id);
846         if (ret) {
847                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
848                         fullpath);
849                 goto out;
850         }
851
852         mntfd = open_file_or_dir(mnt);
853         if (mntfd < 0) {
854                 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
855                 goto out;
856         }
857
858         ret = btrfs_list_get_path_rootid(mntfd, &mntid);
859         if (ret) {
860                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
861                 goto out;
862         }
863
864         if (sv_id == BTRFS_FS_TREE_OBJECTID) {
865                 printf("%s is btrfs root\n", fullpath);
866                 goto out;
867         }
868
869         memset(&get_ri, 0, sizeof(get_ri));
870         get_ri.root_id = sv_id;
871
872         if (btrfs_get_subvol(mntfd, &get_ri)) {
873                 fprintf(stderr, "ERROR: can't find '%s'\n",
874                         svpath);
875                 goto out;
876         }
877
878         ret = 0;
879         /* print the info */
880         printf("%s\n", fullpath);
881         printf("\tName: \t\t\t%s\n", get_ri.name);
882
883         if (uuid_is_null(get_ri.uuid))
884                 strcpy(uuidparse, "-");
885         else
886                 uuid_unparse(get_ri.uuid, uuidparse);
887         printf("\tuuid: \t\t\t%s\n", uuidparse);
888
889         if (uuid_is_null(get_ri.puuid))
890                 strcpy(uuidparse, "-");
891         else
892                 uuid_unparse(get_ri.puuid, uuidparse);
893         printf("\tParent uuid: \t\t%s\n", uuidparse);
894
895         if (get_ri.otime)
896                 strftime(tstr, 256, "%Y-%m-%d %X",
897                          localtime(&get_ri.otime));
898         else
899                 strcpy(tstr, "-");
900         printf("\tCreation time: \t\t%s\n", tstr);
901
902         printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
903         printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
904         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
905         printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
906         printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
907
908         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
909                 printf("\tFlags: \t\t\treadonly\n");
910         else
911                 printf("\tFlags: \t\t\t-\n");
912
913         /* print the snapshots of the given subvol if any*/
914         printf("\tSnapshot(s):\n");
915         filter_set = btrfs_list_alloc_filter_set();
916         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
917                                 (u64)get_ri.uuid);
918         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
919         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
920                         1, raw_prefix);
921
922         /* clean up */
923         if (get_ri.path)
924                 free(get_ri.path);
925         if (get_ri.name)
926                 free(get_ri.name);
927         if (get_ri.full_path)
928                 free(get_ri.full_path);
929         if (filter_set)
930                 btrfs_list_free_filter_set(filter_set);
931
932 out:
933         if (mntfd >= 0)
934                 close(mntfd);
935         if (fd >= 0)
936                 close(fd);
937         if (mnt)
938                 free(mnt);
939         if (fullpath)
940                 free(fullpath);
941
942         return ret;
943 }
944
945 const struct cmd_group subvolume_cmd_group = {
946         subvolume_cmd_group_usage, NULL, {
947                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
948                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
949                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
950                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
951                 { "get-default", cmd_subvol_get_default,
952                         cmd_subvol_get_default_usage, NULL, 0 },
953                 { "set-default", cmd_subvol_set_default,
954                         cmd_subvol_set_default_usage, NULL, 0 },
955                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
956                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
957                 { 0, 0, 0, 0, 0 }
958         }
959 };
960
961 int cmd_subvolume(int argc, char **argv)
962 {
963         return handle_command_group(&subvolume_cmd_group, argc, argv);
964 }