diff options
author | ghigo <ghigo@kreijack.homelinux.net> | 2010-01-24 18:00:05 +0100 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2010-03-11 13:45:47 -0500 |
commit | 6d2cf042471cc728b5399b2beae54603739bc66a (patch) | |
tree | 0d8ea823ec7c1e4676630153eee998cc6fcfb62c | |
parent | 06cbf62fda156d1399022158d671353d1a3aeaec (diff) |
new util: 'btrfs'
This commit introduces a new command called 'btrfs' for managing
a btrfs filesystem. 'btrfs' handles:
- snapshot/subvolume creation
- adding/removal of volume (ie: disk)
- defragment of a tree
- scan of a device searching a btrfs filesystem
- re-balancing of the chunk on the disks
- listing subvolumes and snapshots
This has also been updated to include the new defrag range ioctl.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | btrfs-defrag.c | 112 | ||||
-rw-r--r-- | btrfs-list.c | 55 | ||||
-rw-r--r-- | btrfs.c | 335 | ||||
-rw-r--r-- | btrfs_cmds.c | 788 | ||||
-rw-r--r-- | btrfs_cmds.h | 30 | ||||
-rw-r--r-- | man/Makefile | 5 |
7 files changed, 1167 insertions, 171 deletions
@@ -4,7 +4,7 @@ CFLAGS = -g -Werror -Os objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ - volumes.o utils.o + volumes.o utils.o btrfs-list.o # CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ @@ -17,7 +17,8 @@ bindir = $(prefix)/bin LIBS=-luuid progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ - btrfs-map-logical btrfs-list btrfs-defrag + btrfs \ + btrfs-map-logical # make C=1 to enable sparse ifdef C @@ -36,11 +37,9 @@ all: version $(progs) manpages version: bash version.sh -btrfs-list: $(objects) btrfs-list.o - gcc $(CFLAGS) -o btrfs-list btrfs-list.o $(objects) $(LDFLAGS) $(LIBS) - -btrfs-defrag: $(objects) btrfs-defrag.o - gcc $(CFLAGS) -o btrfs-defrag btrfs-defrag.o $(objects) $(LDFLAGS) $(LIBS) +btrfs: $(objects) btrfs.o btrfs_cmds.o + gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o \ + $(objects) $(LDFLAGS) $(LIBS) btrfsctl: $(objects) btrfsctl.o gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) diff --git a/btrfs-defrag.c b/btrfs-defrag.c index 9aab3ba4..8f1525a3 100644 --- a/btrfs-defrag.c +++ b/btrfs-defrag.c @@ -37,115 +37,3 @@ #include "utils.h" #include "version.h" -static u64 parse_size(char *s) -{ - int len = strlen(s); - char c; - u64 mult = 1; - - if (!isdigit(s[len - 1])) { - c = tolower(s[len - 1]); - switch (c) { - case 'g': - mult *= 1024; - case 'm': - mult *= 1024; - case 'k': - mult *= 1024; - case 'b': - break; - default: - fprintf(stderr, "Unknown size descriptor %c\n", c); - exit(1); - } - s[len - 1] = '\0'; - } - return atoll(s) * mult; -} - -static void print_usage(void) -{ - printf("usage: btrfs-defrag [-c] [-f] [-s start] [-l len] " - "[-t threshold] file ...\n"); - exit(1); -} - -int main(int ac, char **av) -{ - int fd; - int compress = 0; - int flush = 0; - u64 start = 0; - u64 len = (u64)-1; - u32 thresh = 0; - int i; - int errors = 0; - int ret = 0; - int verbose = 0; - struct btrfs_ioctl_defrag_range_args range; - - while(1) { - int c = getopt(ac, av, "vcfs:l:t:"); - if (c < 0) - break; - switch(c) { - case 'c': - compress = 1; - break; - case 'f': - flush = 1; - break; - case 'v': - verbose = 1; - break; - case 's': - start = parse_size(optarg); - break; - case 'l': - len = parse_size(optarg); - break; - case 't': - thresh = parse_size(optarg); - break; - default: - print_usage(); - return 1; - } - } - if (ac - optind == 0) - print_usage(); - - memset(&range, 0, sizeof(range)); - range.start = start; - range.len = len; - range.extent_thresh = thresh; - if (compress) - range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; - if (flush) - range.flags |= BTRFS_DEFRAG_RANGE_START_IO; - - for (i = optind; i < ac; i++) { - fd = open(av[i], O_RDWR); - if (fd < 0) { - fprintf(stderr, "failed to open %s\n", av[i]); - perror("open:"); - errors++; - continue; - } - ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range); - if (ret) { - fprintf(stderr, "ioctl failed on %s ret %d\n", - av[i], ret); - errors++; - } - close(fd); - } - if (verbose) - printf("%s\n", BTRFS_BUILD_VERSION); - if (errors) { - fprintf(stderr, "total %d failures\n", errors); - exit(1); - } - return 0; -} - diff --git a/btrfs-list.c b/btrfs-list.c index c922f097..6305d3c5 100644 --- a/btrfs-list.c +++ b/btrfs-list.c @@ -154,39 +154,6 @@ static struct root_info *tree_search(struct rb_root *root, u64 root_id) } /* - * helper to open either a file or directory so that - * we can run ioctls on it. - */ -static int open_file_or_dir(const char *fname) -{ - int ret; - struct stat st; - DIR *dirstream; - int fd; - - ret = stat(fname, &st); - if (ret < 0) { - perror("stat:"); - exit(1); - } - if (S_ISDIR(st.st_mode)) { - dirstream = opendir(fname); - if (!dirstream) { - perror("opendir"); - exit(1); - } - fd = dirfd(dirstream); - } else { - fd = open(fname, O_RDWR); - } - if (fd < 0) { - perror("open"); - exit(1); - } - return fd; -} - -/* * this allocates a new root in the lookup tree. * * root_id should be the object id of the root @@ -205,11 +172,12 @@ static int add_root(struct root_lookup *root_lookup, { struct root_info *ri; struct rb_node *ret; - ri = malloc(sizeof(*ri) + name_len); + ri = malloc(sizeof(*ri) + name_len + 1); if (!ri) { printf("memory allocation failed\n"); exit(1); } + memset(ri, 0, sizeof(*ri) + name_len + 1); ri->path = NULL; ri->dir_id = dir_id; ri->root_id = root_id; @@ -301,9 +269,9 @@ static int lookup_ino_path(int fd, struct root_info *ri) if (ri->path) return 0; + memset(&args, 0, sizeof(args)); args.treeid = ri->ref_tree; args.objectid = ri->dir_id; - args.name[0] = '\0'; ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); if (ret) { @@ -335,7 +303,7 @@ static int lookup_ino_path(int fd, struct root_info *ri) return 0; } -static int list_subvols(int fd) +int list_subvols(int fd) { struct root_lookup root_lookup; struct rb_node *n; @@ -447,20 +415,5 @@ static int list_subvols(int fd) n = rb_prev(n); } - printf("%s\n", BTRFS_BUILD_VERSION); return ret; } - -int main(int ac, char **av) -{ - int fd; - - if (ac != 2) { - fprintf(stderr, "usage: btrfs-list mount_point\n"); - exit(1); - } - fd = open_file_or_dir(av[1]); - - return list_subvols(fd); -} - diff --git a/btrfs.c b/btrfs.c new file mode 100644 index 00000000..20f74134 --- /dev/null +++ b/btrfs.c @@ -0,0 +1,335 @@ +/* + * 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 "btrfs_cmds.h" +#include "version.h" + +typedef int (*CommandFunction)(int argc, char **argv); + +struct Command { + CommandFunction func; /* function which implements the command */ + int nargs; /* if == 999, any number of arguments + if >= 0, number of arguments, + if < 0, _minimum_ number of arguments */ + char *verb; /* verb */ + char *help; /* help lines; form the 2nd onward they are + indented */ + + /* the following fields are run-time filled by the program */ + char **cmds; /* array of subcommands */ + int ncmds; /* number of subcommand */ +}; + +static struct Command commands[] = { + + /* + avoid short commands different for the case only + */ + { do_clone, 2, + "subvolume snapshot", "<source> [<dest>/]<name>\n" + "Create a writable snapshot of the subvolume <source> with\n" + "the name <name> in the <dest> directory." + }, + { do_delete_subvolume, 1, + "subvolume delete", "<subvolume>\n" + "Delete the subvolume <subvolume>." + }, + { do_create_subvol, 1, + "subvolume create", "[<dest>/]<name>\n" + "Create a subvolume in <dest> (or the current directory if\n" + "not passed)." + }, + { do_subvol_list, 1, "subvolume list", "<path>\n" + "List the snapshot/subvolume of a filesystem." + }, + + { do_defrag, -1, + "filesystem defragment", "[-vcf] [-s start] [-l len] [-t size] <file>|<dir> [<file>|<dir>...]\n" + "Defragment a file or a directory." + }, + { do_fssync, 1, + "filesystem sync", "<path>\n" + "Force a sync on the filesystem <path>." + }, + { do_resize, 2, + "filesystem resize", "[+/-]<newsize>[gkm]|max <filesystem>\n" + "Resize the file system. If 'max' is passed, the filesystem\n" + "will occupe all available space on the device." + }, + { do_show_filesystem, 999, + "filesystem show", "[<uuid>|<label>]\n" + "Show the info of a btrfs filesystem. If no <uuid> or <label>\n" + "is passed, info of all the btrfs filesystem are shown." + }, + { do_balance, 1, + "filesystem balance", "<path>\n" + "Balance the chunks across the device." + }, + { do_scan, + 999, "device scan", "[<device> [<device>..]\n" + "Scan all device for or the passed device for a btrfs\n" + "filesystem." + }, + { do_add_volume, -1, + "device add", "<dev> [<dev>..] <path>\n" + "Add a device to a filesystem." + }, + { do_remove_volume, -1, + "device delete", "<dev> [<dev>..] <path>\n" + "Remove a device from a filesystem." + }, + /* coming soon + { 2, "filesystem label", "<label> <path>\n" + "Set the label of a filesystem" + } + */ + { 0, 0 , 0 } +}; + +static char *get_prgname(char *programname) +{ + char *np; + np = strrchr(programname,'/'); + if(!np) + np = programname; + else + np++; + + return np; +} + +static void print_help(char *programname, struct Command *cmd) +{ + char *pc; + + printf("\t%s %s ", programname, cmd->verb ); + + for(pc = cmd->help; *pc; pc++){ + putchar(*pc); + if(*pc == '\n') + printf("\t\t"); + } + putchar('\n'); +} + +static void help(char *np) +{ + struct Command *cp; + + printf("Usage:\n"); + for( cp = commands; cp->verb; cp++ ) + print_help(np, cp); + + printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np); + printf("\n%s\n", BTRFS_BUILD_VERSION); +} + +static int split_command(char *cmd, char ***commands) +{ + int c, l; + char *p, *s; + + for( *commands = 0, l = c = 0, p = s = cmd ; ; p++, l++ ){ + if ( *p && *p != ' ' ) + continue; + + /* c + 2 so that we have room for the null */ + (*commands) = realloc( (*commands), sizeof(char *)*(c + 2)); + (*commands)[c] = strndup(s, l); + c++; + l = 0; + s = p+1; + if( !*p ) break; + } + + (*commands)[c] = 0; + return c; +} + +/* + This function checks if the passed command is ambiguous +*/ +static int check_ambiguity(struct Command *cmd, char **argv){ + int i; + struct Command *cp; + /* check for ambiguity */ + for( i = 0 ; i < cmd->ncmds ; i++ ){ + int match; + for( match = 0, cp = commands; cp->verb; cp++ ){ + int j, skip; + char *s1, *s2; + + if( cp->ncmds < i ) + continue; + + for( skip = 0, j = 0 ; j < i ; j++ ) + if( strcmp(cmd->cmds[j], cp->cmds[j])){ + skip=1; + break; + } + if(skip) + continue; + + if( !strcmp(cmd->cmds[i], cp->cmds[i])) + continue; + for(s2 = cp->cmds[i], s1 = argv[i+1]; + *s1 == *s2 && *s1; s1++, s2++ ) ; + if( !*s1 ) + match++; + } + if(match){ + int j; + fprintf(stderr, "ERROR: in command '"); + for( j = 0 ; j <= i ; j++ ) + fprintf(stderr, "%s%s",j?" ":"", argv[j+1]); + fprintf(stderr, "', '%s' is ambiguous\n",argv[j]); + return -2; + } + } + return 0; +} + +/* + + This function perform the following jobs: + - show the help if '--help' or 'help' or '-h' are passed + - verify that a command is not ambiguous, otherwise show which + part of the command is ambiguous + - if after a (even partial) command there is '--help' show the help + for all the matching commands + - if the command doesn't' match show an error + - finally, if a command match, they return which command is matched and + the arguments + + The function return 0 in case of help is requested; <0 in case + of uncorrect command; >0 in case of matching commands + argc, argv are the arg-counter and arg-vector (input) + *nargs_ is the number of the arguments after the command (output) + **cmd_ is the invoked command (output) + ***args_ are the arguments after the command + +*/ +static int parse_args(int argc, char **argv, + CommandFunction *func_, + int *nargs_, char **cmd_, char ***args_ ) +{ + struct Command *cp; + struct Command *matchcmd=0; + char *prgname = get_prgname(argv[0]); + int i=0, helprequested=0; + + if( argc < 2 || !strcmp(argv[1], "help") || + !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + help(prgname); + return 0; + } + + for( cp = commands; cp->verb; cp++ ) + if( !cp->ncmds) + cp->ncmds = split_command(cp->verb, &(cp->cmds)); + + for( cp = commands; cp->verb; cp++ ){ + int match; + + if( argc-1 < cp->ncmds ) + continue; + for( match = 1, i = 0 ; i < cp->ncmds ; i++ ){ + char *s1, *s2; + s1 = cp->cmds[i]; + s2 = argv[i+1]; + + for(s2 = cp->cmds[i], s1 = argv[i+1]; + *s1 == *s2 && *s1; + s1++, s2++ ) ; + if( *s1 ){ + match=0; + break; + } + } + + /* If you understand why this code works ... + you are a genious !! */ + if(argc>i+1 && !strcmp(argv[i+1],"--help")){ + if(!helprequested) + printf("Usage:\n"); + print_help(prgname, cp); + helprequested=1; + continue; + } + + if(!match) + continue; + + matchcmd = cp; + *nargs_ = argc-matchcmd->ncmds-1; + *cmd_ = matchcmd->verb; + *args_ = argv+matchcmd->ncmds+1; + *func_ = cp->func; + + break; + } + + if(helprequested){ + printf("\n%s\n", BTRFS_BUILD_VERSION); + return 0; + } + + if(!matchcmd){ + fprintf( stderr, "ERROR: unknown command '%s'\n",argv[1]); + help(prgname); + return -1; + } + + if(check_ambiguity(matchcmd, argv)) + return -2; + + /* check the number of argument */ + if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){ + fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n", + matchcmd->verb, -matchcmd->nargs); + return -2; + } + if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999 ){ + fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n", + matchcmd->verb, matchcmd->nargs); + return -2; + } + + return 1; +} + +int main(int ac, char **av ) +{ + + char *cmd=0, **args=0; + int nargs=0, r; + CommandFunction func=0; + + r = parse_args(ac, av, &func, &nargs, &cmd, &args); + if( r <= 0 ){ + /* error or no command to parse*/ + exit(-r); + } + + exit(func(nargs, args)); + +} + diff --git a/btrfs_cmds.c b/btrfs_cmds.c new file mode 100644 index 00000000..3a21be31 --- /dev/null +++ b/btrfs_cmds.c @@ -0,0 +1,788 @@ +/* + * 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 <sys/ioctl.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <libgen.h> +#include <limits.h> +#include <uuid/uuid.h> +#include <ctype.h> + +#undef ULONG_MAX + +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" +#include "ioctl.h" +#include "volumes.h" + +#include "btrfs_cmds.h" + +#ifdef __CHECKER__ +#define BLKGETSIZE64 0 +#define BTRFS_IOC_SNAP_CREATE 0 +#define BTRFS_VOL_NAME_MAX 255 +struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; +static inline int ioctl(int fd, int define, void *arg) { return 0; } +#endif + +/* + * 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); + +} + +/* + * 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); + +} + +static int open_file_or_dir(const char *fname) +{ + int ret; + struct stat st; + DIR *dirstream; + int fd; + + ret = stat(fname, &st); + if (ret < 0) { + return -1; + } + if (S_ISDIR(st.st_mode)) { + dirstream = opendir(fname); + if (!dirstream) { + return -2; + } + fd = dirfd(dirstream); + } else { + fd = open(fname, O_RDWR); + } + if (fd < 0) { + return -3; + } + return fd; +} + +static u64 parse_size(char *s) +{ + int len = strlen(s); + char c; + u64 mult = 1; + + if (!isdigit(s[len - 1])) { + c = tolower(s[len - 1]); + switch (c) { + case 'g': + mult *= 1024; + case 'm': + mult *= 1024; + case 'k': + mult *= 1024; + case 'b': + break; + default: + fprintf(stderr, "Unknown size descriptor %c\n", c); + exit(1); + } + s[len - 1] = '\0'; + } + return atoll(s) * mult; +} + +int do_defrag(int ac, char **avp) +{ + int fd; + int compress = 0; + int flush = 0; + u64 start = 0; + u64 len = (u64)-1; + u32 thresh = 0; + int i; + int errors = 0; + int ret = 0; + int verbose = 0; + int fancy_ioctl = 0; + struct btrfs_ioctl_defrag_range_args range; + char **av; + + /* + * getopt expects av[0] to be the program name and it seems + * to get confused when this isn't the case + */ + av = malloc((ac + 2) * sizeof(char *)); + av[0] = "defrag"; + av[ac + 1] = NULL; + memcpy(av + 1, avp, ac * sizeof(char *)); + ac += 1; + + optind = 1; + while(1) { + int c = getopt(ac, av, "vcfs:l:t:"); + if (c < 0) + break; + switch(c) { + case 'c': + compress = 1; + fancy_ioctl = 1; + break; + case 'f': + flush = 1; + fancy_ioctl = 1; + break; + case 'v': + verbose = 1; + break; + case 's': + start = parse_size(optarg); + fancy_ioctl = 1; + break; + case 'l': + len = parse_size(optarg); + fancy_ioctl = 1; + break; + case 't': + thresh = parse_size(optarg); + fancy_ioctl = 1; + break; + default: + fprintf(stderr, "Invalid arguments for defragment\n"); + free(av); + return 1; + } + } + if (ac - optind == 0) { + fprintf(stderr, "Invalid arguments for defragment\n"); + free(av); + return 1; + } + + memset(&range, 0, sizeof(range)); + range.start = start; + range.len = len; + range.extent_thresh = thresh; + if (compress) + range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; + if (flush) + range.flags |= BTRFS_DEFRAG_RANGE_START_IO; + + for (i = optind; i < ac; i++) { + if (verbose) + printf("%s\n", av[i]); + fd = open_file_or_dir(av[i]); + if (fd < 0) { + fprintf(stderr, "failed to open %s\n", av[i]); + perror("open:"); + errors++; + continue; + } + if (!fancy_ioctl) { + ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL); + } else { + ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range); + if (ret && errno == ENOTTY) { + fprintf(stderr, "defrag range ioctl not " + "supported in this kernel, please try " + "without any options.\n"); + errors++; + break; + } + } + if (ret) { + fprintf(stderr, "ioctl failed on %s ret %d errno %d\n", + av[i], ret, errno); + errors++; + } + close(fd); + } + if (verbose) + printf("%s\n", BTRFS_BUILD_VERSION); + if (errors) { + fprintf(stderr, "total %d failures\n", errors); + exit(1); + } + + free(av); + return errors + 20; +} + +int do_subvol_list(int argc, char **argv) +{ + int fd; + int ret; + char *subvol; + + subvol = argv[0]; + + 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); + if (ret) + return 19; + return 0; +} + +int do_clone(int argc, char **argv) +{ + char *subvol, *dst; + int res, fd, fddst, len; + char *newname; + char *dstdir; + + subvol = argv[0]; + dst = argv[1]; + struct btrfs_ioctl_vol_args args; + + 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; + } + + printf("Create a snapshot of '%s' in '%s/%s'\n", + subvol, dstdir, newname); + args.fd = fd; + strcpy(args.name, newname); + res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args); + + close(fd); + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot snapshot '%s'\n",subvol); + return 11; + } + + return 0; + +} + +int do_delete_subvolume(int argc, char **argv) +{ + int res, fd, len; + struct btrfs_ioctl_vol_args args; + char *dname, *vname, *cpath; + char *path = argv[0]; + + 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); + strcpy(args.name, vname); + res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); + + close(fd); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot delete '%s/%s'\n",dname, vname); + return 11; + } + + return 0; + +} + +int do_create_subvol(int argc, char **argv) +{ + int res, fddst, len; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args args; + char *dst = argv[0]; + + 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); + strcpy(args.name, newname); + res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); + + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot create subvolume\n"); + return 11; + } + + return 0; + +} + +int do_fssync(int argc, char **argv) +{ + int fd, res; + char *path = argv[0]; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + printf("FSSync '%s'\n", path); + res = ioctl(fd, BTRFS_IOC_SYNC); + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to fs-syncing '%s'\n", path); + return 16; + } + + return 0; +} + +int do_scan(int nargs, char **argv) +{ + int i, fd; + if(!nargs){ + int ret; + + printf("Scanning for Btrfs filesystems\n"); + ret = btrfs_scan_one_dir("/dev", 1); + if (ret){ + fprintf(stderr, "ERROR: error %d while scanning\n", ret); + return 18; + } + return 0; + } + + fd = open("/dev/btrfs-control", O_RDWR); + if (fd < 0) { + perror("failed to open /dev/btrfs-control"); + return 10; + } + + for( i = 0 ; i < nargs ; i++ ){ + struct btrfs_ioctl_vol_args args; + int ret; + + printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]); + + strcpy(args.name, argv[i]); + /* + * FIXME: which are the error code returned by this ioctl ? + * it seems that is impossible to understand if there no is + * a btrfs filesystem from an I/O error !!! + */ + ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); + + if( ret < 0 ){ + close(fd); + fprintf(stderr, "ERROR: unable to scan the device '%s'\n", argv[i]); + return 11; + } + } + + close(fd); + return 0; + +} + +int do_resize(int argc, char **argv) +{ + + struct btrfs_ioctl_vol_args args; + int fd, res, len; + char *amount=argv[0], *path=argv[1]; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + len = strlen(amount); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: size value too long ('%s)\n", + amount); + return 14; + } + + printf("Resize '%s' of '%s'\n", path, amount); + strcpy(args.name, amount); + res = ioctl(fd, BTRFS_IOC_RESIZE, &args); + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to resize '%s'\n", path); + return 30; + } + return 0; +} + +static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) +{ + struct list_head *cur; + struct btrfs_device *device; + + list_for_each(cur, &fs_devices->devices) { + device = list_entry(cur, struct btrfs_device, dev_list); + if ((device->label && strcmp(device->label, search) == 0) || + strcmp(device->name, search) == 0) + return 1; + } + return 0; +} + +static void print_one_uuid(struct btrfs_fs_devices *fs_devices) +{ + char uuidbuf[37]; + struct list_head *cur; + struct btrfs_device *device; + char *super_bytes_used; + u64 devs_found = 0; + u64 total; + + uuid_unparse(fs_devices->fsid, uuidbuf); + device = list_entry(fs_devices->devices.next, struct btrfs_device, + dev_list); + if (device->label && device->label[0]) + printf("Label: '%s' ", device->label); + else + printf("Label: none "); + + super_bytes_used = pretty_sizes(device->super_bytes_used); + + total = device->total_devs; + printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, + (unsigned long long)total, super_bytes_used); + + free(super_bytes_used); + + list_for_each(cur, &fs_devices->devices) { + char *total_bytes; + char *bytes_used; + device = list_entry(cur, struct btrfs_device, dev_list); + total_bytes = pretty_sizes(device->total_bytes); + bytes_used = pretty_sizes(device->bytes_used); + printf("\tdevid %4llu size %s used %s path %s\n", + (unsigned long long)device->devid, + total_bytes, bytes_used, device->name); + free(total_bytes); + free(bytes_used); + devs_found++; + } + if (devs_found < total) { + printf("\t*** Some devices missing\n"); + } + printf("\n"); +} + +int do_show_filesystem(int argc, char **argv) +{ + struct list_head *all_uuids; + struct btrfs_fs_devices *fs_devices; + struct list_head *cur_uuid; + char *search = argv[0]; + int ret; + + ret = btrfs_scan_one_dir("/dev", 0); + if (ret){ + fprintf(stderr, "ERROR: error %d while scanning\n", ret); + return 18; + } + + all_uuids = btrfs_scanned_uuids(); + list_for_each(cur_uuid, all_uuids) { + fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices, + list); + if (search && uuid_search(fs_devices, search) == 0) + continue; + print_one_uuid(fs_devices); + } + printf("%s\n", BTRFS_BUILD_VERSION); + return 0; +} + +int do_add_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0; + + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + return 12; + } + + for(i=0 ; i < (nargs-1) ; i++ ){ + struct btrfs_ioctl_vol_args ioctl_args; + int devfd, res; + u64 dev_block_count = 0; + struct stat st; + + devfd = open(args[i], O_RDWR); + if (!devfd) { + fprintf(stderr, "ERROR: Unable to open device '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + ret = fstat(devfd, &st); + if (ret) { + fprintf(stderr, "ERROR: Unable to stat '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + if (!S_ISBLK(st.st_mode)) { + fprintf(stderr, "ERROR: '%s' is not a block device\n", args[i]); + close(devfd); + ret++; + continue; + } + + res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count); + if (res) { + fprintf(stderr, "ERROR: Unable to init '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + close(devfd); + + strcpy(ioctl_args.name, args[i]); + res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); + if(res<0){ + fprintf(stderr, "ERROR: error adding the device '%s'\n", args[i]); + ret++; + } + + } + + close(fdmnt); + if( ret) + return ret+20; + else + return 0; + +} + +int do_balance(int argc, char **argv) +{ + + int fdmnt, ret=0; + char *path = argv[0]; + struct btrfs_ioctl_vol_args args; + + fdmnt = open_file_or_dir(path); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + memset(&args, 0, sizeof(args)); + ret = ioctl(fdmnt, BTRFS_IOC_BALANCE, &args); + close(fdmnt); + if(ret<0){ + fprintf(stderr, "ERROR: balancing '%s'\n", path); + + return 19; + } + return 0; +} +int do_remove_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0; + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + return 12; + } + + for(i=0 ; i < (nargs-1) ; i++ ){ + struct btrfs_ioctl_vol_args arg; + int res; + + strcpy(arg.name, args[i]); + res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); + if(res<0){ + fprintf(stderr, "ERROR: error removing the device '%s'\n", args[i]); + ret++; + } + } + + close(fdmnt); + if( ret) + return ret+20; + else + return 0; +} diff --git a/btrfs_cmds.h b/btrfs_cmds.h new file mode 100644 index 00000000..cfdbde23 --- /dev/null +++ b/btrfs_cmds.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/* btrfs_cmds.c*/ +int do_clone(int nargs, char **argv); +int do_delete_subvolume(int nargs, char **argv); +int do_create_subvol(int nargs, char **argv); +int do_fssync(int nargs, char **argv); +int do_defrag(int argc, char **argv); +int do_show_filesystem(int nargs, char **argv); +int do_add_volume(int nargs, char **args); +int do_balance(int nargs, char **argv); +int do_remove_volume(int nargs, char **args); +int do_scan(int nargs, char **argv); +int do_resize(int nargs, char **argv); +int do_subvol_list(int nargs, char **argv); +int list_subvols(int fd); diff --git a/man/Makefile b/man/Makefile index 4e8893b2..4a90b759 100644 --- a/man/Makefile +++ b/man/Makefile @@ -7,13 +7,16 @@ mandir = $(prefix)/man man8dir = $(mandir)/man8 MANPAGES = mkfs.btrfs.8.gz btrfsctl.8.gz btrfsck.8.gz btrfs-image.8.gz \ - btrfs-show.8.gz + btrfs-show.8.gz btrfs.8.gz all: $(MANPAGES) mkfs.btrfs.8.gz: mkfs.btrfs.8.in $(GZIP) -n -c mkfs.btrfs.8.in > mkfs.btrfs.8.gz +btrfs.8.gz: btrfs.8.in + $(GZIP) -n -c btrfs.8.in > btrfs.8.gz + btrfsctl.8.gz: btrfsctl.8.in $(GZIP) -n -c btrfsctl.8.in > btrfsctl.8.gz |