btrfs-progs: subvol list: cleanup layout argument setup
[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 == 2) {
925                 /*
926                  * Since the top level btrfs was given don't
927                  * take that as error
928                  */
929                 printf("%s is toplevel subvolume\n", fullpath);
930                 ret = 0;
931                 goto out;
932         }
933         if (ret) {
934                 if (ret < 0) {
935                         error("Failed to get subvol info %s: %s\n",
936                                         fullpath, strerror(-ret));
937                 } else {
938                         error("Failed to get subvol info %s: %d\n",
939                                         fullpath, ret);
940                 }
941                 return ret;
942         }
943
944         /* print the info */
945         printf("%s\n", fullpath);
946         printf("\tName: \t\t\t%s\n", get_ri.name);
947
948         if (uuid_is_null(get_ri.uuid))
949                 strcpy(uuidparse, "-");
950         else
951                 uuid_unparse(get_ri.uuid, uuidparse);
952         printf("\tUUID: \t\t\t%s\n", uuidparse);
953
954         if (uuid_is_null(get_ri.puuid))
955                 strcpy(uuidparse, "-");
956         else
957                 uuid_unparse(get_ri.puuid, uuidparse);
958         printf("\tParent UUID: \t\t%s\n", uuidparse);
959
960         if (uuid_is_null(get_ri.ruuid))
961                 strcpy(uuidparse, "-");
962         else
963                 uuid_unparse(get_ri.ruuid, uuidparse);
964         printf("\tReceived UUID: \t\t%s\n", uuidparse);
965
966         if (get_ri.otime) {
967                 struct tm tm;
968
969                 localtime_r(&get_ri.otime, &tm);
970                 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
971         } else
972                 strcpy(tstr, "-");
973         printf("\tCreation time: \t\t%s\n", tstr);
974
975         printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
976         printf("\tGeneration: \t\t%llu\n", get_ri.gen);
977         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
978         printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
979         printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
980
981         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
982                 printf("\tFlags: \t\t\treadonly\n");
983         else
984                 printf("\tFlags: \t\t\t-\n");
985
986         /* print the snapshots of the given subvol if any*/
987         printf("\tSnapshot(s):\n");
988         filter_set = btrfs_list_alloc_filter_set();
989         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
990                                 (u64)(unsigned long)get_ri.uuid);
991         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
992
993         fd = open_file_or_dir(fullpath, &dirstream1);
994         if (fd < 0) {
995                 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
996                 goto out;
997         }
998         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
999                         1, raw_prefix);
1000
1001 out:
1002         /* clean up */
1003         free(get_ri.path);
1004         free(get_ri.name);
1005         free(get_ri.full_path);
1006         free(filter_set);
1007
1008         close_file_or_dir(fd, dirstream1);
1009         free(fullpath);
1010         return !!ret;
1011 }
1012
1013 static const char * const cmd_subvol_sync_usage[] = {
1014         "btrfs subvolume sync <path> [<subvol-id>...]",
1015         "Wait until given subvolume(s) are completely removed from the filesystem.",
1016         "Wait until given subvolume(s) are completely removed from the filesystem",
1017         "after deletion.",
1018         "If no subvolume id is given, wait until all current deletion requests",
1019         "are completed, but do not wait for subvolumes deleted meanwhile.",
1020         "The status of subvolume ids is checked periodically.",
1021         "",
1022         "-s <N>       sleep N seconds between checks (default: 1)",
1023         NULL
1024 };
1025
1026 #if 0
1027 /*
1028  * If we're looking for any dead subvolume, take a shortcut and look
1029  * for any ORPHAN_ITEMs in the tree root
1030  */
1031 static int fs_has_dead_subvolumes(int fd)
1032 {
1033         int ret;
1034         struct btrfs_ioctl_search_args args;
1035         struct btrfs_ioctl_search_key *sk = &args.key;
1036         struct btrfs_ioctl_search_header sh;
1037         u64 min_subvolid = 0;
1038
1039 again:
1040         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1041         sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1042         sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1043         sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1044         sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1045         sk->min_offset = min_subvolid;
1046         sk->max_offset = (u64)-1;
1047         sk->min_transid = 0;
1048         sk->max_transid = (u64)-1;
1049         sk->nr_items = 1;
1050
1051         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1052         if (ret < 0)
1053                 return -errno;
1054
1055         if (!sk->nr_items)
1056                 return 0;
1057
1058         memcpy(&sh, args.buf, sizeof(sh));
1059         min_subvolid = sh.offset;
1060
1061         /*
1062          * Verify that the root item is really there and we haven't hit
1063          * a stale orphan
1064          */
1065         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1066         sk->min_objectid = min_subvolid;
1067         sk->max_objectid = min_subvolid;
1068         sk->min_type = BTRFS_ROOT_ITEM_KEY;
1069         sk->max_type = BTRFS_ROOT_ITEM_KEY;
1070         sk->min_offset = 0;
1071         sk->max_offset = (u64)-1;
1072         sk->min_transid = 0;
1073         sk->max_transid = (u64)-1;
1074         sk->nr_items = 1;
1075
1076         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1077         if (ret < 0)
1078                 return -errno;
1079
1080         /*
1081          * Stale orphan, try the next one
1082          */
1083         if (!sk->nr_items) {
1084                 min_subvolid++;
1085                 goto again;
1086         }
1087
1088         return 1;
1089 }
1090 #endif
1091
1092 #define SUBVOL_ID_BATCH         1024
1093
1094 /*
1095  * Enumerate all dead subvolumes that exist in the filesystem.
1096  * Fill @ids and reallocate to bigger size if needed.
1097  */
1098 static int enumerate_dead_subvols(int fd, u64 **ids)
1099 {
1100         int ret;
1101         struct btrfs_ioctl_search_args args;
1102         struct btrfs_ioctl_search_key *sk = &args.key;
1103         int idx = 0;
1104         int count = 0;
1105
1106         memset(&args, 0, sizeof(args));
1107
1108         sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1109         sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1110         sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1111         sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1112         sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1113         sk->min_offset = 0;
1114         sk->max_offset = (u64)-1;
1115         sk->min_transid = 0;
1116         sk->max_transid = (u64)-1;
1117         sk->nr_items = 4096;
1118
1119         *ids = NULL;
1120         while (1) {
1121                 struct btrfs_ioctl_search_header *sh;
1122                 unsigned long off;
1123                 int i;
1124
1125                 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1126                 if (ret < 0)
1127                         return -errno;
1128
1129                 if (!sk->nr_items)
1130                         return idx;
1131
1132                 off = 0;
1133                 for (i = 0; i < sk->nr_items; i++) {
1134                         sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1135                         off += sizeof(*sh);
1136
1137                         if (btrfs_search_header_type(sh)
1138                             == BTRFS_ORPHAN_ITEM_KEY) {
1139                                 if (idx >= count) {
1140                                         u64 *newids;
1141
1142                                         count += SUBVOL_ID_BATCH;
1143                                         newids = (u64*)realloc(*ids,
1144                                                         count * sizeof(u64));
1145                                         if (!newids)
1146                                                 return -ENOMEM;
1147                                         *ids = newids;
1148                                 }
1149                                 (*ids)[idx] = btrfs_search_header_offset(sh);
1150                                 idx++;
1151                         }
1152                         off += btrfs_search_header_len(sh);
1153
1154                         sk->min_objectid = btrfs_search_header_objectid(sh);
1155                         sk->min_type = btrfs_search_header_type(sh);
1156                         sk->min_offset = btrfs_search_header_offset(sh);
1157                 }
1158                 if (sk->min_offset < (u64)-1)
1159                         sk->min_offset++;
1160                 else
1161                         break;
1162                 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1163                         break;
1164                 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1165                         break;
1166         }
1167
1168         return idx;
1169 }
1170
1171 static int cmd_subvol_sync(int argc, char **argv)
1172 {
1173         int fd = -1;
1174         int i;
1175         int ret = 1;
1176         DIR *dirstream = NULL;
1177         u64 *ids = NULL;
1178         int id_count;
1179         int sleep_interval = 1;
1180
1181         while (1) {
1182                 int c = getopt(argc, argv, "s:");
1183
1184                 if (c < 0)
1185                         break;
1186
1187                 switch (c) {
1188                 case 's':
1189                         sleep_interval = atoi(optarg);
1190                         if (sleep_interval < 1) {
1191                                 error("invalid sleep interval %s", optarg);
1192                                 ret = 1;
1193                                 goto out;
1194                         }
1195                         break;
1196                 default:
1197                         usage(cmd_subvol_sync_usage);
1198                 }
1199         }
1200
1201         if (check_argc_min(argc - optind, 1))
1202                 usage(cmd_subvol_sync_usage);
1203
1204         fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1205         if (fd < 0) {
1206                 ret = 1;
1207                 goto out;
1208         }
1209         optind++;
1210
1211         id_count = argc - optind;
1212         if (!id_count) {
1213                 id_count = enumerate_dead_subvols(fd, &ids);
1214                 if (id_count < 0) {
1215                         error("can't enumerate dead subvolumes: %s",
1216                                         strerror(-id_count));
1217                         ret = 1;
1218                         goto out;
1219                 }
1220                 if (id_count == 0) {
1221                         ret = 0;
1222                         goto out;
1223                 }
1224         } else {
1225                 ids = (u64*)malloc(id_count * sizeof(u64));
1226                 if (!ids) {
1227                         error("not enough memory");
1228                         ret = 1;
1229                         goto out;
1230                 }
1231
1232                 for (i = 0; i < id_count; i++) {
1233                         u64 id;
1234                         const char *arg;
1235
1236                         arg = argv[optind + i];
1237                         errno = 0;
1238                         id = strtoull(arg, NULL, 10);
1239                         if (errno < 0) {
1240                                 error("unrecognized subvolume id %s", arg);
1241                                 ret = 1;
1242                                 goto out;
1243                         }
1244                         if (id < BTRFS_FIRST_FREE_OBJECTID
1245                                         || id > BTRFS_LAST_FREE_OBJECTID) {
1246                                 error("subvolume id %s out of range\n", arg);
1247                                 ret = 1;
1248                                 goto out;
1249                         }
1250                         ids[i] = id;
1251                 }
1252         }
1253
1254         ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1255
1256 out:
1257         free(ids);
1258         close_file_or_dir(fd, dirstream);
1259
1260         return !!ret;
1261 }
1262
1263 static const char subvolume_cmd_group_info[] =
1264 "manage subvolumes: create, delete, list, etc";
1265
1266 const struct cmd_group subvolume_cmd_group = {
1267         subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1268                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1269                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1270                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1271                 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1272                         NULL, 0 },
1273                 { "get-default", cmd_subvol_get_default,
1274                         cmd_subvol_get_default_usage, NULL, 0 },
1275                 { "set-default", cmd_subvol_set_default,
1276                         cmd_subvol_set_default_usage, NULL, 0 },
1277                 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1278                         NULL, 0 },
1279                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1280                 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1281                 NULL_CMD_STRUCT
1282         }
1283 };
1284
1285 int cmd_subvolume(int argc, char **argv)
1286 {
1287         return handle_command_group(&subvolume_cmd_group, argc, argv);
1288 }