lib/tdb: Sync tdb from samba git tree
authorAmitay Isaacs <amitay@gmail.com>
Fri, 13 Apr 2012 07:38:14 +0000 (17:38 +1000)
committerAmitay Isaacs <amitay@gmail.com>
Fri, 13 Apr 2012 07:49:03 +0000 (17:49 +1000)
Signed-off-by: Amitay Isaacs <amitay@gmail.com>
(This used to be ctdb commit 0f40ea2386892ae10b30beeded0e00edf4c019c3)

24 files changed:
ctdb/lib/tdb/ABI/tdb-1.2.10.sigs [moved from ctdb/lib/tdb/ABI/tdb-1.2.6.sigs with 92% similarity]
ctdb/lib/tdb/common/check.c
ctdb/lib/tdb/common/dump.c
ctdb/lib/tdb/common/error.c
ctdb/lib/tdb/common/freelist.c
ctdb/lib/tdb/common/freelistcheck.c
ctdb/lib/tdb/common/hash.c
ctdb/lib/tdb/common/io.c
ctdb/lib/tdb/common/lock.c
ctdb/lib/tdb/common/open.c
ctdb/lib/tdb/common/summary.c [new file with mode: 0644]
ctdb/lib/tdb/common/tdb.c
ctdb/lib/tdb/common/tdb_private.h
ctdb/lib/tdb/common/transaction.c
ctdb/lib/tdb/common/traverse.c
ctdb/lib/tdb/docs/mainpage.dox [new file with mode: 0644]
ctdb/lib/tdb/include/tdb.h
ctdb/lib/tdb/pytdb.c
ctdb/lib/tdb/python/tests/simple.py
ctdb/lib/tdb/tools/tdbbackup.c
ctdb/lib/tdb/tools/tdbrestore.c
ctdb/lib/tdb/tools/tdbtest.c
ctdb/lib/tdb/tools/tdbtool.c
ctdb/lib/tdb/tools/tdbtorture.c

similarity index 92%
rename from ctdb/lib/tdb/ABI/tdb-1.2.6.sigs
rename to ctdb/lib/tdb/ABI/tdb-1.2.10.sigs
index 1e01f3ba24bfb77f9a5c532edec9db15d03e12f2..61f6c19ee36d086f6cc1a24a8476170d0d0efd90 100644 (file)
@@ -25,6 +25,7 @@ tdb_get_seqnum: int (struct tdb_context *)
 tdb_hash_size: int (struct tdb_context *)
 tdb_increment_seqnum_nonblock: void (struct tdb_context *)
 tdb_jenkins_hash: unsigned int (TDB_DATA *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
 tdb_lockall: int (struct tdb_context *)
 tdb_lockall_mark: int (struct tdb_context *)
 tdb_lockall_nonblock: int (struct tdb_context *)
@@ -48,13 +49,17 @@ tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_c
 tdb_set_max_dead: void (struct tdb_context *, int)
 tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
 tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+tdb_summary: char *(struct tdb_context *)
 tdb_transaction_cancel: int (struct tdb_context *)
 tdb_transaction_commit: int (struct tdb_context *)
 tdb_transaction_prepare_commit: int (struct tdb_context *)
 tdb_transaction_start: int (struct tdb_context *)
 tdb_transaction_start_nonblock: int (struct tdb_context *)
+tdb_transaction_write_lock_mark: int (struct tdb_context *)
+tdb_transaction_write_lock_unmark: int (struct tdb_context *)
 tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
 tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
 tdb_unlockall: int (struct tdb_context *)
 tdb_unlockall_read: int (struct tdb_context *)
 tdb_validate_freelist: int (struct tdb_context *, int *)
index 58c9c26540dcfb29d292e0c00bdb22bc9afc3c20..313f55cbb05ed39632ddc99ff60a189f266be9ec 100644 (file)
@@ -92,7 +92,7 @@ static bool tdb_check_record(struct tdb_context *tdb,
                         off, rec->next));
                goto corrupt;
        }
-       if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0))
+       if (tdb->methods->tdb_oob(tdb, rec->nextsizeof(*rec), 0))
                goto corrupt;
 
        /* Check rec_len: similar to rec->next, implies next record. */
@@ -110,7 +110,7 @@ static bool tdb_check_record(struct tdb_context *tdb,
                goto corrupt;
        }
        /* OOB allows "right at the end" access, so this works for last rec. */
-       if (tdb->methods->tdb_oob(tdb, off+sizeof(*rec)+rec->rec_len, 0))
+       if (tdb->methods->tdb_oob(tdb, offsizeof(*rec)+rec->rec_len, 0))
                goto corrupt;
 
        /* Check tailer. */
@@ -308,7 +308,7 @@ static bool tdb_check_free_record(struct tdb_context *tdb,
 }
 
 /* Slow, but should be very rare. */
-static size_t dead_space(struct tdb_context *tdb, tdb_off_t off)
+size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off)
 {
        size_t len;
 
@@ -322,7 +322,7 @@ static size_t dead_space(struct tdb_context *tdb, tdb_off_t off)
        return len;
 }
 
-int tdb_check(struct tdb_context *tdb,
+_PUBLIC_ int tdb_check(struct tdb_context *tdb,
              int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
              void *private_data)
 {
@@ -345,7 +345,7 @@ int tdb_check(struct tdb_context *tdb,
        }
 
        /* Make sure we know true size of the underlying file. */
-       tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+       tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
 
        /* Header must be OK: also gets us the recovery ptr, if any. */
        if (!tdb_check_header(tdb, &recovery_start))
@@ -406,7 +406,7 @@ int tdb_check(struct tdb_context *tdb,
                                found_recovery = true;
                                break;
                        }
-                       dead = dead_space(tdb, off);
+                       dead = tdb_dead_space(tdb, off);
                        if (dead < sizeof(rec))
                                goto corrupt;
 
index 9f770f81a52381d13d339bfff1b8701ca862cc80..67de04e37c655ede265052ec08338369e2156475 100644 (file)
@@ -80,7 +80,7 @@ static int tdb_dump_chain(struct tdb_context *tdb, int i)
        return tdb_unlock(tdb, i, F_WRLCK);
 }
 
-void tdb_dump_all(struct tdb_context *tdb)
+_PUBLIC_ void tdb_dump_all(struct tdb_context *tdb)
 {
        int i;
        for (i=0;i<tdb->header.hash_size;i++) {
@@ -90,7 +90,7 @@ void tdb_dump_all(struct tdb_context *tdb)
        tdb_dump_chain(tdb, -1);
 }
 
-int tdb_printfreelist(struct tdb_context *tdb)
+_PUBLIC_ int tdb_printfreelist(struct tdb_context *tdb)
 {
        int ret;
        long total_free = 0;
index 9197918ddeaa048f56c1ebbaa611989add0a6bf4..2aaaa8134e4cfd1a5c43077f06399fc13c2f736b 100644 (file)
@@ -27,7 +27,7 @@
 
 #include "tdb_private.h"
 
-enum TDB_ERROR tdb_error(struct tdb_context *tdb)
+_PUBLIC_ enum TDB_ERROR tdb_error(struct tdb_context *tdb)
 {
        return tdb->ecode;
 }
@@ -46,7 +46,7 @@ static struct tdb_errname {
             {TDB_ERR_RDONLY, "write not permitted"} };
 
 /* Error string for the last tdb error */
-const char *tdb_errorstr(struct tdb_context *tdb)
+_PUBLIC_ const char *tdb_errorstr(struct tdb_context *tdb)
 {
        uint32_t i;
        for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
index 79e3c344b8bb0f93a4dd85d4a42807afc1820f35..6358f64a04ab8db19693b05e6f84f282c44c348e 100644 (file)
@@ -56,7 +56,7 @@ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct tdb_record
                           rec->magic, off));
                return -1;
        }
-       if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+       if (tdb->methods->tdb_oob(tdb, rec->nextsizeof(*rec), 0) != 0)
                return -1;
        return 0;
 }
@@ -367,7 +367,7 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_rec
 /* 
    return the size of the freelist - used to decide if we should repack 
 */
-int tdb_freelist_size(struct tdb_context *tdb)
+_PUBLIC_ int tdb_freelist_size(struct tdb_context *tdb)
 {
        tdb_off_t ptr;
        int count=0;
index 8d1ebabe04e85c9c3650797bde2a83f187b9ce2b..ab6e78f02dbb751e5c44b208c330ac0023a6b802 100644 (file)
@@ -43,7 +43,7 @@ static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr)
        return tdb_store(mem_tdb, key, data, TDB_INSERT);
 }
 
-int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries)
+_PUBLIC_ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries)
 {
        struct tdb_context *mem_tdb = NULL;
        struct tdb_record rec;
index a29ba4a7a58ef624815042cbad83f7f9f5ceb4af..1eed7221d2ab3216cae88e7bb8a51baccb2ec6ae 100644 (file)
@@ -22,8 +22,6 @@
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
-#define VALGRIND
-
 #include "tdb_private.h"
 
 /* This is based on the hash algorithm from gdbm */
@@ -216,9 +214,7 @@ static uint32_t hashlittle( const void *key, size_t length )
   u.ptr = key;
   if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
     const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
-#ifdef VALGRIND
     const uint8_t  *k8;
-#endif
 
     /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
     while (length > 12)
@@ -232,36 +228,6 @@ static uint32_t hashlittle( const void *key, size_t length )
     }
 
     /*----------------------------- handle the last (probably partial) block */
-    /*
-     * "k[2]&0xffffff" actually reads beyond the end of the string, but
-     * then masks off the part it's not allowed to read.  Because the
-     * string is aligned, the masked-off tail is in the same word as the
-     * rest of the string.  Every machine with memory protection I've seen
-     * does it on word boundaries, so is OK with this.  But VALGRIND will
-     * still catch it and complain.  The masking trick does make the hash
-     * noticably faster for short strings (like English words).
-     */
-#ifndef VALGRIND
-
-    switch(length)
-    {
-    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
-    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
-    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
-    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
-    case 8 : b+=k[1]; a+=k[0]; break;
-    case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
-    case 6 : b+=k[1]&0xffff; a+=k[0]; break;
-    case 5 : b+=k[1]&0xff; a+=k[0]; break;
-    case 4 : a+=k[0]; break;
-    case 3 : a+=k[0]&0xffffff; break;
-    case 2 : a+=k[0]&0xffff; break;
-    case 1 : a+=k[0]&0xff; break;
-    case 0 : return c;              /* zero length strings require no mixing */
-    }
-
-#else /* make valgrind happy */
-
     k8 = (const uint8_t *)k;
     switch(length)
     {
@@ -279,9 +245,6 @@ static uint32_t hashlittle( const void *key, size_t length )
     case 1 : a+=k8[0]; break;
     case 0 : return c;
     }
-
-#endif /* !valgrind */
-
   } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
     const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */
     const uint8_t  *k8;
@@ -376,7 +339,7 @@ static uint32_t hashlittle( const void *key, size_t length )
   return c;
 }
 
-unsigned int tdb_jenkins_hash(TDB_DATA *key)
+_PUBLIC_ unsigned int tdb_jenkins_hash(TDB_DATA *key)
 {
        return hashlittle(key->dptr, key->dsize);
 }
index 058ca6c6b5194691fe872e3faf35fc0a92dffd97..3131f4fa0af6fb1e331a918526f2d8986cd3980f 100644 (file)
 /* check for an out of bounds access - if it is out of bounds then
    see if the database has been expanded by someone else and expand
    if necessary 
-   note that "len" is the minimum length needed for the db
 */
-static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
+static int tdb_oob(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len,
+                  int probe)
 {
        struct stat st;
-       if (len <= tdb->map_size)
+       if (len + off < len) {
+               if (!probe) {
+                       /* Ensure ecode is set for log fn. */
+                       tdb->ecode = TDB_ERR_IO;
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob off %d len %d wrap\n",
+                                (int)off, (int)len));
+               }
+               return -1;
+       }
+
+       if (off + len <= tdb->map_size)
                return 0;
        if (tdb->flags & TDB_INTERNAL) {
                if (!probe) {
                        /* Ensure ecode is set for log fn. */
                        tdb->ecode = TDB_ERR_IO;
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n",
-                                (int)len, (int)tdb->map_size));
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond internal malloc size %u\n",
+                                (int)(off + len), (int)tdb->map_size));
                }
                return -1;
        }
@@ -53,24 +63,32 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
                return -1;
        }
 
-       if (st.st_size < (size_t)len) {
+       if (st.st_size < (size_t)off + len) {
                if (!probe) {
                        /* Ensure ecode is set for log fn. */
                        tdb->ecode = TDB_ERR_IO;
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n",
-                                (int)len, (int)st.st_size));
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond eof at %u\n",
+                                (int)(off + len), (int)st.st_size));
                }
                return -1;
        }
 
+       /* Beware >4G files! */
+       if ((tdb_off_t)st.st_size != st.st_size) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_oob len %llu too large!\n",
+                        (long long)st.st_size));
+               return -1;
+       }
+
        /* Unmap, update size, remap */
        if (tdb_munmap(tdb) == -1) {
                tdb->ecode = TDB_ERR_IO;
                return -1;
        }
        tdb->map_size = st.st_size;
-       tdb_mmap(tdb);
-       return 0;
+       return tdb_mmap(tdb);
 }
 
 /* write a lump of data at a specified offset */
@@ -86,12 +104,16 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
                return -1;
        }
 
-       if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
+       if (tdb->methods->tdb_oob(tdb, off, len, 0) != 0)
                return -1;
 
        if (tdb->map_ptr) {
                memcpy(off + (char *)tdb->map_ptr, buf, len);
        } else {
+#ifdef HAVE_INCOHERENT_MMAP
+               tdb->ecode = TDB_ERR_IO;
+               return -1;
+#else
                ssize_t written = pwrite(tdb->fd, buf, len, off);
                if ((written != (ssize_t)len) && (written != -1)) {
                        /* try once more */
@@ -116,6 +138,7 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
                                 len, off));
                        return -1;
                }
