btrfs-progs: mkfs: return nozero value on thin provisioned device
[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 succeeded 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         enum btrfs_util_error err;
634         struct btrfs_ioctl_vol_args_v2  args;
635         struct btrfs_qgroup_inherit *inherit = NULL;
636         DIR *dirstream1 = NULL, *dirstream2 = NULL;
637
638         memset(&args, 0, sizeof(args));
639         while (1) {
640                 int c = getopt(argc, argv, "c:i:r");
641                 if (c < 0)
642                         break;
643
644                 switch (c) {
645                 case 'c':
646                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
647                         if (res) {
648                                 retval = res;
649                                 goto out;
650                         }
651                         break;
652                 case 'i':
653                         res = qgroup_inherit_add_group(&inherit, optarg);
654                         if (res) {
655                                 retval = res;
656                                 goto out;
657                         }
658                         break;
659                 case 'r':
660                         readonly = 1;
661                         break;
662                 case 'x':
663                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
664                         if (res) {
665                                 retval = res;
666                                 goto out;
667                         }
668                         break;
669                 default:
670                         usage(cmd_subvol_snapshot_usage);
671                 }
672         }
673
674         if (check_argc_exact(argc - optind, 2))
675                 usage(cmd_subvol_snapshot_usage);
676
677         subvol = argv[optind];
678         dst = argv[optind + 1];
679
680         retval = 1;     /* failure */
681         err = btrfs_util_is_subvolume(subvol);
682         if (err) {
683                 error_btrfs_util(err);
684                 goto out;
685         }
686
687         res = test_isdir(dst);
688         if (res < 0 && res != -ENOENT) {
689                 error("cannot access %s: %s", dst, strerror(-res));
690                 goto out;
691         }
692         if (res == 0) {
693                 error("'%s' exists and it is not a directory", dst);
694                 goto out;
695         }
696
697         if (res > 0) {
698                 dupname = strdup(subvol);
699                 newname = basename(dupname);
700                 dstdir = dst;
701         } else {
702                 dupname = strdup(dst);
703                 newname = basename(dupname);
704                 dupdir = strdup(dst);
705                 dstdir = dirname(dupdir);
706         }
707
708         if (!test_issubvolname(newname)) {
709                 error("invalid snapshot name '%s'", newname);
710                 goto out;
711         }
712
713         len = strlen(newname);
714         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
715                 error("snapshot name too long '%s'", newname);
716                 goto out;
717         }
718
719         fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
720         if (fddst < 0)
721                 goto out;
722
723         fd = btrfs_open_dir(subvol, &dirstream2, 1);
724         if (fd < 0)
725                 goto out;
726
727         if (readonly) {
728                 args.flags |= BTRFS_SUBVOL_RDONLY;
729                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
730                        subvol, dstdir, newname);
731         } else {
732                 printf("Create a snapshot of '%s' in '%s/%s'\n",
733                        subvol, dstdir, newname);
734         }
735
736         args.fd = fd;
737         if (inherit) {
738                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
739                 args.size = qgroup_inherit_size(inherit);
740                 args.qgroup_inherit = inherit;
741         }
742         strncpy_null(args.name, newname);
743
744         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
745
746         if (res < 0) {
747                 error("cannot snapshot '%s': %m", subvol);
748                 goto out;
749         }
750
751         retval = 0;     /* success */
752
753 out:
754         close_file_or_dir(fddst, dirstream1);
755         close_file_or_dir(fd, dirstream2);
756         free(inherit);
757         free(dupname);
758         free(dupdir);
759
760         return retval;
761 }
762
763 static const char * const cmd_subvol_get_default_usage[] = {
764         "btrfs subvolume get-default <path>",
765         "Get the default subvolume of a filesystem",
766         NULL
767 };
768
769 static int cmd_subvol_get_default(int argc, char **argv)
770 {
771         int fd = -1;
772         int ret = 1;
773         uint64_t default_id;
774         DIR *dirstream = NULL;
775         enum btrfs_util_error err;
776         struct btrfs_util_subvolume_info subvol;
777         char *path;
778
779         clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
780
781         if (check_argc_exact(argc - optind, 1))
782                 usage(cmd_subvol_get_default_usage);
783
784         fd = btrfs_open_dir(argv[1], &dirstream, 1);
785         if (fd < 0)
786                 return 1;
787
788         err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
789         if (err) {
790                 error_btrfs_util(err);
791                 goto out;
792         }
793
794         /* no need to resolve roots if FS_TREE is default */
795         if (default_id == BTRFS_FS_TREE_OBJECTID) {
796                 printf("ID 5 (FS_TREE)\n");
797                 ret = 0;
798                 goto out;
799         }
800
801         err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol);
802         if (err) {
803                 error_btrfs_util(err);
804                 goto out;
805         }
806
807         err = btrfs_util_subvolume_path_fd(fd, default_id, &path);
808         if (err) {
809                 error_btrfs_util(err);
810                 goto out;
811         }
812
813         printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
814                subvol.id, subvol.generation, subvol.parent_id, path);
815
816         free(path);
817
818         ret = 0;
819 out:
820         close_file_or_dir(fd, dirstream);
821         return ret;
822 }
823
824 static const char * const cmd_subvol_set_default_usage[] = {
825         "btrfs subvolume set-default <subvolume>\n"
826         "btrfs subvolume set-default <subvolid> <path>",
827         "Set the default subvolume of the filesystem mounted as default.",
828         "The subvolume can be specified by its path,",
829         "or the pair of subvolume id and path to the filesystem.",
830         NULL
831 };
832
833 static int cmd_subvol_set_default(int argc, char **argv)
834 {
835         u64 objectid;
836         char *path;
837         enum btrfs_util_error err;
838
839         clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
840
841         if (check_argc_min(argc - optind, 1) ||
842                         check_argc_max(argc - optind, 2))
843                 usage(cmd_subvol_set_default_usage);
844
845         if (argc - optind == 1) {
846                 /* path to the subvolume is specified */
847                 objectid = 0;
848                 path = argv[optind];
849         } else {
850                 /* subvol id and path to the filesystem are specified */
851                 objectid = arg_strtou64(argv[optind]);
852                 path = argv[optind + 1];
853         }
854
855         err = btrfs_util_set_default_subvolume(path, objectid);
856         if (err) {
857                 error_btrfs_util(err);
858                 return 1;
859         }
860         return 0;
861 }
862
863 static const char * const cmd_subvol_find_new_usage[] = {
864         "btrfs subvolume find-new <path> <lastgen>",
865         "List the recently modified files in a filesystem",
866         NULL
867 };
868
869 static int cmd_subvol_find_new(int argc, char **argv)
870 {
871         int fd;
872         int ret;
873         char *subvol;
874         u64 last_gen;
875         DIR *dirstream = NULL;
876         enum btrfs_util_error err;
877
878         clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
879
880         if (check_argc_exact(argc - optind, 2))
881                 usage(cmd_subvol_find_new_usage);
882
883         subvol = argv[optind];
884         last_gen = arg_strtou64(argv[optind + 1]);
885
886         err = btrfs_util_is_subvolume(subvol);
887         if (err) {
888                 error_btrfs_util(err);
889                 return 1;
890         }
891
892         fd = btrfs_open_dir(subvol, &dirstream, 1);
893         if (fd < 0)
894                 return 1;
895
896         err = btrfs_util_sync_fd(fd);
897         if (err) {
898                 error_btrfs_util(err);
899                 close_file_or_dir(fd, dirstream);
900                 return 1;
901         }
902
903         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
904         close_file_or_dir(fd, dirstream);
905         return !!ret;
906 }
907
908 static const char * const cmd_subvol_show_usage[] = {
909         "btrfs subvolume show [options] <subvol-path>|<mnt>",
910         "Show more information about the subvolume",
911         "-r|--rootid   rootid of the subvolume",
912         "-u|--uuid     uuid of the subvolume",
913         "",
914         "If no option is specified, <subvol-path> will be shown, otherwise",
915         "the rootid or uuid are resolved relative to the <mnt> path.",
916         NULL
917 };
918
919 static int cmd_subvol_show(int argc, char **argv)
920 {
921         char tstr[256];
922         char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
923         char *fullpath = NULL;
924         int fd = -1;
925         int ret = 1;
926         DIR *dirstream1 = NULL;
927         int by_rootid = 0;
928         int by_uuid = 0;
929         u64 rootid_arg = 0;
930         u8 uuid_arg[BTRFS_UUID_SIZE];
931         struct btrfs_util_subvolume_iterator *iter;
932         struct btrfs_util_subvolume_info subvol;
933         char *subvol_path = NULL;
934         enum btrfs_util_error err;
935
936         while (1) {
937                 int c;
938                 static const struct option long_options[] = {
939                         { "rootid", required_argument, NULL, 'r'},
940                         { "uuid", required_argument, NULL, 'u'},
941                         { NULL, 0, NULL, 0 }
942                 };
943
944                 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
945                 if (c < 0)
946                         break;
947
948                 switch (c) {
949                 case 'r':
950                         rootid_arg = arg_strtou64(optarg);
951                         by_rootid = 1;
952                         break;
953                 case 'u':
954                         uuid_parse(optarg, uuid_arg);
955                         by_uuid = 1;
956                         break;
957                 default:
958                         usage(cmd_subvol_show_usage);
959                 }
960         }
961
962         if (check_argc_exact(argc - optind, 1))
963                 usage(cmd_subvol_show_usage);
964
965         if (by_rootid && by_uuid) {
966                 error(
967                 "options --rootid and --uuid cannot be used at the same time");
968                 usage(cmd_subvol_show_usage);
969         }
970
971         fullpath = realpath(argv[optind], NULL);
972         if (!fullpath) {
973                 error("cannot find real path for '%s': %m", argv[optind]);
974                 goto out;
975         }
976
977         fd = open_file_or_dir(fullpath, &dirstream1);
978         if (fd < 0) {
979                 error("can't access '%s'", fullpath);
980                 goto out;
981         }
982
983         if (by_uuid) {
984                 err = btrfs_util_create_subvolume_iterator_fd(fd,
985                                                               BTRFS_FS_TREE_OBJECTID,
986                                                               0, &iter);
987                 if (err) {
988                         error_btrfs_util(err);
989                         goto out;
990                 }
991
992                 for (;;) {
993                         err = btrfs_util_subvolume_iterator_next_info(iter,
994                                                                       &subvol_path,
995                                                                       &subvol);
996                         if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
997                                 uuid_unparse(uuid_arg, uuidparse);
998                                 error("can't find uuid '%s' on '%s'", uuidparse,
999                                       fullpath);
1000                                 btrfs_util_destroy_subvolume_iterator(iter);
1001                                 goto out;
1002                         } else if (err) {
1003                                 error_btrfs_util(err);
1004                                 btrfs_util_destroy_subvolume_iterator(iter);
1005                                 goto out;
1006                         }
1007
1008                         if (uuid_compare(subvol.uuid, uuid_arg) == 0)
1009                                 break;
1010
1011                         free(subvol_path);
1012                 }
1013                 btrfs_util_destroy_subvolume_iterator(iter);
1014         } else {
1015                 /*
1016                  * If !by_rootid, rootid_arg = 0, which means find the
1017                  * subvolume ID of the fd and use that.
1018                  */
1019                 err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
1020                 if (err) {
1021                         error_btrfs_util(err);
1022                         goto out;
1023                 }
1024
1025                 err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
1026                 if (err) {
1027                         error_btrfs_util(err);
1028                         goto out;
1029                 }
1030
1031         }
1032
1033         /* print the info */
1034         printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
1035         printf("\tName: \t\t\t%s\n",
1036                (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
1037                 basename(subvol_path)));
1038
1039         if (uuid_is_null(subvol.uuid))
1040                 strcpy(uuidparse, "-");
1041         else
1042                 uuid_unparse(subvol.uuid, uuidparse);
1043         printf("\tUUID: \t\t\t%s\n", uuidparse);
1044
1045         if (uuid_is_null(subvol.parent_uuid))
1046                 strcpy(uuidparse, "-");
1047         else
1048                 uuid_unparse(subvol.parent_uuid, uuidparse);
1049         printf("\tParent UUID: \t\t%s\n", uuidparse);
1050
1051         if (uuid_is_null(subvol.received_uuid))
1052                 strcpy(uuidparse, "-");
1053         else
1054                 uuid_unparse(subvol.received_uuid, uuidparse);
1055         printf("\tReceived UUID: \t\t%s\n", uuidparse);
1056
1057         if (subvol.otime.tv_sec) {
1058                 struct tm tm;
1059
1060                 localtime_r(&subvol.otime.tv_sec, &tm);
1061                 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1062         } else
1063                 strcpy(tstr, "-");
1064         printf("\tCreation time: \t\t%s\n", tstr);
1065
1066         printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
1067         printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
1068         printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
1069         printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1070         printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1071
1072         if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1073                 printf("\tFlags: \t\t\treadonly\n");
1074         else
1075                 printf("\tFlags: \t\t\t-\n");
1076
1077         /* print the snapshots of the given subvol if any*/
1078         printf("\tSnapshot(s):\n");
1079
1080         err = btrfs_util_create_subvolume_iterator_fd(fd,
1081                                                       BTRFS_FS_TREE_OBJECTID, 0,
1082                                                       &iter);
1083
1084         for (;;) {
1085                 struct btrfs_util_subvolume_info subvol2;
1086                 char *path;
1087
1088                 err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
1089                 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1090                         break;
1091                 } else if (err) {
1092                         error_btrfs_util(err);
1093                         btrfs_util_destroy_subvolume_iterator(iter);
1094                         goto out;
1095                 }
1096
1097                 if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
1098                         printf("\t\t\t\t%s\n", path);
1099
1100                 free(path);
1101         }
1102         btrfs_util_destroy_subvolume_iterator(iter);
1103
1104         ret = 0;
1105 out:
1106         free(subvol_path);
1107         close_file_or_dir(fd, dirstream1);
1108         free(fullpath);
1109         return !!ret;
1110 }
1111
1112 static const char * const cmd_subvol_sync_usage[] = {
1113         "btrfs subvolume sync <path> [<subvol-id>...]",
1114         "Wait until given subvolume(s) are completely removed from the filesystem.",
1115         "Wait until given subvolume(s) are completely removed from the filesystem",
1116         "after deletion.",
1117         "If no subvolume id is given, wait until all current deletion requests",
1118         "are completed, but do not wait for subvolumes deleted meanwhile.",
1119         "The status of subvolume ids is checked periodically.",
1120         "",
1121         "-s <N>       sleep N seconds between checks (default: 1)",
1122         NULL
1123 };
1124
1125 static int cmd_subvol_sync(int argc, char **argv)
1126 {
1127         int fd = -1;
1128         int ret = 1;
1129         DIR *dirstream = NULL;
1130         uint64_t *ids = NULL;
1131         size_t id_count, i;
1132         int sleep_interval = 1;
1133         enum btrfs_util_error err;
1134
1135         while (1) {
1136                 int c = getopt(argc, argv, "s:");
1137
1138                 if (c < 0)
1139                         break;
1140
1141                 switch (c) {
1142                 case 's':
1143                         sleep_interval = atoi(optarg);
1144                         if (sleep_interval < 1) {
1145                                 error("invalid sleep interval %s", optarg);
1146                                 ret = 1;
1147                                 goto out;
1148                         }
1149                         break;
1150                 default:
1151                         usage(cmd_subvol_sync_usage);
1152                 }
1153         }
1154
1155         if (check_argc_min(argc - optind, 1))
1156                 usage(cmd_subvol_sync_usage);
1157
1158         fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1159         if (fd < 0) {
1160                 ret = 1;
1161                 goto out;
1162         }
1163         optind++;
1164
1165         id_count = argc - optind;
1166         if (!id_count) {
1167                 err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count);
1168                 if (err) {
1169                         error_btrfs_util(err);
1170                         ret = 1;
1171                         goto out;
1172                 }
1173                 if (id_count == 0) {
1174                         ret = 0;
1175                         goto out;
1176                 }
1177         } else {
1178                 ids = malloc(id_count * sizeof(uint64_t));
1179                 if (!ids) {
1180                         error("not enough memory");
1181                         ret = 1;
1182                         goto out;
1183                 }
1184
1185                 for (i = 0; i < id_count; i++) {
1186                         u64 id;
1187                         const char *arg;
1188
1189                         arg = argv[optind + i];
1190                         errno = 0;
1191                         id = strtoull(arg, NULL, 10);
1192                         if (errno) {
1193                                 error("unrecognized subvolume id %s", arg);
1194                                 ret = 1;
1195                                 goto out;
1196                         }
1197                         if (id < BTRFS_FIRST_FREE_OBJECTID ||
1198                             id > BTRFS_LAST_FREE_OBJECTID) {
1199                                 error("subvolume id %s out of range", arg);
1200                                 ret = 1;
1201                                 goto out;
1202                         }
1203                         ids[i] = id;
1204                 }
1205         }
1206
1207         ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1208
1209 out:
1210         free(ids);
1211         close_file_or_dir(fd, dirstream);
1212
1213         return !!ret;
1214 }
1215
1216 static const char subvolume_cmd_group_info[] =
1217 "manage subvolumes: create, delete, list, etc";
1218
1219 const struct cmd_group subvolume_cmd_group = {
1220         subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1221                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1222                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1223                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1224                 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1225                         NULL, 0 },
1226                 { "get-default", cmd_subvol_get_default,
1227                         cmd_subvol_get_default_usage, NULL, 0 },
1228                 { "set-default", cmd_subvol_set_default,
1229                         cmd_subvol_set_default_usage, NULL, 0 },
1230                 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1231                         NULL, 0 },
1232                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1233                 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1234                 NULL_CMD_STRUCT
1235         }
1236 };
1237
1238 int cmd_subvolume(int argc, char **argv)
1239 {
1240         return handle_command_group(&subvolume_cmd_group, argc, argv);
1241 }