diff options
Diffstat (limited to 'cmds-subvolume.c')
-rw-r--r-- | cmds-subvolume.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/cmds-subvolume.c b/cmds-subvolume.c new file mode 100644 index 00000000..931506c8 --- /dev/null +++ b/cmds-subvolume.c @@ -0,0 +1,444 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <sys/stat.h> +#include <libgen.h> +#include <limits.h> + +#include "kerncompat.h" +#include "ioctl.h" + +#include "btrfs_cmds.h" + +/* btrfs-list.c */ +int list_subvols(int fd, int print_parent, int get_default); +int find_updated_files(int fd, u64 root_id, u64 oldest_gen); + +/* + * test if path is a directory + * this function return + * 0-> path exists but it is not a directory + * 1-> path exists and it is a directory + * -1 -> path is unaccessible + */ +static int test_isdir(char *path) +{ + struct stat st; + int res; + + res = stat(path, &st); + if(res < 0 ) + return -1; + + return S_ISDIR(st.st_mode); +} + +int do_create_subvol(int argc, char **argv) +{ + int res, fddst, len, e; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args args; + char *dst = argv[1]; + + res = test_isdir(dst); + if(res >= 0 ){ + fprintf(stderr, "ERROR: '%s' exists\n", dst); + return 12; + } + + newname = strdup(dst); + newname = basename(newname); + dstdir = strdup(dst); + dstdir = dirname(dstdir); + + if( !strcmp(newname,".") || !strcmp(newname,"..") || + strchr(newname, '/') ){ + fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n", + newname); + return 14; + } + + len = strlen(newname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: subvolume name too long ('%s)\n", + newname); + return 14; + } + + fddst = open_file_or_dir(dstdir); + if (fddst < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); + return 12; + } + + printf("Create subvolume '%s/%s'\n", dstdir, newname); + strncpy(args.name, newname, BTRFS_PATH_NAME_MAX); + res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); + e = errno; + + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot create subvolume - %s\n", + strerror(e)); + return 11; + } + + return 0; + +} + +/* + * test if path is a subvolume: + * this function return + * 0-> path exists but it is not a subvolume + * 1-> path exists and it is a subvolume + * -1 -> path is unaccessible + */ +static int test_issubvolume(char *path) +{ + struct stat st; + int res; + + res = stat(path, &st); + if(res < 0 ) + return -1; + + return (st.st_ino == 256) && S_ISDIR(st.st_mode); +} + +int do_delete_subvolume(int argc, char **argv) +{ + int res, fd, len, e; + struct btrfs_ioctl_vol_args args; + char *dname, *vname, *cpath; + char *path = argv[1]; + + res = test_issubvolume(path); + if(res<0){ + fprintf(stderr, "ERROR: error accessing '%s'\n", path); + return 12; + } + if(!res){ + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path); + return 13; + } + + cpath = realpath(path, 0); + dname = strdup(cpath); + dname = dirname(dname); + vname = strdup(cpath); + vname = basename(vname); + free(cpath); + + if( !strcmp(vname,".") || !strcmp(vname,"..") || + strchr(vname, '/') ){ + fprintf(stderr, "ERROR: incorrect subvolume name ('%s')\n", + vname); + return 14; + } + + len = strlen(vname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: snapshot name too long ('%s)\n", + vname); + return 14; + } + + fd = open_file_or_dir(dname); + if (fd < 0) { + close(fd); + fprintf(stderr, "ERROR: can't access to '%s'\n", dname); + return 12; + } + + printf("Delete subvolume '%s/%s'\n", dname, vname); + strncpy(args.name, vname, BTRFS_PATH_NAME_MAX); + res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); + e = errno; + + close(fd); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n", + dname, vname, strerror(e)); + return 11; + } + + return 0; + +} + +int do_subvol_list(int argc, char **argv) +{ + int fd; + int ret; + int print_parent = 0; + char *subvol; + int optind = 1; + + while(1) { + int c = getopt(argc, argv, "p"); + if (c < 0) break; + switch(c) { + case 'p': + print_parent = 1; + optind++; + break; + } + } + + if (argc - optind != 1) { + fprintf(stderr, "ERROR: invalid arguments for subvolume list\n"); + return 1; + } + + subvol = argv[optind]; + + ret = test_issubvolume(subvol); + if (ret < 0) { + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if (!ret) { + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); + return 12; + } + ret = list_subvols(fd, print_parent, 0); + if (ret) + return 19; + return 0; +} + +int do_clone(int argc, char **argv) +{ + char *subvol, *dst; + int res, fd, fddst, len, e, optind = 0, readonly = 0; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args_v2 args; + + memset(&args, 0, sizeof(args)); + + while (1) { + int c = getopt(argc, argv, "r"); + + if (c < 0) + break; + switch (c) { + case 'r': + optind++; + readonly = 1; + break; + default: + fprintf(stderr, + "Invalid arguments for subvolume snapshot\n"); + free(argv); + return 1; + } + } + if (argc - optind != 3) { + fprintf(stderr, "Invalid arguments for subvolume snapshot\n"); + free(argv); + return 1; + } + + subvol = argv[optind+1]; + dst = argv[optind+2]; + + res = test_issubvolume(subvol); + if(res<0){ + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if(!res){ + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + res = test_isdir(dst); + if(res == 0 ){ + fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst); + return 12; + } + + if(res>0){ + newname = strdup(subvol); + newname = basename(newname); + dstdir = dst; + }else{ + newname = strdup(dst); + newname = basename(newname); + dstdir = strdup(dst); + dstdir = dirname(dstdir); + } + + if( !strcmp(newname,".") || !strcmp(newname,"..") || + strchr(newname, '/') ){ + fprintf(stderr, "ERROR: incorrect snapshot name ('%s')\n", + newname); + return 14; + } + + len = strlen(newname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: snapshot name too long ('%s)\n", + newname); + return 14; + } + + fddst = open_file_or_dir(dstdir); + if (fddst < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); + return 12; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + close(fddst); + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); + return 12; + } + + if (readonly) { + args.flags |= BTRFS_SUBVOL_RDONLY; + printf("Create a readonly snapshot of '%s' in '%s/%s'\n", + subvol, dstdir, newname); + } else { + printf("Create a snapshot of '%s' in '%s/%s'\n", + subvol, dstdir, newname); + } + + args.fd = fd; + strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX); + res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); + e = errno; + + close(fd); + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n", + subvol, strerror(e)); + return 11; + } + + return 0; + +} + +int do_get_default_subvol(int nargs, char **argv) +{ + int fd; + int ret; + char *subvol; + + subvol = argv[1]; + + ret = test_issubvolume(subvol); + if (ret < 0) { + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if (!ret) { + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); + return 12; + } + ret = list_subvols(fd, 0, 1); + if (ret) + return 19; + return 0; +} + +int do_set_default_subvol(int nargs, char **argv) +{ + int ret=0, fd, e; + u64 objectid; + char *path = argv[2]; + char *subvolid = argv[1]; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + objectid = (unsigned long long)strtoll(subvolid, NULL, 0); + if (errno == ERANGE) { + fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid); + return 30; + } + ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid); + e = errno; + close(fd); + if( ret < 0 ){ + fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n", + strerror(e)); + return 30; + } + return 0; +} + +int do_find_newer(int argc, char **argv) +{ + int fd; + int ret; + char *subvol; + u64 last_gen; + + subvol = argv[1]; + last_gen = atoll(argv[2]); + + ret = test_issubvolume(subvol); + if (ret < 0) { + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if (!ret) { + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); + return 12; + } + ret = find_updated_files(fd, 0, last_gen); + if (ret) + return 19; + return 0; +} + |