Whamcloud - gitweb
AOSP: mke2fs, libext2fs: fix bugs on windows
[tools/e2fsprogs.git] / lib / ext2fs / icount.c
index f321e68..d7de19f 100644 (file)
@@ -4,21 +4,24 @@
  * Copyright (C) 1997 Theodore Ts'o.
  *
  * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
  * %End-Header%
  */
 
-#include <et/com_err.h>
+#include "config.h"
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
 
-#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;
-       __u16   count;
+       ext2_ino_t      ino;
+       __u32           count;
 };
 
 struct ext2_icount {
        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;
+#ifdef CONFIG_TDB
+       char                    *tdb_fn;
+       TDB_CONTEXT             *tdb;
+#endif
+       __u16                   *fullmap;
 };
 
+/*
+ * We now use a 32-bit counter field because it doesn't cost us
+ * anything extra for the in-memory data structure, due to alignment
+ * padding.  But there's no point changing the interface if most of
+ * the time we only care if the number is bigger than 65,000 or not.
+ * So use the following translation function to return a 16-bit count.
+ */
+#define icount_16_xlate(x) (((x) > 65500) ? 65500 : (x))
+
 void ext2fs_free_icount(ext2_icount_t icount)
 {
        if (!icount)
@@ -62,46 +80,201 @@ void ext2fs_free_icount(ext2_icount_t icount)
 
        icount->magic = 0;
        if (icount->list)
-               ext2fs_free_mem((void **) &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);
-       ext2fs_free_mem((void **) &icount);
+#ifdef CONFIG_TDB
+       if (icount->tdb)
+               tdb_close(icount->tdb);
+       if (icount->tdb_fn) {
+               unlink(icount->tdb_fn);
+               free(icount->tdb_fn);
+       }
+#endif
+
+       if (icount->fullmap)
+               ext2fs_free_mem(&icount->fullmap);
+
+       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 = (size_t) hint->size;
-       }
-       
-       retval = ext2fs_get_mem(sizeof(struct ext2_icount), (void **) &icount);
+       *ret = 0;
+
+       retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
        if (retval)
                return retval;
        memset(icount, 0, sizeof(struct ext2_icount));
+       icount->magic = EXT2_ET_MAGIC_ICOUNT;
+       icount->num_inodes = fs->super->s_inodes_count;
+
+       if ((flags & EXT2_ICOUNT_OPT_FULLMAP) &&
+           (flags & EXT2_ICOUNT_OPT_INCREMENT)) {
+               unsigned sz = sizeof(*icount->fullmap) * icount->num_inodes;
 
-       retval = ext2fs_allocate_inode_bitmap(fs, 0, 
-                                             &icount->single);
+               retval = ext2fs_get_mem(sz, &icount->fullmap);
+               /* If we can't allocate, fall back */
+               if (!retval) {
+                       memset(icount->fullmap, 0, sz);
+                       *ret = icount;
+                       return 0;
+               }
+       }
+
+       retval = ext2fs_allocate_inode_bitmap(fs, "icount", &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, "icount_inc",
                                                      &icount->multiple);
                if (retval)
                        goto errout;
        } else
                icount->multiple = 0;
 
+       *ret = icount;
+       return 0;
+
+errout:
+       ext2fs_free_icount(icount);
+       return(retval);
+}
+
+#ifdef CONFIG_TDB
+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]);
+}
+#endif
+
+errcode_t ext2fs_create_icount_tdb(ext2_filsys fs EXT2FS_NO_TDB_UNUSED,
+                                  char *tdb_dir EXT2FS_NO_TDB_UNUSED,
+                                  int flags EXT2FS_NO_TDB_UNUSED,
+                                  ext2_icount_t *ret EXT2FS_NO_TDB_UNUSED)
+{
+#ifdef CONFIG_TDB
+       ext2_icount_t   icount;
+       errcode_t       retval;
+       char            *fn, uuid[40];
+       ext2_ino_t      num_inodes;
+       mode_t          save_umask;
+       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);
+       save_umask = umask(077);
+       fd = mkstemp(fn);
+       if (fd < 0) {
+               retval = errno;
+               ext2fs_free_mem(&fn);
+               goto errout;
+       }
+       icount->tdb_fn = fn;
+       umask(save_umask);
+       /*
+        * This is an overestimate of the size that we will need; the
+        * ideal value is the number of used inodes with a count
+        * greater than 1.  OTOH the times when we really need this is
+        * with the backup programs that use lots of hard links, in
+        * which case the number of inodes in use approaches the ideal
+        * value.
+        */
+       num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count;
+
+       icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC,
+                              O_RDWR | O_CREAT | O_TRUNC, 0600);
+       close(fd);
+       if (icount->tdb == NULL) {
+               retval = errno;
+               goto errout;
+       }
+       *ret = icount;
+       return 0;
+errout:
+       ext2fs_free_icount(icount);
+       return(retval);
+#else
+       return EXT2_ET_UNIMPLEMENTED;
+#endif
+}
+
+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 (icount->fullmap)
+               goto successout;
+
        if (size) {
                icount->size = size;
        } else {
@@ -115,21 +288,20 @@ errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
                        goto errout;
                icount->size += fs->super->s_inodes_count / 50;
        }
