Home | History | Annotate | Download | only in ext2fs
      1 /*
      2  * blkmap64_ba.c --- Simple bitarray implementation for bitmaps
      3  *
      4  * Copyright (C) 2008 Theodore Ts'o.
      5  *
      6  * %Begin-Header%
      7  * This file may be redistributed under the terms of the GNU Public
      8  * License.
      9  * %End-Header%
     10  */
     11 
     12 #include "config.h"
     13 #include <stdio.h>
     14 #include <string.h>
     15 #if HAVE_UNISTD_H
     16 #include <unistd.h>
     17 #endif
     18 #include <fcntl.h>
     19 #include <time.h>
     20 #if HAVE_SYS_STAT_H
     21 #include <sys/stat.h>
     22 #endif
     23 #if HAVE_SYS_TYPES_H
     24 #include <sys/types.h>
     25 #endif
     26 
     27 #include "ext2_fs.h"
     28 #include "ext2fsP.h"
     29 #include "bmap64.h"
     30 
     31 /*
     32  * Private data for bit array implementation of bitmap ops.
     33  * Currently, this is just a pointer to our big flat hunk of memory,
     34  * exactly equivalent to the old-skool char * bitmap member.
     35  */
     36 
     37 struct ext2fs_ba_private_struct {
     38 	char *bitarray;
     39 };
     40 
     41 typedef struct ext2fs_ba_private_struct *ext2fs_ba_private;
     42 
     43 static errcode_t ba_alloc_private_data (ext2fs_generic_bitmap bitmap)
     44 {
     45 	ext2fs_ba_private bp;
     46 	errcode_t	retval;
     47 	size_t		size;
     48 
     49 	/*
     50 	 * Since we only have the one pointer, we could just shove our
     51 	 * private data in the void *private field itself, but then
     52 	 * we'd have to do a fair bit of rewriting if we ever added a
     53 	 * field.  I'm agnostic.
     54 	 */
     55 	retval = ext2fs_get_mem(sizeof (ext2fs_ba_private), &bp);
     56 	if (retval)
     57 		return retval;
     58 
     59 	size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
     60 
     61 	retval = ext2fs_get_mem(size, &bp->bitarray);
     62 	if (retval) {
     63 		ext2fs_free_mem(&bp);
     64 		bp = 0;
     65 		return retval;
     66 	}
     67 	bitmap->private = (void *) bp;
     68 	return 0;
     69 }
     70 
     71 static errcode_t ba_new_bmap(ext2_filsys fs EXT2FS_ATTR((unused)),
     72 			     ext2fs_generic_bitmap bitmap)
     73 {
     74 	ext2fs_ba_private bp;
     75 	errcode_t	retval;
     76 	size_t		size;
     77 
     78 	retval = ba_alloc_private_data (bitmap);
     79 	if (retval)
     80 		return retval;
     81 
     82 	bp = (ext2fs_ba_private) bitmap->private;
     83 	size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
     84 	memset(bp->bitarray, 0, size);
     85 
     86 	return 0;
     87 }
     88 
     89 static void ba_free_bmap(ext2fs_generic_bitmap bitmap)
     90 {
     91 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
     92 
     93 	if (!bp)
     94 		return;
     95 
     96 	if (bp->bitarray) {
     97 		ext2fs_free_mem (&bp->bitarray);
     98 		bp->bitarray = 0;
     99 	}
    100 	ext2fs_free_mem (&bp);
    101 	bp = 0;
    102 }
    103 
    104 static errcode_t ba_copy_bmap(ext2fs_generic_bitmap src,
    105 			      ext2fs_generic_bitmap dest)
    106 {
    107 	ext2fs_ba_private src_bp = (ext2fs_ba_private) src->private;
    108 	ext2fs_ba_private dest_bp;
    109 	errcode_t retval;
    110 	size_t size;
    111 
    112 	retval = ba_alloc_private_data (dest);
    113 	if (retval)
    114 		return retval;
    115 
    116 	dest_bp = (ext2fs_ba_private) dest->private;
    117 
    118 	size = (size_t) (((src->real_end - src->start) / 8) + 1);
    119 	memcpy (dest_bp->bitarray, src_bp->bitarray, size);
    120 
    121 	return 0;
    122 }
    123 
    124 static errcode_t ba_resize_bmap(ext2fs_generic_bitmap bmap,
    125 				__u64 new_end, __u64 new_real_end)
    126 {
    127 	ext2fs_ba_private bp = (ext2fs_ba_private) bmap->private;
    128 	errcode_t	retval;
    129 	size_t		size, new_size;
    130 	__u64		bitno;
    131 
    132 	/*
    133 	 * If we're expanding the bitmap, make sure all of the new
    134 	 * parts of the bitmap are zero.
    135 	 */
    136 	if (new_end > bmap->end) {
    137 		bitno = bmap->real_end;
    138 		if (bitno > new_end)
    139 			bitno = new_end;
    140 		for (; bitno > bmap->end; bitno--)
    141 			ext2fs_clear_bit64(bitno - bmap->start, bp->bitarray);
    142 	}
    143 	if (new_real_end == bmap->real_end) {
    144 		bmap->end = new_end;
    145 		return 0;
    146 	}
    147 
    148 	size = ((bmap->real_end - bmap->start) / 8) + 1;
    149 	new_size = ((new_real_end - bmap->start) / 8) + 1;
    150 
    151 	if (size != new_size) {
    152 		retval = ext2fs_resize_mem(size, new_size, &bp->bitarray);
    153 		if (retval)
    154 			return retval;
    155 	}
    156 	if (new_size > size)
    157 		memset(bp->bitarray + size, 0, new_size - size);
    158 
    159 	bmap->end = new_end;
    160 	bmap->real_end = new_real_end;
    161 	return 0;
    162 
    163 }
    164 
    165 static int ba_mark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg)
    166 {
    167 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    168 	blk64_t bitno = (blk64_t) arg;
    169 
    170 	return ext2fs_set_bit64(bitno - bitmap->start, bp->bitarray);
    171 }
    172 
    173 static int ba_unmark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg)
    174 {
    175 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    176 	blk64_t bitno = (blk64_t) arg;
    177 
    178 	return ext2fs_clear_bit64(bitno - bitmap->start, bp->bitarray);
    179 }
    180 
    181 static int ba_test_bmap(ext2fs_generic_bitmap bitmap, __u64 arg)
    182 {
    183 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    184 	blk64_t bitno = (blk64_t) arg;
    185 
    186 	return ext2fs_test_bit64(bitno - bitmap->start, bp->bitarray);
    187 }
    188 
    189 static void ba_mark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg,
    190 				unsigned int num)
    191 {
    192 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    193 	blk64_t bitno = (blk64_t) arg;
    194 	unsigned int i;
    195 
    196 	for (i = 0; i < num; i++)
    197 		ext2fs_fast_set_bit64(bitno + i - bitmap->start, bp->bitarray);
    198 }
    199 
    200 static void ba_unmark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg,
    201 				  unsigned int num)
    202 {
    203 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    204 	blk64_t bitno = (blk64_t) arg;
    205 	unsigned int i;
    206 
    207 	for (i = 0; i < num; i++)
    208 		ext2fs_fast_clear_bit64(bitno + i - bitmap->start, bp->bitarray);
    209 }
    210 
    211 static int ba_test_clear_bmap_extent(ext2fs_generic_bitmap bitmap,
    212 				     __u64 start, unsigned int len)
    213 {
    214 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    215 	__u64 start_byte, len_byte = len >> 3;
    216 	unsigned int start_bit, len_bit = len % 8;
    217 	unsigned int first_bit = 0;
    218 	unsigned int last_bit  = 0;
    219 	int mark_count = 0;
    220 	int mark_bit = 0;
    221 	int i;
    222 	const char *ADDR;
    223 
    224 	ADDR = bp->bitarray;
    225 	start -= bitmap->start;
    226 	start_byte = start >> 3;
    227 	start_bit = start % 8;
    228 
    229 	if (start_bit != 0) {
    230 		/*
    231 		 * The compared start block number or start inode number
    232 		 * is not the first bit in a byte.
    233 		 */
    234 		mark_count = 8 - start_bit;
    235 		if (len < 8 - start_bit) {
    236 			mark_count = (int)len;
    237 			mark_bit = len + start_bit - 1;
    238 		} else
    239 			mark_bit = 7;
    240 
    241 		for (i = mark_count; i > 0; i--, mark_bit--)
    242 			first_bit |= 1 << mark_bit;
    243 
    244 		/*
    245 		 * Compare blocks or inodes in the first byte.
    246 		 * If there is any marked bit, this function returns 0.
    247 		 */
    248 		if (first_bit & ADDR[start_byte])
    249 			return 0;
    250 		else if (len <= 8 - start_bit)
    251 			return 1;
    252 
    253 		start_byte++;
    254 		len_bit = (len - mark_count) % 8;
    255 		len_byte = (len - mark_count) >> 3;
    256 	}
    257 
    258 	/*
    259 	 * The compared start block number or start inode number is
    260 	 * the first bit in a byte.
    261 	 */
    262 	if (len_bit != 0) {
    263 		/*
    264 		 * The compared end block number or end inode number is
    265 		 * not the last bit in a byte.
    266 		 */
    267 		for (mark_bit = len_bit - 1; mark_bit >= 0; mark_bit--)
    268 			last_bit |= 1 << mark_bit;
    269 
    270 		/*
    271 		 * Compare blocks or inodes in the last byte.
    272 		 * If there is any marked bit, this function returns 0.
    273 		 */
    274 		if (last_bit & ADDR[start_byte + len_byte])
    275 			return 0;
    276 		else if (len_byte == 0)
    277 			return 1;
    278 	}
    279 
    280 	/* Check whether all bytes are 0 */
    281 	return ext2fs_mem_is_zero(ADDR + start_byte, len_byte);
    282 }
    283 
    284 
    285 static errcode_t ba_set_bmap_range(ext2fs_generic_bitmap bitmap,
    286 				     __u64 start, size_t num, void *in)
    287 {
    288 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    289 
    290 	memcpy (bp->bitarray + (start >> 3), in, (num + 7) >> 3);
    291 
    292 	return 0;
    293 }
    294 
    295 static errcode_t ba_get_bmap_range(ext2fs_generic_bitmap bitmap,
    296 				     __u64 start, size_t num, void *out)
    297 {
    298 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    299 
    300 	memcpy (out, bp->bitarray + (start >> 3), (num + 7) >> 3);
    301 
    302 	return 0;
    303 }
    304 
    305 static void ba_clear_bmap(ext2fs_generic_bitmap bitmap)
    306 {
    307 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
    308 
    309 	memset(bp->bitarray, 0,
    310 	       (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
    311 }
    312 
    313 #ifdef ENABLE_BMAP_STATS
    314 static void ba_print_stats(ext2fs_generic_bitmap bitmap)
    315 {
    316 	fprintf(stderr, "%16llu Bytes used by bitarray\n",
    317 		((bitmap->real_end - bitmap->start) >> 3) + 1 +
    318 		sizeof(struct ext2fs_ba_private_struct));
    319 }
    320 #else
    321 static void ba_print_stats(ext2fs_generic_bitmap bitmap EXT2FS_ATTR((unused)))
    322 {
    323 }
    324 #endif
    325 
    326 /* Find the first zero bit between start and end, inclusive. */
    327 static errcode_t ba_find_first_zero(ext2fs_generic_bitmap bitmap,
    328 				    __u64 start, __u64 end, __u64 *out)
    329 {
    330 	ext2fs_ba_private bp = (ext2fs_ba_private)bitmap->private;
    331 	unsigned long bitpos = start - bitmap->start;
    332 	unsigned long count = end - start + 1;
    333 	int byte_found = 0; /* whether a != 0xff byte has been found */
    334 	const unsigned char *pos;
    335 	unsigned long max_loop_count, i;
    336 
    337 	/* scan bits until we hit a byte boundary */
    338 	while ((bitpos & 0x7) != 0 && count > 0) {
    339 		if (!ext2fs_test_bit64(bitpos, bp->bitarray)) {
    340 			*out = bitpos + bitmap->start;
    341 			return 0;
    342 		}
    343 		bitpos++;
    344 		count--;
    345 	}
    346 
    347 	if (!count)
    348 		return ENOENT;
    349 
    350 	pos = ((unsigned char *)bp->bitarray) + (bitpos >> 3);
    351 	/* scan bytes until 8-byte (64-bit) aligned */
    352 	while (count >= 8 && (((uintptr_t)pos) & 0x07)) {
    353 		if (*pos != 0xff) {
    354 			byte_found = 1;
    355 			break;
    356 		}
    357 		pos++;
    358 		count -= 8;
    359 		bitpos += 8;
    360 	}
    361 
    362 	if (!byte_found) {
    363 		max_loop_count = count >> 6; /* 8-byte blocks */
    364 		i = max_loop_count;
    365 		while (i) {
    366 			if (*((const __u64 *)pos) != ((__u64)-1))
    367 				break;
    368 			pos += 8;
    369 			i--;
    370 		}
    371 		count -= 64 * (max_loop_count - i);
    372 		bitpos += 64 * (max_loop_count - i);
    373 
    374 		max_loop_count = count >> 3;
    375 		i = max_loop_count;
    376 		while (i) {
    377 			if (*pos != 0xff) {
    378 				byte_found = 1;
    379 				break;
    380 			}
    381 			pos++;
    382 			i--;
    383 		}
    384 		count -= 8 * (max_loop_count - i);
    385 		bitpos += 8 * (max_loop_count - i);
    386 	}
    387 
    388 	/* Here either count < 8 or byte_found == 1. */
    389 	while (count-- > 0) {
    390 		if (!ext2fs_test_bit64(bitpos, bp->bitarray)) {
    391 			*out = bitpos + bitmap->start;
    392 			return 0;
    393 		}
    394 		bitpos++;
    395 	}
    396 
    397 	return ENOENT;
    398 }
    399 
    400 /* Find the first one bit between start and end, inclusive. */
    401 static errcode_t ba_find_first_set(ext2fs_generic_bitmap bitmap,
    402 				    __u64 start, __u64 end, __u64 *out)
    403 {
    404 	ext2fs_ba_private bp = (ext2fs_ba_private)bitmap->private;
    405 	unsigned long bitpos = start - bitmap->start;
    406 	unsigned long count = end - start + 1;
    407 	int byte_found = 0; /* whether a != 0xff byte has been found */
    408 	const unsigned char *pos;
    409 	unsigned long max_loop_count, i;
    410 
    411 	/* scan bits until we hit a byte boundary */
    412 	while ((bitpos & 0x7) != 0 && count > 0) {
    413 		if (ext2fs_test_bit64(bitpos, bp->bitarray)) {
    414 			*out = bitpos + bitmap->start;
    415 			return 0;
    416 		}
    417 		bitpos++;
    418 		count--;
    419 	}
    420 
    421 	if (!count)
    422 		return ENOENT;
    423 
    424 	pos = ((unsigned char *)bp->bitarray) + (bitpos >> 3);
    425 	/* scan bytes until 8-byte (64-bit) aligned */
    426 	while (count >= 8 && (((uintptr_t)pos) & 0x07)) {
    427 		if (*pos != 0) {
    428 			byte_found = 1;
    429 			break;
    430 		}
    431 		pos++;
    432 		count -= 8;
    433 		bitpos += 8;
    434 	}
    435 
    436 	if (!byte_found) {
    437 		max_loop_count = count >> 6; /* 8-byte blocks */
    438 		i = max_loop_count;
    439 		while (i) {
    440 			if (*((const __u64 *)pos) != 0)
    441 				break;
    442 			pos += 8;
    443 			i--;
    444 		}
    445 		count -= 64 * (max_loop_count - i);
    446 		bitpos += 64 * (max_loop_count - i);
    447 
    448 		max_loop_count = count >> 3;
    449 		i = max_loop_count;
    450 		while (i) {
    451 			if (*pos != 0) {
    452 				byte_found = 1;
    453 				break;
    454 			}
    455 			pos++;
    456 			i--;
    457 		}
    458 		count -= 8 * (max_loop_count - i);
    459 		bitpos += 8 * (max_loop_count - i);
    460 	}
    461 
    462 	/* Here either count < 8 or byte_found == 1. */
    463 	while (count-- > 0) {
    464 		if (ext2fs_test_bit64(bitpos, bp->bitarray)) {
    465 			*out = bitpos + bitmap->start;
    466 			return 0;
    467 		}
    468 		bitpos++;
    469 	}
    470 
    471 	return ENOENT;
    472 }
    473 
    474 struct ext2_bitmap_ops ext2fs_blkmap64_bitarray = {
    475 	.type = EXT2FS_BMAP64_BITARRAY,
    476 	.new_bmap = ba_new_bmap,
    477 	.free_bmap = ba_free_bmap,
    478 	.copy_bmap = ba_copy_bmap,
    479 	.resize_bmap = ba_resize_bmap,
    480 	.mark_bmap = ba_mark_bmap,
    481 	.unmark_bmap = ba_unmark_bmap,
    482 	.test_bmap = ba_test_bmap,
    483 	.test_clear_bmap_extent = ba_test_clear_bmap_extent,
    484 	.mark_bmap_extent = ba_mark_bmap_extent,
    485 	.unmark_bmap_extent = ba_unmark_bmap_extent,
    486 	.set_bmap_range = ba_set_bmap_range,
    487 	.get_bmap_range = ba_get_bmap_range,
    488 	.clear_bmap = ba_clear_bmap,
    489 	.print_stats = ba_print_stats,
    490 	.find_first_zero = ba_find_first_zero,
    491 	.find_first_set = ba_find_first_set
    492 };
    493