]> git.ozlabs.org Git - ccan/blobdiff - ccan/tdb2/check.c
tdb2: remove tdb_get()
[ccan] / ccan / tdb2 / check.c
index 54f96bb0ea8283583faf91693decd8d467970423..1ce56c2c474c892653deff8e79c0f6ca9088daa1 100644 (file)
@@ -77,7 +77,9 @@ static bool check_hash_tree(struct tdb_context *tdb,
                            unsigned hprefix_bits,
                            tdb_off_t used[],
                            size_t num_used,
-                           size_t *num_found);
+                           size_t *num_found,
+                           int (*check)(TDB_DATA, TDB_DATA, void *),
+                           void *private_data);
 
 static bool check_hash_record(struct tdb_context *tdb,
                              tdb_off_t off,
@@ -85,7 +87,9 @@ static bool check_hash_record(struct tdb_context *tdb,
                              unsigned hprefix_bits,
                              tdb_off_t used[],
                              size_t num_used,
-                             size_t *num_found)
+                             size_t *num_found,
+                             int (*check)(TDB_DATA, TDB_DATA, void *),
+                             void *private_data)
 {
        struct tdb_used_record rec;
 
@@ -117,7 +121,7 @@ static bool check_hash_record(struct tdb_context *tdb,
        return check_hash_tree(tdb, off,
                               TDB_SUBLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
                               hprefix, hprefix_bits,
-                              used, num_used, num_found);
+                              used, num_used, num_found, check, private_data);
 }
 
 static int off_cmp(const tdb_off_t *a, const tdb_off_t *b)
@@ -141,7 +145,9 @@ static bool check_hash_tree(struct tdb_context *tdb,
                            unsigned hprefix_bits,
                            tdb_off_t used[],
                            size_t num_used,
-                           size_t *num_found)
+                           size_t *num_found,
+                           int (*check)(TDB_DATA, TDB_DATA, void *),
+                           void *private_data)
 {
        unsigned int g, b;
        const tdb_off_t *hash;
@@ -188,7 +194,8 @@ static bool check_hash_tree(struct tdb_context *tdb,
                                               hprefix_bits
                                                       + group_bits
                                                       + TDB_HASH_GROUP_BITS,
-                                              used, num_used, num_found))
+                                              used, num_used, num_found,
+                                              check, private_data))
                                        goto fail;
                                continue;
                        }
@@ -256,6 +263,22 @@ static bool check_hash_tree(struct tdb_context *tdb,
                                         (long long)rec_hash(&rec));
                                goto fail;
                        }
+
+                       if (check) {
+                               TDB_DATA key, data;
+                               key.dsize = rec_key_length(&rec);
+                               data.dsize = rec_data_length(&rec);
+                               key.dptr = (void *)tdb_access_read(tdb,
+                                                  off + sizeof(rec),
+                                                  key.dsize + data.dsize,
+                                                  false);
+                               if (!key.dptr)
+                                       goto fail;
+                               data.dptr = key.dptr + key.dsize;
+                               if (check(key, data, private_data) != 0)
+                                       goto fail;
+                               tdb_access_release(tdb, key.dptr);
+                       }
                }
        }
        tdb_access_release(tdb, hash);
@@ -268,14 +291,17 @@ fail:
 
 static bool check_hash(struct tdb_context *tdb,
                       tdb_off_t used[],
-                      size_t num_used, size_t num_flists)
+                      size_t num_used, size_t num_flists,
+                      int (*check)(TDB_DATA, TDB_DATA, void *),
+                      void *private_data)
 {
        /* Free lists also show up as used. */
        size_t num_found = num_flists;
 
        if (!check_hash_tree(tdb, offsetof(struct tdb_header, hashtable),
                             TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
-                            0, 0, used, num_used, &num_found))
+                            0, 0, used, num_used, &num_found,
+                            check, private_data))
                return false;
 
        if (num_found != num_used) {
@@ -289,37 +315,37 @@ static bool check_hash(struct tdb_context *tdb,
 static bool check_free(struct tdb_context *tdb,
                       tdb_off_t off,
                       const struct tdb_free_record *frec,
-                      tdb_off_t prev, tdb_off_t flist_off, unsigned int bucket)
+                      tdb_off_t prev, unsigned int flist, unsigned int bucket)
 {
        if (frec_magic(frec) != TDB_FREE_MAGIC) {
                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                         "tdb_check: offset %llu bad magic 0x%llx\n",
-                        (long long)off, (long long)frec->magic_and_meta);
+                        (long long)off, (long long)frec->magic_and_prev);
                return false;
        }