+#endif
        }
        return 0;
 }
@@ -134,13 +157,17 @@ void *tdb_convert(void *buf, uint32_t size)
 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, 
                    tdb_len_t len, int cv)
 {
-       if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) {
+       if (tdb->methods->tdb_oob(tdb, off, len, 0) != 0) {
                return -1;
        }
 
        if (tdb->map_ptr) {
                memcpy(buf, off + (char *)tdb->map_ptr, len);
        } else {
+#ifdef HAVE_INCOHERENT_MMAP
+               tdb->ecode = TDB_ERR_IO;
+               return -1;
+#else
                ssize_t ret = pread(tdb->fd, buf, len, off);
                if (ret != (ssize_t)len) {
                        /* Ensure ecode is set for log fn. */
@@ -151,6 +178,7 @@ static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
                                 (int)tdb->map_size));
                        return -1;
                }
+#endif
        }
        if (cv) {
                tdb_convert(buf, len);
@@ -203,13 +231,23 @@ int tdb_munmap(struct tdb_context *tdb)
        return 0;
 }
 
-void tdb_mmap(struct tdb_context *tdb)
+/* If mmap isn't coherent, *everyone* must always mmap. */
+static bool should_mmap(const struct tdb_context *tdb)
+{
+#ifdef HAVE_INCOHERENT_MMAP
+       return true;
+#else
+       return !(tdb->flags & TDB_NOMMAP);
+#endif
+}
+
+int tdb_mmap(struct tdb_context *tdb)
 {
        if (tdb->flags & TDB_INTERNAL)
-               return;
+               return 0;
 
 #ifdef HAVE_MMAP
-       if (!(tdb->flags & TDB_NOMMAP)) {
+       if (should_mmap(tdb)) {
                tdb->map_ptr = mmap(NULL, tdb->map_size, 
                                    PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
                                    MAP_SHARED|MAP_FILE, tdb->fd, 0);
@@ -222,6 +260,10 @@ void tdb_mmap(struct tdb_context *tdb)
                        tdb->map_ptr = NULL;
                        TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", 
                                 tdb->map_size, strerror(errno)));
+#ifdef HAVE_INCOHERENT_MMAP
+                       tdb->ecode = TDB_ERR_IO;
+                       return -1;
+#endif
                }
        } else {
                tdb->map_ptr = NULL;
@@ -229,6 +271,7 @@ void tdb_mmap(struct tdb_context *tdb)
 #else
        tdb->map_ptr = NULL;
 #endif
+       return 0;
 }
 
 /* expand a file.  we prefer to use ftruncate, as that is what posix
@@ -294,12 +337,39 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
 }
 
 
+/* You need 'size', this tells you how much you should expand by. */
+tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size, int page_size)
+{
+       tdb_off_t new_size, top_size;
+
+       /* limit size in order to avoid using up huge amounts of memory for
+        * in memory tdbs if an oddball huge record creeps in */
+       if (size > 100 * 1024) {
+               top_size = map_size + size * 2;
+       } else {
+               top_size = map_size + size * 100;
+       }
+
+       /* always make room for at least top_size more records, and at
+          least 25% more space. if the DB is smaller than 100MiB,
+          otherwise grow it by 10% only. */
+       if (map_size > 100 * 1024 * 1024) {
+               new_size = map_size * 1.10;
+       } else {
+               new_size = map_size * 1.25;
+       }
+
+       /* Round the database up to a multiple of the page size */
+       new_size = MAX(top_size, new_size);
+       return TDB_ALIGN(new_size, page_size) - map_size;
+}
+
 /* expand the database at least size bytes by expanding the underlying
    file and doing the mmap again if necessary */
 int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
 {
        struct tdb_record rec;
-       tdb_off_t offset, new_size;     
+       tdb_off_t offset;
 
        if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
@@ -307,22 +377,9 @@ int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
        }
 
        /* must know about any previous expansions by another process */
-       tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
-
-       /* always make room for at least 100 more records, and at
-           least 25% more space. Round the database up to a multiple
-           of the page size */
-       new_size = MAX(tdb->map_size + size*100, tdb->map_size * 1.25);
-       size = TDB_ALIGN(new_size, tdb->page_size) - tdb->map_size;
-
-       if (!(tdb->flags & TDB_INTERNAL))
-               tdb_munmap(tdb);
+       tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
 
-       /*
-        * We must ensure the file is unmapped before doing this
-        * to ensure consistency with systems like OpenBSD where
-        * writes and mmaps are not consistent.
-        */
+       size = tdb_expand_adjust(tdb->map_size, size, tdb->page_size);
 
        /* expand the file itself */
        if (!(tdb->flags & TDB_INTERNAL)) {
@@ -330,33 +387,30 @@ int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
                        goto fail;
        }
 
-       tdb->map_size += size;
+       /* form a new freelist record */
+       offset = tdb->map_size;
+       memset(&rec,'\0',sizeof(rec));
+       rec.rec_len = size - sizeof(rec);
 
        if (tdb->flags & TDB_INTERNAL) {
                char *new_map_ptr = (char *)realloc(tdb->map_ptr,
-                                                   tdb->map_size);
+                                                   tdb->map_size + size);
                if (!new_map_ptr) {
-                       tdb->map_size -= size;
                        goto fail;
                }
                tdb->map_ptr = new_map_ptr;
+               tdb->map_size += size;
        } else {
-               /*
-                * We must ensure the file is remapped before adding the space
-                * to ensure consistency with systems like OpenBSD where
-                * writes and mmaps are not consistent.
-                */
-
-               /* We're ok if the mmap fails as we'll fallback to read/write */
-               tdb_mmap(tdb);
+               /* Explicitly remap: if we're in a transaction, this won't
+                * happen automatically! */
+               tdb_munmap(tdb);
+               tdb->map_size += size;
+               if (tdb_mmap(tdb) != 0) {
+                       goto fail;
+               }
        }
 
-       /* form a new freelist record */
-       memset(&rec,'\0',sizeof(rec));
-       rec.rec_len = size - sizeof(rec);
-
        /* link it into the free list */
-       offset = tdb->map_size - size;
        if (tdb_free(tdb, offset, &rec) == -1)
                goto fail;
 
@@ -419,7 +473,7 @@ int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
                 * Optimize by avoiding the malloc/memcpy/free, point the
                 * parser directly at the mmap area.
                 */
-               if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) {
+               if (tdb->methods->tdb_oob(tdb, offsetlen, 0) != 0) {
                        return -1;
                }
                data.dptr = offset + (unsigned char *)tdb->map_ptr;
@@ -446,7 +500,7 @@ int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *r
                TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
                return -1;
        }
-       return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+       return tdb->methods->tdb_oob(tdb, rec->nextsizeof(*rec), 0);
 }
 
 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
index f0da8818d190247be700c69204ec4f0c9327d299..88a52e9dfa4c007c85fb4ef0af6f0c89dec8ae55 100644 (file)
@@ -27,7 +27,7 @@
 
 #include "tdb_private.h"
 
-void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
+_PUBLIC_ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
 {
        tdb->interrupt_sig_ptr = ptr;
 }
@@ -380,7 +380,7 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
 }
 
 /* lock a list in the database. list -1 is the alloc list. non-blocking lock */
-int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
+_PUBLIC_ int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
 {
        return tdb_lock_list(tdb, list, ltype, TDB_LOCK_NOWAIT);
 }
@@ -445,7 +445,7 @@ int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype,
        return ret;
 }
 
-int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
+_PUBLIC_ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
 {
        /* a global lock allows us to avoid per chain locks */
        if (tdb->allrecord_lock.count &&
@@ -644,28 +644,28 @@ int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock)
 }
 
 /* lock entire database with write lock */
-int tdb_lockall(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall");
        return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
 }
 
 /* lock entire database with write lock - mark only */
-int tdb_lockall_mark(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_mark(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall_mark");
        return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY, false);
 }
 
 /* unlock entire database with write lock - unmark only */
-int tdb_lockall_unmark(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_unmark(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall_unmark");
        return tdb_allrecord_unlock(tdb, F_WRLCK, true);
 }
 
 /* lock entire database with write lock - nonblocking varient */
-int tdb_lockall_nonblock(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_nonblock(struct tdb_context *tdb)
 {
        int ret = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_NOWAIT, false);
        tdb_trace_ret(tdb, "tdb_lockall_nonblock", ret);
@@ -673,21 +673,21 @@ int tdb_lockall_nonblock(struct tdb_context *tdb)
 }
 
 /* unlock entire database with write lock */
-int tdb_unlockall(struct tdb_context *tdb)
+_PUBLIC_ int tdb_unlockall(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_unlockall");
        return tdb_allrecord_unlock(tdb, F_WRLCK, false);
 }
 
 /* lock entire database with read lock */
-int tdb_lockall_read(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_read(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_lockall_read");
        return tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
 }
 
 /* lock entire database with read lock - nonblock varient */
-int tdb_lockall_read_nonblock(struct tdb_context *tdb)
+_PUBLIC_ int tdb_lockall_read_nonblock(struct tdb_context *tdb)
 {
        int ret = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_NOWAIT, false);
        tdb_trace_ret(tdb, "tdb_lockall_read_nonblock", ret);
@@ -695,7 +695,7 @@ int tdb_lockall_read_nonblock(struct tdb_context *tdb)
 }
 
 /* unlock entire database with read lock */
-int tdb_unlockall_read(struct tdb_context *tdb)
+_PUBLIC_ int tdb_unlockall_read(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_unlockall_read");
        return tdb_allrecord_unlock(tdb, F_RDLCK, false);
@@ -703,7 +703,7 @@ int tdb_unlockall_read(struct tdb_context *tdb)
 
 /* lock/unlock one hash chain. This is meant to be used to reduce
    contention - it cannot guarantee how many records will be locked */
-int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
        tdb_trace_1rec(tdb, "tdb_chainlock", key);
@@ -713,7 +713,7 @@ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
 /* lock/unlock one hash chain, non-blocking. This is meant to be used
    to reduce contention - it cannot guarantee how many records will be
    locked */
-int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret = tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
        tdb_trace_1rec_ret(tdb, "tdb_chainlock_nonblock", key, ret);
@@ -721,7 +721,7 @@ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
 }
 
 /* mark a chain as locked without actually locking it. Warning! use with great caution! */
-int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret = tdb_nest_lock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))),
                                F_WRLCK, TDB_LOCK_MARK_ONLY);
@@ -730,20 +730,20 @@ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
 }
 
 /* unmark a chain as locked without actually locking it. Warning! use with great caution! */
-int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
 {
        tdb_trace_1rec(tdb, "tdb_chainlock_unmark", key);
        return tdb_nest_unlock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))),
                               F_WRLCK, true);
 }
 
-int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
 {
        tdb_trace_1rec(tdb, "tdb_chainunlock", key);
        return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
 }
 
-int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
 {
        int ret;
        ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
@@ -751,14 +751,12 @@ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
        return ret;
 }
 
-int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
 {
        tdb_trace_1rec(tdb, "tdb_chainunlock_read", key);
        return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
 }
 
-
-
 /* record lock stops delete underneath */
 int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
 {
@@ -862,22 +860,16 @@ void tdb_release_transaction_locks(struct tdb_context *tdb)
        }
 }
 
-int tdb_transaction_write_lock_mark(struct tdb_context *tdb)
-{
-       return tdb_transaction_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY);
-}
+/* Following functions are added specifically to support CTDB. */
 
-int tdb_transaction_write_lock(struct tdb_context *tdb)
+/* Don't do actual fcntl locking, just mark tdb locked */
+_PUBLIC_ int tdb_transaction_write_lock_mark(struct tdb_context *tdb)
 {
-       return tdb_transaction_lock(tdb, F_WRLCK, 0);
-}
-
-int tdb_transaction_write_unlock(struct tdb_context *tdb)
-{
-       return tdb_transaction_unlock(tdb, F_WRLCK);
+       return tdb_transaction_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY);
 }
 
-int tdb_transaction_write_lock_unmark(struct tdb_context *tdb)
+/* Don't do actual fcntl unlocking, just mark tdb unlocked */
+_PUBLIC_ int tdb_transaction_write_lock_unmark(struct tdb_context *tdb)
 {
        return tdb_nest_unlock(tdb, TRANSACTION_LOCK, F_WRLCK, true);
 }
index 66539c3f6c5dd9fece0010a3822355ec07c9af34..8836f8442ee72c0c61bd971639e7a00396145a1c 100644 (file)
@@ -129,7 +129,7 @@ static int tdb_already_open(dev_t device,
    try to call tdb_error or tdb_errname, just do strerror(errno).
 
    @param name may be NULL for internal databases. */
-struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
+_PUBLIC_ struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
                      int open_flags, mode_t mode)
 {
        return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
@@ -162,7 +162,7 @@ static bool check_header_hash(struct tdb_context *tdb,
        return check_header_hash(tdb, false, m1, m2);
 }
 
-struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+_PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                                int open_flags, mode_t mode,
                                const struct tdb_logging_context *log_ctx,
                                tdb_hash_func hash_fn)
@@ -197,6 +197,32 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                tdb->log.log_private = NULL;
        }
 
