Merge branch 'for-chris' of git://repo.or.cz/btrfs-progs-unstable/devel into raid56

Conflicts:
	ctree.h

Signed-off-by: Chris Mason <chris.mason@fusionio.com>
master
Chris Mason 2013-02-06 12:42:24 -05:00
commit 7b1c567c84
45 changed files with 2354 additions and 480 deletions

105
Makefile
View File

@ -8,7 +8,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
send-stream.o send-utils.o qgroup.o raid6.o
cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
cmds-quota.o cmds-qgroup.o
cmds-quota.o cmds-qgroup.o cmds-replace.o
CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
-Wuninitialized -Wshadow -Wundef
@ -20,98 +20,139 @@ bindir = $(prefix)/bin
LIBS=-luuid -lm
RESTORE_LIBS=-lz
ifeq ("$(origin V)", "command line")
BUILD_VERBOSE = $(V)
endif
ifndef BUILD_VERBOSE
BUILD_VERBOSE = 0
endif
ifeq ($(BUILD_VERBOSE),1)
Q =
else
Q = @
endif
MAKEOPTS = --no-print-directory Q=$(Q)
progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \
btrfs btrfs-map-logical btrfs-image btrfs-zero-log btrfs-convert \
btrfs-find-root btrfs-restore btrfstune
btrfs-find-root btrfs-restore btrfstune btrfs-show-super
# make C=1 to enable sparse
ifdef C
check = sparse $(CHECKFLAGS)
else
check = ls
check = true
endif
.c.o:
$(check) $<
$(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $<
$(Q)$(check) $<
@echo " [CC] $@"
$(Q)$(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $<
all: version $(progs) manpages
all: version.h $(progs) manpages
version:
bash version.sh
version.h:
$(Q)bash version.sh
btrfs: $(objects) btrfs.o help.o common.o $(cmds_objects)
$(CC) $(CFLAGS) -o btrfs btrfs.o help.o common.o $(cmds_objects) \
btrfs: $(objects) btrfs.o help.o $(cmds_objects)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs btrfs.o help.o $(cmds_objects) \
$(objects) $(LDFLAGS) $(LIBS) -lpthread
calc-size: $(objects) calc-size.o
$(CC) $(CFLAGS) -o calc-size calc-size.o $(objects) $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o calc-size calc-size.o $(objects) $(LDFLAGS) $(LIBS)
btrfs-find-root: $(objects) find-root.o
$(CC) $(CFLAGS) -o btrfs-find-root find-root.o $(objects) $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-find-root find-root.o $(objects) $(LDFLAGS) $(LIBS)
btrfs-restore: $(objects) restore.o
$(CC) $(CFLAGS) -o btrfs-restore restore.o $(objects) $(LDFLAGS) $(LIBS) $(RESTORE_LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-restore restore.o $(objects) $(LDFLAGS) $(LIBS) $(RESTORE_LIBS)
btrfsctl: $(objects) btrfsctl.o
$(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
btrfs-vol: $(objects) btrfs-vol.o
$(CC) $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS)
btrfs-show: $(objects) btrfs-show.o
$(CC) $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-show btrfs-show.o $(objects) $(LDFLAGS) $(LIBS)
btrfsck: $(objects) btrfsck.o
$(CC) $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
mkfs.btrfs: $(objects) mkfs.o
$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS) -lblkid
btrfs-debug-tree: $(objects) debug-tree.o
$(CC) $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
btrfs-zero-log: $(objects) btrfs-zero-log.o
$(CC) $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-zero-log $(objects) btrfs-zero-log.o $(LDFLAGS) $(LIBS)
btrfs-show-super: $(objects) btrfs-show-super.o
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-show-super $(objects) btrfs-show-super.o $(LDFLAGS) $(LIBS)
btrfs-select-super: $(objects) btrfs-select-super.o
$(CC) $(CFLAGS) -o btrfs-select-super $(objects) btrfs-select-super.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-select-super $(objects) btrfs-select-super.o $(LDFLAGS) $(LIBS)
btrfstune: $(objects) btrfstune.o
$(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
btrfs-map-logical: $(objects) btrfs-map-logical.o
$(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS)
btrfs-corrupt-block: $(objects) btrfs-corrupt-block.o
$(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS)
btrfs-image: $(objects) btrfs-image.o
$(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)
dir-test: $(objects) dir-test.o
$(CC) $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)
quick-test: $(objects) quick-test.o
$(CC) $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
btrfs-convert: $(objects) convert.o
$(CC) $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lcom_err $(LDFLAGS) $(LIBS)
ioctl-test: $(objects) ioctl-test.o
$(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
@echo " [LD] $@"
$(Q)$(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
manpages:
cd man; $(MAKE)
$(Q)$(MAKE) $(MAKEOPTS) -C man
install-man:
cd man; $(MAKE) install
clean :
rm -f $(progs) cscope.out *.o .*.d btrfs-convert btrfs-image btrfs-select-super \
@echo "Cleaning"
$(Q)rm -f $(progs) cscope.out *.o .*.d btrfs-convert btrfs-image btrfs-select-super \
btrfs-zero-log btrfstune dir-test ioctl-test quick-test version.h
cd man; $(MAKE) clean
$(Q)$(MAKE) $(MAKEOPTS) -C man $@
install: $(progs) install-man
$(INSTALL) -m755 -d $(DESTDIR)$(bindir)

View File

@ -491,6 +491,11 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
int ret;
root = open_ctree(input, 0, 0);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
BUG_ON(root->nodesize != root->leafsize);
ret = metadump_init(&metadump, root, out, num_threads,

View File

@ -46,52 +46,6 @@ struct root_lookup {
struct rb_root root;
};
/*
* one of these for each root we find.
*/
struct root_info {
struct rb_node rb_node;
struct rb_node sort_node;
/* this root's id */
u64 root_id;
/* equal the offset of the root's key */
u64 root_offset;
/* flags of the root */
u64 flags;
/* the id of the root that references this one */
u64 ref_tree;
/* the dir id we're in from ref_tree */
u64 dir_id;
u64 top_id;
/* generation when the root is created or last updated */
u64 gen;
/* creation generation of this root in sec*/
u64 ogen;
/* creation time of this root in sec*/
time_t otime;
u8 uuid[BTRFS_UUID_SIZE];
/* path from the subvol we live in to this root, including the
* root's name. This is null until we do the extra lookup ioctl.
*/
char *path;
/* the name of this root in the directory it lives in */
char *name;
char *full_path;
};
struct {
char *name;
char *column_name;
@ -100,12 +54,12 @@ struct {
{
.name = "ID",
.column_name = "ID",
.need_print = 1,
.need_print = 0,
},
{
.name = "gen",
.column_name = "Gen",
.need_print = 1,
.need_print = 0,
},
{
.name = "cgen",
@ -120,13 +74,18 @@ struct {
{
.name = "top level",
.column_name = "Top Level",
.need_print = 1,
.need_print = 0,
},
{
.name = "otime",
.column_name = "OTime",
.need_print = 0,
},
{
.name = "parent_uuid",
.column_name = "Parent UUID",
.need_print = 0,
},
{
.name = "uuid",
.column_name = "UUID",
@ -135,7 +94,7 @@ struct {
{
.name = "path",
.column_name = "Path",
.need_print = 1,
.need_print = 0,
},
{
.name = NULL,
@ -435,7 +394,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
static int update_root(struct root_lookup *root_lookup,
u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
time_t ot, void *uuid)
time_t ot, void *uuid, void *puuid)
{
struct root_info *ri;
@ -472,6 +431,8 @@ static int update_root(struct root_lookup *root_lookup,
ri->otime = ot;
if (uuid)
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
if (puuid)
memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
return 0;
}
@ -489,17 +450,18 @@ static int update_root(struct root_lookup *root_lookup,
* gen: the current generation of the root
* ot: the original time(create time) of the root
* uuid: uuid of the root
* puuid: uuid of the root parent if any
*/
static int add_root(struct root_lookup *root_lookup,
u64 root_id, u64 ref_tree, u64 root_offset, u64 flags,
u64 dir_id, char *name, int name_len, u64 ogen, u64 gen,
time_t ot, void *uuid)
time_t ot, void *uuid, void *puuid)
{
struct root_info *ri;
int ret;
ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags,
dir_id, name, name_len, ogen, gen, ot, uuid);
dir_id, name, name_len, ogen, gen, ot, uuid, puuid);
if (!ret)
return 0;
@ -537,9 +499,12 @@ static int add_root(struct root_lookup *root_lookup,
if (ot)
ri->otime = ot;
if (uuid)
if (uuid)
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
if (puuid)
memcpy(&ri->puuid, puuid, BTRFS_UUID_SIZE);
ret = root_tree_insert(root_lookup, ri);
if (ret) {
printf("failed to insert tree %llu\n", (unsigned long long)root_id);
@ -599,6 +564,12 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
while (1) {
char *tmp;
u64 next;
/*
* ref_tree = 0 indicates the subvolumes
* has been deleted.
*/
if (!found->ref_tree)
return -ENOENT;
int add_len = strlen(found->path);
/* room for / and for null */
@ -627,29 +598,22 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
break;
}
/*
* if the ref_tree = BTRFS_FS_TREE_OBJECTID,
* we are at the top
*/
if (next == BTRFS_FS_TREE_OBJECTID) {
char p[] = "<FS_TREE>";
add_len = strlen(p);
len = strlen(full_path);
tmp = malloc(len + add_len + 2);
memcpy(tmp + add_len + 1, full_path, len);
tmp[add_len] = '/';
memcpy(tmp, p, add_len);
free(full_path);
full_path = tmp;
ri->top_id = next;
break;
}
/*
* if the ref_tree wasn't in our tree of roots, we're
* at the top
*/
* if the ref_tree wasn't in our tree of roots, the
* subvolume was deleted.
*/
found = root_tree_search(rl, next);
if (!found) {
ri->top_id = next;
break;
}
if (!found)
return -ENOENT;
}
ri->full_path = full_path;
@ -672,6 +636,9 @@ static int lookup_ino_path(int fd, struct root_info *ri)
if (ri->path)
return 0;
if (!ri->ref_tree)
return -ENOENT;
memset(&args, 0, sizeof(args));
args.treeid = ri->ref_tree;
args.objectid = ri->dir_id;
@ -679,6 +646,10 @@ static int lookup_ino_path(int fd, struct root_info *ri)
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
e = errno;
if (ret) {
if (e == ENOENT) {
ri->ref_tree = 0;
return -ENOENT;
}
fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n",
(unsigned long long)ri->ref_tree,
strerror(e));
@ -1021,6 +992,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
int i;
time_t t;
u8 uuid[BTRFS_UUID_SIZE];
u8 puuid[BTRFS_UUID_SIZE];
root_lookup_init(root_lookup);
memset(&args, 0, sizeof(args));
@ -1073,7 +1045,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
add_root(root_lookup, sh.objectid, sh.offset,
0, 0, dir_id, name, name_len, 0, 0, 0,
NULL);
NULL, NULL);
} else if (sh.type == BTRFS_ROOT_ITEM_KEY) {
ri = (struct btrfs_root_item *)(args.buf + off);
gen = btrfs_root_generation(ri);
@ -1083,15 +1055,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
t = ri->otime.sec;
ogen = btrfs_root_otransid(ri);
memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE);
} else {
t = 0;
ogen = 0;
memset(uuid, 0, BTRFS_UUID_SIZE);
memset(puuid, 0, BTRFS_UUID_SIZE);
}
add_root(root_lookup, sh.objectid, 0,
sh.offset, flags, 0, NULL, 0, ogen,
gen, t, uuid);
gen, t, uuid, puuid);
}
off += sh.len;
@ -1174,6 +1148,34 @@ static int filter_topid_equal(struct root_info *ri, u64 data)
return ri->top_id == data;
}
static int filter_full_path(struct root_info *ri, u64 data)
{
if (ri->full_path && ri->top_id != data) {
char *tmp;
char p[] = "<FS_TREE>";
int add_len = strlen(p);
int len = strlen(ri->full_path);
tmp = malloc(len + add_len + 2);
if (!tmp) {
fprintf(stderr, "memory allocation failed\n");
exit(1);
}
memcpy(tmp + add_len + 1, ri->full_path, len);
tmp[len + add_len + 1] = '\0';
tmp[add_len] = '/';
memcpy(tmp, p, add_len);
free(ri->full_path);
ri->full_path = tmp;
}
return 1;
}
static int filter_by_parent(struct root_info *ri, u64 data)
{
return !uuid_compare(ri->puuid, (u8 *)data);
}
static btrfs_list_filter_func all_filter_funcs[] = {
[BTRFS_LIST_FILTER_ROOTID] = filter_by_rootid,
[BTRFS_LIST_FILTER_SNAPSHOT_ONLY] = filter_snapshot,
@ -1185,6 +1187,8 @@ static btrfs_list_filter_func all_filter_funcs[] = {
[BTRFS_LIST_FILTER_CGEN_LESS] = filter_cgen_less,
[BTRFS_LIST_FILTER_CGEN_EQUAL] = filter_cgen_equal,
[BTRFS_LIST_FILTER_TOPID_EQUAL] = filter_topid_equal,
[BTRFS_LIST_FILTER_FULL_PATH] = filter_full_path,
[BTRFS_LIST_FILTER_BY_PARENT] = filter_by_parent,
};
struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
@ -1267,12 +1271,11 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
struct root_lookup *sort_tree,
struct btrfs_list_filter_set *filter_set,
struct btrfs_list_comparer_set *comp_set,
int fd)
u64 top_id)
{
struct rb_node *n;
struct root_info *entry;
int ret;
u64 top_id = btrfs_list_get_path_rootid(fd);
root_lookup_init(sort_tree);
@ -1280,10 +1283,13 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols,
while (n) {
entry = rb_entry(n, struct root_info, rb_node);
resolve_root(all_subvols, entry, top_id);
ret = resolve_root(all_subvols, entry, top_id);
if (ret == -ENOENT)
goto skip;
ret = filter_root(entry, filter_set);
if (ret)
sort_tree_insert(sort_tree, entry, comp_set);
skip:
n = rb_prev(n);
}
}
@ -1298,7 +1304,7 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
int ret;
entry = rb_entry(n, struct root_info, rb_node);
ret = lookup_ino_path(fd, entry);
if(ret < 0)
if (ret && ret != -ENOENT)
return ret;
n = rb_next(n);
}
@ -1345,6 +1351,13 @@ static void print_subvolume_column(struct root_info *subv,
uuid_unparse(subv->uuid, uuidparse);
printf("%s", uuidparse);
break;
case BTRFS_LIST_PUUID:
if (uuid_is_null(subv->puuid))
strcpy(uuidparse, "-");
else
uuid_unparse(subv->puuid, uuidparse);
printf("%s", uuidparse);
break;
case BTRFS_LIST_PATH:
BUG_ON(!subv->full_path);
printf("%s", subv->full_path);
@ -1354,6 +1367,22 @@ static void print_subvolume_column(struct root_info *subv,
}
}
static void print_single_volume_info_raw(struct root_info *subv, char *raw_prefix)
{
int i;
for (i = 0; i < BTRFS_LIST_ALL; i++) {
if (!btrfs_list_columns[i].need_print)
continue;
if (raw_prefix)
printf("%s",raw_prefix);
print_subvolume_column(subv, i);
}
printf("\n");
}
static void print_single_volume_info_table(struct root_info *subv)
{
int i;
@ -1420,34 +1449,37 @@ static void print_all_volume_info_tab_head()
}
static void print_all_volume_info(struct root_lookup *sorted_tree,
int is_tab_result)
int layout, char *raw_prefix)
{
struct rb_node *n;
struct root_info *entry;
if (is_tab_result)
if (layout == BTRFS_LIST_LAYOUT_TABLE)
print_all_volume_info_tab_head();
n = rb_first(&sorted_tree->root);
while (n) {
entry = rb_entry(n, struct root_info, sort_node);
if (is_tab_result)
print_single_volume_info_table(entry);
else
switch (layout) {
case BTRFS_LIST_LAYOUT_DEFAULT:
print_single_volume_info_default(entry);
break;
case BTRFS_LIST_LAYOUT_TABLE:
print_single_volume_info_table(entry);
break;
case BTRFS_LIST_LAYOUT_RAW:
print_single_volume_info_raw(entry, raw_prefix);
break;
}
n = rb_next(n);
}
}
int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
struct btrfs_list_comparer_set *comp_set,
int is_tab_result)
int btrfs_list_subvols(int fd, struct root_lookup *root_lookup)
{
struct root_lookup root_lookup;
struct root_lookup root_sort;
int ret;
ret = __list_subvol_search(fd, &root_lookup);
ret = __list_subvol_search(fd, root_lookup);
if (ret) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
strerror(errno));
@ -1458,15 +1490,71 @@ int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
* now we have an rbtree full of root_info objects, but we need to fill
* in their path names within the subvol that is referencing each one.
*/
ret = __list_subvol_fill_paths(fd, &root_lookup);
if (ret < 0)
ret = __list_subvol_fill_paths(fd, root_lookup);
return ret;
}
int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
struct btrfs_list_comparer_set *comp_set,
int layout, int full_path, char *raw_prefix)
{
struct root_lookup root_lookup;
struct root_lookup root_sort;
int ret;
u64 top_id = (full_path ? 0 : btrfs_list_get_path_rootid(fd));
ret = btrfs_list_subvols(fd, &root_lookup);
if (ret)
return ret;
__filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
comp_set, top_id);
print_all_volume_info(&root_sort, layout, raw_prefix);
__free_all_subvolumn(&root_lookup);
return 0;
}
int btrfs_get_subvol(int fd, struct root_info *the_ri)
{
int ret = 1, rr;
struct root_lookup rl;
struct rb_node *rbn;
struct root_info *ri;
u64 root_id = btrfs_list_get_path_rootid(fd);
if (btrfs_list_subvols(fd, &rl))
return ret;
__filter_and_sort_subvol(&root_lookup, &root_sort, filter_set,
comp_set, fd);
print_all_volume_info(&root_sort, is_tab_result);
__free_all_subvolumn(&root_lookup);
rbn = rb_first(&rl.root);
while(rbn) {
ri = rb_entry(rbn, struct root_info, rb_node);
rr = resolve_root(&rl, ri, root_id);
if (rr == -ENOENT) {
ret = -ENOENT;
rbn = rb_next(rbn);
continue;
}
if (!comp_entry_with_rootid(the_ri, ri, 0)) {
memcpy(the_ri, ri, offsetof(struct root_info, path));
if (ri->path)
the_ri->path = strdup(ri->path);
else
the_ri->path = NULL;
if (ri->name)
the_ri->name = strdup(ri->name);
else
the_ri->name = NULL;
if (ri->full_path)
the_ri->full_path = strdup(ri->full_path);
else
the_ri->name = NULL;
ret = 0;
break;
}
rbn = rb_next(rbn);
}
__free_all_subvolumn(&rl);
return ret;
}
@ -1670,7 +1758,11 @@ char *btrfs_list_path_for_root(int fd, u64 root)
struct root_info *entry;
entry = rb_entry(n, struct root_info, rb_node);
resolve_root(&root_lookup, entry, top_id);
ret = resolve_root(&root_lookup, entry, top_id);
if (ret == -ENOENT && entry->root_id == root) {
ret_path = NULL;
break;
}
if (entry->root_id == root) {
ret_path = entry->full_path;
entry->full_path = NULL;

View File

@ -18,7 +18,56 @@
#include "kerncompat.h"
struct root_info;
#define BTRFS_LIST_LAYOUT_DEFAULT 0
#define BTRFS_LIST_LAYOUT_TABLE 1
#define BTRFS_LIST_LAYOUT_RAW 2
/*
* one of these for each root we find.
*/
struct root_info {
struct rb_node rb_node;
struct rb_node sort_node;
/* this root's id */
u64 root_id;
/* equal the offset of the root's key */
u64 root_offset;
/* flags of the root */
u64 flags;
/* the id of the root that references this one */
u64 ref_tree;
/* the dir id we're in from ref_tree */
u64 dir_id;
u64 top_id;
/* generation when the root is created or last updated */
u64 gen;
/* creation generation of this root in sec*/
u64 ogen;
/* creation time of this root in sec*/
time_t otime;
u8 uuid[BTRFS_UUID_SIZE];
u8 puuid[BTRFS_UUID_SIZE];
/* path from the subvol we live in to this root, including the
* root's name. This is null until we do the extra lookup ioctl.
*/
char *path;
/* the name of this root in the directory it lives in */
char *name;
char *full_path;
};
typedef int (*btrfs_list_filter_func)(struct root_info *, u64);
typedef int (*btrfs_list_comp_func)(struct root_info *, struct root_info *,
@ -53,6 +102,7 @@ enum btrfs_list_column_enum {
BTRFS_LIST_PARENT,
BTRFS_LIST_TOP_LEVEL,
BTRFS_LIST_OTIME,
BTRFS_LIST_PUUID,
BTRFS_LIST_UUID,
BTRFS_LIST_PATH,
BTRFS_LIST_ALL,
@ -71,6 +121,8 @@ enum btrfs_list_filter_enum {
BTRFS_LIST_FILTER_CGEN_LESS,
BTRFS_LIST_FILTER_CGEN_MORE,
BTRFS_LIST_FILTER_TOPID_EQUAL,
BTRFS_LIST_FILTER_FULL_PATH,
BTRFS_LIST_FILTER_BY_PARENT,
BTRFS_LIST_FILTER_MAX,
};
@ -98,10 +150,11 @@ int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
enum btrfs_list_comp_enum comparer,
int is_descending);
int btrfs_list_subvols(int fd, struct btrfs_list_filter_set *filter_set,
int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set,
struct btrfs_list_comparer_set *comp_set,
int is_tab_result);
int is_tab_result, int full_path, char *raw_prefix);
int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen);
int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
char *btrfs_list_path_for_root(int fd, u64 root);
u64 btrfs_list_get_path_rootid(int fd);
int btrfs_get_subvol(int fd, struct root_info *the_ri);

