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