+       if (name == NULL && (tdb_flags & TDB_INTERNAL)) {
+               name = "__TDB_INTERNAL__";
+       }
+
+       if (name == NULL) {
+               tdb->name = discard_const_p(char, "__NULL__");
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: called with name == NULL\n"));
+               tdb->name = NULL;
+               errno = EINVAL;
+               goto fail;
+       }
+
+       /* now make a copy of the name, as the caller memory might went away */
+       if (!(tdb->name = (char *)strdup(name))) {
+               /*
+                * set the name as the given string, so that tdb_name() will
+                * work in case of an error.
+                */
+               tdb->name = discard_const_p(char, name);
+               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't strdup(%s)\n",
+                        name));
+               tdb->name = NULL;
+               errno = ENOMEM;
+               goto fail;
+       }
+
        if (hash_fn) {
                tdb->hash_fn = hash_fn;
                hash_alg = "the user defined";
@@ -359,12 +385,17 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                goto fail;
        }
 
-       if (!(tdb->name = (char *)strdup(name))) {
-               errno = ENOMEM;
+       /* Beware truncation! */
+       tdb->map_size = st.st_size;
+       if (tdb->map_size != st.st_size) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                        "len %llu too large!\n", (long long)st.st_size));
+               errno = EIO;
                goto fail;
        }
 
-       tdb->map_size = st.st_size;
        tdb->device = st.st_dev;
        tdb->inode = st.st_ino;
        tdb_mmap(tdb);
@@ -436,11 +467,11 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                else
                        tdb_munmap(tdb);
        }
-       SAFE_FREE(tdb->name);
        if (tdb->fd != -1)
                if (close(tdb->fd) != 0)
                        TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
        SAFE_FREE(tdb->lockrecs);
+       SAFE_FREE(tdb->name);
        SAFE_FREE(tdb);
        errno = save_errno;
        return NULL;
@@ -451,7 +482,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
  * Set the maximum number of dead records per hash chain
  */
 
-void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
+_PUBLIC_ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
 {
        tdb->max_dead_records = max_dead;
 }
@@ -461,7 +492,7 @@ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
  *
  * @returns -1 for error; 0 for success.
  **/
-int tdb_close(struct tdb_context *tdb)
+_PUBLIC_ int tdb_close(struct tdb_context *tdb)
 {
        struct tdb_context **i;
        int ret = 0;
@@ -502,13 +533,13 @@ int tdb_close(struct tdb_context *tdb)
 }
 
 /* register a loging function */
-void tdb_set_logging_function(struct tdb_context *tdb,
-                              const struct tdb_logging_context *log_ctx)
+_PUBLIC_ void tdb_set_logging_function(struct tdb_context *tdb,
+                                       const struct tdb_logging_context *log_ctx)
 {
         tdb->log = *log_ctx;
 }
 
-void *tdb_get_logging_private(struct tdb_context *tdb)
+_PUBLIC_ void *tdb_get_logging_private(struct tdb_context *tdb)
 {
        return tdb->log.log_private;
 }
@@ -556,7 +587,9 @@ static int tdb_reopen_internal(struct tdb_context *tdb, bool active_lock)
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n"));
                goto fail;
        }
-       tdb_mmap(tdb);
+       if (tdb_mmap(tdb) != 0) {
+               goto fail;
+       }
 #endif /* fake pread or pwrite */
 
        /* We may still think we hold the active lock. */
@@ -577,13 +610,13 @@ fail:
 
 /* reopen a tdb - this can be used after a fork to ensure that we have an independent
    seek pointer from our parent and to re-establish locks */
-int tdb_reopen(struct tdb_context *tdb)
+_PUBLIC_ int tdb_reopen(struct tdb_context *tdb)
 {
        return tdb_reopen_internal(tdb, tdb->flags & TDB_CLEAR_IF_FIRST);
 }
 
 /* reopen all tdb's */
-int tdb_reopen_all(int parent_longlived)
+_PUBLIC_ int tdb_reopen_all(int parent_longlived)
 {
        struct tdb_context *tdb;
 
diff --git a/ctdb/lib/tdb/common/summary.c b/ctdb/lib/tdb/common/summary.c
new file mode 100644 (file)
index 0000000..171a1a2
--- /dev/null
@@ -0,0 +1,201 @@
+ /* 
+   Trivial Database: human-readable summary code
+   Copyright (C) Rusty Russell 2010
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include "tdb_private.h"
+
+#define SUMMARY_FORMAT \
+       "Size of file/data: %u/%zu\n" \
+       "Number of records: %zu\n" \
+       "Smallest/average/largest keys: %zu/%zu/%zu\n" \
+       "Smallest/average/largest data: %zu/%zu/%zu\n" \
+       "Smallest/average/largest padding: %zu/%zu/%zu\n" \
+       "Number of dead records: %zu\n" \
+       "Smallest/average/largest dead records: %zu/%zu/%zu\n" \
+       "Number of free records: %zu\n" \
+       "Smallest/average/largest free records: %zu/%zu/%zu\n" \
+       "Number of hash chains: %zu\n" \
+       "Smallest/average/largest hash chains: %zu/%zu/%zu\n" \
+       "Number of uncoalesced records: %zu\n" \
+       "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n" \
+       "Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
+
+/* We don't use tally module, to keep upstream happy. */
+struct tally {
+       size_t min, max, total;
+       size_t num;
+};
+
+static void tally_init(struct tally *tally)
+{
+       tally->total = 0;
+       tally->num = 0;
+       tally->min = tally->max = 0;
+}
+
+static void tally_add(struct tally *tally, size_t len)
+{
+       if (tally->num == 0)
+               tally->max = tally->min = len;
+       else if (len > tally->max)
+               tally->max = len;
+       else if (len < tally->min)
+               tally->min = len;
+       tally->num++;
+       tally->total += len;
+}
+
+static size_t tally_mean(const struct tally *tally)
+{
+       if (!tally->num)
+               return 0;
+       return tally->total / tally->num;
+}
+
+static size_t get_hash_length(struct tdb_context *tdb, unsigned int i)
+{
+       tdb_off_t rec_ptr;
+       size_t count = 0;
+
+       if (tdb_ofs_read(tdb, TDB_HASH_TOP(i), &rec_ptr) == -1)
+               return 0;
+
+       /* keep looking until we find the right record */
+       while (rec_ptr) {
+               struct tdb_record r;
+               ++count;
+               if (tdb_rec_read(tdb, rec_ptr, &r) == -1)
+                       return 0;
+               rec_ptr = r.next;
+       }
+       return count;
+}
+
+_PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
+{
+       tdb_off_t off, rec_off;
+       struct tally freet, keys, data, dead, extra, hash, uncoal;
+       struct tdb_record rec;
+       char *ret = NULL;
+       bool locked;
+       size_t len, unc = 0;
+       struct tdb_record recovery;
+
+       /* Read-only databases use no locking at all: it's best-effort.
+        * We may have a write lock already, so skip that case too. */
+       if (tdb->read_only || tdb->allrecord_lock.count != 0) {
+               locked = false;
+       } else {
+               if (tdb_lockall_read(tdb) == -1)
+                       return NULL;
+               locked = true;
+       }
+
+       if (tdb_recovery_area(tdb, tdb->methods, &rec_off, &recovery) != 0) {
+               goto unlock;
+       }
+
+       tally_init(&freet);
+       tally_init(&keys);
+       tally_init(&data);
+       tally_init(&dead);
+       tally_init(&extra);
+       tally_init(&hash);
+       tally_init(&uncoal);
+
+       for (off = TDB_DATA_START(tdb->header.hash_size);
+            off < tdb->map_size - 1;
+            off += sizeof(rec) + rec.rec_len) {
+               if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
+                                          DOCONV()) == -1)
+                       goto unlock;
+               switch (rec.magic) {
+               case TDB_MAGIC:
+                       tally_add(&keys, rec.key_len);
+                       tally_add(&data, rec.data_len);
+                       tally_add(&extra, rec.rec_len - (rec.key_len
+                                                        + rec.data_len));
+                       if (unc > 1)
+                               tally_add(&uncoal, unc - 1);
+                       unc = 0;
+                       break;
+               case TDB_FREE_MAGIC:
+                       tally_add(&freet, rec.rec_len);
+                       unc++;
+                       break;
+               /* If we crash after ftruncate, we can get zeroes or fill. */
+               case TDB_RECOVERY_INVALID_MAGIC:
+               case 0x42424242:
+                       unc++;
+                       /* If it's a valid recovery, we can trust rec_len. */
+                       if (off != rec_off) {
+                               rec.rec_len = tdb_dead_space(tdb, off)
+                                       - sizeof(rec);
+                       }
+                       /* Fall through */
+               case TDB_DEAD_MAGIC:
+                       tally_add(&dead, rec.rec_len);
+                       break;
+               default:
+                       TDB_LOG((tdb, TDB_DEBUG_ERROR,
+                                "Unexpected record magic 0x%x at offset %d\n",
+                                rec.magic, off));
+                       goto unlock;
+               }
+       }
+       if (unc > 1)
+               tally_add(&uncoal, unc - 1);
+
+       for (off = 0; off < tdb->header.hash_size; off++)
+               tally_add(&hash, get_hash_length(tdb, off));
+
+       /* 20 is max length of a %zu. */
+       len = strlen(SUMMARY_FORMAT) + 35*20 + 1;
+       ret = (char *)malloc(len);
+       if (!ret)
+               goto unlock;
+
+       snprintf(ret, len, SUMMARY_FORMAT,
+                tdb->map_size, keys.total+data.total,
+                keys.num,
+                keys.min, tally_mean(&keys), keys.max,
+                data.min, tally_mean(&data), data.max,
+                extra.min, tally_mean(&extra), extra.max,
+                dead.num,
+                dead.min, tally_mean(&dead), dead.max,
+                freet.num,
+                freet.min, tally_mean(&freet), freet.max,
+                hash.num,
+                hash.min, tally_mean(&hash), hash.max,
+                uncoal.total,
+                uncoal.min, tally_mean(&uncoal), uncoal.max,
+                keys.total * 100.0 / tdb->map_size,
+                data.total * 100.0 / tdb->map_size,
+                extra.total * 100.0 / tdb->map_size,
+                freet.total * 100.0 / tdb->map_size,
+                dead.total * 100.0 / tdb->map_size,
+                (keys.num + freet.num + dead.num)
+                * (sizeof(struct tdb_record) + sizeof(uint32_t))
+                * 100.0 / tdb->map_size,
+                tdb->header.hash_size * sizeof(tdb_off_t)
+                * 100.0 / tdb->map_size);
+
+unlock:
+       if (locked) {
+               tdb_unlockall_read(tdb);
+       }
+       return ret;
+}
index 4d8c5fc59c9952056fa177c75ce93922276b45d8..fc1f5608fa27575d42d336f8a9e1a8364be5b624 100644 (file)
 
 #include "tdb_private.h"
 
-TDB_DATA tdb_null;
+_PUBLIC_ TDB_DATA tdb_null;
 
 /*
   non-blocking increment of the tdb sequence number if the tdb has been opened using
   the TDB_SEQNUM flag
 */
-void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
+_PUBLIC_ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
 {
        tdb_off_t seqnum=0;
 
@@ -124,6 +124,19 @@ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t has
 
 static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
 
+static int tdb_update_hash_cmp(TDB_DATA key, TDB_DATA data, void *private_data)
+{
+       TDB_DATA *dbuf = (TDB_DATA *)private_data;
+
+       if (dbuf->dsize != data.dsize) {
+               return -1;
+       }
+       if (memcmp(dbuf->dptr, data.dptr, data.dsize) != 0) {
+               return -1;
+       }
+       return 0;
+}
+
 /* update an entry in place - this only works if the new data size
    is <= the old data size and the key exists.
    on failure return -1.
@@ -141,18 +154,9 @@ static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
         * surprisingly common (eg. with a ldb re-index). */
        if (rec.key_len == key.dsize && 
            rec.data_len == dbuf.dsize &&
-           rec.full_hash == hash) {
-               TDB_DATA data = _tdb_fetch(tdb, key);
-               if (data.dsize == dbuf.dsize &&
-                   memcmp(data.dptr, dbuf.dptr, data.dsize) == 0) {
-                       if (data.dptr) {
-                               free(data.dptr);
-                       }
-                       return 0;
-               }
-               if (data.dptr) {
-                       free(data.dptr);
-               }
+           rec.full_hash == hash &&
+           tdb_parse_record(tdb, key, tdb_update_hash_cmp, &dbuf) == 0) {
+               return 0;
        }
 
        /* must be long enough key, data and tailer */
@@ -199,7 +203,7 @@ static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
        return ret;
 }
 
-TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
 {
        TDB_DATA ret = _tdb_fetch(tdb, key);
 
@@ -225,7 +229,7 @@ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
  * Return -1 if the record was not found.
  */
 
-int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
+_PUBLIC_ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
                     int (*parser)(TDB_DATA key, TDB_DATA data,
                                   void *private_data),
                     void *private_data)
@@ -270,7 +274,7 @@ static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
        return 1;
 }
 
-int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
 {
        uint32_t hash = tdb->hash_fn(&key);
        int ret;
@@ -429,7 +433,7 @@ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
        return ret;
 }
 
-int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
+_PUBLIC_ int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
 {
        uint32_t hash = tdb->hash_fn(&key);
        int ret;
@@ -473,7 +477,6 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
 {
        struct tdb_record rec;
        tdb_off_t rec_ptr;
-       char *p = NULL;
        int ret = -1;
 
        /* check for it existing, on insert. */
@@ -503,18 +506,6 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
        if (flag != TDB_INSERT)
                tdb_delete_hash(tdb, key, hash);
 
-       /* Copy key+value *before* allocating free space in case malloc
-          fails and we are left with a dead spot in the tdb. */
-
-       if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
-               tdb->ecode = TDB_ERR_OOM;
-               goto fail;
-       }
-
-       memcpy(p, key.dptr, key.dsize);
-       if (dbuf.dsize)
-               memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
-
        if (tdb->max_dead_records != 0) {
                /*
                 * Allow for some dead records per hash chain, look if we can
@@ -534,7 +525,10 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
                        if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
                            || tdb->methods->tdb_write(
                                    tdb, rec_ptr + sizeof(rec),
-                                   p, key.dsize + dbuf.dsize) == -1) {
+                                   key.dptr, key.dsize) == -1
+                           || tdb->methods->tdb_write(
+                                   tdb, rec_ptr + sizeof(rec) + key.dsize,
+                                   dbuf.dptr, dbuf.dsize) == -1) {
                                goto fail;
                        }
                        goto done;
@@ -577,7 +571,10 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
 
        /* write out and point the top of the hash chain at it */
        if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
-           || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+           || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec),
+                                      key.dptr, key.dsize) == -1
+           || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec)+key.dsize,
+                                      dbuf.dptr, dbuf.dsize) == -1
            || tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
                /* Need to tdb_unallocate() here */
                goto fail;
