X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb2%2Ftdb1_check.c;h=07ee07553a633e21cb8855180dc1779a706380d1;hp=306cd7e05f811d55ad03e854c2ff7a3c050952ad;hb=926996e88c32445c874ff9c4f47f159db6b45995;hpb=39f55294799c6443c0ad7bef09f1c113cf89d295 diff --git a/ccan/tdb2/tdb1_check.c b/ccan/tdb2/tdb1_check.c index 306cd7e0..07ee0755 100644 --- a/ccan/tdb2/tdb1_check.c +++ b/ccan/tdb2/tdb1_check.c @@ -25,14 +25,14 @@ #include "tdb1_private.h" /* Since we opened it, these shouldn't fail unless it's recent corruption. */ -static bool tdb1_check_header(struct tdb1_context *tdb, tdb1_off_t *recovery) +static bool tdb1_check_header(struct tdb_context *tdb, tdb1_off_t *recovery) { struct tdb1_header hdr; uint32_t h1, h2; - if (tdb->methods->tdb1_read(tdb, 0, &hdr, sizeof(hdr), 0) == -1) + if (tdb->tdb1.io->tdb1_read(tdb, 0, &hdr, sizeof(hdr), 0) == -1) return false; - if (strcmp(hdr.magic_food, TDB1_MAGIC_FOOD) != 0) + if (strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) goto corrupt; TDB1_CONV(hdr); @@ -50,67 +50,67 @@ static bool tdb1_check_header(struct tdb1_context *tdb, tdb1_off_t *recovery) if (hdr.hash_size == 0) goto corrupt; - if (hdr.hash_size != tdb->header.hash_size) + if (hdr.hash_size != tdb->tdb1.header.hash_size) goto corrupt; if (hdr.recovery_start != 0 && - hdr.recovery_start < TDB1_DATA_START(tdb->header.hash_size)) + hdr.recovery_start < TDB1_DATA_START(tdb->tdb1.header.hash_size)) goto corrupt; *recovery = hdr.recovery_start; return true; corrupt: - tdb->ecode = TDB1_ERR_CORRUPT; - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, "Header is corrupt\n")); + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Header is corrupt\n"); return false; } /* Generic record header check. */ -static bool tdb1_check_record(struct tdb1_context *tdb, +static bool tdb1_check_record(struct tdb_context *tdb, tdb1_off_t off, const struct tdb1_record *rec) { tdb1_off_t tailer; /* Check rec->next: 0 or points to record offset, aligned. */ - if (rec->next > 0 && rec->next < TDB1_DATA_START(tdb->header.hash_size)){ - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d too small next %d\n", - off, rec->next)); + if (rec->next > 0 && rec->next < TDB1_DATA_START(tdb->tdb1.header.hash_size)){ + tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d too small next %d\n", + off, rec->next); goto corrupt; } if (rec->next + sizeof(*rec) < rec->next) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d too large next %d\n", - off, rec->next)); + tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d too large next %d\n", + off, rec->next); goto corrupt; } if ((rec->next % TDB1_ALIGNMENT) != 0) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d misaligned next %d\n", - off, rec->next)); + tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d misaligned next %d\n", + off, rec->next); goto corrupt; } - if (tdb->methods->tdb1_oob(tdb, rec->next+sizeof(*rec), 0)) + if (tdb->tdb1.io->tdb1_oob(tdb, rec->next, sizeof(*rec), 0)) goto corrupt; /* Check rec_len: similar to rec->next, implies next record. */ if ((rec->rec_len % TDB1_ALIGNMENT) != 0) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d misaligned length %d\n", - off, rec->rec_len)); + tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d misaligned length %d\n", + off, rec->rec_len); goto corrupt; } /* Must fit tailer. */ if (rec->rec_len < sizeof(tailer)) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d too short length %d\n", - off, rec->rec_len)); + tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d too short length %d\n", + off, rec->rec_len); goto corrupt; } /* OOB allows "right at the end" access, so this works for last rec. */ - if (tdb->methods->tdb1_oob(tdb, off+sizeof(*rec)+rec->rec_len, 0)) + if (tdb->tdb1.io->tdb1_oob(tdb, off, sizeof(*rec)+rec->rec_len, 0)) goto corrupt; /* Check tailer. */ @@ -118,38 +118,38 @@ static bool tdb1_check_record(struct tdb1_context *tdb, &tailer) == -1) goto corrupt; if (tailer != sizeof(*rec) + rec->rec_len) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d invalid tailer\n", off)); + tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d invalid tailer\n", off); goto corrupt; } return true; corrupt: - tdb->ecode = TDB1_ERR_CORRUPT; + tdb->last_error = TDB_ERR_CORRUPT; return false; } /* Grab some bytes: may copy if can't use mmap. Caller has already done bounds check. */ -static TDB1_DATA get_bytes(struct tdb1_context *tdb, +static TDB_DATA get_bytes(struct tdb_context *tdb, tdb1_off_t off, tdb1_len_t len) { - TDB1_DATA d; + TDB_DATA d; d.dsize = len; - if (tdb->transaction == NULL && tdb->map_ptr != NULL) - d.dptr = (unsigned char *)tdb->map_ptr + off; + if (tdb->tdb1.transaction == NULL && tdb->file->map_ptr != NULL) + d.dptr = (unsigned char *)tdb->file->map_ptr + off; else d.dptr = tdb1_alloc_read(tdb, off, d.dsize); return d; } /* Frees data if we're not able to simply use mmap. */ -static void put_bytes(struct tdb1_context *tdb, TDB1_DATA d) +static void put_bytes(struct tdb_context *tdb, TDB_DATA d) { - if (tdb->transaction == NULL && tdb->map_ptr != NULL) + if (tdb->tdb1.transaction == NULL && tdb->file->map_ptr != NULL) return; free(d.dptr); } @@ -232,22 +232,23 @@ static void record_offset(unsigned char bits[], tdb1_off_t off) } /* Check that an in-use record is valid. */ -static bool tdb1_check_used_record(struct tdb1_context *tdb, +static bool tdb1_check_used_record(struct tdb_context *tdb, tdb1_off_t off, const struct tdb1_record *rec, unsigned char **hashes, - int (*check)(TDB1_DATA, TDB1_DATA, void *), + enum TDB_ERROR (*check)(TDB_DATA, TDB_DATA, + void *), void *private_data) { - TDB1_DATA key, data; + TDB_DATA key, data; if (!tdb1_check_record(tdb, off, rec)) return false; /* key + data + tailer must fit in record */ if (rec->key_len + rec->data_len + sizeof(tdb1_off_t) > rec->rec_len) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d too short for contents\n", off)); + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d too short for contents\n", off); return false; } @@ -255,9 +256,9 @@ static bool tdb1_check_used_record(struct tdb1_context *tdb, if (!key.dptr) return false; - if (tdb->hash_fn(&key) != rec->full_hash) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Record offset %d has incorrect hash\n", off)); + if ((uint32_t)tdb_hash(tdb, key.dptr, key.dsize) != rec->full_hash) { + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Record offset %d has incorrect hash\n", off); goto fail_put_key; } @@ -270,13 +271,18 @@ static bool tdb1_check_used_record(struct tdb1_context *tdb, /* If they supply a check function and this record isn't dead, get data and feed it. */ if (check && rec->magic != TDB1_DEAD_MAGIC) { + enum TDB_ERROR ecode; + data = get_bytes(tdb, off + sizeof(*rec) + rec->key_len, rec->data_len); if (!data.dptr) goto fail_put_key; - if (check(key, data, private_data) == -1) + ecode = check(key, data, private_data); + if (ecode != TDB_SUCCESS) { + tdb->last_error = ecode; goto fail_put_data; + } put_bytes(tdb, data); } @@ -291,7 +297,7 @@ fail_put_key: } /* Check that an unused record is valid. */ -static bool tdb1_check_free_record(struct tdb1_context *tdb, +static bool tdb1_check_free_record(struct tdb_context *tdb, tdb1_off_t off, const struct tdb1_record *rec, unsigned char **hashes) @@ -308,13 +314,13 @@ static bool tdb1_check_free_record(struct tdb1_context *tdb, } /* Slow, but should be very rare. */ -size_t tdb1_dead_space(struct tdb1_context *tdb, tdb1_off_t off) +size_t tdb1_dead_space(struct tdb_context *tdb, tdb1_off_t off) { size_t len; - for (len = 0; off + len < tdb->map_size; len++) { + for (len = 0; off + len < tdb->file->map_size; len++) { char c; - if (tdb->methods->tdb1_read(tdb, off, &c, 1, 0)) + if (tdb->tdb1.io->tdb1_read(tdb, off, &c, 1, 0)) return 0; if (c != 0 && c != 0x42) break; @@ -322,9 +328,9 @@ size_t tdb1_dead_space(struct tdb1_context *tdb, tdb1_off_t off) return len; } -int tdb1_check(struct tdb1_context *tdb, - int (*check)(TDB1_DATA key, TDB1_DATA data, void *private_data), - void *private_data) +int tdb1_check(struct tdb_context *tdb, + enum TDB_ERROR (*check)(TDB_DATA key, TDB_DATA data, void *), + void *private_data) { unsigned int h; unsigned char **hashes; @@ -333,47 +339,49 @@ int tdb1_check(struct tdb1_context *tdb, bool found_recovery = false; tdb1_len_t dead; bool locked; + size_t alloc_len; - /* 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) { + /* We may have a write lock already, so don't re-lock. */ + if (tdb->file->allrecord_lock.count != 0) { locked = false; } else { - if (tdb1_lockall_read(tdb) == -1) + if (tdb_lockall_read(tdb) != TDB_SUCCESS) return -1; locked = true; } /* Make sure we know true size of the underlying file. */ - tdb->methods->tdb1_oob(tdb, tdb->map_size + 1, 1); + tdb->tdb1.io->tdb1_oob(tdb, tdb->file->map_size, 1, 1); /* Header must be OK: also gets us the recovery ptr, if any. */ if (!tdb1_check_header(tdb, &recovery_start)) goto unlock; /* We should have the whole header, too. */ - if (tdb->map_size < TDB1_DATA_START(tdb->header.hash_size)) { - tdb->ecode = TDB1_ERR_CORRUPT; - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, "File too short for hashes\n")); + if (tdb->file->map_size < TDB1_DATA_START(tdb->tdb1.header.hash_size)) { + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "File too short for hashes\n"); goto unlock; } /* One big malloc: pointers then bit arrays. */ - hashes = (unsigned char **)calloc( - 1, sizeof(hashes[0]) * (1+tdb->header.hash_size) - + BITMAP_BITS / CHAR_BIT * (1+tdb->header.hash_size)); + alloc_len = sizeof(hashes[0]) * (1+tdb->tdb1.header.hash_size) + + BITMAP_BITS / CHAR_BIT * (1+tdb->tdb1.header.hash_size); + hashes = (unsigned char **)calloc(1, alloc_len); if (!hashes) { - tdb->ecode = TDB1_ERR_OOM; + tdb->last_error = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR, + "tdb_check: could not allocate %zu", + alloc_len); goto unlock; } /* Initialize pointers */ - hashes[0] = (unsigned char *)(&hashes[1+tdb->header.hash_size]); - for (h = 1; h < 1+tdb->header.hash_size; h++) + hashes[0] = (unsigned char *)(&hashes[1+tdb->tdb1.header.hash_size]); + for (h = 1; h < 1+tdb->tdb1.header.hash_size; h++) hashes[h] = hashes[h-1] + BITMAP_BITS / CHAR_BIT; /* Freelist and hash headers are all in a row: read them. */ - for (h = 0; h < 1+tdb->header.hash_size; h++) { + for (h = 0; h < 1+tdb->tdb1.header.hash_size; h++) { if (tdb1_ofs_read(tdb, TDB1_FREELIST_TOP + h*sizeof(tdb1_off_t), &off) == -1) goto free; @@ -382,10 +390,10 @@ int tdb1_check(struct tdb1_context *tdb, } /* For each record, read it in and check it's ok. */ - for (off = TDB1_DATA_START(tdb->header.hash_size); - off < tdb->map_size; + for (off = TDB1_DATA_START(tdb->tdb1.header.hash_size); + off < tdb->file->map_size; off += sizeof(rec) + rec.rec_len) { - if (tdb->methods->tdb1_read(tdb, off, &rec, sizeof(rec), + if (tdb->tdb1.io->tdb1_read(tdb, off, &rec, sizeof(rec), TDB1_DOCONV()) == -1) goto free; switch (rec.magic) { @@ -410,39 +418,37 @@ int tdb1_check(struct tdb1_context *tdb, if (dead < sizeof(rec)) goto corrupt; - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Dead space at %d-%d (of %u)\n", - off, off + dead, tdb->map_size)); + tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING, + "Dead space at %d-%d (of %u)\n", + off, off + dead, tdb->file->map_size); rec.rec_len = dead - sizeof(rec); break; case TDB1_RECOVERY_MAGIC: if (recovery_start != off) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Unexpected recovery record at offset %d\n", - off)); + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Unexpected recovery record at offset %d\n", + off); goto free; } found_recovery = true; break; default: ; corrupt: - tdb->ecode = TDB1_ERR_CORRUPT; - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Bad magic 0x%x at offset %d\n", - rec.magic, off)); + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Bad magic 0x%x at offset %d\n", + rec.magic, off); goto free; } } /* Now, hashes should all be empty: each record exists and is referred * to by one other. */ - for (h = 0; h < 1+tdb->header.hash_size; h++) { + for (h = 0; h < 1+tdb->tdb1.header.hash_size; h++) { unsigned int i; for (i = 0; i < BITMAP_BITS / CHAR_BIT; i++) { if (hashes[h][i] != 0) { - tdb->ecode = TDB1_ERR_CORRUPT; - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Hashes do not match records\n")); + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Hashes do not match records\n"); goto free; } } @@ -450,15 +456,15 @@ int tdb1_check(struct tdb1_context *tdb, /* We must have found recovery area if there was one. */ if (recovery_start != 0 && !found_recovery) { - TDB1_LOG((tdb, TDB1_DEBUG_ERROR, - "Expected a recovery area at %u\n", - recovery_start)); + tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "Expected a recovery area at %u\n", + recovery_start); goto free; } free(hashes); if (locked) { - tdb1_unlockall_read(tdb); + tdb_unlockall_read(tdb); } return 0; @@ -466,7 +472,7 @@ free: free(hashes); unlock: if (locked) { - tdb1_unlockall_read(tdb); + tdb_unlockall_read(tdb); } return -1; }