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:
parent
06cbf62fda
commit
6d2cf04247
7 changed files with 1167 additions and 171 deletions
13
Makefile
13
Makefile
|
@ -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)
|
||||||
|
|
112
btrfs-defrag.c
112
btrfs-defrag.c
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
55
btrfs-list.c
55
btrfs-list.c
|
@ -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
335
btrfs.c
Normal 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
788
btrfs_cmds.c
Normal 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
30
btrfs_cmds.h
Normal 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);
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue