btrfs-progs: Remove redundant short description from cmd_snapshot_usage
[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 static const char * const cmd_subvol_create_usage[] = {
45         "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
46         "Create a subvolume",
47         "Create a subvolume <name> in <dest>.  If <dest> is not given",
48         "subvolume <name> will be created in the current directory.",
49         "",
50         "-i <qgroupid>  add the newly created subvolume to a qgroup. This",
51         "               option can be given multiple times.",
52         NULL
53 };
54
55 static int cmd_subvol_create(int argc, char **argv)
56 {
57         int     retval, res, len;
58         int     fddst = -1;
59         char    *dupname = NULL;
60         char    *dupdir = NULL;
61         char    *newname;
62         char    *dstdir;
63         char    *dst;
64         struct btrfs_qgroup_inherit *inherit = NULL;
65         DIR     *dirstream = NULL;
66
67         optind = 1;
68         while (1) {
69                 int c = getopt(argc, argv, "c:i:v");
70                 if (c < 0)
71                         break;
72
73                 switch (c) {
74                 case 'c':
75                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
76                         if (res) {
77                                 retval = res;
78                                 goto out;
79                         }
80                         break;
81                 case 'i':
82                         res = qgroup_inherit_add_group(&inherit, optarg);
83                         if (res) {
84                                 retval = res;
85                                 goto out;
86                         }
87                         break;
88                 default:
89                         usage(cmd_subvol_create_usage);
90                 }
91         }
92
93         if (check_argc_exact(argc - optind, 1))
94                 usage(cmd_subvol_create_usage);
95
96         dst = argv[optind];
97
98         retval = 1;     /* failure */
99         res = test_isdir(dst);
100         if (res >= 0) {
101                 fprintf(stderr, "ERROR: '%s' exists\n", dst);
102                 goto out;
103         }
104
105         dupname = strdup(dst);
106         newname = basename(dupname);
107         dupdir = strdup(dst);
108         dstdir = dirname(dupdir);
109
110         if (!test_issubvolname(newname)) {
111                 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
112                         newname);
113                 goto out;
114         }
115
116         len = strlen(newname);
117         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
118                 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
119                         newname);
120                 goto out;
121         }
122
123         fddst = open_file_or_dir(dstdir, &dirstream);
124         if (fddst < 0) {
125                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
126                 goto out;
127         }
128
129         printf("Create subvolume '%s/%s'\n", dstdir, newname);
130         if (inherit) {
131                 struct btrfs_ioctl_vol_args_v2  args;
132
133                 memset(&args, 0, sizeof(args));
134                 strncpy_null(args.name, newname);
135                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
136                 args.size = qgroup_inherit_size(inherit);
137                 args.qgroup_inherit = inherit;
138
139                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
140         } else {
141                 struct btrfs_ioctl_vol_args     args;
142
143                 memset(&args, 0, sizeof(args));
144                 strncpy_null(args.name, newname);
145
146                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
147         }
148
149         if (res < 0) {
150                 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
151                         strerror(errno));
152                 goto out;
153         }
154
155         retval = 0;     /* success */
156 out:
157         close_file_or_dir(fddst, dirstream);
158         free(inherit);
159         free(dupname);
160         free(dupdir);
161
162         return retval;
163 }
164
165 /*
166  * test if path is a subvolume:
167  * this function return
168  * 0-> path exists but it is not a subvolume
169  * 1-> path exists and it is  a subvolume
170  * -1 -> path is unaccessible
171  */
172 int test_issubvolume(char *path)
173 {
174         struct stat     st;
175         int             res;
176
177         res = stat(path, &st);
178         if(res < 0 )
179                 return -1;
180
181         return (st.st_ino == 256) && S_ISDIR(st.st_mode);
182 }
183
184 static int wait_for_commit(int fd)
185 {
186         int ret;
187
188         ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
189         if (ret < 0)
190                 return ret;
191         return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
192 }
193
194 static const char * const cmd_subvol_delete_usage[] = {
195         "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
196         "Delete subvolume(s)",
197         "Delete subvolumes from the filesystem. The corresponding directory",
198         "is removed instantly but the data blocks are removed later.",
199         "The deletion does not involve full commit by default due to",
200         "performance reasons (as a consequence, the subvolume may appear again",
201         "after a crash). Use one of the --commit options to wait until the",
202         "operation is safely stored on the media.",
203         "",
204         "-c|--commit-after      wait for transaction commit at the end of the operation",
205         "-C|--commit-each       wait for transaction commit after deleting each subvolume",
206         NULL
207 };
208
209 static int cmd_subvol_delete(int argc, char **argv)
210 {
211         int     res, len, e, ret = 0;
212         int cnt;
213         int fd = -1;
214         struct btrfs_ioctl_vol_args     args;
215         char    *dname, *vname, *cpath;
216         char    *dupdname = NULL;
217         char    *dupvname = NULL;
218         char    *path;
219         DIR     *dirstream = NULL;
220         int verbose = 0;
221         int commit_mode = 0;
222         struct option long_options[] = {
223                 {"commit-after", no_argument, NULL, 'c'},  /* commit mode 1 */
224                 {"commit-each", no_argument, NULL, 'C'},  /* commit mode 2 */
225                 {NULL, 0, NULL, 0}
226         };
227
228         optind = 1;
229         while (1) {
230                 int c;
231
232                 c = getopt_long(argc, argv, "cC", long_options, NULL);
233                 if (c < 0)
234                         break;
235
236                 switch(c) {
237                 case 'c':
238                         commit_mode = 1;
239                         break;
240                 case 'C':
241                         commit_mode = 2;
242                         break;
243                 case 'v':
244                         verbose++;
245                         break;
246                 default:
247                         usage(cmd_subvol_delete_usage);
248                 }
249         }
250
251         if (check_argc_min(argc - optind, 1))
252                 usage(cmd_subvol_delete_usage);
253
254         if (verbose > 0) {
255                 printf("Transaction commit: %s\n",
256                         !commit_mode ? "none (default)" :
257                         commit_mode == 1 ? "at the end" : "after each");
258         }
259
260         cnt = optind;
261
262 again:
263         path = argv[cnt];
264
265         res = test_issubvolume(path);
266         if (res < 0) {
267                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
268                 ret = 1;
269                 goto out;
270         }
271         if (!res) {
272                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
273                 ret = 1;
274                 goto out;
275         }
276
277         cpath = realpath(path, NULL);
278         if (!cpath) {
279                 ret = errno;
280                 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
281                         path, strerror(errno));
282                 goto out;
283         }
284         dupdname = strdup(cpath);
285         dname = dirname(dupdname);
286         dupvname = strdup(cpath);
287         vname = basename(dupvname);
288         free(cpath);
289
290         if (!test_issubvolname(vname)) {
291                 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
292                         vname);
293                 ret = 1;
294                 goto out;
295         }
296
297         len = strlen(vname);
298         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
299                 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
300                         vname);
301                 ret = 1;
302                 goto out;
303         }
304
305         fd = open_file_or_dir(dname, &dirstream);
306         if (fd < 0) {
307                 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
308                 ret = 1;
309                 goto out;
310         }
311
312         printf("Delete subvolume (%s): '%s/%s'\n",
313                 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
314                 ? "commit" : "no-commit", dname, vname);
315         strncpy_null(args.name, vname);
316         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
317         e = errno;
318
319         if(res < 0 ){
320                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
321                         dname, vname, strerror(e));
322                 ret = 1;
323                 goto out;
324         }
325
326         if (commit_mode == 1) {
327                 res = wait_for_commit(fd);
328                 if (res < 0) {
329                         fprintf(stderr,
330                                 "ERROR: unable to wait for commit after '%s': %s\n",
331                                 path, strerror(errno));
332                         ret = 1;
333                 }
334         }
335
336 out:
337         free(dupdname);
338         free(dupvname);
339         dupdname = NULL;
340         dupvname = NULL;
341         cnt++;
342         if (cnt < argc) {
343                 close_file_or_dir(fd, dirstream);
344                 /* avoid double free */
345                 fd = -1;
346                 dirstream = NULL;
347                 goto again;
348         }
349
350         if (commit_mode == 2 && fd != -1) {
351                 res = wait_for_commit(fd);
352                 if (res < 0) {
353                         fprintf(stderr,
354                                 "ERROR: unable to do final sync: %s\n",
355                                 strerror(errno));
356                         ret = 1;
357                 }
358         }
359         close_file_or_dir(fd, dirstream);
360
361         return ret;
362 }
363
364 /*
365  * Naming of options:
366  * - uppercase for filters and sort options
367  * - lowercase for enabling specific items in the output
368  */
369 static const char * const cmd_subvol_list_usage[] = {
370         "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
371         "[--sort=gen,ogen,rootid,path] <path>",
372         "List subvolumes (and snapshots)",
373         "",
374         "-p           print parent ID",
375         "-a           print all the subvolumes in the filesystem and",
376         "             distinguish absolute and relative path with respect",
377         "             to the given <path>",
378         "-c           print the ogeneration of the subvolume",
379         "-g           print the generation of the subvolume",
380         "-o           print only subvolumes below specified path",
381         "-u           print the uuid of subvolumes (and snapshots)",
382         "-q           print the parent uuid of the snapshots",
383         "-R           print the uuid of the received snapshots",
384         "-t           print the result as a table",
385         "-s           list snapshots only in the filesystem",
386         "-r           list readonly subvolumes (including snapshots)",
387         "-d           list deleted subvolumes that are not yet cleaned",
388         "-G [+|-]value",
389         "             filter the subvolumes by generation",
390         "             (+value: >= value; -value: <= value; value: = value)",
391         "-C [+|-]value",
392         "             filter the subvolumes by ogeneration",
393         "             (+value: >= value; -value: <= value; value: = value)",
394         "--sort=gen,ogen,rootid,path",
395         "             list the subvolume in order of gen, ogen, rootid or path",
396         "             you also can add '+' or '-' in front of each items.",
397         "             (+:ascending, -:descending, ascending default)",
398         NULL,
399 };
400
401 static int cmd_subvol_list(int argc, char **argv)
402 {
403         struct btrfs_list_filter_set *filter_set;
404         struct btrfs_list_comparer_set *comparer_set;
405         u64 flags = 0;
406         int fd = -1;
407         u64 top_id;
408         int ret = -1, uerr = 0;
409         int c;
410         char *subvol;
411         int is_tab_result = 0;
412         int is_list_all = 0;
413         int is_only_in_path = 0;
414         struct option long_options[] = {
415                 {"sort", 1, NULL, 'S'},
416                 {NULL, 0, NULL, 0}
417         };
418         DIR *dirstream = NULL;
419
420         filter_set = btrfs_list_alloc_filter_set();
421         comparer_set = btrfs_list_alloc_comparer_set();
422
423         optind = 1;
424         while(1) {
425                 c = getopt_long(argc, argv,
426                                     "acdgopqsurRG:C:t", long_options, NULL);
427                 if (c < 0)
428                         break;
429
430                 switch(c) {
431                 case 'p':
432                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
433                         break;
434                 case 'a':
435                         is_list_all = 1;
436                         break;
437                 case 'c':
438                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
439                         break;
440                 case 'd':
441                         btrfs_list_setup_filter(&filter_set,
442                                                 BTRFS_LIST_FILTER_DELETED,
443                                                 0);
444                         break;
445                 case 'g':
446                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
447                         break;
448                 case 'o':
449                         is_only_in_path = 1;
450                         break;
451                 case 't':
452                         is_tab_result = 1;
453                         break;
454                 case 's':
455                         btrfs_list_setup_filter(&filter_set,
456                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
457                                                 0);
458                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
459                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
460                         break;
461                 case 'u':
462                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
463                         break;
464                 case 'q':
465                         btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
466                         break;
467                 case 'R':
468                         btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
469                         break;
470                 case 'r':
471                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
472                         break;
473                 case 'G':
474                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
475                         ret = btrfs_list_parse_filter_string(optarg,
476                                                         &filter_set,
477                                                         BTRFS_LIST_FILTER_GEN);
478                         if (ret) {
479                                 uerr = 1;
480                                 goto out;
481                         }
482                         break;
483
484                 case 'C':
485                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
486                         ret = btrfs_list_parse_filter_string(optarg,
487                                                         &filter_set,
488                                                         BTRFS_LIST_FILTER_CGEN);
489                         if (ret) {
490                                 uerr = 1;
491                                 goto out;
492                         }
493                         break;
494                 case 'S':
495                         ret = btrfs_list_parse_sort_string(optarg,
496                                                            &comparer_set);
497                         if (ret) {
498                                 uerr = 1;
499                                 goto out;
500                         }
501                         break;
502
503                 default:
504                         uerr = 1;
505                         goto out;
506                 }
507         }
508
509         if (flags)
510                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
511                                         flags);
512
513         if (check_argc_exact(argc - optind, 1)) {
514                 uerr = 1;
515                 goto out;
516         }
517
518         subvol = argv[optind];
519         fd = open_file_or_dir(subvol, &dirstream);
520         if (fd < 0) {
521                 ret = -1;
522                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
523                 goto out;
524         }
525
526         ret = btrfs_list_get_path_rootid(fd, &top_id);
527         if (ret) {
528                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
529                 goto out;
530         }
531
532         if (is_list_all)
533                 btrfs_list_setup_filter(&filter_set,
534                                         BTRFS_LIST_FILTER_FULL_PATH,
535                                         top_id);
536         else if (is_only_in_path)
537                 btrfs_list_setup_filter(&filter_set,
538                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
539                                         top_id);
540
541         /* by default we shall print the following columns*/
542         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
543         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
544         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
545         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
546
547         if (is_tab_result)
548                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
549                                 BTRFS_LIST_LAYOUT_TABLE,
550                                 !is_list_all && !is_only_in_path, NULL);
551         else
552                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
553                                 BTRFS_LIST_LAYOUT_DEFAULT,
554                                 !is_list_all && !is_only_in_path, NULL);
555
556 out:
557         close_file_or_dir(fd, dirstream);
558         if (filter_set)
559                 btrfs_list_free_filter_set(filter_set);
560         if (comparer_set)
561                 btrfs_list_free_comparer_set(comparer_set);
562         if (uerr)
563                 usage(cmd_subvol_list_usage);
564         return !!ret;
565 }
566
567 static const char * const cmd_snapshot_usage[] = {
568         "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
569         "Create a snapshot of the subvolume",
570         "Create a writable/readonly snapshot of the subvolume <source> with",
571         "the name <name> in the <dest> directory.  If only <dest> is given,",
572         "the subvolume will be named the basename of <source>.",
573         "",
574         "-r             create a readonly snapshot",
575         "-i <qgroupid>  add the newly created snapshot to a qgroup. This",
576         "               option can be given multiple times.",
577         NULL
578 };
579
580 static int cmd_snapshot(int argc, char **argv)
581 {
582         char    *subvol, *dst;
583         int     res, retval;
584         int     fd = -1, fddst = -1;
585         int     len, readonly = 0;
586         char    *dupname = NULL;
587         char    *dupdir = NULL;
588         char    *newname;
589         char    *dstdir;
590         struct btrfs_ioctl_vol_args_v2  args;
591         struct btrfs_qgroup_inherit *inherit = NULL;
592         DIR *dirstream1 = NULL, *dirstream2 = NULL;
593
594         optind = 1;
595         memset(&args, 0, sizeof(args));
596         while (1) {
597                 int c = getopt(argc, argv, "c:i:r");
598                 if (c < 0)
599                         break;
600
601                 switch (c) {
602                 case 'c':
603                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
604                         if (res) {
605                                 retval = res;
606                                 goto out;
607                         }
608                         break;
609                 case 'i':
610                         res = qgroup_inherit_add_group(&inherit, optarg);
611                         if (res) {
612                                 retval = res;
613                                 goto out;
614                         }
615                         break;
616                 case 'r':
617                         readonly = 1;
618                         break;
619                 case 'x':
620                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
621                         if (res) {
622                                 retval = res;
623                                 goto out;
624                         }
625                         break;
626                 default:
627                         usage(cmd_snapshot_usage);
628                 }
629         }
630
631         if (check_argc_exact(argc - optind, 2))
632                 usage(cmd_snapshot_usage);
633
634         subvol = argv[optind];
635         dst = argv[optind + 1];
636
637         retval = 1;     /* failure */
638         res = test_issubvolume(subvol);
639         if (res < 0) {
640                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
641                 goto out;
642         }
643         if (!res) {
644                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
645                 goto out;
646         }
647
648         res = test_isdir(dst);
649         if (res == 0) {
650                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
651                 goto out;
652         }
653
654         if (res > 0) {
655                 dupname = strdup(subvol);
656                 newname = basename(dupname);
657                 dstdir = dst;
658         } else {
659                 dupname = strdup(dst);
660                 newname = basename(dupname);
661                 dupdir = strdup(dst);
662                 dstdir = dirname(dupdir);
663         }
664
665         if (!test_issubvolname(newname)) {
666                 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
667                         newname);
668                 goto out;
669         }
670
671         len = strlen(newname);
672         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
673                 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
674                         newname);
675                 goto out;
676         }
677
678         fddst = open_file_or_dir(dstdir, &dirstream1);
679         if (fddst < 0) {
680                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
681                 goto out;
682         }
683
684         fd = open_file_or_dir(subvol, &dirstream2);
685         if (fd < 0) {
686                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
687                 goto out;
688         }
689
690         if (readonly) {
691                 args.flags |= BTRFS_SUBVOL_RDONLY;
692                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
693                        subvol, dstdir, newname);
694         } else {
695                 printf("Create a snapshot of '%s' in '%s/%s'\n",
696                        subvol, dstdir, newname);
697         }
698
699         args.fd = fd;
700         if (inherit) {
701                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
702                 args.size = qgroup_inherit_size(inherit);
703                 args.qgroup_inherit = inherit;
704         }
705         strncpy_null(args.name, newname);
706
707         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
708
709         if (res < 0) {
710                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
711                         subvol, strerror(errno));
712                 goto out;
713         }
714
715         retval = 0;     /* success */
716
717 out:
718         close_file_or_dir(fddst, dirstream1);
719         close_file_or_dir(fd, dirstream2);
720         free(inherit);
721         free(dupname);
722         free(dupdir);
723
724         return retval;
725 }
726
727 static const char * const cmd_subvol_get_default_usage[] = {
728         "btrfs subvolume get-default <path>",
729         "Get the default subvolume of a filesystem",
730         NULL
731 };
732
733 static int cmd_subvol_get_default(int argc, char **argv)
734 {
735         int fd = -1;
736         int ret;
737         char *subvol;
738         struct btrfs_list_filter_set *filter_set;
739         u64 default_id;
740         DIR *dirstream = NULL;
741
742         if (check_argc_exact(argc, 2))
743                 usage(cmd_subvol_get_default_usage);
744
745         subvol = argv[1];
746         fd = open_file_or_dir(subvol, &dirstream);
747         if (fd < 0) {
748                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
749                 return 1;
750         }
751
752         ret = btrfs_list_get_default_subvolume(fd, &default_id);
753         if (ret) {
754                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
755                         strerror(errno));
756                 goto out;
757         }
758
759         ret = 1;
760         if (default_id == 0) {
761                 fprintf(stderr, "ERROR: 'default' dir item not found\n");
762                 goto out;
763         }
764
765         /* no need to resolve roots if FS_TREE is default */
766         if (default_id == BTRFS_FS_TREE_OBJECTID) {
767                 printf("ID 5 (FS_TREE)\n");
768                 ret = 0;
769                 goto out;
770         }
771
772         filter_set = btrfs_list_alloc_filter_set();
773         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
774                                 default_id);
775
776         /* by default we shall print the following columns*/
777         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
778         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
779         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
780         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
781
782         ret = btrfs_list_subvols_print(fd, filter_set, NULL,
783                 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
784
785         if (filter_set)
786                 btrfs_list_free_filter_set(filter_set);
787 out:
788         close_file_or_dir(fd, dirstream);
789         return !!ret;
790 }
791
792 static const char * const cmd_subvol_set_default_usage[] = {
793         "btrfs subvolume set-default <subvolid> <path>",
794         "Set the default subvolume of a filesystem",
795         NULL
796 };
797
798 static int cmd_subvol_set_default(int argc, char **argv)
799 {
800         int     ret=0, fd, e;
801         u64     objectid;
802         char    *path;
803         char    *subvolid;
804         DIR     *dirstream = NULL;
805
806         if (check_argc_exact(argc, 3))
807                 usage(cmd_subvol_set_default_usage);
808
809         subvolid = argv[1];
810         path = argv[2];
811
812         objectid = arg_strtou64(subvolid);
813
814         fd = open_file_or_dir(path, &dirstream);
815         if (fd < 0) {
816                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
817                 return 1;
818         }
819
820         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
821         e = errno;
822         close_file_or_dir(fd, dirstream);
823         if (ret < 0) {
824                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
825                         strerror(e));
826                 return 1;
827         }
828         return 0;
829 }
830
831 static const char * const cmd_find_new_usage[] = {
832         "btrfs subvolume find-new <path> <lastgen>",
833         "List the recently modified files in a filesystem",
834         NULL
835 };
836
837 static int cmd_find_new(int argc, char **argv)
838 {
839         int fd;
840         int ret;
841         char *subvol;
842         u64 last_gen;
843         DIR *dirstream = NULL;
844
845         if (check_argc_exact(argc, 3))
846                 usage(cmd_find_new_usage);
847
848         subvol = argv[1];
849         last_gen = arg_strtou64(argv[2]);
850
851         ret = test_issubvolume(subvol);
852         if (ret < 0) {
853                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
854                 return 1;
855         }
856         if (!ret) {
857                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
858                 return 1;
859         }
860
861         fd = open_file_or_dir(subvol, &dirstream);
862         if (fd < 0) {
863                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
864                 return 1;
865         }
866
867         ret = ioctl(fd, BTRFS_IOC_SYNC);
868         if (ret < 0) {
869                 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
870                         subvol, strerror(errno));
871                 close_file_or_dir(fd, dirstream);
872                 return 1;
873         }
874
875         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
876         close_file_or_dir(fd, dirstream);
877         return !!ret;
878 }
879
880 static const char * const cmd_subvol_show_usage[] = {
881         "btrfs subvolume show <subvol-path>",
882         "Show more information of the subvolume",
883         NULL
884 };
885
886 static int cmd_subvol_show(int argc, char **argv)
887 {
888         struct root_info get_ri;
889         struct btrfs_list_filter_set *filter_set;
890         char tstr[256];
891         char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
892         char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
893         char raw_prefix[] = "\t\t\t\t";
894         u64 sv_id, mntid;
895         int fd = -1, mntfd = -1;
896         int ret = 1;
897         DIR *dirstream1 = NULL, *dirstream2 = NULL;
898
899         if (check_argc_exact(argc, 2))
900                 usage(cmd_subvol_show_usage);
901
902         fullpath = realpath(argv[1], NULL);
903         if (!fullpath) {
904                 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
905                         argv[1], strerror(errno));
906                 goto out;
907         }
908
909         ret = test_issubvolume(fullpath);
910         if (ret < 0) {
911                 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
912                 goto out;
913         }
914         if (!ret) {
915                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
916                 ret = 1;
917                 goto out;
918         }
919
920         ret = find_mount_root(fullpath, &mnt);
921         if (ret < 0) {
922                 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
923                                 "%s\n", fullpath, strerror(-ret));
924                 goto out;
925         }
926         if (ret > 0) {
927                 fprintf(stderr,
928                         "ERROR: %s doesn't belong to btrfs mount point\n",
929                         fullpath);
930                 goto out;
931         }
932         ret = 1;
933         svpath = get_subvol_name(mnt, fullpath);
934
935         fd = open_file_or_dir(fullpath, &dirstream1);
936         if (fd < 0) {
937                 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
938                 goto out;
939         }
940
941         ret = btrfs_list_get_path_rootid(fd, &sv_id);
942         if (ret) {
943                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
944                         fullpath);
945                 goto out;
946         }
947
948         mntfd = open_file_or_dir(mnt, &dirstream2);
949         if (mntfd < 0) {
950                 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
951                 goto out;
952         }
953
954         ret = btrfs_list_get_path_rootid(mntfd, &mntid);
955         if (ret) {
956                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
957                 goto out;
958         }
959
960         if (sv_id == BTRFS_FS_TREE_OBJECTID) {
961                 printf("%s is btrfs root\n", fullpath);
962                 goto out;
963         }
964
965         memset(&get_ri, 0, sizeof(get_ri));
966         get_ri.root_id = sv_id;
967
968         ret = btrfs_get_subvol(mntfd, &get_ri);
969         if (ret) {
970                 fprintf(stderr, "ERROR: can't find '%s'\n",
971                         svpath);
972                 goto out;
973         }
974
975         /* print the info */
976         printf("%s\n", fullpath);
977         printf("\tName: \t\t\t%s\n", get_ri.name);
978
979         if (uuid_is_null(get_ri.uuid))
980                 strcpy(uuidparse, "-");
981         else
982                 uuid_unparse(get_ri.uuid, uuidparse);
983         printf("\tuuid: \t\t\t%s\n", uuidparse);
984
985         if (uuid_is_null(get_ri.puuid))
986                 strcpy(uuidparse, "-");
987         else
988                 uuid_unparse(get_ri.puuid, uuidparse);
989         printf("\tParent uuid: \t\t%s\n", uuidparse);
990
991         if (get_ri.otime) {
992                 struct tm tm;
993
994                 localtime_r(&get_ri.otime, &tm);
995                 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
996         } else
997                 strcpy(tstr, "-");
998         printf("\tCreation time: \t\t%s\n", tstr);
999
1000         printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
1001         printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
1002         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1003         printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
1004         printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
1005
1006         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1007                 printf("\tFlags: \t\t\treadonly\n");
1008         else
1009                 printf("\tFlags: \t\t\t-\n");
1010
1011         /* print the snapshots of the given subvol if any*/
1012         printf("\tSnapshot(s):\n");
1013         filter_set = btrfs_list_alloc_filter_set();
1014         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1015                                 (u64)(unsigned long)get_ri.uuid);
1016         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1017         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1018                         1, raw_prefix);
1019
1020         /* clean up */
1021         free(get_ri.path);
1022         free(get_ri.name);
1023         free(get_ri.full_path);
1024         btrfs_list_free_filter_set(filter_set);
1025
1026 out:
1027         close_file_or_dir(fd, dirstream1);
1028         close_file_or_dir(mntfd, dirstream2);
1029         free(mnt);
1030         free(fullpath);
1031         return !!ret;
1032 }
1033
1034 static const char * const cmd_subvol_sync_usage[] = {
1035         "btrfs subvolume sync <path> [<subvol-id>...]",
1036         "Wait until given subvolume(s) are completely removed from the filesystem.",
1037         "Wait until given subvolume(s) are completely removed from the filesystem",
1038         "after deletion.",
1039         "If no subvolume id is given, wait until all ongoing deletion requests",
1040         "are complete. This may take long if new deleted subvolumes appear during",
1041         "the sleep interval.",
1042         "",
1043         "-s <N>       sleep N seconds between checks (default: 1)",
1044         NULL
1045 };
1046
1047 static int is_subvolume_cleaned(int fd, u64 subvolid)
1048 {
1049         int ret;
1050         struct btrfs_ioctl_search_args args;
1051         struct btrfs_ioctl_search_key *sk = &args.key;
1052
1053         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1054         sk->min_objectid = subvolid;
1055         sk->max_objectid = subvolid;
1056         sk->min_type = BTRFS_ROOT_ITEM_KEY;
1057         sk->max_type = BTRFS_ROOT_ITEM_KEY;
1058         sk->min_offset = 0;
1059         sk->max_offset = (u64)-1;
1060         sk->min_transid = 0;
1061         sk->max_transid = (u64)-1;
1062         sk->nr_items = 1;
1063
1064         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1065         if (ret < 0)
1066                 return -errno;
1067
1068         if (sk->nr_items == 0)
1069                 return 1;
1070
1071         return 0;
1072 }
1073
1074 /*
1075  * If we're looking for any dead subvolume, take a shortcut and look
1076  * for any ORPHAN_ITEMs in the tree root
1077  */
1078 static int fs_has_dead_subvolumes(int fd)
1079 {
1080         int ret;
1081         struct btrfs_ioctl_search_args args;
1082         struct btrfs_ioctl_search_key *sk = &args.key;
1083         struct btrfs_ioctl_search_header sh;
1084         u64 min_subvolid = 0;
1085
1086 again:
1087         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1088         sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1089         sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1090         sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1091         sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1092         sk->min_offset = min_subvolid;
1093         sk->max_offset = (u64)-1;
1094         sk->min_transid = 0;
1095         sk->max_transid = (u64)-1;
1096         sk->nr_items = 1;
1097
1098         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1099         if (ret < 0)
1100                 return -errno;
1101
1102         if (!sk->nr_items)
1103                 return 0;
1104
1105         memcpy(&sh, args.buf, sizeof(sh));
1106         min_subvolid = sh.offset;
1107
1108         /*
1109          * Verify that the root item is really there and we haven't hit
1110          * a stale orphan
1111          */
1112         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1113         sk->min_objectid = min_subvolid;
1114         sk->max_objectid = min_subvolid;
1115         sk->min_type = BTRFS_ROOT_ITEM_KEY;
1116         sk->max_type = BTRFS_ROOT_ITEM_KEY;
1117         sk->min_offset = 0;
1118         sk->max_offset = (u64)-1;
1119         sk->min_transid = 0;
1120         sk->max_transid = (u64)-1;
1121         sk->nr_items = 1;
1122
1123         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1124         if (ret < 0)
1125                 return -errno;
1126
1127         /*
1128          * Stale orphan, try the next one
1129          */
1130         if (!sk->nr_items) {
1131                 min_subvolid++;
1132                 goto again;
1133         }
1134
1135         return 1;
1136 }
1137
1138 static int cmd_subvol_sync(int argc, char **argv)
1139 {
1140         int fd = -1;
1141         int i;
1142         int ret = 1;
1143         DIR *dirstream = NULL;
1144         u64 *ids = NULL;
1145         int id_count;
1146         int remaining;
1147         int sleep_interval = 1;
1148
1149         optind = 1;
1150         while (1) {
1151                 int c = getopt(argc, argv, "s:");
1152
1153                 if (c < 0)
1154                         break;
1155
1156                 switch (c) {
1157                 case 's':
1158                         sleep_interval = atoi(argv[optind]);
1159                         if (sleep_interval < 1) {
1160                                 fprintf(stderr,
1161                                         "ERROR: invalid sleep interval %s\n",
1162                                         argv[optind]);
1163                                 ret = 1;
1164                                 goto out;
1165                         }
1166                         break;
1167                 default:
1168                         usage(cmd_subvol_sync_usage);
1169                 }
1170         }
1171
1172         if (check_argc_min(argc - optind, 1))
1173                 usage(cmd_subvol_sync_usage);
1174
1175         fd = open_file_or_dir(argv[optind], &dirstream);
1176         if (fd < 0) {
1177                 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1178                 ret = 1;
1179                 goto out;
1180         }
1181         optind++;
1182
1183         id_count = argc - optind;
1184
1185         /*
1186          * Wait for all
1187          */
1188         if (!id_count) {
1189                 while (1) {
1190                         ret = fs_has_dead_subvolumes(fd);
1191                         if (ret < 0) {
1192                                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1193                                                 strerror(-ret));
1194                                 ret = 1;
1195                                 goto out;
1196                         }
1197                         if (!ret)
1198                                 goto out;
1199                         sleep(sleep_interval);
1200                 }
1201         }
1202
1203         /*
1204          * Wait only for the requested ones
1205          */
1206         ids = (u64*)malloc(sizeof(u64) * id_count);
1207
1208         if (!ids) {
1209                 fprintf(stderr, "ERROR: not enough memory\n");
1210                 ret = 1;
1211                 goto out;
1212         }
1213
1214         for (i = 0; i < id_count; i++) {
1215                 u64 id;
1216                 const char *arg;
1217
1218                 arg = argv[optind + i];
1219                 errno = 0;
1220                 id = strtoull(arg, NULL, 10);
1221                 if (errno < 0) {
1222                         fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1223                                 arg);
1224                         ret = 1;
1225                         goto out;
1226                 }
1227                 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1228                         fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1229                                 arg);
1230                         ret = 1;
1231                         goto out;
1232                 }
1233                 ids[i] = id;
1234         }
1235
1236         remaining = id_count;
1237         while (1) {
1238                 for (i = 0; i < id_count; i++) {
1239                         if (!ids[i])
1240                                 continue;
1241                         ret = is_subvolume_cleaned(fd, ids[i]);
1242                         if (ret < 0) {
1243                                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1244                                                 strerror(-ret));
1245                                 goto out;
1246                         }
1247                         if (ret) {
1248                                 printf("Subvolume id %llu is gone\n", ids[i]);
1249                                 ids[i] = 0;
1250                                 remaining--;
1251                         }
1252                 }
1253                 if (!remaining)
1254                         break;
1255                 sleep(sleep_interval);
1256         }
1257
1258 out:
1259         free(ids);
1260         close_file_or_dir(fd, dirstream);
1261
1262         return !!ret;
1263 }
1264
1265 const struct cmd_group subvolume_cmd_group = {
1266         subvolume_cmd_group_usage, NULL, {
1267                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1268                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1269                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1270                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1271                 { "get-default", cmd_subvol_get_default,
1272                         cmd_subvol_get_default_usage, NULL, 0 },
1273                 { "set-default", cmd_subvol_set_default,
1274                         cmd_subvol_set_default_usage, NULL, 0 },
1275                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1276                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1277                 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1278                 NULL_CMD_STRUCT
1279         }
1280 };
1281
1282 int cmd_subvolume(int argc, char **argv)
1283 {
1284         return handle_command_group(&subvolume_cmd_group, argc, argv);
1285 }