Btrfs-progs: fix wrong usage of btrfs subvolume list command
[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
27 #include "kerncompat.h"
28 #include "ioctl.h"
29 #include "qgroup.h"
30
31 #include "commands.h"
32 #include "btrfs-list.h"
33
34 static const char * const subvolume_cmd_group_usage[] = {
35         "btrfs subvolume <command> <args>",
36         NULL
37 };
38
39 /*
40  * test if path is a directory
41  * this function return
42  * 0-> path exists but it is not a directory
43  * 1-> path exists and it is  a directory
44  * -1 -> path is unaccessible
45  */
46 static int test_isdir(char *path)
47 {
48         struct stat     st;
49         int             res;
50
51         res = stat(path, &st);
52         if(res < 0 )
53                 return -1;
54
55         return S_ISDIR(st.st_mode);
56 }
57
58 static const char * const cmd_subvol_create_usage[] = {
59         "btrfs subvolume create [<dest>/]<name>",
60         "Create a subvolume",
61         "Create a subvolume <name> in <dest>.  If <dest> is not given",
62         "subvolume <name> will be created in the current directory.",
63         NULL
64 };
65
66 static int cmd_subvol_create(int argc, char **argv)
67 {
68         int     res, fddst, len, e;
69         char    *newname;
70         char    *dstdir;
71         char    *dst;
72         struct btrfs_qgroup_inherit *inherit = NULL;
73
74         optind = 1;
75         while (1) {
76                 int c = getopt(argc, argv, "c:i:r");
77                 if (c < 0)
78                         break;
79
80                 switch (c) {
81                 case 'c':
82                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
83                         if (res)
84                                 return res;
85                         break;
86                 case 'i':
87                         res = qgroup_inherit_add_group(&inherit, optarg);
88                         if (res)
89                                 return res;
90                         break;
91                 default:
92                         usage(cmd_subvol_create_usage);
93                 }
94         }
95
96         if (check_argc_exact(argc - optind, 1))
97                 usage(cmd_subvol_create_usage);
98
99         dst = argv[optind];
100
101         res = test_isdir(dst);
102         if(res >= 0 ){
103                 fprintf(stderr, "ERROR: '%s' exists\n", dst);
104                 return 12;
105         }
106
107         newname = strdup(dst);
108         newname = basename(newname);
109         dstdir = strdup(dst);
110         dstdir = dirname(dstdir);
111
112         if( !strcmp(newname,".") || !strcmp(newname,"..") ||
113              strchr(newname, '/') ){
114                 fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
115                         newname);
116                 return 14;
117         }
118
119         len = strlen(newname);
120         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
121                 fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
122                         newname);
123                 return 14;
124         }
125
126         fddst = open_file_or_dir(dstdir);
127         if (fddst < 0) {
128                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
129                 return 12;
130         }
131
132         printf("Create subvolume '%s/%s'\n", dstdir, newname);
133         if (inherit) {
134                 struct btrfs_ioctl_vol_args_v2  args;
135
136                 memset(&args, 0, sizeof(args));
137                 strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
138                 args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
139                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
140                 args.size = qgroup_inherit_size(inherit);
141                 args.qgroup_inherit = inherit;
142
143                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
144         } else {
145                 struct btrfs_ioctl_vol_args     args;
146
147                 memset(&args, 0, sizeof(args));
148                 strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
149                 args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
150
151                 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
152         }
153
154         e = errno;
155
156         close(fddst);
157
158         if(res < 0 ){
159                 fprintf( stderr, "ERROR: cannot create subvolume - %s\n",
160                         strerror(e));
161                 return 11;
162         }
163         free(inherit);
164
165         return 0;
166 }
167
168 /*
169  * test if path is a subvolume:
170  * this function return
171  * 0-> path exists but it is not a subvolume
172  * 1-> path exists and it is  a subvolume
173  * -1 -> path is unaccessible
174  */
175 int test_issubvolume(char *path)
176 {
177         struct stat     st;
178         int             res;
179
180         res = stat(path, &st);
181         if(res < 0 )
182                 return -1;
183
184         return (st.st_ino == 256) && S_ISDIR(st.st_mode);
185 }
186
187 static const char * const cmd_subvol_delete_usage[] = {
188         "btrfs subvolume delete <name>",
189         "Delete a subvolume",
190         NULL
191 };
192
193 static int cmd_subvol_delete(int argc, char **argv)
194 {
195         int     res, fd, len, e;
196         struct btrfs_ioctl_vol_args     args;
197         char    *dname, *vname, *cpath;
198         char    *path;
199
200         if (check_argc_exact(argc, 2))
201                 usage(cmd_subvol_delete_usage);
202
203         path = argv[1];
204
205         res = test_issubvolume(path);
206         if(res<0){
207                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
208                 return 12;
209         }
210         if(!res){
211                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
212                 return 13;
213         }
214
215         cpath = realpath(path, 0);
216         dname = strdup(cpath);
217         dname = dirname(dname);
218         vname = strdup(cpath);
219         vname = basename(vname);
220         free(cpath);
221
222         if( !strcmp(vname,".") || !strcmp(vname,"..") ||
223              strchr(vname, '/') ){
224                 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
225                         vname);
226                 return 14;
227         }
228
229         len = strlen(vname);
230         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
231                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
232                         vname);
233                 return 14;
234         }
235
236         fd = open_file_or_dir(dname);
237         if (fd < 0) {
238                 close(fd);
239                 fprintf(stderr, "ERROR: can't access to '%s'\n", dname);
240                 return 12;
241         }
242
243         printf("Delete subvolume '%s/%s'\n", dname, vname);
244         strncpy(args.name, vname, BTRFS_PATH_NAME_MAX);
245         args.name[BTRFS_PATH_NAME_MAX-1] = 0;
246         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
247         e = errno;
248
249         close(fd);
250
251         if(res < 0 ){
252                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
253                         dname, vname, strerror(e));
254                 return 11;
255         }
256
257         return 0;
258 }
259
260 static const char * const cmd_subvol_list_usage[] = {
261         "btrfs subvolume list [-pu] [-s 0|1] <path>",
262         "List subvolumes (and snapshots)",
263         "",
264         "-p           print parent ID",
265         "-u           print the uuid of subvolumes (and snapshots)",
266         "-s value     list snapshots with generation in ascending/descending order",
267         "             (1: ascending, 0: descending)",
268         NULL
269 };
270
271 static int cmd_subvol_list(int argc, char **argv)
272 {
273         int fd;
274         int ret;
275         int print_parent = 0;
276         int print_snap_only = 0;
277         int order = 0;
278         char *subvol;
279         int print_uuid = 0;
280
281         optind = 1;
282         while(1) {
283                 int c = getopt(argc, argv, "ps:u");
284                 if (c < 0)
285                         break;
286
287                 switch(c) {
288                 case 'p':
289                         print_parent = 1;
290                         break;
291                 case 's':
292                         print_snap_only = 1;
293                         order = atoi(optarg);
294                         break;
295                 case 'u':
296                         print_uuid =1;
297                         break;
298                 default:
299                         usage(cmd_subvol_list_usage);
300                 }
301         }
302
303         if (check_argc_exact(argc - optind, 1))
304                 usage(cmd_subvol_list_usage);
305
306         subvol = argv[optind];
307
308         ret = test_issubvolume(subvol);
309         if (ret < 0) {
310                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
311                 return 12;
312         }
313         if (!ret) {
314                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
315                 return 13;
316         }
317
318         fd = open_file_or_dir(subvol);
319         if (fd < 0) {
320                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
321                 return 12;
322         }
323         if (!print_snap_only)
324                 ret = list_subvols(fd, print_parent, 0, print_uuid);
325         else
326                 ret = list_snapshots(fd, print_parent, order, print_uuid);
327         if (ret)
328                 return 19;
329         return 0;
330 }
331
332 static const char * const cmd_snapshot_usage[] = {
333         "btrfs subvolume snapshot [-r] <source> [<dest>/]<name>",
334         "Create a snapshot of the subvolume",
335         "Create a writable/readonly snapshot of the subvolume <source> with",
336         "the name <name> in the <dest> directory",
337         "",
338         "-r     create a readonly snapshot",
339         NULL
340 };
341
342 static int cmd_snapshot(int argc, char **argv)
343 {
344         char    *subvol, *dst;
345         int     res, fd, fddst, len, e, readonly = 0;
346         char    *newname;
347         char    *dstdir;
348         struct btrfs_ioctl_vol_args_v2  args;
349         struct btrfs_qgroup_inherit *inherit = NULL;
350
351         optind = 1;
352         memset(&args, 0, sizeof(args));
353         while (1) {
354                 int c = getopt(argc, argv, "c:i:r");
355                 if (c < 0)
356                         break;
357
358                 switch (c) {
359                 case 'c':
360                         res = qgroup_inherit_add_copy(&inherit, optarg, 0);
361                         if (res)
362                                 return res;
363                         break;
364                 case 'i':
365                         res = qgroup_inherit_add_group(&inherit, optarg);
366                         if (res)
367                                 return res;
368                         break;
369                 case 'r':
370                         readonly = 1;
371                         break;
372                 case 'x':
373                         res = qgroup_inherit_add_copy(&inherit, optarg, 1);
374                         if (res)
375                                 return res;
376                         break;
377                 default:
378                         usage(cmd_snapshot_usage);
379                 }
380         }
381
382         if (check_argc_exact(argc - optind, 2))
383                 usage(cmd_snapshot_usage);
384
385         subvol = argv[optind];
386         dst = argv[optind + 1];
387
388         res = test_issubvolume(subvol);
389         if(res<0){
390                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
391                 return 12;
392         }
393         if(!res){
394                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
395                 return 13;
396         }
397
398         res = test_isdir(dst);
399         if(res == 0 ){
400                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
401                 return 12;
402         }
403
404         if(res>0){
405                 newname = strdup(subvol);
406                 newname = basename(newname);
407                 dstdir = dst;
408         }else{
409                 newname = strdup(dst);
410                 newname = basename(newname);
411                 dstdir = strdup(dst);
412                 dstdir = dirname(dstdir);
413         }
414
415         if( !strcmp(newname,".") || !strcmp(newname,"..") ||
416              strchr(newname, '/') ){
417                 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
418                         newname);
419                 return 14;
420         }
421
422         len = strlen(newname);
423         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
424                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
425                         newname);
426                 return 14;
427         }
428
429         fddst = open_file_or_dir(dstdir);
430         if (fddst < 0) {
431                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
432                 return 12;
433         }
434
435         fd = open_file_or_dir(subvol);
436         if (fd < 0) {
437                 close(fddst);
438                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
439                 return 12;
440         }
441
442         if (readonly) {
443                 args.flags |= BTRFS_SUBVOL_RDONLY;
444                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
445                        subvol, dstdir, newname);
446         } else {
447                 printf("Create a snapshot of '%s' in '%s/%s'\n",
448                        subvol, dstdir, newname);
449         }
450
451         args.fd = fd;
452         if (inherit) {
453                 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
454                 args.size = qgroup_inherit_size(inherit);
455                 args.qgroup_inherit = inherit;
456         }
457         strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
458         args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
459         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
460         e = errno;
461
462         close(fd);
463         close(fddst);
464
465         if(res < 0 ){
466                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
467                         subvol, strerror(e));
468                 return 11;
469         }
470         free(inherit);
471
472         return 0;
473 }
474
475 static const char * const cmd_subvol_get_default_usage[] = {
476         "btrfs subvolume get-default <path>",
477         "Get the default subvolume of a filesystem",
478         NULL
479 };
480
481 static int cmd_subvol_get_default(int argc, char **argv)
482 {
483         int fd;
484         int ret;
485         char *subvol;
486
487         if (check_argc_exact(argc, 2))
488                 usage(cmd_subvol_get_default_usage);
489
490         subvol = argv[1];
491
492         ret = test_issubvolume(subvol);
493         if (ret < 0) {
494                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
495                 return 12;
496         }
497         if (!ret) {
498                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
499                 return 13;
500         }
501
502         fd = open_file_or_dir(subvol);
503         if (fd < 0) {
504                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
505                 return 12;
506         }
507         ret = list_subvols(fd, 0, 1, 0);
508         if (ret)
509                 return 19;
510         return 0;
511 }
512
513 static const char * const cmd_subvol_set_default_usage[] = {
514         "btrfs subvolume set-default <subvolid> <path>",
515         "Set the default subvolume of a filesystem",
516         NULL
517 };
518
519 static int cmd_subvol_set_default(int argc, char **argv)
520 {
521         int     ret=0, fd, e;
522         u64     objectid;
523         char    *path;
524         char    *subvolid;
525
526         if (check_argc_exact(argc, 3))
527                 usage(cmd_subvol_set_default_usage);
528
529         subvolid = argv[1];
530         path = argv[2];
531
532         fd = open_file_or_dir(path);
533         if (fd < 0) {
534                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
535                 return 12;
536         }
537
538         objectid = (unsigned long long)strtoll(subvolid, NULL, 0);
539         if (errno == ERANGE) {
540                 fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid);
541                 return 30;
542         }
543         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
544         e = errno;
545         close(fd);
546         if( ret < 0 ){
547                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
548                         strerror(e));
549                 return 30;
550         }
551         return 0;
552 }
553
554 static const char * const cmd_find_new_usage[] = {
555         "btrfs subvolume find-new <path> <lastgen>",
556         "List the recently modified files in a filesystem",
557         NULL
558 };
559
560 static int cmd_find_new(int argc, char **argv)
561 {
562         int fd;
563         int ret;
564         char *subvol;
565         u64 last_gen;
566
567         if (check_argc_exact(argc, 3))
568                 usage(cmd_find_new_usage);
569
570         subvol = argv[1];
571         last_gen = atoll(argv[2]);
572
573         ret = test_issubvolume(subvol);
574         if (ret < 0) {
575                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
576                 return 12;
577         }
578         if (!ret) {
579                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
580                 return 13;
581         }
582
583         fd = open_file_or_dir(subvol);
584         if (fd < 0) {
585                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
586                 return 12;
587         }
588         ret = find_updated_files(fd, 0, last_gen);
589         if (ret)
590                 return 19;
591         return 0;
592 }
593
594 const struct cmd_group subvolume_cmd_group = {
595         subvolume_cmd_group_usage, NULL, {
596                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
597                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
598                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
599                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
600                 { "get-default", cmd_subvol_get_default,
601                         cmd_subvol_get_default_usage, NULL, 0 },
602                 { "set-default", cmd_subvol_set_default,
603                         cmd_subvol_set_default_usage, NULL, 0 },
604                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
605                 { 0, 0, 0, 0, 0 }
606         }
607 };
608
609 int cmd_subvolume(int argc, char **argv)
610 {
611         return handle_command_group(&subvolume_cmd_group, argc, argv);
612 }