reftable: avoid writing empty keys at the block layer

The public interface (reftable_writer) already ensures that keys are
written in strictly increasing order, and an empty key by definition
fails this check.

However, by also enforcing this at the block layer, it is easier to
verify that records (which are written into blocks) never have to
consider the possibility of empty keys.

Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Han-Wen Nienhuys 2022-02-21 18:46:07 +00:00 committed by Junio C Hamano
parent 1407659110
commit 45c2fcc2a0
3 changed files with 23 additions and 12 deletions

View File

@ -88,8 +88,9 @@ uint8_t block_writer_type(struct block_writer *bw)
return bw->buf[bw->header_off];
}
/* adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
success */
/* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
success. Returns REFTABLE_API_ERROR if attempting to write a record with
empty key. */
int block_writer_add(struct block_writer *w, struct reftable_record *rec)
{
struct strbuf empty = STRBUF_INIT;
@ -105,8 +106,14 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
int is_restart = 0;
struct strbuf key = STRBUF_INIT;
int n = 0;
int err = -1;
reftable_record_key(rec, &key);
if (!key.len) {
err = REFTABLE_API_ERROR;
goto done;
}
n = reftable_encode_key(&is_restart, out, last, key,
reftable_record_val_type(rec));
if (n < 0)
@ -118,16 +125,11 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
goto done;
string_view_consume(&out, n);
if (block_writer_register_restart(w, start.len - out.len, is_restart,
&key) < 0)
goto done;
strbuf_release(&key);
return 0;
err = block_writer_register_restart(w, start.len - out.len, is_restart,
&key);
done:
strbuf_release(&key);
return -1;
return err;
}
int block_writer_finish(struct block_writer *w)
@ -332,6 +334,9 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
if (n < 0)
return -1;
if (!key.len)
return REFTABLE_FORMAT_ERROR;
string_view_consume(&in, n);
n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
if (n < 0)
@ -358,6 +363,8 @@ int block_reader_first_key(struct block_reader *br, struct strbuf *key)
int n = reftable_decode_key(key, &extra, empty, in);
if (n < 0)
return n;
if (!key->len)
return REFTABLE_FORMAT_ERROR;
return 0;
}

View File

@ -42,6 +42,11 @@ static void test_block_read_write(void)
block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
header_off, hash_size(GIT_SHA1_FORMAT_ID));
rec.u.ref.refname = "";
rec.u.ref.value_type = REFTABLE_REF_DELETION;
n = block_writer_add(&bw, &rec);
EXPECT(n == REFTABLE_API_ERROR);
for (i = 0; i < N; i++) {
char name[100];
uint8_t hash[GIT_SHA1_RAWSZ];

View File

@ -240,14 +240,13 @@ static int writer_add_record(struct reftable_writer *w,
writer_reinit_block_writer(w, reftable_record_type(rec));
err = block_writer_add(w->block_writer, rec);
if (err < 0) {
if (err == -1) {
/* we are writing into memory, so an error can only mean it
* doesn't fit. */
err = REFTABLE_ENTRY_TOO_BIG_ERROR;
goto done;
}
err = 0;
done:
strbuf_release(&key);
return err;