@@ -589,8 +586,6 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
        if (ret == 0) {
                tdb_increment_seqnum(tdb);
        }
-
-       SAFE_FREE(p); 
        return ret;
 }
 
@@ -599,7 +594,7 @@ static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
 
    return 0 on success, -1 on failure
 */
-int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+_PUBLIC_ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
 {
        uint32_t hash;
        int ret;
@@ -622,7 +617,7 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
 }
 
 /* Append to an entry. Create if not exist. */
-int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+_PUBLIC_ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
 {
        uint32_t hash;
        TDB_DATA dbuf;
@@ -673,7 +668,7 @@ failed:
   return the name of the current tdb file
   useful for external logging functions
 */
-const char *tdb_name(struct tdb_context *tdb)
+_PUBLIC_ const char *tdb_name(struct tdb_context *tdb)
 {
        return tdb->name;
 }
@@ -683,7 +678,7 @@ const char *tdb_name(struct tdb_context *tdb)
   useful for external routines that want to check the device/inode
   of the fd
 */
-int tdb_fd(struct tdb_context *tdb)
+_PUBLIC_ int tdb_fd(struct tdb_context *tdb)
 {
        return tdb->fd;
 }
@@ -692,7 +687,7 @@ int tdb_fd(struct tdb_context *tdb)
   return the current logging function
   useful for external tdb routines that wish to log tdb errors
 */
-tdb_log_func tdb_log_fn(struct tdb_context *tdb)
+_PUBLIC_ tdb_log_func tdb_log_fn(struct tdb_context *tdb)
 {
        return tdb->log.log_fn;
 }
@@ -708,7 +703,7 @@ tdb_log_func tdb_log_fn(struct tdb_context *tdb)
   The aim of this sequence number is to allow for a very lightweight
   test of a possible tdb change.
 */
-int tdb_get_seqnum(struct tdb_context *tdb)
+_PUBLIC_ int tdb_get_seqnum(struct tdb_context *tdb)
 {
        tdb_off_t seqnum=0;
 
@@ -716,22 +711,22 @@ int tdb_get_seqnum(struct tdb_context *tdb)
        return seqnum;
 }
 
-int tdb_hash_size(struct tdb_context *tdb)
+_PUBLIC_ int tdb_hash_size(struct tdb_context *tdb)
 {
        return tdb->header.hash_size;
 }
 
-size_t tdb_map_size(struct tdb_context *tdb)
+_PUBLIC_ size_t tdb_map_size(struct tdb_context *tdb)
 {
        return tdb->map_size;
 }
 
-int tdb_get_flags(struct tdb_context *tdb)
+_PUBLIC_ int tdb_get_flags(struct tdb_context *tdb)
 {
        return tdb->flags;
 }
 
-void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
+_PUBLIC_ void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
 {
        if ((flags & TDB_ALLOW_NESTING) &&
            (flags & TDB_DISALLOW_NESTING)) {
@@ -751,7 +746,7 @@ void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
        tdb->flags |= flags;
 }
 
-void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
+_PUBLIC_ void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
 {
        if ((flags & TDB_ALLOW_NESTING) &&
            (flags & TDB_DISALLOW_NESTING)) {
@@ -775,7 +770,7 @@ void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
 /*
   enable sequence number handling on an open tdb
 */
-void tdb_enable_seqnum(struct tdb_context *tdb)
+_PUBLIC_ void tdb_enable_seqnum(struct tdb_context *tdb)
 {
        tdb->flags |= TDB_SEQNUM;
 }
@@ -812,7 +807,7 @@ static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t le
 
   This code carefully steps around the recovery area, leaving it alone
  */
-int tdb_wipe_all(struct tdb_context *tdb)
+_PUBLIC_ int tdb_wipe_all(struct tdb_context *tdb)
 {
        int i;
        tdb_off_t offset = 0;
@@ -886,6 +881,8 @@ int tdb_wipe_all(struct tdb_context *tdb)
                }
        }
 
+       tdb_increment_seqnum_nonblock(tdb);
+
        if (tdb_unlockall(tdb) != 0) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n"));
                goto failed;
@@ -919,7 +916,7 @@ static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
 /*
   repack a tdb
  */
-int tdb_repack(struct tdb_context *tdb)
+_PUBLIC_ int tdb_repack(struct tdb_context *tdb)
 {
        struct tdb_context *tmp_db;
        struct traverse_state state;
@@ -993,7 +990,7 @@ int tdb_repack(struct tdb_context *tdb)
 bool tdb_write_all(int fd, const void *buf, size_t count)
 {
        while (count) {
-               size_t ret;
+               ssize_t ret;
                ret = write(fd, buf, count);
                if (ret < 0)
                        return false;
@@ -1006,7 +1003,7 @@ bool tdb_write_all(int fd, const void *buf, size_t count)
 #ifdef TDB_TRACE
 static void tdb_trace_write(struct tdb_context *tdb, const char *str)
 {
-       if (!tdb_write_alltdb->tracefd, str, strlen(str)) {
+       if (!tdb_write_all(tdb->tracefd, str, strlen(str))) {
                close(tdb->tracefd);
                tdb->tracefd = -1;
        }
index 0c621636fa2bc4b60df90fa04a24183e7ce29bb8..0441fb287fdf067645f4ce0d2845b111a554705e 100644 (file)
@@ -1,3 +1,5 @@
+#ifndef TDB_PRIVATE_H
+#define TDB_PRIVATE_H
  /* 
    Unix SMB/CIFS implementation.
 
@@ -180,7 +182,7 @@ struct tdb_methods {
        int (*tdb_read)(struct tdb_context *, tdb_off_t , void *, tdb_len_t , int );
        int (*tdb_write)(struct tdb_context *, tdb_off_t, const void *, tdb_len_t);
        void (*next_hash_chain)(struct tdb_context *, uint32_t *);
-       int (*tdb_oob)(struct tdb_context *, tdb_off_t , int );
+       int (*tdb_oob)(struct tdb_context *, tdb_off_t , tdb_len_t, int );
        int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t );
 };
 
@@ -220,7 +222,7 @@ struct tdb_context {
   internal prototypes
 */
 int tdb_munmap(struct tdb_context *tdb);
-void tdb_mmap(struct tdb_context *tdb);
+int tdb_mmap(struct tdb_context *tdb);
 int tdb_lock(struct tdb_context *tdb, int list, int ltype);
 int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype);
 int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype,
@@ -238,6 +240,10 @@ void tdb_release_transaction_locks(struct tdb_context *tdb);
 int tdb_transaction_lock(struct tdb_context *tdb, int ltype,
                         enum tdb_lock_flags lockflags);
 int tdb_transaction_unlock(struct tdb_context *tdb, int ltype);
+int tdb_recovery_area(struct tdb_context *tdb,
+                     const struct tdb_methods *methods,
+                     tdb_off_t *recovery_offset,
+                     struct tdb_record *rec);
 int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
                       enum tdb_lock_flags flags, bool upgradable);
 int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock);
@@ -267,6 +273,7 @@ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t has
                           struct tdb_record *rec);
 void tdb_io_init(struct tdb_context *tdb);
 int tdb_expand(struct tdb_context *tdb, tdb_off_t size);
+tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size, int page_size);
 int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off,
                      struct tdb_record *rec);
 bool tdb_write_all(int fd, const void *buf, size_t count);
@@ -274,3 +281,5 @@ int tdb_transaction_recover(struct tdb_context *tdb);
 void tdb_header_hash(struct tdb_context *tdb,
                     uint32_t *magic1_hash, uint32_t *magic2_hash);
 unsigned int tdb_old_hash(TDB_DATA *key);
+size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off);
+#endif /* TDB_PRIVATE_H */
index 304a03fa3836940197c1319eeae5ddbcb3fce823..c3477eb80ca27385240a5afc2e60501d150d9767 100644 (file)
@@ -138,8 +138,8 @@ struct tdb_transaction {
        /* old file size before transaction */
        tdb_len_t old_map_size;
 
-       /* we should re-pack on commit */
-       bool need_repack;
+       /* did we expand in this transaction */
+       bool expanded;
 };
 
 
@@ -382,9 +382,10 @@ static void transaction_next_hash_chain(struct tdb_context *tdb, uint32_t *chain
 /*
   out of bounds check during a transaction
 */
-static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
+static int transaction_oob(struct tdb_context *tdb, tdb_off_t off,
+                          tdb_len_t len, int probe)
 {
-       if (len <= tdb->map_size) {
+       if (off + len >= off && off + len <= tdb->map_size) {
                return 0;
        }
        tdb->ecode = TDB_ERR_IO;
@@ -403,7 +404,7 @@ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
                return -1;
        }
 
-       tdb->transaction->need_repack = true;
+       tdb->transaction->expanded = true;
 
        return 0;
 }
@@ -507,7 +508,7 @@ static int _tdb_transaction_start(struct tdb_context *tdb,
 
        /* make sure we know about any file expansions already done by
           anyone else */
-       tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+       tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
        tdb->transaction->old_map_size = tdb->map_size;
 
        /* finally hook the io methods, replacing them with
@@ -529,12 +530,12 @@ fail_allrecord_lock:
        return -1;
 }
 
-int tdb_transaction_start(struct tdb_context *tdb)
+_PUBLIC_ int tdb_transaction_start(struct tdb_context *tdb)
 {
        return _tdb_transaction_start(tdb, TDB_LOCK_WAIT);
 }
 
-int tdb_transaction_start_nonblock(struct tdb_context *tdb)
+_PUBLIC_ int tdb_transaction_start_nonblock(struct tdb_context *tdb)
 {
        return _tdb_transaction_start(tdb, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE);
 }
@@ -548,7 +549,11 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t
                return 0;
        }
 
+#ifdef HAVE_FDATASYNC
        if (fdatasync(tdb->fd) != 0) {
+#else
+       if (fsync(tdb->fd) != 0) {
+#endif
                tdb->ecode = TDB_ERR_IO;
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
                return -1;
@@ -621,7 +626,7 @@ static int _tdb_transaction_cancel(struct tdb_context *tdb)
 /*
   cancel the current transaction
 */
-int tdb_transaction_cancel(struct tdb_context *tdb)
+_PUBLIC_ int tdb_transaction_cancel(struct tdb_context *tdb)
 {
        tdb_trace(tdb, "tdb_transaction_cancel");
        return _tdb_transaction_cancel(tdb);
@@ -654,6 +659,34 @@ static tdb_len_t tdb_recovery_size(struct tdb_context *tdb)
        return recovery_size;
 }
 
+int tdb_recovery_area(struct tdb_context *tdb,
+                     const struct tdb_methods *methods,
+                     tdb_off_t *recovery_offset,
+                     struct tdb_record *rec)
+{
+       if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, recovery_offset) == -1) {
+               return -1;
+       }
+
+       if (*recovery_offset == 0) {
+               rec->rec_len = 0;
+               return 0;
+       }
+
+       if (methods->tdb_read(tdb, *recovery_offset, rec, sizeof(*rec),
+                             DOCONV()) == -1) {
+               return -1;
+       }
+
+       /* ignore invalid recovery regions: can happen in crash */
+       if (rec->magic != TDB_RECOVERY_MAGIC &&
+           rec->magic != TDB_RECOVERY_INVALID_MAGIC) {
+               *recovery_offset = 0;
+               rec->rec_len = 0;
+       }
+       return 0;
+}
+
 /*
   allocate the recovery area, or use an existing recovery area if it is
   large enough
@@ -665,29 +698,16 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
 {
        struct tdb_record rec;
        const struct tdb_methods *methods = tdb->transaction->io_methods;
-       tdb_off_t recovery_head;
+       tdb_off_t recovery_head, new_end;
 
-       if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
+       if (tdb_recovery_area(tdb, methods, &recovery_head, &rec) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n"));
                return -1;
        }
 
-       rec.rec_len = 0;
-
-       if (recovery_head != 0) {
-               if (methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));
-                       return -1;
-               }
-               /* ignore invalid recovery regions: can happen in crash */
-               if (rec.magic != TDB_RECOVERY_MAGIC &&
-                   rec.magic != TDB_RECOVERY_INVALID_MAGIC) {
-                       recovery_head = 0;
-               }
-       }
-
        *recovery_size = tdb_recovery_size(tdb);
 
+       /* Existing recovery area? */
        if (recovery_head != 0 && *recovery_size <= rec.rec_len) {
                /* it fits in the existing area */
                *recovery_max_size = rec.rec_len;
@@ -695,35 +715,51 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
                return 0;
        }
 
