Whamcloud - gitweb
Fix gcc -Wall warnings, especially on 64-bit systems
[tools/e2fsprogs.git] / lib / ext2fs / icount.c
index 5bffc18..de0b614 100644 (file)
@@ -9,19 +9,18 @@
  * %End-Header%
  */
 
-#include <et/com_err.h>
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
-#ifdef HAVE_ERRNO_H
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <errno.h>
-#endif
 
-#include <linux/ext2_fs.h>
+#include "ext2_fs.h"
 #include "ext2fs.h"
+#include "tdb.h"
 
 /*
  * The data storage strategy used by icount relies on the observation
  */
 
 struct ext2_icount_el {
-       ino_t   ino;
+       ext2_ino_t      ino;
        __u16   count;
 };
 
 struct ext2_icount {
-       int                     magic;
+       errcode_t               magic;
        ext2fs_inode_bitmap     single;
        ext2fs_inode_bitmap     multiple;
-       ino_t                   count;
-       ino_t                   size;
-       ino_t                   num_inodes;
-       int                     cursor;
+       ext2_ino_t              count;
+       ext2_ino_t              size;
+       ext2_ino_t              num_inodes;
+       ext2_ino_t              cursor;
        struct ext2_icount_el   *list;
+       struct ext2_icount_el   *last_lookup;
+       char                    *tdb_fn;
+       TDB_CONTEXT             *tdb;
 };
 
 void ext2fs_free_icount(ext2_icount_t icount)
@@ -65,46 +67,157 @@ void ext2fs_free_icount(ext2_icount_t icount)
 
        icount->magic = 0;
        if (icount->list)
-               free(icount->list);
+               ext2fs_free_mem(&icount->list);
        if (icount->single)
                ext2fs_free_inode_bitmap(icount->single);
        if (icount->multiple)
                ext2fs_free_inode_bitmap(icount->multiple);
-       free(icount);
+       if (icount->tdb)
+               tdb_close(icount->tdb);
+       if (icount->tdb_fn) {
+               unlink(icount->tdb_fn);
+               free(icount->tdb_fn);
+       }
+
+       ext2fs_free_mem(&icount);
 }
 
-errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
-                               ext2_icount_t hint, ext2_icount_t *ret)
+static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret)
 {
        ext2_icount_t   icount;
        errcode_t       retval;
-       size_t          bytes;
-       int             i;
 
-       if (hint) {
-               EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
-               if (hint->size > size)
-                       size = hint->size;
-       }
-       
-       icount = malloc(sizeof(struct ext2_icount));
-       if (!icount)
-               return ENOMEM;
+       *ret = 0;
+
+       retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
+       if (retval)
+               return retval;
        memset(icount, 0, sizeof(struct ext2_icount));
 
-       retval = ext2fs_allocate_inode_bitmap(fs, 0, 
-                                             &icount->single);
+       retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single);
        if (retval)
                goto errout;
 
        if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
-               retval = ext2fs_allocate_inode_bitmap(fs, 0, 
+               retval = ext2fs_allocate_inode_bitmap(fs, 0,
                                                      &icount->multiple);
                if (retval)
                        goto errout;
        } else
                icount->multiple = 0;
 
