refs: make errno output explicit for read_raw_ref_fn

This makes it explicit how alternative ref backends should report errors in
read_raw_ref_fn.

read_raw_ref_fn needs to supply a credible errno for a number of cases. These
are primarily:

1) The files backend calls read_raw_ref from lock_raw_ref, and uses the
resulting error codes to create/remove directories as needed.

2) ENOENT should be translated in a zero OID, optionally with REF_ISBROKEN set,
returning the last successfully resolved symref. This is necessary so
read_raw_ref("HEAD") on an empty repo returns refs/heads/main (or the default branch
du-jour), and we know on which branch to create the first commit.

Make this information flow explicit by adding a failure_errno to the signature
of read_raw_ref. All errnos from the files backend are still propagated
unchanged, even though inspection suggests only ENOTDIR, EISDIR and ENOENT are
relevant.

Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Han-Wen Nienhuys 2021-08-23 13:52:40 +02:00 committed by Junio C Hamano
parent 1ae6ed230a
commit 5b12e16bb1
5 changed files with 35 additions and 31 deletions

2
refs.c
View File

@ -1682,7 +1682,7 @@ int refs_read_raw_ref(struct ref_store *ref_store,
} }
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
type); type, &errno);
} }
/* This function needs to return a meaningful errno on failure */ /* This function needs to return a meaningful errno on failure */

View File

@ -238,15 +238,14 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname, static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent, struct object_id *oid, struct strbuf *referent,
unsigned int *type) unsigned int *type, int *failure_errno)
{ {
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
int res = 0; int res = 0;
oidcpy(oid, null_oid()); oidcpy(oid, null_oid());
errno = 0;
res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent, res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,
type); type, failure_errno);
if (res == 0) { if (res == 0) {
trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n", trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n",
@ -254,7 +253,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
} else { } else {
trace_printf_key(&trace_refs, trace_printf_key(&trace_refs,
"read_raw_ref: %s: %d (errno %d)\n", refname, "read_raw_ref: %s: %d (errno %d)\n", refname,
res, errno); res, *failure_errno);
} }
return res; return res;
} }

View File

