]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs.c
Update bcachefs sources to f638850417 bcachefs: bch2_trans_log_msg()
[bcachefs-tools-debian] / libbcachefs.c
index 076fe667282254d19258fc204128148dac2c2693..8ba01947fa7e2842352dd6adab2afea8f35dec19 100644 (file)
 
 #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,
                        unsigned sb_size,
@@ -64,14 +58,20 @@ static void init_layout(struct bch_sb_layout *l,
                    sb_start, sb_pos, sb_end, sb_size);
 }
 
+/* minimum size filesystem we can create, given a bucket size: */
+static u64 min_size(unsigned bucket_size)
+{
+       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: */
@@ -83,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;
@@ -100,21 +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 << 9 < opts.block_size)
-               die("Bucket size (%u) cannot be smaller than block size (%u)",
-                   dev->bucket_size << 9, opts.block_size);
+       if (dev->bucket_size < opts.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 << 9 < opts.btree_node_size)
-               die("Bucket size (%u) cannot be smaller than btree node size (%u)",
-                   dev->bucket_size << 9, opts.btree_node_size);
+           dev->bucket_size < opts.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,
@@ -174,7 +177,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs      fs_opt_strs,
                for (i = devs; i < devs + nr_devs; i++)
                        fs_opts.btree_node_size =
                                min_t(unsigned, fs_opts.btree_node_size,
-                                     i->bucket_size << 9);
+                                     i->bucket_size);
        }
 
        if (uuid_is_null(opts.uuid.b))
@@ -229,7 +232,7 @@ 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_DISCARD(m,       i->discard);
                SET_BCH_MEMBER_DATA_ALLOWED(m,  i->data_allowed);
@@ -238,7 +241,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs      fs_opt_strs,
 
        /* 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->label)
@@ -246,7 +249,14 @@ struct bch_sb *bch2_format(struct bch_opt_strs     fs_opt_strs,
 
                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);
        }
@@ -270,11 +280,13 @@ 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       = i->size;
+                       i->sb_end       = size_sectors;
                }
 
                init_layout(&sb.sb->layout, fs_opts.block_size,
@@ -290,9 +302,9 @@ struct bch_sb *bch2_format(struct bch_opt_strs      fs_opt_strs,
                 */
                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);
+                       u64 backup_sb = size_sectors - (1 << l->sb_max_size_bits);
 
-                       backup_sb = rounddown(backup_sb, i->bucket_size);
+                       backup_sb = rounddown(backup_sb, i->bucket_size >> 9);
                        l->sb_offset[l->nr_superblocks++] = cpu_to_le64(backup_sb);
                }
 
@@ -300,7 +312,8 @@ struct bch_sb *bch2_format(struct bch_opt_strs      fs_opt_strs,
                        /* 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);
@@ -321,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);
@@ -350,503 +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, "Label %u (%.*s)", t.group,
-                               BCH_SB_LABEL_SIZE, g->label);
-               } else {
-                       ret = scnprintf(buf, len, "Bad label %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 label [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)) {
-                               scnprintf(label, sizeof(label), "%.*s (%u)",
-                                       BCH_SB_LABEL_SIZE,
-                                       gi->entries[idx].label, idx);
-                       } else {
-                               strcpy(label, "(bad disk labels section)");
-                       }
-               } else {
-                       strcpy(label, "(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"
-
-                      "    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_member_states[BCH_MEMBER_STATE(m)]
-                      : "unknown",
-
-                      label,
-                      data_allowed_str,
-                      data_has_str,
-
-                      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)
-{
-       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 {
-       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[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';
-
-       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_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_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:                        %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"
-
-              "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"
-               "Metadata 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,
-              sb->dev_idx,
-              label,
-              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_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_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,
-               metadata_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: */
@@ -1079,6 +597,7 @@ next:
 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;
@@ -1088,17 +607,16 @@ 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, "option",
+               ret = bch2_opt_parse(NULL,
                                     &bch2_opt_table[i],
-                                    strs.by_id[i], &v);
+                                    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;
 }
 
@@ -1195,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);