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