/*
* gen_bitmap64.c --- routines to read, write, and manipulate the new qinode and
* block bitmaps.
*
* Copyright (C) 2007, 2008 Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*/
#include <stdio.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include "ext2_fs.h"
#include "ext2fsP.h"
#include "bmap64.h"
/*
* Design of 64-bit bitmaps
*
* In order maintain ABI compatibility with programs that don't
* understand about 64-bit blocks/inodes,
* ext2fs_allocate_inode_bitmap() and ext2fs_allocate_block_bitmap()
* will create old-style bitmaps unless the application passes the
* flag EXT2_FLAG_64BITS to ext2fs_open(). If this flag is
* passed, then we know the application has been recompiled, so we can
* use the new-style bitmaps. If it is not passed, we have to return
* an error if trying to open a filesystem which needs 64-bit bitmaps.
*
* The new bitmaps use a new set of structure magic numbers, so that
* both the old-style and new-style interfaces can identify which
* version of the data structure was used. Both the old-style and
* new-style interfaces will support either type of bitmap, although
* of course 64-bit operation will only be possible when both the
* new-style interface and the new-style bitmap are used.
*
* For example, the new bitmap interfaces will check the structure
* magic numbers and so will be able to detect old-stype bitmap. If
* they see an old-style bitmap, they will pass it to the gen_bitmap.c
* functions for handling. The same will be true for the old
* interfaces as well.
*
* The new-style interfaces will have several different back-end
* implementations, so we can support different encodings that are
* appropriate for different applications. In general the default
* should be whatever makes sense, and what the application/library
* will use. However, e2fsck may need specialized implementations for
* its own uses. For example, when doing parent directory pointer
* loop detections in pass 3, the bitmap will *always* be sparse, so
* e2fsck can request an encoding which is optimized for that.
*/
static void warn_bitmap(ext2fs_generic_bitmap bitmap,
int code, __u64 arg)
{
#ifndef OMIT_COM_ERR
if (bitmap->description)
com_err(0, bitmap->base_error_code+code,
"#%llu for %s", arg, bitmap->description);
else
com_err(0, bitmap->base_error_code + code, "#%llu", arg);
#endif
}
#ifdef BMAP_STATS_OPS
#define INC_STAT(map, name) map->stats.name
#else
#define INC_STAT(map, name) ;;
#endif
errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic,
int type, __u64 start, __u64 end,
__u64 real_end,
const char *descr,
ext2fs_generic_bitmap *ret)
{
ext2fs_generic_bitmap bitmap;
struct ext2_bitmap_ops *ops;
ext2_ino_t num_dirs;
errcode_t retval;
if (!type)
type = EXT2FS_BMAP64_BITARRAY;
switch (type) {
case EXT2FS_BMAP64_BITARRAY:
ops = &ext2fs_blkmap64_bitarray;
break;
case EXT2FS_BMAP64_RBTREE:
ops = &ext2fs_blkmap64_rbtree;
break;
case EXT2FS_BMAP64_AUTODIR:
retval = ext2fs_get_num_dirs(fs, &num_dirs);
if (retval || num_dirs > (fs->super->s_inodes_count / 320))
ops = &ext2fs_blkmap64_bitarray;
else
ops = &ext2fs_blkmap64_rbtree;
break;
default:
return EINVAL;
}
retval = ext2fs_get_memzero(sizeof(struct ext2fs_struct_generic_bitmap),
&bitmap);
if (retval)
return retval;
#ifdef BMAP_STATS
if (gettimeofday(&bitmap->stats.created,
(struct timezone *) NULL) == -1) {
perror("gettimeofday");
ext2fs_free_mem(&bitmap);
return 1;
}
bitmap->stats.type = type;
#endif
/* XXX factor out, repeated in copy_bmap */
bitmap->magic = magic;
bitmap->fs = fs;
bitmap->start = start;
bitmap->end = end;
bitmap->real_end = real_end;
bitmap->bitmap_ops = ops;
bitmap->cluster_bits = 0;
switch (magic) {
case EXT2_ET_MAGIC_INODE_BITMAP64:
bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK;
break;
case EXT2_ET_MAGIC_BLOCK_BITMAP64:
bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK;
bitmap->cluster_bits = fs->cluster_ratio_bits;
break;
default:
bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK;
}
if (descr) {
retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description);
if (retval) {
ext2fs_free_mem(&bitmap);
return retval;
}
strcpy(bitmap->description, descr);
} else
bitmap->description = 0;
retval = bitmap->bitmap_ops->new_bmap(fs, bitmap);
if (retval) {
ext2fs_free_mem(&bitmap->description);
ext2fs_free_mem(&bitmap);
return retval;
}
*ret = bitmap;
return 0;
}
#ifdef BMAP_STATS
static void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap bitmap)
{
struct ext2_bmap_statistics *stats = &bitmap->stats;
#ifdef BMAP_STATS_OPS
float mark_seq_perc = 0.0, test_seq_perc = 0.0;
float mark_back_perc = 0.0, test_back_perc = 0.0;
#endif
double inuse;
struct timeval now;
#ifdef BMAP_STATS_OPS
if (stats->test_count) {
test_seq_perc = ((float)stats->test_seq /
stats->test_count) * 100;
test_back_perc = ((float)stats->test_back /
stats->test_count) * 100;
}
if (stats->mark_count) {
mark_seq_perc = ((float)stats->mark_seq /
stats->mark_count) * 100;
mark_back_perc = ((float)stats->mark_back /
stats->mark_count) * 100;
}
#endif
if (gettimeofday(&now, (struct timezone *) NULL) == -1) {
perror("gettimeofday");
return;
}
inuse = (double) now.tv_sec + \
(((double) now.tv_usec) * 0.000001);
inuse -= (double) stats->created.tv_sec + \
(((double) stats->created.tv_usec) * 0.000001);
fprintf(stderr, "\n[+] %s bitmap (type %d)\n", bitmap->description,
stats->type);
fprintf(stderr, "=================================================\n");
#ifdef BMAP_STATS_OPS
fprintf(stderr, "%16llu bits long\n",
bitmap->real_end - bitmap->start);
fprintf(stderr, "%16lu copy_bmap\n%16lu resize_bmap\n",
stats->copy_count, stats->resize_count);
fprintf(stderr, "%16lu mark bmap\n%16lu unmark_bmap\n",
stats->mark_count, stats->unmark_count);
fprintf(stderr, "%16lu test_bmap\n%16lu mark_bmap_extent\n",
stats->test_count, stats->mark_ext_count);
fprintf(stderr, "%16lu unmark_bmap_extent\n"
"%16lu test_clear_bmap_extent\n",
stats->unmark_ext_count, stats->test_ext_count);
fprintf(stderr, "%16lu set_bmap_range\n%16lu set_bmap_range\n",
stats->set_range_count, stats->get_range_count);
fprintf(stderr, "%16lu clear_bmap\n%16lu contiguous bit test (%.2f%%)\n",
stats->clear_count, stats->test_seq, test_seq_perc);
fprintf(stderr, "%16lu contiguous bit mark (%.2f%%)\n"
"%16llu bits tested backwards (%.2f%%)\n",
stats->mark_seq, mark_seq_perc,
stats->test_back, test_back_perc);
fprintf(stderr, "%16llu bits marked backwards (%.2f%%)\n"
"%16.2f seconds in use\n",
stats->mark_back, mark_back_perc, inuse);
#endif /* BMAP_STATS_OPS */
}
#endif
void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap)
{
if (!bmap)
return;
if (EXT2FS_IS_32_BITMAP(bmap)) {
ext2fs_free_generic_bitmap(bmap);
return;
}
if (!EXT2FS_IS_64_BITMAP(bmap))
return;
#ifdef BMAP_STATS
if (getenv("E2FSPROGS_BITMAP_STATS")) {
ext2fs_print_bmap_statistics(bmap);
bmap->bitmap_ops->print_stats(bmap);
}
#endif
bmap->bitmap_ops->free_bmap(bmap);
if (bmap->description) {
ext2fs_free_mem(&bmap->description);
bmap->description = 0;
}
bmap->magic = 0;
ext2fs_free_mem(&bmap);
}
errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src,
ext2fs_generic_bitmap *dest)
{
char *descr, *new_descr;
ext2fs_generic_bitmap new_bmap;
errcode_t retval;
if (!src)
return EINVAL;
if (EXT2FS_IS_32_BITMAP(src))
return ext2fs_copy_generic_bitmap(src, dest);
if (!EXT2FS_IS_64_BITMAP(src))
return EINVAL;
/* Allocate a new bitmap struct */
retval = ext2fs_get_memzero(sizeof(struct ext2fs_struct_generic_bitmap),
&new_bmap);
if (retval)
return retval;
#ifdef BMAP_STATS_OPS
src->stats.copy_count++;
#endif
#ifdef BMAP_STATS
if (gettimeofday(&new_bmap->stats.created,
(struct timezone *) NULL) == -1) {
perror("gettimeofday");
ext2fs_free_mem(&new_bmap);
return 1;
}
new_bmap->stats.type = src->stats.type;
#endif
/* Copy all the high-level parts over */
new_bmap->magic = src->magic;
new_bmap->fs = src->fs;
new_bmap->start = src->start;
new_bmap->end = src->end;
new_bmap->real_end = src->real_end;
new_bmap->bitmap_ops = src->bitmap_ops;
new_bmap->base_error_code = src->base_error_code;
new_bmap->cluster_bits = src->cluster_bits;
descr = src->description;
if (descr) {
retval = ext2fs_get_mem(strlen(descr)+10, &new_descr);
if (retval) {
ext2fs_free_mem(&new_bmap);
return retval;
}
sprintf(new_descr, "copy of %s", descr);
new_bmap->description = new_descr;
}
retval = src->bitmap_ops->copy_bmap(src, new_bmap);
if (retval) {
ext2fs_free_mem(&new_bmap->description);
ext2fs_free_mem(&new_bmap);
return retval;
}
*dest = new_bmap;
return 0;
}
errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap,
__u64 new_end,
__u64 new_real_end)
{
if (!bmap)
return EINVAL;
if (EXT2FS_IS_32_BITMAP(bmap))
return ext2fs_resize_generic_bitmap(bmap->magic, new_end,
new_real_end, bmap);
if (!EXT2FS_IS_64_BITMAP(bmap))
return EINVAL;
INC_STAT(bmap, resize_count);
return bmap->bitmap_ops->resize_bmap(bmap, new_end, new_real_end);
}
errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap,
errcode_t neq,
__u64 end, __u64 *oend)
{
if (!bitmap)
return EINVAL;
if (EXT2FS_IS_32_BITMAP(bitmap)) {
ext2_ino_t tmp_oend;
int retval;
retval = ext2fs_fudge_generic_bitmap_end(bitmap, bitmap->magic,
neq, end, &tmp_oend);
if (oend)
*oend = tmp_oend;
return retval;
}
if (!EXT2FS_IS_64_BITMAP(bitmap))
return EINVAL;
if (end > bitmap->real_end)
return neq;
if (oend)
*oend = bitmap->end;
bitmap->end = end;
return 0;
}
__u64 ext2fs_get_generic_bmap_start(ext2fs_generic_bitmap bitmap)
{
if (!bitmap)
return EINVAL;
if (EXT2FS_IS_32_BITMAP(bitmap))
return ext2fs_get_generic_bitmap_start(bitmap);
if (!EXT2FS_IS_64_BITMAP(bitmap))
return EINVAL;
return bitmap->start;
}
__u64 ext2fs_get_generic_bmap_end(ext2fs_generic_bitmap bitmap)
{
if (!bitmap)
return EINVAL;
if (EXT2FS_IS_32_BITMAP(bitmap))
return ext2fs_get_generic_bitmap_end(bitmap);
if (!EXT2FS_IS_64_BITMAP(bitmap))
return EINVAL;
return bitmap->end;
}
void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap)
{
if (EXT2FS_IS_32_BITMAP(bitmap))
ext2fs_clear_generic_bitmap(bitmap);
else
bitmap->bitmap_ops->clear_bmap (bitmap);
}
int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 arg)
{
if (!bitmap)
return 0;
if (EXT2FS_IS_32_BITMAP(bitmap)) {
if (arg & ~0xffffffffULL) {
ext2fs_warn_bitmap2(bitmap,
EXT2FS_MARK_ERROR, 0xffffffff);
return 0;
}
return ext2fs_mark_generic_bitmap(bitmap, arg);
}
if (!EXT2FS_IS_64_BITMAP(bitmap))
return 0;
arg >>= bitmap->cluster_bits;
#ifdef BMAP_STATS_OPS
if (arg == bitmap->stats.last_marked + 1)
bitmap->stats.mark_seq++;
if (arg < bitmap->stats.last_marked)
bitmap->stats.mark_back++;
bitmap->stats.last_marked = arg;
bitmap->stats.mark_count++;
#endif
if ((arg < bitmap->start) || (arg > bitmap->end)) {
warn_bitmap(bitmap, EXT2FS_MARK_ERROR, arg);
return 0;
}
return bitmap->bitmap_ops->mark_bmap(bitmap, arg);
}
int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 arg)
{
if (!bitmap)
return 0;
if (EXT2FS_IS_32_BITMAP(bitmap)) {
if (arg & ~0xffffffffULL) {
ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR,
0xffffffff);
return 0;
}
return ext2fs_unmark_generic_bitmap(bitmap, arg);
}
if (!EXT2FS_IS_64_BITMAP(bitmap))
return 0;
arg >>= bitmap->cluster_bits;
INC_STAT(bitmap, unmark_count);
if ((arg < bitmap->start) || (arg > bitmap->end)) {
warn_bitmap(bitmap, EXT2FS_UNMARK_ERROR, arg);
return 0;
}
return bitmap->bitmap_ops->unmark_bmap(bitmap, arg);
}
int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 arg)
{
if (!bitmap)
return 0;
if (EXT2FS_IS_32_BITMAP(bitmap)) {
if (arg & ~0xffffffffULL) {
ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR,
0xffffffff);
return 0;
}
return ext2fs_test_generic_bitmap(bitmap, arg);
}
if (!EXT2FS_IS_64_BITMAP(bitmap))
return 0;
arg >>= bitmap->cluster_bits;
#ifdef BMAP_STATS_OPS
bitmap->stats.test_count++;
if (arg == bitmap->stats.last_tested + 1)
bitmap->stats.test_seq++;
if (arg < bitmap->stats.last_tested)
bitmap->stats.test_back++;
bitmap->stats.last_tested = arg;
#endif
if ((arg < bitmap->start) || (arg > bitmap->end)) {
warn_bitmap(bitmap, EXT2FS_TEST_ERROR, arg);
return 0;
}
return bitmap->bitmap_ops->test_bmap(bitmap, arg);
}
errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bmap,
__u64 start, unsigned int num,
void *in)
{
if (!bmap)
return EINVAL;
if (EXT2FS_IS_32_BITMAP(bmap)) {
if ((start+num-1) & ~0xffffffffULL) {
ext2fs_warn_bitmap2(bmap, EXT2FS_UNMARK_ERROR,
0xffffffff);
return EINVAL;
}
return ext2fs_set_generic_bitmap_range(bmap, bmap->magic,
start, num, in);
}
if (!EXT2FS_IS_64_BITMAP(bmap))
return EINVAL;
INC_STAT(bmap, set_range_count);
return bmap->bitmap_ops->set_bmap_range(bmap, start, num, in);
}
errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bmap,
__u64 start, unsigned int num,
void *out)
{
if (!bmap)
return EINVAL;
if (EXT2FS_IS_32_BITMAP(bmap)) {
if ((start+num-1) & ~0xffffffffULL) {
ext2fs_warn_bitmap2(bmap,
EXT2FS_UNMARK_ERROR, 0xffffffff);
return EINVAL;
}
return ext2fs_get_generic_bitmap_range(bmap, bmap->magic,
start, num, out);
}
if (!EXT2FS_IS_64_BITMAP(bmap))
return EINVAL;
INC_STAT(bmap, get_range_count);
return bmap->bitmap_ops->get_bmap_range(bmap, start, num, out);
}
errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
ext2fs_generic_bitmap bm1,
ext2fs_generic_bitmap bm2)
{
blk64_t i;
if (!bm1 || !bm2)
return EINVAL;
if (bm1->magic != bm2->magic)
return EINVAL;
/* Now we know both bitmaps have the same magic */
if (EXT2FS_IS_32_BITMAP(bm1))
return ext2fs_compare_generic_bitmap(bm1->magic, neq, bm1, bm2);
if (!EXT2FS_IS_64_BITMAP(bm1))
return EINVAL;
if ((bm1->start != bm2->start) ||
(bm1->end != bm2->end))
return neq;
for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
if (ext2fs_test_generic_bmap(bm1, i) !=
ext2fs_test_generic_bmap(bm2, i))
return neq;
return 0;
}
void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap)
{
__u64 start, num;
if (EXT2FS_IS_32_BITMAP(bmap)) {
ext2fs_set_generic_bitmap_padding(bmap);
return;
}
start = bmap->end + 1;
num = bmap->real_end - bmap->end;
bmap->bitmap_ops->mark_bmap_extent(bmap, start, num);
/* XXX ought to warn on error */
}
int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bmap,
blk64_t block, unsigned int num)
{
__u64 end = block + num;
if (!bmap)
return EINVAL;
if (num == 1)
return !ext2fs_test_generic_bmap((ext2fs_generic_bitmap)
bmap, block);
if (EXT2FS_IS_32_BITMAP(bmap)) {
if ((block+num-1) & ~0xffffffffULL) {
ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
EXT2FS_UNMARK_ERROR, 0xffffffff);
return EINVAL;
}
return ext2fs_test_block_bitmap_range(
(ext2fs_generic_bitmap) bmap, block, num);
}
if (!EXT2FS_IS_64_BITMAP(bmap))
return EINVAL;
INC_STAT(bmap, test_ext_count);
/* convert to clusters if necessary */
block >>= bmap->cluster_bits;
end += (1 << bmap->cluster_bits) - 1;
end >>= bmap->cluster_bits;
num = end - block;
if ((block < bmap->start) || (block+num-1 > bmap->end)) {
ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, block,
bmap->description);
return EINVAL;
}
return bmap->bitmap_ops->test_clear_bmap_extent(bmap, block, num);
}
void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bmap,
blk64_t block, unsigned int num)
{
__u64 end = block + num;
if (!bmap)
return;
if (EXT2FS_IS_32_BITMAP(bmap)) {
if ((block+num-1) & ~0xffffffffULL) {
ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
EXT2FS_UNMARK_ERROR, 0xffffffff);
return;
}
ext2fs_mark_block_bitmap_range((ext2fs_generic_bitmap) bmap,
block, num);
}
if (!EXT2FS_IS_64_BITMAP(bmap))
return;
INC_STAT(bmap, mark_ext_count);
/* convert to clusters if necessary */
block >>= bmap->cluster_bits;
end += (1 << bmap->cluster_bits) - 1;
end >>= bmap->cluster_bits;
num = end - block;
if ((block < bmap->start) || (block+num-1 > bmap->end)) {
ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
bmap->description);
return;
}
bmap->bitmap_ops->mark_bmap_extent(bmap, block, num);
}
void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bmap,
blk64_t block, unsigned int num)
{
__u64 end = block + num;
if (!bmap)
return;
if (EXT2FS_IS_32_BITMAP(bmap)) {
if ((block+num-1) & ~0xffffffffULL) {
ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
EXT2FS_UNMARK_ERROR, 0xffffffff);
return;
}
ext2fs_unmark_block_bitmap_range((ext2fs_generic_bitmap) bmap,
block, num);
}
if (!EXT2FS_IS_64_BITMAP(bmap))
return;
INC_STAT(bmap, unmark_ext_count);
/* convert to clusters if necessary */
block >>= bmap->cluster_bits;
end += (1 << bmap->cluster_bits) - 1;
end >>= bmap->cluster_bits;
num = end - block;
if ((block < bmap->start) || (block+num-1 > bmap->end)) {
ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
bmap->description);
return;
}
bmap->bitmap_ops->unmark_bmap_extent(bmap, block, num);
}
void ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap, const char *func)
{
#ifndef OMIT_COM_ERR
if (bitmap && bitmap->description)
com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP,
"called %s with 64-bit bitmap for %s", func,
bitmap->description);
else
com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP,
"called %s with 64-bit bitmap", func);
#endif
}
errcode_t ext2fs_convert_subcluster_bitmap(ext2_filsys fs,
ext2fs_block_bitmap *bitmap)
{
ext2fs_block_bitmap cmap, bmap;
errcode_t retval;
blk64_t i, b_end, c_end;
int n, ratio;
bmap = *bitmap;
if (fs->cluster_ratio_bits == ext2fs_get_bitmap_granularity(bmap))
return 0; /* Nothing to do */
retval = ext2fs_allocate_block_bitmap(fs, "converted cluster bitmap",
&cmap);
if (retval)
return retval;
i = bmap->start;
b_end = bmap->end;
bmap->end = bmap->real_end;
c_end = cmap->end;
cmap->end = cmap->real_end;
n = 0;
ratio = 1 << fs->cluster_ratio_bits;
while (i < bmap->real_end) {
if (ext2fs_test_block_bitmap2(bmap, i)) {
ext2fs_mark_block_bitmap2(cmap, i);
i += ratio - n;
n = 0;
continue;
}
i++; n++;
if (n >= ratio)
n = 0;
}
bmap->end = b_end;
cmap->end = c_end;
ext2fs_free_block_bitmap(bmap);
*bitmap = cmap;
return 0;
}
errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 start, __u64 end, __u64 *out)
{
int b;
if (!bitmap)
return EINVAL;
if (EXT2FS_IS_64_BITMAP(bitmap) && bitmap->bitmap_ops->find_first_zero)
return bitmap->bitmap_ops->find_first_zero(bitmap, start,
end, out);
if (EXT2FS_IS_32_BITMAP(bitmap)) {
blk_t blk = 0;
errcode_t retval;
if (((start) & ~0xffffffffULL) ||
((end) & ~0xffffffffULL)) {
ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, start);
return EINVAL;
}
retval = ext2fs_find_first_zero_generic_bitmap(bitmap, start,
end, &blk);
if (retval == 0)
*out = blk;
return retval;
}
if (!EXT2FS_IS_64_BITMAP(bitmap))
return EINVAL;
start >>= bitmap->cluster_bits;
end >>= bitmap->cluster_bits;
if (start < bitmap->start || end > bitmap->end || start > end) {
warn_bitmap(bitmap, EXT2FS_TEST_ERROR, start);
return EINVAL;
}
while (start <= end) {
b = bitmap->bitmap_ops->test_bmap(bitmap, start);
if (!b) {
*out = start << bitmap->cluster_bits;
return 0;
}
start++;
}
return ENOENT;
}