-       /* we need to free up the old recovery area, then allocate a
-          new one at the end of the file. Note that we cannot use
-          tdb_allocate() to allocate the new one as that might return
-          us an area that is being currently used (as of the start of
-          the transaction) */
-       if (recovery_head != 0) {
-               if (tdb_free(tdb, recovery_head, &rec) == -1) {
-                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n"));
-                       return -1;
+       /* If recovery area in middle of file, we need a new one. */
+       if (recovery_head == 0
+           || recovery_head + sizeof(rec) + rec.rec_len != tdb->map_size) {
+               /* we need to free up the old recovery area, then allocate a
+                  new one at the end of the file. Note that we cannot use
+                  tdb_allocate() to allocate the new one as that might return
+                  us an area that is being currently used (as of the start of
+                  the transaction) */
+               if (recovery_head) {
+                       if (tdb_free(tdb, recovery_head, &rec) == -1) {
+                               TDB_LOG((tdb, TDB_DEBUG_FATAL,
+                                        "tdb_recovery_allocate: failed to"
+                                        " free previous recovery area\n"));
+                               return -1;
+                       }
+
+                       /* the tdb_free() call might have increased
+                        * the recovery size */
+                       *recovery_size = tdb_recovery_size(tdb);
                }
+
+               /* New head will be at end of file. */
+               recovery_head = tdb->map_size;
        }
 
-       /* the tdb_free() call might have increased the recovery size */
-       *recovery_size = tdb_recovery_size(tdb);
+       /* Now we know where it will be. */
+       *recovery_offset = recovery_head;
+
+       /* Expand by more than we need, so we don't do it often. */
+       *recovery_max_size = tdb_expand_adjust(tdb->map_size,
+                                              *recovery_size,
+                                              tdb->page_size)
+               - sizeof(rec);
 
-       /* round up to a multiple of page size */
-       *recovery_max_size = TDB_ALIGN(sizeof(rec) + *recovery_size, tdb->page_size) - sizeof(rec);
-       *recovery_offset = tdb->map_size;
-       recovery_head = *recovery_offset;
+       new_end = recovery_head + sizeof(rec) + *recovery_max_size;
 
        if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, 
-                                    (tdb->map_size - tdb->transaction->old_map_size) +
-                                    sizeof(rec) + *recovery_max_size) == -1) {
+                                    new_end - tdb->transaction->old_map_size)
+           == -1) {
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n"));
                return -1;
        }
 
        /* remap the file (if using mmap) */
-       methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+       methods->tdb_oob(tdb, tdb->map_size, 1, 1);
 
        /* we have to reset the old map size so that we don't try to expand the file
           again in the transaction commit, which would destroy the recovery area */
@@ -782,7 +818,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
        rec->data_len = recovery_size;
        rec->rec_len  = recovery_max_size;
        rec->key_len  = old_map_size;
-       CONVERT(rec);
+       CONVERT(*rec);
 
        /* build the recovery data into a single blob to allow us to do a single
           large write, which should be more efficient */
@@ -829,7 +865,9 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
        /* and the tailer */
        tailer = sizeof(*rec) + recovery_max_size;
        memcpy(p, &tailer, 4);
-       CONVERT(p);
+       if (DOCONV()) {
+               tdb_convert(p, 4);
+       }
 
        /* write the recovery data to the recovery area */
        if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) {
@@ -960,7 +998,7 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
                        return -1;
                }
                tdb->map_size = tdb->transaction->old_map_size;
-               methods->tdb_oob(tdb, tdb->map_size + 1, 1);
+               methods->tdb_oob(tdb, tdb->map_size, 1, 1);
        }
 
        /* Keep the open lock until the actual commit */
@@ -971,20 +1009,42 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
 /*
    prepare to commit the current transaction
 */
-int tdb_transaction_prepare_commit(struct tdb_context *tdb)
-{      
+_PUBLIC_ int tdb_transaction_prepare_commit(struct tdb_context *tdb)
+{
        tdb_trace(tdb, "tdb_transaction_prepare_commit");
        return _tdb_transaction_prepare_commit(tdb);
 }
 
+/* A repack is worthwhile if the largest is less than half total free. */
+static bool repack_worthwhile(struct tdb_context *tdb)
+{
+       tdb_off_t ptr;
+       struct tdb_record rec;
+       tdb_len_t total = 0, largest = 0;
+
+       if (tdb_ofs_read(tdb, FREELIST_TOP, &ptr) == -1) {
+               return false;
+       }
+
+       while (ptr != 0 && tdb_rec_free_read(tdb, ptr, &rec) == 0) {
+               total += rec.rec_len;
+               if (rec.rec_len > largest) {
+                       largest = rec.rec_len;
+               }
+               ptr = rec.next;
+       }
+
+       return total > largest * 2;
+}
+
 /*
   commit the current transaction
 */
-int tdb_transaction_commit(struct tdb_context *tdb)
-{      
+_PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb)
+{
        const struct tdb_methods *methods;
        int i;
-       bool need_repack;
+       bool need_repack = false;
 
        if (tdb->transaction == NULL) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
@@ -1052,6 +1112,11 @@ int tdb_transaction_commit(struct tdb_context *tdb)
                SAFE_FREE(tdb->transaction->blocks[i]);
        } 
 
+       /* Do this before we drop lock or blocks. */
+       if (tdb->transaction->expanded) {
+               need_repack = repack_worthwhile(tdb);
+       }
+
        SAFE_FREE(tdb->transaction->blocks);
        tdb->transaction->num_blocks = 0;
 
@@ -1075,8 +1140,6 @@ int tdb_transaction_commit(struct tdb_context *tdb)
        utime(tdb->name, NULL);
 #endif
 
-       need_repack = tdb->transaction->need_repack;
-
        /* use a transaction cancel to free memory and remove the
           transaction locks */
        _tdb_transaction_cancel(tdb);
index d77086a79aa6c270dd3a7f799fa978ad74ca8da2..517fecb4fc8d6c1fcb50b3fec42b3ab9fa5d3358 100644 (file)
@@ -212,7 +212,7 @@ out:
 /*
   a write style traverse - temporarily marks the db read only
 */
-int tdb_traverse_read(struct tdb_context *tdb, 
+_PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb, 
                      tdb_traverse_func fn, void *private_data)
 {
        struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK };
@@ -241,7 +241,7 @@ int tdb_traverse_read(struct tdb_context *tdb,
   WARNING: The data buffer given to the callback fn does NOT meet the
   alignment restrictions malloc gives you.
 */
-int tdb_traverse(struct tdb_context *tdb, 
+_PUBLIC_ int tdb_traverse(struct tdb_context *tdb, 
                 tdb_traverse_func fn, void *private_data)
 {
        struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK };
@@ -267,7 +267,7 @@ int tdb_traverse(struct tdb_context *tdb,
 
 
 /* find the first entry in the database and return its key */
-TDB_DATA tdb_firstkey(struct tdb_context *tdb)
+_PUBLIC_ TDB_DATA tdb_firstkey(struct tdb_context *tdb)
 {
        TDB_DATA key;
        struct tdb_record rec;
@@ -298,7 +298,7 @@ TDB_DATA tdb_firstkey(struct tdb_context *tdb)
 }
 
 /* find the next entry in the database, returning its key */
-TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)
+_PUBLIC_ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)
 {
        uint32_t oldhash;
        TDB_DATA key = tdb_null;
diff --git a/ctdb/lib/tdb/docs/mainpage.dox b/ctdb/lib/tdb/docs/mainpage.dox
new file mode 100644 (file)
index 0000000..d130769
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+
+@mainpage
+
+This is a simple database API. It was inspired by the realisation that in Samba
+we have several ad-hoc bits of code that essentially implement small databases
+for sharing structures between parts of Samba.
+
+The interface is based on gdbm. gdbm couldn't be use as we needed to be able to
+have multiple writers to the databases at one time.
+
+@section tdb_download Download
+
+You can download the latest releases of tdb from the
+<a href="http://samba.org/ftp/tdb">tdb directory</a> on the samba public source
+archive.
+
+You can download the latest code either via git or rsync.
+
+To fetch via git see the following guide:
+
+<a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a>
+Once you have cloned the tree switch to the master branch and cd into the source/lib/tdb directory.
+
+To fetch via rsync use these commands:
+
+<pre>
+  rsync -Pavz samba.org::ftp/unpacked/standalone_projects/lib/tdb .
+  rsync -Pavz samba.org::ftp/unpacked/standalone_projects/lib/replace .
+</pre>
+
+and build in tdb. It will find the replace library in the directory above
+automatically.
+
+@section tdb_bugs Discussion and bug reports
+
+tdb does not currently have its own mailing list or bug tracking system. For now,
+please use the
+<a href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
+mailing list, and the <a href="http://bugzilla.samba.org/">Samba bugzilla</a> bug
+tracking system.
+
+
+@section tdb_compilation Compilation
+
+add HAVE_MMAP=1 to use mmap instead of read/write
+add NOLOCK=1 to disable locking code
+
+@section tdb_testing Testing
+
+Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
+identical operations via tdb and gdbm then make sure the result is the
+same
+
+Also included is tdbtool, which allows simple database manipulation
+on the commandline.
+
+tdbtest and tdbtool are not built as part of Samba, but are included
+for completeness.
+
+*/
index 536a0b373722347c6322504ca787cb196e91ca45..5eb95625dae199799b262ca9acf62b3f0b36eeb1 100644 (file)
 extern "C" {
 #endif
 
-#include "signal.h"
+#include <signal.h>
 
-/* Samba sets hidden attribute when building libraries: we don't. */
-#ifndef _PUBLIC_
-#define _PUBLIC_
-#endif
+/**
+ * @defgroup tdb The tdb API
+ *
+ * tdb is a Trivial database. In concept, it is very much like GDBM, and BSD's
+ * DB except that it allows multiple simultaneous writers and uses locking
+ * internally to keep writers from trampling on each other. tdb is also
+ * extremely small.
+ *
+ * @section tdb_interface Interface
+ *
+ * The interface is very similar to gdbm except for the following:
+ *
+ * <ul>
+ * <li>different open interface. The tdb_open call is more similar to a
+ * traditional open()</li>
+ * <li>no tdbm_reorganise() function</li>
+ * <li>no tdbm_sync() function. No operations are cached in the library
+ *     anyway</li>
+ * <li>added a tdb_traverse() function for traversing the whole database</li>
+ * <li>added transactions support</li>
+ * </ul>
+ *
+ * A general rule for using tdb is that the caller frees any returned TDB_DATA
+ * structures. Just call free(p.dptr) to free a TDB_DATA return value called p.
+ * This is the same as gdbm.
+ *
+ * @{
+ */
+
+/** Flags to tdb_store() */
+#define TDB_REPLACE 1          /** Unused */
+#define TDB_INSERT 2           /** Don't overwrite an existing entry */
+#define TDB_MODIFY 3           /** Don't create an existing entry    */
 
-/* flags to tdb_store() */
-#define TDB_REPLACE 1          /* Unused */
-#define TDB_INSERT 2           /* Don't overwrite an existing entry */
-#define TDB_MODIFY 3           /* Don't create an existing entry    */
-
-/* flags for tdb_open() */
-#define TDB_DEFAULT 0 /* just a readability place holder */
-#define TDB_CLEAR_IF_FIRST 1
-#define TDB_INTERNAL 2 /* don't store on disk */
-#define TDB_NOLOCK   4 /* don't do any locking */
-#define TDB_NOMMAP   8 /* don't use mmap */
-#define TDB_CONVERT 16 /* convert endian (internal use) */
-#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
-#define TDB_NOSYNC   64 /* don't use synchronous transactions */
-#define TDB_SEQNUM   128 /* maintain a sequence number */
-#define TDB_VOLATILE   256 /* Activate the per-hashchain freelist, default 5 */
-#define TDB_ALLOW_NESTING 512 /* Allow transactions to nest */
-#define TDB_DISALLOW_NESTING 1024 /* Disallow transactions to nest */
-#define TDB_INCOMPATIBLE_HASH 2048 /* Better hashing: can't be opened by tdb < 1.2.6. */
-
-/* error codes */
+/** Flags for tdb_open() */
+#define TDB_DEFAULT 0 /** just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1 /** If this is the first open, wipe the db */
+#define TDB_INTERNAL 2 /** Don't store on disk */
+#define TDB_NOLOCK   4 /** Don't do any locking */
+#define TDB_NOMMAP   8 /** Don't use mmap */
+#define TDB_CONVERT 16 /** Convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /** Header is big-endian (internal use) */
+#define TDB_NOSYNC   64 /** Don't use synchronous transactions */
+#define TDB_SEQNUM   128 /** Maintain a sequence number */
+#define TDB_VOLATILE   256 /** Activate the per-hashchain freelist, default 5 */
+#define TDB_ALLOW_NESTING 512 /** Allow transactions to nest */
+#define TDB_DISALLOW_NESTING 1024 /** Disallow transactions to nest */
+#define TDB_INCOMPATIBLE_HASH 2048 /** Better hashing: can't be opened by tdb < 1.2.6. */
+
+/** The tdb error codes */
 enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
                TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
                TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY,
                TDB_ERR_NESTING};
 
-/* debugging uses one of the following levels */
+/** Debugging uses one of the following levels */
 enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, 
                      TDB_DEBUG_WARNING, TDB_DEBUG_TRACE};
 
+/** The tdb data structure */
 typedef struct TDB_DATA {
        unsigned char *dptr;
        size_t dsize;
@@ -84,7 +109,7 @@ typedef struct TDB_DATA {
 #endif
 #endif
 
-/* this is the context structure that is returned from a db open */
+/** This is the context structure that is returned from a db open. */
 typedef struct tdb_context TDB_CONTEXT;
 
 typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *);
@@ -96,89 +121,724 @@ struct tdb_logging_context {
         void *log_private;
 };
 
-_PUBLIC_ struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
+/**
+ * @brief Open the database and creating it if necessary.
+ *
+ * @param[in]  name     The name of the db to open.
+ *
+ * @param[in]  hash_size The hash size is advisory, use zero for a default
+ *                       value.
+ *
+ * @param[in]  tdb_flags The flags to use to open the db:\n\n
+ *                         TDB_CLEAR_IF_FIRST - Clear database if we are the
+ *                                              only one with it open\n
+ *                         TDB_INTERNAL - Don't use a file, instaed store the
+ *                                        data in memory. The filename is
+ *                                        ignored in this case.\n
+ *                         TDB_NOLOCK - Don't do any locking\n
+ *                         TDB_NOMMAP - Don't use mmap\n
+ *                         TDB_NOSYNC - Don't synchronise transactions to disk\n
+ *                         TDB_SEQNUM - Maintain a sequence number\n
+ *                         TDB_VOLATILE - activate the per-hashchain freelist,
+ *                                        default 5.\n
+ *                         TDB_ALLOW_NESTING - Allow transactions to nest.\n
+ *                         TDB_DISALLOW_NESTING - Disallow transactions to nest.\n
+ *
+ * @param[in]  open_flags Flags for the open(2) function.
+ *
+ * @param[in]  mode     The mode for the open(2) function.
+ *
+ * @return              A tdb context structure, NULL on error.
+ */
+struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
                      int open_flags, mode_t mode);
-_PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+
+/**
+ * @brief Open the database and creating it if necessary.
+ *
+ * This is like tdb_open(), but allows you to pass an initial logging and
+ * hash function. Be careful when passing a hash function - all users of the
+ * database must use the same hash function or you will get data corruption.
+ *
+ * @param[in]  name     The name of the db to open.
+ *
+ * @param[in]  hash_size The hash size is advisory, use zero for a default
+ *                       value.
+ *
+ * @param[in]  tdb_flags The flags to use to open the db:\n\n
+ *                         TDB_CLEAR_IF_FIRST - Clear database if we are the
+ *                                              only one with it open\n
+ *                         TDB_INTERNAL - Don't use a file, instaed store the
+ *                                        data in memory. The filename is
+ *                                        ignored in this case.\n
+ *                         TDB_NOLOCK - Don't do any locking\n
+ *                         TDB_NOMMAP - Don't use mmap\n
+ *                         TDB_NOSYNC - Don't synchronise transactions to disk\n
+ *                         TDB_SEQNUM - Maintain a sequence number\n
+ *                         TDB_VOLATILE - activate the per-hashchain freelist,
+ *                                        default 5.\n
+ *                         TDB_ALLOW_NESTING - Allow transactions to nest.\n
+ *                         TDB_DISALLOW_NESTING - Disallow transactions to nest.\n
+ *
+ * @param[in]  open_flags Flags for the open(2) function.
+ *
+ * @param[in]  mode     The mode for the open(2) function.
+ *
+ * @param[in]  log_ctx  The logging function to use.
+ *
+ * @param[in]  hash_fn  The hash function you want to use.
+ *
+ * @return              A tdb context structure, NULL on error.
+ *
+ * @see tdb_open()
+ */
+struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
                         int open_flags, mode_t mode,
                         const struct tdb_logging_context *log_ctx,
                         tdb_hash_func hash_fn);
-_PUBLIC_ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
-
-_PUBLIC_ int tdb_reopen(struct tdb_context *tdb);
-_PUBLIC_ int tdb_reopen_all(int parent_longlived);
-_PUBLIC_ void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx);
-_PUBLIC_ enum TDB_ERROR tdb_error(struct tdb_context *tdb);
-_PUBLIC_ const char *tdb_errorstr(struct tdb_context *tdb);
-_PUBLIC_ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
+
+/**
+ * @brief Set the maximum number of dead records per hash chain.
+ *
+ * @param[in]  tdb      The database handle to set the maximum.
+ *
+ * @param[in]  max_dead The maximum number of dead records per hash chain.
+ */
+void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
+
+/**
+ * @brief Reopen a tdb.
+ *
+ * This can be used after a fork to ensure that we have an independent seek
+ * pointer from our parent and to re-establish locks.
+ *
+ * @param[in]  tdb      The database to reopen.
+ *
+ * @return              0 on success, -1 on error.
+ */
+int tdb_reopen(struct tdb_context *tdb);
+
+/**
+ * @brief Reopen all tdb's
+ *
+ * If the parent is longlived (ie. a parent daemon architecture), we know it
+ * will keep it's active lock on a tdb opened with CLEAR_IF_FIRST. Thus for
+ * child processes we don't have to add an active lock. This is essential to
+ * improve performance on systems that keep POSIX locks as a non-scalable data
+ * structure in the kernel.
+ *
+ * @param[in]  parent_longlived Wether the parent is longlived or not.
+ *
+ * @return              0 on success, -1 on error.
+ */
+int tdb_reopen_all(int parent_longlived);
+
+/**
+ * @brief Set a different tdb logging function.
+ *
+ * @param[in]  tdb      The tdb to set the logging function.
+ *
+ * @param[in]  log_ctx  The logging function to set.
+ */
+void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx);
+
+/**
+ * @brief Get the tdb last error code.
+ *
+ * @param[in]  tdb      The tdb to get the error code from.
+ *
+ * @return              A TDB_ERROR code.
+ *
+ * @see TDB_ERROR
+ */
+enum TDB_ERROR tdb_error(struct tdb_context *tdb);
+
+/**
+ * @brief Get a error string for the last tdb error
+ *
+ * @param[in]  tdb      The tdb to get the error code from.
+ *
+ * @return              An error string.
+ */
+const char *tdb_errorstr(struct tdb_context *tdb);
+
+/**
+ * @brief Fetch an entry in the database given a key.
+ *
+ * The caller must free the resulting data.
+ *
+ * @param[in]  tdb      The tdb to fetch the key.
+ *
+ * @param[in]  key      The key to fetch.
+ *
+ * @return              The key entry found in the database, NULL on error with
+ *                      TDB_ERROR set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Hand a record to a parser function without allocating it.
+ *
+ * This function is meant as a fast tdb_fetch alternative for large records
+ * that are frequently read. The "key" and "data" arguments point directly
+ * into the tdb shared memory, they are not aligned at any boundary.
+ *
+ * @warning The parser is called while tdb holds a lock on the record. DO NOT
+ * call other tdb routines from within the parser. Also, for good performance
+ * you should make the parser fast to allow parallel operations.
+ *
+ * @param[in]  tdb      The tdb to parse the record.
+ *
+ * @param[in]  key      The key to parse.
+ *
+ * @param[in]  parser   The parser to use to parse the data.
+ *
+ * @param[in]  private_data A private data pointer which is passed to the parser
+ *                          function.
+ *
+ * @return              -1 if the record was not found. If the record was found,
+ *                      the return value of "parser" is passed up to the caller.
+ */
+int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
                              int (*parser)(TDB_DATA key, TDB_DATA data,
                                            void *private_data),
                              void *private_data);
