Skip to content

Commit 4763ccd

Browse files
committed
Merge branch 'hn/reftable-no-empty-keys'
General clean-up in reftable implementation, including clarification of the API documentation, tightening the code to honor documented length limit, etc. * hn/reftable-no-empty-keys: reftable: rename writer_stats to reftable_writer_stats reftable: add test for length of disambiguating prefix reftable: ensure that obj_id_len is >= 2 on writing reftable: avoid writing empty keys at the block layer reftable: add a test that verifies that writing empty keys fails reftable: reject 0 object_id_len Documentation: object_id_len goes up to 31
2 parents d169d51 + 73a4c18 commit 4763ccd

File tree

7 files changed

+136
-19
lines changed

7 files changed

+136
-19
lines changed

Documentation/technical/reftable.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ Obj block format
443443
Object blocks are optional. Writers may choose to omit object blocks,
444444
especially if readers will not use the object name to ref mapping.
445445

446-
Object blocks use unique, abbreviated 2-32 object name keys, mapping to
446+
Object blocks use unique, abbreviated 2-31 byte object name keys, mapping to
447447
ref blocks containing references pointing to that object directly, or as
448448
the peeled value of an annotated tag. Like ref blocks, object blocks use
449449
the file's standard block size. The abbreviation length is available in

reftable/block.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ uint8_t block_writer_type(struct block_writer *bw)
8888
return bw->buf[bw->header_off];
8989
}
9090

91-
/* adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
92-
success */
91+
/* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
92+
success. Returns REFTABLE_API_ERROR if attempting to write a record with
93+
empty key. */
9394
int block_writer_add(struct block_writer *w, struct reftable_record *rec)
9495
{
9596
struct strbuf empty = STRBUF_INIT;
@@ -105,8 +106,14 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
105106
int is_restart = 0;
106107
struct strbuf key = STRBUF_INIT;
107108
int n = 0;
109+
int err = -1;
108110

109111
reftable_record_key(rec, &key);
112+
if (!key.len) {
113+
err = REFTABLE_API_ERROR;
114+
goto done;
115+
}
116+
110117
n = reftable_encode_key(&is_restart, out, last, key,
111118
reftable_record_val_type(rec));
112119
if (n < 0)
@@ -118,16 +125,11 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
118125
goto done;
119126
string_view_consume(&out, n);
120127

121-
if (block_writer_register_restart(w, start.len - out.len, is_restart,
122-
&key) < 0)
123-
goto done;
124-
125-
strbuf_release(&key);
126-
return 0;
127-
128+
err = block_writer_register_restart(w, start.len - out.len, is_restart,
129+
&key);
128130
done:
129131
strbuf_release(&key);
130-
return -1;
132+
return err;
131133
}
132134

133135
int block_writer_finish(struct block_writer *w)
@@ -332,6 +334,9 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
332334
if (n < 0)
333335
return -1;
334336

337+
if (!key.len)
338+
return REFTABLE_FORMAT_ERROR;
339+
335340
string_view_consume(&in, n);
336341
n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
337342
if (n < 0)
@@ -358,6 +363,8 @@ int block_reader_first_key(struct block_reader *br, struct strbuf *key)
358363
int n = reftable_decode_key(key, &extra, empty, in);
359364
if (n < 0)
360365
return n;
366+
if (!key->len)
367+
return REFTABLE_FORMAT_ERROR;
361368

362369
return 0;
363370
}

reftable/block_test.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ static void test_block_read_write(void)
4242
block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
4343
header_off, hash_size(GIT_SHA1_FORMAT_ID));
4444