-       
+
        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
-       retval = ext2fs_get_mem(bytes, (void **) &icount->list);
+       retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el),
+                        &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
@@ -142,6 +314,7 @@ errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
                icount->count = hint->count;
        }
 
+successout:
        *ret = icount;
        return 0;
 
@@ -150,7 +323,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);
@@ -161,27 +335,32 @@ 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;
        errcode_t               retval;
-       ino_t                   new_size = 0;
+       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[(unsigned)icount->count-1].ino;
-                       new_size = icount->count * 
-                               ((float) new_size / icount->num_inodes);
+                       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 
-               retval = ext2fs_resize_mem((size_t) new_size *
+               printf("Reallocating icount %u entries...\n", new_size);
+#endif
+               retval = ext2fs_resize_mem((size_t) icount->size *
                                           sizeof(struct ext2_icount_el),
-                                          (void **) &icount->list);
+                                          (size_t) new_size *
+                                          sizeof(struct ext2_icount_el),
+                                          &icount->list);
                if (retval)
                        return 0;
                icount->size = new_size;
@@ -197,6 +376,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;
 }
 
@@ -206,11 +386,9 @@ 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;
 
        if (!icount || !icount->list)
                return 0;
@@ -221,7 +399,7 @@ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
        }
        if (icount->count == 0)
                return 0;
-       
+
        if (icount->cursor >= icount->count)
                icount->cursor = 0;
        if (ino == icount->list[icount->cursor].ino)
@@ -232,26 +410,7 @@ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
        low = 0;
        high = (int) icount->count-1;
        while (low <= high) {
-#if 0
-               mid = (low+high)/2;
-#else
-               if (low == high)
-                       mid = low;
-               else {
-                       /* Interpolate for efficiency */
-                       lowval = icount->list[low].ino;
-                       highval = icount->list[high].ino;
-
-                       if (ino < lowval)
-                               range = 0;
-                       else if (ino > highval)
-                               range = 1;
-                       else 
-                               range = ((float) (ino - lowval)) /
-                                       (highval - lowval);
-                       mid = low + ((int) (range * (high-low)));
-               }
-#endif
+               mid = ((unsigned)low + (unsigned)high) >> 1;
                if (ino == icount->list[mid].ino) {
                        icount->cursor = mid+1;
                        return &icount->list[mid];
@@ -270,95 +429,166 @@ 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,
+                                __u32 count)
+{
+       struct ext2_icount_el   *el;
+#ifdef CONFIG_TDB
+       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(__u32);
+               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;
+       }
+#endif
+       if (icount->fullmap) {
+               icount->fullmap[ino] = icount_16_xlate(count);
+               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,
+                                __u32 *count)
+{
+       struct ext2_icount_el   *el;
+#ifdef CONFIG_TDB
+       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 = *((__u32 *) data.dptr);
+               free(data.dptr);
+               return 0;
+       }
+#endif
+       if (icount->fullmap) {
+               *count = icount->fullmap[ino];
+               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 EXT2_INVALID_ARGUMENT;
+               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=%ld, list[%d].ino=%ld\n",
+                       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 = EXT2_INVALID_ARGUMENT;
+                       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;
-       
+       __u32   val;
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
        if (!ino || (ino > icount->num_inodes))
-               return EXT2_INVALID_ARGUMENT;
+               return EXT2_ET_INVALID_ARGUMENT;
 
-       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
-               *ret = 1;
-               return 0;
-       }
-       if (icount->multiple &&
-           !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
-               *ret = 0;
-               return 0;
-       }
-       el = get_icount_el(icount, ino, 0);
-       if (!el) {
-               *ret = 0;
-               return 0;
+       if (!icount->fullmap) {
+               if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
+                       *ret = 1;
+                       return 0;
+               }
+               if (icount->multiple &&
+                       !ext2fs_test_inode_bitmap2(icount->multiple, ino)) {
+                       *ret = 0;
+                       return 0;
+               }
        }
-       *ret = el->count;
+       get_inode_count(icount, ino, &val);
+       *ret = icount_16_xlate(val);
        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;
+       __u32                   curr_value;
 
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
        if (!ino || (ino > icount->num_inodes))
-               return EXT2_INVALID_ARGUMENT;
+               return EXT2_ET_INVALID_ARGUMENT;
 
-       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+       if (icount->fullmap) {
+               curr_value = icount_16_xlate(icount->fullmap[ino] + 1);
+               icount->fullmap[ino] = curr_value;
+       } else if (ext2fs_test_inode_bitmap2(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 EXT2_NO_MEMORY;
-               ext2fs_unmark_inode_bitmap(icount->single, ino);
-               el->count = 2;
+               if (set_inode_count(icount, ino, 2))
+                       return EXT2_ET_NO_MEMORY;
+               curr_value = 2;
+               ext2fs_unmark_inode_bitmap2(icount->single, ino);
        } 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 EXT2_NO_MEMORY;
-                       el->count++;
+               if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) {
+                       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);
+                       ext2fs_mark_inode_bitmap2(icount->single, ino);
                        if (ret)
                                *ret = 1;
                        return 0;
@@ -368,41 +598,44 @@ 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 EXT2_NO_MEMORY;
-               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);
+               ext2fs_mark_inode_bitmap2(icount->multiple, ino);
        if (ret)
