diff options
-rw-r--r-- | Makefile | 105 | ||||
-rw-r--r-- | btrfs-image.c | 5 | ||||
-rw-r--r-- | btrfs-list.c | 278 | ||||
-rw-r--r-- | btrfs-list.h | 59 | ||||
-rw-r--r-- | btrfs-select-super.c | 4 | ||||
-rw-r--r-- | btrfs-show-super.c | 282 | ||||
-rw-r--r-- | btrfs.c | 5 | ||||
-rw-r--r-- | btrfsck.c | 59 | ||||
-rw-r--r-- | btrfsctl.c | 7 | ||||
-rw-r--r-- | btrfslabel.c | 8 | ||||
-rw-r--r-- | btrfstune.c | 5 | ||||
-rw-r--r-- | cmds-balance.c | 3 | ||||
-rw-r--r-- | cmds-device.c | 152 | ||||
-rw-r--r-- | cmds-inspect.c | 1 | ||||
-rw-r--r-- | cmds-qgroup.c | 34 | ||||
-rw-r--r-- | cmds-quota.c | 1 | ||||
-rw-r--r-- | cmds-receive.c | 6 | ||||
-rw-r--r-- | cmds-replace.c | 579 | ||||
-rw-r--r-- | cmds-scrub.c | 94 | ||||
-rw-r--r-- | cmds-send.c | 110 | ||||
-rw-r--r-- | cmds-subvolume.c | 284 | ||||
-rw-r--r-- | commands.h | 12 | ||||
-rw-r--r-- | common.c | 46 | ||||
-rw-r--r-- | ctree.h | 45 | ||||
-rw-r--r-- | dir-test.c | 11 | ||||
-rw-r--r-- | disk-io.c | 2 | ||||
-rw-r--r-- | extent-tree.c | 2 | ||||
-rw-r--r-- | find-root.c | 13 | ||||
-rw-r--r-- | ioctl.h | 86 | ||||
-rw-r--r-- | kerncompat.h | 13 | ||||
-rw-r--r-- | man/Makefile | 31 | ||||
-rw-r--r-- | man/btrfs.8.in | 118 | ||||
-rw-r--r-- | man/mkfs.btrfs.8.in | 8 | ||||
-rw-r--r-- | mkfs.c | 92 | ||||
-rw-r--r-- | print-tree.c | 53 | ||||
-rw-r--r-- | qgroup.c | 22 | ||||
-rw-r--r-- | qgroup.h | 2 | ||||
-rw-r--r-- | quick-test.c | 20 | ||||
-rw-r--r-- | random-test.c | 8 | ||||
-rw-r--r-- | send-utils.c | 4 | ||||
-rw-r--r-- | transaction.h | 1 | ||||
-rw-r--r-- | utils.c | 133 | ||||
-rw-r--r-- | utils.h | 9 | ||||
-rw-r--r-- | volumes.c | 16 | ||||
-rw-r--r-- | volumes.h | 2 |
45 files changed, 2352 insertions, 478 deletions
@@ -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)); +} @@ -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, "a_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 } @@ -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; @@ -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 } } }; @@ -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; -} @@ -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); @@ -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); @@ -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); @@ -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, @@ -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); } @@ -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) @@ -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; @@ -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; +} @@ -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 @@ -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) { @@ -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 |