diff options
| author | Junio C Hamano <gitster@pobox.com> | 2025-10-13 22:00:35 -0700 |
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2025-10-13 22:00:35 -0700 |
| commit | f50f046794a06cfb97c4ccc879b08788629dd067 (patch) | |
| tree | 24340fdc7f43cf9c3b9e747a3ed13461e64f9abe /reftable | |
| parent | Merge branch 'ps/commit-graph-per-object-source' (diff) | |
| parent | refs/reftable: add fsck check for checking the table name (diff) | |
| download | git-f50f046794a06cfb97c4ccc879b08788629dd067.tar.gz git-f50f046794a06cfb97c4ccc879b08788629dd067.zip | |
Merge branch 'kn/reftable-consistency-checks'
The reftable backend learned to sanity check its on-disk data more
carefully.
* kn/reftable-consistency-checks:
refs/reftable: add fsck check for checking the table name
reftable: add code to facilitate consistency checks
fsck: order 'fsck_msg_type' alphabetically
Documentation/fsck-msgids: remove duplicate msg id
reftable: check for trailing newline in 'tables.list'
refs: move consistency check msg to generic layer
refs: remove unused headers
Diffstat (limited to 'reftable')
| -rw-r--r-- | reftable/basics.c | 37 | ||||
| -rw-r--r-- | reftable/basics.h | 7 | ||||
| -rw-r--r-- | reftable/fsck.c | 100 | ||||
| -rw-r--r-- | reftable/reftable-fsck.h | 40 | ||||
| -rw-r--r-- | reftable/stack.c | 7 |
5 files changed, 169 insertions, 22 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/stack.c b/reftable/stack.c index f91ce50bcd..65d89820bd 100644 --- a/reftable/stack.c +++ b/reftable/stack.c @@ -109,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; |
