]> git.ozlabs.org Git - ccan/blobdiff - ccan/tdb/tdb.c
Fix sequence logic bug, and satisfies() logic fix/cleanup.
[ccan] / ccan / tdb / tdb.c
index 767452c9b34b520a51bed6aace888b3f5b2dcd4f..3acf05b96e15fdebc4ef893139a477907d293a30 100644 (file)
@@ -153,7 +153,7 @@ static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
  * then the TDB_DATA will have zero length but
  * a non-zero pointer
  */
-TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
+static TDB_DATA do_tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
 {
        tdb_off_t rec_ptr;
        struct list_struct rec;
@@ -162,9 +162,9 @@ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
 
        /* find which hash bucket it is in */
        hash = tdb->hash_fn(&key);
-       if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
+       if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
                return tdb_null;
-
+       }
        ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
                                  rec.data_len);
        ret.dsize = rec.data_len;
@@ -172,6 +172,14 @@ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
        return ret;
 }
 
+TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
+{
+       TDB_DATA ret = do_tdb_fetch(tdb, key);
+
+       tdb_trace_1rec_retrec(tdb, "tdb_fetch", key, ret);
+       return ret;
+}
+
 /*
  * Find an entry in the database and hand the record's data to a parsing
  * function. The parsing function is executed under the chain read lock, so it
@@ -202,8 +210,11 @@ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
        hash = tdb->hash_fn(&key);
 
        if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
+               tdb_trace_1rec_ret(tdb, "tdb_parse_record", key,
+                                  -TDB_ERR_NOEXIST);
                return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
        }
+       tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, 0);
 
        ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
                             rec.data_len, parser, private_data);
@@ -232,7 +243,11 @@ static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
 int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
 {
        uint32_t hash = tdb->hash_fn(&key);
-       return tdb_exists_hash(tdb, key, hash);
+       int ret;
+
+       ret = tdb_exists_hash(tdb, key, hash);
+       tdb_trace_1rec_ret(tdb, "tdb_exists", key, ret);
+       return ret;
 }
 
 /* actually delete an entry in the database given the offset */
@@ -387,7 +402,11 @@ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
 int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
 {
        uint32_t hash = tdb->hash_fn(&key);
-       return tdb_delete_hash(tdb, key, hash);
+       int ret;
+
+       ret = tdb_delete_hash(tdb, key, hash);
+       tdb_trace_1rec_ret(tdb, "tdb_delete", key, ret);
+       return ret;
 }
 
 /*
@@ -419,29 +438,14 @@ static tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash,
        return 0;
 }
 
-/* store an element in the database, replacing any existing element
-   with the same key 
-
-   return 0 on success, -1 on failure
-*/
-int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+static int _tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
+                     int flag, uint32_t hash)
 {
        struct list_struct rec;
-       uint32_t hash;
        tdb_off_t rec_ptr;
        char *p = NULL;
        int ret = -1;
 
-       if (tdb->read_only || tdb->traverse_read) {
-               tdb->ecode = TDB_ERR_RDONLY;
-               return -1;
-       }
-
-       /* find which hash bucket it is in */
-       hash = tdb->hash_fn(&key);
-       if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
-               return -1;
-
        /* check for it existing, on insert. */
        if (flag == TDB_INSERT) {
                if (tdb_exists_hash(tdb, key, hash)) {
@@ -557,6 +561,33 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
        }
 
        SAFE_FREE(p); 
+       return ret;
+}
+
+/* store an element in the database, replacing any existing element
+   with the same key 
+
+   return 0 on success, -1 on failure
+*/
+int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+       uint32_t hash;
+       int ret;
+
+       if (tdb->read_only || tdb->traverse_read) {
+               tdb->ecode = TDB_ERR_RDONLY;
+               tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag,
+                                       -TDB_ERR_RDONLY);
+               return -1;
+       }
+
+       /* find which hash bucket it is in */
+       hash = tdb->hash_fn(&key);
+       if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+               return -1;
+
+       ret = _tdb_store(tdb, key, dbuf, flag, hash);
+       tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, ret);
        tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
        return ret;
 }
@@ -574,13 +605,18 @@ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
        if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
                return -1;
 
-       dbuf = tdb_fetch(tdb, key);
+       dbuf = do_tdb_fetch(tdb, key);
 
        if (dbuf.dptr == NULL) {
                dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize);
        } else {
-               unsigned char *new_dptr = (unsigned char *)realloc(dbuf.dptr,
-                                                    dbuf.dsize + new_dbuf.dsize);
+               unsigned int new_len = dbuf.dsize + new_dbuf.dsize;
+               unsigned char *new_dptr;
+
+               /* realloc '0' is special: don't do that. */
+               if (new_len == 0)
+                       new_len = 1;
+               new_dptr = (unsigned char *)realloc(dbuf.dptr, new_len);
                if (new_dptr == NULL) {
                        free(dbuf.dptr);
                }
@@ -595,7 +631,8 @@ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
        memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize);
        dbuf.dsize += new_dbuf.dsize;
 
