aboutsummaryrefslogtreecommitdiffstats
path: root/reftable
diff options
context:
space:
mode:
authorKarthik Nayak <karthik.188@gmail.com>2025-01-22 06:35:49 +0100
committerJunio C Hamano <gitster@pobox.com>2025-01-22 09:51:36 -0800
commit017bd8923986acd4992fd21f3451fdd15ec6edce (patch)
tree12458bf0bb55eaab7322ff7f72e6c7d2471d914a /reftable
parente7c1b9f1231e49f5e0c5250ec84c68376619f415 (diff)
downloadgit-017bd8923986acd4992fd21f3451fdd15ec6edce.tar.gz
reftable: prevent 'update_index' changes after adding records
The function `reftable_writer_set_limits()` allows updating the 'min_update_index' and 'max_update_index' of a reftable writer. These values are written to both the writer's header and footer. Since the header is written during the first block write, any subsequent changes to the update index would create a mismatch between the header and footer values. The footer would contain the newer values while the header retained the original ones. To protect against this bug, prevent callers from updating these values after any record is written. To do this, modify the function to return an error whenever the limits are modified after any record adds. Check for record adds within `reftable_writer_set_limits()` by checking the `last_key` and `next` variable. The former is updated after each record added, but is reset at certain points. The latter is set after writing the first block. Modify all callers of the function to anticipate a return type and handle it accordingly. Add a unit test to also ensure the function returns the error as expected. Helped-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'reftable')
-rw-r--r--reftable/reftable-error.h1
-rw-r--r--reftable/reftable-writer.h24
-rw-r--r--reftable/stack.c6
-rw-r--r--reftable/writer.c15
4 files changed, 33 insertions, 13 deletions
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index f404826562..a7e33d964d 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -30,6 +30,7 @@ enum reftable_error {
/* Misuse of the API:
* - on writing a record with NULL refname.
+ * - on writing a record before setting the writer limits.
* - on writing a reftable_ref_record outside the table limits
* - on writing a ref or log record before the stack's
* next_update_inde*x
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 5f9afa620b..1ea014d389 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -124,17 +124,21 @@ int reftable_writer_new(struct reftable_writer **out,
int (*flush_func)(void *),
void *writer_arg, const struct reftable_write_options *opts);
-/* Set the range of update indices for the records we will add. When writing a
- table into a stack, the min should be at least
- reftable_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
-
- For transactional updates to a stack, typically min==max, and the
- update_index can be obtained by inspeciting the stack. When converting an
- existing ref database into a single reftable, this would be a range of
- update-index timestamps.
+/*
+ * Set the range of update indices for the records we will add. When writing a
+ * table into a stack, the min should be at least
+ * reftable_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
+ *
+ * For transactional updates to a stack, typically min==max, and the
+ * update_index can be obtained by inspeciting the stack. When converting an
+ * existing ref database into a single reftable, this would be a range of
+ * update-index timestamps.
+ *
+ * The function should be called before adding any records to the writer. If not
+ * it will fail with REFTABLE_API_ERROR.
*/
-void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
- uint64_t max);
+int reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
+ uint64_t max);
/*
Add a reftable_ref_record. The record should have names that come after
diff --git a/reftable/stack.c b/reftable/stack.c
index 531660a49f..9649dbbb04 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -1058,8 +1058,10 @@ static int stack_write_compact(struct reftable_stack *st,
for (size_t i = first; i <= last; i++)
st->stats.bytes += st->readers[i]->size;
- reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
- st->readers[last]->max_update_index);
+ err = reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
+ st->readers[last]->max_update_index);
+ if (err < 0)
+ goto done;
err = reftable_merged_table_new(&mt, st->readers + first, subtabs_len,
st->opts.hash_id);
diff --git a/reftable/writer.c b/reftable/writer.c
index 740c98038e..76e2401817 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -179,11 +179,24 @@ int reftable_writer_new(struct reftable_writer **out,
return 0;
}
-void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
+int reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
uint64_t max)
{
+ /*
+ * Set the min/max update index limits for the reftable writer.
+ * This must be called before adding any records, since:
+ * - The 'next' field gets set after writing the first block.
+ * - The 'last_key' field updates with each new record (but resets
+ * after sections).
+ * Returns REFTABLE_API_ERROR if called after writing has begun.
+ */
+ if (w->next || w->last_key.len)
+ return REFTABLE_API_ERROR;
+
w->min_update_index = min;
w->max_update_index = max;
+
+ return 0;
}
static void writer_release(struct reftable_writer *w)