summaryrefslogtreecommitdiff
path: root/reftable
diff options
context:
space:
mode:
Diffstat (limited to 'reftable')
-rw-r--r--reftable/basics.c37
-rw-r--r--reftable/basics.h7
-rw-r--r--reftable/fsck.c100
-rw-r--r--reftable/reftable-fsck.h40
-rw-r--r--reftable/reftable-stack.h9
-rw-r--r--reftable/reftable-writer.h4
-rw-r--r--reftable/stack.c446
-rw-r--r--reftable/system.c2
-rw-r--r--reftable/system.h4
-rw-r--r--reftable/writer.c23
10 files changed, 407 insertions, 265 deletions
diff --git a/reftable/basics.c b/reftable/basics.c
index 9988ebd635..e969927b61 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -195,44 +195,55 @@ size_t names_length(const char **names)
return p - names;
}
-char **parse_names(char *buf, int size)
+int parse_names(char *buf, int size, char ***out)
{
char **names = NULL;
size_t names_cap = 0;
size_t names_len = 0;
char *p = buf;
char *end = buf + size;
+ int err = 0;
while (p < end) {
char *next = strchr(p, '\n');
- if (next && next < end) {
- *next = 0;
+ if (!next) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ } else if (next < end) {
+ *next = '\0';
} else {
next = end;
}
+
if (p < next) {
if (REFTABLE_ALLOC_GROW(names, names_len + 1,
- names_cap))
- goto err;
+ names_cap)) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
names[names_len] = reftable_strdup(p);
- if (!names[names_len++])
- goto err;
+ if (!names[names_len++]) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
}
p = next + 1;
}
- if (REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap))
- goto err;
+ if (REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap)) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
names[names_len] = NULL;
- return names;
-
-err:
+ *out = names;
+ return 0;
+done:
for (size_t i = 0; i < names_len; i++)
reftable_free(names[i]);
reftable_free(names);
- return NULL;
+ return err;
}
int names_equal(const char **a, const char **b)
diff --git a/reftable/basics.h b/reftable/basics.h
index 7d22f96261..e4b83b2b03 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -167,10 +167,11 @@ void free_names(char **a);
/*
* Parse a newline separated list of names. `size` is the length of the buffer,
- * without terminating '\0'. Empty names are discarded. Returns a `NULL`
- * pointer when allocations fail.
+ * without terminating '\0'. Empty names are discarded.
+ *
+ * Returns 0 on success, a reftable error code on error.
*/
-char **parse_names(char *buf, int size);
+int parse_names(char *buf, int size, char ***out);
/* compares two NULL-terminated arrays of strings. */
int names_equal(const char **a, const char **b);
diff --git a/reftable/fsck.c b/reftable/fsck.c
new file mode 100644
index 0000000000..26b9115b14
--- /dev/null
+++ b/reftable/fsck.c
@@ -0,0 +1,100 @@
+#include "basics.h"
+#include "reftable-fsck.h"
+#include "reftable-table.h"
+#include "stack.h"
+
+static bool table_has_valid_name(const char *name)
+{
+ const char *ptr = name;
+ char *endptr;
+
+ /* strtoull doesn't set errno on success */
+ errno = 0;
+
+ strtoull(ptr, &endptr, 16);
+ if (errno)
+ return false;
+ ptr = endptr;
+
+ if (*ptr != '-')
+ return false;
+ ptr++;
+
+ strtoull(ptr, &endptr, 16);
+ if (errno)
+ return false;
+ ptr = endptr;
+
+ if (*ptr != '-')
+ return false;
+ ptr++;
+
+ strtoul(ptr, &endptr, 16);
+ if (errno)
+ return false;
+ ptr = endptr;
+
+ if (strcmp(ptr, ".ref") && strcmp(ptr, ".log"))
+ return false;
+
+ return true;
+}
+
+typedef int (*table_check_fn)(struct reftable_table *table,
+ reftable_fsck_report_fn report_fn,
+ void *cb_data);
+
+static int table_check_name(struct reftable_table *table,
+ reftable_fsck_report_fn report_fn,
+ void *cb_data)
+{
+ if (!table_has_valid_name(table->name)) {
+ struct reftable_fsck_info info;
+
+ info.error = REFTABLE_FSCK_ERROR_TABLE_NAME;
+ info.msg = "invalid reftable table name";
+ info.path = table->name;
+
+ return report_fn(&info, cb_data);
+ }
+
+ return 0;
+}
+
+static int table_checks(struct reftable_table *table,
+ reftable_fsck_report_fn report_fn,
+ reftable_fsck_verbose_fn verbose_fn UNUSED,
+ void *cb_data)
+{
+ table_check_fn table_check_fns[] = {
+ table_check_name,
+ NULL,
+ };
+ int err = 0;
+
+ for (size_t i = 0; table_check_fns[i]; i++)
+ err |= table_check_fns[i](table, report_fn, cb_data);
+
+ return err;
+}
+
+int reftable_fsck_check(struct reftable_stack *stack,
+ reftable_fsck_report_fn report_fn,
+ reftable_fsck_verbose_fn verbose_fn,
+ void *cb_data)
+{
+ struct reftable_buf msg = REFTABLE_BUF_INIT;
+ int err = 0;
+
+ for (size_t i = 0; i < stack->tables_len; i++) {
+ reftable_buf_reset(&msg);
+ reftable_buf_addstr(&msg, "Checking table: ");
+ reftable_buf_addstr(&msg, stack->tables[i]->name);
+ verbose_fn(msg.buf, cb_data);
+
+ err |= table_checks(stack->tables[i], report_fn, verbose_fn, cb_data);
+ }
+
+ reftable_buf_release(&msg);
+ return err;
+}
diff --git a/reftable/reftable-fsck.h b/reftable/reftable-fsck.h
new file mode 100644
index 0000000000..007a392cf9
--- /dev/null
+++ b/reftable/reftable-fsck.h
@@ -0,0 +1,40 @@
+#ifndef REFTABLE_FSCK_H
+#define REFTABLE_FSCK_H
+
+#include "reftable-stack.h"
+
+enum reftable_fsck_error {
+ /* Invalid table name */
+ REFTABLE_FSCK_ERROR_TABLE_NAME = 0,
+ /* Used for bounds checking, must be last */
+ REFTABLE_FSCK_MAX_VALUE,
+};
+
+/* Represents an individual error encountered during the FSCK checks. */
+struct reftable_fsck_info {
+ enum reftable_fsck_error error;
+ const char *msg;
+ const char *path;
+};
+
+typedef int reftable_fsck_report_fn(struct reftable_fsck_info *info,
+ void *cb_data);
+typedef void reftable_fsck_verbose_fn(const char *msg, void *cb_data);
+
+/*
+ * Given a reftable stack, perform consistency checks on the stack.
+ *
+ * If an issue is encountered, the issue is reported to the callee via the
+ * provided 'report_fn'. If the issue is non-recoverable the flow will not
+ * continue. If it is recoverable, the flow will continue and further issues
+ * will be reported as identified.
+ *
+ * The 'verbose_fn' will be invoked to provide verbose information about
+ * the progress and state of the consistency checks.
+ */
+int reftable_fsck_check(struct reftable_stack *stack,
+ reftable_fsck_report_fn report_fn,
+ reftable_fsck_verbose_fn verbose_fn,
+ void *cb_data);
+
+#endif /* REFTABLE_FSCK_H */
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index 910ec6ef3a..d70fcb705d 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -68,12 +68,15 @@ int reftable_addition_commit(struct reftable_addition *add);
* transaction. Releases the lock if held. */
void reftable_addition_destroy(struct reftable_addition *add);
-/* add a new table to the stack. The write_table function must call
- * reftable_writer_set_limits, add refs and return an error value. */
+/*
+ * Add a new table to the stack. The write_table function must call
+ * reftable_writer_set_limits, add refs and return an error value.
+ * The flags are passed through to `reftable_stack_new_addition()`.
+ */
int reftable_stack_add(struct reftable_stack *st,
int (*write_table)(struct reftable_writer *wr,
void *write_arg),
- void *write_arg);
+ void *write_arg, unsigned flags);
struct reftable_iterator;
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 0fbeff17f4..1e7003cd69 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -156,7 +156,7 @@ int reftable_writer_add_ref(struct reftable_writer *w,
the records before adding them, reordering the records array passed in.
*/
int reftable_writer_add_refs(struct reftable_writer *w,
- struct reftable_ref_record *refs, int n);
+ struct reftable_ref_record *refs, size_t n);
/*
adds reftable_log_records. Log records are keyed by (refname, decreasing
@@ -171,7 +171,7 @@ int reftable_writer_add_log(struct reftable_writer *w,
the records before adding them, reordering records array passed in.
*/
int reftable_writer_add_logs(struct reftable_writer *w,
- struct reftable_log_record *logs, int n);
+ struct reftable_log_record *logs, size_t n);
/* reftable_writer_close finalizes the reftable. The writer is retained so
* statistics can be inspected. */
diff --git a/reftable/stack.c b/reftable/stack.c
index 4caf96aa1d..65d89820bd 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -17,18 +17,6 @@
#include "table.h"
#include "writer.h"
-static int stack_try_add(struct reftable_stack *st,
- int (*write_table)(struct reftable_writer *wr,
- void *arg),
- void *arg);
-static int stack_write_compact(struct reftable_stack *st,
- struct reftable_writer *wr,
- size_t first, size_t last,
- struct reftable_log_expiry_config *config);
-static void reftable_addition_close(struct reftable_addition *add);
-static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
- int reuse_open);
-
static int stack_filename(struct reftable_buf *dest, struct reftable_stack *st,
const char *name)
{
@@ -84,54 +72,6 @@ static int fd_writer_flush(void *arg)
return stack_fsync(writer->opts, writer->fd);
}
-int reftable_new_stack(struct reftable_stack **dest, const char *dir,
- const struct reftable_write_options *_opts)
-{
- struct reftable_buf list_file_name = REFTABLE_BUF_INIT;
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *p;
- int err;
-
- p = reftable_calloc(1, sizeof(*p));
- if (!p) {
- err = REFTABLE_OUT_OF_MEMORY_ERROR;
- goto out;
- }
-
- if (_opts)
- opts = *_opts;
- if (opts.hash_id == 0)
- opts.hash_id = REFTABLE_HASH_SHA1;
-
- *dest = NULL;
-
- reftable_buf_reset(&list_file_name);
- if ((err = reftable_buf_addstr(&list_file_name, dir)) < 0 ||
- (err = reftable_buf_addstr(&list_file_name, "/tables.list")) < 0)
- goto out;
-
- p->list_file = reftable_buf_detach(&list_file_name);
- p->list_fd = -1;
- p->opts = opts;
- p->reftable_dir = reftable_strdup(dir);
- if (!p->reftable_dir) {
- err = REFTABLE_OUT_OF_MEMORY_ERROR;
- goto out;
- }
-
- err = reftable_stack_reload_maybe_reuse(p, 1);
- if (err < 0)
- goto out;
-
- *dest = p;
- err = 0;
-
-out:
- if (err < 0)
- reftable_stack_destroy(p);
- return err;
-}
-
static int fd_read_lines(int fd, char ***namesp)
{
char *buf = NULL;
@@ -169,12 +109,7 @@ static int fd_read_lines(int fd, char ***namesp)
}
buf[size] = 0;
- *namesp = parse_names(buf, size);
- if (!*namesp) {
- err = REFTABLE_OUT_OF_MEMORY_ERROR;
- goto done;
- }
-
+ err = parse_names(buf, size, namesp);
done:
reftable_free(buf);
return err;
@@ -591,9 +526,59 @@ out:
return err;
}
-/* -1 = error
- 0 = up to date
- 1 = changed. */
+int reftable_new_stack(struct reftable_stack **dest, const char *dir,
+ const struct reftable_write_options *_opts)
+{
+ struct reftable_buf list_file_name = REFTABLE_BUF_INIT;
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *p;
+ int err;
+
+ p = reftable_calloc(1, sizeof(*p));
+ if (!p) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
+ if (_opts)
+ opts = *_opts;
+ if (opts.hash_id == 0)
+ opts.hash_id = REFTABLE_HASH_SHA1;
+
+ *dest = NULL;
+
+ reftable_buf_reset(&list_file_name);
+ if ((err = reftable_buf_addstr(&list_file_name, dir)) < 0 ||
+ (err = reftable_buf_addstr(&list_file_name, "/tables.list")) < 0)
+ goto out;
+
+ p->list_file = reftable_buf_detach(&list_file_name);
+ p->list_fd = -1;
+ p->opts = opts;
+ p->reftable_dir = reftable_strdup(dir);
+ if (!p->reftable_dir) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
+ err = reftable_stack_reload_maybe_reuse(p, 1);
+ if (err < 0)
+ goto out;
+
+ *dest = p;
+ err = 0;
+
+out:
+ if (err < 0)
+ reftable_stack_destroy(p);
+ return err;
+}
+
+/*
+ * Check whether the given stack is up-to-date with what we have in memory.
+ * Returns 0 if so, 1 if the stack is out-of-date or a negative error code
+ * otherwise.
+ */
static int stack_uptodate(struct reftable_stack *st)
{
char **names = NULL;
@@ -667,34 +652,6 @@ int reftable_stack_reload(struct reftable_stack *st)
return err;
}
-int reftable_stack_add(struct reftable_stack *st,
- int (*write)(struct reftable_writer *wr, void *arg),
- void *arg)
-{
- int err = stack_try_add(st, write, arg);
- if (err < 0) {
- if (err == REFTABLE_OUTDATED_ERROR) {
- /* Ignore error return, we want to propagate
- REFTABLE_OUTDATED_ERROR.
- */
- reftable_stack_reload(st);
- }
- return err;
- }
-
- return 0;
-}
-
-static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max)
-{
- char buf[100];
- uint32_t rnd = reftable_rand();
- snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
- min, max, rnd);
- reftable_buf_reset(dest);
- return reftable_buf_addstr(dest, buf);
-}
-
struct reftable_addition {
struct reftable_flock tables_list_lock;
struct reftable_stack *stack;
@@ -704,7 +661,25 @@ struct reftable_addition {
uint64_t next_update_index;
};
-#define REFTABLE_ADDITION_INIT {0}
+static void reftable_addition_close(struct reftable_addition *add)
+{
+ struct reftable_buf nm = REFTABLE_BUF_INIT;
+ size_t i;
+
+ for (i = 0; i < add->new_tables_len; i++) {
+ if (!stack_filename(&nm, add->stack, add->new_tables[i]))
+ unlink(nm.buf);
+ reftable_free(add->new_tables[i]);
+ add->new_tables[i] = NULL;
+ }
+ reftable_free(add->new_tables);
+ add->new_tables = NULL;
+ add->new_tables_len = 0;
+ add->new_tables_cap = 0;
+
+ flock_release(&add->tables_list_lock);
+ reftable_buf_release(&nm);
+}
static int reftable_stack_init_addition(struct reftable_addition *add,
struct reftable_stack *st,
@@ -713,18 +688,14 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
struct reftable_buf lock_file_name = REFTABLE_BUF_INIT;
int err;
+ memset(add, 0, sizeof(*add));
add->stack = st;
err = flock_acquire(&add->tables_list_lock, st->list_file,
st->opts.lock_timeout_ms);
- if (err < 0) {
- if (errno == EEXIST) {
- err = REFTABLE_LOCK_ERROR;
- } else {
- err = REFTABLE_IO_ERROR;
- }
+ if (err < 0)
goto done;
- }
+
if (st->opts.default_permissions) {
if (chmod(add->tables_list_lock.path,
st->opts.default_permissions) < 0) {
@@ -754,24 +725,54 @@ done:
return err;
}
-static void reftable_addition_close(struct reftable_addition *add)
+static int stack_try_add(struct reftable_stack *st,
+ int (*write_table)(struct reftable_writer *wr,
+ void *arg),
+ void *arg, unsigned flags)
{
- struct reftable_buf nm = REFTABLE_BUF_INIT;
- size_t i;
+ struct reftable_addition add;
+ int err;
- for (i = 0; i < add->new_tables_len; i++) {
- if (!stack_filename(&nm, add->stack, add->new_tables[i]))
- unlink(nm.buf);
- reftable_free(add->new_tables[i]);
- add->new_tables[i] = NULL;
+ err = reftable_stack_init_addition(&add, st, flags);
+ if (err < 0)
+ goto done;
+
+ err = reftable_addition_add(&add, write_table, arg);
+ if (err < 0)
+ goto done;
+
+ err = reftable_addition_commit(&add);
+done:
+ reftable_addition_close(&add);
+ return err;
+}
+
+int reftable_stack_add(struct reftable_stack *st,
+ int (*write)(struct reftable_writer *wr, void *arg),
+ void *arg, unsigned flags)
+{
+ int err = stack_try_add(st, write, arg, flags);
+ if (err < 0) {
+ if (err == REFTABLE_OUTDATED_ERROR) {
+ /* Ignore error return, we want to propagate
+ REFTABLE_OUTDATED_ERROR.
+ */
+ reftable_stack_reload(st);
+ }
+ return err;
}
- reftable_free(add->new_tables);
- add->new_tables = NULL;
- add->new_tables_len = 0;
- add->new_tables_cap = 0;
- flock_release(&add->tables_list_lock);
- reftable_buf_release(&nm);
+ return 0;
+}
+
+static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max)
+{
+ char buf[100];
+ uint32_t rnd = reftable_rand();
+ snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
+ min, max, rnd);
+ reftable_buf_reset(dest);
+ return reftable_buf_addstr(dest, buf);
}
void reftable_addition_destroy(struct reftable_addition *add)
@@ -841,10 +842,13 @@ int reftable_addition_commit(struct reftable_addition *add)
* control. It is possible that a concurrent writer is already
* trying to compact parts of the stack, which would lead to a
* `REFTABLE_LOCK_ERROR` because parts of the stack are locked
- * already. This is a benign error though, so we ignore it.
+ * already. Similarly, the stack may have been rewritten by a
+ * concurrent writer, which causes `REFTABLE_OUTDATED_ERROR`.
+ * Both of these errors are benign, so we simply ignore them.
*/
err = reftable_stack_auto_compact(add->stack);
- if (err < 0 && err != REFTABLE_LOCK_ERROR)
+ if (err < 0 && err != REFTABLE_LOCK_ERROR &&
+ err != REFTABLE_OUTDATED_ERROR)
goto done;
err = 0;
}
@@ -858,39 +862,18 @@ int reftable_stack_new_addition(struct reftable_addition **dest,
struct reftable_stack *st,
unsigned int flags)
{
- int err = 0;
- struct reftable_addition empty = REFTABLE_ADDITION_INIT;
+ int err;
REFTABLE_CALLOC_ARRAY(*dest, 1);
if (!*dest)
return REFTABLE_OUT_OF_MEMORY_ERROR;
- **dest = empty;
err = reftable_stack_init_addition(*dest, st, flags);
if (err) {
reftable_free(*dest);
*dest = NULL;
}
- return err;
-}
-static int stack_try_add(struct reftable_stack *st,
- int (*write_table)(struct reftable_writer *wr,
- void *arg),
- void *arg)
-{
- struct reftable_addition add = REFTABLE_ADDITION_INIT;
- int err = reftable_stack_init_addition(&add, st, 0);
- if (err < 0)
- goto done;
-
- err = reftable_addition_add(&add, write_table, arg);
- if (err < 0)
- goto done;
-
- err = reftable_addition_commit(&add);
-done:
- reftable_addition_close(&add);
return err;
}
@@ -1007,72 +990,6 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
return 1;
}
-static int stack_compact_locked(struct reftable_stack *st,
- size_t first, size_t last,
- struct reftable_log_expiry_config *config,
- struct reftable_tmpfile *tab_file_out)
-{
- struct reftable_buf next_name = REFTABLE_BUF_INIT;
- struct reftable_buf tab_file_path = REFTABLE_BUF_INIT;
- struct reftable_writer *wr = NULL;
- struct fd_writer writer= {
- .opts = &st->opts,
- };
- struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT;
- int err = 0;
-
- err = format_name(&next_name, reftable_table_min_update_index(st->tables[first]),
- reftable_table_max_update_index(st->tables[last]));
- if (err < 0)
- goto done;
-
- err = stack_filename(&tab_file_path, st, next_name.buf);
- if (err < 0)
- goto done;
-
- err = reftable_buf_addstr(&tab_file_path, ".temp.XXXXXX");
- if (err < 0)
- goto done;
-
- err = tmpfile_from_pattern(&tab_file, tab_file_path.buf);
- if (err < 0)
- goto done;
-
- if (st->opts.default_permissions &&
- chmod(tab_file.path, st->opts.default_permissions) < 0) {
- err = REFTABLE_IO_ERROR;
- goto done;
- }
-
- writer.fd = tab_file.fd;
- err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush,
- &writer, &st->opts);
- if (err < 0)
- goto done;
-
- err = stack_write_compact(st, wr, first, last, config);
- if (err < 0)
- goto done;
-
- err = reftable_writer_close(wr);
- if (err < 0)
- goto done;
-
- err = tmpfile_close(&tab_file);
- if (err < 0)
- goto done;
-
- *tab_file_out = tab_file;
- tab_file = REFTABLE_TMPFILE_INIT;
-
-done:
- tmpfile_delete(&tab_file);
- reftable_writer_free(wr);
- reftable_buf_release(&next_name);
- reftable_buf_release(&tab_file_path);
- return err;
-}
-
static int stack_write_compact(struct reftable_stack *st,
struct reftable_writer *wr,
size_t first, size_t last,
@@ -1172,6 +1089,72 @@ done:
return err;
}
+static int stack_compact_locked(struct reftable_stack *st,
+ size_t first, size_t last,
+ struct reftable_log_expiry_config *config,
+ struct reftable_tmpfile *tab_file_out)
+{
+ struct reftable_buf next_name = REFTABLE_BUF_INIT;
+ struct reftable_buf tab_file_path = REFTABLE_BUF_INIT;
+ struct reftable_writer *wr = NULL;
+ struct fd_writer writer= {
+ .opts = &st->opts,
+ };
+ struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT;
+ int err = 0;
+
+ err = format_name(&next_name, reftable_table_min_update_index(st->tables[first]),
+ reftable_table_max_update_index(st->tables[last]));
+ if (err < 0)
+ goto done;
+
+ err = stack_filename(&tab_file_path, st, next_name.buf);
+ if (err < 0)
+ goto done;
+
+ err = reftable_buf_addstr(&tab_file_path, ".temp.XXXXXX");
+ if (err < 0)
+ goto done;
+
+ err = tmpfile_from_pattern(&tab_file, tab_file_path.buf);
+ if (err < 0)
+ goto done;
+
+ if (st->opts.default_permissions &&
+ chmod(tab_file.path, st->opts.default_permissions) < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ writer.fd = tab_file.fd;
+ err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush,
+ &writer, &st->opts);
+ if (err < 0)
+ goto done;
+
+ err = stack_write_compact(st, wr, first, last, config);
+ if (err < 0)
+ goto done;
+
+ err = reftable_writer_close(wr);
+ if (err < 0)
+ goto done;
+
+ err = tmpfile_close(&tab_file);
+ if (err < 0)
+ goto done;
+
+ *tab_file_out = tab_file;
+ tab_file = REFTABLE_TMPFILE_INIT;
+
+done:
+ tmpfile_delete(&tab_file);
+ reftable_writer_free(wr);
+ reftable_buf_release(&next_name);
+ reftable_buf_release(&tab_file_path);
+ return err;
+}
+
enum stack_compact_range_flags {
/*
* Perform a best-effort compaction. That is, even if we cannot lock
@@ -1219,17 +1202,27 @@ static int stack_compact_range(struct reftable_stack *st,
* which are part of the user-specified range.
*/
err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
- if (err < 0) {
- if (errno == EEXIST)
- err = REFTABLE_LOCK_ERROR;
- else
- err = REFTABLE_IO_ERROR;
+ if (err < 0)
goto done;
- }
+ /*
+ * Check whether the stack is up-to-date. We unfortunately cannot
+ * handle the situation gracefully in case it's _not_ up-to-date
+ * because the range of tables that the user has requested us to
+ * compact may have been changed. So instead we abort.
+ *
+ * We could in theory improve the situation by having the caller not
+ * pass in a range, but instead the list of tables to compact. If so,
+ * we could check that relevant tables still exist. But for now it's
+ * good enough to just abort.
+ */
err = stack_uptodate(st);
- if (err)
+ if (err < 0)
+ goto done;
+ if (err > 0) {
+ err = REFTABLE_OUTDATED_ERROR;
goto done;
+ }
/*
* Lock all tables in the user-provided range. This is the slice of our
@@ -1264,7 +1257,7 @@ static int stack_compact_range(struct reftable_stack *st,
* tables, otherwise there would be nothing to compact.
* In that case, we return a lock error to our caller.
*/
- if (errno == EEXIST && last - (i - 1) >= 2 &&
+ if (err == REFTABLE_LOCK_ERROR && last - (i - 1) >= 2 &&
flags & STACK_COMPACT_RANGE_BEST_EFFORT) {
err = 0;
/*
@@ -1276,13 +1269,9 @@ static int stack_compact_range(struct reftable_stack *st,
*/
first = (i - 1) + 1;
break;
- } else if (errno == EEXIST) {
- err = REFTABLE_LOCK_ERROR;
- goto done;
- } else {
- err = REFTABLE_IO_ERROR;
- goto done;
}
+
+ goto done;
}
/*
@@ -1291,10 +1280,8 @@ static int stack_compact_range(struct reftable_stack *st,
* of tables.
*/
err = flock_close(&table_locks[nlocks++]);
- if (err < 0) {
- err = REFTABLE_IO_ERROR;
+ if (err < 0)
goto done;
- }
}
/*
@@ -1326,13 +1313,8 @@ static int stack_compact_range(struct reftable_stack *st,
* the new table.
*/
err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
- if (err < 0) {
- if (errno == EEXIST)
- err = REFTABLE_LOCK_ERROR;
- else
- err = REFTABLE_IO_ERROR;
+ if (err < 0)
goto done;
- }
if (st->opts.default_permissions) {
if (chmod(tables_list_lock.path,
diff --git a/reftable/system.c b/reftable/system.c
index 1ee268b125..725a25844e 100644
--- a/reftable/system.c
+++ b/reftable/system.c
@@ -72,7 +72,7 @@ int flock_acquire(struct reftable_flock *l, const char *target_path,
reftable_free(lockfile);
if (errno == EEXIST)
return REFTABLE_LOCK_ERROR;
- return -1;
+ return REFTABLE_IO_ERROR;
}
l->fd = get_lock_file_fd(lockfile);
diff --git a/reftable/system.h b/reftable/system.h
index beb9d2431f..c54ed4cad6 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -81,7 +81,9 @@ struct reftable_flock {
* to acquire the lock. If `timeout_ms` is 0 we don't wait, if it is negative
* we block indefinitely.
*
- * Retrun 0 on success, a reftable error code on error.
+ * Retrun 0 on success, a reftable error code on error. Specifically,
+ * `REFTABLE_LOCK_ERROR` should be returned in case the target path is already
+ * locked.
*/
int flock_acquire(struct reftable_flock *l, const char *target_path,
long timeout_ms);
diff --git a/reftable/writer.c b/reftable/writer.c
index 3b4ebdd6dc..0133b64975 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -395,14 +395,16 @@ out:
}
int reftable_writer_add_refs(struct reftable_writer *w,
- struct reftable_ref_record *refs, int n)
+ struct reftable_ref_record *refs, size_t n)
{
int err = 0;
- int i = 0;
- QSORT(refs, n, reftable_ref_record_compare_name);
- for (i = 0; err == 0 && i < n; i++) {
+
+ if (n)
+ qsort(refs, n, sizeof(*refs), reftable_ref_record_compare_name);
+
+ for (size_t i = 0; err == 0 && i < n; i++)
err = reftable_writer_add_ref(w, &refs[i]);
- }
+
return err;
}
@@ -486,15 +488,16 @@ done:
}
int reftable_writer_add_logs(struct reftable_writer *w,
- struct reftable_log_record *logs, int n)
+ struct reftable_log_record *logs, size_t n)
{
int err = 0;
- int i = 0;
- QSORT(logs, n, reftable_log_record_compare_key);
- for (i = 0; err == 0 && i < n; i++) {
+ if (n)
+ qsort(logs, n, sizeof(*logs), reftable_log_record_compare_key);
+
+ for (size_t i = 0; err == 0 && i < n; i++)
err = reftable_writer_add_log(w, &logs[i]);
- }
+
return err;
}