Btrfs-progs: make send/receive compatible with older kernels
[platform/upstream/btrfs-progs.git] / cmds-subvolume.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <libgen.h>
25 #include <limits.h>
26 #include <getopt.h>
27 #include <uuid/uuid.h>
28
29 #include "kerncompat.h"
30 #include "ioctl.h"
31 #include "qgroup.h"
32
33 #include "ctree.h"
34 #include "commands.h"
35 #include "utils.h"
36 #include "btrfs-list.h"
37 #include "utils.h"
38
39 static const char * const subvolume_cmd_group_usage[] = {
40         "btrfs subvolume <command> <args>",
41         NULL
42 };
43
44 /*
45  * test if path is a directory
46  * this function return
47  * 0-> path exists but it is not a directory
48  * 1-> path exists and it is  a directory
49  * -1 -> path is unaccessible
50  */
51 static int test_isdir(char *path)
52 {
53         struct stat     st;
54         int             res;
55
56         res = stat(path, &st);
57         if(res < 0 )
58                 return -1;
59
60         return S_ISDIR(st.st_mode);
61 }
62
63 static const char * const cmd_subvol_create_usage[] = {
64         "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
65         "Create a subvolume",
66         "Create a subvolume <name> in <dest>.  If <dest> is not given",
67         "subvolume <name> will be created in the current directory.",
68         "",
69         "-i <qgroupid>  add the newly created subvolume to a qgroup. This",
70         "               option can be given multiple times.",
71         NULL
72 };
73
74 static int cmd_subvol_create(int argc, char **argv)
75 {
76         int     retval, res, len;
77         int     fddst = -1;
78         char    *dupname = NULL;
79         char    *dupdir = NULL;
80         char    *newname;
81         char    *dstdir;
82         char    *dst;
83         struct btrfs_qgroup_inherit *inherit = NULL;
84         DIR     *dirstream = NULL;
85
86         optind = 1;
87         while (1) {
88                 int c = getopt(argc, argv, "c:i:");
89                 if (c < 0)
90                         break;
91
92                 switch (c) {
93                 case 'c':
94                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
95                         if (res) {
96                                 retval = res;
97                                 goto out;
98                         }
99                         break;
100                 case 'i':
101                         res = qgroup_inherit_add_group(&inherit, optarg);
102                         if (res) {
103                                 retval = res;
104                                 goto out;
105                         }
106                         break;
107                 default:
108                         usage(cmd_subvol_create_usage);
109                 }
110         }
111
112         if (check_argc_exact(argc - optind, 1))
113                 usage(cmd_subvol_create_usage);
114
115         dst = argv[optind];
116
117         retval = 1;     /* failure */
118         res = test_isdir(dst);
119         if (res >= 0) {
120                 fprintf(stderr, "ERROR: '%s' exists\n", dst);
121                 goto out;
122         }
123
124         dupname = strdup(dst);
125         newname = basename(dupname);
126         dupdir = strdup(dst);
127         dstdir = dirname(dupdir);
128
129         if (!strcmp(newname, ".") || !strcmp(newname, "..") ||
130              strchr(newname, '/') ){
131                 fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
132                         newname);
133                 goto out;
134         }
135
136         len = strlen(newname);
137         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
138                 fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
139                         newname);
140                 goto out;
141         }
142
143         fddst = open_file_or_dir(dstdir, &dirstream);
144         if (fddst < 0) {
145                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
146                 goto out;
147         }
148
149         printf("Create subvolume '%s/%s'\n", dstdir, newname);
150         if (inherit) {
151                 struct btrfs_ioctl_vol_args_v2  args;
152
153                 memset(&args, 0, sizeof(args));
154                 strncpy_null(args.name, newname);
155                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
156                 args.size = qgroup_inherit_size(inherit);
157                 args.qgroup_inherit = inherit;
158
159                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
160         } else {
161                 struct btrfs_ioctl_vol_args     args;
162
163                 memset(&args, 0, sizeof(args));
164                 strncpy_null(args.name, newname);
165
166                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
167         }
168
169         if (res < 0) {
170                 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
171                         strerror(errno));
172                 goto out;
173         }
174
175         retval = 0;     /* success */
176 out:
177         close_file_or_dir(fddst, dirstream);
178         free(inherit);
179         free(dupname);
180         free(dupdir);
181
182         return retval;
183 }
184
185 /*
186  * test if path is a subvolume:
187  * this function return
188  * 0-> path exists but it is not a subvolume
189  * 1-> path exists and it is  a subvolume
190  * -1 -> path is unaccessible
191  */
192 int test_issubvolume(char *path)
193 {
194         struct stat     st;
195         int             res;
196
197         res = stat(path, &st);
198         if(res < 0 )
199                 return -1;
200
201         return (st.st_ino == 256) && S_ISDIR(st.st_mode);
202 }
203
204 static int wait_for_commit(int fd)
205 {
206         int ret;
207
208         ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
209         if (ret < 0)
210                 return ret;
211         return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
212 }
213
214 static const char * const cmd_subvol_delete_usage[] = {
215         "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
216         "Delete subvolume(s)",
217         "Delete subvolumes from the filesystem. The corresponding directory",
218         "is removed instantly but the data blocks are removed later.",
219         "The deletion does not involve full commit by default due to",
220         "performance reasons (as a consequence, the subvolume may appear again",
221         "after a crash). Use one of the --commit options to wait until the",
222         "operation is safely stored on the media.",
223         "",
224         "-c|--commit-after      wait for transaction commit at the end of the operation",
225         "-C|--commit-each       wait for transaction commit after deleting each subvolume",
226         NULL
227 };
228
229 static int cmd_subvol_delete(int argc, char **argv)
230 {
231         int     res, len, e, ret = 0;
232         int cnt;
233         int fd = -1;
234         struct btrfs_ioctl_vol_args     args;
235         char    *dname, *vname, *cpath;
236         char    *dupdname = NULL;
237         char    *dupvname = NULL;
238         char    *path;
239         DIR     *dirstream = NULL;
240         int sync_mode = 0;
241         struct option long_options[] = {
242                 {"commit-after", no_argument, NULL, 'c'},  /* sync mode 1 */
243                 {"commit-each", no_argument, NULL, 'C'},  /* sync mode 2 */
244                 {NULL, 0, NULL, 0}
245         };
246
247         optind = 1;
248         while (1) {
249                 int c;
250
251                 c = getopt_long(argc, argv, "cC", long_options, NULL);
252                 if (c < 0)
253                         break;
254
255                 switch(c) {
256                 case 'c':
257                         sync_mode = 1;
258                         break;
259                 case 'C':
260                         sync_mode = 2;
261                         break;
262                 default:
263                         usage(cmd_subvol_delete_usage);
264                 }
265         }
266
267         if (check_argc_min(argc - optind, 1))
268                 usage(cmd_subvol_delete_usage);
269
270         printf("Transaction commit: %s\n",
271                 !sync_mode ? "none (default)" :
272                 sync_mode == 1 ? "at the end" : "after each");
273
274         cnt = optind;
275
276 again:
277         path = argv[cnt];
278
279         res = test_issubvolume(path);
280         if (res < 0) {
281                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
282                 ret = 1;
283                 goto out;
284         }
285         if (!res) {
286                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
287                 ret = 1;
288                 goto out;
289         }
290
291         cpath = realpath(path, NULL);
292         if (!cpath) {
293                 ret = errno;
294                 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
295                         path, strerror(errno));
296                 goto out;
297         }
298         dupdname = strdup(cpath);
299         dname = dirname(dupdname);
300         dupvname = strdup(cpath);
301         vname = basename(dupvname);
302         free(cpath);
303
304         if (!strcmp(vname, ".") || !strcmp(vname, "..") ||
305              strchr(vname, '/')) {
306                 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
307                         vname);
308                 ret = 1;
309                 goto out;
310         }
311
312         len = strlen(vname);
313         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
314                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
315                         vname);
316                 ret = 1;
317                 goto out;
318         }
319
320         fd = open_file_or_dir(dname, &dirstream);
321         if (fd < 0) {
322                 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
323                 ret = 1;
324                 goto out;
325         }
326
327         printf("Delete subvolume '%s/%s'\n", dname, vname);
328         strncpy_null(args.name, vname);
329         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
330         e = errno;
331
332         if(res < 0 ){
333                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
334                         dname, vname, strerror(e));
335                 ret = 1;
336                 goto out;
337         }
338
339         if (sync_mode == 1) {
340                 res = wait_for_commit(fd);
341                 if (res < 0) {
342                         fprintf(stderr,
343                                 "ERROR: unable to wait for commit after '%s': %s\n",
344                                 path, strerror(errno));
345                         ret = 1;
346                 }
347         }
348
349 out:
350         free(dupdname);
351         free(dupvname);
352         dupdname = NULL;
353         dupvname = NULL;
354         cnt++;
355         if (cnt < argc) {
356                 close_file_or_dir(fd, dirstream);
357                 /* avoid double free */
358                 fd = -1;
359                 dirstream = NULL;
360                 goto again;
361         }
362
363         if (sync_mode == 2 && fd != -1) {
364                 res = wait_for_commit(fd);
365                 if (res < 0) {
366                         fprintf(stderr,
367                                 "ERROR: unable to do final sync: %s\n",
368                                 strerror(errno));
369                         ret = 1;
370                 }
371         }
372         close_file_or_dir(fd, dirstream);
373
374         return ret;
375 }
376
377 /*
378  * Naming of options:
379  * - uppercase for filters and sort options
380  * - lowercase for enabling specific items in the output
381  */
382 static const char * const cmd_subvol_list_usage[] = {
383         "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
384         "[--sort=gen,ogen,rootid,path] <path>",
385         "List subvolumes (and snapshots)",
386         "",
387         "-p           print parent ID",
388         "-a           print all the subvolumes in the filesystem and",
389         "             distinguish absolute and relative path with respect",
390         "             to the given <path>",
391         "-c           print the ogeneration of the subvolume",
392         "-g           print the generation of the subvolume",
393         "-o           print only subvolumes bellow specified path",
394         "-u           print the uuid of subvolumes (and snapshots)",
395         "-q           print the parent uuid of the snapshots",
396         "-t           print the result as a table",
397         "-s           list snapshots only in the filesystem",
398         "-r           list readonly subvolumes (including snapshots)",
399         "-d           list deleted subvolumes that are not yet cleaned",
400         "-G [+|-]value",
401         "             filter the subvolumes by generation",
402         "             (+value: >= value; -value: <= value; value: = value)",
403         "-C [+|-]value",
404         "             filter the subvolumes by ogeneration",
405         "             (+value: >= value; -value: <= value; value: = value)",
406         "--sort=gen,ogen,rootid,path",
407         "             list the subvolume in order of gen, ogen, rootid or path",
408         "             you also can add '+' or '-' in front of each items.",
409         "             (+:ascending, -:descending, ascending default)",
410         NULL,
411 };
412
413 static int cmd_subvol_list(int argc, char **argv)
414 {
415         struct btrfs_list_filter_set *filter_set;
416         struct btrfs_list_comparer_set *comparer_set;
417         u64 flags = 0;
418         int fd = -1;
419         u64 top_id;
420         int ret = -1, uerr = 0;
421         int c;
422         char *subvol;
423         int is_tab_result = 0;
424         int is_list_all = 0;
425         int is_only_in_path = 0;
426         struct option long_options[] = {
427                 {"sort", 1, NULL, 'S'},
428                 {NULL, 0, NULL, 0}
429         };
430         DIR *dirstream = NULL;
431
432         filter_set = btrfs_list_alloc_filter_set();
433         comparer_set = btrfs_list_alloc_comparer_set();
434
435         optind = 1;
436         while(1) {
437                 c = getopt_long(argc, argv,
438                                     "acdgopqsurG:C:t", long_options, NULL);
439                 if (c < 0)
440                         break;
441
442                 switch(c) {
443                 case 'p':
444                         btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
445                         break;
446                 case 'a':
447                         is_list_all = 1;
448                         break;
449                 case 'c':
450                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
451                         break;
452                 case 'd':
453                         btrfs_list_setup_filter(&filter_set,
454                                                 BTRFS_LIST_FILTER_DELETED,
455                                                 0);
456                         break;
457                 case 'g':
458                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
459                         break;
460                 case 'o':
461                         is_only_in_path = 1;
462                         break;
463                 case 't':
464                         is_tab_result = 1;
465                         break;
466                 case 's':
467                         btrfs_list_setup_filter(&filter_set,
468                                                 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
469                                                 0);
470                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
471                         btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
472                         break;
473                 case 'u':
474                         btrfs_list_setup_print_column(BTRFS_LIST_UUID);
475                         break;
476                 case 'q':
477                         btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
478                         break;
479                 case 'r':
480                         flags |= BTRFS_ROOT_SUBVOL_RDONLY;
481                         break;
482                 case 'G':
483                         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
484                         ret = btrfs_list_parse_filter_string(optarg,
485                                                         &filter_set,
486                                                         BTRFS_LIST_FILTER_GEN);
487                         if (ret) {
488                                 uerr = 1;
489                                 goto out;
490                         }
491                         break;
492
493                 case 'C':
494                         btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
495                         ret = btrfs_list_parse_filter_string(optarg,
496                                                         &filter_set,
497                                                         BTRFS_LIST_FILTER_CGEN);
498                         if (ret) {
499                                 uerr = 1;
500                                 goto out;
501                         }
502                         break;
503                 case 'S':
504                         ret = btrfs_list_parse_sort_string(optarg,
505                                                            &comparer_set);
506                         if (ret) {
507                                 uerr = 1;
508                                 goto out;
509                         }
510                         break;
511
512                 default:
513                         uerr = 1;
514                         goto out;
515                 }
516         }
517
518         if (flags)
519                 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
520                                         flags);
521
522         if (check_argc_exact(argc - optind, 1)) {
523                 uerr = 1;
524                 goto out;
525         }
526
527         subvol = argv[optind];
528         fd = open_file_or_dir(subvol, &dirstream);
529         if (fd < 0) {
530                 ret = -1;
531                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
532                 goto out;
533         }
534
535         ret = btrfs_list_get_path_rootid(fd, &top_id);
536         if (ret) {
537                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
538                 goto out;
539         }
540
541         if (is_list_all)
542                 btrfs_list_setup_filter(&filter_set,
543                                         BTRFS_LIST_FILTER_FULL_PATH,
544                                         top_id);
545         else if (is_only_in_path)
546                 btrfs_list_setup_filter(&filter_set,
547                                         BTRFS_LIST_FILTER_TOPID_EQUAL,
548                                         top_id);
549
550         /* by default we shall print the following columns*/
551         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
552         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
553         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
554         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
555
556         if (is_tab_result)
557                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
558                                 BTRFS_LIST_LAYOUT_TABLE,
559                                 !is_list_all && !is_only_in_path, NULL);
560         else
561                 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
562                                 BTRFS_LIST_LAYOUT_DEFAULT,
563                                 !is_list_all && !is_only_in_path, NULL);
564
565 out:
566         close_file_or_dir(fd, dirstream);
567         if (filter_set)
568                 btrfs_list_free_filter_set(filter_set);
569         if (comparer_set)
570                 btrfs_list_free_comparer_set(comparer_set);
571         if (uerr)
572                 usage(cmd_subvol_list_usage);
573         return !!ret;
574 }
575
576 static const char * const cmd_snapshot_usage[] = {
577         "btrfs subvolume snapshot [-r] <source> <dest>|[<dest>/]<name>",
578         "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
579         "Create a snapshot of the subvolume",
580         "Create a writable/readonly snapshot of the subvolume <source> with",
581         "the name <name> in the <dest> directory.  If only <dest> is given,",
582         "the subvolume will be named the basename of <source>.",
583         "",
584         "-r             create a readonly snapshot",
585         "-i <qgroupid>  add the newly created snapshot to a qgroup. This",
586         "               option can be given multiple times.",
587         NULL
588 };
589
590 static int cmd_snapshot(int argc, char **argv)
591 {
592         char    *subvol, *dst;
593         int     res, retval;
594         int     fd = -1, fddst = -1;
595         int     len, readonly = 0;
596         char    *dupname = NULL;
597         char    *dupdir = NULL;
598         char    *newname;
599         char    *dstdir;
600         struct btrfs_ioctl_vol_args_v2  args;
601         struct btrfs_qgroup_inherit *inherit = NULL;
602         DIR *dirstream1 = NULL, *dirstream2 = NULL;
603
604         optind = 1;
605         memset(&args, 0, sizeof(args));
606         while (1) {
607                 int c = getopt(argc, argv, "c:i:r");
608                 if (c < 0)
609                         break;
610
611                 switch (c) {
612                 case 'c':
613                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
614                         if (res) {
615                                 retval = res;
616                                 goto out;
617                         }
618                         break;
619                 case 'i':
620                         res = qgroup_inherit_add_group(&inherit, optarg);
621                         if (res) {
622                                 retval = res;
623                                 goto out;
624                         }
625                         break;
626                 case 'r':
627                         readonly = 1;
628                         break;
629                 case 'x':
630                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
631                         if (res) {
632                                 retval = res;
633                                 goto out;
634                         }
635                         break;
636                 default:
637                         usage(cmd_snapshot_usage);
638                 }
639         }
640
641         if (check_argc_exact(argc - optind, 2))
642                 usage(cmd_snapshot_usage);
643
644         subvol = argv[optind];
645         dst = argv[optind + 1];
646
647         retval = 1;     /* failure */
648         res = test_issubvolume(subvol);
649         if (res < 0) {
650                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
651                 goto out;
652         }
653         if (!res) {
654                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
655                 goto out;
656         }
657
658         res = test_isdir(dst);
659         if (res == 0) {
660                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
661                 goto out;
662         }
663
664         if (res > 0) {
665                 dupname = strdup(subvol);
666                 newname = basename(dupname);
667                 dstdir = dst;
668         } else {
669                 dupname = strdup(dst);
670                 newname = basename(dupname);
671                 dupdir = strdup(dst);
672                 dstdir = dirname(dupdir);
673         }
674
675         if (!strcmp(newname, ".") || !strcmp(newname, "..") ||
676              strchr(newname, '/') ){
677                 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
678                         newname);
679                 goto out;
680         }
681
682         len = strlen(newname);
683         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
684                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
685                         newname);
686                 goto out;
687         }
688
689         fddst = open_file_or_dir(dstdir, &dirstream1);
690         if (fddst < 0) {
691                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
692                 goto out;
693         }
694
695         fd = open_file_or_dir(subvol, &dirstream2);
696         if (fd < 0) {
697                 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
698                 goto out;
699         }
700
701         if (readonly) {
702                 args.flags |= BTRFS_SUBVOL_RDONLY;
703                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
704                        subvol, dstdir, newname);
705         } else {
706                 printf("Create a snapshot of '%s' in '%s/%s'\n",
707                        subvol, dstdir, newname);
708         }
709
710         args.fd = fd;
711         if (inherit) {
712                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
713                 args.size = qgroup_inherit_size(inherit);
714                 args.qgroup_inherit = inherit;
715         }
716         strncpy_null(args.name, newname);
717
718         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
719
720         if (res < 0) {
721                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
722                         subvol, strerror(errno));
723                 goto out;
724         }
725
726         retval = 0;     /* success */
727
728 out:
729         close_file_or_dir(fddst, dirstream1);
730         close_file_or_dir(fd, dirstream2);
731         free(inherit);
732         free(dupname);
733         free(dupdir);
734
735         return retval;
736 }
737
738 static const char * const cmd_subvol_get_default_usage[] = {
739         "btrfs subvolume get-default <path>",
740         "Get the default subvolume of a filesystem",
741         NULL
742 };
743
744 static int cmd_subvol_get_default(int argc, char **argv)
745 {
746         int fd = -1;
747         int ret;
748         char *subvol;
749         struct btrfs_list_filter_set *filter_set;
750         u64 default_id;
751         DIR *dirstream = NULL;
752
753         if (check_argc_exact(argc, 2))
754                 usage(cmd_subvol_get_default_usage);
755
756         subvol = argv[1];
757         fd = open_file_or_dir(subvol, &dirstream);
758         if (fd < 0) {
759                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
760                 return 1;
761         }
762
763         ret = btrfs_list_get_default_subvolume(fd, &default_id);
764         if (ret) {
765                 fprintf(stderr, "ERROR: can't perform the search - %s\n",
766                         strerror(errno));
767                 goto out;
768         }
769
770         ret = 1;
771         if (default_id == 0) {
772                 fprintf(stderr, "ERROR: 'default' dir item not found\n");
773                 goto out;
774         }
775
776         /* no need to resolve roots if FS_TREE is default */
777         if (default_id == BTRFS_FS_TREE_OBJECTID) {
778                 printf("ID 5 (FS_TREE)\n");
779                 ret = 0;
780                 goto out;
781         }
782
783         filter_set = btrfs_list_alloc_filter_set();
784         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
785                                 default_id);
786
787         /* by default we shall print the following columns*/
788         btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
789         btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
790         btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
791         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
792
793         ret = btrfs_list_subvols_print(fd, filter_set, NULL,
794                 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
795
796         if (filter_set)
797                 btrfs_list_free_filter_set(filter_set);
798 out:
799         close_file_or_dir(fd, dirstream);
800         return !!ret;
801 }
802
803 static const char * const cmd_subvol_set_default_usage[] = {
804         "btrfs subvolume set-default <subvolid> <path>",
805         "Set the default subvolume of a filesystem",
806         NULL
807 };
808
809 static int cmd_subvol_set_default(int argc, char **argv)
810 {
811         int     ret=0, fd, e;
812         u64     objectid;
813         char    *path;
814         char    *subvolid;
815         DIR     *dirstream = NULL;
816
817         if (check_argc_exact(argc, 3))
818                 usage(cmd_subvol_set_default_usage);
819
820         subvolid = argv[1];
821         path = argv[2];
822
823         objectid = (unsigned long long)strtoll(subvolid, NULL, 0);
824         if (errno == ERANGE) {
825                 fprintf(stderr, "ERROR: invalid tree id (%s)\n", subvolid);
826                 return 1;
827         }
828
829         fd = open_file_or_dir(path, &dirstream);
830         if (fd < 0) {
831                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
832                 return 1;
833         }
834
835         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
836         e = errno;
837         close_file_or_dir(fd, dirstream);
838         if (ret < 0) {
839                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
840                         strerror(e));
841                 return 1;
842         }
843         return 0;
844 }
845
846 static const char * const cmd_find_new_usage[] = {
847         "btrfs subvolume find-new <path> <lastgen>",
848         "List the recently modified files in a filesystem",
849         NULL
850 };
851
852 static int cmd_find_new(int argc, char **argv)
853 {
854         int fd;
855         int ret;
856         char *subvol;
857         u64 last_gen;
858         DIR *dirstream = NULL;
859
860         if (check_argc_exact(argc, 3))
861                 usage(cmd_find_new_usage);
862
863         subvol = argv[1];
864         last_gen = atoll(argv[2]);
865
866         ret = test_issubvolume(subvol);
867         if (ret < 0) {
868                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
869                 return 1;
870         }
871         if (!ret) {
872                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
873                 return 1;
874         }
875
876         fd = open_file_or_dir(subvol, &dirstream);
877         if (fd < 0) {
878                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
879                 return 1;
880         }
881
882         ret = ioctl(fd, BTRFS_IOC_SYNC);
883         if (ret < 0) {
884                 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
885                         subvol, strerror(errno));
886                 close_file_or_dir(fd, dirstream);
887                 return 1;
888         }
889
890         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
891         close_file_or_dir(fd, dirstream);
892         return !!ret;
893 }
894
895 static const char * const cmd_subvol_show_usage[] = {
896         "btrfs subvolume show <subvol-path>",
897         "Show more information of the subvolume",
898         NULL
899 };
900
901 static int cmd_subvol_show(int argc, char **argv)
902 {
903         struct root_info get_ri;
904         struct btrfs_list_filter_set *filter_set;
905         char tstr[256];
906         char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
907         char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
908         char raw_prefix[] = "\t\t\t\t";
909         u64 sv_id, mntid;
910         int fd = -1, mntfd = -1;
911         int ret = 1;
912         DIR *dirstream1 = NULL, *dirstream2 = NULL;
913
914         if (check_argc_exact(argc, 2))
915                 usage(cmd_subvol_show_usage);
916
917         fullpath = realpath(argv[1], NULL);
918         if (!fullpath) {
919                 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
920                         argv[1], strerror(errno));
921                 goto out;
922         }
923
924         ret = test_issubvolume(fullpath);
925         if (ret < 0) {
926                 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
927                 goto out;
928         }
929         if (!ret) {
930                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
931                 goto out;
932         }
933
934         ret = find_mount_root(fullpath, &mnt);
935         if (ret < 0) {
936                 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
937                                 "%s\n", fullpath, strerror(-ret));
938                 goto out;
939         }
940         ret = 1;
941         svpath = get_subvol_name(mnt, fullpath);
942
943         fd = open_file_or_dir(fullpath, &dirstream1);
944         if (fd < 0) {
945                 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
946                 goto out;
947         }
948
949         ret = btrfs_list_get_path_rootid(fd, &sv_id);
950         if (ret) {
951                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
952                         fullpath);
953                 goto out;
954         }
955
956         mntfd = open_file_or_dir(mnt, &dirstream2);
957         if (mntfd < 0) {
958                 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
959                 goto out;
960         }
961
962         ret = btrfs_list_get_path_rootid(mntfd, &mntid);
963         if (ret) {
964                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
965                 goto out;
966         }
967
968         if (sv_id == BTRFS_FS_TREE_OBJECTID) {
969                 printf("%s is btrfs root\n", fullpath);
970                 goto out;
971         }
972
973         memset(&get_ri, 0, sizeof(get_ri));
974         get_ri.root_id = sv_id;
975
976         if (btrfs_get_subvol(mntfd, &get_ri)) {
977                 fprintf(stderr, "ERROR: can't find '%s'\n",
978                         svpath);
979                 goto out;
980         }
981
982         ret = 0;
983         /* print the info */
984         printf("%s\n", fullpath);
985         printf("\tName: \t\t\t%s\n", get_ri.name);
986
987         if (uuid_is_null(get_ri.uuid))
988                 strcpy(uuidparse, "-");
989         else
990                 uuid_unparse(get_ri.uuid, uuidparse);
991         printf("\tuuid: \t\t\t%s\n", uuidparse);
992
993         if (uuid_is_null(get_ri.puuid))
994                 strcpy(uuidparse, "-");
995         else
996                 uuid_unparse(get_ri.puuid, uuidparse);
997         printf("\tParent uuid: \t\t%s\n", uuidparse);
998
999         if (get_ri.otime) {
1000                 struct tm tm;
1001
1002                 localtime_r(&get_ri.otime, &tm);
1003                 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
1004         } else
1005                 strcpy(tstr, "-");
1006         printf("\tCreation time: \t\t%s\n", tstr);
1007
1008         printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
1009         printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
1010         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1011         printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
1012         printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
1013
1014         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1015                 printf("\tFlags: \t\t\treadonly\n");
1016         else
1017                 printf("\tFlags: \t\t\t-\n");
1018
1019         /* print the snapshots of the given subvol if any*/
1020         printf("\tSnapshot(s):\n");
1021         filter_set = btrfs_list_alloc_filter_set();
1022         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1023                                 (u64)(unsigned long)get_ri.uuid);
1024         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1025         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1026                         1, raw_prefix);
1027
1028         /* clean up */
1029         free(get_ri.path);
1030         free(get_ri.name);
1031         free(get_ri.full_path);
1032         btrfs_list_free_filter_set(filter_set);
1033
1034 out:
1035         close_file_or_dir(fd, dirstream1);
1036         close_file_or_dir(mntfd, dirstream2);
1037         free(mnt);
1038         free(fullpath);
1039         return !!ret;
1040 }
1041
1042 const struct cmd_group subvolume_cmd_group = {
1043         subvolume_cmd_group_usage, NULL, {
1044                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1045                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1046                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1047                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1048                 { "get-default", cmd_subvol_get_default,
1049                         cmd_subvol_get_default_usage, NULL, 0 },
1050                 { "set-default", cmd_subvol_set_default,
1051                         cmd_subvol_set_default_usage, NULL, 0 },
1052                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1053                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1054                 NULL_CMD_STRUCT
1055         }
1056 };
1057
1058 int cmd_subvolume(int argc, char **argv)
1059 {
1060         return handle_command_group(&subvolume_cmd_group, argc, argv);
1061 }