+       icount->magic = EXT2_ET_MAGIC_ICOUNT;
+       icount->num_inodes = fs->super->s_inodes_count;
+
+       *ret = icount;
+       return 0;
+
+errout:
+       ext2fs_free_icount(icount);
+       return(retval);
+}
+
+struct uuid {
+       __u32   time_low;
+       __u16   time_mid;
+       __u16   time_hi_and_version;
+       __u16   clock_seq;
+       __u8    node[6];
+};
+
+static void unpack_uuid(void *in, struct uuid *uu)
+{
+       __u8    *ptr = in;
+       __u32   tmp;
+
+       tmp = *ptr++;
+       tmp = (tmp << 8) | *ptr++;
+       tmp = (tmp << 8) | *ptr++;
+       tmp = (tmp << 8) | *ptr++;
+       uu->time_low = tmp;
+
+       tmp = *ptr++;
+       tmp = (tmp << 8) | *ptr++;
+       uu->time_mid = tmp;
+
+       tmp = *ptr++;
+       tmp = (tmp << 8) | *ptr++;
+       uu->time_hi_and_version = tmp;
+
+       tmp = *ptr++;
+       tmp = (tmp << 8) | *ptr++;
+       uu->clock_seq = tmp;
+
+       memcpy(uu->node, ptr, 6);
+}
+
+static void uuid_unparse(void *uu, char *out)
+{
+       struct uuid uuid;
+
+       unpack_uuid(uu, &uuid);
+       sprintf(out,
+               "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+               uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+               uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+               uuid.node[0], uuid.node[1], uuid.node[2],
+               uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
+                                  int flags, ext2_icount_t *ret)
+{
+       ext2_icount_t   icount;
+       errcode_t       retval;
+       char            *fn, uuid[40];
+       int             fd;
+
+       retval = alloc_icount(fs, flags,  &icount);
+       if (retval)
+               return retval;
+
+       retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn);
+       if (retval)
+               goto errout;
+       uuid_unparse(fs->super->s_uuid, uuid);
+       sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid);
+       fd = mkstemp(fn);
+
+       icount->tdb_fn = fn;
+       icount->tdb = tdb_open(fn, 0, TDB_CLEAR_IF_FIRST,
+                              O_RDWR | O_CREAT | O_TRUNC, 0600);
+       if (icount->tdb) {
+               close(fd);
+               *ret = icount;
+               return 0;
+       }
+
+       retval = errno;
+       close(fd);
+
+errout:
+       ext2fs_free_icount(icount);
+       return(retval);
+}
+
+errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
+                               ext2_icount_t hint, ext2_icount_t *ret)
+{
+       ext2_icount_t   icount;
+       errcode_t       retval;
+       size_t          bytes;
+       ext2_ino_t      i;
+
+       if (hint) {
+               EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
+               if (hint->size > size)
+                       size = (size_t) hint->size;
+       }
+
+       retval = alloc_icount(fs, flags, &icount);
+       if (retval)
+               return retval;
+
        if (size) {
                icount->size = size;
        } else {
@@ -118,21 +231,19 @@ errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
                        goto errout;
                icount->size += fs->super->s_inodes_count / 50;
        }
-       
-       bytes = icount->size * sizeof(struct ext2_icount_el);
+
+       bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
 #if 0
-       printf("Icount allocated %d entries, %d bytes.\n",
+       printf("Icount allocated %u entries, %d bytes.\n",
               icount->size, bytes);
 #endif
-       icount->list = malloc(bytes);
-       if (!icount->list)
+       retval = ext2fs_get_mem(bytes, &icount->list);
+       if (retval)
                goto errout;
        memset(icount->list, 0, bytes);
 
-       icount->magic = EXT2_ET_MAGIC_ICOUNT;
        icount->count = 0;
        icount->cursor = 0;
-       icount->num_inodes = fs->super->s_inodes_count;
 
        /*
         * Populate the sorted list with those entries which were
@@ -153,7 +264,8 @@ errout:
        return(retval);
 }
 
-errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, int size,
+errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
+                              unsigned int size,
                               ext2_icount_t *ret)
 {
        return ext2fs_create_icount2(fs, flags, size, 0, ret);
@@ -164,31 +276,37 @@ errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, int size,
  *     specified position.
  */
 static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
-                                           ino_t ino, int pos)
+                                           ext2_ino_t ino, int pos)
 {
-       struct ext2_icount_el *el, *new_list;
-       ino_t                   new_size = 0;
+       struct ext2_icount_el   *el;
+       errcode_t               retval;
+       ext2_ino_t              new_size = 0;
        int                     num;
 
+       if (icount->last_lookup && icount->last_lookup->ino == ino)
+               return icount->last_lookup;
+
        if (icount->count >= icount->size) {
                if (icount->count) {
-                       new_size = icount->list[icount->count-1].ino;
-                       new_size = icount->count * 
-                               ((float) new_size / icount->num_inodes);
+                       new_size = icount->list[(unsigned)icount->count-1].ino;
+                       new_size = (ext2_ino_t) (icount->count *
+                               ((float) icount->num_inodes / new_size));
                }
                if (new_size < (icount->size + 100))
                        new_size = icount->size + 100;
 #if 0
-               printf("Reallocating icount %d entries...\n", new_size);
-#endif 
-               new_list = realloc(icount->list,
-                       new_size * sizeof(struct ext2_icount_el));
-               if (!new_list)
+               printf("Reallocating icount %u entries...\n", new_size);
+#endif
+               retval = ext2fs_resize_mem((size_t) icount->size *
+                                          sizeof(struct ext2_icount_el),
+                                          (size_t) new_size *
+                                          sizeof(struct ext2_icount_el),
+                                          &icount->list);
+               if (retval)
                        return 0;
                icount->size = new_size;
-               icount->list = new_list;
        }
-       num = icount->count - pos;
+       num = (int) icount->count - pos;
        if (num < 0)
                return 0;       /* should never happen */
        if (num) {
@@ -199,6 +317,7 @@ static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
        el = &icount->list[pos];
        el->count = 0;
        el->ino = ino;
+       icount->last_lookup = el;
        return el;
 }
 
@@ -208,22 +327,22 @@ static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
  *     and we can't find an entry, create one in the sorted list.
  */
 static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
-                                           ino_t ino, int create)
+                                           ext2_ino_t ino, int create)
 {
        float   range;
        int     low, high, mid;
-       ino_t   lowval, highval;
+       ext2_ino_t      lowval, highval;
 
        if (!icount || !icount->list)
                return 0;
 
        if (create && ((icount->count == 0) ||
-                      (ino > icount->list[icount->count-1].ino))) {
-               return insert_icount_el(icount, ino, icount->count);
+                      (ino > icount->list[(unsigned)icount->count-1].ino))) {
+               return insert_icount_el(icount, ino, (unsigned) icount->count);
        }
        if (icount->count == 0)
                return 0;