-       if (frec_flist(frec) != flist_off) {
+       if (frec_flist(frec) != flist) {
                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
-                        "tdb_check: offset %llu bad freelist 0x%llx\n",
-                        (long long)off, (long long)frec_flist(frec));
+                        "tdb_check: offset %llu bad freelist %u\n",
+                        (long long)off, frec_flist(frec));
                return false;
        }
 
        if (tdb->methods->oob(tdb, off
-                             + frec->data_len+sizeof(struct tdb_used_record),
+                             + frec_len(frec) + sizeof(struct tdb_used_record),
                              false))
                return false;
-       if (size_to_bucket(frec->data_len) != bucket) {
+       if (size_to_bucket(frec_len(frec)) != bucket) {
                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                         "tdb_check: offset %llu in wrong bucket %u vs %u\n",
                         (long long)off,
-                        bucket, size_to_bucket(frec->data_len));
+                        bucket, size_to_bucket(frec_len(frec)));
                return false;
        }
-       if (prev != frec->prev) {
+       if (prev != frec_prev(frec)) {
                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                         "tdb_check: offset %llu bad prev %llu vs %llu\n",
                         (long long)off,
-                        (long long)prev, (long long)frec->prev);
+                        (long long)prev, (long long)frec_len(frec));
                return false;
        }
        return true;
