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 
ba_alloc_private_data(ext2fs_generic_bitmap bitmap)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 
ba_new_bmap(ext2_filsys fs EXT2FS_ATTR ((unused)),ext2fs_generic_bitmap bitmap)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 
ba_free_bmap(ext2fs_generic_bitmap bitmap)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 
ba_copy_bmap(ext2fs_generic_bitmap src,ext2fs_generic_bitmap dest)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 
ba_resize_bmap(ext2fs_generic_bitmap bmap,__u64 new_end,__u64 new_real_end)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 
ba_mark_bmap(ext2fs_generic_bitmap bitmap,__u64 arg)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 
ba_unmark_bmap(ext2fs_generic_bitmap bitmap,__u64 arg)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 
ba_test_bmap(ext2fs_generic_bitmap bitmap,__u64 arg)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 
ba_mark_bmap_extent(ext2fs_generic_bitmap bitmap,__u64 arg,unsigned int num)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 
ba_unmark_bmap_extent(ext2fs_generic_bitmap bitmap,__u64 arg,unsigned int num)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 
ba_test_clear_bmap_extent(ext2fs_generic_bitmap bitmap,__u64 start,unsigned int len)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 
ba_set_bmap_range(ext2fs_generic_bitmap bitmap,__u64 start,size_t num,void * in)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 
ba_get_bmap_range(ext2fs_generic_bitmap bitmap,__u64 start,size_t num,void * out)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 
ba_clear_bmap(ext2fs_generic_bitmap bitmap)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
ba_print_stats(ext2fs_generic_bitmap bitmap)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
ba_print_stats(ext2fs_generic_bitmap bitmap EXT2FS_ATTR ((unused)))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. */
ba_find_first_zero(ext2fs_generic_bitmap bitmap,__u64 start,__u64 end,__u64 * out)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. */
ba_find_first_set(ext2fs_generic_bitmap bitmap,__u64 start,__u64 end,__u64 * out)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