-               *ret = el->count;
+               *ret = icount_16_xlate(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;
+       __u32                   curr_value;
 
        if (!ino || (ino > icount->num_inodes))
-               return EXT2_INVALID_ARGUMENT;
+               return EXT2_ET_INVALID_ARGUMENT;
 
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
-       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
-               ext2fs_unmark_inode_bitmap(icount->single, ino);
+       if (icount->fullmap) {
+               if (!icount->fullmap[ino])
+                       return EXT2_ET_INVALID_ARGUMENT;
+
+               curr_value = --icount->fullmap[ino];
+               if (ret)
+                       *ret = icount_16_xlate(curr_value);
+               return 0;
+       }
+
+       if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
+               ext2fs_unmark_inode_bitmap2(icount->single, ino);
                if (icount->multiple)
-                       ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+                       ext2fs_unmark_inode_bitmap2(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;
@@ -410,73 +643,279 @@ errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ino_t ino,
        }
 
        if (icount->multiple &&
-           !ext2fs_test_inode_bitmap(icount->multiple, ino))
-               return EXT2_INVALID_ARGUMENT;
-       
-       el = get_icount_el(icount, ino, 0);
-       if (!el || el->count == 0)
-               return EXT2_INVALID_ARGUMENT;
+           !ext2fs_test_inode_bitmap2(icount->multiple, ino))
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       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;
 
-       el->count--;
-       if (el->count == 1)
-               ext2fs_mark_inode_bitmap(icount->single, ino);
-       if ((el->count == 0) && icount->multiple)
-               ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+       if (curr_value == 1)
+               ext2fs_mark_inode_bitmap2(icount->single, ino);
+       if ((curr_value == 0) && icount->multiple)
+               ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
 
        if (ret)
-               *ret = el->count;
+               *ret = icount_16_xlate(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 EXT2_INVALID_ARGUMENT;
+               return EXT2_ET_INVALID_ARGUMENT;
 
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
+       if (icount->fullmap)
+               return set_inode_count(icount, ino, count);
+
        if (count == 1) {
-               ext2fs_mark_inode_bitmap(icount->single, ino);
+               ext2fs_mark_inode_bitmap2(icount->single, ino);
                if (icount->multiple)
-                       ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+                       ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
                return 0;
        }
        if (count == 0) {
-               ext2fs_unmark_inode_bitmap(icount->single, ino);
+               ext2fs_unmark_inode_bitmap2(icount->single, ino);
                if (icount->multiple) {
                        /*
                         * If the icount->multiple bitmap is enabled,
                         * 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;
-               }
+                       ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
+               } else
+                       set_inode_count(icount, ino, 0);
                return 0;
        }
 
-       /*
-        * Get the icount element
-        */
-       el = get_icount_el(icount, ino, 1);
-       if (!el)
-               return EXT2_NO_MEMORY;
-       el->count = count;
-       ext2fs_unmark_inode_bitmap(icount->single, ino);
+       if (set_inode_count(icount, ino, count))
+               return EXT2_ET_NO_MEMORY;
+       ext2fs_unmark_inode_bitmap2(icount->single, ino);
        if (icount->multiple)
-               ext2fs_mark_inode_bitmap(icount->multiple, ino);
+               ext2fs_mark_inode_bitmap2(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));
+       ext2fs_blocks_count_set(&param, 12000);
+
+       retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &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) {
+#ifdef CONFIG_TDB
+               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
+               printf("Skipped\n");
+               return 0;
+#endif
+       } 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