Btrfs-progs: switch to arg_strtou64() part3
[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 = arg_strtou64(subvolid);
824
825         fd = open_file_or_dir(path, &dirstream);
826         if (fd < 0) {
827                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
828                 return 1;
829         }
830
831         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
832         e = errno;
833         close_file_or_dir(fd, dirstream);
834         if (ret < 0) {
835                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
836                         strerror(e));
837                 return 1;
838         }
839         return 0;
840 }
841
842 static const char * const cmd_find_new_usage[] = {
843         "btrfs subvolume find-new <path> <lastgen>",
844         "List the recently modified files in a filesystem",
845         NULL
846 };
847
848 static int cmd_find_new(int argc, char **argv)
849 {
850         int fd;
851         int ret;
852         char *subvol;
853         u64 last_gen;
854         DIR *dirstream = NULL;
855
856         if (check_argc_exact(argc, 3))
857                 usage(cmd_find_new_usage);
858
859         subvol = argv[1];
860         last_gen = arg_strtou64(argv[2]);
861
862         ret = test_issubvolume(subvol);
863         if (ret < 0) {
864                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
865                 return 1;
866         }
867         if (!ret) {
868                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
869                 return 1;
870         }
871
872         fd = open_file_or_dir(subvol, &dirstream);
873         if (fd < 0) {
874                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
875                 return 1;
876         }
877
878         ret = ioctl(fd, BTRFS_IOC_SYNC);
879         if (ret < 0) {
880                 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
881                         subvol, strerror(errno));
882                 close_file_or_dir(fd, dirstream);
883                 return 1;
884         }
885
886         ret = btrfs_list_find_updated_files(fd, 0, last_gen);
887         close_file_or_dir(fd, dirstream);
888         return !!ret;
889 }
890
891 static const char * const cmd_subvol_show_usage[] = {
892         "btrfs subvolume show <subvol-path>",
893         "Show more information of the subvolume",
894         NULL
895 };
896
897 static int cmd_subvol_show(int argc, char **argv)
898 {
899         struct root_info get_ri;
900         struct btrfs_list_filter_set *filter_set;
901         char tstr[256];
902         char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
903         char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
904         char raw_prefix[] = "\t\t\t\t";
905         u64 sv_id, mntid;
906         int fd = -1, mntfd = -1;
907         int ret = 1;
908         DIR *dirstream1 = NULL, *dirstream2 = NULL;
909
910         if (check_argc_exact(argc, 2))
911                 usage(cmd_subvol_show_usage);
912
913         fullpath = realpath(argv[1], NULL);
914         if (!fullpath) {
915                 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
916                         argv[1], strerror(errno));
917                 goto out;
918         }
919
920         ret = test_issubvolume(fullpath);
921         if (ret < 0) {
922                 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
923                 goto out;
924         }
925         if (!ret) {
926                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
927                 goto out;
928         }
929
930         ret = find_mount_root(fullpath, &mnt);
931         if (ret < 0) {
932                 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
933                                 "%s\n", fullpath, strerror(-ret));
934                 goto out;
935         }
936         ret = 1;
937         svpath = get_subvol_name(mnt, fullpath);
938
939         fd = open_file_or_dir(fullpath, &dirstream1);
940         if (fd < 0) {
941                 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
942                 goto out;
943         }
944
945         ret = btrfs_list_get_path_rootid(fd, &sv_id);
946         if (ret) {
947                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
948                         fullpath);
949                 goto out;
950         }
951
952         mntfd = open_file_or_dir(mnt, &dirstream2);
953         if (mntfd < 0) {
954                 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
955                 goto out;
956         }
957
958         ret = btrfs_list_get_path_rootid(mntfd, &mntid);
959         if (ret) {
960                 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
961                 goto out;
962         }
963
964         if (sv_id == BTRFS_FS_TREE_OBJECTID) {
965                 printf("%s is btrfs root\n", fullpath);
966                 goto out;
967         }
968
969         memset(&get_ri, 0, sizeof(get_ri));
970         get_ri.root_id = sv_id;
971
972         if (btrfs_get_subvol(mntfd, &get_ri)) {
973                 fprintf(stderr, "ERROR: can't find '%s'\n",
974                         svpath);
975                 goto out;
976         }
977
978         ret = 0;
979         /* print the info */
980         printf("%s\n", fullpath);
981         printf("\tName: \t\t\t%s\n", get_ri.name);
982
983         if (uuid_is_null(get_ri.uuid))
984                 strcpy(uuidparse, "-");
985         else
986                 uuid_unparse(get_ri.uuid, uuidparse);
987         printf("\tuuid: \t\t\t%s\n", uuidparse);
988
989         if (uuid_is_null(get_ri.puuid))
990                 strcpy(uuidparse, "-");
991         else
992                 uuid_unparse(get_ri.puuid, uuidparse);
993         printf("\tParent uuid: \t\t%s\n", uuidparse);
994
995         if (get_ri.otime) {
996                 struct tm tm;
997
998                 localtime_r(&get_ri.otime, &tm);
999                 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
1000         } else
1001                 strcpy(tstr, "-");
1002         printf("\tCreation time: \t\t%s\n", tstr);
1003
1004         printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
1005         printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
1006         printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1007         printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
1008         printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
1009
1010         if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1011                 printf("\tFlags: \t\t\treadonly\n");
1012         else
1013                 printf("\tFlags: \t\t\t-\n");
1014
1015         /* print the snapshots of the given subvol if any*/
1016         printf("\tSnapshot(s):\n");
1017         filter_set = btrfs_list_alloc_filter_set();
1018         btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1019                                 (u64)(unsigned long)get_ri.uuid);
1020         btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1021         btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1022                         1, raw_prefix);
1023
1024         /* clean up */
1025         free(get_ri.path);
1026         free(get_ri.name);
1027         free(get_ri.full_path);
1028         btrfs_list_free_filter_set(filter_set);
1029
1030 out:
1031         close_file_or_dir(fd, dirstream1);
1032         close_file_or_dir(mntfd, dirstream2);
1033         free(mnt);
1034         free(fullpath);
1035         return !!ret;
1036 }
1037
1038 const struct cmd_group subvolume_cmd_group = {
1039         subvolume_cmd_group_usage, NULL, {
1040                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1041                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1042                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1043                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1044                 { "get-default", cmd_subvol_get_default,
1045                         cmd_subvol_get_default_usage, NULL, 0 },
1046                 { "set-default", cmd_subvol_set_default,
1047                         cmd_subvol_set_default_usage, NULL, 0 },
1048                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1049                 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1050                 NULL_CMD_STRUCT
1051         }
1052 };
1053
1054 int cmd_subvolume(int argc, char **argv)
1055 {
1056         return handle_command_group(&subvolume_cmd_group, argc, argv);
1057 }