View File

@ -84,8 +84,10 @@ int main(int ac, char **av)
root = open_ctree(av[optind], bytenr, 1);
if (root == NULL)
if (!root) {
fprintf(stderr, "Open ctree failed\n");
return 1;
}
/* make the super writing code think we've read the first super */
root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;

282
btrfs-show-super.c 100644
View File

@ -0,0 +1,282 @@
/*
* Copyright (C) 2012 STRATO AG. All rights reserved.
*
* 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.
*/
#define _XOPEN_SOURCE 500
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <ctype.h>
#include <uuid/uuid.h>
#include <errno.h>
#include "kerncompat.h"
#include "ctree.h"
#include "disk-io.h"
#include "print-tree.h"
#include "transaction.h"
#include "list.h"
#include "version.h"
#include "utils.h"
#include "crc32c.h"
static void print_usage(void);
static void dump_superblock(struct btrfs_super_block *sb);
int main(int argc, char **argv);
static int load_and_dump_sb(char *, int fd, u64 sb_bytenr);
static void print_usage(void)
{
fprintf(stderr,
"usage: btrfs-show-super [-i super_mirror|-a] dev [dev..]\n");
fprintf(stderr, "\tThe super_mirror number is between 0 and %d.\n",
BTRFS_SUPER_MIRROR_MAX - 1);
fprintf(stderr, "\tIf -a is passed all the superblocks are showed.\n");
fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
}
int main(int argc, char **argv)
{
int opt;
int all = 0;
char *filename;
int fd = -1;
int arg, i;
u64 sb_bytenr = btrfs_sb_offset(0);
while ((opt = getopt(argc, argv, "ai:")) != -1) {
switch (opt) {
case 'i':
arg = atoi(optarg);
if (arg < 0 || arg >= BTRFS_SUPER_MIRROR_MAX) {
fprintf(stderr,
"Illegal super_mirror %d\n",
arg);
print_usage();
exit(1);
}
sb_bytenr = btrfs_sb_offset(arg);
break;
case 'a':
all = 1;
break;
default:
print_usage();
exit(1);
}
}
if (argc < optind + 1) {
print_usage();
exit(1);
}
for (i = optind; i < argc; i++) {
filename = argv[i];
fd = open(filename, O_RDONLY, 0666);
if (fd < 0) {
fprintf(stderr, "Could not open %s\n", filename);
close(fd);
exit(1);
}
if (all) {
int idx;
for (idx = 0; idx < BTRFS_SUPER_MIRROR_MAX; idx++) {
sb_bytenr = btrfs_sb_offset(idx);
if (load_and_dump_sb(filename, fd, sb_bytenr)) {
close(fd);
exit(1);
}
putchar('\n');
}
} else {
load_and_dump_sb(filename, fd, sb_bytenr);
putchar('\n');
}
close(fd);
}
exit(0);
}
static int load_and_dump_sb(char *filename, int fd, u64 sb_bytenr)
{
u8 super_block_data[BTRFS_SUPER_INFO_SIZE];
struct btrfs_super_block *sb;
u64 ret;
sb = (struct btrfs_super_block *)super_block_data;
ret = pread64(fd, super_block_data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
if (ret != BTRFS_SUPER_INFO_SIZE) {
int e = errno;
/* check if the disk if too short for further superblock */
if (ret == 0 && e == 0)
return 0;
fprintf(stderr,
"ERROR: Failed to read the superblock on %s at %llu\n",
filename, (unsigned long long)sb_bytenr);
fprintf(stderr,
"ERROR: error = '%s', errno = %d\n", strerror(e), e);
return 1;
}
printf("superblock: bytenr=%llu, device=%s\n", sb_bytenr, filename);
printf("---------------------------------------------------------\n");
dump_superblock(sb);
return 0;
}
static int check_csum_sblock(void *sb, int csum_size)
{
char result[csum_size];
u32 crc = ~(u32)0;
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, result);
return !memcmp(sb, &result, csum_size);
}
static void dump_superblock(struct btrfs_super_block *sb)
{
int i;
char *s, buf[36+1];
u8 *p;
printf("csum\t\t\t0x");
for (i = 0, p = sb->csum; i < btrfs_super_csum_size(sb); i++)
printf("%02x", p[i]);
if (check_csum_sblock(sb, btrfs_super_csum_size(sb)))
printf(" [match]");
else
printf(" [DON'T MATCH]");
putchar('\n');
printf("bytenr\t\t\t%llu\n",
(unsigned long long)btrfs_super_bytenr(sb));
printf("flags\t\t\t0x%llx\n",
(unsigned long long)btrfs_super_flags(sb));
printf("magic\t\t\t");
s = (char *) &sb->magic;
for (i = 0; i < 8; i++)
putchar(isprint(s[i]) ? s[i] : '.');
if (!memcmp(BTRFS_MAGIC, &sb->magic, 8))
printf(" [match]\n");
else
printf(" [DON'T MATCH]\n");
uuid_unparse(sb->fsid, buf);
printf("fsid\t\t\t%s\n", buf);
printf("label\t\t\t");
s = sb->label;
for (i = 0; i < BTRFS_LABEL_SIZE && s[i]; i++)
putchar(isprint(s[i]) ? s[i] : '.');
putchar('\n');
printf("generation\t\t%llu\n",
(unsigned long long)btrfs_super_generation(sb));
printf("root\t\t\t%llu\n", (unsigned long long)btrfs_super_root(sb));
printf("sys_array_size\t\t%llu\n",
(unsigned long long)btrfs_super_sys_array_size(sb));
printf("chunk_root_generation\t%llu\n",
(unsigned long long)btrfs_super_chunk_root_generation(sb));
printf("root_level\t\t%llu\n",
(unsigned long long)btrfs_super_root_level(sb));
printf("chunk_root\t\t%llu\n",
(unsigned long long)btrfs_super_chunk_root(sb));
printf("chunk_root_level\t%llu\n",
(unsigned long long)btrfs_super_chunk_root_level(sb));
printf("log_root\t\t%llu\n",
(unsigned long long)btrfs_super_log_root(sb));
printf("log_root_transid\t%llu\n",
(unsigned long long)btrfs_super_log_root_transid(sb));
printf("log_root_level\t\t%llu\n",
(unsigned long long)btrfs_super_log_root_level(sb));
printf("total_bytes\t\t%llu\n",
(unsigned long long)btrfs_super_total_bytes(sb));
printf("bytes_used\t\t%llu\n",
(unsigned long long)btrfs_super_bytes_used(sb));
printf("sectorsize\t\t%llu\n",
(unsigned long long)btrfs_super_sectorsize(sb));
printf("nodesize\t\t%llu\n",
(unsigned long long)btrfs_super_nodesize(sb));
printf("leafsize\t\t%llu\n",
(unsigned long long)btrfs_super_leafsize(sb));
printf("stripesize\t\t%llu\n",
(unsigned long long)btrfs_super_stripesize(sb));
printf("root_dir\t\t%llu\n",
(unsigned long long)btrfs_super_root_dir(sb));
printf("num_devices\t\t%llu\n",
(unsigned long long)btrfs_super_num_devices(sb));
printf("compat_flags\t\t0x%llx\n",
(unsigned long long)btrfs_super_compat_flags(sb));
printf("compat_ro_flags\t\t0x%llx\n",
(unsigned long long)btrfs_super_compat_ro_flags(sb));
printf("incompat_flags\t\t0x%llx\n",
(unsigned long long)btrfs_super_incompat_flags(sb));
printf("csum_type\t\t%llu\n",
(unsigned long long)btrfs_super_csum_type(sb));
printf("csum_size\t\t%llu\n",
(unsigned long long)btrfs_super_csum_size(sb));
printf("cache_generation\t%llu\n",
(unsigned long long)btrfs_super_cache_generation(sb));
uuid_unparse(sb->dev_item.uuid, buf);
printf("dev_item.uuid\t\t%s\n", buf);
uuid_unparse(sb->dev_item.fsid, buf);
printf("dev_item.fsid\t\t%s %s\n", buf,
!memcmp(sb->dev_item.fsid, sb->fsid, BTRFS_FSID_SIZE) ?
"[match]" : "[DON'T MATCH]");
printf("dev_item.type\t\t%llu\n", (unsigned long long)
btrfs_stack_device_type(&sb->dev_item));
printf("dev_item.total_bytes\t%llu\n", (unsigned long long)
btrfs_stack_device_total_bytes(&sb->dev_item));
printf("dev_item.bytes_used\t%llu\n", (unsigned long long)
btrfs_stack_device_bytes_used(&sb->dev_item));
printf("dev_item.io_align\t%u\n", (unsigned int)
btrfs_stack_device_io_align(&sb->dev_item));
printf("dev_item.io_width\t%u\n", (unsigned int)
btrfs_stack_device_io_width(&sb->dev_item));
printf("dev_item.sector_size\t%u\n", (unsigned int)
btrfs_stack_device_sector_size(&sb->dev_item));
printf("dev_item.devid\t\t%llu\n",
btrfs_stack_device_id(&sb->dev_item));
printf("dev_item.dev_group\t%u\n", (unsigned int)
btrfs_stack_device_group(&sb->dev_item));
printf("dev_item.seek_speed\t%u\n", (unsigned int)
btrfs_stack_device_seek_speed(&sb->dev_item));
printf("dev_item.bandwidth\t%u\n", (unsigned int)
btrfs_stack_device_bandwidth(&sb->dev_item));
printf("dev_item.generation\t%llu\n", (unsigned long long)
btrfs_stack_device_generation(&sb->dev_item));
}