-_PUBLIC_ int tdb_delete(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
-_PUBLIC_ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf);
-_PUBLIC_ int tdb_close(struct tdb_context *tdb);
-_PUBLIC_ TDB_DATA tdb_firstkey(struct tdb_context *tdb);
-_PUBLIC_ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *);
-_PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *);
-_PUBLIC_ int tdb_exists(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_lockall(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_nonblock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_unlockall(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_read(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_read_nonblock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_unlockall_read(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_mark(struct tdb_context *tdb);
-_PUBLIC_ int tdb_lockall_unmark(struct tdb_context *tdb);
-_PUBLIC_ const char *tdb_name(struct tdb_context *tdb);
-_PUBLIC_ int tdb_fd(struct tdb_context *tdb);
-_PUBLIC_ tdb_log_func tdb_log_fn(struct tdb_context *tdb);
-_PUBLIC_ void *tdb_get_logging_private(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_start(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_start_nonblock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_prepare_commit(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_cancel(struct tdb_context *tdb);
-_PUBLIC_ int tdb_get_seqnum(struct tdb_context *tdb);
-_PUBLIC_ int tdb_hash_size(struct tdb_context *tdb);
-_PUBLIC_ size_t tdb_map_size(struct tdb_context *tdb);
-_PUBLIC_ int tdb_get_flags(struct tdb_context *tdb);
-_PUBLIC_ void tdb_add_flags(struct tdb_context *tdb, unsigned flag);
-_PUBLIC_ void tdb_remove_flags(struct tdb_context *tdb, unsigned flag);
-_PUBLIC_ void tdb_enable_seqnum(struct tdb_context *tdb);
-_PUBLIC_ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb);
-_PUBLIC_ unsigned int tdb_jenkins_hash(TDB_DATA *key);
-_PUBLIC_ int tdb_check(struct tdb_context *tdb,
-             int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
+
+/**
+ * @brief Delete an entry in the database given a key.
+ *
+ * @param[in]  tdb      The tdb to delete the key.
+ *
+ * @param[in]  key      The key to delete.
+ *
+ * @return              0 on success, -1 if the key doesn't exist.
+ */
+int tdb_delete(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Store an element in the database.
+ *
+ * This replaces any existing element with the same key.
+ *
+ * @param[in]  tdb      The tdb to store the entry.
+ *
+ * @param[in]  key      The key to use to store the entry.
+ *
+ * @param[in]  dbuf     The data to store under the key.
+ *
+ * @param[in]  flag     The flags to store the key:\n\n
+ *                      TDB_INSERT: Don't overwrite an existing entry.\n
+ *                      TDB_MODIFY: Don't create a new entry\n
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+
+/**
+ * @brief Append data to an entry.
+ *
+ * If the entry doesn't exist, it will create a new one.
+ *
+ * @param[in]  tdb      The database to use.
+ *
+ * @param[in]  key      The key to append the data.
+ *
+ * @param[in]  new_dbuf The data to append to the key.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf);
+
+/**
+ * @brief Close a database.
+ *
+ * @param[in]  tdb      The database to close.
+ *
+ * @return              0 for success, -1 on error.
+ */
+int tdb_close(struct tdb_context *tdb);
+
+/**
+ * @brief Find the first entry in the database and return its key.
+ *
+ * The caller must free the returned data.
+ *
+ * @param[in]  tdb      The database to use.
+ *
+ * @return              The first entry of the database, an empty TDB_DATA entry
+ *                      if the database is empty.
+ */
+TDB_DATA tdb_firstkey(struct tdb_context *tdb);
+
+/**
+ * @brief Find the next entry in the database, returning its key.
+ *
+ * The caller must free the returned data.
+ *
+ * @param[in]  tdb      The database to use.
+ *
+ * @param[in]  key      The key from which you want the next key.
+ *
+ * @return              The next entry of the current key, an empty TDB_DATA
+ *                      entry if there is no entry.
+ */
+TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Traverse the entire database.
+ *
+ * While travering the function fn(tdb, key, data, state) is called on each
+ * element. If fn is NULL then it is not called. A non-zero return value from
+ * fn() indicates that the traversal should stop. Traversal callbacks may not
+ * start transactions.
+ *
+ * @warning The data buffer given to the callback fn does NOT meet the alignment
+ * restrictions malloc gives you.
+ *
+ * @param[in]  tdb      The database to traverse.
+ *
+ * @param[in]  fn       The function to call on each entry.
+ *
+ * @param[in]  private_data The private data which should be passed to the
+ *                          traversing function.
+ *
+ * @return              The record count traversed, -1 on error.
+ */
+int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data);
+
+/**
+ * @brief Traverse the entire database.
+ *
+ * While traversing the database the function fn(tdb, key, data, state) is
+ * called on each element, but marking the database read only during the
+ * traversal, so any write operations will fail. This allows tdb to use read
+ * locks, which increases the parallelism possible during the traversal.
+ *
+ * @param[in]  tdb      The database to traverse.
+ *
+ * @param[in]  fn       The function to call on each entry.
+ *
+ * @param[in]  private_data The private data which should be passed to the
+ *                          traversing function.
+ *
+ * @return              The record count traversed, -1 on error.
+ */
+int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data);
+
+/**
+ * @brief Check if an entry in the database exists.
+ *
+ * @note 1 is returned if the key is found and 0 is returned if not found this
+ * doesn't match the conventions in the rest of this module, but is compatible
+ * with gdbm.
+ *
+ * @param[in]  tdb      The database to check if the entry exists.
+ *
+ * @param[in]  key      The key to check if the entry exists.
+ *
+ * @return              1 if the key is found, 0 if not.
+ */
+int tdb_exists(struct tdb_context *tdb, TDB_DATA key);
+
+/**
+ * @brief Lock entire database with a write lock.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with a write lock.
+ *
+ * This is the non-blocking call.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Unlock entire database with write lock.
+ *
+ * @param[in]  tdb      The database to unlock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_unlockall(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with a read lock.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_read(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with a read lock.
+ *
+ * This is the non-blocking call.
+ *
+ * @param[in]  tdb      The database to lock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall_read()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_read_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Unlock entire database with read lock.
+ *
+ * @param[in]  tdb      The database to unlock.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_lockall_read()
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_unlockall_read(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with write lock - mark only.
+ *
+ * @todo Add more details.
+ *
+ * @param[in]  tdb      The database to mark.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_mark(struct tdb_context *tdb);
+
+/**
+ * @brief Lock entire database with write lock - unmark only.
+ *
+ * @todo Add more details.
+ *
+ * @param[in]  tdb      The database to mark.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_lockall_unmark(struct tdb_context *tdb);
+
+/**
+ * @brief Get the name of the current tdb file.
+ *
+ * This is useful for external logging functions.
+ *
+ * @param[in]  tdb      The database to get the name from.
+ *
+ * @return              The name of the database.
+ */
+const char *tdb_name(struct tdb_context *tdb);
+
+/**
+ * @brief Get the underlying file descriptor being used by tdb.
+ *
+ * This is useful for external routines that want to check the device/inode
+ * of the fd.
+ *
+ * @param[in]  tdb      The database to get the fd from.
+ *
+ * @return              The file descriptor or -1.
+ */
+int tdb_fd(struct tdb_context *tdb);
+
+/**
+ * @brief Get the current logging function.
+ *
+ * This is useful for external tdb routines that wish to log tdb errors.
+ *
+ * @param[in]  tdb      The database to get the logging function from.
+ *
+ * @return              The logging function of the database.
+ *
+ * @see tdb_get_logging_private()
+ */
+tdb_log_func tdb_log_fn(struct tdb_context *tdb);
+
+/**
+ * @brief Get the private data of the logging function.
+ *
+ * @param[in]  tdb      The database to get the data from.
+ *
+ * @return              The private data pointer of the logging function.
+ *
+ * @see tdb_log_fn()
+ */
+void *tdb_get_logging_private(struct tdb_context *tdb);
+
+/**
+ * @brief Start a transaction.
+ *
+ * All operations after the transaction start can either be committed with
+ * tdb_transaction_commit() or cancelled with tdb_transaction_cancel().
+ *
+ * If you call tdb_transaction_start() again on the same tdb context while a
+ * transaction is in progress, then the same transaction buffer is re-used. The
+ * number of tdb_transaction_{commit,cancel} operations must match the number
+ * of successful tdb_transaction_start() calls.
+ *
+ * Note that transactions are by default disk synchronous, and use a recover
+ * area in the database to automatically recover the database on the next open
+ * if the system crashes during a transaction. You can disable the synchronous
+ * transaction recovery setup using the TDB_NOSYNC flag, which will greatly
+ * speed up operations at the risk of corrupting your database if the system
+ * crashes.
+ *
+ * Operations made within a transaction are not visible to other users of the
+ * database until a successful commit.
+ *
+ * @param[in]  tdb      The database to start the transaction.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_start(struct tdb_context *tdb);
+
+/**
+ * @brief Start a transaction, non-blocking.
+ *
+ * @param[in]  tdb      The database to start the transaction.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ * @see tdb_transaction_start()
+ */
+int tdb_transaction_start_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Prepare to commit a current transaction, for two-phase commits.
+ *
+ * Once prepared for commit, the only allowed calls are tdb_transaction_commit()
+ * or tdb_transaction_cancel(). Preparing allocates disk space for the pending
+ * updates, so a subsequent commit should succeed (barring any hardware
+ * failures).
+ *
+ * @param[in]  tdb      The database to prepare the commit.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_prepare_commit(struct tdb_context *tdb);
+
+/**
+ * @brief Commit a current transaction.
+ *
+ * This updates the database and releases the current transaction locks.
+ *
+ * @param[in]  tdb      The database to commit the transaction.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_commit(struct tdb_context *tdb);
+
+/**
+ * @brief Cancel a current transaction.
+ *
+ * This discards all write and lock operations that have been made since the
+ * transaction started.
+ *
+ * @param[in]  tdb      The tdb to cancel the transaction on.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_transaction_cancel(struct tdb_context *tdb);
+
+/**
+ * @brief Get the tdb sequence number.
+ *
+ * Only makes sense if the writers opened with TDB_SEQNUM set. Note that this
+ * sequence number will wrap quite quickly, so it should only be used for a
+ * 'has something changed' test, not for code that relies on the count of the
+ * number of changes made. If you want a counter then use a tdb record.
+ *
+ * The aim of this sequence number is to allow for a very lightweight test of a
+ * possible tdb change.
+ *
+ * @param[in]  tdb      The database to get the sequence number from.
+ *
+ * @return              The sequence number or 0.
+ *
+ * @see tdb_open()
+ * @see tdb_enable_seqnum()
+ */
+int tdb_get_seqnum(struct tdb_context *tdb);
+
+/**
+ * @brief Get the hash size.
+ *
+ * @param[in]  tdb      The database to get the hash size from.
+ *
+ * @return              The hash size.
+ */
+int tdb_hash_size(struct tdb_context *tdb);
+
+/**
+ * @brief Get the map size.
+ *
+ * @param[in]  tdb     The database to get the map size from.
+ *
+ * @return             The map size.
+ */
+size_t tdb_map_size(struct tdb_context *tdb);
+
+/**
+ * @brief Get the tdb flags set during open.
+ *
+ * @param[in]  tdb      The database to get the flags form.
+ *
+ * @return              The flags set to on the database.
+ */
+int tdb_get_flags(struct tdb_context *tdb);
+
+/**
+ * @brief Add flags to the database.
+ *
+ * @param[in]  tdb      The database to add the flags.
+ *
+ * @param[in]  flag     The tdb flags to add.
+ */
+void tdb_add_flags(struct tdb_context *tdb, unsigned flag);
+
+/**
+ * @brief Remove flags from the database.
+ *
+ * @param[in]  tdb      The database to remove the flags.
+ *
+ * @param[in]  flag     The tdb flags to remove.
+ */
+void tdb_remove_flags(struct tdb_context *tdb, unsigned flag);
+
+/**
+ * @brief Enable sequence number handling on an open tdb.
+ *
+ * @param[in]  tdb      The database to enable sequence number handling.
+ *
+ * @see tdb_get_seqnum()
+ */
+void tdb_enable_seqnum(struct tdb_context *tdb);
+
+/**
+ * @brief Increment the tdb sequence number.
+ *
+ * This only works if the tdb has been opened using the TDB_SEQNUM flag or
+ * enabled useing tdb_enable_seqnum().
+ *
+ * @param[in]  tdb      The database to increment the sequence number.
+ *
+ * @see tdb_enable_seqnum()
+ * @see tdb_get_seqnum()
+ */
+void tdb_increment_seqnum_nonblock(struct tdb_context *tdb);
+
+/**
+ * @brief Create a hash of the key.
+ *
+ * @param[in]  key      The key to hash
+ *
+ * @return              The hash.
+ */
+unsigned int tdb_jenkins_hash(TDB_DATA *key);
+
+/**
+ * @brief Check the consistency of the database.
+ *
+ * This check the consistency of the database calling back the check function
+ * (if non-NULL) on each record.  If some consistency check fails, or the
+ * supplied check function returns -1, tdb_check returns -1, otherwise 0.
+ *
+ * @note The logging function (if set) will be called with additional
+ * information on the corruption found.
+ *
+ * @param[in]  tdb      The database to check.
+ *
+ * @param[in]  check    The check function to use.
+ *
+ * @param[in]  private_data the private data to pass to the check function.
+ *
+ * @return              0 on success, -1 on error with error code set.
+ *
+ * @see tdb_error()
+ * @see tdb_errorstr()
+ */
+int tdb_check(struct tdb_context *tdb,
+             int (*check) (TDB_DATA key, TDB_DATA data, void *private_data),
              void *private_data);
 
+/* @} ******************************************************************/
+
 /* Low level locking functions: use with care */
-_PUBLIC_ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
-_PUBLIC_ int tdb_transaction_write_lock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_write_unlock(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_write_lock_mark(struct tdb_context *tdb);
-_PUBLIC_ int tdb_transaction_write_lock_unmark(struct tdb_context *tdb);
-
-_PUBLIC_ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
+int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
+int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
+
+void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
 
 /* wipe and repack */
-_PUBLIC_ int tdb_wipe_all(struct tdb_context *tdb);
-_PUBLIC_ int tdb_repack(struct tdb_context *tdb);
+int tdb_wipe_all(struct tdb_context *tdb);
+int tdb_repack(struct tdb_context *tdb);
 
 /* Debug functions. Not used in production. */
-_PUBLIC_ void tdb_dump_all(struct tdb_context *tdb);
-_PUBLIC_ int tdb_printfreelist(struct tdb_context *tdb);
-_PUBLIC_ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries);
-_PUBLIC_ int tdb_freelist_size(struct tdb_context *tdb);
+void tdb_dump_all(struct tdb_context *tdb);
+int tdb_printfreelist(struct tdb_context *tdb);
+int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries);
+int tdb_freelist_size(struct tdb_context *tdb);
+char *tdb_summary(struct tdb_context *tdb);
 
-_PUBLIC_ extern TDB_DATA tdb_null;
+extern TDB_DATA tdb_null;
 
 #ifdef  __cplusplus
 }
index b857438e16ec0ef6185be78f0a28275be512f6a1..48927522f3a8477d565b34ddec960c6e675d4aea 100644 (file)
@@ -9,7 +9,7 @@
      ** NOTE! The following LGPL license applies to the tdb
      ** library. This does NOT imply that all of Samba is released
      ** under the LGPL
-   
+
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
@@ -41,7 +41,7 @@ typedef struct {
        bool closed;
 } PyTdbObject;
 
-PyAPI_DATA(PyTypeObject) PyTdb;
+staticforward PyTypeObject PyTdb;
 
 static void PyErr_SetTDBError(TDB_CONTEXT *tdb)
 {
@@ -479,7 +479,7 @@ static void tdb_object_dealloc(PyTdbObject *self)
 {
        if (!self->closed)
                tdb_close(self->ctx);
-       PyObject_Del(self);
+       self->ob_type->tp_free(self);
 }
 
 static PyObject *obj_getitem(PyTdbObject *self, PyObject *key)
@@ -538,8 +538,8 @@ static PyMappingMethods tdb_object_mapping = {
        .mp_subscript = (binaryfunc)obj_getitem,
        .mp_ass_subscript = (objobjargproc)obj_setitem,
 };
-PyTypeObject PyTdb = {
-       .tp_name = "Tdb",
+static PyTypeObject PyTdb = {
+       .tp_name = "tdb.Tdb",
        .tp_basicsize = sizeof(PyTdbObject),
        .tp_methods = tdb_object_methods,
        .tp_getset = tdb_object_getsetters,
@@ -558,6 +558,7 @@ static PyMethodDef tdb_methods[] = {
        { NULL }
 };
 
+void inittdb(void);
 void inittdb(void)
 {
        PyObject *m;
@@ -568,7 +569,8 @@ void inittdb(void)
        if (PyType_Ready(&PyTdbIterator) < 0)
                return;
 
-       m = Py_InitModule3("tdb", tdb_methods, "TDB is a simple key-value database similar to GDBM that supports multiple writers.");
+       m = Py_InitModule3("tdb", tdb_methods,
+               "simple key-value database that supports multiple writers.");
        if (m == NULL)
                return;
 
index 615de494b51c83ae145bf991913f11e5c7b181da..2877092fe31615d96af1d8167334b183215d1006 100644 (file)
@@ -12,13 +12,21 @@ import os, tempfile
 
 
 class OpenTdbTests(TestCase):
+
     def test_nonexistant_read(self):
-        self.assertRaises(IOError, tdb.Tdb, "/some/nonexistant/file", 0, tdb.DEFAULT, os.O_RDWR)
+        self.assertRaises(IOError, tdb.Tdb, "/some/nonexistant/file", 0,
+                tdb.DEFAULT, os.O_RDWR)
 
 class CloseTdbTests(TestCase):
 
     def test_double_close(self):
-        self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR)
+        # No hash size in tdb2.
+        if tdb.__version__.startswith("2"):
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
+        else:
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
         self.assertNotEqual(None, self.tdb)
 
         # ensure that double close does not crash python
@@ -36,9 +44,15 @@ class InternalTdbTests(TestCase):
 
 
 class SimpleTdbTests(TestCase):
+
     def setUp(self):
         super(SimpleTdbTests, self).setUp()
-        self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR)
+        if tdb.__version__.startswith("2"):
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
+        else:
+            self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT,
+                               os.O_CREAT|os.O_RDWR)
         self.assertNotEqual(None, self.tdb)
 
     def tearDown(self):
@@ -51,7 +65,8 @@ class SimpleTdbTests(TestCase):
         self.tdb.lock_all()
 
     def test_max_dead(self):
-        self.tdb.max_dead = 20
+        if not tdb.__version__.startswith("2"):
+            self.tdb.max_dead = 20
 
     def test_unlockall(self):
         self.tdb.lock_all()
@@ -62,7 +77,8 @@ class SimpleTdbTests(TestCase):
         self.tdb.read_unlock_all()
 
     def test_reopen(self):
-        self.tdb.reopen()
+        if not tdb.__version__.startswith("2"):
+            self.tdb.reopen()
 
     def test_store(self):
         self.tdb.store("bar", "bla")
@@ -70,7 +86,8 @@ class SimpleTdbTests(TestCase):
 
     def test_getitem(self):
         self.tdb["bar"] = "foo"
-        self.tdb.reopen()
+        if not tdb.__version__.startswith("2"):
+            self.tdb.reopen()
         self.assertEquals("foo", self.tdb["bar"])
 
     def test_delete(self):
@@ -86,13 +103,16 @@ class SimpleTdbTests(TestCase):
         self.assertRaises(KeyError, lambda: self.tdb["bla"])
 
     def test_hash_size(self):
-        self.tdb.hash_size
+        if not tdb.__version__.startswith("2"):
+            self.tdb.hash_size
 
     def test_map_size(self):
-        self.tdb.map_size
+        if not tdb.__version__.startswith("2"):
+            self.tdb.map_size
 
     def test_freelist_size(self):
-        self.tdb.freelist_size
+        if not tdb.__version__.startswith("2"):
+            self.tdb.freelist_size
 
     def test_name(self):
         self.tdb.filename
@@ -100,7 +120,9 @@ class SimpleTdbTests(TestCase):
     def test_iterator(self):
         self.tdb["bla"] = "1"
         self.tdb["brainslug"] = "2"
-        self.assertEquals(["bla", "brainslug"], list(self.tdb))
+        l = list(self.tdb)
+        l.sort()
+        self.assertEquals(["bla", "brainslug"], l)
 
     def test_transaction_cancel(self):
         self.tdb["bloe"] = "2"
@@ -138,17 +160,19 @@ class SimpleTdbTests(TestCase):
         self.assertEquals(0, len(list(self.tdb)))
 
     def test_repack(self):
-        self.tdb["foo"] = "abc"
-        self.tdb["bar"] = "def"
-        del self.tdb["foo"]
-        self.tdb.repack()
+        if not tdb.__version__.startswith("2"):
+            self.tdb["foo"] = "abc"
+            self.tdb["bar"] = "def"
+            del self.tdb["foo"]
+            self.tdb.repack()
 
     def test_seqnum(self):
-        self.tdb.enable_seqnum()
-        seq1 = self.tdb.seqnum
-        self.tdb.increment_seqnum_nonblock()
-        seq2 = self.tdb.seqnum
-        self.assertEquals(seq2-seq1, 1)
+        if not tdb.__version__.startswith("2"):
+            self.tdb.enable_seqnum()
+            seq1 = self.tdb.seqnum
+            self.tdb.increment_seqnum_nonblock()
+            seq2 = self.tdb.seqnum
+            self.assertEquals(seq2-seq1, 1)
 
     def test_len(self):
         self.assertEquals(0, len(list(self.tdb)))
@@ -156,8 +180,12 @@ class SimpleTdbTests(TestCase):
         self.assertEquals(1, len(list(self.tdb)))
 
     def test_add_flags(self):
-        self.tdb.add_flags(tdb.NOMMAP)
-        self.tdb.remove_flags(tdb.NOMMAP)
+        if tdb.__version__.startswith("2"):
+            self.tdb.add_flag(tdb.NOMMAP)
+            self.tdb.remove_flag(tdb.NOMMAP)
+        else:
+            self.tdb.add_flags(tdb.NOMMAP)
+            self.tdb.remove_flags(tdb.NOMMAP)
 
 
 class VersionTests(TestCase):
index 6aca8dd99c50bfdcef1c26cd2bc4bd2e154fcaaa..11ecaa0290aee983048da5a454cf71f0176ea794 100644 (file)
@@ -152,8 +152,9 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
                return 1;
        }
 
-       if (tdb_transaction_start(tdb_new) != 0) {
-               printf("Failed to start transaction on new tdb\n");
+       /* lock the backup tdb so that nobody else can change it */
+       if (tdb_lockall(tdb_new) != 0) {
+               printf("Failed to lock backup tdb\n");
                tdb_close(tdb);
                tdb_close(tdb_new);
                unlink(tmp_name);
@@ -177,12 +178,16 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
        /* close the old tdb */
        tdb_close(tdb);
 
-       if (tdb_transaction_commit(tdb_new) != 0) {
-               fprintf(stderr, "Failed to commit new tdb\n");
-               tdb_close(tdb_new);
-               unlink(tmp_name);
-               free(tmp_name);         
-               return 1;
+       /* copy done, unlock the backup tdb */
+       tdb_unlockall(tdb_new);
+
+#ifdef HAVE_FDATASYNC
+       if (fdatasync(tdb_fd(tdb_new)) != 0) {
+#else
+       if (fsync(tdb_fd(tdb_new)) != 0) {
+#endif
+               /* not fatal */
+               fprintf(stderr, "failed to fsync backup file\n");
        }
 
        /* close the new tdb and re-open read-only */
index 485c440df1f9c871078ce889adab94cc98ccbb47..8106cf9b06a55794cb6b3b489da54596fa315dd5 100644 (file)
@@ -5,7 +5,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include <assert.h>
@@ -39,7 +38,7 @@ static int read_linehead(FILE *f)
                if (c == EOF) {
                        return -1;
                }
-               if (c == '\(') {
+               if (c == '(') {
                        break;
                }
        }
@@ -171,7 +170,7 @@ static int read_rec(FILE *f, TDB_CONTEXT *tdb, int *eof)
            || (swallow(f, "}\n", NULL) == -1)) {
                goto fail;
        }
-       if (tdb_store(tdb, key, data, TDB_INSERT) == -1) {
+       if (tdb_store(tdb, key, data, TDB_INSERT) != 0) {
                fprintf(stderr, "TDB error: %s\n", tdb_errorstr(tdb));
                goto fail;
        }
@@ -207,7 +206,6 @@ static int restore_tdb(const char *fname)
                fprintf(stderr, "Error closing tdb\n");
                return 1;
        }
-       fprintf(stderr, "EOF\n");
        return 0;
 }
 
index 416bc50a5b073c07fad11c2d10a7a08fdb9253ae..44c78efda5cf27fa2e7ee85f330d37a0c006858d 100644 (file)
@@ -215,16 +215,38 @@ static void merge_test(void)
        tdb_delete(db, key);
 }
 
+static char *test_path(const char *filename)
+{
+       const char *prefix = getenv("TEST_DATA_PREFIX");
+
+       if (prefix) {
+               char *path = NULL;
+               int ret;
+
+               ret = asprintf(&path, "%s/%s", prefix, filename);
+               if (ret == -1) {
+                       return NULL;
+               }
+               return path;
+       }
+
+       return strdup(filename);
+}
+
  int main(int argc, const char *argv[])
 {
        int i, seed=0;
        int loops = 10000;
        int num_entries;
-       char test_gdbm[] = "test.gdbm";
+       char test_gdbm[1] = "test.gdbm";
+       char *test_tdb;
 
-       unlink("test.gdbm");
+       test_gdbm[0] = test_path("test.gdbm");
+       test_tdb = test_path("test.tdb");
 
-       db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST, 
+       unlink(test_gdbm[0]);
+
+       db = tdb_open(test_tdb, 0, TDB_CLEAR_IF_FIRST,
                      O_RDWR | O_CREAT | O_TRUNC, 0600);
        gdbm = gdbm_open(test_gdbm, 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, 
                         0600, NULL);
@@ -261,5 +283,8 @@ static void merge_test(void)
        tdb_close(db);
        gdbm_close(gdbm);
 
+       free(test_gdbm[0]);
+       free(test_tdb);
+
        return 0;
 }
index b380883e0ac247e18b4b324fd50cdd6e8e4830d0..dc5747f87aa1634bdb20d9d9d79d0e476a151f55 100644 (file)
@@ -61,6 +61,7 @@ enum commands {
        CMD_NEXT,
        CMD_SYSTEM,
        CMD_CHECK,
+       CMD_REPACK,
        CMD_QUIT,
        CMD_HELP
 };
@@ -98,6 +99,7 @@ COMMAND_TABLE cmd_table[] = {
        {"quit",        CMD_QUIT},
        {"q",           CMD_QUIT},
        {"!",           CMD_SYSTEM},
+       {"repack",      CMD_REPACK},
        {NULL,          CMD_HELP}
 };
 
@@ -203,6 +205,7 @@ static void help(void)
 "  list                 : print the database hash table and freelist\n"
 "  free                 : print the database freelist\n"
 "  check                : check the integrity of an opened database\n"
+"  repack               : repack the database\n"
 "  speed                : perform speed tests on the database\n"
 "  ! command            : execute system command\n"
 "  1 | first            : print the first record\n"
@@ -257,7 +260,7 @@ static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
        dbuf.dptr = (unsigned char *)data;
        dbuf.dsize = datalen;
 
-       if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+       if (tdb_store(tdb, key, dbuf, TDB_INSERT) != 0) {
                terror("insert failed");
        }
 }
@@ -284,7 +287,7 @@ static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
        printf("Storing key:\n");
        print_rec(tdb, key, dbuf, NULL);
 
-       if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+       if (tdb_store(tdb, key, dbuf, TDB_REPLACE) != 0) {
                terror("store failed");
        }
 }
@@ -363,7 +366,7 @@ static void move_rec(char *keyname, size_t keylen, char* tdbname)
                return;
        }
        
-       if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
+       if (tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) != 0) {
                terror("failed to move record");
        }
        else
@@ -409,12 +412,14 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *
 
 static void info_tdb(void)
 {
-       int count;
-       total_bytes = 0;
-       if ((count = tdb_traverse(tdb, traverse_fn, NULL)) == -1)
+       char *summary = tdb_summary(tdb);
+
+       if (!summary) {
                printf("Error = %s\n", tdb_errorstr(tdb));
-       else
-               printf("%d records totalling %d bytes\n", count, total_bytes);
+       } else {
+               printf("%s", summary);
+               free(summary);
+       }
 }
 
 static void speed_tdb(const char *tlimit)
@@ -445,12 +450,9 @@ static void speed_tdb(const char *tlimit)
        printf("Testing fetch speed for %u seconds\n", timelimit);
        _start_timer();
        do {
-               long int r = random();
-               TDB_DATA key, dbuf;
+               TDB_DATA key;
                key.dptr = discard_const_p(uint8_t, str);
                key.dsize = strlen((char *)key.dptr);
-               dbuf.dptr = (uint8_t *) &r;
-               dbuf.dsize = sizeof(r);
                tdb_fetch(tdb, key);
                t = _end_timer();
                ops++;
@@ -538,9 +540,9 @@ static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
                print_rec(the_tdb, *pkey, dbuf, NULL);
 }
 
-static int count(TDB_DATA key, TDB_DATA data, void *private)
+static int count(TDB_DATA key, TDB_DATA data, void *private_data)
 {
-       (*(unsigned int *)private)++;
+       (*(unsigned int *)private_data)++;
        return 0;
 }
 
@@ -609,6 +611,10 @@ static int do_command(void)
                        bIterate = 0;
                        tdb_transaction_commit(tdb);
                        return 0;
+               case CMD_REPACK:
+                       bIterate = 0;
+                       tdb_repack(tdb);
+                       return 0;
                case CMD_TRANSACTION_CANCEL:
                        bIterate = 0;
                        tdb_transaction_cancel(tdb);
@@ -691,7 +697,7 @@ static int do_command(void)
        return 0;
 }
 
-static char *convert_string(char *instring, size_t *sizep)
+static char *tdb_convert_string(char *instring, size_t *sizep)
 {
        size_t length = 0;
        char *outp, *inp;
@@ -757,15 +763,15 @@ int main(int argc, char *argv[])
                                        }
                                }
                        }
