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