+ 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)
+{
+}
+
+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);