Whamcloud - gitweb
libext2fs: find/alloc a range of empty blocks
[tools/e2fsprogs.git] / lib / ext2fs / icount.c
index d1e11ae..5e1f5c6 100644 (file)
@@ -4,11 +4,12 @@
  * 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 "config.h"
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -43,7 +44,7 @@
 
 struct ext2_icount_el {
        ext2_ino_t      ino;
-       __u16   count;
+       __u32           count;
 };
 
 struct ext2_icount {
@@ -60,6 +61,15 @@ struct ext2_icount {
        TDB_CONTEXT             *tdb;
 };
 
+/*
+ * 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)
@@ -94,12 +104,12 @@ static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret)
                return retval;
        memset(icount, 0, sizeof(struct ext2_icount));
 
-       retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single);
+       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;
@@ -170,6 +180,8 @@ errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
        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);
@@ -181,20 +193,34 @@ errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
                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);
-
-       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;
+       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;
 
-       retval = errno;
+       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);
@@ -237,7 +263,8 @@ errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
        printf("Icount allocated %u entries, %d bytes.\n",
               icount->size, bytes);
 #endif
-       retval = ext2fs_get_mem(bytes, &icount->list);
+       retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el),
+                        &icount->list);
        if (retval)
                goto errout;
        memset(icount->list, 0, bytes);
@@ -329,9 +356,7 @@ static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
 static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
                                            ext2_ino_t ino, int create)
 {
-       float   range;
        int     low, high, mid;
-       ext2_ino_t      lowval, highval;
 
        if (!icount || !icount->list)
                return 0;
@@ -353,31 +378,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);
-                               if (range > 0.9)
-                                       range = 0.9;
-                               if (range < 0.1)
-                                       range = 0.1;
-                       }
-                       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];
@@ -397,7 +398,7 @@ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
 }
 
 static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
-                                __u16 count)
+                                __u32 count)
 {
        struct ext2_icount_el   *el;
        TDB_DATA key, data;
@@ -406,7 +407,7 @@ static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
                key.dptr = (unsigned char *) &ino;
                key.dsize = sizeof(ext2_ino_t);
                data.dptr = (unsigned char *) &count;
-               data.dsize = sizeof(__u16);
+               data.dsize = sizeof(__u32);
                if (count) {
                        if (tdb_store(icount->tdb, key, data, TDB_REPLACE))
                                return tdb_error(icount->tdb) +
@@ -428,7 +429,7 @@ static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
 }
 
 static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino,
-                                __u16 *count)
+                                __u32 *count)
 {
        struct ext2_icount_el   *el;
        TDB_DATA key, data;
@@ -443,7 +444,7 @@ static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino,
                        return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS;
                }
 
-               *count = *((__u16 *) data.dptr);
+               *count = *((__u32 *) data.dptr);
                free(data.dptr);
                return 0;
        }
@@ -482,38 +483,37 @@ errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
 
 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_ET_INVALID_ARGUMENT;
 
-       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+       if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
                *ret = 1;
                return 0;
        }
        if (icount->multiple &&
-           !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+           !ext2fs_test_inode_bitmap2(icount->multiple, ino)) {
                *ret = 0;
                return 0;
        }
-       get_inode_count(icount, ino, ret);
+       get_inode_count(icount, ino, &val);
+       *ret = icount_16_xlate(val);
        return 0;
 }
 
 errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
                                  __u16 *ret)
 {
-       struct ext2_icount_el   *el;
-       __u16                   curr_value;
+       __u32                   curr_value;
 
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
        if (!ino || (ino > icount->num_inodes))
                return EXT2_ET_INVALID_ARGUMENT;
 
-       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+       if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
                /*
                 * If the existing count is 1, then we know there is
                 * no entry in the list.
@@ -521,14 +521,14 @@ errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
                if (set_inode_count(icount, ino, 2))
                        return EXT2_ET_NO_MEMORY;
                curr_value = 2;
-               ext2fs_unmark_inode_bitmap(icount->single, ino);
+               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 we need to fix it.
                 */
-               if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+               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))
@@ -538,8 +538,7 @@ errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
                         * 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;
@@ -555,26 +554,26 @@ errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
                        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 = curr_value;
+               *ret = icount_16_xlate(curr_value);
        return 0;
 }
 
 errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
                                  __u16 *ret)
 {
-       __u16                   curr_value;
+       __u32                   curr_value;
 
        if (!ino || (ino > icount->num_inodes))
                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 (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 {
                        set_inode_count(icount, ino, 0);
                }
@@ -584,7 +583,7 @@ errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
        }
 
        if (icount->multiple &&
-           !ext2fs_test_inode_bitmap(icount->multiple, ino))
+           !ext2fs_test_inode_bitmap2(icount->multiple, ino))
                return EXT2_ET_INVALID_ARGUMENT;
 
        get_inode_count(icount, ino, &curr_value);
@@ -595,12 +594,12 @@ errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
                return EXT2_ET_NO_MEMORY;
 
        if (curr_value == 1)
-               ext2fs_mark_inode_bitmap(icount->single, ino);
+               ext2fs_mark_inode_bitmap2(icount->single, ino);
        if ((curr_value == 0) && icount->multiple)
-               ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+               ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
 
        if (ret)
-               *ret = curr_value;
+               *ret = icount_16_xlate(curr_value);
        return 0;
 }
 
@@ -613,19 +612,19 @@ errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
        EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
        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);
+                       ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
                } else
                        set_inode_count(icount, ino, 0);
                return 0;
@@ -633,9 +632,9 @@ errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
 
        if (set_inode_count(icount, ino, count))
                return EXT2_ET_NO_MEMORY;
-       ext2fs_unmark_inode_bitmap(icount->single, ino);
+       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;
 }
 
@@ -731,15 +730,14 @@ struct test_program extended[] = {
 static void setup(void)
 {
        errcode_t       retval;
-       int             i;
        struct ext2_super_block param;
 
        initialize_ext2_error_table();
 
        memset(&param, 0, sizeof(param));
-       param.s_blocks_count = 12000;
+       ext2fs_blocks_count_set(&param, 12000);
 
-       retval = ext2fs_initialize("test fs", 0, &param,
+       retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
                                   test_io_manager, &test_fs);
        if (retval) {
                com_err("setup", retval,