Home | History | Annotate | Download | only in ext2fs
      1 /*
      2  * icount.c --- an efficient inode count abstraction
      3  *
      4  * Copyright (C) 1997 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 #if HAVE_UNISTD_H
     13 #include <unistd.h>
     14 #endif
     15 #include <string.h>
     16 #include <stdio.h>
     17 #include <sys/stat.h>
     18 #include <fcntl.h>
     19 #include <errno.h>
     20 
     21 #include "ext2_fs.h"
     22 #include "ext2fs.h"
     23 #include "tdb.h"
     24 
     25 /*
     26  * The data storage strategy used by icount relies on the observation
     27  * that most inode counts are either zero (for non-allocated inodes),
     28  * one (for most files), and only a few that are two or more
     29  * (directories and files that are linked to more than one directory).
     30  *
     31  * Also, e2fsck tends to load the icount data sequentially.
     32  *
     33  * So, we use an inode bitmap to indicate which inodes have a count of
     34  * one, and then use a sorted list to store the counts for inodes
     35  * which are greater than one.
     36  *
     37  * We also use an optional bitmap to indicate which inodes are already
     38  * in the sorted list, to speed up the use of this abstraction by
     39  * e2fsck's pass 2.  Pass 2 increments inode counts as it finds them,
     40  * so this extra bitmap avoids searching the sorted list to see if a
     41  * particular inode is on the sorted list already.
     42  */
     43 
     44 struct ext2_icount_el {
     45 	ext2_ino_t	ino;
     46 	__u16	count;
     47 };
     48 
     49 struct ext2_icount {
     50 	errcode_t		magic;
     51 	ext2fs_inode_bitmap	single;
     52 	ext2fs_inode_bitmap	multiple;
     53 	ext2_ino_t		count;
     54 	ext2_ino_t		size;
     55 	ext2_ino_t		num_inodes;
     56 	ext2_ino_t		cursor;
     57 	struct ext2_icount_el	*list;
     58 	struct ext2_icount_el	*last_lookup;
     59 	char			*tdb_fn;
     60 	TDB_CONTEXT		*tdb;
     61 };
     62 
     63 void ext2fs_free_icount(ext2_icount_t icount)
     64 {
     65 	if (!icount)
     66 		return;
     67 
     68 	icount->magic = 0;
     69 	if (icount->list)
     70 		ext2fs_free_mem(&icount->list);
     71 	if (icount->single)
     72 		ext2fs_free_inode_bitmap(icount->single);
     73 	if (icount->multiple)
     74 		ext2fs_free_inode_bitmap(icount->multiple);
     75 	if (icount->tdb)
     76 		tdb_close(icount->tdb);
     77 	if (icount->tdb_fn) {
     78 		unlink(icount->tdb_fn);
     79 		free(icount->tdb_fn);
     80 	}
     81 
     82 	ext2fs_free_mem(&icount);
     83 }
     84 
     85 static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret)
     86 {
     87 	ext2_icount_t	icount;
     88 	errcode_t	retval;
     89 
     90 	*ret = 0;
     91 
     92 	retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
     93 	if (retval)
     94 		return retval;
     95 	memset(icount, 0, sizeof(struct ext2_icount));
     96 
     97 	retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single);
     98 	if (retval)
     99 		goto errout;
    100 
    101 	if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
    102 		retval = ext2fs_allocate_inode_bitmap(fs, 0,
    103 						      &icount->multiple);
    104 		if (retval)
    105 			goto errout;
    106 	} else
    107 		icount->multiple = 0;
    108 
    109 	icount->magic = EXT2_ET_MAGIC_ICOUNT;
    110 	icount->num_inodes = fs->super->s_inodes_count;
    111 
    112 	*ret = icount;
    113 	return 0;
    114 
    115 errout:
    116 	ext2fs_free_icount(icount);
    117 	return(retval);
    118 }
    119 
    120 struct uuid {
    121 	__u32	time_low;
    122 	__u16	time_mid;
    123 	__u16	time_hi_and_version;
    124 	__u16	clock_seq;
    125 	__u8	node[6];
    126 };
    127 
    128 static void unpack_uuid(void *in, struct uuid *uu)
    129 {
    130 	__u8	*ptr = in;
    131 	__u32	tmp;
    132 
    133 	tmp = *ptr++;
    134 	tmp = (tmp << 8) | *ptr++;
    135 	tmp = (tmp << 8) | *ptr++;
    136 	tmp = (tmp << 8) | *ptr++;
    137 	uu->time_low = tmp;
    138 
    139 	tmp = *ptr++;
    140 	tmp = (tmp << 8) | *ptr++;
    141 	uu->time_mid = tmp;
    142 
    143 	tmp = *ptr++;
    144 	tmp = (tmp << 8) | *ptr++;
    145 	uu->time_hi_and_version = tmp;
    146 
    147 	tmp = *ptr++;
    148 	tmp = (tmp << 8) | *ptr++;
    149 	uu->clock_seq = tmp;
    150 
    151 	memcpy(uu->node, ptr, 6);
    152 }
    153 
    154 static void uuid_unparse(void *uu, char *out)
    155 {
    156 	struct uuid uuid;
    157 
    158 	unpack_uuid(uu, &uuid);
    159 	sprintf(out,
    160 		"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
    161 		uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
    162 		uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
    163 		uuid.node[0], uuid.node[1], uuid.node[2],
    164 		uuid.node[3], uuid.node[4], uuid.node[5]);
    165 }
    166 
    167 errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
    168 				   int flags, ext2_icount_t *ret)
    169 {
    170 	ext2_icount_t	icount;
    171 	errcode_t	retval;
    172 	char 		*fn, uuid[40];
    173 	int		fd;
    174 
    175 	retval = alloc_icount(fs, flags,  &icount);
    176 	if (retval)
    177 		return retval;
    178 
    179 	retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn);
    180 	if (retval)
    181 		goto errout;
    182 	uuid_unparse(fs->super->s_uuid, uuid);
    183 	sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid);
    184 	fd = mkstemp(fn);
    185 
    186 	icount->tdb_fn = fn;
    187 	icount->tdb = tdb_open(fn, 0, TDB_CLEAR_IF_FIRST,
    188 			       O_RDWR | O_CREAT | O_TRUNC, 0600);
    189 	if (icount->tdb) {
    190 		close(fd);
    191 		*ret = icount;
    192 		return 0;
    193 	}
    194 
    195 	retval = errno;
    196 	close(fd);
    197 
    198 errout:
    199 	ext2fs_free_icount(icount);
    200 	return(retval);
    201 }
    202 
    203 errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
    204 				ext2_icount_t hint, ext2_icount_t *ret)
    205 {
    206 	ext2_icount_t	icount;
    207 	errcode_t	retval;
    208 	size_t		bytes;
    209 	ext2_ino_t	i;
    210 
    211 	if (hint) {
    212 		EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
    213 		if (hint->size > size)
    214 			size = (size_t) hint->size;
    215 	}
    216 
    217 	retval = alloc_icount(fs, flags, &icount);
    218 	if (retval)
    219 		return retval;
    220 
    221 	if (size) {
    222 		icount->size = size;
    223 	} else {
    224 		/*
    225 		 * Figure out how many special case inode counts we will
    226 		 * have.  We know we will need one for each directory;
    227 		 * we also need to reserve some extra room for file links
    228 		 */
    229 		retval = ext2fs_get_num_dirs(fs, &icount->size);
    230 		if (retval)
    231 			goto errout;
    232 		icount->size += fs->super->s_inodes_count / 50;
    233 	}
    234 
    235 	bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
    236 #if 0
    237 	printf("Icount allocated %u entries, %d bytes.\n",
    238 	       icount->size, bytes);
    239 #endif
    240 	retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el),
    241 			 &icount->list);
    242 	if (retval)
    243 		goto errout;
    244 	memset(icount->list, 0, bytes);
    245 
    246 	icount->count = 0;
    247 	icount->cursor = 0;
    248 
    249 	/*
    250 	 * Populate the sorted list with those entries which were
    251 	 * found in the hint icount (since those are ones which will
    252 	 * likely need to be in the sorted list this time around).
    253 	 */
    254 	if (hint) {
    255 		for (i=0; i < hint->count; i++)
    256 			icount->list[i].ino = hint->list[i].ino;
    257 		icount->count = hint->count;
    258 	}
    259 
    260 	*ret = icount;
    261 	return 0;
    262 
    263 errout:
    264 	ext2fs_free_icount(icount);
    265 	return(retval);
    266 }
    267 
    268 errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
    269 			       unsigned int size,
    270 			       ext2_icount_t *ret)
    271 {
    272 	return ext2fs_create_icount2(fs, flags, size, 0, ret);
    273 }
    274 
    275 /*
    276  * insert_icount_el() --- Insert a new entry into the sorted list at a
    277  * 	specified position.
    278  */
    279 static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
    280 					    ext2_ino_t ino, int pos)
    281 {
    282 	struct ext2_icount_el 	*el;
    283 	errcode_t		retval;
    284 	ext2_ino_t		new_size = 0;
    285 	int			num;
    286 
    287 	if (icount->last_lookup && icount->last_lookup->ino == ino)
    288 		return icount->last_lookup;
    289 
    290 	if (icount->count >= icount->size) {
    291 		if (icount->count) {
    292 			new_size = icount->list[(unsigned)icount->count-1].ino;
    293 			new_size = (ext2_ino_t) (icount->count *
    294 				((float) icount->num_inodes / new_size));
    295 		}
    296 		if (new_size < (icount->size + 100))
    297 			new_size = icount->size + 100;
    298 #if 0
    299 		printf("Reallocating icount %u entries...\n", new_size);
    300 #endif
    301 		retval = ext2fs_resize_mem((size_t) icount->size *
    302 					   sizeof(struct ext2_icount_el),
    303 					   (size_t) new_size *
    304 					   sizeof(struct ext2_icount_el),
    305 					   &icount->list);
    306 		if (retval)
    307 			return 0;
    308 		icount->size = new_size;
    309 	}
    310 	num = (int) icount->count - pos;
    311 	if (num < 0)
    312 		return 0;	/* should never happen */
    313 	if (num) {
    314 		memmove(&icount->list[pos+1], &icount->list[pos],
    315 			sizeof(struct ext2_icount_el) * num);
    316 	}
    317 	icount->count++;
    318 	el = &icount->list[pos];
    319 	el->count = 0;
    320 	el->ino = ino;
    321 	icount->last_lookup = el;
    322 	return el;
    323 }
    324 
    325 /*
    326  * get_icount_el() --- given an inode number, try to find icount
    327  * 	information in the sorted list.  If the create flag is set,
    328  * 	and we can't find an entry, create one in the sorted list.
    329  */
    330 static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
    331 					    ext2_ino_t ino, int create)
    332 {
    333 	float	range;
    334 	int	low, high, mid;
    335 	ext2_ino_t	lowval, highval;
    336 
    337 	if (!icount || !icount->list)
    338 		return 0;
    339 
    340 	if (create && ((icount->count == 0) ||
    341 		       (ino > icount->list[(unsigned)icount->count-1].ino))) {
    342 		return insert_icount_el(icount, ino, (unsigned) icount->count);
    343 	}
    344 	if (icount->count == 0)
    345 		return 0;
    346 
    347 	if (icount->cursor >= icount->count)
    348 		icount->cursor = 0;
    349 	if (ino == icount->list[icount->cursor].ino)
    350 		return &icount->list[icount->cursor++];
    351 #if 0
    352 	printf("Non-cursor get_icount_el: %u\n", ino);
    353 #endif
    354 	low = 0;
    355 	high = (int) icount->count-1;
    356 	while (low <= high) {
    357 #if 0
    358 		mid = (low+high)/2;
    359 #else
    360 		if (low == high)
    361 			mid = low;
    362 		else {
    363 			/* Interpolate for efficiency */
    364 			lowval = icount->list[low].ino;
    365 			highval = icount->list[high].ino;
    366 
    367 			if (ino < lowval)
    368 				range = 0;
    369 			else if (ino > highval)
    370 				range = 1;
    371 			else {
    372 				range = ((float) (ino - lowval)) /
    373 					(highval - lowval);
    374 				if (range > 0.9)
    375 					range = 0.9;
    376 				if (range < 0.1)
    377 					range = 0.1;
    378 			}
    379 			mid = low + ((int) (range * (high-low)));
    380 		}
    381 #endif
    382 		if (ino == icount->list[mid].ino) {
    383 			icount->cursor = mid+1;
    384 			return &icount->list[mid];
    385 		}
    386 		if (ino < icount->list[mid].ino)
    387 			high = mid-1;
    388 		else
    389 			low = mid+1;
    390 	}
    391 	/*
    392 	 * If we need to create a new entry, it should be right at
    393 	 * low (where high will be left at low-1).
    394 	 */
    395 	if (create)
    396 		return insert_icount_el(icount, ino, low);
    397 	return 0;
    398 }
    399 
    400 static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
    401 				 __u16 count)
    402 {
    403 	struct ext2_icount_el 	*el;
    404 	TDB_DATA key, data;
    405 
    406 	if (icount->tdb) {
    407 		key.dptr = (unsigned char *) &ino;
    408 		key.dsize = sizeof(ext2_ino_t);
    409 		data.dptr = (unsigned char *) &count;
    410 		data.dsize = sizeof(__u16);
    411 		if (count) {
    412 			if (tdb_store(icount->tdb, key, data, TDB_REPLACE))
    413 				return tdb_error(icount->tdb) +
    414 					EXT2_ET_TDB_SUCCESS;
    415 		} else {
    416 			if (tdb_delete(icount->tdb, key))
    417 				return tdb_error(icount->tdb) +
    418 					EXT2_ET_TDB_SUCCESS;
    419 		}
    420 		return 0;
    421 	}
    422 
    423 	el = get_icount_el(icount, ino, 1);
    424 	if (!el)
    425 		return EXT2_ET_NO_MEMORY;
    426 
    427 	el->count = count;
    428 	return 0;
    429 }
    430 
    431 static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino,
    432 				 __u16 *count)
    433 {
    434 	struct ext2_icount_el 	*el;
    435 	TDB_DATA key, data;
    436 
    437 	if (icount->tdb) {
    438 		key.dptr = (unsigned char *) &ino;
    439 		key.dsize = sizeof(ext2_ino_t);
    440 
    441 		data = tdb_fetch(icount->tdb, key);
    442 		if (data.dptr == NULL) {
    443 			*count = 0;
    444 			return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS;
    445 		}
    446 
    447 		*count = *((__u16 *) data.dptr);
    448 		free(data.dptr);
    449 		return 0;
    450 	}
    451 	el = get_icount_el(icount, ino, 0);
    452 	if (!el) {
    453 		*count = 0;
    454 		return ENOENT;
    455 	}
    456 
    457 	*count = el->count;
    458 	return 0;
    459 }
    460 
    461 errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
    462 {
    463 	errcode_t	ret = 0;
    464 	unsigned int	i;
    465 	const char *bad = "bad icount";
    466 
    467 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
    468 
    469 	if (icount->count > icount->size) {
    470 		fprintf(out, "%s: count > size\n", bad);
    471 		return EXT2_ET_INVALID_ARGUMENT;
    472 	}
    473 	for (i=1; i < icount->count; i++) {
    474 		if (icount->list[i-1].ino >= icount->list[i].ino) {
    475 			fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
    476 				bad, i-1, icount->list[i-1].ino,
    477 				i, icount->list[i].ino);
    478 			ret = EXT2_ET_INVALID_ARGUMENT;
    479 		}
    480 	}
    481 	return ret;
    482 }
    483 
    484 errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
    485 {
    486 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
    487 
    488 	if (!ino || (ino > icount->num_inodes))
    489 		return EXT2_ET_INVALID_ARGUMENT;
    490 
    491 	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
    492 		*ret = 1;
    493 		return 0;
    494 	}
    495 	if (icount->multiple &&
    496 	    !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
    497 		*ret = 0;
    498 		return 0;
    499 	}
    500 	get_inode_count(icount, ino, ret);
    501 	return 0;
    502 }
    503 
    504 errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
    505 				  __u16 *ret)
    506 {
    507 	__u16			curr_value;
    508 
    509 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
    510 
    511 	if (!ino || (ino > icount->num_inodes))
    512 		return EXT2_ET_INVALID_ARGUMENT;
    513 
    514 	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
    515 		/*
    516 		 * If the existing count is 1, then we know there is
    517 		 * no entry in the list.
    518 		 */
    519 		if (set_inode_count(icount, ino, 2))
    520 			return EXT2_ET_NO_MEMORY;
    521 		curr_value = 2;
    522 		ext2fs_unmark_inode_bitmap(icount->single, ino);
    523 	} else if (icount->multiple) {
    524 		/*
    525 		 * The count is either zero or greater than 1; if the
    526 		 * inode is set in icount->multiple, then there should
    527 		 * be an entry in the list, so we need to fix it.
    528 		 */
    529 		if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
    530 			get_inode_count(icount, ino, &curr_value);
    531 			curr_value++;
    532 			if (set_inode_count(icount, ino, curr_value))
    533 				return EXT2_ET_NO_MEMORY;
    534 		} else {
    535 			/*
    536 			 * The count was zero; mark the single bitmap
    537 			 * and return.
    538 			 */
    539 			ext2fs_mark_inode_bitmap(icount->single, ino);
    540 			if (ret)
    541 				*ret = 1;
    542 			return 0;
    543 		}
    544 	} else {
    545 		/*
    546 		 * The count is either zero or greater than 1; try to
    547 		 * find an entry in the list to determine which.
    548 		 */
    549 		get_inode_count(icount, ino, &curr_value);
    550 		curr_value++;
    551 		if (set_inode_count(icount, ino, curr_value))
    552 			return EXT2_ET_NO_MEMORY;
    553 	}
    554 	if (icount->multiple)
    555 		ext2fs_mark_inode_bitmap(icount->multiple, ino);
    556 	if (ret)
    557 		*ret = curr_value;
    558 	return 0;
    559 }
    560 
    561 errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
    562 				  __u16 *ret)
    563 {
    564 	__u16			curr_value;
    565 
    566 	if (!ino || (ino > icount->num_inodes))
    567 		return EXT2_ET_INVALID_ARGUMENT;
    568 
    569 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
    570 
    571 	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
    572 		ext2fs_unmark_inode_bitmap(icount->single, ino);
    573 		if (icount->multiple)
    574 			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
    575 		else {
    576 			set_inode_count(icount, ino, 0);
    577 		}
    578 		if (ret)
    579 			*ret = 0;
    580 		return 0;
    581 	}
    582 
    583 	if (icount->multiple &&
    584 	    !ext2fs_test_inode_bitmap(icount->multiple, ino))
    585 		return EXT2_ET_INVALID_ARGUMENT;
    586 
    587 	get_inode_count(icount, ino, &curr_value);
    588 	if (!curr_value)
    589 		return EXT2_ET_INVALID_ARGUMENT;
    590 	curr_value--;
    591 	if (set_inode_count(icount, ino, curr_value))
    592 		return EXT2_ET_NO_MEMORY;
    593 
    594 	if (curr_value == 1)
    595 		ext2fs_mark_inode_bitmap(icount->single, ino);
    596 	if ((curr_value == 0) && icount->multiple)
    597 		ext2fs_unmark_inode_bitmap(icount->multiple, ino);
    598 
    599 	if (ret)
    600 		*ret = curr_value;
    601 	return 0;
    602 }
    603 
    604 errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
    605 			      __u16 count)
    606 {
    607 	if (!ino || (ino > icount->num_inodes))
    608 		return EXT2_ET_INVALID_ARGUMENT;
    609 
    610 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
    611 
    612 	if (count == 1) {
    613 		ext2fs_mark_inode_bitmap(icount->single, ino);
    614 		if (icount->multiple)
    615 			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
    616 		return 0;
    617 	}
    618 	if (count == 0) {
    619 		ext2fs_unmark_inode_bitmap(icount->single, ino);
    620 		if (icount->multiple) {
    621 			/*
    622 			 * If the icount->multiple bitmap is enabled,
    623 			 * we can just clear both bitmaps and we're done
    624 			 */
    625 			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
    626 		} else
    627 			set_inode_count(icount, ino, 0);
    628 		return 0;
    629 	}
    630 
    631 	if (set_inode_count(icount, ino, count))
    632 		return EXT2_ET_NO_MEMORY;
    633 	ext2fs_unmark_inode_bitmap(icount->single, ino);
    634 	if (icount->multiple)
    635 		ext2fs_mark_inode_bitmap(icount->multiple, ino);
    636 	return 0;
    637 }
    638 
    639 ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
    640 {
    641 	if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
    642 		return 0;
    643 
    644 	return icount->size;
    645 }
    646 
    647 #ifdef DEBUG
    648 
    649 ext2_filsys	test_fs;
    650 ext2_icount_t	icount;
    651 
    652 #define EXIT		0x00
    653 #define FETCH		0x01
    654 #define STORE		0x02
    655 #define INCREMENT	0x03
    656 #define DECREMENT	0x04
    657 
    658 struct test_program {
    659 	int		cmd;
    660 	ext2_ino_t	ino;
    661 	__u16		arg;
    662 	__u16		expected;
    663 };
    664 
    665 struct test_program prog[] = {
    666 	{ STORE, 42, 42, 42 },
    667 	{ STORE, 1,  1, 1 },
    668 	{ STORE, 2,  2, 2 },
    669 	{ STORE, 3,  3, 3 },
    670 	{ STORE, 10, 1, 1 },
    671 	{ STORE, 42, 0, 0 },
    672 	{ INCREMENT, 5, 0, 1 },
    673 	{ INCREMENT, 5, 0, 2 },
    674 	{ INCREMENT, 5, 0, 3 },
    675 	{ INCREMENT, 5, 0, 4 },
    676 	{ DECREMENT, 5, 0, 3 },
    677 	{ DECREMENT, 5, 0, 2 },
    678 	{ DECREMENT, 5, 0, 1 },
    679 	{ DECREMENT, 5, 0, 0 },
    680 	{ FETCH, 10, 0, 1 },
    681 	{ FETCH, 1, 0, 1 },
    682 	{ FETCH, 2, 0, 2 },
    683 	{ FETCH, 3, 0, 3 },
    684 	{ INCREMENT, 1, 0, 2 },
    685 	{ DECREMENT, 2, 0, 1 },
    686 	{ DECREMENT, 2, 0, 0 },
    687 	{ FETCH, 12, 0, 0 },
    688 	{ EXIT, 0, 0, 0 }
    689 };
    690 
    691 struct test_program extended[] = {
    692 	{ STORE, 1,  1, 1 },
    693 	{ STORE, 2,  2, 2 },
    694 	{ STORE, 3,  3, 3 },
    695 	{ STORE, 4,  4, 4 },
    696 	{ STORE, 5,  5, 5 },
    697 	{ STORE, 6,  1, 1 },
    698 	{ STORE, 7,  2, 2 },
    699 	{ STORE, 8,  3, 3 },
    700 	{ STORE, 9,  4, 4 },
    701 	{ STORE, 10, 5, 5 },
    702 	{ STORE, 11, 1, 1 },
    703 	{ STORE, 12, 2, 2 },
    704 	{ STORE, 13, 3, 3 },
    705 	{ STORE, 14, 4, 4 },
    706 	{ STORE, 15, 5, 5 },
    707 	{ STORE, 16, 1, 1 },
    708 	{ STORE, 17, 2, 2 },
    709 	{ STORE, 18, 3, 3 },
    710 	{ STORE, 19, 4, 4 },
    711 	{ STORE, 20, 5, 5 },
    712 	{ STORE, 21, 1, 1 },
    713 	{ STORE, 22, 2, 2 },
    714 	{ STORE, 23, 3, 3 },
    715 	{ STORE, 24, 4, 4 },
    716 	{ STORE, 25, 5, 5 },
    717 	{ STORE, 26, 1, 1 },
    718 	{ STORE, 27, 2, 2 },
    719 	{ STORE, 28, 3, 3 },
    720 	{ STORE, 29, 4, 4 },
    721 	{ STORE, 30, 5, 5 },
    722 	{ EXIT, 0, 0, 0 }
    723 };
    724 
    725 /*
    726  * Setup the variables for doing the inode scan test.
    727  */
    728 static void setup(void)
    729 {
    730 	errcode_t	retval;
    731 	struct ext2_super_block param;
    732 
    733 	initialize_ext2_error_table();
    734 
    735 	memset(&param, 0, sizeof(param));
    736 	param.s_blocks_count = 12000;
    737 
    738 	retval = ext2fs_initialize("test fs", 0, &param,
    739 				   test_io_manager, &test_fs);
    740 	if (retval) {
    741 		com_err("setup", retval,
    742 			"while initializing filesystem");
    743 		exit(1);
    744 	}
    745 	retval = ext2fs_allocate_tables(test_fs);
    746 	if (retval) {
    747 		com_err("setup", retval,
    748 			"while allocating tables for test filesystem");
    749 		exit(1);
    750 	}
    751 }
    752 
    753 int run_test(int flags, int size, char *dir, struct test_program *prog)
    754 {
    755 	errcode_t	retval;
    756 	ext2_icount_t	icount;
    757 	struct test_program *pc;
    758 	__u16		result;
    759 	int		problem = 0;
    760 
    761 	if (dir) {
    762 		retval = ext2fs_create_icount_tdb(test_fs, dir,
    763 						  flags, &icount);
    764 		if (retval) {
    765 			com_err("run_test", retval,
    766 				"while creating icount using tdb");
    767 			exit(1);
    768 		}
    769 	} else {
    770 		retval = ext2fs_create_icount2(test_fs, flags, size, 0,
    771 					       &icount);
    772 		if (retval) {
    773 			com_err("run_test", retval, "while creating icount");
    774 			exit(1);
    775 		}
    776 	}
    777 	for (pc = prog; pc->cmd != EXIT; pc++) {
    778 		switch (pc->cmd) {
    779 		case FETCH:
    780 			printf("icount_fetch(%u) = ", pc->ino);
    781 			break;
    782 		case STORE:
    783 			retval = ext2fs_icount_store(icount, pc->ino, pc->arg);
    784 			if (retval) {
    785 				com_err("run_test", retval,
    786 					"while calling icount_store");
    787 				exit(1);
    788 			}
    789 			printf("icount_store(%u, %u) = ", pc->ino, pc->arg);
    790 			break;
    791 		case INCREMENT:
    792 			retval = ext2fs_icount_increment(icount, pc->ino, 0);
    793 			if (retval) {
    794 				com_err("run_test", retval,
    795 					"while calling icount_increment");
    796 				exit(1);
    797 			}
    798 			printf("icount_increment(%u) = ", pc->ino);
    799 			break;
    800 		case DECREMENT:
    801 			retval = ext2fs_icount_decrement(icount, pc->ino, 0);
    802 			if (retval) {
    803 				com_err("run_test", retval,
    804 					"while calling icount_decrement");
    805 				exit(1);
    806 			}
    807 			printf("icount_decrement(%u) = ", pc->ino);
    808 			break;
    809 		}
    810 		retval = ext2fs_icount_fetch(icount, pc->ino, &result);
    811 		if (retval) {
    812 			com_err("run_test", retval,
    813 				"while calling icount_fetch");
    814 			exit(1);
    815 		}
    816 		printf("%u (%s)\n", result, (result == pc->expected) ?
    817 		       "OK" : "NOT OK");
    818 		if (result != pc->expected)
    819 			problem++;
    820 	}
    821 	printf("icount size is %u\n", ext2fs_get_icount_size(icount));
    822 	retval = ext2fs_icount_validate(icount, stdout);
    823 	if (retval) {
    824 		com_err("run_test", retval, "while calling icount_validate");
    825 		exit(1);
    826 	}
    827 	ext2fs_free_icount(icount);
    828 	return problem;
    829 }
    830 
    831 
    832 int main(int argc, char **argv)
    833 {
    834 	int failed = 0;
    835 
    836 	setup();
    837 	printf("Standard icount run:\n");
    838 	failed += run_test(0, 0, 0, prog);
    839 	printf("\nMultiple bitmap test:\n");
    840 	failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog);
    841 	printf("\nResizing icount:\n");
    842 	failed += run_test(0, 3, 0, extended);
    843 	printf("\nStandard icount run with tdb:\n");
    844 	failed += run_test(0, 0, ".", prog);
    845 	printf("\nMultiple bitmap test with tdb:\n");
    846 	failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog);
    847 	if (failed)
    848 		printf("FAILED!\n");
    849 	return failed;
    850 }
    851 #endif
    852