X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs.c;h=4fe2c3db401a41afb2f79319986bb4819e31d310;hb=fa7a74fcc8b3fdedb075cf2d04e70a20b326a632;hp=7ff02b881513cb79b785ebc59153560247f9dfbe;hpb=a9a46467e618c5f040107cd87ebe7afa7863bc3c;p=bcachefs-tools-debian diff --git a/libbcachefs.c b/libbcachefs.c index 7ff02b8..4fe2c3d 100644 --- a/libbcachefs.c +++ b/libbcachefs.c @@ -29,64 +29,49 @@ #define NSEC_PER_SEC 1000000000L -/* 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 */ + u64 sb_pos = sb_start; + 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); + + /* Create two superblocks in the allowed range: */ + for (i = 0; i < l->nr_superblocks; i++) { + if (sb_pos != BCH_SB_SECTOR) + sb_pos = round_up(sb_pos, block_size); + + l->sb_offset[i] = cpu_to_le64(sb_pos); + sb_pos += sb_size; + } + + if (sb_pos > sb_end) + die("insufficient space for superblocks: start %llu end %llu > %llu size %u", + sb_start, sb_pos, sb_end, sb_size); } -void bch2_pick_bucket_size(struct bch_opts opts, struct dev_opts *dev) +/* minimum size filesystem we can create, given a bucket size: */ +static u64 min_size(unsigned bucket_size) { - if (!dev->sb_offset) { - dev->sb_offset = BCH_SB_SECTOR; - dev->sb_end = BCH_SB_SECTOR + 256; - } + return BCH_MIN_NR_NBUCKETS * bucket_size; +} +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; + dev->size = get_size(dev->path, dev->fd); if (!dev->bucket_size) { if (dev->size < min_size(opts.block_size)) - die("cannot format %s, too small (%llu sectors, min %llu)", + die("cannot format %s, too small (%llu bytes, min %llu)", dev->path, dev->size, min_size(opts.block_size)); /* Bucket size must be >= block size: */ @@ -98,16 +83,16 @@ void bch2_pick_bucket_size(struct bch_opts opts, struct dev_opts *dev) opts.btree_node_size); /* Want a bucket size of at least 128k, if possible: */ - dev->bucket_size = max(dev->bucket_size, 256U); + dev->bucket_size = max(dev->bucket_size, 128ULL << 10); if (dev->size >= min_size(dev->bucket_size)) { unsigned scale = max(1, - ilog2(dev->size / min_size(dev->bucket_size)) / 4); + ilog2(dev->size / min_size(dev->bucket_size)) / 4); scale = rounddown_pow_of_two(scale); /* max bucket size 1 mb */ - dev->bucket_size = min(dev->bucket_size * scale, 1U << 11); + dev->bucket_size = min(dev->bucket_size * scale, 1ULL << 20); } else { do { dev->bucket_size /= 2; @@ -115,19 +100,24 @@ void bch2_pick_bucket_size(struct bch_opts opts, struct dev_opts *dev) } } - dev->nbuckets = dev->size / dev->bucket_size; + dev->nbuckets = dev->size / dev->bucket_size; if (dev->bucket_size < opts.block_size) - die("Bucket size cannot be smaller than block size"); + die("Bucket size (%llu) cannot be smaller than block size (%u)", + dev->bucket_size, opts.block_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"); + die("Bucket size (%llu) cannot be smaller than btree node size (%u)", + dev->bucket_size, opts.btree_node_size); if (dev->nbuckets < BCH_MIN_NR_NBUCKETS) - die("Not enough buckets: %llu, need %u (bucket size %u)", + die("Not enough buckets: %llu, need %u (bucket size %llu)", dev->nbuckets, BCH_MIN_NR_NBUCKETS, dev->bucket_size); + if (dev->bucket_size > (u32) U16_MAX << 9) + die("Bucket size (%llu) too big (max %u)", + dev->bucket_size, (u32) U16_MAX << 9); } static unsigned parse_target(struct bch_sb_handle *sb, @@ -182,7 +172,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, /* calculate btree node size: */ if (!opt_defined(fs_opts, btree_node_size)) { /* 256k default btree node size */ - opt_set(fs_opts, btree_node_size, 512); + opt_set(fs_opts, btree_node_size, 256 << 10); for (i = devs; i < devs + nr_devs; i++) fs_opts.btree_node_size = @@ -190,25 +180,21 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, i->bucket_size); } - if (!is_power_of_2(fs_opts.block_size)) - die("block size must be power of 2"); - - 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)) uuid_generate(opts.uuid.b); if (bch2_sb_realloc(&sb, 0)) die("insufficient memory"); - sb.sb->version = le16_to_cpu(bcachefs_metadata_version_current); - sb.sb->version_min = le16_to_cpu(bcachefs_metadata_version_current); + 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(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) @@ -219,22 +205,15 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, 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); + __bch2_opt_set_sb(sb.sb, &bch2_opt_table[opt_id], v); } - SET_BCH_SB_ENCODED_EXTENT_MAX_BITS(sb.sb, - ilog2(opts.encoded_extent_max)); - struct timespec now; if (clock_gettime(CLOCK_REALTIME, &now)) die("error getting current time: %m"); @@ -245,7 +224,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, /* Member info: */ mi = bch2_sb_resize_members(&sb, (sizeof(*mi) + sizeof(struct bch_member) * - nr_devs) / sizeof(u64)); + nr_devs) / sizeof(u64)); for (i = devs; i < devs + nr_devs; i++) { struct bch_member *m = mi->members + (i - devs); @@ -253,25 +232,31 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, uuid_generate(m->uuid.b); m->nbuckets = cpu_to_le64(i->nbuckets); m->first_bucket = 0; - m->bucket_size = cpu_to_le16(i->bucket_size); + m->bucket_size = cpu_to_le16(i->bucket_size >> 9); - SET_BCH_MEMBER_REPLACEMENT(m, 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); } - /* Disk groups */ + /* Disk labels*/ for (i = devs; i < devs + nr_devs; i++) { - struct bch_member *m = mi->members + (i - devs); + struct bch_member *m; int idx; - if (!i->group) + if (!i->label) continue; - idx = bch2_disk_path_find_or_create(&sb, i->group); + idx = bch2_disk_path_find_or_create(&sb, i->label); if (idx < 0) - die("error creating disk path: %s", idx); + die("error creating disk path: %s", strerror(-idx)); + + /* + * Recompute mi and m after each sb modification: its location + * in memory may have changed due to reallocation. + */ + mi = bch2_sb_get_members(sb.sb); + m = mi->members + (i - devs); SET_BCH_MEMBER_GROUP(m, idx + 1); } @@ -282,6 +267,8 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, parse_target(&sb, devs, nr_devs, fs_opt_strs.background_target)); SET_BCH_SB_PROMOTE_TARGET(sb.sb, 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) { @@ -293,16 +280,40 @@ struct bch_sb *bch2_format(struct bch_opt_strs fs_opt_strs, } for (i = devs; i < devs + nr_devs; i++) { + u64 size_sectors = i->size >> 9; + sb.sb->dev_idx = i - devs; + if (!i->sb_offset) { + i->sb_offset = BCH_SB_SECTOR; + i->sb_end = size_sectors; + } + 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 = size_sectors - (1 << l->sb_max_size_bits); + + backup_sb = rounddown(backup_sb, i->bucket_size >> 9); + 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]; - xpwrite(i->fd, zeroes, BCH_SB_SECTOR << 9, 0); + xpwrite(i->fd, zeroes, BCH_SB_SECTOR << 9, 0, + "zeroing start of disk"); } bch2_super_write(i->fd, sb.sb); @@ -323,12 +334,14 @@ void bch2_super_write(int fd, struct bch_sb *sb) if (sb->offset == BCH_SB_SECTOR) { /* Write backup layout */ xpwrite(fd, &sb->layout, sizeof(sb->layout), - BCH_SB_LAYOUT_SECTOR << 9); + BCH_SB_LAYOUT_SECTOR << 9, + "backup layout"); } sb->csum = csum_vstruct(NULL, BCH_SB_CSUM_TYPE(sb), nonce, sb); xpwrite(fd, sb, vstruct_bytes(sb), - le64_to_cpu(sb->offset) << 9); + le64_to_cpu(sb->offset) << 9, + "superblock"); } fsync(fd); @@ -352,486 +365,6 @@ struct bch_sb *__bch2_super_read(int fd, u64 sector) return ret; } -static unsigned get_dev_has_data(struct bch_sb *sb, unsigned dev) -{ - struct bch_sb_field_replicas *replicas; - struct bch_replicas_entry *r; - unsigned i, data_has = 0; - - replicas = bch2_sb_get_replicas(sb); - - if (replicas) - for_each_replicas_entry(replicas, r) - 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) -{ - struct bch_sb_layout *l = &sb->layout; - unsigned i; - - printf(" type: %u\n" - " superblock max size: %s\n" - " nr superblocks: %u\n" - " Offsets: ", - l->layout_type, - pr_units(1 << l->sb_max_size_bits, units), - l->nr_superblocks); - - for (i = 0; i < l->nr_superblocks; i++) { - if (i) - printf(", "); - printf("%llu", le64_to_cpu(l->sb_offset[i])); - } - putchar('\n'); -} - -static void bch2_sb_print_journal(struct bch_sb *sb, struct bch_sb_field *f, - enum units units) -{ - struct bch_sb_field_journal *journal = field_to_type(f, journal); - unsigned i, nr = bch2_nr_journal_buckets(journal); - - printf(" Buckets: "); - for (i = 0; i < nr; i++) { - if (i) - putchar(' '); - printf("%llu", le64_to_cpu(journal->buckets[i])); - } - putchar('\n'); -} - -static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f, - enum units units) -{ - struct bch_sb_field_members *mi = field_to_type(f, members); - struct bch_sb_field_disk_groups *gi = bch2_sb_get_disk_groups(sb); - unsigned i; - - for (i = 0; i < sb->nr_devices; i++) { - struct bch_member *m = mi->members + i; - time_t last_mount = le64_to_cpu(m->last_mount); - char member_uuid_str[40]; - char data_allowed_str[100]; - char data_has_str[100]; - char group[BCH_SB_LABEL_SIZE+10]; - char time_str[64]; - - if (!bch2_member_exists(m)) - continue; - - uuid_unparse(m->uuid.b, member_uuid_str); - - if (BCH_MEMBER_GROUP(m)) { - unsigned idx = BCH_MEMBER_GROUP(m) - 1; - - if (idx < disk_groups_nr(gi)) { - snprintf(group, sizeof(group), "%.*s (%u)", - BCH_SB_LABEL_SIZE, - gi->entries[idx].label, idx); - } else { - strcpy(group, "(bad disk groups section)"); - } - } else { - strcpy(group, "(none)"); - } - - 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_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" - " Bucket size: %s\n" - " First bucket: %u\n" - " Buckets: %llu\n" - " Last mount: %s\n" - " State: %s\n" - " Group: %s\n" - " Data allowed: %s\n" - - " Has data: %s\n" - - " Replacement policy: %s\n" - " Discard: %llu\n", - i, member_uuid_str, - pr_units(le16_to_cpu(m->bucket_size) * - le64_to_cpu(m->nbuckets), units), - pr_units(le16_to_cpu(m->bucket_size), units), - le16_to_cpu(m->first_bucket), - le64_to_cpu(m->nbuckets), - time_str, - - BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR - ? bch2_dev_state[BCH_MEMBER_STATE(m)] - : "unknown", - - group, - data_allowed_str, - data_has_str, - - BCH_MEMBER_REPLACEMENT(m) < CACHE_REPLACEMENT_NR - ? bch2_cache_replacement_policies[BCH_MEMBER_REPLACEMENT(m)] - : "unknown", - - BCH_MEMBER_DISCARD(m)); - } -} - -static void bch2_sb_print_crypt(struct bch_sb *sb, struct bch_sb_field *f, - enum units units) -{ - struct bch_sb_field_crypt *crypt = field_to_type(f, crypt); - - printf(" KFD: %llu\n" - " scrypt n: %llu\n" - " scrypt r: %llu\n" - " scrypt p: %llu\n", - BCH_CRYPT_KDF_TYPE(crypt), - BCH_KDF_SCRYPT_N(crypt), - BCH_KDF_SCRYPT_R(crypt), - 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) -{ - struct bch_sb_field_replicas *replicas = field_to_type(f, replicas); - struct bch_replicas_entry *e; - unsigned i; - - for_each_replicas_entry(replicas, e) { - 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_devs; i++) { - if (i) - putchar(' '); - printf("%u", e->devs[i]); - } - printf("]\n"); - } -} - -static void bch2_sb_print_quota(struct bch_sb *sb, struct bch_sb_field *f, - enum units units) -{ -} - -static void bch2_sb_print_disk_groups(struct bch_sb *sb, struct bch_sb_field *f, - enum units units) -{ -} - -static void bch2_sb_print_clean(struct bch_sb *sb, struct bch_sb_field *f, - enum units units) -{ -} - -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 { - sb_field_print_fn print; -}; - -static const struct bch_sb_field_toolops bch2_sb_field_ops[] = { -#define x(f, nr) \ - [BCH_SB_FIELD_##f] = { \ - .print = bch2_sb_print_##f, \ - }, - BCH_SB_FIELDS() -#undef x -}; - -static inline void bch2_sb_field_print(struct bch_sb *sb, - struct bch_sb_field *f, - enum units units) -{ - unsigned type = le32_to_cpu(f->type); - - if (type < BCH_SB_FIELD_NR) - bch2_sb_field_ops[type].print(sb, f, units); - else - printf("(unknown field %u)\n", type); -} - -void bch2_sb_print(struct bch_sb *sb, bool print_layout, - unsigned fields, enum units units) -{ - struct bch_sb_field_members *mi; - char user_uuid_str[40], internal_uuid_str[40]; - char features_str[200]; - 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]; - 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'; - - 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; - - for (m = mi->members; - m < mi->members + sb->nr_devices; - m++) - 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_flags_to_text(&PBUF(features_str), - bch2_sb_features, - le64_to_cpu(sb->features[0])); - - vstruct_for_each(sb, f) - fields_have |= 1 << le32_to_cpu(f->type); - bch2_flags_to_text(&PBUF(fields_have_str), - bch2_sb_fields, fields_have); - - printf("External UUID: %s\n" - "Internal UUID: %s\n" - "Label: %s\n" - "Version: %llu\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" - - "Metadata replicas: %llu\n" - "Data replicas: %llu\n" - - "Metadata checksum type: %s (%llu)\n" - "Data checksum type: %s (%llu)\n" - "Compression type: %s (%llu)\n" - - "Foreground write target: %s\n" - "Background write target: %s\n" - "Promote target: %s\n" - - "String hash type: %s (%llu)\n" - "32 bit inodes: %llu\n" - "GC reserve percentage: %llu%%\n" - "Root reserve percentage: %llu%%\n" - - "Devices: %u live, %u total\n" - "Sections: %s\n" - "Superblock size: %llu\n", - user_uuid_str, - internal_uuid_str, - label, - le64_to_cpu(sb->version), - 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 - ? bch2_error_actions[BCH_SB_ERROR_ACTION(sb)] - : "unknown", - - BCH_SB_CLEAN(sb), - 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_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_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_opts[BCH_SB_COMPRESSION_TYPE(sb)] - : "unknown", - BCH_SB_COMPRESSION_TYPE(sb), - - foreground_str, - background_str, - promote_str, - - BCH_SB_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR - ? bch2_str_hash_types[BCH_SB_STR_HASH_TYPE(sb)] - : "unknown", - BCH_SB_STR_HASH_TYPE(sb), - - BCH_SB_INODE_32BIT(sb), - BCH_SB_GC_RESERVE(sb), - BCH_SB_ROOT_RESERVE(sb), - - nr_devices, sb->nr_devices, - fields_have_str, - vstruct_bytes(sb)); - - if (print_layout) { - printf("\n" - "Layout:\n"); - bch2_sb_print_layout(sb, units); - } - - vstruct_for_each(sb, f) { - unsigned type = le32_to_cpu(f->type); - char name[60]; - - if (!(fields & (1 << type))) - continue; - - if (type < BCH_SB_FIELD_NR) { - scnprintf(name, sizeof(name), "%s", bch2_sb_fields[type]); - name[0] = toupper(name[0]); - } else { - scnprintf(name, sizeof(name), "(unknown field %u)", type); - } - - printf("\n%s (size %llu):\n", name, vstruct_bytes(f)); - if (type < BCH_SB_FIELD_NR) - bch2_sb_field_print(sb, f, units); - } -} - /* ioctl interface: */ /* Global control device: */ @@ -868,7 +401,9 @@ struct bchfs_handle bcache_fs_open(const char *path) free(ctl); } else { /* It's a path: */ - ret.ioctl_fd = xopen(path, O_RDONLY); + ret.ioctl_fd = open(path, O_RDONLY); + if (ret.ioctl_fd < 0) + die("Error opening filesystem at %s: %m", path); struct bch_ioctl_query_uuid uuid; if (ioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid) < 0) @@ -891,7 +426,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; @@ -935,6 +470,17 @@ 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); @@ -979,6 +525,16 @@ int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd) /* 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) { @@ -1010,10 +566,9 @@ struct bch_opt_strs bch2_cmdline_opts_get(int *argc, char *argv[], optid = bch2_opt_lookup(optstr); if (optid < 0 || - !(bch2_opt_table[optid].mode & opt_types)) { - free(optstr); + !(bch2_opt_table[optid].flags & opt_types)) { i++; - continue; + goto next; } if (!valstr && @@ -1025,13 +580,15 @@ struct bch_opt_strs bch2_cmdline_opts_get(int *argc, char *argv[], if (!valstr) valstr = "1"; - opts.by_id[optid] = valstr; + 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; @@ -1040,6 +597,7 @@ struct bch_opt_strs bch2_cmdline_opts_get(int *argc, char *argv[], struct bch_opts bch2_parse_opts(struct bch_opt_strs strs) { struct bch_opts opts = bch2_opts_empty(); + struct printbuf err = PRINTBUF; unsigned i; int ret; u64 v; @@ -1049,40 +607,35 @@ struct bch_opts bch2_parse_opts(struct bch_opt_strs strs) bch2_opt_table[i].type == BCH_OPT_FN) continue; - ret = bch2_opt_parse(NULL, &bch2_opt_table[i], - strs.by_id[i], &v); + ret = bch2_opt_parse(NULL, + &bch2_opt_table[i], + strs.by_id[i], &v, &err); if (ret < 0) - die("Invalid %s: %s", - bch2_opt_table[i].attr.name, - strerror(-ret)); + die("Invalid option %s", err.buf); bch2_opt_set_by_id(&opts, i, v); } + printbuf_exit(&err); 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; - void tabalign() { - while (c < helpcol) { - putchar(' '); - c++; - } - } - void newline() { - printf("\n"); - c = 0; - } for (opt = bch2_opt_table; opt < bch2_opt_table + bch2_opts_nr; opt++) { - if (!(opt->mode & opt_types)) + if (!(opt->flags & opt_types)) continue; c += printf(" --%s", opt->attr.name); @@ -1108,21 +661,24 @@ void bch2_opts_usage(unsigned opt_types) const char *l = opt->help; if (c >= helpcol) - newline(); + newline(c); while (1) { const char *n = strchrnul(l, '\n'); - tabalign(); + while (c < helpcol) { + putchar(' '); + c++; + } printf("%.*s", (int) (n - l), l); - newline(); + newline(c); if (!*n) break; l = n + 1; } } else { - newline(); + newline(c); } } } @@ -1133,7 +689,7 @@ dev_names bchu_fs_get_devices(struct bchfs_handle fs) struct dirent *d; dev_names devs; - darray_init(devs); + darray_init(&devs); while ((errno = 0), (d = readdir(dir))) { struct dev_name n = { 0, NULL, NULL }; @@ -1157,7 +713,7 @@ dev_names bchu_fs_get_devices(struct bchfs_handle fs) n.label = read_file_str(fs.sysfs_fd, label_attr); free(label_attr); - darray_append(devs, n); + darray_push(&devs, n); } closedir(dir);