+static int hash_levels(size_t hash_block_size, size_t digest_size,
+ off_t data_file_blocks, off_t *hash_position, int *levels,
+ off_t *hash_level_block, off_t *hash_level_size)
+{
+ size_t hash_per_block_bits;
+ off_t s, s_shift;
+ int i;
+
+ if (!digest_size)
+ return -EINVAL;
+
+ hash_per_block_bits = get_bits_down(hash_block_size / digest_size);
+ if (!hash_per_block_bits)
+ return -EINVAL;
+
+ *levels = 0;
+ while (hash_per_block_bits * *levels < 64 &&
+ (data_file_blocks - 1) >> (hash_per_block_bits * *levels))
+ (*levels)++;
+
+ if (*levels > VERITY_MAX_LEVELS)
+ return -EINVAL;
+
+ for (i = *levels - 1; i >= 0; i--) {
+ if (hash_level_block)
+ hash_level_block[i] = *hash_position;
+ // verity position of block data_file_blocks at level i
+ s_shift = (i + 1) * hash_per_block_bits;
+ if (s_shift > 63)
+ return -EINVAL;
+ s = (data_file_blocks + ((off_t)1 << s_shift) - 1) >> ((i + 1) * hash_per_block_bits);
+ if (hash_level_size)
+ hash_level_size[i] = s;
+ if ((*hash_position + s) < *hash_position ||
+ (*hash_position + s) < 0)
+ return -EINVAL;
+ *hash_position += s;
+ }
+
+ return 0;
+}
+