68ebd402b81cea90b29d05e390f23410d6159ff5
[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
30 #include "commands.h"
31
32 /* btrfs-list.c */
33 int list_subvols(int fd, int print_parent, int get_default);
34 int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
35
36 static const char subvolume_cmd_group_usage[] =
37         "btrfs subvolume <command> <args>";
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         struct btrfs_ioctl_vol_args     args;
72         char    *dst;
73
74         if (check_argc_exact(argc, 2))
75                 usage(cmd_subvol_create_usage);
76
77         dst = argv[1];
78
79         res = test_isdir(dst);
80         if(res >= 0 ){
81                 fprintf(stderr, "ERROR: '%s' exists\n", dst);
82                 return 12;
83         }
84
85         newname = strdup(dst);
86         newname = basename(newname);
87         dstdir = strdup(dst);
88         dstdir = dirname(dstdir);
89
90         if( !strcmp(newname,".") || !strcmp(newname,"..") ||
91              strchr(newname, '/') ){
92                 fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
93                         newname);
94                 return 14;
95         }
96
97         len = strlen(newname);
98         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
99                 fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
100                         newname);
101                 return 14;
102         }
103
104         fddst = open_file_or_dir(dstdir);
105         if (fddst < 0) {
106                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
107                 return 12;
108         }
109
110         printf("Create subvolume '%s/%s'\n", dstdir, newname);
111         strncpy(args.name, newname, BTRFS_PATH_NAME_MAX);
112         res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
113         e = errno;
114
115         close(fddst);
116
117         if(res < 0 ){
118                 fprintf( stderr, "ERROR: cannot create subvolume - %s\n",
119                         strerror(e));
120                 return 11;
121         }
122
123         return 0;
124 }
125
126 /*
127  * test if path is a subvolume:
128  * this function return
129  * 0-> path exists but it is not a subvolume
130  * 1-> path exists and it is  a subvolume
131  * -1 -> path is unaccessible
132  */
133 static int test_issubvolume(char *path)
134 {
135         struct stat     st;
136         int             res;
137
138         res = stat(path, &st);
139         if(res < 0 )
140                 return -1;
141
142         return (st.st_ino == 256) && S_ISDIR(st.st_mode);
143 }
144
145 static const char * const cmd_subvol_delete_usage[] = {
146         "btrfs subvolume delete <name>",
147         "Delete a subvolume",
148         NULL
149 };
150
151 static int cmd_subvol_delete(int argc, char **argv)
152 {
153         int     res, fd, len, e;
154         struct btrfs_ioctl_vol_args     args;
155         char    *dname, *vname, *cpath;
156         char    *path;
157
158         if (check_argc_exact(argc, 2))
159                 usage(cmd_subvol_delete_usage);
160
161         path = argv[1];
162
163         res = test_issubvolume(path);
164         if(res<0){
165                 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
166                 return 12;
167         }
168         if(!res){
169                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
170                 return 13;
171         }
172
173         cpath = realpath(path, 0);
174         dname = strdup(cpath);
175         dname = dirname(dname);
176         vname = strdup(cpath);
177         vname = basename(vname);
178         free(cpath);
179
180         if( !strcmp(vname,".") || !strcmp(vname,"..") ||
181              strchr(vname, '/') ){
182                 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
183                         vname);
184                 return 14;
185         }
186
187         len = strlen(vname);
188         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
189                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
190                         vname);
191                 return 14;
192         }
193
194         fd = open_file_or_dir(dname);
195         if (fd < 0) {
196                 close(fd);
197                 fprintf(stderr, "ERROR: can't access to '%s'\n", dname);
198                 return 12;
199         }
200
201         printf("Delete subvolume '%s/%s'\n", dname, vname);
202         strncpy(args.name, vname, BTRFS_PATH_NAME_MAX);
203         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
204         e = errno;
205
206         close(fd);
207
208         if(res < 0 ){
209                 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
210                         dname, vname, strerror(e));
211                 return 11;
212         }
213
214         return 0;
215 }
216
217 static const char * const cmd_subvol_list_usage[] = {
218         "btrfs subvolume list [-p] <path>",
219         "List subvolumes (and snapshots)",
220         "",
221         "-p     print parent ID",
222         NULL
223 };
224
225 static int cmd_subvol_list(int argc, char **argv)
226 {
227         int fd;
228         int ret;
229         int print_parent = 0;
230         char *subvol;
231
232         optind = 1;
233         while(1) {
234                 int c = getopt(argc, argv, "p");
235                 if (c < 0)
236                         break;
237
238                 switch(c) {
239                 case 'p':
240                         print_parent = 1;
241                         break;
242                 default:
243                         usage(cmd_subvol_list_usage);
244                 }
245         }
246
247         if (check_argc_exact(argc - optind, 1))
248                 usage(cmd_subvol_list_usage);
249
250         subvol = argv[optind];
251
252         ret = test_issubvolume(subvol);
253         if (ret < 0) {
254                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
255                 return 12;
256         }
257         if (!ret) {
258                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
259                 return 13;
260         }
261
262         fd = open_file_or_dir(subvol);
263         if (fd < 0) {
264                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
265                 return 12;
266         }
267         ret = list_subvols(fd, print_parent, 0);
268         if (ret)
269                 return 19;
270         return 0;
271 }
272
273 static const char * const cmd_snapshot_usage[] = {
274         "btrfs subvolume snapshot [-r] <source> [<dest>/]<name>",
275         "Create a snapshot of the subvolume",
276         "Create a writable/readonly snapshot of the subvolume <source> with",
277         "the name <name> in the <dest> directory",
278         "",
279         "-r     create a readonly snapshot",
280         NULL
281 };
282
283 static int cmd_snapshot(int argc, char **argv)
284 {
285         char    *subvol, *dst;
286         int     res, fd, fddst, len, e, readonly = 0;
287         char    *newname;
288         char    *dstdir;
289         struct btrfs_ioctl_vol_args_v2  args;
290
291         memset(&args, 0, sizeof(args));
292
293         optind = 1;
294         while (1) {
295                 int c = getopt(argc, argv, "r");
296                 if (c < 0)
297                         break;
298
299                 switch (c) {
300                 case 'r':
301                         readonly = 1;
302                         break;
303                 default:
304                         usage(cmd_snapshot_usage);
305                 }
306         }
307
308         if (check_argc_exact(argc - optind, 2))
309                 usage(cmd_snapshot_usage);
310
311         subvol = argv[optind];
312         dst = argv[optind + 1];
313
314         res = test_issubvolume(subvol);
315         if(res<0){
316                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
317                 return 12;
318         }
319         if(!res){
320                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
321                 return 13;
322         }
323
324         res = test_isdir(dst);
325         if(res == 0 ){
326                 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
327                 return 12;
328         }
329
330         if(res>0){
331                 newname = strdup(subvol);
332                 newname = basename(newname);
333                 dstdir = dst;
334         }else{
335                 newname = strdup(dst);
336                 newname = basename(newname);
337                 dstdir = strdup(dst);
338                 dstdir = dirname(dstdir);
339         }
340
341         if( !strcmp(newname,".") || !strcmp(newname,"..") ||
342              strchr(newname, '/') ){
343                 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
344                         newname);
345                 return 14;
346         }
347
348         len = strlen(newname);
349         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
350                 fprintf(stderr, "ERROR: snapshot name too long ('%s)\n",
351                         newname);
352                 return 14;
353         }
354
355         fddst = open_file_or_dir(dstdir);
356         if (fddst < 0) {
357                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
358                 return 12;
359         }
360
361         fd = open_file_or_dir(subvol);
362         if (fd < 0) {
363                 close(fddst);
364                 fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
365                 return 12;
366         }
367
368         if (readonly) {
369                 args.flags |= BTRFS_SUBVOL_RDONLY;
370                 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
371                        subvol, dstdir, newname);
372         } else {
373                 printf("Create a snapshot of '%s' in '%s/%s'\n",
374                        subvol, dstdir, newname);
375         }
376
377         args.fd = fd;
378         strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
379         res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
380         e = errno;
381
382         close(fd);
383         close(fddst);
384
385         if(res < 0 ){
386                 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
387                         subvol, strerror(e));
388                 return 11;
389         }
390
391         return 0;
392 }
393
394 static const char * const cmd_subvol_get_default_usage[] = {
395         "btrfs subvolume get-dafault <path>",
396         "Get the default subvolume of a filesystem",
397         NULL
398 };
399
400 static int cmd_subvol_get_default(int argc, char **argv)
401 {
402         int fd;
403         int ret;
404         char *subvol;
405
406         if (check_argc_exact(argc, 2))
407                 usage(cmd_subvol_get_default_usage);
408
409         subvol = argv[1];
410
411         ret = test_issubvolume(subvol);
412         if (ret < 0) {
413                 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
414                 return 12;
415         }
416         if (!ret) {
417                 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
418                 return 13;
419         }
420
421         fd = open_file_or_dir(subvol);
422         if (fd < 0) {
423                 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
424                 return 12;
425         }
426         ret = list_subvols(fd, 0, 1);
427         if (ret)
428                 return 19;
429         return 0;
430 }
431
432 static const char * const cmd_subvol_set_default_usage[] = {
433         "btrfs subvolume set-dafault <subvolid> <path>",
434         "Set the default subvolume of a filesystem",
435         NULL
436 };
437
438 static int cmd_subvol_set_default(int argc, char **argv)
439 {
440         int     ret=0, fd, e;
441         u64     objectid;
442         char    *path;
443         char    *subvolid;
444
445         if (check_argc_exact(argc, 3))
446                 usage(cmd_subvol_set_default_usage);
447
448         subvolid = argv[1];
449         path = argv[2];
450
451         fd = open_file_or_dir(path);
452         if (fd < 0) {
453                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
454                 return 12;
455         }
456
457         objectid = (unsigned long long)strtoll(subvolid, NULL, 0);
458         if (errno == ERANGE) {
459                 fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid);
460                 return 30;
461         }
462         ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
463         e = errno;
464         close(fd);
465         if( ret < 0 ){
466                 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
467                         strerror(e));
468                 return 30;
469         }
470         return 0;
471 }
472
473 static const char * const cmd_find_new_usage[] = {
474         "btrfs subvolume find-new <path> <lastgen>",
475         "List the recently modified files in a filesystem",
476         NULL
477 };
478
479 static int cmd_find_new(int argc, char **argv)
480 {
481         int fd;
482         int ret;
483         char *subvol;
484         u64 last_gen;
485
486         if (check_argc_exact(argc, 3))
487                 usage(cmd_find_new_usage);
488
489         subvol = argv[1];
490         last_gen = atoll(argv[2]);
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 = find_updated_files(fd, 0, last_gen);
508         if (ret)
509                 return 19;
510         return 0;
511 }
512
513 const struct cmd_group subvolume_cmd_group = {
514         subvolume_cmd_group_usage, NULL, {
515                 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
516                 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
517                 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
518                 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
519                 { "get-default", cmd_subvol_get_default,
520                         cmd_subvol_get_default_usage, NULL, 0 },
521                 { "set-default", cmd_subvol_set_default,
522                         cmd_subvol_set_default_usage, NULL, 0 },
523                 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
524                 { 0, 0, 0, 0, 0 }
525         }
526 };
527
528 int cmd_subvolume(int argc, char **argv)
529 {
530         return handle_command_group(&subvolume_cmd_group, argc, argv);
531 }