summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Mason <chris.mason@fusionio.com>2013-02-06 12:42:24 -0500
committerChris Mason <chris.mason@fusionio.com>2013-02-06 12:42:24 -0500
commit7b1c567c84a6292c138db3bd1638e19e73e0e593 (patch)
tree9ab6983c6fa2cbb837e9b782d1020899e0d2d846
parent5ffe6597e708d74c02d72d79ebb4a8fd2181e227 (diff)
parent2161e1b6f35d1c084fda49b479951219117c86e9 (diff)
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>
-rw-r--r--Makefile105
-rw-r--r--btrfs-image.c5
-rw-r--r--btrfs-list.c278
-rw-r--r--btrfs-list.h59
-rw-r--r--btrfs-select-super.c4
-rw-r--r--btrfs-show-super.c282
-rw-r--r--btrfs.c5
-rw-r--r--btrfsck.c59
-rw-r--r--btrfsctl.c7
-rw-r--r--btrfslabel.c8
-rw-r--r--btrfstune.c5
-rw-r--r--cmds-balance.c3
-rw-r--r--cmds-device.c152
-rw-r--r--cmds-inspect.c1
-rw-r--r--cmds-qgroup.c34
-rw-r--r--cmds-quota.c1
-rw-r--r--cmds-receive.c6
-rw-r--r--cmds-replace.c579
-rw-r--r--cmds-scrub.c94
-rw-r--r--cmds-send.c110
-rw-r--r--cmds-subvolume.c284
-rw-r--r--commands.h12
-rw-r--r--common.c46
-rw-r--r--ctree.h45
-rw-r--r--dir-test.c11
-rw-r--r--disk-io.c2
-rw-r--r--extent-tree.c2
-rw-r--r--find-root.c13
-rw-r--r--ioctl.h86
-rw-r--r--kerncompat.h13
-rw-r--r--man/Makefile31
-rw-r--r--man/btrfs.8.in118
-rw-r--r--man/mkfs.btrfs.8.in8
-rw-r--r--mkfs.c92
-rw-r--r--print-tree.c53
-rw-r--r--qgroup.c22
-rw-r--r--qgroup.h2
-rw-r--r--quick-test.c20
-rw-r--r--random-test.c8
-rw-r--r--send-utils.c4
-rw-r--r--transaction.h1
-rw-r--r--utils.c133
-rw-r--r--utils.h9
-rw-r--r--volumes.c16
-rw-r--r--volumes.h2
45 files changed, 2352 insertions, 478 deletions
diff --git a/Makefile b/Makefile
index decf8ec0..596bf93f 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/btrfs-image.c b/btrfs-image.c
index f2bbcc83..7dc131db 100644
--- a/btrfs-image.c
+++ b/btrfs-image.c
@@ -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,
diff --git a/btrfs-list.c b/btrfs-list.c
index cb42fbc7..d02d6205 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -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,7 +74,7 @@ struct {
{
.name = "top level",
.column_name = "Top Level",
- .need_print = 1,
+ .need_print = 0,
},
{
.name = "otime",
@@ -128,6 +82,11 @@ struct {
.need_print = 0,
},
{
+ .name = "parent_uuid",
+ .column_name = "Parent UUID",
+ .need_print = 0,
+ },
+ {
.name = "uuid",
.column_name = "UUID",
.need_print = 0,
@@ -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)
- return ret;
+ 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, fd);
+ comp_set, top_id);
- print_all_volume_info(&root_sort, is_tab_result);
+ 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;
+
+ 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;
diff --git a/btrfs-list.h b/btrfs-list.h
index cde4b3cc..5d874549 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -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);
diff --git a/btrfs-select-super.c b/btrfs-select-super.c
index 51eb9c96..0c4f5c0a 100644
--- a/btrfs-select-super.c
+++ b/btrfs-select-super.c
@@ -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;
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
new file mode 100644
index 00000000..45eb1024
--- /dev/null
+++ b/btrfs-show-super.c
@@ -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));
+}
diff --git a/btrfs.c b/btrfs.c
index 687acec7..7b0e50fb 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -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 }
diff --git a/btrfsck.c b/btrfsck.c
index a8510085..dd0f18bf 100644
--- a/btrfsck.c
+++ b/btrfsck.c
@@ -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;
diff --git a/btrfsctl.c b/btrfsctl.c
index 518684c6..049a5f35 100644
--- a/btrfsctl.c
+++ b/btrfsctl.c
@@ -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) {
diff --git a/btrfslabel.c b/btrfslabel.c
index cb142b0a..a421a8b3 100644
--- a/btrfslabel.c
+++ b/btrfslabel.c
@@ -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);
}
diff --git a/btrfstune.c b/btrfstune.c
index 47830c5a..6950f74d 100644
--- a/btrfstune.c
+++ b/btrfstune.c
@@ -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)
diff --git a/cmds-balance.c b/cmds-balance.c
index c5552492..dde7c683 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -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
diff --git a/cmds-device.c b/cmds-device.c
index 4787aca1..d4938f4e 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -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 }
}
};
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 25b83d2f..f10bf55e 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -23,6 +23,7 @@
#include "kerncompat.h"
#include "ioctl.h"
+#include "utils.h"
#include "commands.h"
#include "btrfs-list.h"
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 1525c116..085881f3 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -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) {
diff --git a/cmds-quota.c b/cmds-quota.c
index cf9ad976..8481514c 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -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>",
diff --git a/cmds-receive.c b/cmds-receive.c
index 37d07dbb..973687f3 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -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",
diff --git a/cmds-replace.c b/cmds-replace.c
new file mode 100644
index 00000000..d14c9b57
--- /dev/null
+++ b/cmds-replace.c
@@ -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);
+}
diff --git a/cmds-scrub.c b/cmds-scrub.c
index b864b2b9..efdfdb4f 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -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));
diff --git a/cmds-send.c b/cmds-send.c
index ac1d3cf6..69e9bcea 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -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);
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index ac39f7bf..63c5990f 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -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 }
}
};
diff --git a/commands.h b/commands.h
index bb6d2dd7..33eb99a8 100644
--- a/commands.h
+++ b/commands.h
@@ -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);
diff --git a/common.c b/common.c
deleted file mode 100644
index 03f65703..00000000
--- a/common.c
+++ /dev/null
@@ -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;
-}
diff --git a/ctree.h b/ctree.h
index e82f4c9f..9bdcf6b6 100644
--- a/ctree.h
+++ b/ctree.h
@@ -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
@@ -1049,6 +1067,18 @@ struct btrfs_root {
#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);
diff --git a/dir-test.c b/dir-test.c
index 3ae9c68b..c7644d68 100644
--- a/dir-test.c
+++ b/dir-test.c
@@ -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);
diff --git a/disk-io.c b/disk-io.c
index 8b369050..a7d5c583 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -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);
diff --git a/extent-tree.c b/extent-tree.c
index 7fb77019..85f5670d 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -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;
diff --git a/find-root.c b/find-root.c
index 43ac0b7e..3d713235 100644
--- a/find-root.c
+++ b/find-root.c
@@ -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);
diff --git a/ioctl.h b/ioctl.h
index 6fda3a1d..d57afb2d 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -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
diff --git a/kerncompat.h b/kerncompat.h
index 28b9799d..0ab2baf9 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -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
diff --git a/man/Makefile b/man/Makefile
index 4a90b759..fefbda97 100644
--- a/man/Makefile
+++ b/man/Makefile
@@ -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)
diff --git a/man/btrfs.8.in b/man/btrfs.8.in
index d20e332e..94f4ffea 100644
--- a/man/btrfs.8.in
+++ b/man/btrfs.8.in
@@ -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-c\fP print the ogeneration of the subvolume, aliases: ogen or origin generation
+
+\fB-g\fP print the generation of the subvolume
+
+\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 wille be listed.
+\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-s\fP only snapshot subvolumes in the filesystem will be listed.
-\fB-g [+|-]value\fP
+\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
diff --git a/man/mkfs.btrfs.8.in b/man/mkfs.btrfs.8.in
index 72025ed7..45386587 100644
--- a/man/mkfs.btrfs.8.in
+++ b/man/mkfs.btrfs.8.in
@@ -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,
diff --git a/mkfs.c b/mkfs.c
index 7cd0c23c..9dd10d68 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -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)) {
diff --git a/print-tree.c b/print-tree.c
index 7c615dd2..5739e19b 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -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);
}
diff --git a/qgroup.c b/qgroup.c
index 4083b57e..dafde12b 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -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)
diff --git a/qgroup.h b/qgroup.h
index f7af8c53..ad14c888 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -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);
diff --git a/quick-test.c b/quick-test.c
index fa6fd838..05d73fd5 100644
--- a/quick-test.c
+++ b/quick-test.c
@@ -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++) {
diff --git a/random-test.c b/random-test.c
index 0003236a..3a07e6d1 100644
--- a/random-test.c
+++ b/random-test.c
@@ -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);
diff --git a/send-utils.c b/send-utils.c
index fcde5c28..d8d39728 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -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;
}
diff --git a/transaction.h b/transaction.h
index a1070e0d..e8610b1d 100644
--- a/transaction.h
+++ b/transaction.h
@@ -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;
diff --git a/utils.c b/utils.c
index 938f9a53..d92f3178 100644
--- a/utils.c
+++ b/utils.c
@@ -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;
+}
diff --git a/utils.h b/utils.h
index 714fd7ac..2d2c23d2 100644
--- a/utils.h
+++ b/utils.h
@@ -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
diff --git a/volumes.c b/volumes.c
index 9df3efef..de9f2642 100644
--- a/volumes.c
+++ b/volumes.c
@@ -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)
{
diff --git a/volumes.h b/volumes.h
index 59d00b6d..911f7881 100644
--- a/volumes.h
+++ b/volumes.h
@@ -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