-       
+
        if (icount->cursor >= icount->count)
                icount->cursor = 0;
        if (ino == icount->list[icount->cursor].ino)
@@ -232,7 +351,7 @@ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
        printf("Non-cursor get_icount_el: %u\n", ino);
 #endif
        low = 0;
-       high = icount->count-1;
+       high = (int) icount->count-1;
        while (low <= high) {
 #if 0
                mid = (low+high)/2;
@@ -248,9 +367,14 @@ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
                                range = 0;
                        else if (ino > highval)
                                range = 1;
-                       else 
+                       else {
                                range = ((float) (ino - lowval)) /
                                        (highval - lowval);
+                               if (range > 0.9)
+                                       range = 0.9;
+                               if (range < 0.1)
+                                       range = 0.1;
+                       }
                        mid = low + ((int) (range * (high-low)));
                }
 #endif
@@ -272,37 +396,96 @@ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
        return 0;
 }
 
+static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
+                                __u16 count)
+{
+       struct ext2_icount_el   *el;
+       TDB_DATA key, data;
+
+       if (icount->tdb) {
+               key.dptr = (unsigned char *) &ino;
+               key.dsize = sizeof(ext2_ino_t);
+               data.dptr = (unsigned char *) &count;
+               data.dsize = sizeof(__u16);
+               if (count) {
+                       if (tdb_store(icount->tdb, key, data, TDB_REPLACE))
+                               return tdb_error(icount->tdb) +
+                                       EXT2_ET_TDB_SUCCESS;
+               } else {
+                       if (tdb_delete(icount->tdb, key))
+                               return tdb_error(icount->tdb) +
+                                       EXT2_ET_TDB_SUCCESS;
+               }
+               return 0;
+       }
+
+       el = get_icount_el(icount, ino, 1);
+       if (!el)
+               return EXT2_ET_NO_MEMORY;
+
+       el->count = count;
+       return 0;
+}
+
+static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino,
+                                __u16 *count)
+{
+       struct ext2_icount_el   *el;
+       TDB_DATA key, data;
+
+       if (icount->tdb) {
+               key.dptr = (unsigned char *) &ino;
+               key.dsize = sizeof(ext2_ino_t);
+
+               data = tdb_fetch(icount->tdb, key);
+               if (data.dptr == NULL) {
+                       *count = 0;
+                       return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS;
+               }
+
+               *count = *((__u16 *) data.dptr);
+               free(data.dptr);
+               return 0;
+       }
+       el = get_icount_el(icount, ino, 0);
+       if (!el) {
+               *count = 0;
+               return ENOENT;
+       }
+
+       *count = el->count;
+       return 0;
+}
+
 errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
 {
        errcode_t       ret = 0;
-       int             i;
+       unsigned int    i;
        const char *bad = "bad icount";
-       
+
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
        if (icount->count > icount->size) {
                fprintf(out, "%s: count > size\n", bad);
-               return EINVAL;
+               return EXT2_ET_INVALID_ARGUMENT;
        }
        for (i=1; i < icount->count; i++) {
                if (icount->list[i-1].ino >= icount->list[i].ino) {
                        fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
                                bad, i-1, icount->list[i-1].ino,
                                i, icount->list[i].ino);
-                       ret = EINVAL;
+                       ret = EXT2_ET_INVALID_ARGUMENT;
                }
        }
        return ret;
 }
 
-errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ino_t ino, __u16 *ret)
+errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
 {
-       struct ext2_icount_el   *el;
-       
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
        if (!ino || (ino > icount->num_inodes))
-               return EINVAL;
+               return EXT2_ET_INVALID_ARGUMENT;
 
        if (ext2fs_test_inode_bitmap(icount->single, ino)) {
                *ret = 1;
@@ -313,53 +496,45 @@ errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ino_t ino, __u16 *ret)
                *ret = 0;
                return 0;
        }
-       el = get_icount_el(icount, ino, 0);
-       if (!el) {
-               *ret = 0;
-               return 0;
-       }
-       *ret = el->count;
+       get_inode_count(icount, ino, ret);
        return 0;
 }
 
-errcode_t ext2fs_icount_increment(ext2_icount_t icount, ino_t ino,
+errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
                                  __u16 *ret)
 {
-       struct ext2_icount_el   *el;
+       __u16                   curr_value;
 
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
        if (!ino || (ino > icount->num_inodes))
-               return EINVAL;
+               return EXT2_ET_INVALID_ARGUMENT;
 
        if (ext2fs_test_inode_bitmap(icount->single, ino)) {
                /*
                 * If the existing count is 1, then we know there is
                 * no entry in the list.
                 */
-               el = get_icount_el(icount, ino, 1);
-               if (!el)
-                       return ENOMEM;
+               if (set_inode_count(icount, ino, 2))
+                       return EXT2_ET_NO_MEMORY;
+               curr_value = 2;
                ext2fs_unmark_inode_bitmap(icount->single, ino);
-               el->count = 2;
        } else if (icount->multiple) {
                /*
                 * The count is either zero or greater than 1; if the
                 * inode is set in icount->multiple, then there should
-                * be an entry in the list, so find it using
-                * get_icount_el().
+                * be an entry in the list, so we need to fix it.
                 */
                if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
-                       el = get_icount_el(icount, ino, 1);
-                       if (!el)
-                               return ENOMEM;
-                       el->count++;
+                       get_inode_count(icount, ino, &curr_value);
+                       curr_value++;
+                       if (set_inode_count(icount, ino, curr_value))
+                               return EXT2_ET_NO_MEMORY;
                } else {
                        /*
                         * The count was zero; mark the single bitmap
                         * and return.
                         */
-               zero_count:
                        ext2fs_mark_inode_bitmap(icount->single, ino);
                        if (ret)
                                *ret = 1;
@@ -370,30 +545,25 @@ errcode_t ext2fs_icount_increment(ext2_icount_t icount, ino_t ino,
                 * The count is either zero or greater than 1; try to
                 * find an entry in the list to determine which.
                 */
-               el = get_icount_el(icount, ino, 0);
-               if (!el) {
-                       /* No entry means the count was zero */
-                       goto zero_count;
-               }
-               el = get_icount_el(icount, ino, 1);
-               if (!el)
-                       return ENOMEM;
-               el->count++;
+               get_inode_count(icount, ino, &curr_value);
+               curr_value++;
+               if (set_inode_count(icount, ino, curr_value))
+                       return EXT2_ET_NO_MEMORY;
        }
        if (icount->multiple)
                ext2fs_mark_inode_bitmap(icount->multiple, ino);
        if (ret)
-               *ret = el->count;
+               *ret = curr_value;
        return 0;
 }
 
-errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ino_t ino,
+errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
                                  __u16 *ret)
 {
-       struct ext2_icount_el   *el;
+       __u16                   curr_value;
 
        if (!ino || (ino > icount->num_inodes))
-               return EINVAL;
+               return EXT2_ET_INVALID_ARGUMENT;
 
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
@@ -402,9 +572,7 @@ errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ino_t ino,
                if (icount->multiple)
                        ext2fs_unmark_inode_bitmap(icount->multiple, ino);
                else {
-                       el = get_icount_el(icount, ino, 0);
-                       if (el)
-                               el->count = 0;
+                       set_inode_count(icount, ino, 0);
                }
                if (ret)
                        *ret = 0;
@@ -413,30 +581,30 @@ errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ino_t ino,
 
        if (icount->multiple &&
            !ext2fs_test_inode_bitmap(icount->multiple, ino))
-               return EINVAL;
-       
-       el = get_icount_el(icount, ino, 0);
-       if (!el || el->count == 0)
-               return EINVAL;
+               return EXT2_ET_INVALID_ARGUMENT;
 
-       el->count--;
-       if (el->count == 1)
+       get_inode_count(icount, ino, &curr_value);
+       if (!curr_value)
+               return EXT2_ET_INVALID_ARGUMENT;
+       curr_value--;
+       if (set_inode_count(icount, ino, curr_value))
+               return EXT2_ET_NO_MEMORY;
+
+       if (curr_value == 1)
                ext2fs_mark_inode_bitmap(icount->single, ino);
-       if ((el->count == 0) && icount->multiple)
+       if ((curr_value == 0) && icount->multiple)
                ext2fs_unmark_inode_bitmap(icount->multiple, ino);
 
        if (ret)
-               *ret = el->count;
+               *ret = curr_value;
        return 0;
 }
 
