From ed81f39468c4d9089310fb4950b09c1f5886c4ef Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 17 Mar 2011 22:12:21 +1030 Subject: [PATCH] tdb2: make sure records with extra padding have a 0 byte. As detailed in doc/design.lyx section 2.16 "Record Headers Are Not Expandible", we make sure that if there is padding at the end of a record, the first byte of padding is a zero. --- ccan/tdb2/check.c | 43 ++++++++++++++++++++++++++++++++++++---- ccan/tdb2/doc/design.lyx | 6 ++++++ ccan/tdb2/free.c | 9 +++++++++ ccan/tdb2/tdb.c | 28 +++++++++++++++++++------- ccan/tdb2/test/layout.c | 8 ++++++++ 5 files changed, 83 insertions(+), 11 deletions(-) diff --git a/ccan/tdb2/check.c b/ccan/tdb2/check.c index 9bbd1264..b602d80d 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,6 +60,16 @@ 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) { @@ -568,7 +579,7 @@ 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; @@ -697,6 +708,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, @@ -724,6 +757,7 @@ enum TDB_ERROR tdb_check_(struct tdb_context *tdb, { 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); @@ -737,12 +771,13 @@ enum TDB_ERROR tdb_check_(struct tdb_context *tdb, return 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; diff --git a/ccan/tdb2/doc/design.lyx b/ccan/tdb2/doc/design.lyx index af94f6b9..dfbc19fa 100644 --- a/ccan/tdb2/doc/design.lyx +++ b/ccan/tdb2/doc/design.lyx @@ -1434,7 +1434,13 @@ Status \end_layout \begin_layout Standard + +\change_deleted 0 1300360766 Incomplete. +\change_inserted 0 1300360767 +Complete. +\change_unchanged + \end_layout \begin_layout Subsection diff --git a/ccan/tdb2/free.c b/ccan/tdb2/free.c index d2285567..e853d97e 100644 --- a/ccan/tdb2/free.c +++ b/ccan/tdb2/free.c @@ -523,6 +523,15 @@ again: goto unlock_err; } + /* For futureproofing, we put a 0 in any unused space. */ + if (rec_extra_padding(&rec)) { + ecode = tdb->methods->twrite(tdb, best_off + sizeof(rec) + + keylen + datalen, "", 1); + if (ecode != TDB_SUCCESS) { + goto unlock_err; + } + } + /* Bucket of leftover will be <= current bucket, so nested * locking is allowed. */ if (leftover) { diff --git a/ccan/tdb2/tdb.c b/ccan/tdb2/tdb.c index 4ffcebf5..42e75f9d 100644 --- a/ccan/tdb2/tdb.c +++ b/ccan/tdb2/tdb.c @@ -510,6 +510,21 @@ static enum TDB_ERROR replace_data(struct tdb_context *tdb, return TDB_SUCCESS; } +static enum TDB_ERROR update_data(struct tdb_context *tdb, + tdb_off_t off, + struct tdb_data dbuf, + tdb_len_t extra) +{ + enum TDB_ERROR ecode; + + ecode = tdb->methods->twrite(tdb, off, dbuf.dptr, dbuf.dsize); + if (ecode == TDB_SUCCESS && extra) { + /* Put a zero in; future versions may append other data. */ + ecode = tdb->methods->twrite(tdb, off + dbuf.dsize, "", 1); + } + return ecode; +} + enum TDB_ERROR tdb_store(struct tdb_context *tdb, struct tdb_data key, struct tdb_data dbuf, int flag) { @@ -542,11 +557,10 @@ enum TDB_ERROR tdb_store(struct tdb_context *tdb, if (ecode != TDB_SUCCESS) { goto out; } - ecode = tdb->methods->twrite(tdb, - off + sizeof(rec) - + key.dsize, - dbuf.dptr, - dbuf.dsize); + ecode = update_data(tdb, + off + sizeof(rec) + + key.dsize, dbuf, + old_room - dbuf.dsize); if (ecode != TDB_SUCCESS) { goto out; } @@ -602,8 +616,8 @@ enum TDB_ERROR tdb_append(struct tdb_context *tdb, } off += sizeof(rec) + key.dsize + old_dlen; - ecode = tdb->methods->twrite(tdb, off, dbuf.dptr, - dbuf.dsize); + ecode = update_data(tdb, off, dbuf, + rec_extra_padding(&rec)); goto out; } diff --git a/ccan/tdb2/test/layout.c b/ccan/tdb2/test/layout.c index 16f07719..77ad1f8e 100644 --- a/ccan/tdb2/test/layout.c +++ b/ccan/tdb2/test/layout.c @@ -92,6 +92,12 @@ static void set_free_record(void *mem, tdb_len_t len) /* We do all the work in add_to_freetable */ } +static void add_zero_pad(struct tdb_used_record *u, size_t len, size_t extra) +{ + if (extra) + ((char *)(u + 1))[len] = '\0'; +} + static void set_data_record(void *mem, struct tdb_context *tdb, struct tle_used *used) { @@ -103,6 +109,7 @@ static void set_data_record(void *mem, struct tdb_context *tdb, memcpy(u + 1, used->key.dptr, used->key.dsize); memcpy((char *)(u + 1) + used->key.dsize, used->data.dptr, used->data.dsize); + add_zero_pad(u, used->key.dsize + used->data.dsize, used->extra); } static void set_hashtable(void *mem, struct tdb_context *tdb, @@ -113,6 +120,7 @@ static void set_hashtable(void *mem, struct tdb_context *tdb, set_header(tdb, u, TDB_HTABLE_MAGIC, 0, len, len + htable->extra, 0); memset(u + 1, 0, len); + add_zero_pad(u, len, htable->extra); } static void set_freetable(void *mem, struct tdb_context *tdb, -- 2.39.2