-                       if (arg1) arg1 = convert_string(arg1,&arg1len);
-                       if (arg2) arg2 = convert_string(arg2,&arg2len);
+                       if (arg1) arg1 = tdb_convert_string(arg1,&arg1len);
+                       if (arg2) arg2 = tdb_convert_string(arg2,&arg2len);
                        if (do_command()) break;
                }
                break;
        case 5:
-               arg2 = convert_string(argv[4],&arg2len);
+               arg2 = tdb_convert_string(argv[4],&arg2len);
        case 4:
-               arg1 = convert_string(argv[3],&arg1len);
+               arg1 = tdb_convert_string(argv[3],&arg1len);
        case 3:
                cmdname = argv[2];
        default:
index 79fe3cd5e0e9ba6dd9c95b882770b5f2fde37f43..64c504344120708c4d015c10c0fe5811adb92e6e 100644 (file)
@@ -228,9 +228,9 @@ static void send_count_and_suicide(int sig)
        kill(getpid(), SIGUSR2);
 }
 
-static int run_child(int i, int seed, unsigned num_loops, unsigned start)
+static int run_child(const char *filename, int i, int seed, unsigned num_loops, unsigned start)
 {
-       db = tdb_open_ex("torture.tdb", hash_size, TDB_DEFAULT,
+       db = tdb_open_ex(filename, hash_size, TDB_DEFAULT,
                         O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
        if (!db) {
                fatal("db open failed");
@@ -270,6 +270,24 @@ static int run_child(int i, int seed, unsigned num_loops, unsigned start)
        return (error_count < 100 ? error_count : 100);
 }
 
+static char *test_path(const char *filename)
+{
+       const char *prefix = getenv("TEST_DATA_PREFIX");
+
+       if (prefix) {
+               char *path = NULL;
+               int ret;
+
+               ret = asprintf(&path, "%s/%s", prefix, filename);
+               if (ret == -1) {
+                       return NULL;
+               }
+               return path;
+       }
+
+       return strdup(filename);
+}
+
 int main(int argc, char * const *argv)
 {
        int i, seed = -1;
@@ -280,6 +298,7 @@ int main(int argc, char * const *argv)
        pid_t *pids;
        int kill_random = 0;
        int *done;
+       char *test_tdb;
 
        log_ctx.log_fn = tdb_log;
 
@@ -308,7 +327,9 @@ int main(int argc, char * const *argv)
                }
        }
 
-       unlink("torture.tdb");
+       test_tdb = test_path("torture.tdb");
+
+       unlink(test_tdb);
 
        if (seed == -1) {
                seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
@@ -316,7 +337,7 @@ int main(int argc, char * const *argv)
 
        if (num_procs == 1 && !kill_random) {
                /* Don't fork for this case, makes debugging easier. */
-               error_count = run_child(0, seed, num_loops, 0);
+               error_count = run_child(test_tdb, 0, seed, num_loops, 0);
                goto done;
        }
 
@@ -336,7 +357,7 @@ int main(int argc, char * const *argv)
                                printf("Testing with %d processes, %d loops, %d hash_size, seed=%d%s\n",
                                       num_procs, num_loops, hash_size, seed, always_transaction ? " (all within transactions)" : "");
                        }
-                       exit(run_child(i, seed, num_loops, 0));
+                       exit(run_child(test_tdb, i, seed, num_loops, 0));
                }
        }
 
@@ -389,8 +410,8 @@ int main(int argc, char * const *argv)
                                }
                                pids[j] = fork();
                                if (pids[j] == 0)
-                                       exit(run_child(j, seed, num_loops,
-                                                      done[j]));
+                                       exit(run_child(test_tdb, j, seed,
+                                                      num_loops, done[j]));
                                printf("Restarting child %i for %u-%u\n",
                                       j, done[j], num_loops);
                                continue;
@@ -414,7 +435,7 @@ int main(int argc, char * const *argv)
 
 done:
        if (error_count == 0) {
-               db = tdb_open_ex("torture.tdb", hash_size, TDB_DEFAULT,
+               db = tdb_open_ex(test_tdb, hash_size, TDB_DEFAULT,
                                 O_RDWR, 0, &log_ctx, NULL);
                if (!db) {
                        fatal("db open failed");
@@ -427,5 +448,6 @@ done:
                printf("OK\n");
        }
 
+       free(test_tdb);
        return error_count;
 }