X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs.c;h=34246dc9106d5ffd83712f4b6571b4e91e042abe;hb=3f7b0b0832a61ccaf590f3503521eb7cf8f14f64;hp=3278645b8a1f5d41f46b3ac39acd0ccc5ff25c43;hpb=800408be11898f6d53ceecfd894cce8860fda26a;p=bcachefs-tools-debian diff --git a/libbcachefs.c b/libbcachefs.c index 3278645..34246dc 100644 --- a/libbcachefs.c +++ b/libbcachefs.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -20,66 +21,49 @@ #include "libbcachefs/btree_cache.h" #include "libbcachefs/checksum.h" #include "libbcachefs/disk_groups.h" +#include "libbcachefs/journal_seq_blacklist.h" #include "libbcachefs/opts.h" #include "libbcachefs/replicas.h" #include "libbcachefs/super-io.h" +#include "tools-util.h" #define NSEC_PER_SEC 1000000000L -#define BCH_MIN_NR_NBUCKETS (1 << 10) - /* minimum size filesystem we can create, given a bucket size: */ static u64 min_size(unsigned bucket_size) { return BCH_MIN_NR_NBUCKETS * bucket_size; } -static void init_layout(struct bch_sb_layout *l, unsigned block_size, - u64 start, u64 end) +static void init_layout(struct bch_sb_layout *l, + unsigned block_size, + unsigned sb_size, + u64 sb_start, u64 sb_end) { - unsigned sb_size; - u64 backup; /* offset of 2nd sb */ + unsigned i; memset(l, 0, sizeof(*l)); - if (start != BCH_SB_SECTOR) - start = round_up(start, block_size); - end = round_down(end, block_size); - - if (start >= end) - die("insufficient space for superblocks"); - - /* - * Create two superblocks in the allowed range: reserve a maximum of 64k - */ - sb_size = min_t(u64, 128, end - start / 2); - - backup = start + sb_size; - backup = round_up(backup, block_size); - - backup = min(backup, end); - - sb_size = min(end - backup, backup- start); - sb_size = rounddown_pow_of_two(sb_size); - - if (sb_size < 8) - die("insufficient space for superblocks"); - l->magic = BCACHE_MAGIC; l->layout_type = 0; l->nr_superblocks = 2; l->sb_max_size_bits = ilog2(sb_size); - l->sb_offset[0] = cpu_to_le64(start); - l->sb_offset[1] = cpu_to_le64(backup); -} -void bch2_pick_bucket_size(struct format_opts opts, struct dev_opts *dev) -{ - if (!dev->sb_offset) { - dev->sb_offset = BCH_SB_SECTOR; - dev->sb_end = BCH_SB_SECTOR + 256; + /* Create two superblocks in the allowed range: */ + for (i = 0; i < l->nr_superblocks; i++) { + if (sb_start != BCH_SB_SECTOR) + sb_start = round_up(sb_start, block_size); + + l->sb_offset[i] = cpu_to_le64(sb_start); + sb_start += sb_size; } + if (sb_start >= sb_end) + die("insufficient space for superblocks"); +} + +void bch2_pick_bucket_size(struct bch_opts opts, struct dev_opts *dev) +{ if (!dev->size) dev->size = get_size(dev->path, dev->fd) >> 9; @@ -92,7 +76,9 @@ void bch2_pick_bucket_size(struct format_opts opts, struct dev_opts *dev) dev->bucket_size = opts.block_size; /* Bucket size must be >= btree node size: */ - dev->bucket_size = max(dev->bucket_size, opts.btree_node_size); + if (opt_defined(opts, btree_node_size)) + dev->bucket_size = max_t(unsigned, dev->bucket_size, + opts.btree_node_size); /* Want a bucket size of at least 128k, if possible: */ dev->bucket_size = max(dev->bucket_size, 256U); @@ -117,7 +103,8 @@ void bch2_pick_bucket_size(struct format_opts opts, struct dev_opts *dev) if (dev->bucket_size < opts.block_size) die("Bucket size cannot be smaller than block size"); - if (dev->bucket_size < opts.btree_node_size) + if (opt_defined(opts, btree_node_size) && + dev->bucket_size < opts.btree_node_size) die("Bucket size cannot be smaller than btree node size"); if (dev->nbuckets < BCH_MIN_NR_NBUCKETS) @@ -148,37 +135,48 @@ static unsigned parse_target(struct bch_sb_handle *sb, return 0; } -struct bch_sb *bch2_format(struct format_opts opts, - struct dev_opts *devs, size_t nr_devs) +struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, + struct bch_opts fs_opts, + struct format_opts opts, + struct dev_opts *devs, + size_t nr_devs) { struct bch_sb_handle sb = { NULL }; struct dev_opts *i; struct bch_sb_field_members *mi; + unsigned max_dev_block_size = 0; + unsigned opt_id; + + for (i = devs; i < devs + nr_devs; i++) + max_dev_block_size = max(max_dev_block_size, + get_blocksize(i->path, i->fd)); /* calculate block size: */ - if (!opts.block_size) - for (i = devs; i < devs + nr_devs; i++) - opts.block_size = max(opts.block_size, - get_blocksize(i->path, i->fd)); + if (!opt_defined(fs_opts, block_size)) { + opt_set(fs_opts, block_size, max_dev_block_size); + } else if (fs_opts.block_size < max_dev_block_size) + die("blocksize too small: %u, must be greater than device blocksize %u", + fs_opts.block_size, max_dev_block_size); /* calculate bucket sizes: */ for (i = devs; i < devs + nr_devs; i++) - bch2_pick_bucket_size(opts, i); + bch2_pick_bucket_size(fs_opts, i); /* calculate btree node size: */ - if (!opts.btree_node_size) { + if (!opt_defined(fs_opts, btree_node_size)) { /* 256k default btree node size */ - opts.btree_node_size = 512; + opt_set(fs_opts, btree_node_size, 512); for (i = devs; i < devs + nr_devs; i++) - opts.btree_node_size = - min(opts.btree_node_size, i->bucket_size); + fs_opts.btree_node_size = + min_t(unsigned, fs_opts.btree_node_size, + i->bucket_size); } - if (!is_power_of_2(opts.block_size)) + if (!is_power_of_2(fs_opts.block_size)) die("block size must be power of 2"); - if (!is_power_of_2(opts.btree_node_size)) + if (!is_power_of_2(fs_opts.btree_node_size)) die("btree node size must be power of 2"); if (uuid_is_null(opts.uuid.b)) @@ -187,35 +185,41 @@ struct bch_sb *bch2_format(struct format_opts opts, if (bch2_sb_realloc(&sb, 0)) die("insufficient memory"); - sb.sb->version = cpu_to_le64(BCH_SB_VERSION_MAX); + sb.sb->version = le16_to_cpu(opts.version); + sb.sb->version_min = le16_to_cpu(opts.version); sb.sb->magic = BCACHE_MAGIC; - sb.sb->block_size = cpu_to_le16(opts.block_size); + sb.sb->block_size = cpu_to_le16(fs_opts.block_size); sb.sb->user_uuid = opts.uuid; sb.sb->nr_devices = nr_devs; + if (opts.version == bcachefs_metadata_version_current) + sb.sb->features[0] |= BCH_SB_FEATURES_ALL; + uuid_generate(sb.sb->uuid.b); if (opts.label) - strncpy((char *) sb.sb->label, opts.label, sizeof(sb.sb->label)); - - SET_BCH_SB_CSUM_TYPE(sb.sb, opts.meta_csum_type); - SET_BCH_SB_META_CSUM_TYPE(sb.sb, opts.meta_csum_type); - SET_BCH_SB_DATA_CSUM_TYPE(sb.sb, opts.data_csum_type); - SET_BCH_SB_COMPRESSION_TYPE(sb.sb, opts.compression_type); - SET_BCH_SB_BACKGROUND_COMPRESSION_TYPE(sb.sb, - opts.background_compression_type); - - SET_BCH_SB_BTREE_NODE_SIZE(sb.sb, opts.btree_node_size); - SET_BCH_SB_GC_RESERVE(sb.sb, 8); - SET_BCH_SB_META_REPLICAS_WANT(sb.sb, opts.meta_replicas); - SET_BCH_SB_META_REPLICAS_REQ(sb.sb, opts.meta_replicas_required); - SET_BCH_SB_DATA_REPLICAS_WANT(sb.sb, opts.data_replicas); - SET_BCH_SB_DATA_REPLICAS_REQ(sb.sb, opts.data_replicas_required); - SET_BCH_SB_ERROR_ACTION(sb.sb, opts.on_error_action); - SET_BCH_SB_STR_HASH_TYPE(sb.sb, BCH_STR_HASH_SIPHASH); - SET_BCH_SB_ENCODED_EXTENT_MAX_BITS(sb.sb,ilog2(opts.encoded_extent_max)); - - SET_BCH_SB_POSIX_ACL(sb.sb, 1); + memcpy(sb.sb->label, + opts.label, + min(strlen(opts.label), sizeof(sb.sb->label))); + + for (opt_id = 0; + opt_id < bch2_opts_nr; + opt_id++) { + const struct bch_option *opt = &bch2_opt_table[opt_id]; + u64 v; + + if (opt->set_sb == SET_NO_SB_OPT) + continue; + + v = bch2_opt_defined_by_id(&fs_opts, opt_id) + ? bch2_opt_get_by_id(&fs_opts, opt_id) + : bch2_opt_get_by_id(&bch2_opts_default, opt_id); + + opt->set_sb(sb.sb, v); + } + + SET_BCH_SB_ENCODED_EXTENT_MAX_BITS(sb.sb, + ilog2(opts.encoded_extent_max)); struct timespec now; if (clock_gettime(CLOCK_REALTIME, &now)) @@ -237,7 +241,7 @@ struct bch_sb *bch2_format(struct format_opts opts, m->first_bucket = 0; m->bucket_size = cpu_to_le16(i->bucket_size); - SET_BCH_MEMBER_REPLACEMENT(m, CACHE_REPLACEMENT_LRU); + SET_BCH_MEMBER_REPLACEMENT(m, BCH_CACHE_REPLACEMENT_lru); SET_BCH_MEMBER_DISCARD(m, i->discard); SET_BCH_MEMBER_DATA_ALLOWED(m, i->data_allowed); SET_BCH_MEMBER_DURABILITY(m, i->durability + 1); @@ -259,11 +263,13 @@ struct bch_sb *bch2_format(struct format_opts opts, } SET_BCH_SB_FOREGROUND_TARGET(sb.sb, - parse_target(&sb, devs, nr_devs, opts.foreground_target)); + parse_target(&sb, devs, nr_devs, fs_opt_strs.foreground_target)); SET_BCH_SB_BACKGROUND_TARGET(sb.sb, - parse_target(&sb, devs, nr_devs, opts.background_target)); + parse_target(&sb, devs, nr_devs, fs_opt_strs.background_target)); SET_BCH_SB_PROMOTE_TARGET(sb.sb, - parse_target(&sb, devs, nr_devs, opts.promote_target)); + parse_target(&sb, devs, nr_devs, fs_opt_strs.promote_target)); + SET_BCH_SB_METADATA_TARGET(sb.sb, + parse_target(&sb, devs, nr_devs, fs_opt_strs.metadata_target)); /* Crypt: */ if (opts.encrypted) { @@ -277,9 +283,30 @@ struct bch_sb *bch2_format(struct format_opts opts, for (i = devs; i < devs + nr_devs; i++) { sb.sb->dev_idx = i - devs; - init_layout(&sb.sb->layout, opts.block_size, + if (!i->sb_offset) { + i->sb_offset = BCH_SB_SECTOR; + i->sb_end = i->size; + } + + init_layout(&sb.sb->layout, fs_opts.block_size, + opts.superblock_size, i->sb_offset, i->sb_end); + /* + * Also create a backup superblock at the end of the disk: + * + * If we're not creating a superblock at the default offset, it + * means we're being run from the migrate tool and we could be + * overwriting existing data if we write to the end of the disk: + */ + if (i->sb_offset == BCH_SB_SECTOR) { + struct bch_sb_layout *l = &sb.sb->layout; + u64 backup_sb = i->size - (1 << l->sb_max_size_bits); + + backup_sb = rounddown(backup_sb, i->bucket_size); + l->sb_offset[l->nr_superblocks++] = cpu_to_le64(backup_sb); + } + if (i->sb_offset == BCH_SB_SECTOR) { /* Zero start of disk */ static const char zeroes[BCH_SB_SECTOR << 9]; @@ -344,13 +371,59 @@ static unsigned get_dev_has_data(struct bch_sb *sb, unsigned dev) if (replicas) for_each_replicas_entry(replicas, r) - for (i = 0; i < r->nr; i++) + for (i = 0; i < r->nr_devs; i++) if (r->devs[i] == dev) data_has |= 1 << r->data_type; return data_has; } +static int bch2_sb_get_target(struct bch_sb *sb, char *buf, size_t len, u64 v) +{ + struct target t = target_decode(v); + int ret; + + switch (t.type) { + case TARGET_NULL: + return scnprintf(buf, len, "none"); + case TARGET_DEV: { + struct bch_sb_field_members *mi = bch2_sb_get_members(sb); + struct bch_member *m = mi->members + t.dev; + + if (bch2_dev_exists(sb, mi, t.dev)) { + char uuid_str[40]; + + uuid_unparse(m->uuid.b, uuid_str); + + ret = scnprintf(buf, len, "Device %u (%s)", t.dev, + uuid_str); + } else { + ret = scnprintf(buf, len, "Bad device %u", t.dev); + } + + break; + } + case TARGET_GROUP: { + struct bch_sb_field_disk_groups *gi; + gi = bch2_sb_get_disk_groups(sb); + + struct bch_disk_group *g = gi->entries + t.group; + + if (t.group < disk_groups_nr(gi) && !BCH_GROUP_DELETED(g)) { + ret = scnprintf(buf, len, "Group %u (%.*s)", t.group, + BCH_SB_LABEL_SIZE, g->label); + } else { + ret = scnprintf(buf, len, "Bad group %u", t.group); + } + break; + } + default: + BUG(); + } + + return ret; +} + /* superblock printing: */ static void bch2_sb_print_layout(struct bch_sb *sb, enum units units) @@ -402,7 +475,8 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f, char member_uuid_str[40]; char data_allowed_str[100]; char data_has_str[100]; - char group[64]; + char group[BCH_SB_LABEL_SIZE+10]; + char time_str[64]; if (!bch2_member_exists(m)) continue; @@ -413,28 +487,37 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f, unsigned idx = BCH_MEMBER_GROUP(m) - 1; if (idx < disk_groups_nr(gi)) { - memcpy(group, gi->entries[idx].label, - BCH_SB_LABEL_SIZE); - group[BCH_SB_LABEL_SIZE] = '\0'; + snprintf(group, sizeof(group), "%.*s (%u)", + BCH_SB_LABEL_SIZE, + gi->entries[idx].label, idx); } else { - strcpy(group, "(bad disk groups section"); + strcpy(group, "(bad disk groups section)"); } + } else { + strcpy(group, "(none)"); } - bch2_scnprint_flag_list(data_allowed_str, - sizeof(data_allowed_str), - bch2_data_types, - BCH_MEMBER_DATA_ALLOWED(m)); + bch2_flags_to_text(&PBUF(data_allowed_str), + bch2_data_types, + BCH_MEMBER_DATA_ALLOWED(m)); if (!data_allowed_str[0]) strcpy(data_allowed_str, "(none)"); - bch2_scnprint_flag_list(data_has_str, - sizeof(data_has_str), - bch2_data_types, - get_dev_has_data(sb, i)); + bch2_flags_to_text(&PBUF(data_has_str), + bch2_data_types, + get_dev_has_data(sb, i)); if (!data_has_str[0]) strcpy(data_has_str, "(none)"); + if (last_mount) { + struct tm *tm = localtime(&last_mount); + size_t err = strftime(time_str, sizeof(time_str), "%c", tm); + if (!err) + strcpy(time_str, "(formatting error)"); + } else { + strcpy(time_str, "(never)"); + } + printf(" Device %u:\n" " UUID: %s\n" " Size: %s\n" @@ -456,17 +539,17 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f, pr_units(le16_to_cpu(m->bucket_size), units), le16_to_cpu(m->first_bucket), le64_to_cpu(m->nbuckets), - last_mount ? ctime(&last_mount) : "(never)", + time_str, BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR - ? bch2_dev_state[BCH_MEMBER_STATE(m)] + ? bch2_member_states[BCH_MEMBER_STATE(m)] : "unknown", group, data_allowed_str, data_has_str, - BCH_MEMBER_REPLACEMENT(m) < CACHE_REPLACEMENT_NR + BCH_MEMBER_REPLACEMENT(m) < BCH_CACHE_REPLACEMENT_NR ? bch2_cache_replacement_policies[BCH_MEMBER_REPLACEMENT(m)] : "unknown", @@ -489,6 +572,26 @@ static void bch2_sb_print_crypt(struct bch_sb *sb, struct bch_sb_field *f, BCH_KDF_SCRYPT_P(crypt)); } +static void bch2_sb_print_replicas_v0(struct bch_sb *sb, struct bch_sb_field *f, + enum units units) +{ + struct bch_sb_field_replicas_v0 *replicas = field_to_type(f, replicas_v0); + struct bch_replicas_entry_v0 *e; + unsigned i; + + for_each_replicas_entry(replicas, e) { + printf_pad(32, " %s:", bch2_data_types[e->data_type]); + + putchar('['); + for (i = 0; i < e->nr_devs; i++) { + if (i) + putchar(' '); + printf("%u", e->devs[i]); + } + printf("]\n"); + } +} + static void bch2_sb_print_replicas(struct bch_sb *sb, struct bch_sb_field *f, enum units units) { @@ -497,10 +600,13 @@ static void bch2_sb_print_replicas(struct bch_sb *sb, struct bch_sb_field *f, unsigned i; for_each_replicas_entry(replicas, e) { - printf_pad(32, " %s:", bch2_data_types[e->data_type]); + printf_pad(32, " %s: %u/%u", + bch2_data_types[e->data_type], + e->nr_required, + e->nr_devs); putchar('['); - for (i = 0; i < e->nr; i++) { + for (i = 0; i < e->nr_devs; i++) { if (i) putchar(' '); printf("%u", e->devs[i]); @@ -519,6 +625,32 @@ static void bch2_sb_print_disk_groups(struct bch_sb *sb, struct bch_sb_field *f, { } +static void bch2_sb_print_clean(struct bch_sb *sb, struct bch_sb_field *f, + enum units units) +{ + struct bch_sb_field_clean *clean = field_to_type(f, clean); + + + printf(" flags: %x", le32_to_cpu(clean->flags)); + printf(" journal seq: %llx", le64_to_cpu(clean->journal_seq)); +} + +static void bch2_sb_print_journal_seq_blacklist(struct bch_sb *sb, struct bch_sb_field *f, + enum units units) +{ + struct bch_sb_field_journal_seq_blacklist *bl = field_to_type(f, journal_seq_blacklist); + unsigned i, nr = blacklist_nr_entries(bl); + + for (i = 0; i < nr; i++) { + struct journal_seq_blacklist_entry *e = + bl->start + i; + + printf(" %llu-%llu\n", + le64_to_cpu(e->start), + le64_to_cpu(e->end)); + } +} + typedef void (*sb_field_print_fn)(struct bch_sb *, struct bch_sb_field *, enum units); struct bch_sb_field_toolops { @@ -551,17 +683,35 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout, { struct bch_sb_field_members *mi; char user_uuid_str[40], internal_uuid_str[40]; + char features_str[500]; + char compat_features_str[500]; char fields_have_str[200]; char label[BCH_SB_LABEL_SIZE + 1]; + char time_str[64]; + char foreground_str[64]; + char background_str[64]; + char promote_str[64]; + char metadata_str[64]; struct bch_sb_field *f; u64 fields_have = 0; unsigned nr_devices = 0; + time_t time_base = le64_to_cpu(sb->time_base_lo) / NSEC_PER_SEC; + + memcpy(label, sb->label, BCH_SB_LABEL_SIZE); + label[BCH_SB_LABEL_SIZE] = '\0'; - memset(label, 0, sizeof(label)); - memcpy(label, sb->label, sizeof(sb->label)); uuid_unparse(sb->user_uuid.b, user_uuid_str); uuid_unparse(sb->uuid.b, internal_uuid_str); + if (time_base) { + struct tm *tm = localtime(&time_base); + size_t err = strftime(time_str, sizeof(time_str), "%c", tm); + if (!err) + strcpy(time_str, "(formatting error)"); + } else { + strcpy(time_str, "(not set)"); + } + mi = bch2_sb_get_members(sb); if (mi) { struct bch_member *m; @@ -572,19 +722,45 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout, nr_devices += bch2_member_exists(m); } + bch2_sb_get_target(sb, foreground_str, sizeof(foreground_str), + BCH_SB_FOREGROUND_TARGET(sb)); + + bch2_sb_get_target(sb, background_str, sizeof(background_str), + BCH_SB_BACKGROUND_TARGET(sb)); + + bch2_sb_get_target(sb, promote_str, sizeof(promote_str), + BCH_SB_PROMOTE_TARGET(sb)); + + bch2_sb_get_target(sb, metadata_str, sizeof(metadata_str), + BCH_SB_METADATA_TARGET(sb)); + + bch2_flags_to_text(&PBUF(features_str), + bch2_sb_features, + le64_to_cpu(sb->features[0])); + + bch2_flags_to_text(&PBUF(compat_features_str), + bch2_sb_compat, + le64_to_cpu(sb->compat[0])); + vstruct_for_each(sb, f) fields_have |= 1 << le32_to_cpu(f->type); - bch2_scnprint_flag_list(fields_have_str, sizeof(fields_have_str), - bch2_sb_fields, fields_have); + bch2_flags_to_text(&PBUF(fields_have_str), + bch2_sb_fields, fields_have); printf("External UUID: %s\n" "Internal UUID: %s\n" + "Device index: %u\n" "Label: %s\n" - "Version: %llu\n" + "Version: %u\n" + "Oldest version on disk: %u\n" + "Created: %s\n" + "Squence number: %llu\n" "Block_size: %s\n" "Btree node size: %s\n" "Error action: %s\n" "Clean: %llu\n" + "Features: %s\n" + "Compat features: %s\n" "Metadata replicas: %llu\n" "Data replicas: %llu\n" @@ -593,9 +769,10 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout, "Data checksum type: %s (%llu)\n" "Compression type: %s (%llu)\n" - "Foreground write target: %llu\n" - "Background write target: %llu\n" - "Promote target: %llu\n" + "Foreground write target: %s\n" + "Background write target: %s\n" + "Promote target: %s\n" + "Metadata target: %s\n" "String hash type: %s (%llu)\n" "32 bit inodes: %llu\n" @@ -607,38 +784,45 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout, "Superblock size: %llu\n", user_uuid_str, internal_uuid_str, + sb->dev_idx, label, - le64_to_cpu(sb->version), + le16_to_cpu(sb->version), + le16_to_cpu(sb->version_min), + time_str, + le64_to_cpu(sb->seq), pr_units(le16_to_cpu(sb->block_size), units), pr_units(BCH_SB_BTREE_NODE_SIZE(sb), units), - BCH_SB_ERROR_ACTION(sb) < BCH_NR_ERROR_ACTIONS + BCH_SB_ERROR_ACTION(sb) < BCH_ON_ERROR_NR ? bch2_error_actions[BCH_SB_ERROR_ACTION(sb)] : "unknown", BCH_SB_CLEAN(sb), + features_str, + compat_features_str, BCH_SB_META_REPLICAS_WANT(sb), BCH_SB_DATA_REPLICAS_WANT(sb), BCH_SB_META_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR - ? bch2_csum_types[BCH_SB_META_CSUM_TYPE(sb)] + ? bch2_csum_opts[BCH_SB_META_CSUM_TYPE(sb)] : "unknown", BCH_SB_META_CSUM_TYPE(sb), BCH_SB_DATA_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR - ? bch2_csum_types[BCH_SB_DATA_CSUM_TYPE(sb)] + ? bch2_csum_opts[BCH_SB_DATA_CSUM_TYPE(sb)] : "unknown", BCH_SB_DATA_CSUM_TYPE(sb), BCH_SB_COMPRESSION_TYPE(sb) < BCH_COMPRESSION_OPT_NR - ? bch2_compression_types[BCH_SB_COMPRESSION_TYPE(sb)] + ? bch2_compression_opts[BCH_SB_COMPRESSION_TYPE(sb)] : "unknown", BCH_SB_COMPRESSION_TYPE(sb), - BCH_SB_FOREGROUND_TARGET(sb), - BCH_SB_BACKGROUND_TARGET(sb), - BCH_SB_PROMOTE_TARGET(sb), + foreground_str, + background_str, + promote_str, + metadata_str, BCH_SB_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR ? bch2_str_hash_types[BCH_SB_STR_HASH_TYPE(sb)] @@ -738,7 +922,7 @@ struct bchfs_handle bcache_fs_open(const char *path) * Given a path to a block device, open the filesystem it belongs to; also * return the device's idx: */ -struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx) +struct bchfs_handle bchu_fs_open_by_dev(const char *path, int *idx) { char buf[1024], *uuid_str; @@ -782,34 +966,51 @@ struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx) return bcache_fs_open(uuid_str); } +int bchu_dev_path_to_idx(struct bchfs_handle fs, const char *dev_path) +{ + int idx; + struct bchfs_handle fs2 = bchu_fs_open_by_dev(dev_path, &idx); + + if (memcmp(&fs.uuid, &fs2.uuid, sizeof(fs.uuid))) + idx = -1; + bcache_fs_close(fs2); + return idx; +} + int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd) { int progress_fd = xioctl(fs.ioctl_fd, BCH_IOCTL_DATA, &cmd); while (1) { - struct bch_ioctl_data_progress p; + struct bch_ioctl_data_event e; - if (read(progress_fd, &p, sizeof(p)) != sizeof(p)) - die("error reading from progress fd"); + if (read(progress_fd, &e, sizeof(e)) != sizeof(e)) + die("error reading from progress fd %m"); - if (p.data_type == U8_MAX) + if (e.type) + continue; + + if (e.p.data_type == U8_MAX) break; printf("\33[2K\r"); printf("%llu%% complete: current position %s", - p.sectors_done * 100 / p.sectors_total, - bch2_data_types[p.data_type]); - - switch (p.data_type) { - case BCH_DATA_BTREE: - case BCH_DATA_USER: + e.p.sectors_total + ? e.p.sectors_done * 100 / e.p.sectors_total + : 0, + bch2_data_types[e.p.data_type]); + + switch (e.p.data_type) { + case BCH_DATA_btree: + case BCH_DATA_user: printf(" %s:%llu:%llu", - bch2_btree_ids[p.btree_id], - p.pos.inode, - p.pos.offset); + bch2_btree_ids[e.p.btree_id], + e.p.pos.inode, + e.p.pos.offset); } + fflush(stdout); sleep(1); } printf("\nDone\n"); @@ -817,3 +1018,200 @@ int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd) close(progress_fd); return 0; } + +/* option parsing */ + +void bch2_opt_strs_free(struct bch_opt_strs *opts) +{ + unsigned i; + + for (i = 0; i < bch2_opts_nr; i++) { + free(opts->by_id[i]); + opts->by_id[i] = NULL; + } +} + +struct bch_opt_strs bch2_cmdline_opts_get(int *argc, char *argv[], + unsigned opt_types) +{ + struct bch_opt_strs opts; + unsigned i = 1; + + memset(&opts, 0, sizeof(opts)); + + while (i < *argc) { + char *optstr = strcmp_prefix(argv[i], "--"); + char *valstr = NULL, *p; + int optid, nr_args = 1; + + if (!optstr) { + i++; + continue; + } + + optstr = strdup(optstr); + + p = optstr; + while (isalpha(*p) || *p == '_') + p++; + + if (*p == '=') { + *p = '\0'; + valstr = p + 1; + } + + optid = bch2_opt_lookup(optstr); + if (optid < 0 || + !(bch2_opt_table[optid].mode & opt_types)) { + i++; + goto next; + } + + if (!valstr && + bch2_opt_table[optid].type != BCH_OPT_BOOL) { + nr_args = 2; + valstr = argv[i + 1]; + } + + if (!valstr) + valstr = "1"; + + opts.by_id[optid] = strdup(valstr); + + *argc -= nr_args; + memmove(&argv[i], + &argv[i + nr_args], + sizeof(char *) * (*argc - i)); + argv[*argc] = NULL; +next: + free(optstr); + } + + return opts; +} + +struct bch_opts bch2_parse_opts(struct bch_opt_strs strs) +{ + struct bch_opts opts = bch2_opts_empty(); + unsigned i; + int ret; + u64 v; + + for (i = 0; i < bch2_opts_nr; i++) { + if (!strs.by_id[i] || + bch2_opt_table[i].type == BCH_OPT_FN) + continue; + + ret = bch2_opt_parse(NULL, &bch2_opt_table[i], + strs.by_id[i], &v); + if (ret < 0) + die("Invalid %s: %s", + bch2_opt_table[i].attr.name, + strerror(-ret)); + + bch2_opt_set_by_id(&opts, i, v); + } + + return opts; +} + +#define newline(c) \ + do { \ + printf("\n"); \ + c = 0; \ + } while(0) +void bch2_opts_usage(unsigned opt_types) +{ + const struct bch_option *opt; + unsigned i, c = 0, helpcol = 30; + + + + for (opt = bch2_opt_table; + opt < bch2_opt_table + bch2_opts_nr; + opt++) { + if (!(opt->mode & opt_types)) + continue; + + c += printf(" --%s", opt->attr.name); + + switch (opt->type) { + case BCH_OPT_BOOL: + break; + case BCH_OPT_STR: + c += printf("=("); + for (i = 0; opt->choices[i]; i++) { + if (i) + c += printf("|"); + c += printf("%s", opt->choices[i]); + } + c += printf(")"); + break; + default: + c += printf("=%s", opt->hint); + break; + } + + if (opt->help) { + const char *l = opt->help; + + if (c >= helpcol) + newline(c); + + while (1) { + const char *n = strchrnul(l, '\n'); + + while (c < helpcol) { + putchar(' '); + c++; + } + printf("%.*s", (int) (n - l), l); + newline(c); + + if (!*n) + break; + l = n + 1; + } + } else { + newline(c); + } + } +} + +dev_names bchu_fs_get_devices(struct bchfs_handle fs) +{ + DIR *dir = fdopendir(fs.sysfs_fd); + struct dirent *d; + dev_names devs; + + darray_init(devs); + + while ((errno = 0), (d = readdir(dir))) { + struct dev_name n = { 0, NULL, NULL }; + + if (sscanf(d->d_name, "dev-%u", &n.idx) != 1) + continue; + + char *block_attr = mprintf("dev-%u/block", n.idx); + + char sysfs_block_buf[4096]; + ssize_t r = readlinkat(fs.sysfs_fd, block_attr, + sysfs_block_buf, sizeof(sysfs_block_buf)); + if (r > 0) { + sysfs_block_buf[r] = '\0'; + n.dev = strdup(basename(sysfs_block_buf)); + } + + free(block_attr); + + char *label_attr = mprintf("dev-%u/label", n.idx); + n.label = read_file_str(fs.sysfs_fd, label_attr); + free(label_attr); + + darray_append(devs, n); + } + + closedir(dir); + + return devs; +}