View File

@ -247,10 +247,11 @@ const struct cmd_group btrfs_cmd_group = {
{ "device", cmd_device, NULL, &device_cmd_group, 0 },
{ "scrub", cmd_scrub, NULL, &scrub_cmd_group, 0 },
{ "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
{ "send", cmd_send, NULL, &send_cmd_group, 0 },
{ "receive", cmd_receive, NULL, &receive_cmd_group, 0 },
{ "send", cmd_send, cmd_send_usage, NULL, 0 },
{ "receive", cmd_receive, cmd_receive_usage, NULL, 0 },
{ "quota", cmd_quota, NULL, &quota_cmd_group, 0 },
{ "qgroup", cmd_qgroup, NULL, &qgroup_cmd_group, 0 },
{ "replace", cmd_replace, NULL, &replace_cmd_group, 0 },
{ "help", cmd_help, cmd_help_usage, NULL, 0 },
{ "version", cmd_version, cmd_version_usage, NULL, 0 },
{ 0, 0, 0, 0, 0 }

View File

@ -22,8 +22,11 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>
#include <uuid/uuid.h>
#include "kerncompat.h"
#include "ctree.h"
#include "volumes.h"
@ -96,6 +99,7 @@ struct inode_backref {
unsigned int found_inode_ref:1;
unsigned int filetype:8;
int errors;
unsigned int ref_type;
u64 dir;
u64 index;
u16 namelen;
@ -469,12 +473,14 @@ static int add_inode_backref(struct cache_tree *inode_cache,
backref->filetype = filetype;
backref->found_dir_item = 1;
} else if (itemtype == BTRFS_INODE_REF_KEY) {
} else if ((itemtype == BTRFS_INODE_REF_KEY) ||
(itemtype == BTRFS_INODE_EXTREF_KEY)) {
if (backref->found_inode_ref)
backref->errors |= REF_ERR_DUP_INODE_REF;
if (backref->found_dir_index && backref->index != index)
backref->errors |= REF_ERR_INDEX_UNMATCH;
backref->ref_type = itemtype;
backref->index = index;
backref->found_inode_ref = 1;
} else {
@ -510,7 +516,7 @@ static int merge_inode_recs(struct inode_record *src, struct inode_record *dst,
add_inode_backref(dst_cache, dst->ino,
backref->dir, backref->index,
backref->name, backref->namelen, 0,
BTRFS_INODE_REF_KEY, backref->errors);
backref->ref_type, backref->errors);
}
}
@ -914,6 +920,49 @@ static int process_inode_ref(struct extent_buffer *eb,
return 0;
}
static int process_inode_extref(struct extent_buffer *eb,
int slot, struct btrfs_key *key,
struct shared_node *active_node)
{
u32 total;
u32 cur = 0;
u32 len;
u32 name_len;
u64 index;
u64 parent;
int error;
struct cache_tree *inode_cache;
struct btrfs_inode_extref *extref;
char namebuf[BTRFS_NAME_LEN];
inode_cache = &active_node->inode_cache;
extref = btrfs_item_ptr(eb, slot, struct btrfs_inode_extref);
total = btrfs_item_size_nr(eb, slot);
while (cur < total) {
name_len = btrfs_inode_extref_name_len(eb, extref);
index = btrfs_inode_extref_index(eb, extref);
parent = btrfs_inode_extref_parent(eb, extref);
if (name_len <= BTRFS_NAME_LEN) {
len = name_len;
error = 0;
} else {
len = BTRFS_NAME_LEN;
error = REF_ERR_NAME_TOO_LONG;
}
read_extent_buffer(eb, namebuf,
(unsigned long)(extref + 1), len);
add_inode_backref(inode_cache, key->objectid, parent,
index, namebuf, len, 0, key->type, error);
len = sizeof(*extref) + name_len;
extref = (struct btrfs_inode_extref *)((char *)extref + len);
cur += len;
}
return 0;
}
static u64 count_csum_range(struct btrfs_root *root, u64 start, u64 len)
{
struct btrfs_key key;
@ -1100,6 +1149,9 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
case BTRFS_INODE_REF_KEY:
ret = process_inode_ref(eb, i, &key, active_node);
break;
case BTRFS_INODE_EXTREF_KEY:
ret = process_inode_extref(eb, i, &key, active_node);
break;
case BTRFS_INODE_ITEM_KEY:
ret = process_inode_item(eb, i, &key, active_node);
break;
@ -3492,6 +3544,7 @@ int main(int ac, char **av)
struct btrfs_fs_info *info;
struct btrfs_trans_handle *trans = NULL;
u64 bytenr = 0;
char uuidbuf[37];
int ret;
int num;
int repair = 0;
@ -3544,6 +3597,8 @@ int main(int ac, char **av)
}
info = open_ctree_fs_info(av[optind], bytenr, rw, 1);
uuid_unparse(info->super_copy.fsid, uuidbuf);
printf("Checking filesystem on %s\nUUID: %s\n", av[optind], uuidbuf);
if (info == NULL)
return 1;

View File

@ -63,7 +63,7 @@ static void print_usage(void)
exit(1);
}
static int open_file_or_dir(const char *fname)
static int btrfsctl_open_file_or_dir(const char *fname)
{
int ret;
struct stat st;
@ -91,6 +91,7 @@ static int open_file_or_dir(const char *fname)
}
return fd;
}
int main(int ac, char **av)
{
char *fname = NULL;
@ -128,7 +129,7 @@ int main(int ac, char **av)
snap_location = strdup(fullpath);
snap_location = dirname(snap_location);
snap_fd = open_file_or_dir(snap_location);
snap_fd = btrfsctl_open_file_or_dir(snap_location);
name = strdup(fullpath);
name = basename(name);
@ -238,7 +239,7 @@ int main(int ac, char **av)
}
name = fname;
} else {
fd = open_file_or_dir(fname);
fd = btrfsctl_open_file_or_dir(fname);
}
if (name) {

View File

@ -46,7 +46,7 @@
#define GET_LABEL 3
#define SET_LABEL 4
static void change_label_unmounted(char *dev, char *nLabel)
static int change_label_unmounted(char *dev, char *nLabel)
{
struct btrfs_root *root;
struct btrfs_trans_handle *trans;
@ -56,7 +56,7 @@ static void change_label_unmounted(char *dev, char *nLabel)
*/
root = open_ctree(dev, 0, 1);
if (!root) /* errors are printed by open_ctree() */
return;
return -1;
trans = btrfs_start_transaction(root, 1);
strncpy(root->fs_info->super_copy.label, nLabel, BTRFS_LABEL_SIZE);
@ -65,6 +65,7 @@ static void change_label_unmounted(char *dev, char *nLabel)
/* Now we close it since we are done. */
close_ctree(root);
return 0;
}
int get_label_unmounted(char *dev)
@ -123,6 +124,5 @@ int set_label(char *btrfs_dev, char *nLabel)
fprintf(stderr, "FATAL: the filesystem has to be unmounted\n");
return -2;
}
change_label_unmounted(btrfs_dev, nLabel);
return 0;
return change_label_unmounted(btrfs_dev, nLabel);
}

View File

