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