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
44struct ext2_icount_el {
45	ext2_ino_t	ino;
46	__u32		count;
47};
48
49struct 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
72void 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
94static 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
124errout:
125	ext2fs_free_icount(icount);
126	return(retval);
127}
128
129struct 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
137static 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
163static 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
176errcode_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
220errout:
221	ext2fs_free_icount(icount);
222	return(retval);
223}
224
225errcode_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
285errout:
286	ext2fs_free_icount(icount);
287	return(retval);
288}
289
290errcode_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 */
301static 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 */
352static 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
396static 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
427static 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
457errcode_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
480errcode_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
502errcode_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
559errcode_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
602errcode_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
637ext2_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
647ext2_filsys	test_fs;
648ext2_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
656struct test_program {
657	int		cmd;
658	ext2_ino_t	ino;
659	__u16		arg;
660	__u16		expected;
661};
662
663struct 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
689struct 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 */
726static 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
751int 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
830int 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