-errcode_t ext2fs_icount_store(ext2_icount_t icount, ino_t ino,
+errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
                              __u16 count)
 {
-       struct ext2_icount_el   *el;
-
        if (!ino || (ino > icount->num_inodes))
-               return EINVAL;
+               return EXT2_ET_INVALID_ARGUMENT;
 
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
@@ -454,31 +622,229 @@ errcode_t ext2fs_icount_store(ext2_icount_t icount, ino_t ino,
                         * we can just clear both bitmaps and we're done
                         */
                        ext2fs_unmark_inode_bitmap(icount->multiple, ino);
-               } else {
-                       el = get_icount_el(icount, ino, 0);
-                       if (el)
-                               el->count = 0;
-               }
+               } else
+                       set_inode_count(icount, ino, 0);
                return 0;
        }
 
-       /*
-        * Get the icount element
-        */
-       el = get_icount_el(icount, ino, 1);
-       if (!el)
-               return ENOMEM;
-       el->count = count;
+       if (set_inode_count(icount, ino, count))
+               return EXT2_ET_NO_MEMORY;
        ext2fs_unmark_inode_bitmap(icount->single, ino);
        if (icount->multiple)
                ext2fs_mark_inode_bitmap(icount->multiple, ino);
        return 0;
 }
 
-ino_t ext2fs_get_icount_size(ext2_icount_t icount)
+ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
 {
        if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
                return 0;
 
        return icount->size;
 }
