+ return true;
+}
+
+static inline int is_power_of(uint32_t a, uint32_t b)
+{
+ while (1) {
+ if (a < b)
+ return 0;
+ if (a == b)
+ return 1;
+ if ((a % b) != 0)
+ return 0;
+ a = a / b;
+ }
+}
+
+bool ext4_sb_sparse(uint32_t group)
+{
+ if (group <= 1)
+ return 1;
+
+ if (!(group & 1))
+ return 0;
+
+ return (is_power_of(group, 7) || is_power_of(group, 5) ||
+ is_power_of(group, 3));
+}
+
+bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t group)
+{
+ if (ext4_sb_feature_ro_com(s, EXT4_FRO_COM_SPARSE_SUPER) &&
+ !ext4_sb_sparse(group))
+ return false;
+ return true;
+}
+
+static uint32_t ext4_bg_num_gdb_meta(struct ext4_sblock *s, uint32_t group)
+{
+ uint32_t dsc_per_block =
+ ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
+
+ uint32_t metagroup = group / dsc_per_block;
+ uint32_t first = metagroup * dsc_per_block;
+ uint32_t last = first + dsc_per_block - 1;
+
+ if (group == first || group == first + 1 || group == last)
+ return 1;
+ return 0;
+}
+
+static uint32_t ext4_bg_num_gdb_nometa(struct ext4_sblock *s, uint32_t group)
+{
+ if (!ext4_sb_is_super_in_bg(s, group))
+ return 0;
+ uint32_t dsc_per_block =
+ ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
+
+ uint32_t db_count =
+ (ext4_block_group_cnt(s) + dsc_per_block - 1) / dsc_per_block;
+
+ if (ext4_sb_feature_incom(s, EXT4_FINCOM_META_BG))
+ return ext4_sb_first_meta_bg(s);
+
+ return db_count;
+}
+
+uint32_t ext4_bg_num_gdb(struct ext4_sblock *s, uint32_t group)
+{
+ uint32_t dsc_per_block =
+ ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);
+ uint32_t first_meta_bg = ext4_sb_first_meta_bg(s);
+ uint32_t metagroup = group / dsc_per_block;
+
+ if (!ext4_sb_feature_incom(s,EXT4_FINCOM_META_BG) ||
+ metagroup < first_meta_bg)
+ return ext4_bg_num_gdb_nometa(s, group);
+
+ return ext4_bg_num_gdb_meta(s, group);
+}
+
+uint32_t ext4_num_base_meta_clusters(struct ext4_sblock *s,
+ uint32_t block_group)
+{
+ uint32_t num;
+ uint32_t dsc_per_block =
+ ext4_sb_get_block_size(s) / ext4_sb_get_desc_size(s);