summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorghigo <ghigo@kreijack.homelinux.net>2010-01-24 18:00:05 +0100
committerChris Mason <chris.mason@oracle.com>2010-03-11 13:45:47 -0500
commit6d2cf042471cc728b5399b2beae54603739bc66a (patch)
tree0d8ea823ec7c1e4676630153eee998cc6fcfb62c
parent06cbf62fda156d1399022158d671353d1a3aeaec (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--Makefile13
-rw-r--r--btrfs-defrag.c112
-rw-r--r--btrfs-list.c55
-rw-r--r--btrfs.c335
-rw-r--r--btrfs_cmds.c788
-rw-r--r--btrfs_cmds.h30
-rw-r--r--man/Makefile5
7 files changed, 1167 insertions, 171 deletions
diff --git a/Makefile b/Makefile
index 10002681..9735fc6e 100644
--- a/Makefile
+++ b/Makefile
@@ -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