new util: 'btrfs'
authorghigo <ghigo@kreijack.homelinux.net>
Sun, 24 Jan 2010 17:00:05 +0000 (18:00 +0100)
committerChris Mason <chris.mason@oracle.com>
Thu, 11 Mar 2010 18:45:47 +0000 (13:45 -0500)
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>
Makefile
btrfs-defrag.c
btrfs-list.c
btrfs.c [new file with mode: 0644]
btrfs_cmds.c [new file with mode: 0644]
btrfs_cmds.h [new file with mode: 0644]
man/Makefile

index 1000268..9735fc6 100644 (file)
--- 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)
index 9aab3ba..8f1525a 100644 (file)
 #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;
-}
-
index c922f09..6305d3c 100644 (file)
@@ -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 (file)
index 0000000..20f7413
--- /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 (file)
index 0000000..3a21be3
--- /dev/null
@@ -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 (file)
index 0000000..cfdbde2
--- /dev/null
@@ -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);
index 4e8893b..4a90b75 100644 (file)
@@ -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