@@ -327,6 +353,7 @@ static bool check_free(struct tdb_context *tdb,
                       
 static bool check_free_list(struct tdb_context *tdb,
                            tdb_off_t flist_off,
+                           unsigned flist_num,
                            tdb_off_t free[],
                            size_t num_free,
                            size_t *num_found)
@@ -358,7 +385,7 @@ static bool check_free_list(struct tdb_context *tdb,
                                return false;
                        if (tdb_read_convert(tdb, off, &f, sizeof(f)))
                                return false;
-                       if (!check_free(tdb, off, &f, prev, flist_off, i))
+                       if (!check_free(tdb, off, &f, prev, flist_num, i))
                                return false;
 
                        /* FIXME: Check hash bits */
@@ -409,20 +436,23 @@ static bool check_linear(struct tdb_context *tdb,
                        struct tdb_used_record u;
                        struct tdb_free_record f;
                        struct tdb_recovery_record r;
-               } pad, *p;
-               p = tdb_get(tdb, off, &pad, sizeof(pad));
-               if (!p)
+               } rec;
+               /* r is larger: only get that if we need to. */
+               if (tdb_read_convert(tdb, off, &rec, sizeof(rec.f)) == -1)
                        return false;
 
                /* If we crash after ftruncate, we can get zeroes or fill. */
-               if (p->r.magic == TDB_RECOVERY_INVALID_MAGIC
-                   || p->r.magic ==  0x4343434343434343ULL) {
+               if (rec.r.magic == TDB_RECOVERY_INVALID_MAGIC
+                   || rec.r.magic ==  0x4343434343434343ULL) {
+                       if (tdb_read_convert(tdb, off, &rec, sizeof(rec.r)))
+                               return false;
+
                        if (recovery == off) {
                                found_recovery = true;
-                               len = sizeof(p->r) + p->r.max_len;
+                               len = sizeof(rec.r) + rec.r.max_len;
                        } else {
                                len = dead_space(tdb, off);
-                               if (len < sizeof(p->r)) {
+                               if (len < sizeof(rec.r)) {
                                        tdb->log(tdb, TDB_DEBUG_ERROR,
                                                 tdb->log_priv,
                                                 "tdb_check: invalid dead space"
@@ -435,7 +465,9 @@ static bool check_linear(struct tdb_context *tdb,
                                         (size_t)off, (size_t)(off + len),
                                         (size_t)tdb->map_size);
                        }
-               } else if (p->r.magic == TDB_RECOVERY_MAGIC) {
+               } else if (rec.r.magic == TDB_RECOVERY_MAGIC) {
+                       if (tdb_read_convert(tdb, off, &rec, sizeof(rec.r)))
+                               return false;
                        if (recovery != off) {
                                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                                         "tdb_check: unexpected recovery"
@@ -443,11 +475,23 @@ static bool check_linear(struct tdb_context *tdb,
                                         (size_t)off);
                                return false;
                        }
+                       if (rec.r.len > rec.r.max_len) {
+                               tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
+                                        "tdb_check: invalid recovery length"
+                                        " %zu\n", (size_t)rec.r.len);
+                               return false;
+                       }
+                       if (rec.r.eof > tdb->map_size) {
+                               tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
+                                        "tdb_check: invalid old EOF"
+                                        " %zu\n", (size_t)rec.r.eof);
+                               return false;
+                       }
                        found_recovery = true;
-                       len = sizeof(p->r) + p->r.max_len;
-               } else if (frec_magic(&p->f) == TDB_FREE_MAGIC
-                          || frec_magic(&p->f) == TDB_COALESCING_MAGIC) {
-                       len = sizeof(p->u) + p->f.data_len;
+                       len = sizeof(rec.r) + rec.r.max_len;
+               } else if (frec_magic(&rec.f) == TDB_FREE_MAGIC
+                          || frec_magic(&rec.f) == TDB_COALESCING_MAGIC) {
+                       len = sizeof(rec.u) + frec_len(&rec.f);
                        if (off + len > tdb->map_size) {
                                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                                         "tdb_check: free overlength %llu"
@@ -456,18 +500,18 @@ static bool check_linear(struct tdb_context *tdb,
                                return false;
                        }
                        /* This record is free! */
-                       if (frec_magic(&p->f) == TDB_FREE_MAGIC
+                       if (frec_magic(&rec.f) == TDB_FREE_MAGIC
                            && !append(free, num_free, off))
                                return false;
                } else {
                        uint64_t klen, dlen, extra;
 
                        /* This record is used! */
-                       if (rec_magic(&p->u) != TDB_MAGIC) {
+                       if (rec_magic(&rec.u) != TDB_MAGIC) {
                                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                                         "tdb_check: Bad magic 0x%llx"
                                         " at offset %llu\n",
-                                        (long long)rec_magic(&p->u),
+                                        (long long)rec_magic(&rec.u),
                                         (long long)off);
                                return false;
                        }
@@ -475,11 +519,11 @@ static bool check_linear(struct tdb_context *tdb,
                        if (!append(used, num_used, off))
                                return false;
 
-                       klen = rec_key_length(&p->u);
-                       dlen = rec_data_length(&p->u);
-                       extra = rec_extra_padding(&p->u);
+                       klen = rec_key_length(&rec.u);
+                       dlen = rec_data_length(&rec.u);
+                       extra = rec_extra_padding(&rec.u);
 
-                       len = sizeof(p->u) + klen + dlen + extra;
+                       len = sizeof(rec.u) + klen + dlen + extra;
                        if (off + len > tdb->map_size) {
                                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                                         "tdb_check: used overlength %llu"
@@ -488,7 +532,7 @@ static bool check_linear(struct tdb_context *tdb,
                                return false;
                        }
 
-                       if (len < sizeof(p->f)) {
+                       if (len < sizeof(rec.f)) {
                                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                                         "tdb_check: too short record %llu at"
                                         " %llu\n",
@@ -509,7 +553,6 @@ static bool check_linear(struct tdb_context *tdb,
        return true;
 }
 
-/* FIXME: call check() function. */
 int tdb_check(struct tdb_context *tdb,
              int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
              void *private_data)
@@ -535,19 +578,20 @@ int tdb_check(struct tdb_context *tdb,
        for (flist = first_flist(tdb); flist; flist = next_flist(tdb, flist)) {
                if (flist == TDB_OFF_ERR)
                        goto fail;
-               if (!check_free_list(tdb, flist, free, num_free, &num_found))
+               if (!check_free_list(tdb, flist, num_flists, free, num_free,
+                                    &num_found))
                        goto fail;
                num_flists++;
        }
 
        /* FIXME: Check key uniqueness? */
-       if (!check_hash(tdb, used, num_used, num_flists))
+       if (!check_hash(tdb, used, num_used, num_flists, check, private_data))
                goto fail;
 
        if (num_found != num_free) {
                tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
                         "tdb_check: Not all entries are in free table\n");
-               return false;
+               return -1;
        }
 
        tdb_allrecord_unlock(tdb, F_RDLCK);