@ -108,6 +108,11 @@ int main(int argc, char *argv[])
root = open_ctree(device, 0, 1);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
return 1;
}
if (seeding_flag) {
ret = update_seeding_flag(root, seeding_value);
if (!ret)

View File

@ -28,6 +28,7 @@
#include "volumes.h"
#include "commands.h"
#include "utils.h"
static const char * const balance_cmd_group_usage[] = {
"btrfs [filesystem] balance <command> [options] <path>",
@ -353,7 +354,7 @@ static const char * const cmd_balance_start_usage[] = {
"",
"-d[filters] act on data chunks",
"-m[filters] act on metadata chunks",
"-s[filetrs] act on system chunks (only under -f)",
"-s[filters] act on system chunks (only under -f)",
"-v be verbose",
"-f force reducing of metadata integrity",
NULL

View File

@ -250,11 +250,163 @@ static int cmd_scan_dev(int argc, char **argv)
return 0;
}
static const char * const cmd_ready_dev_usage[] = {
"btrfs device ready <device>",
"Check device to see if it has all of it's devices in cache for mounting",
NULL
};
static int cmd_ready_dev(int argc, char **argv)
{
struct btrfs_ioctl_vol_args args;
int fd;
int ret;
if (check_argc_min(argc, 2))
usage(cmd_ready_dev_usage);
fd = open("/dev/btrfs-control", O_RDWR);
if (fd < 0) {
perror("failed to open /dev/btrfs-control");
return 10;
}
strncpy(args.name, argv[argc - 1], BTRFS_PATH_NAME_MAX);
ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
if (ret < 0) {
fprintf(stderr, "ERROR: unable to determine if the device '%s'"
" is ready for mounting - %s\n", argv[argc - 1],
strerror(errno));
ret = 1;
}
close(fd);
return ret;
}
static const char * const cmd_dev_stats_usage[] = {
"btrfs device stats [-z] <path>|<device>",
"Show current device IO stats. -z to reset stats afterwards.",
NULL
};
static int cmd_dev_stats(int argc, char **argv)
{
char *path;
struct btrfs_ioctl_fs_info_args fi_args;
struct btrfs_ioctl_dev_info_args *di_args = NULL;
int ret;
int fdmnt;
int i;
char c;
int fdres = -1;
int err = 0;
__u64 flags = 0;
optind = 1;
while ((c = getopt(argc, argv, "z")) != -1) {
switch (c) {
case 'z':
flags = BTRFS_DEV_STATS_RESET;
break;
case '?':
default:
fprintf(stderr, "ERROR: device stat args invalid.\n"
" device stat [-z] <path>|<device>\n"
" -z to reset stats after reading.\n");
return 1;
}
}
if (optind + 1 != argc) {
fprintf(stderr, "ERROR: device stat needs path|device as single"
" argument\n");
return 1;
}
path = argv[optind];
fdmnt = open_file_or_dir(path);
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access '%s'\n", path);
return 12;
}
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
if (ret) {
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
"%s\n", strerror(-ret));
err = 1;
goto out;
}
if (!fi_args.num_devices) {
fprintf(stderr, "ERROR: no devices found\n");
err = 1;
goto out;
}
for (i = 0; i < fi_args.num_devices; i++) {
struct btrfs_ioctl_get_dev_stats args = {0};
__u8 path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
strncpy((char *)path, (char *)di_args[i].path,
BTRFS_DEVICE_PATH_NAME_MAX);
path[BTRFS_DEVICE_PATH_NAME_MAX] = '\0';
args.devid = di_args[i].devid;
args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
args.flags = flags;
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
fprintf(stderr,
"ERROR: ioctl(BTRFS_IOC_GET_DEV_STATS) on %s failed: %s\n",
path, strerror(errno));
err = 1;
} else {
if (args.nr_items >= BTRFS_DEV_STAT_WRITE_ERRS + 1)
printf("[%s].write_io_errs %llu\n",
path,
(unsigned long long) args.values[
BTRFS_DEV_STAT_WRITE_ERRS]);
if (args.nr_items >= BTRFS_DEV_STAT_READ_ERRS + 1)
printf("[%s].read_io_errs %llu\n",
path,
(unsigned long long) args.values[
BTRFS_DEV_STAT_READ_ERRS]);
if (args.nr_items >= BTRFS_DEV_STAT_FLUSH_ERRS + 1)
printf("[%s].flush_io_errs %llu\n",
path,
(unsigned long long) args.values[
BTRFS_DEV_STAT_FLUSH_ERRS]);
if (args.nr_items >= BTRFS_DEV_STAT_CORRUPTION_ERRS + 1)
printf("[%s].corruption_errs %llu\n",
path,
(unsigned long long) args.values[
BTRFS_DEV_STAT_CORRUPTION_ERRS]);
if (args.nr_items >= BTRFS_DEV_STAT_GENERATION_ERRS + 1)
printf("[%s].generation_errs %llu\n",
path,
(unsigned long long) args.values[
BTRFS_DEV_STAT_GENERATION_ERRS]);
}
}
out:
free(di_args);
close(fdmnt);
if (fdres > -1)
close(fdres);
return err;
}
const struct cmd_group device_cmd_group = {
device_cmd_group_usage, NULL, {
{ "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
{ "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
{ "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 },
{ "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 },
{ "stats", cmd_dev_stats, cmd_dev_stats_usage, NULL, 0 },
{ 0, 0, 0, 0, 0 }
}
};

View File

@ -23,6 +23,7 @@
#include "kerncompat.h"
#include "ioctl.h"
#include "utils.h"
#include "commands.h"
#include "btrfs-list.h"

View File

@ -24,26 +24,14 @@
#include "ioctl.h"
#include "commands.h"
#include "qgroup.h"
#include "utils.h"
static const char * const qgroup_cmd_group_usage[] = {
"btrfs qgroup <command> [options] <path>",
NULL
};
static u64 parse_qgroupid(char *p)
{
char *s = strchr(p, '/');
u64 level;
u64 id;
if (!s)
return atoll(p);
level = atoll(p);
id = atoll(s + 1);
return (level << 48) | id;
}
static int qgroup_assign(int assign, int argc, char **argv)
{
int ret = 0;
@ -63,7 +51,7 @@ static int qgroup_assign(int assign, int argc, char **argv)
/*
* FIXME src should accept subvol path
*/
if (args.src >= args.dst) {
if ((args.src >> 48) >= (args.dst >> 48)) {
fprintf(stderr, "ERROR: bad relation requested '%s'\n", path);
return 12;
}
@ -351,7 +339,7 @@ static int cmd_qgroup_limit(int argc, char **argv)
int ret = 0;
int fd;
int e;
char *path;
char *path = NULL;
struct btrfs_ioctl_qgroup_limit_args args;
unsigned long long size;
int compressed = 0;
@ -383,7 +371,6 @@ static int cmd_qgroup_limit(int argc, char **argv)
}
memset(&args, 0, sizeof(args));
args.qgroupid = parse_qgroupid(argv[optind + 1]);
if (size) {
if (compressed)
args.lim.flags |= BTRFS_QGROUP_LIMIT_RFER_CMPR |
@ -397,9 +384,8 @@ static int cmd_qgroup_limit(int argc, char **argv)
}
}
if (args.qgroupid == 0) {
if (check_argc_exact(argc - optind, 2))
usage(cmd_qgroup_limit_usage);
if (argc - optind == 2) {
args.qgroupid = 0;
path = argv[optind + 1];
ret = test_issubvolume(path);
if (ret < 0) {
@ -415,11 +401,11 @@ static int cmd_qgroup_limit(int argc, char **argv)
* keep qgroupid at 0, this indicates that the subvolume the
* fd refers to is to be limited
*/
} else {
if (check_argc_exact(argc - optind, 3))
usage(cmd_qgroup_limit_usage);
} else if (argc - optind == 3) {
args.qgroupid = parse_qgroupid(argv[optind + 1]);
path = argv[optind + 2];
}
} else
usage(cmd_qgroup_limit_usage);
fd = open_file_or_dir(path);
if (fd < 0) {

View File

@ -23,6 +23,7 @@
#include "ioctl.h"
#include "commands.h"
#include "utils.h"
static const char * const quota_cmd_group_usage[] = {
"btrfs quota <command> [options] <path>",

View File

@ -881,8 +881,8 @@ static const char * const receive_cmd_group_usage[] = {
NULL
};
static const char * const cmd_receive_usage[] = {
"btrfs receive [-v] [-i <infile>] <mount>",
const char * const cmd_receive_usage[] = {
"btrfs receive [-v] [-f <infile>] <mount>",
"Receive subvolumes from stdin.",
"Receives one or more subvolumes that were previously ",
"sent with btrfs send. The received subvolumes are stored",
@ -893,7 +893,7 @@ static const char * const cmd_receive_usage[] = {
"After receiving a subvolume, it is immediately set to",
"read only.\n",
"-v Enable verbose debug output. Each",
" occurrency of this option increases the",
" occurrence of this option increases the",
" verbose level more.",
"-f <infile> By default, btrfs receive uses stdin",
" to receive the subvolumes. Use this",

579
cmds-replace.c 100644
View File

@ -0,0 +1,579 @@
/*
* Copyright (C) 2012 STRATO. All rights reserved.
*
* 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 <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <assert.h>
#include <inttypes.h>
#include <sys/wait.h>
#include "kerncompat.h"
#include "ctree.h"
#include "ioctl.h"
#include "utils.h"
#include "volumes.h"
#include "disk-io.h"
#include "commands.h"
static int print_replace_status(int fd, const char *path, int once);
static char *time2string(char *buf, size_t s, __u64 t);
static char *progress2string(char *buf, size_t s, int progress_1000);
static const char *replace_dev_result2string(__u64 result)
{
switch (result) {
case BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR:
return "no error";
case BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED:
return "not started";
case BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED:
return "already started";
default:
return "<illegal result value>";
}
}
static const char * const replace_cmd_group_usage[] = {
"btrfs replace <command> [<args>]",
NULL
};
static int is_numerical(const char *str)
{
if (!(*str >= '0' && *str <= '9'))
return 0;
while (*str >= '0' && *str <= '9')
str++;
if (*str != '\0')
return 0;
return 1;
}
static int dev_replace_cancel_fd = -1;
static void dev_replace_sigint_handler(int signal)
{
struct btrfs_ioctl_dev_replace_args args = {0};
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
ioctl(dev_replace_cancel_fd, BTRFS_IOC_DEV_REPLACE, &args);
}
static int dev_replace_handle_sigint(int fd)
{
struct sigaction sa = {
.sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
};
dev_replace_cancel_fd = fd;
return sigaction(SIGINT, &sa, NULL);
}
static const char *const cmd_start_replace_usage[] = {
"btrfs replace start srcdev|devid targetdev [-Bfr] mount_point",
"Replace device of a btrfs filesystem.",
"On a live filesystem, duplicate the data to the target device which",
"is currently stored on the source device. If the source device is not",
"available anymore, or if the -r option is set, the data is built",
"only using the RAID redundancy mechanisms. After completion of the",
"operation, the source device is removed from the filesystem.",
"If the srcdev is a numerical value, it is assumed to be the device id",
"of the filesystem which is mounted at mount_point, otherwise it is",
"the path to the source device. If the source device is disconnected,",
"from the system, you have to use the devid parameter format.",
"The targetdev needs to be same size or larger than the srcdev.",
"",
"-r only read from srcdev if no other zero-defect mirror exists",
" (enable this if your drive has lots of read errors, the access",
" would be very slow)",
"-f force using and overwriting targetdev even if it looks like",
" containing a valid btrfs filesystem. A valid filesystem is",
" assumed if a btrfs superblock is found which contains a",
" correct checksum. Devices which are currently mounted are",
" never allowed to be used as the targetdev",
"-B do not background",
NULL
};
static int cmd_start_replace(int argc, char **argv)
{
struct btrfs_ioctl_dev_replace_args start_args = {0};
struct btrfs_ioctl_dev_replace_args status_args = {0};
int ret;
int i;
int c;
int fdmnt = -1;
int fdsrcdev = -1;
int fddstdev = -1;
char *path;
char *srcdev;
char *dstdev;
int avoid_reading_from_srcdev = 0;
int force_using_targetdev = 0;
u64 total_devs = 1;
struct btrfs_fs_devices *fs_devices_mnt = NULL;
struct stat st;
u64 dstdev_block_count;
int do_not_background = 0;
int mixed = 0;
while ((c = getopt(argc, argv, "Brf")) != -1) {
switch (c) {
case 'B':
do_not_background = 1;
break;
case 'r':
avoid_reading_from_srcdev = 1;
break;
case 'f':
force_using_targetdev = 1;
break;
case '?':
default:
usage(cmd_start_replace_usage);
}
}
start_args.start.cont_reading_from_srcdev_mode =
avoid_reading_from_srcdev ?
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
if (check_argc_exact(argc - optind, 3))
usage(cmd_start_replace_usage);
path = argv[optind + 2];
fdmnt = open_file_or_dir(path);
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
path, strerror(errno));
goto leave_with_error;
}
/* check for possible errors before backgrounding */
status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
if (ret) {
fprintf(stderr,
"ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
path, strerror(errno),
replace_dev_result2string(status_args.result));
goto leave_with_error;
}
if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
fprintf(stderr,
"ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
path, replace_dev_result2string(status_args.result));
goto leave_with_error;
}
if (status_args.status.replace_state ==
BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
fprintf(stderr,
"ERROR: btrfs replace on \"%s\" already started!\n",
path);
goto leave_with_error;
}
srcdev = argv[optind];
dstdev = argv[optind + 1];
if (is_numerical(srcdev)) {
struct btrfs_ioctl_fs_info_args fi_args;
struct btrfs_ioctl_dev_info_args *di_args = NULL;
if (atoi(srcdev) == 0) {
fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n",
srcdev);
goto leave_with_error;
}
start_args.start.srcdevid = (__u64)atoi(srcdev);
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
if (ret) {
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
"%s\n", strerror(-ret));
free(di_args);
goto leave_with_error;
}
if (!fi_args.num_devices) {
fprintf(stderr, "ERROR: no devices found\n");
free(di_args);
goto leave_with_error;
}
for (i = 0; i < fi_args.num_devices; i++)
if (start_args.start.srcdevid == di_args[i].devid)
break;
if (i == fi_args.num_devices) {
fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
srcdev, path);
goto leave_with_error;
}
} else {
fdsrcdev = open(srcdev, O_RDWR);
if (!fdsrcdev) {
fprintf(stderr, "Error: Unable to open device '%s'\n",
srcdev);
goto leave_with_error;
}
ret = fstat(fdsrcdev, &st);
if (ret) {
fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
goto leave_with_error;
}
if (!S_ISBLK(st.st_mode)) {
fprintf(stderr, "Error: '%s' is not a block device\n",
srcdev);
goto leave_with_error;
}
strncpy((char *)start_args.start.srcdev_name, srcdev,
BTRFS_DEVICE_PATH_NAME_MAX);
close(fdsrcdev);
fdsrcdev = -1;
start_args.start.srcdevid = 0;
}
ret = check_mounted(dstdev);
if (ret < 0) {
fprintf(stderr, "Error checking %s mount status\n", dstdev);
goto leave_with_error;
}
if (ret == 1) {
fprintf(stderr,
"Error, target device %s is in use and currently mounted!\n",
dstdev);
goto leave_with_error;
}
fddstdev = open(dstdev, O_RDWR);
if (fddstdev < 0) {
fprintf(stderr, "Unable to open %s\n", dstdev);
goto leave_with_error;
}
ret = btrfs_scan_one_device(fddstdev, dstdev, &fs_devices_mnt,
&total_devs, BTRFS_SUPER_INFO_OFFSET);
if (ret >= 0 && !force_using_targetdev) {
fprintf(stderr,
"Error, target device %s contains filesystem, use '-f' to force overwriting.\n",
dstdev);
goto leave_with_error;
}
ret = fstat(fddstdev, &st);
if (ret) {
fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev);
goto leave_with_error;
}
if (!S_ISBLK(st.st_mode)) {
fprintf(stderr, "Error: '%s' is not a block device\n", dstdev);
goto leave_with_error;
}
strncpy((char *)start_args.start.tgtdev_name, dstdev,
BTRFS_DEVICE_PATH_NAME_MAX);
if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
&mixed, 0)) {
fprintf(stderr, "Error: Failed to prepare device '%s'\n",
dstdev);
goto leave_with_error;
}
close(fddstdev);
fddstdev = -1;
dev_replace_handle_sigint(fdmnt);
if (!do_not_background) {
if (daemon(0, 0) < 0) {
fprintf(stderr, "ERROR, backgrounding failed: %s\n",
strerror(errno));
goto leave_with_error;
}
}
start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
if (do_not_background) {
if (ret) {
fprintf(stderr,
"ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
path, strerror(errno),
replace_dev_result2string(start_args.result));
goto leave_with_error;
}
if (start_args.result !=
BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
fprintf(stderr,
"ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
path,
replace_dev_result2string(start_args.result));
goto leave_with_error;
}
}
close(fdmnt);
return 0;
leave_with_error:
if (fdmnt != -1)
close(fdmnt);
if (fdsrcdev != -1)
close(fdsrcdev);
if (fddstdev != -1)
close(fddstdev);
return -1;
}
static const char *const cmd_status_replace_usage[] = {
"btrfs replace status mount_point [-1]",
"Print status and progress information of a running device replace",
"operation",
"",
"-1 print once instead of print continously until the replace",
" operation finishes (or is canceled)",
NULL
};
static int cmd_status_replace(int argc, char **argv)
{
int fd;
int e;
int c;
char *path;
int once = 0;
int ret;
while ((c = getopt(argc, argv, "1")) != -1) {
switch (c) {
case '1':
once = 1;
break;
case '?':
default:
usage(cmd_status_replace_usage);
}
}
if (check_argc_exact(argc - optind, 1))
usage(cmd_status_replace_usage);
path = argv[optind];
fd = open_file_or_dir(path);
e = errno;
if (fd < 0) {
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
path, strerror(e));
return -1;
}
ret = print_replace_status(fd, path, once);
close(fd);
return ret;
}
static int print_replace_status(int fd, const char *path, int once)
{
struct btrfs_ioctl_dev_replace_args args = {0};
struct btrfs_ioctl_dev_replace_status_params *status;
int ret;
int prevent_loop = 0;
int skip_stats;
int num_chars;
char string1[80];
char string2[80];
char string3[80];
for (;;) {
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
if (ret) {
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
path, strerror(errno),
replace_dev_result2string(args.result));
return ret;
}
status = &args.status;
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
path,
replace_dev_result2string(args.result));
return -1;
}
skip_stats = 0;
num_chars = 0;
switch (status->replace_state) {
case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
num_chars =
printf("%s done",
progress2string(string3,
sizeof(string3),
status->progress_1000));
break;
case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
prevent_loop = 1;
printf("Started on %s, finished on %s",
time2string(string1, sizeof(string1),
status->time_started),
time2string(string2, sizeof(string2),
status->time_stopped));
break;
case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
prevent_loop = 1;
printf("Started on %s, canceled on %s at %s",
time2string(string1, sizeof(string1),
status->time_started),
time2string(string2, sizeof(string2),
status->time_stopped),
progress2string(string3, sizeof(string3),
status->progress_1000));
break;
case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
prevent_loop = 1;
printf("Started on %s, suspended on %s at %s",
time2string(string1, sizeof(string1),
status->time_started),
time2string(string2, sizeof(string2),
status->time_stopped),
progress2string(string3, sizeof(string3),
status->progress_1000));
break;
case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
prevent_loop = 1;
skip_stats = 1;
printf("Never started");
break;
default:
prevent_loop = 1;
assert(0);
break;
}
if (!skip_stats)
num_chars += printf(
", %llu write errs, %llu uncorr. read errs",
(unsigned long long)status->num_write_errors,
(unsigned long long)
status->num_uncorrectable_read_errors);
if (once || prevent_loop) {
printf("\n");
return 0;
}
fflush(stdout);
sleep(1);
while (num_chars > 0) {
putchar('\b');
num_chars--;
}
}
return 0;
}
static char *
time2string(char *buf, size_t s, __u64 t)
{
struct tm t_tm;
time_t t_time_t;
t_time_t = (time_t)t;
assert((__u64)t_time_t == t);
localtime_r(&t_time_t, &t_tm);
strftime(buf, s, "%e.%b %T", &t_tm);
return buf;
}
static char *
progress2string(char *buf, size_t s, int progress_1000)
{
snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
assert(s > 0);
buf[s - 1] = '\0';
return buf;
}
static const char *const cmd_cancel_replace_usage[] = {
"btrfs replace cancel mount_point",
"Cancel a running device replace operation.",
NULL
};
static int cmd_cancel_replace(int argc, char **argv)
{
struct btrfs_ioctl_dev_replace_args args = {0};
int ret;
int c;
int fd;
int e;
char *path;
while ((c = getopt(argc, argv, "")) != -1) {
switch (c) {
case '?':
default:
usage(cmd_cancel_replace_usage);
}
}
if (check_argc_exact(argc - optind, 1))
usage(cmd_cancel_replace_usage);
path = argv[optind];
fd = open_file_or_dir(path);
if (fd < 0) {
fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
path, strerror(errno));
return -1;
}
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
e = errno;
close(fd);
if (ret) {
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
path, strerror(e),
replace_dev_result2string(args.result));
return ret;
}
return 0;
}
const struct cmd_group replace_cmd_group = {
replace_cmd_group_usage, NULL, {
{ "start", cmd_start_replace, cmd_start_replace_usage, NULL,
0 },
{ "status", cmd_status_replace, cmd_status_replace_usage, NULL,
0 },
{ "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
0 },
{ 0, 0, 0, 0, 0 }
}
};
int cmd_replace(int argc, char **argv)
{
return handle_command_group(&replace_cmd_group, argc, argv);
}

View File