-       ret = tdb_store(tdb, key, dbuf, 0);
+       ret = _tdb_store(tdb, key, dbuf, 0, hash);
+       tdb_trace_2rec_retrec(tdb, "tdb_append", key, new_dbuf, dbuf);
        
 failed:
        tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
@@ -648,6 +685,7 @@ int tdb_get_seqnum(struct tdb_context *tdb)
        tdb_off_t seqnum=0;
 
        tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
+       tdb_trace_ret(tdb, "tdb_get_seqnum", seqnum);
        return seqnum;
 }
 
@@ -729,6 +767,8 @@ int tdb_wipe_all(struct tdb_context *tdb)
                return -1;
        }
 
+       tdb_trace(tdb, "tdb_wipe_all");
+
        /* see if the tdb has a recovery area, and remember its size
           if so. We don't want to lose this as otherwise each
           tdb_wipe_all() in a transaction will increase the size of
@@ -800,3 +840,143 @@ failed:
        tdb_unlockall(tdb);
        return -1;
 }
+
+#ifdef TDB_TRACE
+static void tdb_trace_write(struct tdb_context *tdb, const char *str)
+{
+       if (write(tdb->tracefd, str, strlen(str)) != strlen(str)) {
+               close(tdb->tracefd);
+               tdb->tracefd = -1;
+       }
+}
+
+static void tdb_trace_start(struct tdb_context *tdb)
+{
+       tdb_off_t seqnum=0;
+       char msg[sizeof(tdb_off_t) * 4];
+
+       tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
+       sprintf(msg, "%u ", seqnum);
+       tdb_trace_write(tdb, msg);
+}
+
+static void tdb_trace_end(struct tdb_context *tdb)
+{
+       tdb_trace_write(tdb, "\n");
+}
+
+static void tdb_trace_end_ret(struct tdb_context *tdb, int ret)
+{
+       char msg[sizeof(ret) * 4];
+       sprintf(msg, " = %i\n", ret);
+       tdb_trace_write(tdb, msg);
+}
+
+static void tdb_trace_record(struct tdb_context *tdb, TDB_DATA rec)
+{
+       char msg[20];
+       unsigned int i;
+
+       /* We differentiate zero-length records from non-existent ones. */
+       if (rec.dptr == NULL) {
+               tdb_trace_write(tdb, " NULL");
+               return;
+       }
+       sprintf(msg, " %zu:", rec.dsize);
+       tdb_trace_write(tdb, msg);
+       for (i = 0; i < rec.dsize; i++) {
+               sprintf(msg, "%02x", rec.dptr[i]);
+               tdb_trace_write(tdb, msg);
+       }
+}
+
+void tdb_trace(struct tdb_context *tdb, const char *op)
+{
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_end(tdb);
+}
+
+void tdb_trace_open(struct tdb_context *tdb, const char *op,
+                   unsigned hash_size, unsigned tdb_flags, unsigned open_flags)
+{
+       char msg[128];
+
+       sprintf(msg, "%s %u %#x %#x", op, hash_size, tdb_flags, open_flags);
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, msg);
+       tdb_trace_end(tdb);
+}
+
+void tdb_trace_ret(struct tdb_context *tdb, const char *op, int ret)
+{
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_end_ret(tdb, ret);
+}
+
+void tdb_trace_retrec(struct tdb_context *tdb, const char *op, TDB_DATA ret)
+{
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_write(tdb, " =");
+       tdb_trace_record(tdb, ret);
+       tdb_trace_end(tdb);
+}
+
+void tdb_trace_1rec(struct tdb_context *tdb, const char *op,
+                   TDB_DATA rec)
+{
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_record(tdb, rec);
+       tdb_trace_end(tdb);
+}
+
+void tdb_trace_1rec_ret(struct tdb_context *tdb, const char *op,
+                       TDB_DATA rec, int ret)
+{
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_record(tdb, rec);
+       tdb_trace_end_ret(tdb, ret);
+}
+
+void tdb_trace_1rec_retrec(struct tdb_context *tdb, const char *op,
+                          TDB_DATA rec, TDB_DATA ret)
+{
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_record(tdb, rec);
+       tdb_trace_write(tdb, " =");
+       tdb_trace_record(tdb, ret);
+       tdb_trace_end(tdb);
+}
+
+void tdb_trace_2rec_flag_ret(struct tdb_context *tdb, const char *op,
+                            TDB_DATA rec1, TDB_DATA rec2, unsigned flag,
+                            int ret)
+{
+       char msg[sizeof(ret) * 4];
+
+       sprintf(msg, " %#x", flag); 
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_record(tdb, rec1);
+       tdb_trace_record(tdb, rec2);
+       tdb_trace_write(tdb, msg);
+       tdb_trace_end_ret(tdb, ret);
+}
+
+void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op,
+                          TDB_DATA rec1, TDB_DATA rec2, TDB_DATA ret)
+{
+       tdb_trace_start(tdb);
+       tdb_trace_write(tdb, op);
+       tdb_trace_record(tdb, rec1);
+       tdb_trace_record(tdb, rec2);
+       tdb_trace_write(tdb, " =");
+       tdb_trace_record(tdb, ret);
+       tdb_trace_end(tdb);
+}
+#endif