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