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