45+
rec.u.ref.refname = "";
46+
rec.u.ref.value_type = REFTABLE_REF_DELETION;
47+
n = block_writer_add(&bw, &rec);
48+
EXPECT(n == REFTABLE_API_ERROR);
49+
4550
for (i = 0; i < N; i++) {
4651
char name[100];
4752
uint8_t hash[GIT_SHA1_RAWSZ];

reftable/reader.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
155155
r->log_offsets.is_present = (first_block_typ == BLOCK_TYPE_LOG ||
156156
r->log_offsets.offset > 0);
157157
r->obj_offsets.is_present = r->obj_offsets.offset > 0;
158+
if (r->obj_offsets.is_present && !r->object_id_len) {
159+
err = REFTABLE_FORMAT_ERROR;
160+
goto done;
161+
}
162+
158163
err = 0;
159164
done:
160165
return err;

reftable/readwrite_test.c

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
100100
n = reftable_writer_close(w);
101101
EXPECT(n == 0);
102102

103-
stats = writer_stats(w);
103+
stats = reftable_writer_stats(w);
104104
for (i = 0; i < stats->ref_stats.blocks; i++) {
105105
int off = i * opts.block_size;
106106
if (off == 0) {
@@ -239,7 +239,7 @@ static void test_log_write_read(void)
239239
n = reftable_writer_close(w);
240240
EXPECT(n == 0);
241241

242-
stats = writer_stats(w);
242+
stats = reftable_writer_stats(w);
243243
EXPECT(stats->log_stats.blocks > 0);
244244
reftable_writer_free(w);
245245
w = NULL;
@@ -330,7 +330,7 @@ static void test_log_zlib_corruption(void)
330330
n = reftable_writer_close(w);
331331
EXPECT(n == 0);
332332

333-
stats = writer_stats(w);
333+
stats = reftable_writer_stats(w);
334334
EXPECT(stats->log_stats.blocks > 0);
335335
reftable_writer_free(w);
336336
w = NULL;
@@ -667,6 +667,102 @@ static void test_write_empty_table(void)
667667
strbuf_release(&buf);
668668
}
669669

670+
static void test_write_object_id_min_length(void)
671+
{
672+
struct reftable_write_options opts = {
673+
.block_size = 75,
674+
};
675+
struct strbuf buf = STRBUF_INIT;
676+
struct reftable_writer *w =
677+
reftable_new_writer(&strbuf_add_void, &buf, &opts);
678+
uint8_t hash[GIT_SHA1_RAWSZ] = {42};
679+
struct reftable_ref_record ref = {
680+
.update_index = 1,
681+
.value_type = REFTABLE_REF_VAL1,
682+
.value.val1 = hash,
683+
};
684+
int err;
685+
int i;
686+
687+
reftable_writer_set_limits(w, 1, 1);
688+
689+
/* Write the same hash in many refs. If there is only 1 hash, the
690+
* disambiguating prefix is length 0 */
691+
for (i = 0; i < 256; i++) {
692+
char name[256];
693+
snprintf(name, sizeof(name), "ref%05d", i);
694+
ref.refname = name;
695+
err = reftable_writer_add_ref(w, &ref);
696+
EXPECT_ERR(err);
697+
}
698+
699+
err = reftable_writer_close(w);
700+
EXPECT_ERR(err);
701+
EXPECT(reftable_writer_stats(w)->object_id_len == 2);
702+
reftable_writer_free(w);
703+
strbuf_release(&buf);
704+
}
705+
706+
static void test_write_object_id_length(void)
707+
{
708+
struct reftable_write_options opts = {
709+
.block_size = 75,
710+
};
711+
struct strbuf buf = STRBUF_INIT;
712+
struct reftable_writer *w =
713+
reftable_new_writer(&strbuf_add_void, &buf, &opts);
714+
uint8_t hash[GIT_SHA1_RAWSZ] = {42};
715+
struct reftable_ref_record ref = {
716+
.update_index = 1,
717+
.value_type = REFTABLE_REF_VAL1,
718+
.value.val1 = hash,
719+
};
720+
int err;
721+
int i;
722+
723+
reftable_writer_set_limits(w, 1, 1);
724+
725+
/* Write the same hash in many refs. If there is only 1 hash, the
726+
* disambiguating prefix is length 0 */
727+
for (i = 0; i < 256; i++) {
728+
char name[256];
729+
snprintf(name, sizeof(name), "ref%05d", i);
730+
ref.refname = name;
731+
ref.value.val1[15] = i;
732+
err = reftable_writer_add_ref(w, &ref);
733+
EXPECT_ERR(err);
734+
}
735+
736+
err = reftable_writer_close(w);
737+
EXPECT_ERR(err);
738+
EXPECT(reftable_writer_stats(w)->object_id_len == 16);
739+
reftable_writer_free(w);
740+
strbuf_release(&buf);
741+
}
742+
743+
static void test_write_empty_key(void)
744+
{
745+
struct reftable_write_options opts = { 0 };
746+
struct strbuf buf = STRBUF_INIT;
747+
struct reftable_writer *w =
748+
reftable_new_writer(&strbuf_add_void, &buf, &opts);
749+
struct reftable_ref_record ref = {
750+
.refname = "",
751+
.update_index = 1,
752+
.value_type = REFTABLE_REF_DELETION,
753+
};
754+
int err;
755+
756+
reftable_writer_set_limits(w, 1, 1);
757+
err = reftable_writer_add_ref(w, &ref);
758+
EXPECT(err == REFTABLE_API_ERROR);
759+
760+
err = reftable_writer_close(w);
761+
EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR);
762+
reftable_writer_free(w);
763+
strbuf_release(&buf);
764+
}
765+
670766
static void test_write_key_order(void)
671767
{
672768
struct reftable_write_options opts = { 0 };
@@ -746,7 +842,10 @@ int readwrite_test_main(int argc, const char *argv[])
746842
RUN_TEST(test_table_read_write_seek_index);
747843
RUN_TEST(test_table_refs_for_no_index);
748844
RUN_TEST(test_table_refs_for_obj_index);
845+
RUN_TEST(test_write_empty_key);
749846
RUN_TEST(test_write_empty_table);
750847
RUN_TEST(test_log_overflow);
848+
RUN_TEST(test_write_object_id_length);
849+
RUN_TEST(test_write_object_id_min_length);
751850
return 0;
752851
}

reftable/reftable-writer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ int reftable_writer_close(struct reftable_writer *w);
143143
144144
This struct becomes invalid when the writer is freed.
145145
*/
146-
const struct reftable_stats *writer_stats(struct reftable_writer *w);
146+
const struct reftable_stats *reftable_writer_stats(struct reftable_writer *w);
147147

148148
/* reftable_writer_free deallocates memory for the writer */
149149
void reftable_writer_free(struct reftable_writer *w);

reftable/writer.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,14 +240,13 @@ static int writer_add_record(struct reftable_writer *w,
240240

241241
writer_reinit_block_writer(w, reftable_record_type(rec));
242242
err = block_writer_add(w->block_writer, rec);
243-
if (err < 0) {
243+
if (err == -1) {
244244
/* we are writing into memory, so an error can only mean it
245245
* doesn't fit. */
246246
err = REFTABLE_ENTRY_TOO_BIG_ERROR;
247247
goto done;
248248
}
249249

250-
err = 0;
251250
done:
252251
strbuf_release(&key);
253252
return err;
@@ -516,7 +515,9 @@ static void object_record_free(void *void_arg, void *key)
516515
static int writer_dump_object_index(struct reftable_writer *w)
517516
{
518517
struct write_record_arg closure = { .w = w };
519-
struct common_prefix_arg common = { NULL };
518+
struct common_prefix_arg common = {
519+
.max = 1, /* obj_id_len should be >= 2. */
520+
};
520521
if (w->obj_index_tree) {
521522
infix_walk(w->obj_index_tree, &update_common, &common);
522523
}
@@ -694,7 +695,7 @@ static int writer_flush_block(struct reftable_writer *w)
694695
return writer_flush_nonempty_block(w);
695696
}
696697

697-
const struct reftable_stats *writer_stats(struct reftable_writer *w)
698+
const struct reftable_stats *reftable_writer_stats(struct reftable_writer *w)
698699
{
699700
return &w->stats;
700701
}

0 commit comments

Comments
 (0)