* multiple entries. Each entry will contain a different update_index,
* so set the limits accordingly.
*/
- reftable_writer_set_limits(writer, ts, ts + arg->max_index);
+ ret = reftable_writer_set_limits(writer, ts, ts + arg->max_index);
+ if (ret < 0)
+ goto done;
for (i = 0; i < arg->updates_nr; i++) {
struct reftable_transaction_update *tx_update = &arg->updates[i];
deletion_ts = creation_ts = reftable_stack_next_update_index(arg->be->stack);
if (arg->delete_old)
creation_ts++;
- reftable_writer_set_limits(writer, deletion_ts, creation_ts);
+ ret = reftable_writer_set_limits(writer, deletion_ts, creation_ts);
+ if (ret < 0)
+ goto done;
/*
* Add the new reference. If this is a rename then we also delete the
if (ret <= 0)
goto done;
- reftable_writer_set_limits(writer, ts, ts);
+ ret = reftable_writer_set_limits(writer, ts, ts);
+ if (ret < 0)
+ goto done;
/*
* The existence entry has both old and new object ID set to the
uint64_t ts = reftable_stack_next_update_index(arg->stack);
int ret;
- reftable_writer_set_limits(writer, ts, ts);
+ ret = reftable_writer_set_limits(writer, ts, ts);
+ if (ret < 0)
+ goto out;
ret = reftable_stack_init_log_iterator(arg->stack, &it);
if (ret < 0)
if (arg->records[i].value_type == REFTABLE_LOG_UPDATE)
live_records++;
- reftable_writer_set_limits(writer, ts, ts);
+ ret = reftable_writer_set_limits(writer, ts, ts);
+ if (ret < 0)
+ return ret;
if (!is_null_oid(&arg->update_oid)) {
struct reftable_ref_record ref = {0};
/* 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
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
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);
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)
static int write_test_ref(struct reftable_writer *wr, void *arg)
{
struct reftable_ref_record *ref = arg;
- reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
+ check(!reftable_writer_set_limits(wr, ref->update_index,
+ ref->update_index));
return reftable_writer_add_ref(wr, ref);
}
{
struct write_log_arg *wla = arg;
- reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
+ check(!reftable_writer_set_limits(wr, wla->update_index,
+ wla->update_index));
return reftable_writer_add_log(wr, wla->log);
}
static int write_nothing(struct reftable_writer *wr, void *arg UNUSED)
{
- reftable_writer_set_limits(wr, 1, 1);
+ check(!reftable_writer_set_limits(wr, 1, 1));
return 0;
}
clear_dir(dir);
}
+static int write_limits_after_ref(struct reftable_writer *wr, void *arg)
+{
+ struct reftable_ref_record *ref = arg;
+ check(!reftable_writer_set_limits(wr, ref->update_index, ref->update_index));
+ check(!reftable_writer_add_ref(wr, ref));
+ return reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
+}
+
+static void t_reftable_invalid_limit_updates(void)
+{
+ struct reftable_ref_record ref = {
+ .refname = (char *) "HEAD",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ struct reftable_write_options opts = {
+ .default_permissions = 0660,
+ };
+ struct reftable_addition *add = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_stack *st = NULL;
+ int err;
+
+ err = reftable_new_stack(&st, dir, &opts);
+ check(!err);
+
+ reftable_addition_destroy(add);
+
+ err = reftable_stack_new_addition(&add, st, 0);
+ check(!err);
+
+ /*
+ * write_limits_after_ref also updates the update indexes after adding
+ * the record. This should cause an err to be returned, since the limits
+ * must be set at the start.
+ */
+ err = reftable_addition_add(add, write_limits_after_ref, &ref);
+ check_int(err, ==, REFTABLE_API_ERROR);
+
+ reftable_addition_destroy(add);
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
TEST(t_empty_add(), "empty addition to stack");
TEST(t_read_file(), "read_lines works");
TEST(t_reflog_expire(), "expire reflog entries");
+ TEST(t_reftable_invalid_limit_updates(), "prevent limit updates after adding records");
TEST(t_reftable_stack_add(), "add multiple refs and logs to stack");
TEST(t_reftable_stack_add_one(), "add a single ref record to stack");
TEST(t_reftable_stack_add_performs_auto_compaction(), "addition to stack triggers auto-compaction");