+
+#ifdef DEBUG
+
+ext2_filsys    test_fs;
+ext2_icount_t  icount;
+
+#define EXIT           0x00
+#define FETCH          0x01
+#define STORE          0x02
+#define INCREMENT      0x03
+#define DECREMENT      0x04
+
+struct test_program {
+       int             cmd;
+       ext2_ino_t      ino;
+       __u16           arg;
+       __u16           expected;
+};
+
+struct test_program prog[] = {
+       { STORE, 42, 42, 42 },
+       { STORE, 1,  1, 1 },
+       { STORE, 2,  2, 2 },
+       { STORE, 3,  3, 3 },
+       { STORE, 10, 1, 1 },
+       { STORE, 42, 0, 0 },
+       { INCREMENT, 5, 0, 1 },
+       { INCREMENT, 5, 0, 2 },
+       { INCREMENT, 5, 0, 3 },
+       { INCREMENT, 5, 0, 4 },
+       { DECREMENT, 5, 0, 3 },
+       { DECREMENT, 5, 0, 2 },
+       { DECREMENT, 5, 0, 1 },
+       { DECREMENT, 5, 0, 0 },
+       { FETCH, 10, 0, 1 },
+       { FETCH, 1, 0, 1 },
+       { FETCH, 2, 0, 2 },
+       { FETCH, 3, 0, 3 },
+       { INCREMENT, 1, 0, 2 },
+       { DECREMENT, 2, 0, 1 },
+       { DECREMENT, 2, 0, 0 },
+       { FETCH, 12, 0, 0 },
+       { EXIT, 0, 0, 0 }
+};
+
+struct test_program extended[] = {
+       { STORE, 1,  1, 1 },
+       { STORE, 2,  2, 2 },
+       { STORE, 3,  3, 3 },
+       { STORE, 4,  4, 4 },
+       { STORE, 5,  5, 5 },
+       { STORE, 6,  1, 1 },
+       { STORE, 7,  2, 2 },
+       { STORE, 8,  3, 3 },
+       { STORE, 9,  4, 4 },
+       { STORE, 10, 5, 5 },
+       { STORE, 11, 1, 1 },
+       { STORE, 12, 2, 2 },
+       { STORE, 13, 3, 3 },
+       { STORE, 14, 4, 4 },
+       { STORE, 15, 5, 5 },
+       { STORE, 16, 1, 1 },
+       { STORE, 17, 2, 2 },
+       { STORE, 18, 3, 3 },
+       { STORE, 19, 4, 4 },
+       { STORE, 20, 5, 5 },
+       { STORE, 21, 1, 1 },
+       { STORE, 22, 2, 2 },
+       { STORE, 23, 3, 3 },
+       { STORE, 24, 4, 4 },
+       { STORE, 25, 5, 5 },
+       { STORE, 26, 1, 1 },
+       { STORE, 27, 2, 2 },
+       { STORE, 28, 3, 3 },
+       { STORE, 29, 4, 4 },
+       { STORE, 30, 5, 5 },
+       { EXIT, 0, 0, 0 }
+};
+
+/*
+ * Setup the variables for doing the inode scan test.
+ */
+static void setup(void)
+{
+       errcode_t       retval;
+       struct ext2_super_block param;
+
+       initialize_ext2_error_table();
+
+       memset(&param, 0, sizeof(param));
+       param.s_blocks_count = 12000;
+
+       retval = ext2fs_initialize("test fs", 0, &param,
+                                  test_io_manager, &test_fs);
+       if (retval) {
+               com_err("setup", retval,
+                       "while initializing filesystem");
+               exit(1);
+       }
+       retval = ext2fs_allocate_tables(test_fs);
+       if (retval) {
+               com_err("setup", retval,
+                       "while allocating tables for test filesystem");
+               exit(1);
+       }
+}
+
+int run_test(int flags, int size, char *dir, struct test_program *prog)
+{
+       errcode_t       retval;
+       ext2_icount_t   icount;
+       struct test_program *pc;
+       __u16           result;
+       int             problem = 0;
+
+       if (dir) {
+               retval = ext2fs_create_icount_tdb(test_fs, dir,
+                                                 flags, &icount);
+               if (retval) {
+                       com_err("run_test", retval,
+                               "while creating icount using tdb");
+                       exit(1);
+               }
+       } else {
+               retval = ext2fs_create_icount2(test_fs, flags, size, 0,
+                                              &icount);
+               if (retval) {
+                       com_err("run_test", retval, "while creating icount");
+                       exit(1);
+               }
+       }
+       for (pc = prog; pc->cmd != EXIT; pc++) {
+               switch (pc->cmd) {
+               case FETCH:
+                       printf("icount_fetch(%u) = ", pc->ino);
+                       break;
+               case STORE:
+                       retval = ext2fs_icount_store(icount, pc->ino, pc->arg);
+                       if (retval) {
+                               com_err("run_test", retval,
+                                       "while calling icount_store");
+                               exit(1);
+                       }
+                       printf("icount_store(%u, %u) = ", pc->ino, pc->arg);
+                       break;
+               case INCREMENT:
+                       retval = ext2fs_icount_increment(icount, pc->ino, 0);
+                       if (retval) {
+                               com_err("run_test", retval,
+                                       "while calling icount_increment");
+                               exit(1);
+                       }
+                       printf("icount_increment(%u) = ", pc->ino);
+                       break;
+               case DECREMENT:
+                       retval = ext2fs_icount_decrement(icount, pc->ino, 0);
+                       if (retval) {
+                               com_err("run_test", retval,
+                                       "while calling icount_decrement");
+                               exit(1);
+                       }
+                       printf("icount_decrement(%u) = ", pc->ino);
+                       break;
+               }
+               retval = ext2fs_icount_fetch(icount, pc->ino, &result);
+               if (retval) {
+                       com_err("run_test", retval,
+                               "while calling icount_fetch");
+                       exit(1);
+               }
+               printf("%u (%s)\n", result, (result == pc->expected) ?
+                      "OK" : "NOT OK");
+               if (result != pc->expected)
+                       problem++;
+       }
+       printf("icount size is %u\n", ext2fs_get_icount_size(icount));
+       retval = ext2fs_icount_validate(icount, stdout);
+       if (retval) {
+               com_err("run_test", retval, "while calling icount_validate");
+               exit(1);
+       }
+       ext2fs_free_icount(icount);
+       return problem;
+}
+
+
+int main(int argc, char **argv)
+{
+       int failed = 0;
+
+       setup();
+       printf("Standard icount run:\n");
+       failed += run_test(0, 0, 0, prog);
+       printf("\nMultiple bitmap test:\n");
+       failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog);
+       printf("\nResizing icount:\n");
+       failed += run_test(0, 3, 0, extended);
+       printf("\nStandard icount run with tdb:\n");
+       failed += run_test(0, 0, ".", prog);
+       printf("\nMultiple bitmap test with tdb:\n");
+       failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog);
+       if (failed)
+               printf("FAILED!\n");
+       return failed;
+}
+#endif