@ -969,89 +969,6 @@ static struct scrub_file_record *last_dev_scrub(
return NULL;
}
static int scrub_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args)
{
int ret;
di_args->devid = devid;
memset(&di_args->uuid, '\0', sizeof(di_args->uuid));
ret = ioctl(fd, BTRFS_IOC_DEV_INFO, di_args);
return ret ? -errno : 0;
}
static int scrub_fs_info(char *path,
struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret)
{
int ret = 0;
int ndevs = 0;
int i = 1;
int fd;
struct btrfs_fs_devices *fs_devices_mnt = NULL;
struct btrfs_ioctl_dev_info_args *di_args;
char mp[BTRFS_PATH_NAME_MAX + 1];
memset(fi_args, 0, sizeof(*fi_args));
fd = open_file_or_dir(path);
if (fd < 0) {
fprintf(stderr, "ERROR: can't access to '%s'\n", path);
return -1;
}
ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
if (ret && errno == EINVAL) {
/* path is no mounted btrfs. try if it's a device */
ret = check_mounted_where(fd, path, mp, sizeof(mp),
&fs_devices_mnt);
if (!ret)
return -EINVAL;
if (ret < 0)
return ret;
fi_args->num_devices = 1;
fi_args->max_id = fs_devices_mnt->latest_devid;
i = fs_devices_mnt->latest_devid;
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
close(fd);
fd = open_file_or_dir(mp);
if (fd < 0)
return -errno;
} else if (ret) {
close(fd);
return -errno;
}
if (!fi_args->num_devices) {
close(fd);
return 0;
}
di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
if (!di_args) {
close(fd);
return -errno;
}
for (; i <= fi_args->max_id; ++i) {
BUG_ON(ndevs >= fi_args->num_devices);
ret = scrub_device_info(fd, i, &di_args[ndevs]);
if (ret == -ENODEV)
continue;
if (ret) {
close(fd);
return ret;
}
++ndevs;
}
BUG_ON(ndevs == 0);
close(fd);
return 0;
}
int mkdir_p(char *path)
{
int i;
@ -1172,7 +1089,7 @@ static int scrub_start(int argc, char **argv, int resume)
return 12;
}
ret = scrub_fs_info(path, &fi_args, &di_args);
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
if (ret) {
ERR(!do_quiet, "ERROR: getting dev info for scrub failed: "
"%s\n", strerror(-ret));
@ -1604,6 +1521,7 @@ static int cmd_scrub_status(int argc, char **argv)
};
int ret;
int i;
int fdmnt;
int print_raw = 0;
int do_stats_per_dev = 0;
int c;
@ -1631,7 +1549,13 @@ static int cmd_scrub_status(int argc, char **argv)
path = argv[optind];
ret = scrub_fs_info(path, &fi_args, &di_args);
fdmnt = open_file_or_dir(path);
if (fdmnt < 0) {
fprintf(stderr, "ERROR: can't access to '%s'\n", path);
return 12;
}
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
if (ret) {
fprintf(stderr, "ERROR: getting dev info for scrub failed: "
"%s\n", strerror(-ret));

View File

@ -152,7 +152,7 @@ static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
if (tmp < 0)
tmp *= -1;
if (tmp < best_diff) {
best_parent = parent;
best_parent = parent2;
best_diff = tmp;
}
}
@ -236,7 +236,7 @@ out:
return ERR_PTR(ret);
}
static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
{
int ret;
pthread_t t_read;
@ -273,6 +273,7 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
goto out;
}
memset(&io_send, 0, sizeof(io_send));
io_send.send_fd = pipefd[1];
send->send_fd = pipefd[0];
@ -288,7 +289,7 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
io_send.clone_sources = (__u64*)send->clone_sources;
io_send.clone_sources_count = send->clone_sources_count;
io_send.parent_root = parent_root;
io_send.parent_root = parent_root_id;
ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
if (ret) {
ret = -errno;
@ -333,12 +334,12 @@ out:
return ret;
}
static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
char *get_subvol_name(char *mnt, char *full_path)
{
int len = strlen(s->root_path);
int len = strlen(mnt);
if (!len)
return full_path;
if (s->root_path[len - 1] != '/')
if (mnt[len - 1] != '/')
len += 1;
return full_path + len;
@ -425,21 +426,17 @@ int cmd_send_start(int argc, char **argv)
char *snapshot_parent = NULL;
u64 root_id;
u64 parent_root_id = 0;
int full_send = 1;
memset(&send, 0, sizeof(send));
send.dump_fd = fileno(stdout);
if (isatty(send.dump_fd)) {
fprintf(stderr, "ERROR: not dumping send stream into a terminal, redirect it into a file\n");
return 1;
}
while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
switch (c) {
case 'v':
g_verbose++;
break;
case 'i': {
case 'c':
subvol = realpath(optarg, NULL);
if (!subvol) {
ret = -errno;
@ -452,7 +449,7 @@ int cmd_send_start(int argc, char **argv)
if (ret < 0)
goto out;
ret = get_root_id(&send, get_subvol_name(&send, subvol),
ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
&root_id);
if (ret < 0) {
fprintf(stderr, "ERROR: could not resolve "
@ -461,12 +458,16 @@ int cmd_send_start(int argc, char **argv)
}
add_clone_source(&send, root_id);
free(subvol);
full_send = 0;
break;
}
case 'f':
outname = optarg;
break;
case 'p':
if (snapshot_parent) {
fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
return 1;
}
snapshot_parent = realpath(optarg, NULL);
if (!snapshot_parent) {
ret = -errno;
@ -474,7 +475,12 @@ int cmd_send_start(int argc, char **argv)
"%s\n", optarg, strerror(-ret));
goto out;
}
full_send = 0;
break;
case 'i':
fprintf(stderr,
"ERROR: -i was removed, use -c instead\n");
return 1;
case '?':
default:
fprintf(stderr, "ERROR: send args invalid.\n");
@ -497,6 +503,13 @@ int cmd_send_start(int argc, char **argv)
}
}
if (isatty(send.dump_fd)) {
fprintf(stderr,
"ERROR: not dumping send stream into a terminal, "
"redirect it into a file\n");
return 1;
}
/* use first send subvol to determine mount_root */
subvol = argv[optind];
@ -513,7 +526,7 @@ int cmd_send_start(int argc, char **argv)
if (snapshot_parent != NULL) {
ret = get_root_id(&send,
get_subvol_name(&send, snapshot_parent),
get_subvol_name(send.root_path, snapshot_parent),
&parent_root_id);
if (ret < 0) {
fprintf(stderr, "ERROR: could not resolve root_id "
@ -572,7 +585,7 @@ int cmd_send_start(int argc, char **argv)
goto out;
}
ret = get_root_id(&send, get_subvol_name(&send, subvol),
ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
&root_id);
if (ret < 0) {
fprintf(stderr, "ERROR: could not resolve root_id "
@ -580,10 +593,13 @@ int cmd_send_start(int argc, char **argv)
goto out;
}
if (!parent_root_id) {
if (!full_send && !parent_root_id) {
ret = find_good_parent(&send, root_id, &parent_root_id);
if (ret < 0)
parent_root_id = 0;
if (ret < 0) {
fprintf(stderr, "ERROR: parent determination failed for %lld\n",
root_id);
goto out;
}
}
ret = is_subvol_ro(&send, subvol);
@ -604,6 +620,7 @@ int cmd_send_start(int argc, char **argv)
add_clone_source(&send, root_id);
parent_root_id = 0;
full_send = 0;
free(subvol);
}
@ -620,43 +637,32 @@ static const char * const send_cmd_group_usage[] = {
NULL
};
static const char * const cmd_send_usage[] = {
"btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
const char * const cmd_send_usage[] = {
"btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
"Send the subvolume to stdout.",
"Sends the subvolume specified by <subvol> to stdout.",
"By default, this will send the whole subvolume. To do",
"an incremental send, one or multiple '-i <clone_source>'",
"arguments have to be specified. A 'clone source' is",
"a subvolume that is known to exist on the receiving",
"side in exactly the same state as on the sending side.\n",
"Normally, a good snapshot parent is searched automatically",
"in the list of 'clone sources'. To override this, use",
"'-p <parent>' to manually specify a snapshot parent.",
"A manually specified snapshot parent is also regarded",
"as 'clone source'.\n",
"-v Enable verbose debug output. Each",
" occurrency of this option increases the",
" verbose level more.",
"-i <subvol> Informs btrfs send that this subvolume,",
" can be taken as 'clone source'. This can",
" be used for incremental sends.",
"-p <subvol> Disable automatic snaphot parent",
" determination and use <subvol> as parent.",
" This subvolume is also added to the list",
" of 'clone sources' (see -i).",
"-f <outfile> Output is normally written to stdout.",
" To write to a file, use this option.",
" An alternative would be to use pipes.",
"By default, this will send the whole subvolume. To do an incremental",
"send, use '-p <parent>'. If you want to allow btrfs to clone from",
"any additional local snapshots, use -c <clone-src> (multiple times",
"where applicable). You must not specify clone sources unless you",
"guarantee that these snapshots are exactly in the same state on both",
"sides, the sender and the receiver. It is allowed to omit the",
"'-p <parent>' option when '-c <clone-src>' options are given, in",
"which case 'btrfs send' will determine a suitable parent among the",
"clone sources itself.",
"\n",
"-v Enable verbose debug output. Each occurrence of",
" this option increases the verbose level more.",
"-p <parent> Send an incremental stream from <parent> to",
" <subvol>.",
"-c <clone-src> Use this snapshot as a clone source for an ",
" incremental send (multiple allowed)",
"-f <outfile> Output is normally written to stdout. To write to",
" a file, use this option. An alternative would be to",
" use pipes.",
NULL
};
const struct cmd_group send_cmd_group = {
send_cmd_group_usage, NULL, {
{ "send", cmd_send_start, cmd_send_usage, NULL, 0 },
{ 0, 0, 0, 0, 0 },
},
};
int cmd_send(int argc, char **argv)
{
return cmd_send_start(argc, argv);

View File

@ -24,6 +24,7 @@
#include <libgen.h>
#include <limits.h>
#include <getopt.h>
#include <uuid/uuid.h>
#include "kerncompat.h"
#include "ioctl.h"
@ -32,6 +33,7 @@
#include "ctree.h"
#include "commands.h"
#include "btrfs-list.h"
#include "utils.h"
static const char * const subvolume_cmd_group_usage[] = {
"btrfs subvolume <command> <args>",
@ -271,21 +273,32 @@ out:
return ret;
}
/*
* Naming of options:
* - uppercase for filters and sort options
* - lowercase for enabling specific items in the output
*/
static const char * const cmd_subvol_list_usage[] = {
"btrfs subvolume list [-apurts] [-g [+|-]value] [-c [+|-]value] "
"btrfs subvolume list [-agopurts] [-G [+|-]value] [-C [+|-]value] "
"[--sort=gen,ogen,rootid,path] <path>",
"List subvolumes (and snapshots)",
"",
"-p print parent ID",
"-a print all the subvolumes in the filesystem.",
"-a print all the subvolumes in the filesystem and",
" distinguish absolute and relative path with respect",
" to the given <path>",
"-c print the ogeneration of the subvolume",
"-g print the generation of the subvolume",
"-o print only subvolumes bellow specified path",
"-u print the uuid of subvolumes (and snapshots)",
"-q print the parent uuid of the snapshots",
"-t print the result as a table",
"-s list snapshots only in the filesystem",
"-r list readonly subvolumes (including snapshots)",
"-g [+|-]value",
"-G [+|-]value",
" filter the subvolumes by generation",
" (+value: >= value; -value: <= value; value: = value)",
"-c [+|-]value",
"-C [+|-]value",
" filter the subvolumes by ogeneration",
" (+value: >= value; -value: <= value; value: = value)",
"--sort=gen,ogen,rootid,path",
@ -300,13 +313,14 @@ static int cmd_subvol_list(int argc, char **argv)
struct btrfs_list_filter_set *filter_set;
struct btrfs_list_comparer_set *comparer_set;
u64 flags = 0;
int fd;
int fd = -1;
u64 top_id;
int ret;
int ret = -1, uerr = 0;
int c;
char *subvol;
int is_tab_result = 0;
int is_list_all = 0;
int is_only_in_path = 0;
struct option long_options[] = {
{"sort", 1, NULL, 'S'},
{0, 0, 0, 0}
@ -318,7 +332,7 @@ static int cmd_subvol_list(int argc, char **argv)
optind = 1;
while(1) {
c = getopt_long(argc, argv,
"apsurg:c:t", long_options, NULL);
"acgopqsurG:C:t", long_options, NULL);
if (c < 0)
break;
@ -329,6 +343,15 @@ static int cmd_subvol_list(int argc, char **argv)
case 'a':
is_list_all = 1;
break;
case 'c':
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
break;
case 'g':
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
break;
case 'o':
is_only_in_path = 1;
break;
case 't':
is_tab_result = 1;
break;
@ -338,39 +361,49 @@ static int cmd_subvol_list(int argc, char **argv)
0);
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
break;
case 'u':
btrfs_list_setup_print_column(BTRFS_LIST_UUID);
break;
case 'q':
btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
break;
case 'r':
flags |= BTRFS_ROOT_SUBVOL_RDONLY;
break;
case 'g':
case 'G':
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
ret = btrfs_list_parse_filter_string(optarg,
&filter_set,
BTRFS_LIST_FILTER_GEN);
if (ret)
usage(cmd_subvol_list_usage);
if (ret) {
uerr = 1;
goto out;
}
break;
case 'c':
case 'C':
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
ret = btrfs_list_parse_filter_string(optarg,
&filter_set,
BTRFS_LIST_FILTER_CGEN);
if (ret)
usage(cmd_subvol_list_usage);
if (ret) {
uerr = 1;
goto out;
}
break;
case 'S':
ret = btrfs_list_parse_sort_string(optarg,
&comparer_set);
if (ret)
usage(cmd_subvol_list_usage);
if (ret) {
uerr = 1;
goto out;
}
break;
default:
usage(cmd_subvol_list_usage);
uerr = 1;
goto out;
}
}
@ -378,38 +411,66 @@ static int cmd_subvol_list(int argc, char **argv)
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
flags);
if (check_argc_exact(argc - optind, 1))
usage(cmd_subvol_list_usage);
if (check_argc_exact(argc - optind, 1)) {
uerr = 1;
goto out;
}
subvol = argv[optind];
ret = test_issubvolume(subvol);
if (ret < 0) {
fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
return 12;
goto out;
}
if (!ret) {
fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
return 13;
ret = -1;
goto out;
}
fd = open_file_or_dir(subvol);
if (fd < 0) {
ret = -1;
fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
return 12;
goto out;
}
top_id = btrfs_list_get_path_rootid(fd);
if (!is_list_all)
if (is_list_all)
btrfs_list_setup_filter(&filter_set,
BTRFS_LIST_FILTER_FULL_PATH,
top_id);
else if (is_only_in_path)
btrfs_list_setup_filter(&filter_set,
BTRFS_LIST_FILTER_TOPID_EQUAL,
top_id);
ret = btrfs_list_subvols(fd, filter_set, comparer_set,
is_tab_result);
if (ret)
return 19;
return 0;
/* by default we shall print the following columns*/
btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
if (is_tab_result)
ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
BTRFS_LIST_LAYOUT_TABLE,
!is_list_all && !is_only_in_path, NULL);
else
ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
BTRFS_LIST_LAYOUT_DEFAULT,
!is_list_all && !is_only_in_path, NULL);
out:
if (filter_set)
btrfs_list_free_filter_set(filter_set);
if (comparer_set)
btrfs_list_free_comparer_set(comparer_set);
if (uerr)
usage(cmd_subvol_list_usage);
return ret;
}
static const char * const cmd_snapshot_usage[] = {
@ -612,7 +673,17 @@ static int cmd_subvol_get_default(int argc, char **argv)
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
default_id);
ret = btrfs_list_subvols(fd, filter_set, NULL, 0);
/* by default we shall print the following columns*/
btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
ret = btrfs_list_subvols_print(fd, filter_set, NULL,
BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
if (filter_set)
btrfs_list_free_filter_set(filter_set);
if (ret)
return 19;
return 0;
@ -699,6 +770,160 @@ static int cmd_find_new(int argc, char **argv)
return 0;
}
static const char * const cmd_subvol_show_usage[] = {
"btrfs subvolume show <subvol-path>",
"Show more information of the subvolume",
NULL
};
static int cmd_subvol_show(int argc, char **argv)
{
struct root_info get_ri;
struct btrfs_list_filter_set *filter_set;
char tstr[256];
char uuidparse[37];
char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
char raw_prefix[] = "\t\t\t\t";
u64 sv_id, mntid;
int fd = -1, mntfd = -1;
int ret = -1;
if (check_argc_exact(argc, 2))
usage(cmd_subvol_show_usage);
fullpath = realpath(argv[1], 0);
if (!fullpath) {
fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
argv[1], strerror(errno));
goto out;
}
ret = test_issubvolume(fullpath);
if (ret < 0) {
fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
goto out;
}
if (!ret) {
fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
ret = -1;
goto out;
}
ret = find_mount_root(fullpath, &mnt);
if (ret < 0) {
fprintf(stderr, "ERROR: find_mount_root failed on %s: "
"%s\n", fullpath, strerror(-ret));
goto out;
}
ret = -1;
svpath = get_subvol_name(mnt, fullpath);
fd = open_file_or_dir(fullpath);
if (fd < 0) {
fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
goto out;
}
sv_id = btrfs_list_get_path_rootid(fd);
if (sv_id < 0) {
fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
fullpath);
goto out;
}
mntfd = open_file_or_dir(mnt);
if (mntfd < 0) {
fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
goto out;
}
mntid = btrfs_list_get_path_rootid(mntfd);
if (mntid < 0) {
fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
goto out;
}
if (sv_id == BTRFS_FS_TREE_OBJECTID) {
printf("%s is btrfs root\n", fullpath);
goto out;
}
memset(&get_ri, 0, sizeof(get_ri));
get_ri.root_id = sv_id;
if (btrfs_get_subvol(mntfd, &get_ri)) {
fprintf(stderr, "ERROR: can't find '%s'\n",
svpath);
goto out;
}
ret = 0;
/* print the info */
printf("%s\n", fullpath);
printf("\tName: \t\t\t%s\n", get_ri.name);
if (uuid_is_null(get_ri.uuid))
strcpy(uuidparse, "-");
else
uuid_unparse(get_ri.uuid, uuidparse);
printf("\tuuid: \t\t\t%s\n", uuidparse);
if (uuid_is_null(get_ri.puuid))
strcpy(uuidparse, "-");
else
uuid_unparse(get_ri.puuid, uuidparse);
printf("\tParent uuid: \t\t%s\n", uuidparse);
if (get_ri.otime)
strftime(tstr, 256, "%Y-%m-%d %X",
localtime(&get_ri.otime));
else
strcpy(tstr, "-");
printf("\tCreation time: \t\t%s\n", tstr);
printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
printf("\tGen at creation: \t%llu\n", get_ri.ogen);
printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
printf("\tFlags: \t\t\treadonly\n");
else
printf("\tFlags: \t\t\t-\n");
/* print the snapshots of the given subvol if any*/
printf("\tSnapshot(s):\n");
filter_set = btrfs_list_alloc_filter_set();
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
(u64)get_ri.uuid);
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1, raw_prefix);
/* clean up */
if (get_ri.path)
free(get_ri.path);
if (get_ri.name)
free(get_ri.name);
if (get_ri.full_path)
free(get_ri.full_path);
if (filter_set)
btrfs_list_free_filter_set(filter_set);
out:
if (mntfd >= 0)
close(mntfd);
if (fd >= 0)
close(fd);
if (mnt)
free(mnt);
if (fullpath)
free(fullpath);
return ret;
}
const struct cmd_group subvolume_cmd_group = {
subvolume_cmd_group_usage, NULL, {
{ "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
@ -710,6 +935,7 @@ const struct cmd_group subvolume_cmd_group = {
{ "set-default", cmd_subvol_set_default,
cmd_subvol_set_default_usage, NULL, 0 },
{ "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
{ "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
{ 0, 0, 0, 0, 0 }
}
};

View File

@ -79,9 +79,6 @@ void help_ambiguous_token(const char *arg, const struct cmd_group *grp);
void help_command_group(const struct cmd_group *grp, int argc, char **argv);
/* common.c */
int open_file_or_dir(const char *fname);
extern const struct cmd_group subvolume_cmd_group;
extern const struct cmd_group filesystem_cmd_group;
extern const struct cmd_group balance_cmd_group;
@ -92,6 +89,10 @@ extern const struct cmd_group send_cmd_group;
extern const struct cmd_group receive_cmd_group;
extern const struct cmd_group quota_cmd_group;
extern const struct cmd_group qgroup_cmd_group;
extern const struct cmd_group replace_cmd_group;
extern const char * const cmd_send_usage[];
extern const char * const cmd_receive_usage[];
int cmd_subvolume(int argc, char **argv);
int cmd_filesystem(int argc, char **argv);
@ -103,6 +104,11 @@ int cmd_send(int argc, char **argv);
int cmd_receive(int argc, char **argv);
int cmd_quota(int argc, char **argv);
int cmd_qgroup(int argc, char **argv);
int cmd_replace(int argc, char **argv);
/* subvolume exported functions */
int test_issubvolume(char *path);
/* send.c */
int find_mount_root(const char *path, char **mount_root);
char *get_subvol_name(char *mnt, char *full_path);

View File

@ -1,46 +0,0 @@
/*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
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;
}

45
ctree.h
View File

@ -122,6 +122,13 @@ struct btrfs_trans_handle;
*/
#define BTRFS_NAME_LEN 255
/*
* Theoretical limit is larger, but we keep this down to a sane
* value. That should limit greatly the possibility of collisions on
* inode ref items.
*/
#define BTRFS_LINK_MAX 65535U
/* 32 bytes in various csum fields */
#define BTRFS_CSUM_SIZE 32
@ -424,6 +431,7 @@ struct btrfs_super_block {
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2)
#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO (1ULL << 3)
/*
* some patches floated around with a second compression method
* lets save that incompat here for when they do get in
@ -439,6 +447,7 @@ struct btrfs_super_block {
#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA (1ULL << 5)
#define BTRFS_FEATURE_INCOMPAT_RAID56 (1ULL << 7)
#define BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF (1ULL << 6)
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
@ -447,6 +456,7 @@ struct btrfs_super_block {
BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \
BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \
BTRFS_FEATURE_INCOMPAT_RAID56 | \
BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
@ -587,6 +597,13 @@ struct btrfs_inode_ref {
/* name goes here */
} __attribute__ ((__packed__));
struct btrfs_inode_extref {
__le64 parent_objectid;
__le64 index;
__le16 name_len;
__u8 name[0]; /* name goes here */
} __attribute__ ((__packed__));
struct btrfs_timespec {
__le64 sec;
__le32 nsec;
@ -964,6 +981,7 @@ struct btrfs_root {
*/
#define BTRFS_INODE_ITEM_KEY 1
#define BTRFS_INODE_REF_KEY 12
#define BTRFS_INODE_EXTREF_KEY 13
#define BTRFS_XATTR_ITEM_KEY 24
#define BTRFS_ORPHAN_ITEM_KEY 48
@ -1048,6 +1066,18 @@ struct btrfs_root {
#define BTRFS_QGROUP_LIMIT_KEY 244
#define BTRFS_QGROUP_RELATION_KEY 246
/*
* Persistently stores the io stats in the device tree.
* One key for all stats, (0, BTRFS_DEV_STATS_KEY, devid).
*/
#define BTRFS_DEV_STATS_KEY 249
/*
* Persistently stores the device replace state in the device tree.
* The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
*/
#define BTRFS_DEV_REPLACE_KEY 250
/*
* string items are for debugging. They just store a short string of
* data in the FS
@ -1090,19 +1120,15 @@ static inline u##bits btrfs_##name(struct extent_buffer *eb, \
type *s) \
{ \
unsigned long offset = (unsigned long)s; \
u##bits m; \
type *p = (type *) (eb->data + offset); \
memcpy(&m, &p->member, sizeof(m)); \
return le##bits##_to_cpu(m); \
return get_unaligned_le##bits(&p->member); \
} \
static inline void btrfs_set_##name(struct extent_buffer *eb, \
type *s, u##bits val) \
{ \
unsigned long offset = (unsigned long)s; \
u##bits m; \
type *p = (type *) (eb->data + offset); \
m = cpu_to_le##bits(val); \
memcpy(&p->member, &m, sizeof(m)); \
put_unaligned_le##bits(val, &p->member); \
}
#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
@ -1255,6 +1281,13 @@ BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
BTRFS_SETGET_STACK_FUNCS(stack_inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64);
/* struct btrfs_inode_extref */
BTRFS_SETGET_FUNCS(inode_extref_parent, struct btrfs_inode_extref,
parent_objectid, 64);
BTRFS_SETGET_FUNCS(inode_extref_name_len, struct btrfs_inode_extref,
name_len, 16);
BTRFS_SETGET_FUNCS(inode_extref_index, struct btrfs_inode_extref, index, 64);
/* struct btrfs_inode_item */
BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64);
BTRFS_SETGET_FUNCS(inode_sequence, struct btrfs_inode_item, sequence, 64);

View File

@ -436,6 +436,12 @@ int main(int ac, char **av)
radix_tree_init();
root = open_ctree(av[ac-1], &super, 0);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
return 1;
}
trans = btrfs_start_transaction(root, 1);
dir_oid = btrfs_super_root_dir(&super);
@ -479,6 +485,11 @@ int main(int ac, char **av)
btrfs_header_nritems(&root->node->node.header));
close_ctree(root, &super);
root = open_ctree("dbfile", &super, 0);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
return 1;
}
}
while(count--) {
ret = ops[op](trans, root, &radix);

View File

@ -1173,7 +1173,7 @@ int write_dev_supers(struct btrfs_root *root, struct btrfs_super_block *sb,
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes)
if (bytenr + BTRFS_SUPER_INFO_SIZE > device->total_bytes)
break;
btrfs_set_super_bytenr(sb, bytenr);

View File

@ -3410,7 +3410,7 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
}
while(1) {
cache = btrfs_lookup_block_group(fs_info, start);
cache = btrfs_lookup_first_block_group(fs_info, start);
if (!cache)
break;
start = cache->key.objectid + cache->key.offset;

View File

@ -35,13 +35,12 @@
#include "utils.h"
#include "crc32c.h"
static int verbose = 0;
static u16 csum_size = 0;
static u64 search_objectid = BTRFS_ROOT_TREE_OBJECTID;
static void usage()
{
fprintf(stderr, "Usage: find-roots [-v] <device>\n");
fprintf(stderr, "Usage: find-roots [-o search_objectid] <device>\n");
}
int csum_block(void *buf, u32 len)
@ -415,11 +414,8 @@ int main(int argc, char **argv)
int opt;
int ret;
while ((opt = getopt(argc, argv, "vo:")) != -1) {
while ((opt = getopt(argc, argv, "o:")) != -1) {
switch(opt) {
case 'v':
verbose++;
break;
case 'o':
errno = 0;
search_objectid = (u64)strtoll(optarg, NULL,
@ -449,8 +445,11 @@ int main(int argc, char **argv)
root = open_ctree_broken(dev_fd, argv[optind]);
close(dev_fd);
if (!root)
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
csum_size = btrfs_super_csum_size(&root->fs_info->super_copy);
ret = find_root(root);

86
ioctl.h
View File

@ -32,6 +32,8 @@ struct btrfs_ioctl_vol_args {
char name[BTRFS_PATH_NAME_MAX + 1];
};
#define BTRFS_DEVICE_PATH_NAME_MAX 1024
#define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0)
#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
#define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2)
@ -108,7 +110,48 @@ struct btrfs_ioctl_scrub_args {
__u64 unused[(1024-32-sizeof(struct btrfs_scrub_progress))/8];
};
#define BTRFS_DEVICE_PATH_NAME_MAX 1024
#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS 0
#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID 1
struct btrfs_ioctl_dev_replace_start_params {
__u64 srcdevid; /* in, if 0, use srcdev_name instead */
__u64 cont_reading_from_srcdev_mode; /* in, see #define
* above */
__u8 srcdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
__u8 tgtdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
};
#define BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED 0
#define BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED 1
#define BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED 2
#define BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED 3
#define BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED 4
struct btrfs_ioctl_dev_replace_status_params {
__u64 replace_state; /* out, see #define above */
__u64 progress_1000; /* out, 0 <= x <= 1000 */
__u64 time_started; /* out, seconds since 1-Jan-1970 */
__u64 time_stopped; /* out, seconds since 1-Jan-1970 */
__u64 num_write_errors; /* out */
__u64 num_uncorrectable_read_errors; /* out */
};
#define BTRFS_IOCTL_DEV_REPLACE_CMD_START 0
#define BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS 1
#define BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL 2
#define BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR 0
#define BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED 1
#define BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED 2
struct btrfs_ioctl_dev_replace_args {
__u64 cmd; /* in */
__u64 result; /* out */
union {
struct btrfs_ioctl_dev_replace_start_params start;
struct btrfs_ioctl_dev_replace_status_params status;
}; /* in/out */
__u64 spare[64];
};
struct btrfs_ioctl_dev_info_args {
__u64 devid; /* in/out */
__u8 uuid[BTRFS_UUID_SIZE]; /* in/out */
@ -349,6 +392,39 @@ struct btrfs_ioctl_qgroup_create_args {
__u64 qgroupid;
};
enum btrfs_dev_stat_values {
/* disk I/O failure stats */
BTRFS_DEV_STAT_WRITE_ERRS, /* EIO or EREMOTEIO from lower layers */
BTRFS_DEV_STAT_READ_ERRS, /* EIO or EREMOTEIO from lower layers */
BTRFS_DEV_STAT_FLUSH_ERRS, /* EIO or EREMOTEIO from lower layers */
/* stats for indirect indications for I/O failures */
BTRFS_DEV_STAT_CORRUPTION_ERRS, /* checksum error, bytenr error or
* contents is illegal: this is an
* indication that the block was damaged
* during read or write, or written to
* wrong location or read from wrong
* location */
BTRFS_DEV_STAT_GENERATION_ERRS, /* an indication that blocks have not
* been written */
BTRFS_DEV_STAT_VALUES_MAX
};
/* Reset statistics after reading; needs SYS_ADMIN capability */
#define BTRFS_DEV_STATS_RESET (1ULL << 0)
struct btrfs_ioctl_get_dev_stats {
__u64 devid; /* in */
__u64 nr_items; /* in/out */
__u64 flags; /* in/out */
/* out values: */
__u64 values[BTRFS_DEV_STAT_VALUES_MAX];
__u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */
};
/* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
struct btrfs_ioctl_vol_args)
@ -419,7 +495,8 @@ struct btrfs_ioctl_clone_range_args {
struct btrfs_ioctl_ino_path_args)
#define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
struct btrfs_ioctl_ino_path_args)
#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
struct btrfs_ioctl_received_subvol_args)
#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
@ -432,4 +509,9 @@ struct btrfs_ioctl_clone_range_args {
struct btrfs_ioctl_qgroup_create_args)
#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
struct btrfs_ioctl_qgroup_limit_args)
#define BTRFS_IOC_GET_DEV_STATS _IOWR(BTRFS_IOCTL_MAGIC, 52, \
struct btrfs_ioctl_get_dev_stats)
#define BTRFS_IOC_DEV_REPLACE _IOWR(BTRFS_IOCTL_MAGIC, 53, \
struct btrfs_ioctl_dev_replace_args)
#endif

View File

@ -271,6 +271,19 @@ typedef u64 __bitwise __be64;
#define cpu_to_le16(x) ((__force __le16)(u16)(x))
#define le16_to_cpu(x) ((__force u16)(__le16)(x))
#endif
struct __una_u16 { u16 x; } __attribute__((__packed__));
struct __una_u32 { u32 x; } __attribute__((__packed__));
struct __una_u64 { u64 x; } __attribute__((__packed__));
#define get_unaligned_le8(p) (*((u8 *)(p)))
#define put_unaligned_le8(val,p) ((*((u8 *)(p))) = (val))
#define get_unaligned_le16(p) le16_to_cpu(((const struct __una_u16 *)(p))->x)
#define put_unaligned_le16(val,p) (((struct __una_u16 *)(p))->x = cpu_to_le16(val))
#define get_unaligned_le32(p) le32_to_cpu(((const struct __una_u32 *)(p))->x)
#define put_unaligned_le32(val,p) (((struct __una_u32 *)(p))->x = cpu_to_le32(val))
#define get_unaligned_le64(p) le64_to_cpu(((const struct __una_u64 *)(p))->x)
#define put_unaligned_le64(val,p) (((struct __una_u64 *)(p))->x = cpu_to_le64(val))
#endif
#ifndef noinline

View File

@ -1,4 +1,4 @@
GZIP=gzip
GZIPCMD=gzip
INSTALL= install
prefix ?= /usr/local
@ -6,31 +6,24 @@ bindir = $(prefix)/bin
mandir = $(prefix)/man
man8dir = $(mandir)/man8
# clear out all suffixes
.SUFFIXES:
# list only those we use
.SUFFIXES: .in .gz
MANPAGES = mkfs.btrfs.8.gz btrfsctl.8.gz btrfsck.8.gz btrfs-image.8.gz \
btrfs-show.8.gz btrfs.8.gz
INFILES = ${MANPAGES:.in=.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
btrfsck.8.gz: btrfsck.8.in
$(GZIP) -n -c btrfsck.8.in > btrfsck.8.gz
btrfs-image.8.gz: btrfs-image.8.in
$(GZIP) -n -c btrfs-image.8.in > btrfs-image.8.gz
btrfs-show.8.gz: btrfs-show.8.in
$(GZIP) -n -c btrfs-show.8.in > btrfs-show.8.gz
.in.gz :
@echo " [MAN] $@"
$(Q)$(GZIPCMD) -n -c $< > $@
clean :
rm -f $(MANPAGES)
@echo "Cleaning manpages"
$(Q)rm -f $(MANPAGES)
install: $(MANPAGES)
$(INSTALL) -m755 -d $(DESTDIR)$(man8dir)

View File

@ -11,12 +11,16 @@ btrfs \- control a btrfs filesystem
.PP
\fBbtrfs\fP \fBsubvolume create\fP\fI [<dest>/]<name>\fP
.PP
\fBbtrfs\fP \fBsubvolume list\fP\fI [-aprts] [-g [+|-]value] [-c [+|-]value] [--rootid=rootid,gen,ogen,path] <path>\fP
\fBbtrfs\fP \fBsubvolume list\fP\fI [-acgoprts] [-G [+|-]value] [-C [+|-]value] [--sort=rootid,gen,ogen,path] <path>\fP
.PP
\fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP
.PP
\fBbtrfs\fP \fBsubvolume get-default\fP\fI <path>\fP
.PP
\fBbtrfs\fP \fBsubvolume find-new\fP\fI <subvolume> <last_gen>\fP
.PP
\fBbtrfs\fP \fBsubvolume show\fP\fI <path>\fP
.PP
\fBbtrfs\fP \fBfilesystem defragment\fP -c[zlib|lzo] [-l \fIlen\fR] \
[-s \fIstart\fR] [-t \fIsize\fR] -[vf] <\fIfile\fR>|<\fIdir\fR> \
[<\fIfile\fR>|<\fIdir\fR>...]
@ -27,16 +31,22 @@ btrfs \- control a btrfs filesystem
.PP
\fBbtrfs\fP \fBfilesystem label\fP\fI <dev> [newlabel]\fP
.PP
\fBbtrfs\fP \fBsubvolume find-new\fP\fI <subvolume> <last_gen>\fP
.PP
\fBbtrfs\fP \fBfilesystem balance\fP\fI <path> \fP
.PP
\fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices|<device> [<device>...]]\fP
.PP
\fBbtrfs\fP \fBdevice stats\fP [-z] {\fI<path>\fP|\fI<device>\fP}
.PP
\fBbtrfs\fP \fBdevice add\fP\fI <device> [<device>...] <path> \fP
.PP
\fBbtrfs\fP \fBdevice delete\fP\fI <device> [<device>...] <path> \fP
.PP
\fBbtrfs\fP \fBreplace start\fP \fI[-Bfr] <srcdev>|<devid> <targetdev> <path>\fP
.PP
\fBbtrfs\fP \fBreplace status\fP \fI[-1] <path>\fP
.PP
\fBbtrfs\fP \fBreplace cancel\fP \fI<path>\fP
.PP
\fBbtrfs\fP \fBscrub start\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
.PP
\fBbtrfs\fP \fBscrub cancel\fP {\fI<path>\fP|\fI<device>\fP}
@ -106,7 +116,7 @@ Create a subvolume in \fI<dest>\fR (or in the current directory if
\fI<dest>\fR is omitted).
.TP
\fBsubvolume list\fR\fI [-aprts][-g [+|-]value] [-c [+|-]value] [--sort=gen,ogen,rootid,path] <path>\fR
\fBsubvolume list\fR\fI [-acgoprts] [-G [+|-]value] [-C [+|-]value] [--sort=rootid,gen,ogen,path] <path>\fR
.RS
List the subvolumes present in the filesystem \fI<path>\fR. For every
subvolume the following information is shown by default.
@ -115,31 +125,40 @@ where path is the relative path of the subvolume to the \fItop level\fR
subvolume.
The subvolume's ID may be used by the \fBsubvolume set-default\fR command, or
at mount time via the \fIsubvol=\fR option.
at mount time via the \fIsubvolid=\fR option.
If \fI-p\fR is given, then \fIparent <ID>\fR is added to the output between ID
and top level. The parent's ID may be used at mount time via the
\fIsubvolrootid=\fR option.
\fB-t\fP print the result as a table.
\fB-a\fP print all the subvolumes in the filesystem.
\fB-a\fP print all the subvolumes in the filesystem and distinguish between
absolute and relative path with respect to the given <path>.
\fB-r\fP only readonly subvolumes in the filesystem wille be listed.
\fB-c\fP print the ogeneration of the subvolume, aliases: ogen or origin generation
\fB-s\fP only snapshot subvolumes in the filesystem will be listed.
\fB-g\fP print the generation of the subvolume
\fB-g [+|-]value\fP
\fB-u\fP print the UUID of the subvolume
\fB-o\fP print only subvolumes bellow specified <path>.
\fB-r\fP only readonly subvolumes in the filesystem will be listed.
\fB-s\fP only snapshot subvolumes in the filesystem will be listed.
\fB-G [+|-]value\fP
list subvolumes in the filesystem that its generation is
>=, <= or = value. '+' means >= value, '-' means <= value, If there is
neither '+' nor '-', it means = value.
\fB-c [+|-]value\fP
\fB-C [+|-]value\fP
list subvolumes in the filesystem that its ogeneration is
>=, <= or = value. The usage is the same to '-g' option.
\fB--sort=gen,ogen,path,rootid\fP
\fB--sort=rootid,gen,ogen,path\fP
list subvolumes in order by specified items.
you can add '+' or '-' in front of each items, '+' means ascending,'-'
you can add '+' or '-' in front of each items, '+' means ascending, '-'
means descending. The default is ascending.
for \fB--sort\fP you can combine some items together by ',', just like
@ -158,6 +177,14 @@ Get the default subvolume of the filesystem \fI<path>\fR. The output format
is similar to \fBsubvolume list\fR command.
.TP
\fBsubvolume find-new\fR\fI <subvolume> <last_gen>\fR
List the recently modified files in a subvolume, after \fI<last_gen>\fR ID.
.TP
\fBsubvolume show\fR\fI <path>\fR
Show information of a given subvolume in the \fI<path>\fR.
.TP
\fBfilesystem defragment\fP -c[zlib|lzo] [-l \fIlen\fR] [-s \fIstart\fR] \
[-t \fIsize\fR] -[vf] <\fIfile\fR>|<\fIdir\fR> [<\fIfile\fR>|<\fIdir\fR>...]
@ -182,15 +209,14 @@ defragment operations.
\fB-t size\fP defragment only files at least \fIsize\fR bytes big
For \fBstart\fP, \fBlen\fP, \fBsize\fP it is possible to append a suffix
like \fBk\fP for 1 KBytes, \fBm\fP for 1 MBytes...
NOTE: defragmenting with kernels up to 2.6.37 will unlink COW-ed copies of data,
don't use it if you use snapshots, have de-duplicated your data or made
copies with \fBcp --reflink\fP.
.TP
\fBsubvolume find-new\fR\fI <subvolume> <last_gen>\fR
List the recently modified files in a subvolume, after \fI<last_gen>\fR ID.
.TP
\fBfilesystem sync\fR\fI <path> \fR
Force a sync for the filesystem identified by \fI<path>\fR.
.TP
@ -252,6 +278,18 @@ Balance the chunks of the filesystem identified by \fI<path>\fR
across the devices.
.TP
\fBdevice stats\fP [-z] {\fI<path>\fP|\fI<device>\fP}
Read and print the device IO stats for all devices of the filesystem
identified by \fI<path>\fR or for a single \fI<device>\fR.
.RS
\fIOptions\fR
.TP
.B -z
Reset stats to zero after reading them.
.RE
.TP
\fBdevice add\fR\fI <dev> [<dev>..] <path>\fR
Add device(s) to the filesystem identified by \fI<path>\fR.
.TP
@ -268,6 +306,54 @@ Finally, if \fB--all-devices\fP is passed, all the devices under /dev are
scanned.
.TP
\fBreplace start\fR \fI[-Bfr] <srcdev>|<devid> <targetdev> <path>\fR
Replace device of a btrfs filesystem.
On a live filesystem, duplicate the data to the target device which
is currently stored on the source device. If the source device is not
available anymore, or if the \fB-r\fR option is set, the data is built
only using the RAID redundancy mechanisms. After completion of the
operation, the source device is removed from the filesystem.
If the \fIsrcdev\fR is a numerical value, it is assumed to be the device id
of the filesystem which is mounted at mount_point, otherwise is is
the path to the source device. If the source device is disconnected,
from the system, you have to use the \fIdevid\fR parameter format.
The targetdev needs to be same size or larger than the \fIsrcdev\fR.
.RS
\fIOptions\fR
.TP
.B -r
only read from \fIsrcdev\fR if no other zero-defect mirror exists (enable
this if your drive has lots of read errors, the access would be very slow)
.TP
.B -f
force using and overwriting \fItargetdev\fR even if it looks like
containing a valid btrfs filesystem. A valid filesystem is
assumed if a btrfs superblock is found which contains a
correct checksum. Devices which are currently mounted are
never allowed to be used as the \fItargetdev\fR
.TP
.B -B
do not background
.RE
.TP
\fBreplace status\fR \fI[-1] <path>\fR
Print status and progress information of a running device replace operation.
.RS
\fIOptions\fR
.TP
.B -1
print once instead of print continously until the replace
operation finishes (or is canceled)
.RE
.TP
\fBreplace cancel\fR \fI<path>\fR
Cancel a running device replace operation.
.TP
\fBscrub start\fP [-Bdqru] {\fI<path>\fP|\fI<device>\fP}
Start a scrub on all devices of the filesystem identified by \fI<path>\fR or on
a single \fI<device>\fR. Without options, scrub is started as a background

View File

@ -47,7 +47,10 @@ Specify a label for the filesystem.
.TP
\fB\-m\fR, \fB\-\-metadata \fIprofile\fR
Specify how metadata must be spanned across the devices specified. Valid
values are raid0, raid1, raid10 or single.
values are raid0, raid1, raid10, single or dup. Single device will have dup
set by default except in the case of SSDs which will default to single. This is
because SSDs can remap blocks internally so duplicate blocks could end up in the
same erase block which negates the benefits of doing metadata duplication.
.TP
\fB\-M\fR, \fB\-\-mixed\fR
Mix data and metadata chunks together for more efficient space
@ -69,6 +72,9 @@ Do not perform whole device TRIM operation by default.
.TP
\fB\-V\fR, \fB\-\-version\fR
Print the \fBmkfs.btrfs\fP version and exit.
.SH UNIT
As default the unit is the byte, however it is possible to append a suffix
to the arguments like \fBk\fP for KBytes, \fBm\fP for MBytes...
.SH AVAILABILITY
.B mkfs.btrfs
is part of btrfs-progs. Btrfs is currently under heavy development,

92
mkfs.c
View File

@ -39,6 +39,7 @@
#include <linux/fs.h>
#include <ctype.h>
#include <attr/xattr.h>
#include <blkid/blkid.h>
#include "ctree.h"
#include "disk-io.h"
#include "volumes.h"
@ -204,7 +205,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
static int create_raid_groups(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 data_profile,
int data_profile_opt, u64 metadata_profile,
int metadata_profile_opt, int mixed)
int metadata_profile_opt, int mixed, int ssd)
{
u64 num_devices = btrfs_super_num_devices(&root->fs_info->super_copy);
u64 allowed = 0;
@ -216,8 +217,12 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
* For mixed groups defaults are single/single.
*/
if (!metadata_profile_opt && !mixed) {
if (num_devices == 1 && ssd)
printf("Detected a SSD, turning off metadata "
"duplication. Mkfs with -m dup if you want to "
"force metadata duplication.\n");
metadata_profile = (num_devices > 1) ?
BTRFS_BLOCK_GROUP_RAID1 : BTRFS_BLOCK_GROUP_DUP;
BTRFS_BLOCK_GROUP_RAID1 : (ssd) ? 0: BTRFS_BLOCK_GROUP_DUP;
}
if (!data_profile_opt && !mixed) {
data_profile = (num_devices > 1) ?
@ -366,7 +371,6 @@ static u64 parse_profile(char *s)
static char *parse_label(char *input)
{
int i;
int len = strlen(input);
if (len >= BTRFS_LABEL_SIZE) {
@ -374,12 +378,6 @@ static char *parse_label(char *input)
BTRFS_LABEL_SIZE - 1);
exit(1);
}
for (i = 0; i < len; i++) {
if (input[i] == '/' || input[i] == '\\') {
fprintf(stderr, "invalid label %s\n", input);
exit(1);
}
}
return strdup(input);
}
@ -1207,6 +1205,54 @@ static int check_leaf_or_node_size(u32 size, u32 sectorsize)
return 0;
}
static int is_ssd(const char *file)
{
char *devname;
blkid_probe probe;
char *dev;
char path[PATH_MAX];
dev_t disk;
int fd;
char rotational;
probe = blkid_new_probe_from_filename(file);
if (!probe)
return 0;
/*
* We want to use blkid_devno_to_wholedisk() but it's broken for some
* reason on F17 at least so we'll do this trickery
*/
disk = blkid_probe_get_wholedisk_devno(probe);
if (!disk)
return 0;
devname = blkid_devno_to_devname(disk);
if (!devname)
return 0;
dev = strrchr(devname, '/');
dev++;
snprintf(path, PATH_MAX, "/sys/block/%s/queue/rotational", dev);
free(devname);
blkid_free_probe(probe);
fd = open(path, O_RDONLY);
if (fd < 0) {
return 0;
}
if (read(fd, &rotational, sizeof(char)) < sizeof(char)) {
close(fd);
return 0;
}
close(fd);
return !atoi((const char *)&rotational);
}
int main(int ac, char **av)
{
char *file;
@ -1220,7 +1266,7 @@ int main(int ac, char **av)
u64 alloc_start = 0;
u64 metadata_profile = 0;
u64 data_profile = 0;
u32 leafsize = getpagesize();
u32 leafsize = sysconf(_SC_PAGESIZE);
u32 sectorsize = 4096;
u32 nodesize = leafsize;
u32 stripesize = 4096;
@ -1233,6 +1279,7 @@ int main(int ac, char **av)
int data_profile_opt = 0;
int metadata_profile_opt = 0;
int nodiscard = 0;
int ssd = 0;
char *source_dir = NULL;
int source_dir_set = 0;
@ -1240,6 +1287,8 @@ int main(int ac, char **av)
u64 size_of_data = 0;
u64 source_dir_size = 0;
char *pretty_buf;
struct btrfs_super_block *super;
u64 flags;
while(1) {
int c;
@ -1296,7 +1345,7 @@ int main(int ac, char **av)
print_usage();
}
}
sectorsize = max(sectorsize, (u32)getpagesize());
sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
if (check_leaf_or_node_size(leafsize, sectorsize))
exit(1);
if (check_leaf_or_node_size(nodesize, sectorsize))
@ -1351,7 +1400,12 @@ int main(int ac, char **av)
fprintf(stderr, "unable to zero the output file\n");
exit(1);
}
/* our "device" is the new image file */
dev_block_count = block_count;
}
ssd = is_ssd(file);
if (mixed) {
if (metadata_profile != data_profile) {
fprintf(stderr, "With mixed block groups data and metadata "
@ -1376,7 +1430,8 @@ int main(int ac, char **av)
root = open_ctree(file, 0, O_RDWR);
if (!root) {
fprintf(stderr, "ctree init failed\n");
fprintf(stderr, "Open ctree failed\n");
close(fd);
exit(1);
}
root->fs_info->alloc_start = alloc_start;
@ -1437,20 +1492,21 @@ raid_groups:
if (!source_dir_set) {
ret = create_raid_groups(trans, root, data_profile,
data_profile_opt, metadata_profile,
metadata_profile_opt, mixed);
metadata_profile_opt, mixed, ssd);
BUG_ON(ret);
}
ret = create_data_reloc_tree(trans, root);
BUG_ON(ret);
if (mixed) {
struct btrfs_super_block *super = &root->fs_info->super_copy;
u64 flags = btrfs_super_incompat_flags(super);
super = &root->fs_info->super_copy;
flags = btrfs_super_incompat_flags(super);
flags |= BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF;
if (mixed)
flags |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS;
btrfs_set_super_incompat_flags(super, flags);
}
btrfs_set_super_incompat_flags(super, flags);
if ((data_profile | metadata_profile) &
(BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)) {

View File

@ -61,6 +61,42 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item,
return 0;
}
static int print_inode_extref_item(struct extent_buffer *eb,
struct btrfs_item *item,
struct btrfs_inode_extref *extref)
{
u32 total;
u32 cur = 0;
u32 len;
u32 name_len = 0;
u64 index = 0;
u64 parent_objid;
char namebuf[BTRFS_NAME_LEN];
total = btrfs_item_size(eb, item);
while (cur < total) {
index = btrfs_inode_extref_index(eb, extref);
name_len = btrfs_inode_extref_name_len(eb, extref);
parent_objid = btrfs_inode_extref_parent(eb, extref);
len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf);
read_extent_buffer(eb, namebuf, (unsigned long)(extref->name), len);
printf("\t\tinode extref index %llu parent %llu namelen %u "
"name: %.*s\n",
(unsigned long long)index,
(unsigned long long)parent_objid,
name_len, len, namebuf);
len = sizeof(*extref) + name_len;
extref = (struct btrfs_inode_extref *)((char *)extref + len);
cur += len;
}
return 0;
}
static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *item,
struct btrfs_inode_ref *ref)
{
@ -371,6 +407,9 @@ static void print_key_type(u64 objectid, u8 type)
case BTRFS_INODE_REF_KEY:
printf("INODE_REF");
break;
case BTRFS_INODE_EXTREF_KEY:
printf("INODE_EXTREF");
break;
case BTRFS_DIR_ITEM_KEY:
printf("DIR_ITEM");
break;
@ -440,6 +479,9 @@ static void print_key_type(u64 objectid, u8 type)
case BTRFS_BALANCE_ITEM_KEY:
printf("BALANCE_ITEM");
break;
case BTRFS_DEV_REPLACE_KEY:
printf("DEV_REPLACE_ITEM");
break;
case BTRFS_STRING_ITEM_KEY:
printf("STRING_ITEM");
break;
@ -455,6 +497,9 @@ static void print_key_type(u64 objectid, u8 type)
case BTRFS_QGROUP_LIMIT_KEY:
printf("BTRFS_QGROUP_LIMIT_KEY");
break;
case BTRFS_DEV_STATS_KEY:
printf("DEV_STATS_ITEM");
break;
default:
printf("UNKNOWN.%d", type);
};
@ -576,6 +621,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
struct btrfs_extent_data_ref *dref;
struct btrfs_shared_data_ref *sref;
struct btrfs_inode_ref *iref;
struct btrfs_inode_extref *iref2;
struct btrfs_dev_extent *dev_extent;
struct btrfs_disk_key disk_key;
struct btrfs_block_group_item bg_item;
@ -623,6 +669,10 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
iref = btrfs_item_ptr(l, i, struct btrfs_inode_ref);
print_inode_ref_item(l, item, iref);
break;
case BTRFS_INODE_EXTREF_KEY:
iref2 = btrfs_item_ptr(l, i, struct btrfs_inode_extref);
print_inode_extref_item(l, item, iref2);
break;
case BTRFS_DIR_ITEM_KEY:
case BTRFS_DIR_INDEX_KEY:
case BTRFS_XATTR_ITEM_KEY:
@ -777,6 +827,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
str = l->data + btrfs_item_ptr_offset(l, i);
printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str);
break;
case BTRFS_DEV_STATS_KEY:
printf("\t\tdevice stats\n");
break;
};
fflush(stdout);
}

View File

@ -22,15 +22,29 @@
u64 parse_qgroupid(char *p)
{
char *s = strchr(p, '/');
char *ptr_src_end = p + strlen(p);
char *ptr_parse_end = NULL;
u64 level;
u64 id;
if (!s)
return atoll(p);
level = atoll(p);
id = atoll(s + 1);
if (!s) {
id = strtoull(p, &ptr_parse_end, 10);
if (ptr_parse_end != ptr_src_end)
goto err;
return id;
}
level = strtoull(p, &ptr_parse_end, 10);
if (ptr_parse_end != s)
goto err;
id = strtoull(s+1, &ptr_parse_end, 10);
if (ptr_parse_end != ptr_src_end)
goto err;
return (level << 48) | id;
err:
fprintf(stderr, "ERROR:invalid qgroupid\n");
exit(-1);
}
int qgroup_inherit_size(struct btrfs_qgroup_inherit *p)

View File

@ -20,7 +20,9 @@
#define _BTRFS_QGROUP_H
#include "ioctl.h"
#include "kerncompat.h"
u64 parse_qgroupid(char *p);
int qgroup_inherit_size(struct btrfs_qgroup_inherit *p);
int qgroup_inherit_realloc(struct btrfs_qgroup_inherit **inherit,
int incgroups, int inccopies);

View File

@ -52,6 +52,10 @@ int main(int ac, char **av) {
radix_tree_init();
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
trans = btrfs_start_transaction(root, 1);
srand(55);
btrfs_set_key_type(&ins, BTRFS_STRING_ITEM_KEY);
@ -75,6 +79,10 @@ int main(int ac, char **av) {
close_ctree(root);
exit(1);
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
printf("starting search\n");
srand(55);
for (i = 0; i < run_size; i++) {
@ -94,6 +102,10 @@ int main(int ac, char **av) {
close_ctree(root);
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
printf("node %p level %d total ptrs %d free spc %lu\n", root->node,
btrfs_header_level(root->node),
btrfs_header_nritems(root->node),
@ -122,6 +134,10 @@ int main(int ac, char **av) {
close_ctree(root);
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
trans = btrfs_start_transaction(root, 1);
srand(128);
for (i = 0; i < run_size; i++) {
@ -138,6 +154,10 @@ int main(int ac, char **av) {
close_ctree(root);
root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, O_RDWR);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
srand(128);
printf("starting search2\n");
for (i = 0; i < run_size; i++) {

View File

@ -356,6 +356,10 @@ int main(int ac, char **av)
struct btrfs_trans_handle *trans;
radix_tree_init();
root = open_ctree("dbfile", &super);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
exit(1);
}
fill_radix(root, &radix);
signal(SIGTERM, sigstopper);
@ -398,6 +402,10 @@ int main(int ac, char **av)
btrfs_header_nritems(&root->node->node.header));
close_ctree(root, &super);
root = open_ctree("dbfile", &super);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
goto out;
}
}
while(count--) {
ret = ops[op](trans, root, &radix);

View File

@ -240,7 +240,8 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
memcpy(&root_item, root_item_ptr,
sizeof(root_item));
root_item_valid = 1;
} else if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
} else if (sh->type == BTRFS_ROOT_BACKREF_KEY ||
root_item_valid) {
if (!root_item_valid)
goto skip;
@ -274,7 +275,6 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
subvol_uuid_search_add(s, si);
root_item_valid = 0;
} else {
root_item_valid = 0;
goto skip;
}

View File

@ -34,6 +34,7 @@ btrfs_start_transaction(struct btrfs_root *root, int num_blocks)
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_trans_handle *h = malloc(sizeof(*h));
BUG_ON(!h);
BUG_ON(root->commit_root);
BUG_ON(fs_info->running_transaction);
fs_info->running_transaction = h;

133
utils.c
View File

@ -16,10 +16,12 @@
* Boston, MA 021110-1307, USA.
*/
#define _XOPEN_SOURCE 600
#define __USE_XOPEN2K
#define _XOPEN_SOURCE 700
#define __USE_XOPEN2K8
#define __XOPEN2K8 /* due to an error in dirent.h, to get dirfd() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef __CHECKER__
#include <sys/ioctl.h>
#include <sys/mount.h>
@ -652,21 +654,22 @@ int is_loop_device (const char* device) {
* the associated file (e.g. /images/my_btrfs.img) */
int resolve_loop_device(const char* loop_dev, char* loop_file, int max_len)
{
int loop_fd;
int ret_ioctl;
struct loop_info loopinfo;
int ret;
FILE *f;
char fmt[20];
char p[PATH_MAX];
char real_loop_dev[PATH_MAX];
if ((loop_fd = open(loop_dev, O_RDONLY)) < 0)
if (!realpath(loop_dev, real_loop_dev))
return -errno;
snprintf(p, PATH_MAX, "/sys/block/%s/loop/backing_file", strrchr(real_loop_dev, '/'));
if (!(f = fopen(p, "r")))
return -errno;
ret_ioctl = ioctl(loop_fd, LOOP_GET_STATUS, &loopinfo);
close(loop_fd);
if (ret_ioctl == 0) {
strncpy(loop_file, loopinfo.lo_name, max_len);
if (max_len > 0)
loop_file[max_len-1] = 0;
} else
snprintf(fmt, 20, "%%%i[^\n]", max_len-1);
ret = fscanf(f, fmt, loop_file);
fclose(f);
if (ret == EOF)
return -errno;
return 0;
@ -1016,8 +1019,14 @@ again:
}
fd = open(fullpath, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to read %s: %s\n", fullpath,
strerror(errno));
/* ignore the following errors:
ENXIO (device don't exists)
ENOMEDIUM (No medium found ->
like a cd tray empty)
*/
if(errno != ENXIO && errno != ENOMEDIUM)
fprintf(stderr, "failed to read %s: %s\n",
fullpath, strerror(errno));
continue;
}
ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices,
@ -1268,3 +1277,95 @@ u64 parse_size(char *s)
return strtoull(s, NULL, 10) * mult;
}
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;
}
int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args)
{
int ret;
di_args->devid = devid;
memset(&di_args->uuid, '\0', sizeof(di_args->uuid));
ret = ioctl(fd, BTRFS_IOC_DEV_INFO, di_args);
return ret ? -errno : 0;
}
int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret)
{
int ret = 0;
int ndevs = 0;
int i = 1;
struct btrfs_fs_devices *fs_devices_mnt = NULL;
struct btrfs_ioctl_dev_info_args *di_args;
char mp[BTRFS_PATH_NAME_MAX + 1];
memset(fi_args, 0, sizeof(*fi_args));
ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
if (ret && (errno == EINVAL || errno == ENOTTY)) {
/* path is not a mounted btrfs. Try if it's a device */
ret = check_mounted_where(fd, path, mp, sizeof(mp),
&fs_devices_mnt);
if (!ret)
return -EINVAL;
if (ret < 0)
return ret;
fi_args->num_devices = 1;
fi_args->max_id = fs_devices_mnt->latest_devid;
i = fs_devices_mnt->latest_devid;
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
close(fd);
fd = open_file_or_dir(mp);
if (fd < 0)
return -errno;
} else if (ret) {
return -errno;
}
if (!fi_args->num_devices)
return 0;
di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
if (!di_args)
return -errno;
for (; i <= fi_args->max_id; ++i) {
BUG_ON(ndevs >= fi_args->num_devices);
ret = get_device_info(fd, i, &di_args[ndevs]);
if (ret == -ENODEV)
continue;
if (ret)
return ret;
ndevs++;
}
BUG_ON(ndevs == 0);
return 0;
}

