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>
This commit is contained in:
ghigo 2010-01-24 18:00:05 +01:00 committed by Chris Mason
parent 06cbf62fda
commit 6d2cf04247
7 changed files with 1167 additions and 171 deletions

View file

@ -4,7 +4,7 @@ CFLAGS = -g -Werror -Os
objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ 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 \ 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 \ 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 \ CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
@ -17,7 +17,8 @@ bindir = $(prefix)/bin
LIBS=-luuid LIBS=-luuid
progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ 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 # make C=1 to enable sparse
ifdef C ifdef C
@ -36,11 +37,9 @@ all: version $(progs) manpages
version: version:
bash version.sh bash version.sh
btrfs-list: $(objects) btrfs-list.o btrfs: $(objects) btrfs.o btrfs_cmds.o
gcc $(CFLAGS) -o btrfs-list btrfs-list.o $(objects) $(LDFLAGS) $(LIBS) gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o \
$(objects) $(LDFLAGS) $(LIBS)
btrfs-defrag: $(objects) btrfs-defrag.o
gcc $(CFLAGS) -o btrfs-defrag btrfs-defrag.o $(objects) $(LDFLAGS) $(LIBS)
btrfsctl: $(objects) btrfsctl.o btrfsctl: $(objects) btrfsctl.o
gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)

View file

@ -37,115 +37,3 @@
#include "utils.h" #include "utils.h"
#include "version.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;
}

View file

@ -153,39 +153,6 @@ static struct root_info *tree_search(struct rb_root *root, u64 root_id)
return NULL; return NULL;
} }
/*
* 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. * this allocates a new root in the lookup tree.
* *
@ -205,11 +172,12 @@ static int add_root(struct root_lookup *root_lookup,
{ {
struct root_info *ri; struct root_info *ri;
struct rb_node *ret; struct rb_node *ret;
ri = malloc(sizeof(*ri) + name_len); ri = malloc(sizeof(*ri) + name_len + 1);
if (!ri) { if (!ri) {
printf("memory allocation failed\n"); printf("memory allocation failed\n");
exit(1); exit(1);
} }
memset(ri, 0, sizeof(*ri) + name_len + 1);
ri->path = NULL; ri->path = NULL;
ri->dir_id = dir_id; ri->dir_id = dir_id;
ri->root_id = root_id; ri->root_id = root_id;
@ -301,9 +269,9 @@ static int lookup_ino_path(int fd, struct root_info *ri)
if (ri->path) if (ri->path)
return 0; return 0;
memset(&args, 0, sizeof(args));
args.treeid = ri->ref_tree; args.treeid = ri->ref_tree;
args.objectid = ri->dir_id; args.objectid = ri->dir_id;
args.name[0] = '\0';
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
if (ret) { if (ret) {
@ -335,7 +303,7 @@ static int lookup_ino_path(int fd, struct root_info *ri)
return 0; return 0;
} }
static int list_subvols(int fd) int list_subvols(int fd)
{ {
struct root_lookup root_lookup; struct root_lookup root_lookup;
struct rb_node *n; struct rb_node *n;
@ -447,20 +415,5 @@ static int list_subvols(int fd)
n = rb_prev(n); n = rb_prev(n);
} }
printf("%s\n", BTRFS_BUILD_VERSION);
return ret; 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);
}

335
btrfs.c Normal file
View file

@ -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));
}

788
btrfs_cmds.c Normal file
View file

@ -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;
}

30
btrfs_cmds.h Normal file
View file

@ -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);

View file

@ -7,13 +7,16 @@ mandir = $(prefix)/man
man8dir = $(mandir)/man8 man8dir = $(mandir)/man8
MANPAGES = mkfs.btrfs.8.gz btrfsctl.8.gz btrfsck.8.gz btrfs-image.8.gz \ 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) all: $(MANPAGES)
mkfs.btrfs.8.gz: mkfs.btrfs.8.in mkfs.btrfs.8.gz: mkfs.btrfs.8.in
$(GZIP) -n -c mkfs.btrfs.8.in > mkfs.btrfs.8.gz $(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 btrfsctl.8.gz: btrfsctl.8.in
$(GZIP) -n -c btrfsctl.8.in > btrfsctl.8.gz $(GZIP) -n -c btrfsctl.8.in > btrfsctl.8.gz