X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb2%2Fcheck.c;h=52fb188764dfa0e2c85250e3658200db2fc67894;hp=4f340a2e15e7faaa7112a90291fa2945faea510b;hb=024a5647e6c81735a93d826b56db0db4bf86fab8;hpb=91505ce279ed86e2575f7f69091638e10659f462 diff --git a/ccan/tdb2/check.c b/ccan/tdb2/check.c index 4f340a2e..52fb1887 100644 --- a/ccan/tdb2/check.c +++ b/ccan/tdb2/check.c @@ -30,7 +30,8 @@ static bool append(tdb_off_t **arr, size_t *num, tdb_off_t off) return true; } -static enum TDB_ERROR check_header(struct tdb_context *tdb, tdb_off_t *recovery) +static enum TDB_ERROR check_header(struct tdb_context *tdb, tdb_off_t *recovery, + uint64_t *features) { uint64_t hash_test; struct tdb_header hdr; @@ -59,9 +60,20 @@ static enum TDB_ERROR check_header(struct tdb_context *tdb, tdb_off_t *recovery) hdr.magic_food); } + /* Features which are used must be a subset of features offered. */ + if (hdr.features_used & ~hdr.features_offered) { + return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "check: features used (0x%llx) which" + " are not offered (0x%llx)", + (long long)hdr.features_used, + (long long)hdr.features_offered); + } + + *features = hdr.features_offered; *recovery = hdr.recovery; if (*recovery) { - if (*recovery < sizeof(hdr) || *recovery > tdb->map_size) { + if (*recovery < sizeof(hdr) + || *recovery > tdb->file->map_size) { return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, "tdb_check:" " invalid recovery offset %zu", @@ -80,8 +92,9 @@ static enum TDB_ERROR check_hash_tree(struct tdb_context *tdb, tdb_off_t used[], size_t num_used, size_t *num_found, - int (*check)(TDB_DATA, TDB_DATA, void *), - void *private_data); + enum TDB_ERROR (*check)(TDB_DATA, + TDB_DATA, void *), + void *data); static enum TDB_ERROR check_hash_chain(struct tdb_context *tdb, tdb_off_t off, @@ -89,8 +102,10 @@ static enum TDB_ERROR check_hash_chain(struct tdb_context *tdb, tdb_off_t used[], size_t num_used, size_t *num_found, - int (*check)(TDB_DATA, TDB_DATA, void *), - void *private_data) + enum TDB_ERROR (*check)(TDB_DATA, + TDB_DATA, + void *), + void *data) { struct tdb_used_record rec; enum TDB_ERROR ecode; @@ -126,9 +141,9 @@ static enum TDB_ERROR check_hash_chain(struct tdb_context *tdb, off += sizeof(rec); ecode = check_hash_tree(tdb, off, 0, hash, 64, - used, num_used, num_found, check, private_data); + used, num_used, num_found, check, data); if (ecode != TDB_SUCCESS) { - return false; + return ecode; } off = tdb_read_off(tdb, off + offsetof(struct tdb_chain, next)); @@ -139,7 +154,7 @@ static enum TDB_ERROR check_hash_chain(struct tdb_context *tdb, return TDB_SUCCESS; (*num_found)++; return check_hash_chain(tdb, off, hash, used, num_used, num_found, - check, private_data); + check, data); } static enum TDB_ERROR check_hash_record(struct tdb_context *tdb, @@ -149,15 +164,17 @@ static enum TDB_ERROR check_hash_record(struct tdb_context *tdb, tdb_off_t used[], size_t num_used, size_t *num_found, - int (*check)(TDB_DATA, TDB_DATA, void*), - void *private_data) + enum TDB_ERROR (*check)(TDB_DATA, + TDB_DATA, + void *), + void *data) { struct tdb_used_record rec; enum TDB_ERROR ecode; if (hprefix_bits >= 64) return check_hash_chain(tdb, off, hprefix, used, num_used, - num_found, check, private_data); + num_found, check, data); ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec)); if (ecode != TDB_SUCCESS) { @@ -193,7 +210,7 @@ static enum TDB_ERROR 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, check, private_data); + used, num_used, num_found, check, data); } static int off_cmp(const tdb_off_t *a, const tdb_off_t *b) @@ -218,8 +235,9 @@ static enum TDB_ERROR check_hash_tree(struct tdb_context *tdb, tdb_off_t used[], size_t num_used, size_t *num_found, - int (*check)(TDB_DATA, TDB_DATA, void *), - void *private_data) + enum TDB_ERROR (*check)(TDB_DATA, + TDB_DATA, void *), + void *data) { unsigned int g, b; const tdb_off_t *hash; @@ -300,7 +318,7 @@ static enum TDB_ERROR check_hash_tree(struct tdb_context *tdb, + group_bits + TDB_HASH_GROUP_BITS, used, num_used, num_found, - check, private_data); + check, data); if (ecode != TDB_SUCCESS) { goto fail; } @@ -383,23 +401,27 @@ static enum TDB_ERROR check_hash_tree(struct tdb_context *tdb, check: 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 (TDB_PTR_IS_ERR(key.dptr)) { - ecode = TDB_PTR_ERR(key.dptr); + TDB_DATA k, d; + const unsigned char *kptr; + + kptr = tdb_access_read(tdb, + off + sizeof(rec), + rec_key_length(&rec) + + rec_data_length(&rec), + false); + if (TDB_PTR_IS_ERR(kptr)) { + ecode = TDB_PTR_ERR(kptr); goto fail; } - data.dptr = key.dptr + key.dsize; - if (check(key, data, private_data) != 0) { - ecode = TDB_ERR_CORRUPT; + + k = tdb_mkdata(kptr, rec_key_length(&rec)); + d = tdb_mkdata(kptr + k.dsize, + rec_data_length(&rec)); + ecode = check(k, d, data); + tdb_access_release(tdb, kptr); + if (ecode != TDB_SUCCESS) { goto fail; } - tdb_access_release(tdb, key.dptr); } } } @@ -415,7 +437,7 @@ static enum TDB_ERROR check_hash(struct tdb_context *tdb, tdb_off_t used[], size_t num_used, size_t num_ftables, int (*check)(TDB_DATA, TDB_DATA, void *), - void *private_data) + void *data) { /* Free tables also show up as used. */ size_t num_found = num_ftables; @@ -424,7 +446,7 @@ static enum TDB_ERROR check_hash(struct tdb_context *tdb, ecode = check_hash_tree(tdb, offsetof(struct tdb_header, hashtable), TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS, 0, 0, used, num_used, &num_found, - check, private_data); + check, data); if (ecode == TDB_SUCCESS) { if (num_found != num_used) { ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, @@ -470,7 +492,7 @@ static enum TDB_ERROR check_free(struct tdb_context *tdb, (long long)off, bucket, size_to_bucket(frec_len(frec))); } - if (prev != frec_prev(frec)) { + if (prev && prev != frec_prev(frec)) { return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, "tdb_check: offset %llu bad prev" " (%llu vs %llu)", @@ -506,7 +528,7 @@ static enum TDB_ERROR check_free_table(struct tdb_context *tdb, } for (i = 0; i < TDB_FREE_BUCKETS; i++) { - tdb_off_t off, prev = 0, *p; + tdb_off_t off, prev = 0, *p, first = 0; struct tdb_free_record f; h = bucket_off(ftable_off, i); @@ -514,13 +536,17 @@ static enum TDB_ERROR check_free_table(struct tdb_context *tdb, if (TDB_OFF_IS_ERR(off)) { return off; } + if (!first) { + off &= TDB_OFF_MASK; + first = off; + } ecode = tdb_read_convert(tdb, off, &f, sizeof(f)); if (ecode != TDB_SUCCESS) { return ecode; } ecode = check_free(tdb, off, &f, prev, ftable_num, i); if (ecode != TDB_SUCCESS) { - return false; + return ecode; } /* FIXME: Check hash bits */ @@ -537,6 +563,18 @@ static enum TDB_ERROR check_free_table(struct tdb_context *tdb, (*num_found)++; prev = off; } + + if (first) { + /* Now we can check first back pointer. */ + ecode = tdb_read_convert(tdb, first, &f, sizeof(f)); + if (ecode != TDB_SUCCESS) { + return ecode; + } + ecode = check_free(tdb, first, &f, prev, ftable_num, i); + if (ecode != TDB_SUCCESS) { + return ecode; + } + } } return TDB_SUCCESS; } @@ -547,7 +585,7 @@ tdb_off_t dead_space(struct tdb_context *tdb, tdb_off_t off) size_t len; enum TDB_ERROR ecode; - for (len = 0; off + len < tdb->map_size; len++) { + for (len = 0; off + len < tdb->file->map_size; len++) { char c; ecode = tdb->methods->tread(tdb, off, &c, 1); if (ecode != TDB_SUCCESS) { @@ -562,14 +600,16 @@ tdb_off_t dead_space(struct tdb_context *tdb, tdb_off_t off) static enum TDB_ERROR check_linear(struct tdb_context *tdb, tdb_off_t **used, size_t *num_used, tdb_off_t **fr, size_t *num_free, - tdb_off_t recovery) + uint64_t features, tdb_off_t recovery) { tdb_off_t off; tdb_len_t len; enum TDB_ERROR ecode; bool found_recovery = false; - for (off = sizeof(struct tdb_header); off < tdb->map_size; off += len) { + for (off = sizeof(struct tdb_header); + off < tdb->file->map_size; + off += len) { union { struct tdb_used_record u; struct tdb_free_record f; @@ -607,7 +647,7 @@ static enum TDB_ERROR check_linear(struct tdb_context *tdb, tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING, "Dead space at %zu-%zu (of %zu)", (size_t)off, (size_t)(off + len), - (size_t)tdb->map_size); + (size_t)tdb->file->map_size); } } else if (rec.r.magic == TDB_RECOVERY_MAGIC) { ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec.r)); @@ -629,7 +669,7 @@ static enum TDB_ERROR check_linear(struct tdb_context *tdb, " length %zu", (size_t)rec.r.len); } - if (rec.r.eof > tdb->map_size) { + if (rec.r.eof > tdb->file->map_size) { return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, "tdb_check: invalid old EOF" @@ -639,7 +679,7 @@ static enum TDB_ERROR check_linear(struct tdb_context *tdb, len = sizeof(rec.r) + rec.r.max_len; } else if (frec_magic(&rec.f) == TDB_FREE_MAGIC) { len = sizeof(rec.u) + frec_len(&rec.f); - if (off + len > tdb->map_size) { + if (off + len > tdb->file->map_size) { return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, "tdb_check: free overlength" @@ -674,7 +714,7 @@ static enum TDB_ERROR check_linear(struct tdb_context *tdb, extra = rec_extra_padding(&rec.u); len = sizeof(rec.u) + klen + dlen + extra; - if (off + len > tdb->map_size) { + if (off + len > tdb->file->map_size) { return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, "tdb_check: used overlength" @@ -691,6 +731,28 @@ static enum TDB_ERROR check_linear(struct tdb_context *tdb, (long long)len, (long long)off); } + + /* Check that records have correct 0 at end (but may + * not in future). */ + if (extra && !features) { + const char *p; + char c; + p = tdb_access_read(tdb, off + sizeof(rec.u) + + klen + dlen, 1, false); + if (TDB_PTR_IS_ERR(p)) + return TDB_PTR_ERR(p); + c = *p; + tdb_access_release(tdb, p); + + if (c != '\0') { + return tdb_logerr(tdb, TDB_ERR_CORRUPT, + TDB_LOG_ERROR, + "tdb_check:" + " non-zero extra" + " at %llu", + (long long)off); + } + } } else { return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, @@ -711,39 +773,39 @@ static enum TDB_ERROR check_linear(struct tdb_context *tdb, return TDB_SUCCESS; } -int tdb_check(struct tdb_context *tdb, - int (*check)(TDB_DATA key, TDB_DATA data, void *private_data), - void *private_data) +enum TDB_ERROR tdb_check_(struct tdb_context *tdb, + enum TDB_ERROR (*check)(TDB_DATA, TDB_DATA, void *), + void *data) { tdb_off_t *fr = NULL, *used = NULL, ft, recovery; size_t num_free = 0, num_used = 0, num_found = 0, num_ftables = 0; + uint64_t features; enum TDB_ERROR ecode; ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false); if (ecode != TDB_SUCCESS) { - tdb->ecode = ecode; - return -1; + return tdb->last_error = ecode; } ecode = tdb_lock_expand(tdb, F_RDLCK); if (ecode != TDB_SUCCESS) { - tdb->ecode = ecode; tdb_allrecord_unlock(tdb, F_RDLCK); - return -1; + return tdb->last_error = ecode; } - ecode = check_header(tdb, &recovery); + ecode = check_header(tdb, &recovery, &features); if (ecode != TDB_SUCCESS) goto out; /* First we do a linear scan, checking all records. */ - ecode = check_linear(tdb, &used, &num_used, &fr, &num_free, recovery); + ecode = check_linear(tdb, &used, &num_used, &fr, &num_free, features, + recovery); if (ecode != TDB_SUCCESS) goto out; for (ft = first_ftable(tdb); ft; ft = next_ftable(tdb, ft)) { if (TDB_OFF_IS_ERR(ft)) { - tdb->ecode = ft; + ecode = ft; goto out; } ecode = check_free_table(tdb, ft, num_ftables, fr, num_free, @@ -754,8 +816,7 @@ int tdb_check(struct tdb_context *tdb, } /* FIXME: Check key uniqueness? */ - ecode = check_hash(tdb, used, num_used, num_ftables, check, - private_data); + ecode = check_hash(tdb, used, num_used, num_ftables, check, data); if (ecode != TDB_SUCCESS) goto out; @@ -770,9 +831,5 @@ out: tdb_unlock_expand(tdb, F_RDLCK); free(fr); free(used); - if (ecode != TDB_SUCCESS) { - tdb->ecode = ecode; - return -1; - } - return 0; + return tdb->last_error = ecode; }