View File

@ -19,6 +19,8 @@
#ifndef __UTILS__
#define __UTILS__
#include "ctree.h"
#define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024)
int make_btrfs(int fd, const char *device, const char *label,
@ -44,8 +46,11 @@ int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
char *pretty_sizes(u64 size);
int check_label(char *input);
int get_mountpt(char *dev, char *mntpt, size_t size);
int btrfs_scan_block_devices(int run_ioctl);
u64 parse_size(char *s);
int open_file_or_dir(const char *fname);
int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args);
int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret);
#endif

View File

@ -1385,6 +1385,22 @@ struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
return NULL;
}
struct btrfs_device *btrfs_find_device_by_devid(struct btrfs_root *root,
u64 devid, int instance)
{
struct list_head *head = &root->fs_info->fs_devices->devices;
struct btrfs_device *dev;
struct list_head *cur;
int num_found = 0;
list_for_each(cur, head) {
dev = list_entry(cur, struct btrfs_device, dev_list);
if (dev->devid == devid && num_found++ == instance)
return dev;
}
return NULL;
}
int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree,
struct btrfs_fs_devices *fs_devices)
{

View File

@ -188,4 +188,6 @@ int btrfs_add_system_chunk(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_key *key,
struct btrfs_chunk *chunk, int item_size);
int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
struct btrfs_device *btrfs_find_device_by_devid(struct btrfs_root *root,
u64 devid, int instance);
#endif