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