@ -341,9 +341,9 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
return refs->loose; return refs->loose;
} }
static int files_read_raw_ref(struct ref_store *ref_store, static int files_read_raw_ref(struct ref_store *ref_store, const char *refname,
const char *refname, struct object_id *oid, struct object_id *oid, struct strbuf *referent,
struct strbuf *referent, unsigned int *type) unsigned int *type, int *failure_errno)
{ {
struct files_ref_store *refs = struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@ -354,7 +354,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,
struct stat st; struct stat st;
int fd; int fd;
int ret = -1; int ret = -1;
int save_errno;
int remaining_retries = 3; int remaining_retries = 3;
*type = 0; *type = 0;
@ -459,10 +458,9 @@ stat_ref:
ret = parse_loose_ref_contents(buf, oid, referent, type); ret = parse_loose_ref_contents(buf, oid, referent, type);
out: out:
save_errno = errno; *failure_errno = errno;
strbuf_release(&sb_path); strbuf_release(&sb_path);
strbuf_release(&sb_contents); strbuf_release(&sb_contents);
errno = save_errno;
return ret; return ret;
} }
@ -540,6 +538,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
struct strbuf ref_file = STRBUF_INIT; struct strbuf ref_file = STRBUF_INIT;
int attempts_remaining = 3; int attempts_remaining = 3;
int ret = TRANSACTION_GENERIC_ERROR; int ret = TRANSACTION_GENERIC_ERROR;
int failure_errno;
assert(err); assert(err);
files_assert_main_repository(refs, "lock_raw_ref"); files_assert_main_repository(refs, "lock_raw_ref");
@ -610,7 +609,9 @@ retry:
if (hold_lock_file_for_update_timeout( if (hold_lock_file_for_update_timeout(
&lock->lk, ref_file.buf, LOCK_NO_DEREF, &lock->lk, ref_file.buf, LOCK_NO_DEREF,
get_files_ref_lock_timeout_ms()) < 0) { get_files_ref_lock_timeout_ms()) < 0) {
if (errno == ENOENT && --attempts_remaining > 0) { int myerr = errno;
errno = 0;
if (myerr == ENOENT && --attempts_remaining > 0) {
/* /*
* Maybe somebody just deleted one of the * Maybe somebody just deleted one of the
* directories leading to ref_file. Try * directories leading to ref_file. Try
@ -618,7 +619,7 @@ retry:
*/ */
goto retry; goto retry;
} else { } else {
unable_to_lock_message(ref_file.buf, errno, err); unable_to_lock_message(ref_file.buf, myerr, err);
goto error_return; goto error_return;
} }
} }
@ -628,9 +629,9 @@ retry:
* fear that its value will change. * fear that its value will change.
*/ */
if (files_read_raw_ref(&refs->base, refname, if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent,
&lock->old_oid, referent, type)) { type, &failure_errno)) {
if (errno == ENOENT) { if (failure_errno == ENOENT) {
if (mustexist) { if (mustexist) {
/* Garden variety missing reference. */ /* Garden variety missing reference. */
strbuf_addf(err, "unable to resolve reference '%s'", strbuf_addf(err, "unable to resolve reference '%s'",
@ -654,7 +655,7 @@ retry:
* reference named "refs/foo/bar/baz". * reference named "refs/foo/bar/baz".
*/ */
} }
} else if (errno == EISDIR) { } else if (failure_errno == EISDIR) {
/* /*
* There is a directory in the way. It might have * There is a directory in the way. It might have
* contained references that have been deleted. If * contained references that have been deleted. If
@ -692,13 +693,13 @@ retry:
goto error_return; goto error_return;
} }
} }
} else if (errno == EINVAL && (*type & REF_ISBROKEN)) { } else if (failure_errno == EINVAL && (*type & REF_ISBROKEN)) {
strbuf_addf(err, "unable to resolve reference '%s': " strbuf_addf(err, "unable to resolve reference '%s': "
"reference broken", refname); "reference broken", refname);
goto error_return; goto error_return;
} else { } else {
strbuf_addf(err, "unable to resolve reference '%s': %s", strbuf_addf(err, "unable to resolve reference '%s': %s",
refname, strerror(errno)); refname, strerror(failure_errno));
goto error_return; goto error_return;
} }

View File

@ -724,9 +724,9 @@ static struct snapshot *get_snapshot(struct packed_ref_store *refs)
return refs->snapshot; return refs->snapshot;
} }
static int packed_read_raw_ref(struct ref_store *ref_store, static int packed_read_raw_ref(struct ref_store *ref_store, const char *refname,
const char *refname, struct object_id *oid, struct object_id *oid, struct strbuf *referent,
struct strbuf *referent, unsigned int *type) unsigned int *type, int *failure_errno)
{ {
struct packed_ref_store *refs = struct packed_ref_store *refs =
packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@ -739,7 +739,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store,
if (!rec) { if (!rec) {
/* refname is not a packed reference. */ /* refname is not a packed reference. */
errno = ENOENT; *failure_errno = ENOENT;
return -1; return -1;
} }

View File

@ -620,11 +620,15 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
* properly-formatted or even safe reference name. NEITHER INPUT NOR * properly-formatted or even safe reference name. NEITHER INPUT NOR
* OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION. * OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION.
* *
* Return 0 on success. If the ref doesn't exist, set errno to ENOENT * Return 0 on success, or -1 on failure. If the ref exists but is neither a
* and return -1. If the ref exists but is neither a symbolic ref nor * symbolic ref nor an object ID, it is broken. In this case set REF_ISBROKEN in
* an object ID, it is broken; set REF_ISBROKEN in type, and return -1 * type, and return -1 (failure_errno should not be ENOENT)
* (errno should not be ENOENT) If there is another error reading the *
* ref, set errno appropriately and return -1. * failure_errno provides errno codes that are interpreted beyond error
* reporting. The following error codes have special meaning:
* * ENOENT: the ref doesn't exist
* * EISDIR: ref name is a directory
* * ENOTDIR: ref prefix is not a directory
* *
* Backend-specific flags might be set in type as well, regardless of * Backend-specific flags might be set in type as well, regardless of
* outcome. * outcome.
@ -638,9 +642,9 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
* - in all other cases, referent will be untouched, and therefore * - in all other cases, referent will be untouched, and therefore
* refname will still be valid and unchanged. * refname will still be valid and unchanged.
*/ */
typedef int read_raw_ref_fn(struct ref_store *ref_store, typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
const char *refname, struct object_id *oid, struct object_id *oid, struct strbuf *referent,
struct strbuf *referent, unsigned int *type); unsigned int *type, int *failure_errno);
struct ref_storage_be { struct ref_storage_be {
struct ref_storage_be *next; struct ref_storage_be *next;