btrfs-progs: mkfs: add option to skip trim
[btrfs-progs-unstable/devel.git] / cmds-subvolume.c
blob3508ce6159e19d68926cfdef8113b0051194dfe9
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.
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.
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.
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>
27 #include "kerncompat.h"
28 #include "ioctl.h"
30 #include "commands.h"
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);
36 static const char * const subvolume_cmd_group_usage[] = {
37 "btrfs subvolume <command> <args>",
38 NULL
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
48 static int test_isdir(char *path)
50 struct stat st;
51 int res;
53 res = stat(path, &st);
54 if(res < 0 )
55 return -1;
57 return S_ISDIR(st.st_mode);
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
68 static int cmd_subvol_create(int argc, char **argv)
70 int res, fddst, len, e;
71 char *newname;
72 char *dstdir;
73 struct btrfs_ioctl_vol_args args;
74 char *dst;
76 if (check_argc_exact(argc, 2))
77 usage(cmd_subvol_create_usage);
79 dst = argv[1];
81 res = test_isdir(dst);
82 if(res >= 0 ){
83 fprintf(stderr, "ERROR: '%s' exists\n", dst);
84 return 12;
87 newname = strdup(dst);
88 newname = basename(newname);
89 dstdir = strdup(dst);
90 dstdir = dirname(dstdir);
92 if( !strcmp(newname,".") || !strcmp(newname,"..") ||
93 strchr(newname, '/') ){
94 fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
95 newname);
96 return 14;
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;
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;
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;
118 close(fddst);
120 if(res < 0 ){
121 fprintf( stderr, "ERROR: cannot create subvolume - %s\n",
122 strerror(e));
123 return 11;
126 return 0;
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
136 static int test_issubvolume(char *path)
138 struct stat st;
139 int res;
141 res = stat(path, &st);
142 if(res < 0 )
143 return -1;
145 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
148 static const char * const cmd_subvol_delete_usage[] = {
149 "btrfs subvolume delete <name>",
150 "Delete a subvolume",
151 NULL
154 static int cmd_subvol_delete(int argc, char **argv)
156 int res, fd, len, e;
157 struct btrfs_ioctl_vol_args args;
158 char *dname, *vname, *cpath;
159 char *path;
161 if (check_argc_exact(argc, 2))
162 usage(cmd_subvol_delete_usage);
164 path = argv[1];
166 res = test_issubvolume(path);
167 if(res<0){
168 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
169 return 12;
171 if(!res){
172 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
173 return 13;
176 cpath = realpath(path, 0);
177 dname = strdup(cpath);
178 dname = dirname(dname);
179 vname = strdup(cpath);
180 vname = basename(vname);
181 free(cpath);
183 if( !strcmp(vname,".") || !strcmp(vname,"..") ||
184 strchr(vname, '/') ){
185 fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n",
186 vname);
187 return 14;
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;
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;
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;
210 close(fd);
212 if(res < 0 ){
213 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
214 dname, vname, strerror(e));
215 return 11;
218 return 0;
221 static const char * const cmd_subvol_list_usage[] = {
222 "btrfs subvolume list [-p] <path>",
223 "List subvolumes (and snapshots)",
225 "-p print parent ID",
226 NULL
229 static int cmd_subvol_list(int argc, char **argv)
231 int fd;
232 int ret;
233 int print_parent = 0;
234 char *subvol;
236 optind = 1;
237 while(1) {
238 int c = getopt(argc, argv, "p");
239 if (c < 0)
240 break;
242 switch(c) {
243 case 'p':
244 print_parent = 1;
245 break;
246 default:
247 usage(cmd_subvol_list_usage);
251 if (check_argc_exact(argc - optind, 1))
252 usage(cmd_subvol_list_usage);
254 subvol = argv[optind];
256 ret = test_issubvolume(subvol);
257 if (ret < 0) {
258 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
259 return 12;
261 if (!ret) {
262 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
263 return 13;
266 fd = open_file_or_dir(subvol);
267 if (fd < 0) {
268 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
269 return 12;
271 ret = list_subvols(fd, print_parent, 0);
272 if (ret)
273 return 19;
274 return 0;
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",
283 "-r create a readonly snapshot",
284 NULL
287 static int cmd_snapshot(int argc, char **argv)
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;
295 memset(&args, 0, sizeof(args));
297 optind = 1;
298 while (1) {
299 int c = getopt(argc, argv, "r");
300 if (c < 0)
301 break;
303 switch (c) {
304 case 'r':
305 readonly = 1;
306 break;
307 default:
308 usage(cmd_snapshot_usage);
312 if (check_argc_exact(argc - optind, 2))
313 usage(cmd_snapshot_usage);
315 subvol = argv[optind];
316 dst = argv[optind + 1];
318 res = test_issubvolume(subvol);
319 if(res<0){
320 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
321 return 12;
323 if(!res){
324 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
325 return 13;
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;
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);
345 if( !strcmp(newname,".") || !strcmp(newname,"..") ||
346 strchr(newname, '/') ){
347 fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n",
348 newname);
349 return 14;
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;
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;
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;
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);
381 args.fd = fd;
382 strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
383 args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
384 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
385 e = errno;
387 close(fd);
388 close(fddst);
390 if(res < 0 ){
391 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
392 subvol, strerror(e));
393 return 11;
396 return 0;
399 static const char * const cmd_subvol_get_default_usage[] = {
400 "btrfs subvolume get-default <path>",
401 "Get the default subvolume of a filesystem",
402 NULL
405 static int cmd_subvol_get_default(int argc, char **argv)
407 int fd;
408 int ret;
409 char *subvol;
411 if (check_argc_exact(argc, 2))
412 usage(cmd_subvol_get_default_usage);
414 subvol = argv[1];
416 ret = test_issubvolume(subvol);
417 if (ret < 0) {
418 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
419 return 12;
421 if (!ret) {
422 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
423 return 13;
426 fd = open_file_or_dir(subvol);
427 if (fd < 0) {
428 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
429 return 12;
431 ret = list_subvols(fd, 0, 1);
432 if (ret)
433 return 19;
434 return 0;
437 static const char * const cmd_subvol_set_default_usage[] = {
438 "btrfs subvolume set-default <subvolid> <path>",
439 "Set the default subvolume of a filesystem",
440 NULL
443 static int cmd_subvol_set_default(int argc, char **argv)
445 int ret=0, fd, e;
446 u64 objectid;
447 char *path;
448 char *subvolid;
450 if (check_argc_exact(argc, 3))
451 usage(cmd_subvol_set_default_usage);
453 subvolid = argv[1];
454 path = argv[2];
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;
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;
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;
475 return 0;
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
484 static int cmd_find_new(int argc, char **argv)
486 int fd;
487 int ret;
488 char *subvol;
489 u64 last_gen;
491 if (check_argc_exact(argc, 3))
492 usage(cmd_find_new_usage);
494 subvol = argv[1];
495 last_gen = atoll(argv[2]);
497 ret = test_issubvolume(subvol);
498 if (ret < 0) {
499 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
500 return 12;
502 if (!ret) {
503 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
504 return 13;
507 fd = open_file_or_dir(subvol);
508 if (fd < 0) {
509 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
510 return 12;
512 ret = find_updated_files(fd, 0, last_gen);
513 if (ret)
514 return 19;
515 return 0;
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 }
533 int cmd_subvolume(int argc, char **argv)
535 return handle_command_group(&subvolume_cmd_group, argc, argv);